mirror of
https://github.com/ReCloudStudio/WebHooker.git
synced 2026-09-22 16:11:29 +00:00
Add a field filter type reading any payload value by JSONPath with array expansion, 12 comparison operators (eq/ne/contains/startsWith/endsWith/regex/gt/gte/lt/lte/in/exists), a visual AST builder (all/any/not) in the route editor, chip-based multi-value input, a stateless POST /admin/api/test-match dry-run, and named filter fragments stored in D1 (d1_fragments, migration 0010) inlined into route ASTs on insert.
1225 lines
26 KiB
CSS
1225 lines
26 KiB
CSS
/* ---------------------------------------------------------------------------
|
|
* Theme tokens — RGB triplets so Tailwind opacity modifiers (bg-accent/10)
|
|
* work against them; values switch automatically with prefers-color-scheme
|
|
* (unless an explicit data-theme="light" is set on <html>).
|
|
* ------------------------------------------------------------------------- */
|
|
:root {
|
|
color-scheme: light;
|
|
--font-ui: "Plus Jakarta Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
|
--font-mono: "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
|
|
--radius: 12px;
|
|
--radius-sm: 8px;
|
|
--header-bg: rgba(255, 255, 255, 0.82);
|
|
--scrim: rgba(15, 23, 42, 0.4);
|
|
--knob: #ffffff;
|
|
--dot: rgba(15, 23, 42, 0.05);
|
|
--shadow: 0 1px 2px rgba(15, 23, 42, 0.04), 0 8px 24px rgba(15, 23, 42, 0.06);
|
|
|
|
--bg: 246 247 249;
|
|
--surface: 255 255 255;
|
|
--surface-2: 243 244 246;
|
|
--surface-3: 236 238 242;
|
|
--border: 230 232 236;
|
|
--border-strong: 212 216 222;
|
|
--text: 15 23 42;
|
|
--muted: 91 100 114;
|
|
--faint: 154 163 178;
|
|
--accent: 79 70 229;
|
|
--accent-strong: 67 56 202;
|
|
--accent-dim: 238 242 255;
|
|
--accent-text: 55 48 163;
|
|
--accent-border: 224 231 255;
|
|
--ok: 22 163 74;
|
|
--ok-dim: 236 253 243;
|
|
--warn: 180 83 9;
|
|
--warn-dim: 254 243 199;
|
|
--bad: 220 38 38;
|
|
--bad-dim: 254 242 242;
|
|
--info: 37 99 235;
|
|
--info-dim: 239 246 255;
|
|
--body-text: 48 54 61;
|
|
--code-bg: 240 241 243;
|
|
}
|
|
|
|
@media (prefers-color-scheme: dark) {
|
|
:root:not([data-theme="light"]) {
|
|
color-scheme: dark;
|
|
--header-bg: rgba(15, 20, 28, 0.82);
|
|
--scrim: rgba(2, 6, 12, 0.6);
|
|
--knob: #dfe4ee;
|
|
--dot: rgba(148, 163, 184, 0.08);
|
|
--shadow: 0 1px 2px rgba(0, 0, 0, 0.4), 0 12px 32px rgba(0, 0, 0, 0.5);
|
|
|
|
--bg: 11 15 23;
|
|
--surface: 19 26 36;
|
|
--surface-2: 26 34 48;
|
|
--surface-3: 35 45 61;
|
|
--border: 39 49 67;
|
|
--border-strong: 56 70 96;
|
|
--text: 230 233 239;
|
|
--muted: 154 166 184;
|
|
--faint: 107 119 137;
|
|
--accent: 99 102 241;
|
|
--accent-strong: 129 140 248;
|
|
--accent-dim: 28 33 64;
|
|
--accent-text: 199 210 254;
|
|
--accent-border: 47 55 102;
|
|
--ok: 34 197 94;
|
|
--ok-dim: 16 36 26;
|
|
--warn: 251 191 36;
|
|
--warn-dim: 42 36 16;
|
|
--bad: 240 82 82;
|
|
--bad-dim: 42 22 24;
|
|
--info: 59 130 246;
|
|
--info-dim: 23 37 84;
|
|
--body-text: 195 202 214;
|
|
--code-bg: 26 34 48;
|
|
}
|
|
}
|
|
|
|
@tailwind base;
|
|
@tailwind components;
|
|
@tailwind utilities;
|
|
|
|
@layer base {
|
|
html,
|
|
body {
|
|
height: 100%;
|
|
}
|
|
|
|
body {
|
|
@apply min-h-screen bg-bg text-text font-ui text-sm leading-[1.55] antialiased;
|
|
}
|
|
|
|
body::before {
|
|
content: "";
|
|
position: fixed;
|
|
inset: 0;
|
|
z-index: 0;
|
|
pointer-events: none;
|
|
background-image: radial-gradient(var(--dot) 1px, transparent 1px);
|
|
background-size: 22px 22px;
|
|
mask-image: linear-gradient(to bottom, rgba(0, 0, 0, 0.6), transparent 40%);
|
|
}
|
|
}
|
|
|
|
@layer components {
|
|
/* ---- Layout ---- */
|
|
.shell {
|
|
@apply relative z-[1] min-h-screen;
|
|
}
|
|
|
|
.header {
|
|
@apply sticky top-0 z-20 flex items-center justify-between gap-4 px-8 py-3.5 border-b border-border;
|
|
background: var(--header-bg);
|
|
backdrop-filter: blur(12px);
|
|
}
|
|
|
|
.brand {
|
|
@apply flex items-center gap-3;
|
|
}
|
|
|
|
.brand-mark {
|
|
@apply grid place-items-center h-[38px] w-[38px] rounded-[10px] bg-accent text-white font-extrabold text-[15px] tracking-wide;
|
|
}
|
|
|
|
.brand h1 {
|
|
@apply text-lg font-extrabold tracking-tight;
|
|
}
|
|
|
|
.tagline {
|
|
@apply mt-px block text-[10px] font-semibold uppercase tracking-[2.5px] text-faint;
|
|
}
|
|
|
|
.main {
|
|
@apply relative z-[1] mx-auto max-w-[1040px] px-8 pb-24 pt-8 max-sm:px-4 max-sm:pb-20 max-sm:pt-5;
|
|
}
|
|
|
|
/* ---- Buttons ---- */
|
|
.btn {
|
|
@apply inline-flex cursor-pointer items-center justify-center gap-2 rounded-sm border border-border-strong bg-surface px-4 py-2 font-ui text-[13px] font-semibold text-text no-underline transition-[border-color,background,color,transform] duration-150;
|
|
}
|
|
|
|
.btn:hover {
|
|
@apply border-accent text-accent;
|
|
}
|
|
|
|
.btn:active {
|
|
@apply translate-y-px;
|
|
}
|
|
|
|
.btn:disabled {
|
|
@apply cursor-not-allowed opacity-50;
|
|
}
|
|
|
|
.btn-accent {
|
|
@apply border-accent bg-accent text-white;
|
|
}
|
|
|
|
.btn-accent:hover {
|
|
@apply border-accent-strong bg-accent-strong text-white;
|
|
}
|
|
|
|
.btn-ghost {
|
|
@apply border-transparent bg-transparent;
|
|
}
|
|
|
|
.btn-ghost:hover {
|
|
@apply border-transparent bg-surface-2 text-text;
|
|
}
|
|
|
|
.btn-sm {
|
|
@apply px-3 py-1.5 text-xs;
|
|
}
|
|
|
|
.btn-lg {
|
|
@apply px-6 py-3 text-sm;
|
|
}
|
|
|
|
.icon-btn {
|
|
@apply grid h-[30px] w-[30px] place-items-center cursor-pointer rounded-sm border border-border bg-transparent text-[13px] text-muted transition-all duration-150;
|
|
}
|
|
|
|
.icon-btn:hover {
|
|
@apply border-accent bg-accent-dim text-accent;
|
|
}
|
|
|
|
.icon-btn:disabled {
|
|
@apply cursor-not-allowed opacity-35;
|
|
}
|
|
|
|
.icon-btn:disabled:hover {
|
|
@apply border-border bg-transparent text-muted;
|
|
}
|
|
|
|
.icon-btn.danger:hover {
|
|
@apply border-bad bg-bad-dim text-bad;
|
|
}
|
|
|
|
/* ---- Cards ---- */
|
|
.card {
|
|
@apply animate-rise rounded border border-border bg-surface p-5 transition-[border-color,transform] duration-150;
|
|
}
|
|
|
|
.card:hover {
|
|
@apply border-border-strong;
|
|
}
|
|
|
|
.card.disabled {
|
|
@apply opacity-55;
|
|
}
|
|
|
|
.card-head {
|
|
@apply flex items-start justify-between gap-3.5;
|
|
}
|
|
|
|
.card-title {
|
|
@apply flex flex-wrap items-center gap-2.5;
|
|
}
|
|
|
|
.route-name {
|
|
@apply text-base font-bold tracking-tight;
|
|
}
|
|
|
|
.route-id {
|
|
@apply font-mono text-xs text-faint;
|
|
}
|
|
|
|
.badge {
|
|
@apply inline-block rounded-full bg-surface-2 px-2 py-0.5 text-[11px] font-semibold text-muted;
|
|
}
|
|
|
|
.badge.lang {
|
|
@apply bg-accent-dim text-accent-text;
|
|
}
|
|
|
|
.badge.fallback {
|
|
@apply bg-warn-dim text-warn;
|
|
}
|
|
|
|
.badge.stop {
|
|
@apply bg-bad-dim text-bad;
|
|
}
|
|
|
|
.card-actions {
|
|
@apply flex flex-shrink-0 gap-1.5;
|
|
}
|
|
|
|
/* ---- Route cards ---- */
|
|
.route-card {
|
|
@apply flex animate-rise items-start gap-4 rounded border border-border bg-surface p-4 transition-[border-color,opacity] duration-150;
|
|
}
|
|
|
|
.route-card:hover {
|
|
@apply border-border-strong;
|
|
}
|
|
|
|
.route-card.disabled {
|
|
@apply opacity-50;
|
|
}
|
|
|
|
.route-card-main {
|
|
@apply min-w-0 flex-1 space-y-3;
|
|
}
|
|
|
|
.route-card-header {
|
|
@apply flex flex-wrap items-center gap-2.5;
|
|
}
|
|
|
|
.route-card-title {
|
|
@apply flex min-w-0 items-center gap-2;
|
|
}
|
|
|
|
.route-card-badges {
|
|
@apply ml-auto flex flex-wrap items-center gap-1.5;
|
|
}
|
|
|
|
.route-badge {
|
|
@apply inline-flex items-center gap-1 rounded-full border border-border px-2 py-0.5 text-[10.5px] font-semibold;
|
|
}
|
|
|
|
.route-badge-dot {
|
|
@apply inline-block h-1.5 w-1.5 rounded-full;
|
|
}
|
|
|
|
.route-badge-dc {
|
|
@apply bg-accent-dim/30 text-accent-text;
|
|
}
|
|
|
|
.route-badge-tg {
|
|
@apply bg-info-dim/30 text-info;
|
|
}
|
|
|
|
.route-badge-fallback {
|
|
@apply border-warn/30 bg-warn-dim/20 text-warn;
|
|
}
|
|
|
|
.route-badge-stop {
|
|
@apply border-bad/30 bg-bad-dim/20 text-bad;
|
|
}
|
|
|
|
.route-badge-role {
|
|
@apply border-accent/30 bg-accent-dim/20 text-accent-text;
|
|
}
|
|
|
|
.route-card-filters {
|
|
@apply flex flex-wrap gap-1.5;
|
|
}
|
|
|
|
.route-chip {
|
|
@apply inline-flex items-center gap-1 rounded border border-border bg-surface-2 px-2 py-0.5 text-[11px];
|
|
}
|
|
|
|
.route-chip-type {
|
|
@apply font-bold uppercase tracking-wider text-faint;
|
|
}
|
|
|
|
.route-chip-val {
|
|
@apply font-medium text-text;
|
|
}
|
|
|
|
.route-chip.exclude {
|
|
@apply border-bad/20 bg-bad-dim/20;
|
|
}
|
|
|
|
.route-chip.exclude .route-chip-type {
|
|
@apply text-bad;
|
|
}
|
|
|
|
.route-chip-empty {
|
|
@apply italic;
|
|
}
|
|
|
|
.route-card-targets {
|
|
@apply space-y-1.5;
|
|
}
|
|
|
|
.route-target {
|
|
@apply space-y-1;
|
|
}
|
|
|
|
.route-target-row {
|
|
@apply flex items-center gap-2 text-xs;
|
|
}
|
|
|
|
.route-target-label {
|
|
@apply shrink-0 rounded border border-border bg-surface-2 px-1.5 py-px text-[10px] font-bold uppercase tracking-wider text-faint;
|
|
}
|
|
|
|
.route-target-id {
|
|
@apply rounded-[5px] bg-surface-2 px-1.5 py-px font-mono text-[11.5px] leading-relaxed text-text;
|
|
}
|
|
|
|
.route-card-actions {
|
|
@apply flex shrink-0 flex-col gap-0.5;
|
|
}
|
|
|
|
.route-action-btn {
|
|
@apply flex h-7 w-7 items-center justify-center rounded text-muted transition-colors hover:bg-surface-2 hover:text-text disabled:pointer-events-none disabled:opacity-25;
|
|
}
|
|
|
|
.route-action-btn-danger:hover {
|
|
@apply bg-bad-dim/30 text-bad;
|
|
}
|
|
|
|
/* ---- Toggle switch ---- */
|
|
.switch {
|
|
@apply relative h-[22px] w-[38px] flex-shrink-0;
|
|
}
|
|
|
|
.switch input {
|
|
@apply h-0 w-0 opacity-0;
|
|
}
|
|
|
|
.switch .track {
|
|
@apply absolute inset-0 cursor-pointer rounded-full border border-border-strong bg-surface-3 transition-[background,border-color] duration-200;
|
|
}
|
|
|
|
.switch .track::after {
|
|
content: "";
|
|
position: absolute;
|
|
top: 2px;
|
|
left: 2px;
|
|
width: 16px;
|
|
height: 16px;
|
|
border-radius: 50%;
|
|
background: var(--knob);
|
|
box-shadow: 0 1px 3px rgba(15, 23, 42, 0.2);
|
|
transition: transform 0.18s;
|
|
}
|
|
|
|
.switch input:checked + .track {
|
|
@apply border-accent bg-accent;
|
|
}
|
|
|
|
.switch input:checked + .track::after {
|
|
transform: translateX(16px);
|
|
}
|
|
|
|
/* ---- Filter chips ---- */
|
|
.chips {
|
|
@apply mt-3.5 flex flex-wrap gap-2;
|
|
}
|
|
|
|
.chip {
|
|
@apply inline-flex items-center gap-1.5 rounded-full bg-surface-2 px-2.5 py-1 text-xs;
|
|
}
|
|
|
|
.chip .f-type {
|
|
@apply text-[10px] font-bold uppercase tracking-wider text-faint;
|
|
}
|
|
|
|
.chip .f-val {
|
|
@apply font-medium text-text;
|
|
}
|
|
|
|
.chip.exclude {
|
|
@apply bg-bad-dim;
|
|
}
|
|
|
|
.chip.exclude .f-type {
|
|
@apply text-bad;
|
|
}
|
|
|
|
/* ---- Targets / meta ---- */
|
|
.target {
|
|
@apply mt-3 flex flex-wrap gap-x-[18px] gap-y-2 text-xs text-muted;
|
|
}
|
|
|
|
.target b {
|
|
@apply mr-1 font-semibold tracking-wider text-faint;
|
|
}
|
|
|
|
.target code {
|
|
@apply rounded-[5px] bg-surface-2 px-1.5 py-px font-mono text-text;
|
|
}
|
|
|
|
.empty {
|
|
@apply py-[72px] text-center text-muted;
|
|
}
|
|
|
|
.empty p {
|
|
@apply mb-4;
|
|
}
|
|
|
|
/* ---- Login ---- */
|
|
.login {
|
|
@apply grid min-h-[62vh] place-items-center;
|
|
}
|
|
|
|
.login-card {
|
|
@apply max-w-[420px] rounded-2xl border border-border bg-surface px-[52px] py-12 text-center;
|
|
}
|
|
|
|
.login-card h2 {
|
|
@apply mb-2.5 text-[22px] font-extrabold tracking-tight;
|
|
}
|
|
|
|
.login-card p {
|
|
@apply mb-6 leading-relaxed text-muted;
|
|
}
|
|
|
|
.login-card code {
|
|
@apply rounded-[5px] bg-accent-dim px-1.5 py-px font-mono text-accent;
|
|
}
|
|
|
|
/* ---- Drawer / overlay ---- */
|
|
.overlay {
|
|
@apply fixed inset-0 z-40 backdrop-blur-[2px];
|
|
background: var(--scrim);
|
|
}
|
|
|
|
.editor {
|
|
@apply fixed bottom-0 right-0 top-0 z-50 flex w-[min(520px,100vw)] flex-col border-l border-border bg-surface shadow-drawer;
|
|
}
|
|
|
|
.editor-head {
|
|
@apply flex items-center justify-between gap-3 border-b border-border px-6 py-4;
|
|
background: var(--header-bg);
|
|
backdrop-filter: blur(12px);
|
|
}
|
|
|
|
.editor-heading {
|
|
@apply flex min-w-0 flex-col gap-0.5;
|
|
}
|
|
|
|
.editor-eyebrow {
|
|
@apply text-[10px] font-bold uppercase tracking-[2px] text-accent;
|
|
}
|
|
|
|
.editor-head h2 {
|
|
@apply truncate text-base font-extrabold tracking-tight;
|
|
}
|
|
|
|
.editor-head .icon-btn {
|
|
@apply h-8 w-8 rounded-full;
|
|
}
|
|
|
|
.editor-body {
|
|
@apply flex-1 overflow-y-auto px-6 py-5;
|
|
scrollbar-width: thin;
|
|
scrollbar-color: var(--scrim) transparent;
|
|
}
|
|
|
|
.editor-section {
|
|
@apply mb-7;
|
|
}
|
|
|
|
.editor-section-title {
|
|
@apply mb-3.5 flex items-center gap-2.5 text-[10.5px] font-bold uppercase tracking-[1.5px] text-faint;
|
|
}
|
|
|
|
.editor-section-title::after {
|
|
content: "";
|
|
@apply h-px flex-1 bg-border;
|
|
}
|
|
|
|
.editor-foot {
|
|
@apply flex justify-end gap-2 border-t border-border bg-surface px-6 py-4;
|
|
box-shadow: 0 -8px 24px -16px rgba(15, 23, 42, 0.18);
|
|
}
|
|
|
|
/* ---- Forms ---- */
|
|
.field {
|
|
@apply mb-4;
|
|
}
|
|
|
|
.field > label {
|
|
@apply mb-1.5 block text-[11px] font-bold uppercase tracking-[1.2px] text-faint;
|
|
}
|
|
|
|
.lbl-note {
|
|
@apply font-medium normal-case tracking-normal text-faint;
|
|
}
|
|
|
|
.hint {
|
|
@apply mt-1.5 text-[11px] text-faint;
|
|
}
|
|
|
|
.err {
|
|
@apply mt-2 min-h-4 text-xs text-bad;
|
|
}
|
|
|
|
.input {
|
|
@apply w-full rounded-sm border border-border-strong bg-surface px-3 py-2.5 font-ui text-[13.5px] text-text transition-[border-color,box-shadow] duration-150;
|
|
}
|
|
|
|
.input:focus {
|
|
@apply border-accent outline-none ring-[3px] ring-accent-dim;
|
|
}
|
|
|
|
.select {
|
|
@apply w-full cursor-pointer rounded-sm border border-border-strong bg-surface px-3 py-2.5 font-ui text-[13.5px] text-text outline-none;
|
|
}
|
|
|
|
.select:focus {
|
|
@apply border-accent ring-[3px] ring-accent-dim;
|
|
}
|
|
|
|
.row2 {
|
|
@apply grid grid-cols-2 gap-3 max-sm:grid-cols-1;
|
|
}
|
|
|
|
.inline {
|
|
@apply flex items-center gap-2;
|
|
}
|
|
|
|
.inline input[type="checkbox"] {
|
|
@apply h-4 w-4 accent-accent;
|
|
}
|
|
|
|
.inline span {
|
|
@apply text-[12.5px] font-medium text-muted;
|
|
}
|
|
|
|
.templates {
|
|
@apply flex flex-wrap gap-1.5;
|
|
}
|
|
|
|
.template-chip {
|
|
@apply cursor-pointer rounded-full border border-border-strong bg-surface-2 px-3 py-1 font-ui text-xs font-semibold text-muted transition-[border-color,color,background] duration-150;
|
|
}
|
|
|
|
.template-chip:hover {
|
|
@apply border-accent text-accent;
|
|
}
|
|
|
|
.template-chip.active {
|
|
@apply border-accent bg-accent-dim text-accent;
|
|
}
|
|
|
|
.filter-row {
|
|
@apply mb-2 grid grid-cols-[130px_1fr_auto_auto] items-center gap-2 rounded-sm border border-border bg-surface-2 p-2.5 max-sm:grid-cols-[100px_1fr_auto];
|
|
}
|
|
|
|
.filter-row select {
|
|
@apply w-full rounded border border-border-strong bg-surface px-2 py-1.5 font-ui text-[12.5px] font-medium text-text;
|
|
}
|
|
|
|
.filter-row input[type="text"] {
|
|
@apply w-full;
|
|
}
|
|
|
|
.target-row {
|
|
@apply mb-2 grid grid-cols-[minmax(130px,1fr)_auto] items-center gap-2 rounded-sm border border-border bg-surface-2 p-2.5 max-sm:grid-cols-[1fr_auto];
|
|
grid-template-areas:
|
|
"select del"
|
|
"in1 in2";
|
|
}
|
|
|
|
.target-row select {
|
|
@apply w-full rounded border border-border-strong bg-surface px-2 py-1.5 font-ui text-[12.5px] font-medium text-text;
|
|
grid-area: select;
|
|
}
|
|
|
|
.target-row input.tg-in1 {
|
|
grid-area: in1;
|
|
}
|
|
|
|
.target-row input.tg-in2 {
|
|
grid-area: in2;
|
|
}
|
|
|
|
.target-row > .icon-btn {
|
|
grid-area: del;
|
|
justify-self: end;
|
|
}
|
|
|
|
.add-filter {
|
|
@apply mt-1 w-full justify-center;
|
|
}
|
|
|
|
.forge-source-row {
|
|
@apply mb-2 grid items-center gap-2 rounded-sm border border-border bg-surface-2 p-2.5;
|
|
grid-template-areas:
|
|
"host type del"
|
|
"name name name";
|
|
grid-template-columns: 1fr auto auto;
|
|
}
|
|
|
|
.forge-source-row .f-host {
|
|
grid-area: host;
|
|
}
|
|
|
|
.forge-source-row .select.forge-type {
|
|
grid-area: type;
|
|
@apply w-[110px];
|
|
}
|
|
|
|
.forge-source-row .f-name {
|
|
grid-area: name;
|
|
}
|
|
|
|
.forge-source-row > .icon-btn {
|
|
grid-area: del;
|
|
justify-self: end;
|
|
}
|
|
|
|
.node-editor {
|
|
@apply mb-1;
|
|
}
|
|
|
|
.node-nested {
|
|
@apply ml-4 border-l border-border pl-3;
|
|
}
|
|
|
|
.leaf-row {
|
|
@apply mb-1.5 flex flex-wrap items-center gap-2 rounded-sm border border-border bg-surface-2 p-2.5;
|
|
}
|
|
|
|
.node-select {
|
|
@apply w-[120px] shrink-0 rounded border border-border-strong bg-surface px-2 py-1.5 font-ui text-[12.5px] font-medium text-text;
|
|
}
|
|
|
|
.node-path {
|
|
@apply w-[160px] shrink-0;
|
|
}
|
|
|
|
.node-values {
|
|
@apply min-w-[180px] flex-1;
|
|
}
|
|
|
|
.node-exclude {
|
|
@apply flex shrink-0 cursor-pointer items-center gap-1.5 text-[12px] font-medium text-muted;
|
|
}
|
|
|
|
.node-group {
|
|
@apply mb-1.5 rounded-sm border border-border bg-surface-2 p-2.5;
|
|
}
|
|
|
|
.node-group-head {
|
|
@apply mb-2 flex flex-wrap items-center gap-2;
|
|
}
|
|
|
|
.node-combo {
|
|
@apply w-[130px] rounded border border-border-strong bg-surface px-2 py-1.5 font-ui text-[12.5px] font-semibold text-text;
|
|
}
|
|
|
|
.node-combo-label {
|
|
@apply inline-flex items-center rounded border border-border-strong bg-surface px-2 py-1.5 font-ui text-[12.5px] font-semibold text-text;
|
|
}
|
|
|
|
.node-actions {
|
|
@apply ml-auto flex flex-wrap items-center gap-1.5;
|
|
}
|
|
|
|
.node-add {
|
|
@apply px-2 py-1 text-[12px];
|
|
}
|
|
|
|
.node-children {
|
|
@apply flex flex-col gap-1.5;
|
|
}
|
|
|
|
.node-empty {
|
|
@apply py-1 text-[12px] italic text-muted;
|
|
}
|
|
|
|
.test-payload {
|
|
@apply font-mono text-[12px];
|
|
}
|
|
|
|
.test-result {
|
|
@apply mt-1 flex flex-col gap-1.5 rounded-sm border border-border bg-surface-2 p-2.5;
|
|
}
|
|
|
|
.test-result.ok {
|
|
@apply border-ok;
|
|
}
|
|
|
|
.test-badge {
|
|
@apply inline-flex w-fit items-center rounded-full bg-bad px-2 py-0.5 text-[11px] font-semibold text-white;
|
|
}
|
|
|
|
.test-result.ok .test-badge {
|
|
@apply bg-ok;
|
|
}
|
|
|
|
.test-explanation {
|
|
@apply font-mono text-[12px] text-muted;
|
|
}
|
|
|
|
.fragments-list {
|
|
@apply mb-2 flex flex-col gap-1.5;
|
|
}
|
|
|
|
.fragment-row {
|
|
@apply flex items-center gap-2 rounded-sm border border-border bg-surface-2 p-2;
|
|
}
|
|
|
|
.fragment-name {
|
|
@apply min-w-0 flex-1 truncate text-[13px] font-medium text-text;
|
|
}
|
|
|
|
.fragment-action {
|
|
@apply shrink-0 px-2 py-1 text-[12px];
|
|
}
|
|
|
|
.fragment-save {
|
|
@apply flex items-center gap-2;
|
|
}
|
|
|
|
/* ---- Toasts ---- */
|
|
.toasts {
|
|
@apply pointer-events-none fixed bottom-7 left-1/2 z-[60] flex -translate-x-1/2 flex-col items-center gap-2;
|
|
}
|
|
|
|
.toast {
|
|
@apply pointer-events-auto cursor-pointer rounded-full bg-text px-[18px] py-2.5 text-[13px] font-semibold text-white;
|
|
}
|
|
|
|
.toast.ok {
|
|
@apply bg-ok;
|
|
}
|
|
|
|
.toast.bad {
|
|
@apply bg-bad;
|
|
}
|
|
|
|
/* ---- Tabs ---- */
|
|
.tabs {
|
|
@apply mb-5 inline-flex gap-0.5 rounded-[10px] border border-border bg-surface-2 p-1;
|
|
}
|
|
|
|
.tab {
|
|
@apply cursor-pointer rounded-[7px] border-none bg-transparent px-4 py-1.5 font-ui text-[13px] font-semibold text-muted transition-[background,color] duration-150;
|
|
}
|
|
|
|
.tab:hover {
|
|
@apply text-text;
|
|
}
|
|
|
|
.tab.active {
|
|
@apply bg-surface text-accent shadow-[0_1px_3px_rgba(15,23,42,0.08)];
|
|
}
|
|
|
|
/* ---- Toolbar / KPIs ---- */
|
|
.toolbar {
|
|
@apply mb-[18px] flex items-center justify-between;
|
|
}
|
|
|
|
.crumbs {
|
|
@apply flex items-center gap-3;
|
|
}
|
|
|
|
.kpi-label {
|
|
@apply text-[11px] font-bold uppercase tracking-[1.5px] text-faint;
|
|
}
|
|
|
|
.kpi {
|
|
@apply ml-2 text-2xl font-extrabold tracking-tight text-accent;
|
|
}
|
|
|
|
.status {
|
|
@apply flex items-center gap-2 text-xs font-medium text-muted;
|
|
}
|
|
|
|
.dot {
|
|
@apply h-2 w-2 rounded-full bg-border-strong;
|
|
}
|
|
|
|
.dot.ok {
|
|
@apply bg-ok;
|
|
}
|
|
|
|
.dot.bad {
|
|
@apply bg-bad;
|
|
}
|
|
|
|
/* ---- Logs / audit ---- */
|
|
.log-toolbar {
|
|
@apply mb-3.5 flex flex-wrap items-center justify-between gap-3;
|
|
}
|
|
|
|
.log-filters {
|
|
@apply flex items-center gap-1.5;
|
|
}
|
|
|
|
.filter-label {
|
|
@apply text-[11px] font-bold uppercase tracking-wider text-muted;
|
|
}
|
|
|
|
.filter-select {
|
|
@apply cursor-pointer rounded border border-border bg-surface px-2 py-1 text-[13px] text-text outline-none;
|
|
}
|
|
|
|
.filter-select:focus {
|
|
@apply border-accent;
|
|
}
|
|
|
|
.log-list {
|
|
@apply flex flex-col gap-2.5;
|
|
}
|
|
|
|
.log-entry {
|
|
@apply rounded border border-border border-l-[3px] border-l-ok bg-surface p-4 transition-colors duration-150;
|
|
}
|
|
|
|
.log-entry:hover {
|
|
@apply border-border-strong;
|
|
}
|
|
|
|
.log-entry.fail {
|
|
@apply border-l-bad;
|
|
}
|
|
|
|
.log-entry.clickable {
|
|
@apply cursor-pointer;
|
|
}
|
|
|
|
.log-head {
|
|
@apply flex flex-wrap items-center gap-2.5;
|
|
}
|
|
|
|
.log-route {
|
|
@apply text-sm font-bold tracking-tight;
|
|
}
|
|
|
|
.log-event {
|
|
@apply rounded-full bg-accent-dim px-2.5 py-0.5 text-[11px] font-bold uppercase tracking-wide text-accent;
|
|
}
|
|
|
|
.log-time {
|
|
@apply ml-auto text-xs font-medium text-faint;
|
|
}
|
|
|
|
.log-meta {
|
|
@apply mt-2 flex flex-wrap gap-x-[18px] gap-y-1 text-xs text-muted;
|
|
}
|
|
|
|
.log-meta b {
|
|
@apply mr-1 font-semibold tracking-wider text-faint;
|
|
}
|
|
|
|
.log-meta code {
|
|
@apply rounded-[5px] bg-surface-2 px-1.5 py-px font-mono text-text;
|
|
}
|
|
|
|
.log-error {
|
|
@apply mt-2 rounded-sm bg-bad-dim px-2.5 py-2 text-xs font-medium break-all text-bad;
|
|
}
|
|
|
|
.empty-log {
|
|
@apply py-14 text-center text-faint;
|
|
}
|
|
|
|
.log-status {
|
|
@apply rounded-full px-2.5 py-0.5 text-[11px] font-bold tracking-wide;
|
|
}
|
|
|
|
.log-status.ok {
|
|
@apply bg-ok-dim text-ok;
|
|
}
|
|
|
|
.log-status.bad {
|
|
@apply bg-bad-dim text-bad;
|
|
}
|
|
|
|
.log-detail {
|
|
@apply max-h-[82vh] w-[calc(100vw-48px)] max-w-[720px] overflow-y-auto rounded border border-border bg-surface p-[22px] shadow-modal;
|
|
}
|
|
|
|
.modal-overlay {
|
|
@apply fixed inset-0 z-[60] flex items-center justify-center p-6 backdrop-blur-[2px];
|
|
background: var(--scrim);
|
|
}
|
|
|
|
.detail-head {
|
|
@apply mb-3.5 flex items-center justify-between;
|
|
}
|
|
|
|
.detail-head h3 {
|
|
@apply text-[15px] tracking-wide;
|
|
}
|
|
|
|
.detail-grid {
|
|
@apply grid grid-cols-[minmax(120px,30%)_1fr] gap-x-4 gap-y-1.5 text-[13px];
|
|
}
|
|
|
|
.detail-grid dt {
|
|
@apply pt-1.5 text-[11px] font-semibold tracking-wide text-faint;
|
|
}
|
|
|
|
.detail-grid dd {
|
|
@apply break-all pt-1.5 text-text;
|
|
}
|
|
|
|
.detail-grid code {
|
|
@apply rounded-[5px] bg-surface-2 px-1.5 py-0.5 font-mono text-xs;
|
|
}
|
|
|
|
.detail-grid dd code {
|
|
@apply whitespace-pre-wrap;
|
|
}
|
|
|
|
/* ---- Members / invites ---- */
|
|
.members-panel {
|
|
@apply mt-7 rounded border border-border bg-surface p-5;
|
|
}
|
|
|
|
.panel-head {
|
|
@apply mb-3 flex flex-wrap items-baseline gap-2.5;
|
|
}
|
|
|
|
.panel-head h3 {
|
|
@apply text-sm tracking-wide;
|
|
}
|
|
|
|
.members-panel h4 {
|
|
@apply my-3.5 mb-2 text-xs;
|
|
}
|
|
|
|
.member-list {
|
|
@apply m-0 mb-3 flex list-none flex-col gap-2 p-0;
|
|
}
|
|
|
|
.member-row,
|
|
.invite-row {
|
|
@apply flex flex-wrap items-center gap-2.5 rounded-sm border border-border bg-surface-2 px-3 py-2;
|
|
}
|
|
|
|
.member-login {
|
|
@apply min-w-[120px] flex-1 text-[13px] font-semibold;
|
|
}
|
|
|
|
.role-pill {
|
|
@apply rounded-full px-2.5 py-0.5 text-[11px] font-bold uppercase tracking-wide;
|
|
}
|
|
|
|
.role-pill.owner {
|
|
@apply bg-accent-dim text-accent;
|
|
}
|
|
|
|
.role-pill.admin {
|
|
@apply bg-ok-dim text-ok;
|
|
}
|
|
|
|
.role-pill.viewer {
|
|
@apply border border-border bg-surface text-faint;
|
|
}
|
|
|
|
.member-add,
|
|
.invite-create {
|
|
@apply flex flex-wrap items-center gap-2;
|
|
}
|
|
|
|
.text-input {
|
|
@apply min-w-[200px] rounded border border-border bg-surface px-2.5 py-1.5 text-[13px] text-text outline-none;
|
|
}
|
|
|
|
.text-input:focus {
|
|
@apply border-accent;
|
|
}
|
|
|
|
.invite-box {
|
|
@apply mt-3.5 border-t border-dashed border-border pt-1.5;
|
|
}
|
|
|
|
.invite-list {
|
|
@apply m-2.5 mb-0 flex list-none flex-col gap-2 p-0;
|
|
}
|
|
|
|
.invite-token {
|
|
@apply font-mono text-xs text-muted;
|
|
}
|
|
|
|
.invite-exp {
|
|
@apply text-xs text-faint;
|
|
}
|
|
|
|
/* ---- Legal pages ---- */
|
|
.legal-body h2 {
|
|
@apply mb-2 mt-7 text-[17px] font-bold;
|
|
}
|
|
|
|
.legal-body p,
|
|
.legal-body li {
|
|
@apply text-[15px] text-body-text;
|
|
}
|
|
|
|
.legal-body a {
|
|
@apply text-accent;
|
|
}
|
|
|
|
.legal-body ul {
|
|
@apply pl-5;
|
|
}
|
|
|
|
.legal-body code {
|
|
@apply rounded-[6px] border border-border bg-code-bg px-1.5 py-px font-mono text-[13px];
|
|
}
|
|
|
|
/* ---- Webhook panel ---- */
|
|
.webhook-panel .wh-row {
|
|
@apply mb-2 flex flex-wrap items-center gap-2.5;
|
|
}
|
|
|
|
.webhook-panel .wh-label {
|
|
@apply min-w-[52px] text-[11px] font-bold uppercase tracking-wider text-muted;
|
|
}
|
|
|
|
.webhook-panel .wh-value {
|
|
@apply min-w-[180px] flex-1 rounded-sm border border-border bg-surface-2 px-2.5 py-1.5 text-[12.5px] break-all;
|
|
}
|
|
|
|
.webhook-panel .wh-actions {
|
|
@apply mt-1.5 flex gap-2;
|
|
}
|
|
|
|
.webhook-panel .wh-usage {
|
|
@apply mt-3.5;
|
|
}
|
|
|
|
.webhook-panel .wh-usage summary {
|
|
@apply cursor-pointer text-xs text-muted;
|
|
}
|
|
|
|
.webhook-panel .wh-code {
|
|
@apply mt-2.5 overflow-x-auto rounded-sm border border-border bg-surface-2 p-3 text-[11.5px] leading-relaxed text-muted;
|
|
}
|
|
|
|
/* ---- Metrics / stats ---- */
|
|
.metric-kpis {
|
|
@apply mb-6 grid grid-cols-2 gap-3 max-md:grid-cols-1 sm:grid-cols-3;
|
|
}
|
|
|
|
.metric-kpi {
|
|
@apply rounded-sm border border-border bg-surface p-4;
|
|
}
|
|
|
|
.metric-kpi-value {
|
|
@apply block text-2xl font-extrabold tracking-tight tabular-nums;
|
|
}
|
|
|
|
.metric-kpi-label {
|
|
@apply mt-1 block text-[11px] font-bold uppercase tracking-[1.2px] text-faint;
|
|
}
|
|
|
|
.metric-kpi.accent .metric-kpi-value {
|
|
@apply text-accent;
|
|
}
|
|
|
|
.metric-kpi.ok .metric-kpi-value {
|
|
@apply text-ok;
|
|
}
|
|
|
|
.metric-kpi.bad {
|
|
@apply border-bad/30 bg-bad/5;
|
|
}
|
|
|
|
.metric-kpi.bad .metric-kpi-value {
|
|
@apply text-bad;
|
|
}
|
|
|
|
.metric-kpi.warn .metric-kpi-value {
|
|
@apply text-warn;
|
|
}
|
|
|
|
.metric-kpi.info .metric-kpi-value {
|
|
@apply text-info;
|
|
}
|
|
|
|
.metric-block {
|
|
@apply mb-4 rounded border border-border bg-surface p-5;
|
|
}
|
|
|
|
.metric-block-title {
|
|
@apply m-0 text-[11px] font-bold uppercase tracking-[1.2px] text-faint;
|
|
}
|
|
|
|
.metric-block-head {
|
|
@apply mb-3 flex items-center justify-between gap-2;
|
|
}
|
|
|
|
.metric-block-head .metric-block-title {
|
|
@apply mb-0;
|
|
}
|
|
|
|
.metric-sort-toggle {
|
|
@apply rounded px-2 py-0.5 text-[10px] font-bold uppercase tracking-wider text-faint transition-colors;
|
|
}
|
|
|
|
.metric-sort-toggle:hover {
|
|
@apply bg-surface-3 text-text;
|
|
}
|
|
|
|
.metric-row {
|
|
@apply flex items-center gap-3 border-t border-border py-2.5 first:border-t-0 max-sm:flex-wrap;
|
|
}
|
|
|
|
.metric-key {
|
|
@apply w-[110px] shrink-0 truncate font-mono text-[12.5px] text-text;
|
|
}
|
|
|
|
.metric-total {
|
|
@apply w-12 shrink-0 text-right text-xs font-semibold tabular-nums text-muted;
|
|
}
|
|
|
|
.metric-bar {
|
|
@apply flex h-5 flex-1 overflow-hidden rounded bg-surface-3;
|
|
}
|
|
|
|
.metric-fill {
|
|
@apply flex items-center justify-center text-[11px] font-bold tabular-nums leading-none text-white/90;
|
|
min-width: 0;
|
|
transition: flex 0.3s;
|
|
}
|
|
|
|
.metric-fill.ok {
|
|
@apply bg-ok;
|
|
}
|
|
|
|
.metric-fill.bad {
|
|
@apply bg-bad;
|
|
}
|
|
|
|
.metric-status-row {
|
|
@apply flex flex-wrap gap-1.5;
|
|
}
|
|
|
|
.metric-status {
|
|
@apply rounded-full px-2.5 py-0.5 text-[11px] font-bold tabular-nums tracking-wide;
|
|
}
|
|
|
|
.metric-status.ok {
|
|
@apply bg-ok-dim text-ok;
|
|
}
|
|
|
|
.metric-status.bad {
|
|
@apply bg-bad-dim text-bad;
|
|
}
|
|
}
|
|
|
|
/* ---- Vue transition glue (lifecycle classes) ---- */
|
|
.toast-enter-active,
|
|
.toast-leave-active {
|
|
transition:
|
|
opacity 0.22s,
|
|
transform 0.22s;
|
|
}
|
|
|
|
.toast-enter-from,
|
|
.toast-leave-to {
|
|
opacity: 0;
|
|
transform: translateY(12px);
|
|
}
|
|
|
|
.fade-enter-active,
|
|
.fade-leave-active {
|
|
transition: opacity 0.2s;
|
|
}
|
|
|
|
.fade-enter-from,
|
|
.fade-leave-to {
|
|
opacity: 0;
|
|
}
|
|
|
|
.slide-enter-active,
|
|
.slide-leave-active {
|
|
transition: transform 0.28s cubic-bezier(0.32, 0.72, 0, 1);
|
|
}
|
|
|
|
.slide-enter-from,
|
|
.slide-leave-to {
|
|
transform: translateX(100%);
|
|
}
|