synfull 0.1.3 → 0.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +674 -21
- package/README.md +84 -23
- package/backend/app/api/routes.py +84 -4
- package/backend/app/integrations/discord_alerts.py +111 -13
- package/backend/app/schemas/alerts.py +10 -0
- package/backend/app/services/management.py +8 -5
- package/frontend/dist/assets/index-BdVFYwZZ.js +251 -0
- package/frontend/dist/assets/index-pBaLmQTy.css +1 -0
- package/frontend/dist/index.html +2 -2
- package/package.json +1 -1
- package/scripts/synfull.cjs +89 -2
- package/frontend/dist/assets/index-B40UafPr.css +0 -1
- package/frontend/dist/assets/index-ERXSRoTY.js +0 -249
|
@@ -0,0 +1 @@
|
|
|
1
|
+
@import"https://fonts.googleapis.com/css2?family=DM+Sans:wght@400;500;600;700&family=Space+Grotesk:wght@500;600;700&display=swap";@tailwind base;@tailwind components;@tailwind utilities;:root[data-theme]{color:var(--text-main)}:root[data-theme=synfull]{color:#e7ebf3}body{color:var(--text-main);background:radial-gradient(ellipse at 72% -10%,color-mix(in srgb,var(--accent) 10%,transparent),transparent 38%),var(--page-bg)}.sidebar{background:var(--sidebar-bg)}.panel,.metric-card,.skeleton-card{background:var(--panel-bg);border-color:var(--surface-border)}.form-input,.webhook-input-row input{background:var(--field-bg);color:var(--text-main)}:root[data-theme=hacker] body{font-family:ui-monospace,SFMono-Regular,Consolas,monospace}:root[data-theme=hacker] .brand-icon,:root[data-theme=hacker] .metric-icon{background:#062712;color:#40ff8a;box-shadow:0 0 15px #17d45c28}:root[data-theme=hacker] .nav-item.active,:root[data-theme=hacker] .nav-item:hover{background:#0b2615;color:#c8ffd8}:root[data-theme=hacker] .metric-card,:root[data-theme=hacker] .panel{box-shadow:0 0 20px #10a64712,inset 0 1px #7aff9e0c}:root[data-theme=light] .nav-item,:root[data-theme=light] td,:root[data-theme=light] .detail-row,:root[data-theme=light] .metric-title,:root[data-theme=light] .topbar{color:#40506a}:root[data-theme=light] th{color:#586880}:root[data-theme=light] .host-pill,:root[data-theme=light] .console-button,:root[data-theme=light] .throttle-field select{background:#edf1f7;color:#27364e;border-color:#cbd4e1}:root[data-theme=light] .status{background:#d8f4e6;color:#176742}:root[data-theme=light] .metric-icon.blue{background:#e4eaff;color:#315fe8}:root[data-theme=light] .metric-icon.violet{background:#eee6ff;color:#7044b5}:root[data-theme=light] .metric-icon.green{background:#d8f4e6;color:#176742}:root[data-theme=light] .metric-icon.amber{background:#fff0d5;color:#98620a}:root{font-family:DM Sans,sans-serif;color:#e7ebf3;background:#0b111b;font-synthesis:none;text-rendering:optimizeLegibility;--surface:rgba(17,26,39,.82);--surface-border:rgba(134,157,194,.14);--accent:#8290ff;--page-bg:#0b111b;--sidebar-bg:rgba(12,19,30,.88);--panel-bg:linear-gradient(145deg,rgba(20,30,45,.91),rgba(14,22,34,.82));--text-main:#e7ebf3;--text-muted:#8490a2;--field-bg:rgba(7,12,20,.7);--border-color:rgba(134,157,194,.14)}:root[data-theme=monochrome]{--accent:#d1d5db;--page-bg:#111315;--sidebar-bg:rgba(15,17,19,.94);--panel-bg:linear-gradient(145deg,rgba(31,33,35,.96),rgba(22,24,26,.94));--surface-border:rgba(220,224,228,.12);--text-main:#f0f1f2;--text-muted:#9da1a6;--field-bg:rgba(10,11,12,.8)}:root[data-theme=hacker]{--accent:#40ff8a;--page-bg:#020805;--sidebar-bg:rgba(1,10,5,.97);--panel-bg:linear-gradient(145deg,rgba(5,21,12,.98),rgba(3,13,8,.98));--surface-border:rgba(53,255,121,.2);--text-main:#d8ffe5;--text-muted:#80ad8c;--field-bg:#020805}:root[data-theme=light]{--accent:#315fe8;--page-bg:#f1f4f8;--sidebar-bg:rgba(255,255,255,.96);--panel-bg:linear-gradient(145deg,#fff,#f7f9fc);--surface-border:rgba(49,65,89,.14);--text-main:#172033;--text-muted:#627087;--field-bg:#f8fafc;color:var(--text-main)}:root[data-theme=oled]{--accent:#97a6ff;--page-bg:#000;--sidebar-bg:rgba(0,0,0,.98);--panel-bg:linear-gradient(145deg,#08090b,#030405);--surface-border:rgba(154,169,199,.15);--text-main:#eff2f7;--text-muted:#919aaa;--field-bg:#000}*{box-sizing:border-box}body{margin:0;min-width:350px;min-height:100vh;background:radial-gradient(ellipse at 72% -10%,rgba(76,95,192,.17),transparent 38%),radial-gradient(ellipse at 18% 47%,rgba(32,115,119,.09),transparent 32%),#0b111b;background-attachment:fixed}.app-shell{min-height:100vh}.app-shell:before{content:"";position:fixed;top:0;right:0;bottom:0;left:0;pointer-events:none;z-index:-1;opacity:.13;background-image:radial-gradient(rgba(185,202,228,.4) .6px,transparent .6px);background-size:25px 25px;-webkit-mask-image:linear-gradient(to bottom,black,transparent 70%);mask-image:linear-gradient(to bottom,black,transparent 70%)}.sidebar{width:232px;position:fixed;inset:0 auto 0 0;background:#0c131ee0;-webkit-backdrop-filter:blur(18px);backdrop-filter:blur(18px);border-right:1px solid rgba(134,157,194,.13);padding:25px 16px;display:flex;flex-direction:column;z-index:2;box-shadow:12px 0 50px #0000001f}.brand{display:flex;align-items:center;gap:10px;font-family:Space Grotesk,sans-serif;font-size:21px;font-weight:700;letter-spacing:-.8px;padding:0 10px 42px}.brand-icon{height:31px;width:31px;border-radius:9px;background:linear-gradient(145deg,#8190ff,#5869dc);color:#fff;display:grid;place-items:center;box-shadow:0 5px 20px #6474e540,inset 0 1px #ffffff42}.nav-label,.eyebrow{font-size:10px;letter-spacing:1.2px;color:#728096;font-weight:700}.nav-label{padding:0 11px 10px}.integration-label{padding-top:25px}.nav-item{display:flex;align-items:center;gap:11px;padding:10px 11px;border-radius:7px;color:#8995a8;text-decoration:none;font-size:13px;margin:2px 0;transition:background .15s,color .15s}.nav-item.active,.nav-item:hover{background:linear-gradient(100deg,#6c7edd2e,#6c7edd12);color:#edf0fa}.nav-item.active{box-shadow:inset 2px 0 #91a0ff}.nav-item.active svg{color:#9ba6ff}.nav-badge{margin-left:auto;min-width:19px;height:19px;padding:0 5px;border-radius:12px;display:grid;place-items:center;background:#49342b;color:#f0b57c;font-size:10px;font-weight:700}.nav-badge.online{background:#173a31;color:#65d5a3}.sidebar-bottom{margin-top:auto;border-top:1px solid #202a38;padding:18px 8px 0;color:#a9b3c4;font-size:12px}.sidebar-bottom small{display:block;color:#78869a;margin:6px 0 0 17px}.live-dot{display:inline-block;width:7px;height:7px;background:#49ce9a;border-radius:50%;margin-right:8px;box-shadow:0 0 9px #49ce9a70}.main{margin-left:232px;min-height:100vh;padding:0 36px 22px;max-width:1700px;position:relative}.topbar{height:62px;border-bottom:1px solid rgba(134,157,194,.13);display:flex;align-items:center;justify-content:space-between;font-size:12px;color:#c9d0dd}.eyebrow{margin-right:7px}.connection{color:#a7b2c2;font-size:11px}.page-heading{display:flex;align-items:center;justify-content:space-between;margin:32px 0 23px}.page-heading h1{font-family:Space Grotesk,sans-serif;font-size:24px;letter-spacing:-.8px;margin:0 0 6px}.page-heading p,.panel-head p{margin:0;color:#8490a2;font-size:12px}.host-pill{background:#131d2a;border:1px solid #273344;padding:8px 11px;border-radius:7px;font-size:11px;color:#bcc5d3}.metric-grid{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:14px}.metric-card,.panel{background:linear-gradient(145deg,#141e2de8,#0e1622d1);border:1px solid var(--surface-border);border-radius:12px;box-shadow:0 12px 34px #00000024,inset 0 1px #ffffff06;-webkit-backdrop-filter:blur(12px);backdrop-filter:blur(12px);transition:border-color .2s,transform .2s,box-shadow .2s}.metric-card:hover,.panel:hover{border-color:#90a2ff3d;box-shadow:0 16px 42px #0003,inset 0 1px #ffffff09}.metric-card{padding:17px;min-height:143px;position:relative;overflow:hidden}.metric-card:after{content:"";position:absolute;right:-26px;top:-48px;width:100px;height:100px;border-radius:50%;background:#7d8cff0b;filter:blur(2px);pointer-events:none}.metric-icon{width:30px;height:30px;border-radius:7px;display:grid;place-items:center;margin-bottom:12px}.metric-icon svg{width:16px}.blue{color:#8e9cff;background:#222b47}.violet{color:#c097ff;background:#302442}.green{color:#55d7a1;background:#18372f}.amber{color:#f4bd66;background:#393021}.metric-title{font-size:11px;color:#9ba7b9}.metric-value{font-family:Space Grotesk,sans-serif;font-size:24px;letter-spacing:-.8px;margin-top:3px}.metric-note{font-size:10px;color:#738196;margin-top:4px}.content-grid{display:grid;grid-template-columns:minmax(0,1.65fr) minmax(265px,1fr);gap:14px;margin-top:14px}.panel{padding:17px 18px}.panel-head{display:flex;align-items:center;justify-content:space-between}.panel-head h2{font-size:13px;margin:0 0 4px}.panel-head p{font-size:10px}.live-tag{font-size:9px;color:#53d8a3;letter-spacing:.8px}.chart{height:203px;margin:16px -8px -2px -10px}.details-panel .panel-head{margin-bottom:14px}.detail-row{display:flex;justify-content:space-between;gap:10px;border-top:1px solid #222d3a;padding:10px 0;font-size:10px;color:#8e9aab}.detail-row strong{font-weight:500;color:#d2d9e4;text-align:right;overflow-wrap:anywhere}.table-panel{margin-top:14px;padding-bottom:5px}.table-panel>.panel-head{margin-bottom:11px}.text-link{color:#9ba7ff;text-decoration:none;font-size:11px}.table-scroll{width:100%;overflow-x:auto}table{border-collapse:collapse;width:100%;text-align:left;font-size:10px;white-space:nowrap}th{font-size:9px;letter-spacing:.65px;color:#748197;font-weight:600;padding:9px 10px;border-bottom:1px solid #25303e}td{padding:10px;border-bottom:1px solid #202a38;color:#c6cfdd}tbody tr:last-child td{border-bottom:0}.process-name{font-weight:600;color:#e0e6f0}.process-name small{display:block;color:#758297;font-size:9px;font-weight:400;margin-top:3px;max-width:330px;overflow:hidden;text-overflow:ellipsis}.status{background:#173528;color:#67d9a4;padding:3px 7px;border-radius:8px;font-size:9px}.status-stopped{background:#3a3025;color:#e6b56f}.empty{text-align:center;padding:22px;color:#8290a4;font-size:12px}footer{color:#7d899b;font-size:10px;padding:22px 2px}footer span{margin-left:9px;color:#626f82}.alert{margin-top:15px;padding:10px 13px;background:#382827;border:1px solid #66403d;color:#ffc0b9;border-radius:7px;font-size:12px}.route-page{padding-bottom:8px}.process-full{width:100%;margin-top:0}.update-actions{display:flex;align-items:center;gap:12px;flex-wrap:wrap;margin-top:12px}.update-result{margin:12px 0 0;color:#aab6c8;font-size:11px}.update-command{display:flex;align-items:center;gap:12px;flex-wrap:wrap;border-top:1px solid var(--surface-border);padding-top:12px;margin-top:12px;color:var(--text-muted);font-size:10px}.update-command code{color:var(--text-main);background:var(--field-bg);border:1px solid var(--surface-border);border-radius:6px;padding:8px 10px;font:11px ui-monospace,SFMono-Regular,Consolas,monospace;overflow-wrap:anywhere}.info-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:14px}.info-card{min-width:0}.info-card>.panel-head{margin-bottom:13px}.info-card>.panel-head>svg{color:#9aa7ff}.info-card .detail-row{font-size:11px}.volume-card,.network-card{border-top:1px solid #222d3a;padding:12px 0}.volume-title{display:flex;justify-content:space-between;gap:10px;font-size:11px}.volume-title span,.volume-caption{font-size:9px;color:#8190a3}.volume-bar{height:6px;background:#222d3a;border-radius:4px;margin:10px 0 6px;overflow:hidden}.volume-bar span{display:block;height:100%;background:linear-gradient(90deg,#6879ee,#9c8aff);border-radius:4px}.network-card>strong{display:block;color:#d6ddea;font-size:11px;margin-bottom:5px}.unsupported{padding:14px 0;color:#909bae;font-size:11px;line-height:1.6}.integration-state{border-radius:20px;padding:7px 10px;font-size:10px;border:1px solid #273344;white-space:nowrap}.state-online{color:#67d9a4;background:#153128}.state-offline{color:#e7b270;background:#342b22}.integration-message{min-height:205px;display:flex;flex-direction:column;align-items:center;justify-content:center;text-align:center;margin-top:12px;padding:35px}.integration-message .metric-icon{margin-bottom:15px}.integration-message h2{font-size:15px;margin:0 0 7px}.integration-message p{max-width:490px;color:#8c99ab;font-size:11px;line-height:1.7;margin:0}.integration-metrics{grid-template-columns:repeat(2,minmax(0,1fr));margin-bottom:14px}.integration-table{margin-top:0}.integration-table>.panel-head{margin-bottom:11px}.container-link{padding:0;border:0;background:none;color:#a7b2ff;font:inherit;font-weight:600;cursor:pointer}.container-link:hover{text-decoration:underline}.container-actions{display:flex;gap:5px}.console-button{border:1px solid #334158;border-radius:5px;padding:5px 8px;background:#1a2636;color:#d7dfed;font:inherit;font-size:9px;cursor:pointer;white-space:nowrap}.console-button:hover:not(:disabled){background:#26364b;border-color:#54698a}.console-button:disabled{opacity:.42;cursor:not-allowed}.secondary-button{padding:7px 10px;font-size:10px}.docker-console{border-top:1px solid #25303e;margin-top:17px;padding-top:15px}.console-header{display:flex;align-items:center;justify-content:space-between;gap:12px}.console-header h3{font-size:12px;margin:0 0 4px}.console-header p{font-size:10px;color:#8794a7;margin:0}.log-output{height:330px;overflow:auto;background:#05090fd4;border:1px solid #202b39;border-radius:7px;padding:13px;color:#c4d2df;font:11px/1.55 ui-monospace,SFMono-Regular,Consolas,monospace;white-space:pre-wrap;overflow-wrap:anywhere;margin:12px 0 0}.console-placeholder{min-height:100px;display:grid;place-items:center;color:#77869a;font-size:11px}.console-alert{margin-top:10px}.discord-alert-panel{margin-top:14px;padding:18px;background:linear-gradient(145deg,#181e30e0,#0e1622d6);border:1px solid rgba(161,139,255,.17);border-radius:12px;box-shadow:0 12px 34px #00000024,inset 0 1px #ffffff06}.discord-alert-heading{display:flex;align-items:center;gap:12px}.discord-alert-heading .metric-icon{flex:none;margin:0;color:#c2a6ff;background:#302744}.discord-alert-heading h3{font-size:13px;margin:0 0 4px}.discord-alert-heading p{color:#8e9bad;font-size:10px;margin:0}.toggle-control{display:flex;align-items:center;gap:8px;margin-left:auto;cursor:pointer}.toggle-control input{position:absolute;opacity:0;width:1px;height:1px}.toggle-track{width:31px;height:18px;border-radius:20px;background:#303b4c;position:relative;transition:background .18s}.toggle-track:after{content:"";position:absolute;width:12px;height:12px;top:3px;left:3px;border-radius:50%;background:#a2afc1;transition:transform .18s,background .18s}.toggle-control input:checked+.toggle-track{background:#365c50}.toggle-control input:checked+.toggle-track:after{transform:translate(13px);background:#75e0ad}.toggle-control input:focus-visible+.toggle-track{outline:2px solid #a6a5ff;outline-offset:2px}.toggle-label{color:#a9b4c4;font-size:10px;min-width:44px}.webhook-field{margin-top:18px}.webhook-field>label,.alert-options-title{display:block;color:#cbd4e1;font-size:10px;font-weight:600;margin-bottom:7px}.webhook-input-row{display:flex;gap:8px}.webhook-input-row input{width:100%;min-width:0;background:#070c14b3;border:1px solid #2c394b;border-radius:6px;padding:9px 11px;color:#dbe3ef;font:11px DM Sans,sans-serif;outline:none}.webhook-input-row input:focus{border-color:#8c83df;box-shadow:0 0 0 3px #8c83df1f}.webhook-input-row input::placeholder{color:#6f7d91}.webhook-field small{display:block;color:#77869a;font-size:9px;margin-top:6px}.alert-options{display:flex;flex-wrap:wrap;gap:8px 18px;margin-top:17px}.alert-option{display:flex;align-items:center;gap:7px;color:#aeb9c9;font-size:10px;cursor:pointer}.alert-option input{position:absolute;opacity:0;width:1px;height:1px}.check-ui{width:14px;height:14px;border:1px solid #445269;border-radius:4px;background:#121b28;position:relative}.alert-option input:checked+.check-ui{background:#756dcc;border-color:#9289ed}.alert-option input:checked+.check-ui:after{content:"";position:absolute;left:4px;top:1px;width:4px;height:7px;border:solid white;border-width:0 1.5px 1.5px 0;transform:rotate(45deg)}.alert-option input:focus-visible+.check-ui{outline:2px solid #a6a5ff;outline-offset:2px}.alert-form-footer{display:flex;align-items:center;justify-content:space-between;gap:12px;border-top:1px solid rgba(134,157,194,.1);padding-top:14px;margin-top:17px}.throttle-field{display:flex;align-items:center;gap:9px;color:#8e9bad;font-size:10px}.throttle-field select{background:#111a27;color:#d4deeb;border:1px solid #2c394b;border-radius:5px;padding:6px 8px;font-size:10px}.save-alert-button{background:linear-gradient(120deg,#5c65bf,#7064bd);border-color:#8479d4;padding:7px 11px}.save-alert-button:hover:not(:disabled){background:linear-gradient(120deg,#6b75d3,#8275d4)}.alert-success{margin-top:11px;color:#76ddb0;background:#235f452e;border:1px solid rgba(80,180,130,.17);border-radius:6px;padding:8px 10px;font-size:10px}.dashboard-skeleton{display:flex;flex-direction:column;gap:14px}.skeleton-metric-grid{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:14px}.skeleton-metric-grid.two-columns{grid-template-columns:repeat(2,minmax(0,1fr));margin-bottom:14px}.skeleton-content-grid{display:grid;grid-template-columns:minmax(0,1.65fr) minmax(265px,1fr);gap:14px}.skeleton-card{background:linear-gradient(145deg,#141e2dc7,#0e1622b3);border:1px solid var(--surface-border);border-radius:12px;padding:17px 18px;overflow:hidden}.metric-skeleton{min-height:143px}.skeleton{position:relative;overflow:hidden;background:#8999b41c;border-radius:6px}.skeleton:after{content:"";position:absolute;top:0;right:0;bottom:0;left:0;transform:translate(-100%);background:linear-gradient(90deg,transparent,rgba(200,214,241,.1),transparent);animation:skeleton-shimmer 1.7s ease-in-out infinite}.skeleton-icon{width:30px;height:30px;border-radius:8px;margin-bottom:13px}.skeleton-line{height:9px;margin:12px 0}.skeleton-line.short{width:38%}.skeleton-line.medium{width:62%}.skeleton-line.full{width:100%;height:26px;margin:0;border-radius:0;background:transparent;border-bottom:1px solid rgba(134,157,194,.08)}.skeleton-line.full:after{top:8px;right:0;bottom:8px;left:0;background:linear-gradient(90deg,transparent,rgba(200,214,241,.08),transparent)}.skeleton-line.value-line{height:25px;width:54%;margin:8px 0}.chart-skeleton{min-height:255px}.skeleton-chart{height:185px;margin-top:20px;background:linear-gradient(180deg,#8290ff14,#8290ff04);border-bottom:1px solid rgba(134,157,194,.11);border-radius:4px}.detail-skeleton{min-height:255px}.table-skeleton{min-height:160px}.skeleton-row{height:29px;margin-top:12px;border-radius:4px;background:linear-gradient(90deg,rgba(137,153,180,.05) 0 28%,transparent 28% 37%,rgba(137,153,180,.05) 37% 56%,transparent 56% 65%,rgba(137,153,180,.05) 65% 82%,transparent 82%)}.process-skeleton{padding:9px 16px}.process-skeleton .skeleton-row{display:flex;align-items:center;justify-content:space-between;height:46px;margin:0;border-radius:0;border-bottom:1px solid rgba(134,157,194,.08);background:none}.process-skeleton .skeleton-line{height:8px;margin:0}.process-skeleton .skeleton-line:first-child{width:36%}.process-skeleton .skeleton-line:nth-child(2){width:12%}.process-skeleton .skeleton-line:nth-child(3){width:20%}.info-skeleton{min-height:220px}.info-skeleton .skeleton-line.full{height:30px}@keyframes skeleton-shimmer{to{transform:translate(100%)}}@media(prefers-reduced-motion:reduce){*,*:before,*:after{scroll-behavior:auto!important;animation-duration:.01ms!important;animation-iteration-count:1!important;transition-duration:.01ms!important}}@media(max-width:1000px){.skeleton-metric-grid{grid-template-columns:repeat(2,minmax(0,1fr))}.skeleton-content-grid{grid-template-columns:1fr}.sidebar{width:190px}.main{margin-left:190px;padding-left:22px;padding-right:22px}.metric-grid{grid-template-columns:repeat(2,minmax(0,1fr))}.content-grid{grid-template-columns:1fr}}@media(max-width:650px){.sidebar{width:58px;padding:19px 8px;overflow-y:auto}.brand{padding:0 5px 34px}.brand>span,.nav-label,.nav-item span:not(.nav-badge){font-size:0}.nav-item{justify-content:center;padding:11px 0;gap:0}.nav-item svg{width:18px;height:18px}.nav-badge{position:absolute;margin-left:32px;margin-top:-17px;min-width:15px;height:15px;font-size:8px;padding:0 3px}.sidebar-bottom{font-size:0;padding:15px 5px}.sidebar-bottom small{display:none}.main{margin-left:58px;padding:0 14px 20px}.topbar{font-size:10px}.page-heading{align-items:flex-start;gap:10px}.page-heading h1{font-size:20px}.host-pill,.integration-state{font-size:9px}.metric-grid{gap:9px}.metric-card{padding:13px;min-height:134px}.metric-value{font-size:20px}.content-grid{gap:9px}.panel{padding:14px}.chart{height:180px}.process-name small{max-width:150px}.info-grid,.skeleton-content-grid{grid-template-columns:1fr}.skeleton-metric-grid{gap:9px}.skeleton-card{padding:14px}.volume-title{flex-wrap:wrap}.discord-alert-panel{padding:14px}.discord-alert-heading{flex-wrap:wrap}.toggle-control{width:100%;margin-left:42px}.alert-form-footer{align-items:flex-start;flex-direction:column}.save-alert-button{align-self:stretch}.webhook-input-row{flex-direction:column}.webhook-input-row button{align-self:flex-start}.integration-metrics{grid-template-columns:repeat(2,minmax(0,1fr))}}.auth-screen{min-height:100vh;display:grid;place-items:center;padding:24px;background:radial-gradient(ellipse at 50% 25%,rgba(93,107,193,.2),transparent 45%)}.auth-card{width:min(100%,430px);padding:30px}.auth-card h1{font:600 23px Space Grotesk,sans-serif;margin:18px 0 8px}.auth-card>p,.muted{color:#8e9bad;font-size:11px;line-height:1.6;margin:0 0 18px}.auth-card small{display:block;color:#78869a;font-size:10px;margin:6px 0 12px}.form-label{display:block;color:#cbd4e1;font-size:11px;font-weight:600;margin:13px 0 7px}.form-input{width:100%;min-width:0;background:#070c14b3;border:1px solid #2c394b;border-radius:6px;padding:9px 11px;color:#dbe3ef;font:11px DM Sans,sans-serif;outline:none}.form-input:focus{border-color:#8c83df;box-shadow:0 0 0 3px #8c83df1f}.auth-submit{display:flex;align-items:center;justify-content:center;gap:8px;width:100%;margin-top:18px;padding:10px;background:linear-gradient(120deg,#5c65bf,#7064bd);border-color:#8479d4;font-size:12px}.topbar-actions{display:flex;align-items:center;gap:12px}.user-chip{color:#b9c3d2;font-size:10px}.nav-label.integration-label{margin-top:14px;padding-top:13px;border-top:1px solid rgba(134,157,194,.08)}.config-panel{margin-top:14px}.config-panel h2,.history-chart h2{font-size:13px;margin:0 0 6px}.form-row{display:flex;align-items:center;gap:8px;margin:12px 0;flex-wrap:wrap}.form-row .form-input{flex:1 1 180px}.small-input{max-width:110px!important}.save-button{margin-right:8px}.security-summary{display:flex;align-items:center;gap:12px;padding:4px 0 18px;margin-bottom:18px;border-bottom:1px solid #25303e}.security-summary>svg{color:#9ba7ff;flex:none}.security-summary strong{font-size:12px}.security-summary p{margin:4px 0 0}.config-panel h2:not(:first-child){margin-top:20px}.chip-list{display:flex;flex-wrap:wrap;gap:6px;margin:10px 0}.target-chip{border:1px solid #354258;border-radius:16px;background:#172234;color:#cbd4e1;padding:5px 9px;font-size:10px;cursor:pointer}.history-chart{margin-top:14px}.history-chart .chart{height:300px;margin-top:14px}.threshold-grid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:14px}.threshold-input{display:flex;align-items:center;gap:8px}.threshold-input span{font-size:11px;color:#8e9bad}.log-search{max-width:560px}.auth-card .alert{margin-top:10px}@media(max-width:650px){.auth-card{padding:22px}.topbar-actions{gap:5px}.user-chip{display:none}.form-row{align-items:stretch}.form-row .form-input{flex-basis:100%}.threshold-grid{grid-template-columns:1fr}.history-chart .chart{height:230px}.nav-label.integration-label{font-size:0;padding:7px 0;margin-top:6px}.nav-label.integration-label:after{content:"·";font-size:16px;display:block;text-align:center}}:root[data-theme] body{color:var(--text-main);background:radial-gradient(ellipse at 72% -10%,color-mix(in srgb,var(--accent) 10%,transparent),transparent 38%),var(--page-bg)}:root[data-theme] .sidebar{background:var(--sidebar-bg)}:root[data-theme] .panel,:root[data-theme] .metric-card,:root[data-theme] .skeleton-card{background:var(--panel-bg);border-color:var(--surface-border)}:root[data-theme] .form-input,:root[data-theme] .webhook-input-row input{background:var(--field-bg);color:var(--text-main)}:root[data-theme] .theme-choice{background:var(--panel-bg);color:var(--text-main);border:1px solid var(--surface-border);border-radius:8px;padding:13px;text-align:left;cursor:pointer;transition:border-color .15s,transform .15s}:root[data-theme] .theme-choice:hover,:root[data-theme] .theme-choice.selected{border-color:var(--accent);transform:translateY(-1px)}:root[data-theme] .theme-choice strong,:root[data-theme] .theme-choice span{display:block}:root[data-theme] .theme-choice span{color:var(--text-muted);font-size:10px;line-height:1.5;margin-top:5px}.theme-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:9px;margin-top:12px}.permission-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:9px;margin-top:12px}.user-list{display:grid;gap:10px}.user-card{padding:13px;border:1px solid var(--surface-border);border-radius:8px}.user-card-heading{display:flex;justify-content:space-between;gap:8px;color:var(--text-main);font-size:11px}.user-card-heading span{color:var(--text-muted)}:root[data-theme=monochrome] .nav-item.active,:root[data-theme=monochrome] .nav-item:hover{background:#ffffff10;color:#fff}:root[data-theme=monochrome] .nav-item.active,:root[data-theme=monochrome] .nav-item.active svg{color:#fff}:root[data-theme=monochrome] .metric-icon.blue,:root[data-theme=monochrome] .metric-icon.violet,:root[data-theme=monochrome] .metric-icon.green,:root[data-theme=monochrome] .metric-icon.amber{background:#303234;color:#e5e7eb}@media(max-width:650px){.theme-grid,.permission-grid{grid-template-columns:1fr}}
|
package/frontend/dist/index.html
CHANGED
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
6
|
<meta name="theme-color" content="#0b1220" />
|
|
7
7
|
<title>Synfull</title>
|
|
8
|
-
<script type="module" crossorigin src="/assets/index-
|
|
9
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
8
|
+
<script type="module" crossorigin src="/assets/index-BdVFYwZZ.js"></script>
|
|
9
|
+
<link rel="stylesheet" crossorigin href="/assets/index-pBaLmQTy.css">
|
|
10
10
|
</head>
|
|
11
11
|
<body>
|
|
12
12
|
<div id="root"></div>
|
package/package.json
CHANGED
package/scripts/synfull.cjs
CHANGED
|
@@ -224,6 +224,92 @@ function stopServer() {
|
|
|
224
224
|
fs.rmSync(PID_FILE, { force: true })
|
|
225
225
|
console.log('Synfull stopped.')
|
|
226
226
|
}
|
|
227
|
+
function processExists(pid) {
|
|
228
|
+
try { process.kill(pid, 0); return true } catch (error) { return error.code === 'EPERM' }
|
|
229
|
+
}
|
|
230
|
+
function sleep(milliseconds) {
|
|
231
|
+
return new Promise(resolve => setTimeout(resolve, milliseconds))
|
|
232
|
+
}
|
|
233
|
+
async function waitForProcessExit(pid, timeoutMs = 15000) {
|
|
234
|
+
const deadline = Date.now() + timeoutMs
|
|
235
|
+
while (processExists(pid) && Date.now() < deadline) await sleep(250)
|
|
236
|
+
if (processExists(pid)) throw new Error(`Synfull process ${pid} did not stop; the runtime was not updated.`)
|
|
237
|
+
}
|
|
238
|
+
function windowsTaskStatus() {
|
|
239
|
+
const result = spawnSync('schtasks.exe', ['/Query', '/TN', 'Synfull', '/FO', 'LIST'], { encoding: 'utf8', windowsHide: true })
|
|
240
|
+
if (result.status !== 0) return null
|
|
241
|
+
const status = (result.stdout || '').match(/^Status:\s*(.+)$/im)
|
|
242
|
+
return status?.[1]?.trim() || 'Ready'
|
|
243
|
+
}
|
|
244
|
+
async function waitForWindowsTaskStopped(timeoutMs = 15000) {
|
|
245
|
+
const deadline = Date.now() + timeoutMs
|
|
246
|
+
while (/running/i.test(windowsTaskStatus() || '') && Date.now() < deadline) await sleep(500)
|
|
247
|
+
if (/running/i.test(windowsTaskStatus() || '')) throw new Error('Synfull scheduled task did not stop; the runtime was not updated.')
|
|
248
|
+
}
|
|
249
|
+
async function reloadServer(options = {}) {
|
|
250
|
+
const windows = options.windows ?? WINDOWS
|
|
251
|
+
const spawnCommand = options.spawnSync || spawnSync
|
|
252
|
+
const runCommand = options.run || run
|
|
253
|
+
const ensure = options.ensureRuntime || ensureRuntime
|
|
254
|
+
const processAlive = options.processExists || processExists
|
|
255
|
+
const stop = options.stopServer || stopServer
|
|
256
|
+
const start = options.startServer || (() => startServer(false))
|
|
257
|
+
const startScheduledTask = options.startScheduledTask || (() => runCommand('schtasks.exe', ['/Run', '/TN', 'Synfull']))
|
|
258
|
+
const pidFile = options.pidFile || PID_FILE
|
|
259
|
+
|
|
260
|
+
if (windows) {
|
|
261
|
+
const query = spawnCommand('schtasks.exe', ['/Query', '/TN', 'Synfull', '/FO', 'LIST'], { encoding: 'utf8', windowsHide: true })
|
|
262
|
+
const status = query.status === 0 ? ((query.stdout || '').match(/^Status:\s*(.+)$/im)?.[1]?.trim() || 'Ready') : null
|
|
263
|
+
if (status !== null) {
|
|
264
|
+
if (/running/i.test(status)) {
|
|
265
|
+
runCommand('schtasks.exe', ['/End', '/TN', 'Synfull'])
|
|
266
|
+
const waitForTask = options.waitForWindowsTaskStopped || waitForWindowsTaskStopped
|
|
267
|
+
await waitForTask()
|
|
268
|
+
}
|
|
269
|
+
ensure()
|
|
270
|
+
startScheduledTask()
|
|
271
|
+
console.log('Synfull runtime refreshed and scheduled task started.')
|
|
272
|
+
return
|
|
273
|
+
}
|
|
274
|
+
} else {
|
|
275
|
+
const service = spawnCommand('systemctl', ['cat', 'synfull-npm.service'], { stdio: 'ignore', windowsHide: true })
|
|
276
|
+
if (service.status === 0) {
|
|
277
|
+
const active = spawnCommand('systemctl', ['is-active', '--quiet', 'synfull-npm.service'], { stdio: 'ignore', windowsHide: true }).status === 0
|
|
278
|
+
if (active) runCommand('sudo', ['systemctl', 'stop', 'synfull-npm.service'])
|
|
279
|
+
try {
|
|
280
|
+
ensure()
|
|
281
|
+
} catch (error) {
|
|
282
|
+
if (active) {
|
|
283
|
+
try { runCommand('sudo', ['systemctl', 'start', 'synfull-npm.service']) }
|
|
284
|
+
catch (restartError) { error.message += ` The previous service could not be restored: ${restartError.message}` }
|
|
285
|
+
}
|
|
286
|
+
throw error
|
|
287
|
+
}
|
|
288
|
+
if (!active) {
|
|
289
|
+
console.log('Synfull runtime refreshed; the systemd service was already stopped and remains stopped.')
|
|
290
|
+
return
|
|
291
|
+
}
|
|
292
|
+
runCommand('sudo', ['systemctl', 'start', 'synfull-npm.service'])
|
|
293
|
+
const restarted = spawnCommand('systemctl', ['is-active', '--quiet', 'synfull-npm.service'], { stdio: 'ignore', windowsHide: true }).status === 0
|
|
294
|
+
if (!restarted) throw new Error('Synfull runtime was refreshed, but the systemd service did not become active. Check `systemctl status synfull-npm.service`.')
|
|
295
|
+
console.log('Synfull runtime refreshed and systemd service started.')
|
|
296
|
+
return
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
if (fs.existsSync(pidFile)) {
|
|
301
|
+
const pid = Number(fs.readFileSync(pidFile, 'utf8'))
|
|
302
|
+
if (Number.isInteger(pid) && processAlive(pid)) {
|
|
303
|
+
stop()
|
|
304
|
+
const waitForExit = options.waitForProcessExit || waitForProcessExit
|
|
305
|
+
await waitForExit(pid)
|
|
306
|
+
} else {
|
|
307
|
+
fs.rmSync(pidFile, { force: true })
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
ensure()
|
|
311
|
+
start()
|
|
312
|
+
}
|
|
227
313
|
function status() {
|
|
228
314
|
let running = false
|
|
229
315
|
let pid = ''
|
|
@@ -276,7 +362,7 @@ function uninstallStartup() {
|
|
|
276
362
|
console.log('Synfull startup registration removed; application data was retained.')
|
|
277
363
|
}
|
|
278
364
|
function help() {
|
|
279
|
-
console.log(`Synfull ${VERSION}\n\nUsage: synfull <command>\n\nCommands:\n setup Prepare local runtime and install Python dependencies\n start Start Synfull in the background\n run Run Synfull in the foreground (for service managers)\n stop Stop a background Synfull process\n status Show status and local URLs\n open Open the local dashboard\n install-service Register start-at-login (Windows) or start-at-boot (Linux)\n uninstall-service Remove startup registration without deleting data\n\nSetup asks whether to allow LAN connections; localhost-only is the default. Use a trusted LAN, open the port in your firewall only as needed, and change the initial admin password locally before remote sign-in.`)
|
|
365
|
+
console.log(`Synfull ${VERSION}\n\nUsage: synfull <command>\n\nCommands:\n setup Prepare local runtime and install Python dependencies\n start Start Synfull in the background\n reload Refresh installed files from this package and restart Synfull\n run Run Synfull in the foreground (for service managers)\n stop Stop a background Synfull process\n status Show status and local URLs\n open Open the local dashboard\n install-service Register start-at-login (Windows) or start-at-boot (Linux)\n uninstall-service Remove startup registration without deleting data\n\nSetup asks whether to allow LAN connections; localhost-only is the default. Use a trusted LAN, open the port in your firewall only as needed, and change the initial admin password locally before remote sign-in.`)
|
|
280
366
|
}
|
|
281
367
|
async function main() {
|
|
282
368
|
const command = process.argv[2]
|
|
@@ -290,6 +376,7 @@ async function main() {
|
|
|
290
376
|
console.log(`Runtime ready. Data directory: ${STATE_DIR}`)
|
|
291
377
|
}
|
|
292
378
|
else if (command === 'start') startServer(false)
|
|
379
|
+
else if (command === 'reload') await reloadServer()
|
|
293
380
|
else if (command === 'run') startServer(true)
|
|
294
381
|
else if (command === 'stop') stopServer()
|
|
295
382
|
else if (command === 'status') status()
|
|
@@ -304,4 +391,4 @@ async function main() {
|
|
|
304
391
|
else help()
|
|
305
392
|
}
|
|
306
393
|
if (require.main === module) main().catch(error => { console.error(`Synfull: ${error.message}`); process.exitCode = 1 })
|
|
307
|
-
module.exports = { ensureRuntime, serverEnvironment, configuredBindHost, selectedBindHost, saveBindHost, configureDockerAccess, restartSynfullService, APP_DIR, STATE_DIR, ROOT }
|
|
394
|
+
module.exports = { ensureRuntime, serverEnvironment, configuredBindHost, selectedBindHost, saveBindHost, configureDockerAccess, restartSynfullService, reloadServer, APP_DIR, STATE_DIR, ROOT }
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
@import"https://fonts.googleapis.com/css2?family=DM+Sans:wght@400;500;600;700&family=Space+Grotesk:wght@500;600;700&display=swap";@tailwind base;@tailwind components;@tailwind utilities;:root[data-theme]{color:var(--text-main)}:root[data-theme=synfull]{color:#e7ebf3}body{color:var(--text-main);background:radial-gradient(ellipse at 72% -10%,color-mix(in srgb,var(--accent) 10%,transparent),transparent 38%),var(--page-bg)}.sidebar{background:var(--sidebar-bg)}.panel,.metric-card,.skeleton-card{background:var(--panel-bg);border-color:var(--surface-border)}.form-input,.webhook-input-row input{background:var(--field-bg);color:var(--text-main)}:root[data-theme=hacker] body{font-family:ui-monospace,SFMono-Regular,Consolas,monospace}:root[data-theme=hacker] .brand-icon,:root[data-theme=hacker] .metric-icon{background:#062712;color:#40ff8a;box-shadow:0 0 15px #17d45c28}:root[data-theme=hacker] .nav-item.active,:root[data-theme=hacker] .nav-item:hover{background:#0b2615;color:#c8ffd8}:root[data-theme=hacker] .metric-card,:root[data-theme=hacker] .panel{box-shadow:0 0 20px #10a64712,inset 0 1px #7aff9e0c}:root[data-theme=light] .nav-item,:root[data-theme=light] td,:root[data-theme=light] .detail-row,:root[data-theme=light] .metric-title,:root[data-theme=light] .topbar{color:#40506a}:root[data-theme=light] th{color:#586880}:root[data-theme=light] .host-pill,:root[data-theme=light] .console-button,:root[data-theme=light] .throttle-field select{background:#edf1f7;color:#27364e;border-color:#cbd4e1}:root[data-theme=light] .status{background:#d8f4e6;color:#176742}:root[data-theme=light] .metric-icon.blue{background:#e4eaff;color:#315fe8}:root[data-theme=light] .metric-icon.violet{background:#eee6ff;color:#7044b5}:root[data-theme=light] .metric-icon.green{background:#d8f4e6;color:#176742}:root[data-theme=light] .metric-icon.amber{background:#fff0d5;color:#98620a}:root{font-family:DM Sans,sans-serif;color:#e7ebf3;background:#0b111b;font-synthesis:none;text-rendering:optimizeLegibility;--surface:rgba(17,26,39,.82);--surface-border:rgba(134,157,194,.14);--accent:#8290ff;--page-bg:#0b111b;--sidebar-bg:rgba(12,19,30,.88);--panel-bg:linear-gradient(145deg,rgba(20,30,45,.91),rgba(14,22,34,.82));--text-main:#e7ebf3;--text-muted:#8490a2;--field-bg:rgba(7,12,20,.7);--border-color:rgba(134,157,194,.14)}:root[data-theme=monochrome]{--accent:#d1d5db;--page-bg:#111315;--sidebar-bg:rgba(15,17,19,.94);--panel-bg:linear-gradient(145deg,rgba(31,33,35,.96),rgba(22,24,26,.94));--surface-border:rgba(220,224,228,.12);--text-main:#f0f1f2;--text-muted:#9da1a6;--field-bg:rgba(10,11,12,.8)}:root[data-theme=hacker]{--accent:#40ff8a;--page-bg:#020805;--sidebar-bg:rgba(1,10,5,.97);--panel-bg:linear-gradient(145deg,rgba(5,21,12,.98),rgba(3,13,8,.98));--surface-border:rgba(53,255,121,.2);--text-main:#d8ffe5;--text-muted:#80ad8c;--field-bg:#020805}:root[data-theme=light]{--accent:#315fe8;--page-bg:#f1f4f8;--sidebar-bg:rgba(255,255,255,.96);--panel-bg:linear-gradient(145deg,#fff,#f7f9fc);--surface-border:rgba(49,65,89,.14);--text-main:#172033;--text-muted:#627087;--field-bg:#f8fafc;color:var(--text-main)}:root[data-theme=oled]{--accent:#97a6ff;--page-bg:#000;--sidebar-bg:rgba(0,0,0,.98);--panel-bg:linear-gradient(145deg,#08090b,#030405);--surface-border:rgba(154,169,199,.15);--text-main:#eff2f7;--text-muted:#919aaa;--field-bg:#000}*{box-sizing:border-box}body{margin:0;min-width:350px;min-height:100vh;background:radial-gradient(ellipse at 72% -10%,rgba(76,95,192,.17),transparent 38%),radial-gradient(ellipse at 18% 47%,rgba(32,115,119,.09),transparent 32%),#0b111b;background-attachment:fixed}.app-shell{min-height:100vh}.app-shell:before{content:"";position:fixed;top:0;right:0;bottom:0;left:0;pointer-events:none;z-index:-1;opacity:.13;background-image:radial-gradient(rgba(185,202,228,.4) .6px,transparent .6px);background-size:25px 25px;-webkit-mask-image:linear-gradient(to bottom,black,transparent 70%);mask-image:linear-gradient(to bottom,black,transparent 70%)}.sidebar{width:232px;position:fixed;inset:0 auto 0 0;background:#0c131ee0;-webkit-backdrop-filter:blur(18px);backdrop-filter:blur(18px);border-right:1px solid rgba(134,157,194,.13);padding:25px 16px;display:flex;flex-direction:column;z-index:2;box-shadow:12px 0 50px #0000001f}.brand{display:flex;align-items:center;gap:10px;font-family:Space Grotesk,sans-serif;font-size:21px;font-weight:700;letter-spacing:-.8px;padding:0 10px 42px}.brand-icon{height:31px;width:31px;border-radius:9px;background:linear-gradient(145deg,#8190ff,#5869dc);color:#fff;display:grid;place-items:center;box-shadow:0 5px 20px #6474e540,inset 0 1px #ffffff42}.nav-label,.eyebrow{font-size:10px;letter-spacing:1.2px;color:#728096;font-weight:700}.nav-label{padding:0 11px 10px}.integration-label{padding-top:25px}.nav-item{display:flex;align-items:center;gap:11px;padding:10px 11px;border-radius:7px;color:#8995a8;text-decoration:none;font-size:13px;margin:2px 0;transition:background .15s,color .15s}.nav-item.active,.nav-item:hover{background:linear-gradient(100deg,#6c7edd2e,#6c7edd12);color:#edf0fa}.nav-item.active{box-shadow:inset 2px 0 #91a0ff}.nav-item.active svg{color:#9ba6ff}.nav-badge{margin-left:auto;min-width:19px;height:19px;padding:0 5px;border-radius:12px;display:grid;place-items:center;background:#49342b;color:#f0b57c;font-size:10px;font-weight:700}.nav-badge.online{background:#173a31;color:#65d5a3}.sidebar-bottom{margin-top:auto;border-top:1px solid #202a38;padding:18px 8px 0;color:#a9b3c4;font-size:12px}.sidebar-bottom small{display:block;color:#78869a;margin:6px 0 0 17px}.live-dot{display:inline-block;width:7px;height:7px;background:#49ce9a;border-radius:50%;margin-right:8px;box-shadow:0 0 9px #49ce9a70}.main{margin-left:232px;min-height:100vh;padding:0 36px 22px;max-width:1700px;position:relative}.topbar{height:62px;border-bottom:1px solid rgba(134,157,194,.13);display:flex;align-items:center;justify-content:space-between;font-size:12px;color:#c9d0dd}.eyebrow{margin-right:7px}.connection{color:#a7b2c2;font-size:11px}.page-heading{display:flex;align-items:center;justify-content:space-between;margin:32px 0 23px}.page-heading h1{font-family:Space Grotesk,sans-serif;font-size:24px;letter-spacing:-.8px;margin:0 0 6px}.page-heading p,.panel-head p{margin:0;color:#8490a2;font-size:12px}.host-pill{background:#131d2a;border:1px solid #273344;padding:8px 11px;border-radius:7px;font-size:11px;color:#bcc5d3}.metric-grid{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:14px}.metric-card,.panel{background:linear-gradient(145deg,#141e2de8,#0e1622d1);border:1px solid var(--surface-border);border-radius:12px;box-shadow:0 12px 34px #00000024,inset 0 1px #ffffff06;-webkit-backdrop-filter:blur(12px);backdrop-filter:blur(12px);transition:border-color .2s,transform .2s,box-shadow .2s}.metric-card:hover,.panel:hover{border-color:#90a2ff3d;box-shadow:0 16px 42px #0003,inset 0 1px #ffffff09}.metric-card{padding:17px;min-height:143px;position:relative;overflow:hidden}.metric-card:after{content:"";position:absolute;right:-26px;top:-48px;width:100px;height:100px;border-radius:50%;background:#7d8cff0b;filter:blur(2px);pointer-events:none}.metric-icon{width:30px;height:30px;border-radius:7px;display:grid;place-items:center;margin-bottom:12px}.metric-icon svg{width:16px}.blue{color:#8e9cff;background:#222b47}.violet{color:#c097ff;background:#302442}.green{color:#55d7a1;background:#18372f}.amber{color:#f4bd66;background:#393021}.metric-title{font-size:11px;color:#9ba7b9}.metric-value{font-family:Space Grotesk,sans-serif;font-size:24px;letter-spacing:-.8px;margin-top:3px}.metric-note{font-size:10px;color:#738196;margin-top:4px}.content-grid{display:grid;grid-template-columns:minmax(0,1.65fr) minmax(265px,1fr);gap:14px;margin-top:14px}.panel{padding:17px 18px}.panel-head{display:flex;align-items:center;justify-content:space-between}.panel-head h2{font-size:13px;margin:0 0 4px}.panel-head p{font-size:10px}.live-tag{font-size:9px;color:#53d8a3;letter-spacing:.8px}.chart{height:203px;margin:16px -8px -2px -10px}.details-panel .panel-head{margin-bottom:14px}.detail-row{display:flex;justify-content:space-between;gap:10px;border-top:1px solid #222d3a;padding:10px 0;font-size:10px;color:#8e9aab}.detail-row strong{font-weight:500;color:#d2d9e4;text-align:right;overflow-wrap:anywhere}.table-panel{margin-top:14px;padding-bottom:5px}.table-panel>.panel-head{margin-bottom:11px}.text-link{color:#9ba7ff;text-decoration:none;font-size:11px}.table-scroll{width:100%;overflow-x:auto}table{border-collapse:collapse;width:100%;text-align:left;font-size:10px;white-space:nowrap}th{font-size:9px;letter-spacing:.65px;color:#748197;font-weight:600;padding:9px 10px;border-bottom:1px solid #25303e}td{padding:10px;border-bottom:1px solid #202a38;color:#c6cfdd}tbody tr:last-child td{border-bottom:0}.process-name{font-weight:600;color:#e0e6f0}.process-name small{display:block;color:#758297;font-size:9px;font-weight:400;margin-top:3px;max-width:330px;overflow:hidden;text-overflow:ellipsis}.status{background:#173528;color:#67d9a4;padding:3px 7px;border-radius:8px;font-size:9px}.status-stopped{background:#3a3025;color:#e6b56f}.empty{text-align:center;padding:22px;color:#8290a4;font-size:12px}footer{color:#7d899b;font-size:10px;padding:22px 2px}footer span{margin-left:9px;color:#626f82}.alert{margin-top:15px;padding:10px 13px;background:#382827;border:1px solid #66403d;color:#ffc0b9;border-radius:7px;font-size:12px}.route-page{padding-bottom:8px}.process-full{width:100%;margin-top:0}.process-full table{background:#111a27;border:1px solid #202c3c;border-radius:8px}.process-full th,.process-full td{padding:12px}.process-full table th:first-child,.process-full table td:first-child{padding-left:16px}.info-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:14px}.info-card{min-width:0}.info-card>.panel-head{margin-bottom:13px}.info-card>.panel-head>svg{color:#9aa7ff}.info-card .detail-row{font-size:11px}.volume-card,.network-card{border-top:1px solid #222d3a;padding:12px 0}.volume-title{display:flex;justify-content:space-between;gap:10px;font-size:11px}.volume-title span,.volume-caption{font-size:9px;color:#8190a3}.volume-bar{height:6px;background:#222d3a;border-radius:4px;margin:10px 0 6px;overflow:hidden}.volume-bar span{display:block;height:100%;background:linear-gradient(90deg,#6879ee,#9c8aff);border-radius:4px}.network-card>strong{display:block;color:#d6ddea;font-size:11px;margin-bottom:5px}.unsupported{padding:14px 0;color:#909bae;font-size:11px;line-height:1.6}.integration-state{border-radius:20px;padding:7px 10px;font-size:10px;border:1px solid #273344;white-space:nowrap}.state-online{color:#67d9a4;background:#153128}.state-offline{color:#e7b270;background:#342b22}.integration-message{min-height:205px;display:flex;flex-direction:column;align-items:center;justify-content:center;text-align:center;margin-top:12px;padding:35px}.integration-message .metric-icon{margin-bottom:15px}.integration-message h2{font-size:15px;margin:0 0 7px}.integration-message p{max-width:490px;color:#8c99ab;font-size:11px;line-height:1.7;margin:0}.integration-metrics{grid-template-columns:repeat(2,minmax(0,1fr));margin-bottom:14px}.integration-table{margin-top:0}.integration-table>.panel-head{margin-bottom:11px}.container-link{padding:0;border:0;background:none;color:#a7b2ff;font:inherit;font-weight:600;cursor:pointer}.container-link:hover{text-decoration:underline}.container-actions{display:flex;gap:5px}.console-button{border:1px solid #334158;border-radius:5px;padding:5px 8px;background:#1a2636;color:#d7dfed;font:inherit;font-size:9px;cursor:pointer;white-space:nowrap}.console-button:hover:not(:disabled){background:#26364b;border-color:#54698a}.console-button:disabled{opacity:.42;cursor:not-allowed}.secondary-button{padding:7px 10px;font-size:10px}.docker-console{border-top:1px solid #25303e;margin-top:17px;padding-top:15px}.console-header{display:flex;align-items:center;justify-content:space-between;gap:12px}.console-header h3{font-size:12px;margin:0 0 4px}.console-header p{font-size:10px;color:#8794a7;margin:0}.log-output{height:330px;overflow:auto;background:#05090fd4;border:1px solid #202b39;border-radius:7px;padding:13px;color:#c4d2df;font:11px/1.55 ui-monospace,SFMono-Regular,Consolas,monospace;white-space:pre-wrap;overflow-wrap:anywhere;margin:12px 0 0}.console-placeholder{min-height:100px;display:grid;place-items:center;color:#77869a;font-size:11px}.console-alert{margin-top:10px}.discord-alert-panel{margin-top:14px;padding:18px;background:linear-gradient(145deg,#181e30e0,#0e1622d6);border:1px solid rgba(161,139,255,.17);border-radius:12px;box-shadow:0 12px 34px #00000024,inset 0 1px #ffffff06}.discord-alert-heading{display:flex;align-items:center;gap:12px}.discord-alert-heading .metric-icon{flex:none;margin:0;color:#c2a6ff;background:#302744}.discord-alert-heading h3{font-size:13px;margin:0 0 4px}.discord-alert-heading p{color:#8e9bad;font-size:10px;margin:0}.toggle-control{display:flex;align-items:center;gap:8px;margin-left:auto;cursor:pointer}.toggle-control input{position:absolute;opacity:0;width:1px;height:1px}.toggle-track{width:31px;height:18px;border-radius:20px;background:#303b4c;position:relative;transition:background .18s}.toggle-track:after{content:"";position:absolute;width:12px;height:12px;top:3px;left:3px;border-radius:50%;background:#a2afc1;transition:transform .18s,background .18s}.toggle-control input:checked+.toggle-track{background:#365c50}.toggle-control input:checked+.toggle-track:after{transform:translate(13px);background:#75e0ad}.toggle-control input:focus-visible+.toggle-track{outline:2px solid #a6a5ff;outline-offset:2px}.toggle-label{color:#a9b4c4;font-size:10px;min-width:44px}.webhook-field{margin-top:18px}.webhook-field>label,.alert-options-title{display:block;color:#cbd4e1;font-size:10px;font-weight:600;margin-bottom:7px}.webhook-input-row{display:flex;gap:8px}.webhook-input-row input{width:100%;min-width:0;background:#070c14b3;border:1px solid #2c394b;border-radius:6px;padding:9px 11px;color:#dbe3ef;font:11px DM Sans,sans-serif;outline:none}.webhook-input-row input:focus{border-color:#8c83df;box-shadow:0 0 0 3px #8c83df1f}.webhook-input-row input::placeholder{color:#6f7d91}.webhook-field small{display:block;color:#77869a;font-size:9px;margin-top:6px}.alert-options{display:flex;flex-wrap:wrap;gap:8px 18px;margin-top:17px}.alert-option{display:flex;align-items:center;gap:7px;color:#aeb9c9;font-size:10px;cursor:pointer}.alert-option input{position:absolute;opacity:0;width:1px;height:1px}.check-ui{width:14px;height:14px;border:1px solid #445269;border-radius:4px;background:#121b28;position:relative}.alert-option input:checked+.check-ui{background:#756dcc;border-color:#9289ed}.alert-option input:checked+.check-ui:after{content:"";position:absolute;left:4px;top:1px;width:4px;height:7px;border:solid white;border-width:0 1.5px 1.5px 0;transform:rotate(45deg)}.alert-option input:focus-visible+.check-ui{outline:2px solid #a6a5ff;outline-offset:2px}.alert-form-footer{display:flex;align-items:center;justify-content:space-between;gap:12px;border-top:1px solid rgba(134,157,194,.1);padding-top:14px;margin-top:17px}.throttle-field{display:flex;align-items:center;gap:9px;color:#8e9bad;font-size:10px}.throttle-field select{background:#111a27;color:#d4deeb;border:1px solid #2c394b;border-radius:5px;padding:6px 8px;font-size:10px}.save-alert-button{background:linear-gradient(120deg,#5c65bf,#7064bd);border-color:#8479d4;padding:7px 11px}.save-alert-button:hover:not(:disabled){background:linear-gradient(120deg,#6b75d3,#8275d4)}.alert-success{margin-top:11px;color:#76ddb0;background:#235f452e;border:1px solid rgba(80,180,130,.17);border-radius:6px;padding:8px 10px;font-size:10px}.dashboard-skeleton{display:flex;flex-direction:column;gap:14px}.skeleton-metric-grid{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:14px}.skeleton-metric-grid.two-columns{grid-template-columns:repeat(2,minmax(0,1fr));margin-bottom:14px}.skeleton-content-grid{display:grid;grid-template-columns:minmax(0,1.65fr) minmax(265px,1fr);gap:14px}.skeleton-card{background:linear-gradient(145deg,#141e2dc7,#0e1622b3);border:1px solid var(--surface-border);border-radius:12px;padding:17px 18px;overflow:hidden}.metric-skeleton{min-height:143px}.skeleton{position:relative;overflow:hidden;background:#8999b41c;border-radius:6px}.skeleton:after{content:"";position:absolute;top:0;right:0;bottom:0;left:0;transform:translate(-100%);background:linear-gradient(90deg,transparent,rgba(200,214,241,.1),transparent);animation:skeleton-shimmer 1.7s ease-in-out infinite}.skeleton-icon{width:30px;height:30px;border-radius:8px;margin-bottom:13px}.skeleton-line{height:9px;margin:12px 0}.skeleton-line.short{width:38%}.skeleton-line.medium{width:62%}.skeleton-line.full{width:100%;height:26px;margin:0;border-radius:0;background:transparent;border-bottom:1px solid rgba(134,157,194,.08)}.skeleton-line.full:after{top:8px;right:0;bottom:8px;left:0;background:linear-gradient(90deg,transparent,rgba(200,214,241,.08),transparent)}.skeleton-line.value-line{height:25px;width:54%;margin:8px 0}.chart-skeleton{min-height:255px}.skeleton-chart{height:185px;margin-top:20px;background:linear-gradient(180deg,#8290ff14,#8290ff04);border-bottom:1px solid rgba(134,157,194,.11);border-radius:4px}.detail-skeleton{min-height:255px}.table-skeleton{min-height:160px}.skeleton-row{height:29px;margin-top:12px;border-radius:4px;background:linear-gradient(90deg,rgba(137,153,180,.05) 0 28%,transparent 28% 37%,rgba(137,153,180,.05) 37% 56%,transparent 56% 65%,rgba(137,153,180,.05) 65% 82%,transparent 82%)}.process-skeleton{padding:9px 16px}.process-skeleton .skeleton-row{display:flex;align-items:center;justify-content:space-between;height:46px;margin:0;border-radius:0;border-bottom:1px solid rgba(134,157,194,.08);background:none}.process-skeleton .skeleton-line{height:8px;margin:0}.process-skeleton .skeleton-line:first-child{width:36%}.process-skeleton .skeleton-line:nth-child(2){width:12%}.process-skeleton .skeleton-line:nth-child(3){width:20%}.info-skeleton{min-height:220px}.info-skeleton .skeleton-line.full{height:30px}@keyframes skeleton-shimmer{to{transform:translate(100%)}}@media(prefers-reduced-motion:reduce){*,*:before,*:after{scroll-behavior:auto!important;animation-duration:.01ms!important;animation-iteration-count:1!important;transition-duration:.01ms!important}}@media(max-width:1000px){.skeleton-metric-grid{grid-template-columns:repeat(2,minmax(0,1fr))}.skeleton-content-grid{grid-template-columns:1fr}.sidebar{width:190px}.main{margin-left:190px;padding-left:22px;padding-right:22px}.metric-grid{grid-template-columns:repeat(2,minmax(0,1fr))}.content-grid{grid-template-columns:1fr}}@media(max-width:650px){.sidebar{width:58px;padding:19px 8px;overflow-y:auto}.brand{padding:0 5px 34px}.brand>span,.nav-label,.nav-item span:not(.nav-badge){font-size:0}.nav-item{justify-content:center;padding:11px 0;gap:0}.nav-item svg{width:18px;height:18px}.nav-badge{position:absolute;margin-left:32px;margin-top:-17px;min-width:15px;height:15px;font-size:8px;padding:0 3px}.sidebar-bottom{font-size:0;padding:15px 5px}.sidebar-bottom small{display:none}.main{margin-left:58px;padding:0 14px 20px}.topbar{font-size:10px}.page-heading{align-items:flex-start;gap:10px}.page-heading h1{font-size:20px}.host-pill,.integration-state{font-size:9px}.metric-grid{gap:9px}.metric-card{padding:13px;min-height:134px}.metric-value{font-size:20px}.content-grid{gap:9px}.panel{padding:14px}.chart{height:180px}.process-name small{max-width:150px}.info-grid,.skeleton-content-grid{grid-template-columns:1fr}.skeleton-metric-grid{gap:9px}.skeleton-card{padding:14px}.volume-title{flex-wrap:wrap}.discord-alert-panel{padding:14px}.discord-alert-heading{flex-wrap:wrap}.toggle-control{width:100%;margin-left:42px}.alert-form-footer{align-items:flex-start;flex-direction:column}.save-alert-button{align-self:stretch}.webhook-input-row{flex-direction:column}.webhook-input-row button{align-self:flex-start}.integration-metrics{grid-template-columns:repeat(2,minmax(0,1fr))}}.auth-screen{min-height:100vh;display:grid;place-items:center;padding:24px;background:radial-gradient(ellipse at 50% 25%,rgba(93,107,193,.2),transparent 45%)}.auth-card{width:min(100%,430px);padding:30px}.auth-card h1{font:600 23px Space Grotesk,sans-serif;margin:18px 0 8px}.auth-card>p,.muted{color:#8e9bad;font-size:11px;line-height:1.6;margin:0 0 18px}.auth-card small{display:block;color:#78869a;font-size:10px;margin:6px 0 12px}.form-label{display:block;color:#cbd4e1;font-size:11px;font-weight:600;margin:13px 0 7px}.form-input{width:100%;min-width:0;background:#070c14b3;border:1px solid #2c394b;border-radius:6px;padding:9px 11px;color:#dbe3ef;font:11px DM Sans,sans-serif;outline:none}.form-input:focus{border-color:#8c83df;box-shadow:0 0 0 3px #8c83df1f}.auth-submit{display:flex;align-items:center;justify-content:center;gap:8px;width:100%;margin-top:18px;padding:10px;background:linear-gradient(120deg,#5c65bf,#7064bd);border-color:#8479d4;font-size:12px}.topbar-actions{display:flex;align-items:center;gap:12px}.user-chip{color:#b9c3d2;font-size:10px}.nav-label.integration-label{margin-top:14px;padding-top:13px;border-top:1px solid rgba(134,157,194,.08)}.config-panel{margin-top:14px}.config-panel h2,.history-chart h2{font-size:13px;margin:0 0 6px}.form-row{display:flex;align-items:center;gap:8px;margin:12px 0;flex-wrap:wrap}.form-row .form-input{flex:1 1 180px}.small-input{max-width:110px!important}.save-button{margin-right:8px}.security-summary{display:flex;align-items:center;gap:12px;padding:4px 0 18px;margin-bottom:18px;border-bottom:1px solid #25303e}.security-summary>svg{color:#9ba7ff;flex:none}.security-summary strong{font-size:12px}.security-summary p{margin:4px 0 0}.config-panel h2:not(:first-child){margin-top:20px}.chip-list{display:flex;flex-wrap:wrap;gap:6px;margin:10px 0}.target-chip{border:1px solid #354258;border-radius:16px;background:#172234;color:#cbd4e1;padding:5px 9px;font-size:10px;cursor:pointer}.history-chart{margin-top:14px}.history-chart .chart{height:300px;margin-top:14px}.threshold-grid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:14px}.threshold-input{display:flex;align-items:center;gap:8px}.threshold-input span{font-size:11px;color:#8e9bad}.log-search{max-width:560px}.auth-card .alert{margin-top:10px}@media(max-width:650px){.auth-card{padding:22px}.topbar-actions{gap:5px}.user-chip{display:none}.form-row{align-items:stretch}.form-row .form-input{flex-basis:100%}.threshold-grid{grid-template-columns:1fr}.history-chart .chart{height:230px}.nav-label.integration-label{font-size:0;padding:7px 0;margin-top:6px}.nav-label.integration-label:after{content:"·";font-size:16px;display:block;text-align:center}}:root[data-theme] body{color:var(--text-main);background:radial-gradient(ellipse at 72% -10%,color-mix(in srgb,var(--accent) 10%,transparent),transparent 38%),var(--page-bg)}:root[data-theme] .sidebar{background:var(--sidebar-bg)}:root[data-theme] .panel,:root[data-theme] .metric-card,:root[data-theme] .skeleton-card{background:var(--panel-bg);border-color:var(--surface-border)}:root[data-theme] .form-input,:root[data-theme] .webhook-input-row input{background:var(--field-bg);color:var(--text-main)}:root[data-theme] .theme-choice{background:var(--panel-bg);color:var(--text-main);border:1px solid var(--surface-border);border-radius:8px;padding:13px;text-align:left;cursor:pointer;transition:border-color .15s,transform .15s}:root[data-theme] .theme-choice:hover,:root[data-theme] .theme-choice.selected{border-color:var(--accent);transform:translateY(-1px)}:root[data-theme] .theme-choice strong,:root[data-theme] .theme-choice span{display:block}:root[data-theme] .theme-choice span{color:var(--text-muted);font-size:10px;line-height:1.5;margin-top:5px}.theme-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:9px;margin-top:12px}.permission-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:9px;margin-top:12px}.user-list{display:grid;gap:10px}.user-card{padding:13px;border:1px solid var(--surface-border);border-radius:8px}.user-card-heading{display:flex;justify-content:space-between;gap:8px;color:var(--text-main);font-size:11px}.user-card-heading span{color:var(--text-muted)}:root[data-theme=monochrome] .nav-item.active,:root[data-theme=monochrome] .nav-item:hover{background:#ffffff10;color:#fff}:root[data-theme=monochrome] .nav-item.active,:root[data-theme=monochrome] .nav-item.active svg{color:#fff}:root[data-theme=monochrome] .metric-icon.blue,:root[data-theme=monochrome] .metric-icon.violet,:root[data-theme=monochrome] .metric-icon.green,:root[data-theme=monochrome] .metric-icon.amber{background:#303234;color:#e5e7eb}@media(max-width:650px){.theme-grid,.permission-grid{grid-template-columns:1fr}}
|