job_harbor 0.5.1 → 0.6.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2d42e14694f6b0fdc4c6165f73b88514a7895789b0261478b19657acc2efc4c0
4
- data.tar.gz: 76e84c7a5121a862964fa927cedcf22bb12c73e12e46c54bb5e596a81d8b4f4e
3
+ metadata.gz: 3a1b68a22bd455a51f26a4a616e40e00e76698ee0c71e017adc058971d9388ac
4
+ data.tar.gz: fcbb5ee40a161e6e99a90a2ffed21aae04d99eecc4ccae726c42288fda8fa2fd
5
5
  SHA512:
6
- metadata.gz: d49ef23794ef494dd367573f07d1ae480d69387d0f98fd0d508c75e47eeb1b50e089cf9608c83f3a5c7629b5480d66ba6a8ca91775fdbd140bccd4220b434e12
7
- data.tar.gz: b07cc34e24590dac8ef298b584e8106dc5aef8399951c68c242dd33293db966ba58a9794d809fed2a158bff13b2f73a13cc7a47779ba4728548d646c09c9124b
6
+ metadata.gz: 50b14d40d3828c535ccae32548a2c6a0ba9849e2eea29a1b2444e02144ac6d99adc88591c00974052b76f0d611fe272fdfa096276706fbcc9326cd3b46d2298d
7
+ data.tar.gz: 9b797624b652746ca7f4cf3067e22baeb800acc179d3bcbb59a02874d5712857bded64cd3d59a3e9bbb9cc95824313841c4343b5442d854620d3c67382f7d801
@@ -0,0 +1,2 @@
1
+ /*! tailwindcss v4.1.3 | MIT License | https://tailwindcss.com */
2
+ @layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-space-y-reverse:0;--tw-border-style:solid;--tw-font-weight:initial;--tw-tracking:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial}}}@layer theme{:root,:host{--font-sans:ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--font-mono:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;--color-sky-500:oklch(68.5% .169 237.323);--spacing:.25rem;--breakpoint-2xl:96rem;--text-xs:.75rem;--text-xs--line-height:calc(1/.75);--text-sm:.875rem;--text-sm--line-height:calc(1.25/.875);--text-lg:1.125rem;--text-lg--line-height:calc(1.75/1.125);--text-xl:1.25rem;--text-xl--line-height:calc(1.75/1.25);--text-2xl:1.5rem;--text-2xl--line-height:calc(2/1.5);--font-weight-medium:500;--font-weight-semibold:600;--font-weight-bold:700;--tracking-tight:-.025em;--tracking-wide:.025em;--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono)}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;font-family:var(--default-font-family,ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab, red, red)){::placeholder{color:color-mix(in oklab,currentcolor 50%,transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){appearance:button}::file-selector-button{appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}:root{--background:0 0% 98%;--foreground:240 10% 3.9%;--card:0 0% 100%;--card-foreground:240 10% 3.9%;--primary:240 5.9% 10%;--primary-foreground:0 0% 98%;--secondary:240 4.8% 95.9%;--secondary-foreground:240 5.9% 10%;--muted:240 4.8% 95.9%;--muted-foreground:240 3.8% 46.1%;--accent:240 4.8% 93.8%;--accent-foreground:240 5.9% 10%;--destructive:0 84.2% 60.2%;--destructive-foreground:0 0% 98%;--border:240 5.9% 90%;--input:240 5.9% 90%;--ring:240 5.9% 10%;--radius:.65rem;--chart-1:12 76% 61%;--chart-2:173 58% 39%;--chart-3:197 37% 24%;--chart-4:43 74% 66%;--chart-5:27 87% 67%}.dark{--background:240 10% 5.4%;--foreground:0 0% 92%;--card:240 10% 3.9%;--card-foreground:0 0% 92%;--primary:0 0% 95%;--primary-foreground:240 5.9% 10%;--secondary:240 3.7% 15.9%;--secondary-foreground:0 0% 92%;--muted:240 3.7% 15.9%;--muted-foreground:240 5% 64.9%;--accent:240 3.7% 15.9%;--accent-foreground:0 0% 92%;--destructive:0 62.8% 30.6%;--destructive-foreground:0 0% 92%;--border:240 3.7% 15.9%;--input:240 3.7% 15.9%;--ring:240 4.9% 83.9%;--chart-1:220 70% 50%;--chart-2:160 60% 45%;--chart-3:30 80% 55%;--chart-4:280 65% 60%;--chart-5:340 75% 55%}*,:before,:after{box-sizing:border-box}body{background-color:hsl(var(--background));color:hsl(var(--foreground));-webkit-font-smoothing:antialiased;font-family:ui-sans-serif,system-ui,sans-serif,Apple Color Emoji,Segoe UI Emoji}}@layer components{.navbar{border-bottom:1px solid hsl(var(--border));background-color:hsl(var(--card));align-items:center;gap:1rem;height:3.5rem;padding:0 1.5rem;display:flex}.navbar-section{align-items:center;gap:.375rem;display:flex}.navbar-item{color:hsl(var(--muted-foreground));border-radius:calc(var(--radius) - 2px);white-space:nowrap;align-items:center;gap:.375rem;padding:.375rem .75rem;font-size:.875rem;font-weight:500;text-decoration:none;transition:color .15s,background-color .15s;display:inline-flex}.navbar-item:hover{color:hsl(var(--foreground));background-color:hsl(var(--accent))}.navbar-item-current{color:hsl(var(--primary-foreground));background-color:hsl(var(--primary))}.navbar-item-current:hover{color:hsl(var(--primary-foreground));background-color:hsl(var(--primary));opacity:.9}.navbar-badge{border-radius:calc(var(--radius) - 4px);background-color:hsl(var(--muted));color:hsl(var(--muted-foreground));padding:.125rem .375rem;font-size:.75rem;font-weight:600}.navbar-item-current .navbar-badge{background-color:hsl(var(--primary-foreground)/.2);color:hsl(var(--primary-foreground))}.card{border-radius:var(--radius);border:1px solid hsl(var(--border));background-color:hsl(var(--card));color:hsl(var(--card-foreground));box-shadow:0 1px 2px #0000000d}.card-header{flex-direction:column;gap:.375rem;padding:1.5rem;display:flex}.card-title{letter-spacing:-.025em;margin:0;font-size:1rem;font-weight:600;line-height:1}.card-description{color:hsl(var(--muted-foreground));font-size:.875rem}.card-content{padding:1.5rem;padding-top:0}.card-footer{align-items:center;padding:0 1.5rem 1.5rem;display:flex}.badge{border-radius:calc(var(--radius) - 2px);white-space:nowrap;align-items:center;gap:.375rem;padding:.125rem .625rem;font-size:.75rem;font-weight:500;transition:color .15s,background-color .15s;display:inline-flex}.badge-primary{background-color:hsl(var(--primary));color:hsl(var(--primary-foreground))}.badge-secondary{background-color:hsl(var(--secondary));color:hsl(var(--secondary-foreground))}.badge-destructive{background-color:hsl(var(--destructive));color:hsl(var(--destructive-foreground))}.badge-outline{border:1px solid hsl(var(--border));color:hsl(var(--foreground));background-color:#0000}.badge-red{color:#ef4444;background-color:#ef44441a}.dark .badge-red{color:#fca5a5;background-color:#ef444426}.badge-amber{color:#d97706;background-color:#fbbf241a}.dark .badge-amber{color:#fcd34d;background-color:#fbbf2426}.badge-yellow{color:#a16207;background-color:#facc151a}.dark .badge-yellow{color:#fde047;background-color:#facc1526}.badge-green{color:#16a34a;background-color:#22c55e1a}.dark .badge-green{color:#86efac;background-color:#22c55e26}.badge-sky{color:#0284c7;background-color:#0ea5e91a}.dark .badge-sky{color:#7dd3fc;background-color:#0ea5e926}.badge-zinc{color:#52525b;background-color:#52525b1a}.dark .badge-zinc{color:#a1a1aa;background-color:#52525b33}.circle{border-radius:9999px;flex-shrink:0;width:.5rem;height:.5rem;display:inline-block}.circle-red{background-color:#ef4444}.circle-amber{background-color:#fbbf24}.circle-green{background-color:#22c55e}.circle-yellow{background-color:#facc15}.circle-sky{background-color:#0ea5e9}.circle-zinc{background-color:#52525b}.btn{white-space:nowrap;border-radius:calc(var(--radius) - 2px);cursor:pointer;border:none;justify-content:center;align-items:center;gap:.5rem;font-size:.875rem;font-weight:500;line-height:1;text-decoration:none;transition:color .15s,background-color .15s,opacity .15s;display:inline-flex}.btn:disabled{opacity:.5;cursor:not-allowed}.btn-default{background-color:hsl(var(--primary));color:hsl(var(--primary-foreground));height:2.5rem;padding:.5rem 1rem}.btn-default:hover{opacity:.9}.btn-secondary{background-color:hsl(var(--secondary));color:hsl(var(--secondary-foreground));border:1px solid hsl(var(--border));height:2.5rem;padding:.5rem 1rem}.btn-secondary:hover{background-color:hsl(var(--accent))}.btn-destructive{background-color:hsl(var(--destructive));color:hsl(var(--destructive-foreground));height:2.5rem;padding:.5rem 1rem}.btn-destructive:hover{opacity:.9}.btn-outline{border:1px solid hsl(var(--border));color:hsl(var(--foreground));background-color:#0000;height:2.5rem;padding:.5rem 1rem}.btn-outline:hover{background-color:hsl(var(--accent))}.btn-ghost{color:hsl(var(--foreground));background-color:#0000;height:2.5rem;padding:.5rem 1rem}.btn-ghost:hover{background-color:hsl(var(--accent))}.btn-icon{width:2.5rem;height:2.5rem;color:hsl(var(--foreground));border:1px solid hsl(var(--border));background-color:#0000;padding:0}.btn-icon:hover{background-color:hsl(var(--accent))}.btn-sm{height:1.75rem;padding:.25rem .5rem;font-size:.75rem}.btn-xs{height:1.5rem;padding:.125rem .5rem;font-size:.75rem}.select{border-radius:calc(var(--radius) - 2px);border:1px solid hsl(var(--input));background-color:hsl(var(--card));width:12rem;height:2.25rem;color:hsl(var(--foreground));cursor:pointer;align-items:center;padding:.375rem .75rem;font-size:.875rem;display:inline-flex}.select:focus{outline:2px solid hsl(var(--ring));outline-offset:2px}.select option{background-color:hsl(var(--card));color:hsl(var(--foreground))}.table-wrapper{overflow-x:auto}table.sqd-table{border-collapse:collapse;caption-side:bottom;width:100%;font-size:.875rem}table.sqd-table thead tr{border-bottom:1px solid hsl(var(--border))}table.sqd-table th{text-align:left;vertical-align:middle;text-transform:uppercase;letter-spacing:.05em;height:2.5rem;color:hsl(var(--muted-foreground));white-space:nowrap;padding:0 1rem;font-size:.75rem;font-weight:500}table.sqd-table tbody tr{border-bottom:1px solid hsl(var(--border));transition:background-color .15s}table.sqd-table tbody tr:hover{background-color:hsl(var(--muted)/.5)}table.sqd-table td{vertical-align:middle;padding:.75rem 1rem}.info-line{align-items:center;gap:1rem;padding:.5rem 0;display:flex}.info-line-label{color:hsl(var(--muted-foreground));white-space:nowrap;flex-shrink:0;font-size:.875rem}.info-line-separator{background-color:hsl(var(--border));flex:1;height:1px}.info-line-value{color:hsl(var(--foreground));text-align:right;white-space:nowrap;font-size:.875rem;font-weight:500}.alert{border-radius:var(--radius);border:1px solid;align-items:center;gap:.75rem;padding:1rem;font-size:.875rem;display:flex;position:relative}.alert-success{color:#16a34a;background-color:#22c55e0d;border-color:#22c55e4d}.dark .alert-success{color:#86efac;background-color:#22c55e1a}.alert-error{color:#dc2626;background-color:#ef44440d;border-color:#ef44444d}.dark .alert-error{color:#fca5a5;background-color:#ef44441a}.alert-warning{color:#d97706;background-color:#fbbf240d;border-color:#fbbf244d}.dark .alert-warning{color:#fcd34d;background-color:#fbbf241a}.alert-info{color:#0284c7;background-color:#0ea5e90d;border-color:#0ea5e94d}.dark .alert-info{color:#7dd3fc;background-color:#0ea5e91a}.pagination-nav{justify-content:center;align-items:center;gap:.25rem;display:flex}.pagination-link{min-width:2rem;height:2rem;color:hsl(var(--muted-foreground));border-radius:calc(var(--radius) - 2px);justify-content:center;align-items:center;padding:0 .5rem;font-size:.875rem;font-weight:500;text-decoration:none;transition:background-color .15s;display:inline-flex}.pagination-link:hover{background-color:hsl(var(--accent));color:hsl(var(--foreground))}.pagination-active{background-color:hsl(var(--primary));color:hsl(var(--primary-foreground));border:1px solid hsl(var(--primary))}.pagination-disabled{opacity:.5;cursor:not-allowed}.link{color:hsl(var(--foreground));text-underline-offset:4px;text-decoration:underline}.link:hover{opacity:.75}.sqd-chart{width:100%;height:200px;position:relative}.sqd-chart canvas{width:100%!important;height:100%!important}.code-block{background-color:hsl(var(--muted));border-radius:calc(var(--radius) - 2px);white-space:pre-wrap;word-break:break-word;padding:.75rem;font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;font-size:.8125rem;overflow-x:auto}.stat-card{border-radius:var(--radius);border:1px solid hsl(var(--border));background-color:hsl(var(--card));flex-direction:column;gap:.25rem;padding:1.5rem;text-decoration:none;transition:border-color .15s;display:flex}a.stat-card:hover{border-color:hsl(var(--foreground)/.2)}.stat-card-label{color:hsl(var(--muted-foreground));align-items:center;gap:.5rem;font-size:.875rem;font-weight:500;display:flex}.stat-card-value{color:hsl(var(--foreground));font-size:2.25rem;font-weight:700;line-height:1}.sqd-rate-low{color:#16a34a;background-color:#22c55e1a}.dark .sqd-rate-low{color:#86efac;background-color:#22c55e26}.sqd-rate-medium{color:#d97706;background-color:#fbbf241a}.dark .sqd-rate-medium{color:#fcd34d;background-color:#fbbf2426}.sqd-rate-high{color:#dc2626;background-color:#ef44441a}.dark .sqd-rate-high{color:#fca5a5;background-color:#ef444426}.sqd-retry-badge{border-radius:calc(var(--radius) - 4px);color:#d97706;background-color:#fbbf2426;padding:.125rem .375rem;font-size:.6875rem;font-weight:600}.dark .sqd-retry-badge{color:#fcd34d}html:not(.dark) .sqd-theme-icon-sun{display:none}html:not(.dark) .sqd-theme-icon-moon,html.dark .sqd-theme-icon-sun{display:block}html.dark .sqd-theme-icon-moon{display:none}}@layer utilities{.relative{position:relative}.static{position:static}.container{width:100%}@media (min-width:40rem){.container{max-width:40rem}}@media (min-width:48rem){.container{max-width:48rem}}@media (min-width:64rem){.container{max-width:64rem}}@media (min-width:80rem){.container{max-width:80rem}}@media (min-width:96rem){.container{max-width:96rem}}.mx-auto{margin-inline:auto}.mt-1{margin-top:calc(var(--spacing)*1)}.mt-3{margin-top:calc(var(--spacing)*3)}.mt-4{margin-top:calc(var(--spacing)*4)}.mt-6{margin-top:calc(var(--spacing)*6)}.mb-1{margin-bottom:calc(var(--spacing)*1)}.mb-3{margin-bottom:calc(var(--spacing)*3)}.mb-4{margin-bottom:calc(var(--spacing)*4)}.mb-6{margin-bottom:calc(var(--spacing)*6)}.ml-1{margin-left:calc(var(--spacing)*1)}.ml-2{margin-left:calc(var(--spacing)*2)}.ml-4{margin-left:calc(var(--spacing)*4)}.ml-auto{margin-left:auto}.block{display:block}.flex{display:flex}.grid{display:grid}.hidden{display:none}.inline{display:inline}.table{display:table}.h-3\.5{height:calc(var(--spacing)*3.5)}.h-4{height:calc(var(--spacing)*4)}.h-6{height:calc(var(--spacing)*6)}.h-12{height:calc(var(--spacing)*12)}.min-h-screen{min-height:100vh}.w-3\.5{width:calc(var(--spacing)*3.5)}.w-4{width:calc(var(--spacing)*4)}.w-6{width:calc(var(--spacing)*6)}.w-12{width:calc(var(--spacing)*12)}.w-full{width:100%}.max-w-screen-2xl{max-width:var(--breakpoint-2xl)}.flex-shrink-0{flex-shrink:0}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.flex-wrap{flex-wrap:wrap}.items-center{align-items:center}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.gap-1{gap:calc(var(--spacing)*1)}.gap-1\.5{gap:calc(var(--spacing)*1.5)}.gap-2{gap:calc(var(--spacing)*2)}.gap-4{gap:calc(var(--spacing)*4)}.gap-6{gap:calc(var(--spacing)*6)}:where(.space-y-1>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*1)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*1)*calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-3>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*3)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*3)*calc(1 - var(--tw-space-y-reverse)))}.border{border-style:var(--tw-border-style);border-width:1px}.px-6{padding-inline:calc(var(--spacing)*6)}.py-6{padding-block:calc(var(--spacing)*6)}.py-12{padding-block:calc(var(--spacing)*12)}.pl-1{padding-left:calc(var(--spacing)*1)}.text-center{text-align:center}.font-mono{font-family:var(--font-mono)}.text-2xl{font-size:var(--text-2xl);line-height:var(--tw-leading,var(--text-2xl--line-height))}.text-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-xl{font-size:var(--text-xl);line-height:var(--tw-leading,var(--text-xl--line-height))}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.font-bold{--tw-font-weight:var(--font-weight-bold);font-weight:var(--font-weight-bold)}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.tracking-tight{--tw-tracking:var(--tracking-tight);letter-spacing:var(--tracking-tight)}.tracking-wide{--tw-tracking:var(--tracking-wide);letter-spacing:var(--tracking-wide)}.text-muted-foreground{color:hsl(var(--muted-foreground))}.text-sky-500{color:var(--color-sky-500)}.uppercase{text-transform:uppercase}.no-underline{text-decoration-line:none}.opacity-75{opacity:.75}.ring{--tw-ring-shadow:var(--tw-ring-inset,)0 0 0 calc(1px + var(--tw-ring-offset-width))var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.filter{filter:var(--tw-blur,)var(--tw-brightness,)var(--tw-contrast,)var(--tw-grayscale,)var(--tw-hue-rotate,)var(--tw-invert,)var(--tw-saturate,)var(--tw-sepia,)var(--tw-drop-shadow,)}@media (min-width:40rem){.sm\:inline{display:inline}.sm\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.sm\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.sm\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}}@media (min-width:64rem){.lg\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.lg\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}}}@property --tw-space-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-tracking{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"<length>";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-blur{syntax:"*";inherits:false}@property --tw-brightness{syntax:"*";inherits:false}@property --tw-contrast{syntax:"*";inherits:false}@property --tw-grayscale{syntax:"*";inherits:false}@property --tw-hue-rotate{syntax:"*";inherits:false}@property --tw-invert{syntax:"*";inherits:false}@property --tw-opacity{syntax:"*";inherits:false}@property --tw-saturate{syntax:"*";inherits:false}@property --tw-sepia{syntax:"*";inherits:false}@property --tw-drop-shadow{syntax:"*";inherits:false}@property --tw-drop-shadow-color{syntax:"*";inherits:false}@property --tw-drop-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-drop-shadow-size{syntax:"*";inherits:false}
@@ -0,0 +1,459 @@
1
+ @import "tailwindcss";
2
+ @config "../../../../tailwind.config.js";
3
+ @source "../../../views/**/*.erb";
4
+ @source "../../../components/**/*.rb";
5
+
6
+ @layer base {
7
+ :root {
8
+ --background: 0 0% 98%;
9
+ --foreground: 240 10% 3.9%;
10
+ --card: 0 0% 100%;
11
+ --card-foreground: 240 10% 3.9%;
12
+ --primary: 240 5.9% 10%;
13
+ --primary-foreground: 0 0% 98%;
14
+ --secondary: 240 4.8% 95.9%;
15
+ --secondary-foreground: 240 5.9% 10%;
16
+ --muted: 240 4.8% 95.9%;
17
+ --muted-foreground: 240 3.8% 46.1%;
18
+ --accent: 240 4.8% 93.8%;
19
+ --accent-foreground: 240 5.9% 10%;
20
+ --destructive: 0 84.2% 60.2%;
21
+ --destructive-foreground: 0 0% 98%;
22
+ --border: 240 5.9% 90%;
23
+ --input: 240 5.9% 90%;
24
+ --ring: 240 5.9% 10%;
25
+ --radius: 0.65rem;
26
+ --chart-1: 12 76% 61%;
27
+ --chart-2: 173 58% 39%;
28
+ --chart-3: 197 37% 24%;
29
+ --chart-4: 43 74% 66%;
30
+ --chart-5: 27 87% 67%;
31
+ }
32
+
33
+ .dark {
34
+ --background: 240 10% 5.4%;
35
+ --foreground: 0 0% 92%;
36
+ --card: 240 10% 3.9%;
37
+ --card-foreground: 0 0% 92%;
38
+ --primary: 0 0% 95%;
39
+ --primary-foreground: 240 5.9% 10%;
40
+ --secondary: 240 3.7% 15.9%;
41
+ --secondary-foreground: 0 0% 92%;
42
+ --muted: 240 3.7% 15.9%;
43
+ --muted-foreground: 240 5% 64.9%;
44
+ --accent: 240 3.7% 15.9%;
45
+ --accent-foreground: 0 0% 92%;
46
+ --destructive: 0 62.8% 30.6%;
47
+ --destructive-foreground: 0 0% 92%;
48
+ --border: 240 3.7% 15.9%;
49
+ --input: 240 3.7% 15.9%;
50
+ --ring: 240 4.9% 83.9%;
51
+ --chart-1: 220 70% 50%;
52
+ --chart-2: 160 60% 45%;
53
+ --chart-3: 30 80% 55%;
54
+ --chart-4: 280 65% 60%;
55
+ --chart-5: 340 75% 55%;
56
+ }
57
+
58
+ *, *::before, *::after { box-sizing: border-box; }
59
+
60
+ body {
61
+ font-family: ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji";
62
+ background-color: hsl(var(--background));
63
+ color: hsl(var(--foreground));
64
+ -webkit-font-smoothing: antialiased;
65
+ }
66
+ }
67
+
68
+ @layer components {
69
+ /* Navbar */
70
+ .navbar {
71
+ display: flex;
72
+ align-items: center;
73
+ gap: 1rem;
74
+ padding: 0 1.5rem;
75
+ height: 3.5rem;
76
+ border-bottom: 1px solid hsl(var(--border));
77
+ background-color: hsl(var(--card));
78
+ }
79
+ .navbar-section { display: flex; align-items: center; gap: 0.375rem; }
80
+ .navbar-item {
81
+ display: inline-flex;
82
+ align-items: center;
83
+ gap: 0.375rem;
84
+ padding: 0.375rem 0.75rem;
85
+ font-size: 0.875rem;
86
+ font-weight: 500;
87
+ color: hsl(var(--muted-foreground));
88
+ text-decoration: none;
89
+ border-radius: calc(var(--radius) - 2px);
90
+ transition: color 150ms, background-color 150ms;
91
+ white-space: nowrap;
92
+ }
93
+ .navbar-item:hover {
94
+ color: hsl(var(--foreground));
95
+ background-color: hsl(var(--accent));
96
+ }
97
+ .navbar-item-current {
98
+ color: hsl(var(--primary-foreground));
99
+ background-color: hsl(var(--primary));
100
+ }
101
+ .navbar-item-current:hover {
102
+ color: hsl(var(--primary-foreground));
103
+ background-color: hsl(var(--primary));
104
+ opacity: 0.9;
105
+ }
106
+ .navbar-badge {
107
+ padding: 0.125rem 0.375rem;
108
+ font-size: 0.75rem;
109
+ font-weight: 600;
110
+ border-radius: calc(var(--radius) - 4px);
111
+ background-color: hsl(var(--muted));
112
+ color: hsl(var(--muted-foreground));
113
+ }
114
+ .navbar-item-current .navbar-badge {
115
+ background-color: hsl(var(--primary-foreground) / 0.2);
116
+ color: hsl(var(--primary-foreground));
117
+ }
118
+
119
+ /* Cards */
120
+ .card {
121
+ border-radius: var(--radius);
122
+ border: 1px solid hsl(var(--border));
123
+ background-color: hsl(var(--card));
124
+ color: hsl(var(--card-foreground));
125
+ box-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05);
126
+ }
127
+ .card-header {
128
+ display: flex;
129
+ flex-direction: column;
130
+ gap: 0.375rem;
131
+ padding: 1.5rem;
132
+ }
133
+ .card-title {
134
+ margin: 0;
135
+ font-size: 1rem;
136
+ font-weight: 600;
137
+ line-height: 1;
138
+ letter-spacing: -0.025em;
139
+ }
140
+ .card-description {
141
+ font-size: 0.875rem;
142
+ color: hsl(var(--muted-foreground));
143
+ }
144
+ .card-content { padding: 1.5rem; padding-top: 0; }
145
+ .card-footer { display: flex; align-items: center; padding: 1.5rem; padding-top: 0; }
146
+
147
+ /* Badges */
148
+ .badge {
149
+ display: inline-flex;
150
+ align-items: center;
151
+ gap: 0.375rem;
152
+ padding: 0.125rem 0.625rem;
153
+ font-size: 0.75rem;
154
+ font-weight: 500;
155
+ border-radius: calc(var(--radius) - 2px);
156
+ white-space: nowrap;
157
+ transition: color 150ms, background-color 150ms;
158
+ }
159
+ .badge-primary { background-color: hsl(var(--primary)); color: hsl(var(--primary-foreground)); }
160
+ .badge-secondary { background-color: hsl(var(--secondary)); color: hsl(var(--secondary-foreground)); }
161
+ .badge-destructive { background-color: hsl(var(--destructive)); color: hsl(var(--destructive-foreground)); }
162
+ .badge-outline { border: 1px solid hsl(var(--border)); color: hsl(var(--foreground)); background-color: transparent; }
163
+
164
+ .badge-red { background-color: rgb(239 68 68 / 0.1); color: rgb(239 68 68); }
165
+ .dark .badge-red { background-color: rgb(239 68 68 / 0.15); color: rgb(252 165 165); }
166
+ .badge-amber { background-color: rgb(251 191 36 / 0.1); color: rgb(217 119 6); }
167
+ .dark .badge-amber { background-color: rgb(251 191 36 / 0.15); color: rgb(252 211 77); }
168
+ .badge-yellow { background-color: rgb(250 204 21 / 0.1); color: rgb(161 98 7); }
169
+ .dark .badge-yellow { background-color: rgb(250 204 21 / 0.15); color: rgb(253 224 71); }
170
+ .badge-green { background-color: rgb(34 197 94 / 0.1); color: rgb(22 163 74); }
171
+ .dark .badge-green { background-color: rgb(34 197 94 / 0.15); color: rgb(134 239 172); }
172
+ .badge-sky { background-color: rgb(14 165 233 / 0.1); color: rgb(2 132 199); }
173
+ .dark .badge-sky { background-color: rgb(14 165 233 / 0.15); color: rgb(125 211 252); }
174
+ .badge-zinc { background-color: rgb(82 82 91 / 0.1); color: rgb(82 82 91); }
175
+ .dark .badge-zinc { background-color: rgb(82 82 91 / 0.2); color: rgb(161 161 170); }
176
+
177
+ /* Status Circles */
178
+ .circle { display: inline-block; width: 0.5rem; height: 0.5rem; border-radius: 9999px; flex-shrink: 0; }
179
+ .circle-red { background-color: rgb(239 68 68); }
180
+ .circle-amber { background-color: rgb(251 191 36); }
181
+ .circle-green { background-color: rgb(34 197 94); }
182
+ .circle-yellow { background-color: rgb(250 204 21); }
183
+ .circle-sky { background-color: rgb(14 165 233); }
184
+ .circle-zinc { background-color: rgb(82 82 91); }
185
+
186
+ /* Buttons */
187
+ .btn {
188
+ display: inline-flex;
189
+ align-items: center;
190
+ justify-content: center;
191
+ gap: 0.5rem;
192
+ font-size: 0.875rem;
193
+ font-weight: 500;
194
+ white-space: nowrap;
195
+ border-radius: calc(var(--radius) - 2px);
196
+ border: none;
197
+ cursor: pointer;
198
+ transition: color 150ms, background-color 150ms, opacity 150ms;
199
+ text-decoration: none;
200
+ line-height: 1;
201
+ }
202
+ .btn:disabled { opacity: 0.5; cursor: not-allowed; }
203
+ .btn-default {
204
+ background-color: hsl(var(--primary));
205
+ color: hsl(var(--primary-foreground));
206
+ padding: 0.5rem 1rem;
207
+ height: 2.5rem;
208
+ }
209
+ .btn-default:hover { opacity: 0.9; }
210
+ .btn-secondary {
211
+ background-color: hsl(var(--secondary));
212
+ color: hsl(var(--secondary-foreground));
213
+ border: 1px solid hsl(var(--border));
214
+ padding: 0.5rem 1rem;
215
+ height: 2.5rem;
216
+ }
217
+ .btn-secondary:hover { background-color: hsl(var(--accent)); }
218
+ .btn-destructive {
219
+ background-color: hsl(var(--destructive));
220
+ color: hsl(var(--destructive-foreground));
221
+ padding: 0.5rem 1rem;
222
+ height: 2.5rem;
223
+ }
224
+ .btn-destructive:hover { opacity: 0.9; }
225
+ .btn-outline {
226
+ border: 1px solid hsl(var(--border));
227
+ background-color: transparent;
228
+ color: hsl(var(--foreground));
229
+ padding: 0.5rem 1rem;
230
+ height: 2.5rem;
231
+ }
232
+ .btn-outline:hover { background-color: hsl(var(--accent)); }
233
+ .btn-ghost {
234
+ background-color: transparent;
235
+ color: hsl(var(--foreground));
236
+ padding: 0.5rem 1rem;
237
+ height: 2.5rem;
238
+ }
239
+ .btn-ghost:hover { background-color: hsl(var(--accent)); }
240
+ .btn-icon {
241
+ padding: 0;
242
+ width: 2.5rem;
243
+ height: 2.5rem;
244
+ background-color: transparent;
245
+ color: hsl(var(--foreground));
246
+ border: 1px solid hsl(var(--border));
247
+ }
248
+ .btn-icon:hover { background-color: hsl(var(--accent)); }
249
+ .btn-sm { height: 1.75rem; padding: 0.25rem 0.5rem; font-size: 0.75rem; }
250
+ .btn-xs { height: 1.5rem; padding: 0.125rem 0.5rem; font-size: 0.75rem; }
251
+
252
+ /* Select */
253
+ .select {
254
+ display: inline-flex;
255
+ height: 2.25rem;
256
+ width: 12rem;
257
+ align-items: center;
258
+ border-radius: calc(var(--radius) - 2px);
259
+ border: 1px solid hsl(var(--input));
260
+ background-color: hsl(var(--card));
261
+ padding: 0.375rem 0.75rem;
262
+ font-size: 0.875rem;
263
+ color: hsl(var(--foreground));
264
+ cursor: pointer;
265
+ }
266
+ .select:focus { outline: 2px solid hsl(var(--ring)); outline-offset: 2px; }
267
+ .select option { background-color: hsl(var(--card)); color: hsl(var(--foreground)); }
268
+
269
+ /* Tables */
270
+ .table-wrapper { overflow-x: auto; }
271
+ table.sqd-table { width: 100%; border-collapse: collapse; font-size: 0.875rem; caption-side: bottom; }
272
+ table.sqd-table thead tr { border-bottom: 1px solid hsl(var(--border)); }
273
+ table.sqd-table th {
274
+ height: 2.5rem;
275
+ padding: 0 1rem;
276
+ text-align: left;
277
+ vertical-align: middle;
278
+ font-weight: 500;
279
+ font-size: 0.75rem;
280
+ text-transform: uppercase;
281
+ letter-spacing: 0.05em;
282
+ color: hsl(var(--muted-foreground));
283
+ white-space: nowrap;
284
+ }
285
+ table.sqd-table tbody tr { border-bottom: 1px solid hsl(var(--border)); transition: background-color 150ms; }
286
+ table.sqd-table tbody tr:hover { background-color: hsl(var(--muted) / 0.5); }
287
+ table.sqd-table td { padding: 0.75rem 1rem; vertical-align: middle; }
288
+
289
+ /* Info Lines */
290
+ .info-line {
291
+ display: flex;
292
+ align-items: center;
293
+ gap: 1rem;
294
+ padding: 0.5rem 0;
295
+ }
296
+ .info-line-label {
297
+ font-size: 0.875rem;
298
+ color: hsl(var(--muted-foreground));
299
+ white-space: nowrap;
300
+ flex-shrink: 0;
301
+ }
302
+ .info-line-separator {
303
+ flex: 1;
304
+ height: 1px;
305
+ background-color: hsl(var(--border));
306
+ }
307
+ .info-line-value {
308
+ font-size: 0.875rem;
309
+ font-weight: 500;
310
+ color: hsl(var(--foreground));
311
+ text-align: right;
312
+ white-space: nowrap;
313
+ }
314
+
315
+ /* Alerts / Flash */
316
+ .alert {
317
+ position: relative;
318
+ display: flex;
319
+ align-items: center;
320
+ gap: 0.75rem;
321
+ padding: 1rem;
322
+ border-radius: var(--radius);
323
+ border: 1px solid;
324
+ font-size: 0.875rem;
325
+ }
326
+ .alert-success {
327
+ border-color: rgb(34 197 94 / 0.3);
328
+ background-color: rgb(34 197 94 / 0.05);
329
+ color: rgb(22 163 74);
330
+ }
331
+ .dark .alert-success { color: rgb(134 239 172); background-color: rgb(34 197 94 / 0.1); }
332
+ .alert-error {
333
+ border-color: rgb(239 68 68 / 0.3);
334
+ background-color: rgb(239 68 68 / 0.05);
335
+ color: rgb(220 38 38);
336
+ }
337
+ .dark .alert-error { color: rgb(252 165 165); background-color: rgb(239 68 68 / 0.1); }
338
+ .alert-warning {
339
+ border-color: rgb(251 191 36 / 0.3);
340
+ background-color: rgb(251 191 36 / 0.05);
341
+ color: rgb(217 119 6);
342
+ }
343
+ .dark .alert-warning { color: rgb(252 211 77); background-color: rgb(251 191 36 / 0.1); }
344
+ .alert-info {
345
+ border-color: rgb(14 165 233 / 0.3);
346
+ background-color: rgb(14 165 233 / 0.05);
347
+ color: rgb(2 132 199);
348
+ }
349
+ .dark .alert-info { color: rgb(125 211 252); background-color: rgb(14 165 233 / 0.1); }
350
+
351
+ /* Pagination */
352
+ .pagination-nav { display: flex; align-items: center; justify-content: center; gap: 0.25rem; }
353
+ .pagination-link {
354
+ display: inline-flex;
355
+ align-items: center;
356
+ justify-content: center;
357
+ min-width: 2rem;
358
+ height: 2rem;
359
+ padding: 0 0.5rem;
360
+ font-size: 0.875rem;
361
+ font-weight: 500;
362
+ color: hsl(var(--muted-foreground));
363
+ text-decoration: none;
364
+ border-radius: calc(var(--radius) - 2px);
365
+ transition: background-color 150ms;
366
+ }
367
+ .pagination-link:hover { background-color: hsl(var(--accent)); color: hsl(var(--foreground)); }
368
+ .pagination-active {
369
+ background-color: hsl(var(--primary));
370
+ color: hsl(var(--primary-foreground));
371
+ border: 1px solid hsl(var(--primary));
372
+ }
373
+ .pagination-disabled { opacity: 0.5; cursor: not-allowed; }
374
+
375
+ /* Link utility */
376
+ .link { color: hsl(var(--foreground)); text-decoration: underline; text-underline-offset: 4px; }
377
+ .link:hover { opacity: 0.75; }
378
+
379
+ /* Chart */
380
+ .sqd-chart { position: relative; width: 100%; height: 200px; }
381
+ .sqd-chart canvas { width: 100% !important; height: 100% !important; }
382
+
383
+ /* Code blocks */
384
+ .code-block {
385
+ font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
386
+ font-size: 0.8125rem;
387
+ background-color: hsl(var(--muted));
388
+ padding: 0.75rem;
389
+ border-radius: calc(var(--radius) - 2px);
390
+ overflow-x: auto;
391
+ white-space: pre-wrap;
392
+ word-break: break-word;
393
+ }
394
+
395
+ /* Stat card */
396
+ .stat-card {
397
+ border-radius: var(--radius);
398
+ border: 1px solid hsl(var(--border));
399
+ background-color: hsl(var(--card));
400
+ padding: 1.5rem;
401
+ display: flex;
402
+ flex-direction: column;
403
+ gap: 0.25rem;
404
+ text-decoration: none;
405
+ transition: border-color 150ms;
406
+ }
407
+ a.stat-card:hover { border-color: hsl(var(--foreground) / 0.2); }
408
+ .stat-card-label {
409
+ font-size: 0.875rem;
410
+ font-weight: 500;
411
+ color: hsl(var(--muted-foreground));
412
+ display: flex;
413
+ align-items: center;
414
+ gap: 0.5rem;
415
+ }
416
+ .stat-card-value {
417
+ font-size: 2.25rem;
418
+ font-weight: 700;
419
+ line-height: 1;
420
+ color: hsl(var(--foreground));
421
+ }
422
+
423
+ /* Utility: sqd-rate badge colors (reused from failure stats) */
424
+ .sqd-rate-low { background-color: rgb(34 197 94 / 0.1); color: rgb(22 163 74); }
425
+ .dark .sqd-rate-low { background-color: rgb(34 197 94 / 0.15); color: rgb(134 239 172); }
426
+ .sqd-rate-medium { background-color: rgb(251 191 36 / 0.1); color: rgb(217 119 6); }
427
+ .dark .sqd-rate-medium { background-color: rgb(251 191 36 / 0.15); color: rgb(252 211 77); }
428
+ .sqd-rate-high { background-color: rgb(239 68 68 / 0.1); color: rgb(220 38 38); }
429
+ .dark .sqd-rate-high { background-color: rgb(239 68 68 / 0.15); color: rgb(252 165 165); }
430
+
431
+ /* Legacy compat: sqd- prefixed classes used by existing tests */
432
+ .sqd-nav-link { }
433
+ .sqd-nav-badge { }
434
+ .sqd-theme-toggle { }
435
+ .sqd-theme-icon-sun, .sqd-theme-icon-moon { }
436
+ .sqd-refresh-selector { }
437
+ .sqd-refresh-select { }
438
+ .sqd-per-page-selector { }
439
+ .sqd-per-page-select { }
440
+ .sqd-filters { }
441
+ .sqd-filter-select { }
442
+ .sqd-failure-rates { }
443
+ .sqd-failure-table { }
444
+ .sqd-retry-badge {
445
+ padding: 0.125rem 0.375rem;
446
+ font-size: 0.6875rem;
447
+ font-weight: 600;
448
+ border-radius: calc(var(--radius) - 4px);
449
+ background-color: rgb(251 191 36 / 0.15);
450
+ color: rgb(217 119 6);
451
+ }
452
+ .dark .sqd-retry-badge { color: rgb(252 211 77); }
453
+
454
+ /* Dark theme icon visibility */
455
+ html:not(.dark) .sqd-theme-icon-sun { display: none; }
456
+ html:not(.dark) .sqd-theme-icon-moon { display: block; }
457
+ html.dark .sqd-theme-icon-sun { display: block; }
458
+ html.dark .sqd-theme-icon-moon { display: none; }
459
+ }
@@ -6,492 +6,7 @@
6
6
  <meta charset="utf-8">
7
7
  <%= csrf_meta_tags %>
8
8
  <%= csp_meta_tag %>
9
-
10
- <%# Tailwind Play CDN - zero build step %>
11
- <script src="https://cdn.tailwindcss.com?plugins=forms"></script>
12
- <script>
13
- tailwind.config = {
14
- darkMode: 'class',
15
- theme: {
16
- extend: {
17
- colors: {
18
- border: 'hsl(var(--border))',
19
- input: 'hsl(var(--input))',
20
- ring: 'hsl(var(--ring))',
21
- background: 'hsl(var(--background))',
22
- foreground: 'hsl(var(--foreground))',
23
- primary: { DEFAULT: 'hsl(var(--primary))', foreground: 'hsl(var(--primary-foreground))' },
24
- secondary: { DEFAULT: 'hsl(var(--secondary))', foreground: 'hsl(var(--secondary-foreground))' },
25
- destructive: { DEFAULT: 'hsl(var(--destructive))', foreground: 'hsl(var(--destructive-foreground))' },
26
- muted: { DEFAULT: 'hsl(var(--muted))', foreground: 'hsl(var(--muted-foreground))' },
27
- accent: { DEFAULT: 'hsl(var(--accent))', foreground: 'hsl(var(--accent-foreground))' },
28
- card: { DEFAULT: 'hsl(var(--card))', foreground: 'hsl(var(--card-foreground))' },
29
- },
30
- borderRadius: {
31
- lg: 'var(--radius)',
32
- md: 'calc(var(--radius) - 2px)',
33
- sm: 'calc(var(--radius) - 4px)',
34
- },
35
- },
36
- },
37
- }
38
- </script>
39
-
40
- <style>
41
- /* === HSL Variable System (shadcn-inspired) === */
42
- :root {
43
- --background: 0 0% 98%;
44
- --foreground: 240 10% 3.9%;
45
- --card: 0 0% 100%;
46
- --card-foreground: 240 10% 3.9%;
47
- --primary: 240 5.9% 10%;
48
- --primary-foreground: 0 0% 98%;
49
- --secondary: 240 4.8% 95.9%;
50
- --secondary-foreground: 240 5.9% 10%;
51
- --muted: 240 4.8% 95.9%;
52
- --muted-foreground: 240 3.8% 46.1%;
53
- --accent: 240 4.8% 93.8%;
54
- --accent-foreground: 240 5.9% 10%;
55
- --destructive: 0 84.2% 60.2%;
56
- --destructive-foreground: 0 0% 98%;
57
- --border: 240 5.9% 90%;
58
- --input: 240 5.9% 90%;
59
- --ring: 240 5.9% 10%;
60
- --radius: 0.65rem;
61
- --chart-1: 12 76% 61%;
62
- --chart-2: 173 58% 39%;
63
- --chart-3: 197 37% 24%;
64
- --chart-4: 43 74% 66%;
65
- --chart-5: 27 87% 67%;
66
- }
67
-
68
- .dark {
69
- --background: 240 10% 5.4%;
70
- --foreground: 0 0% 92%;
71
- --card: 240 10% 3.9%;
72
- --card-foreground: 0 0% 92%;
73
- --primary: 0 0% 95%;
74
- --primary-foreground: 240 5.9% 10%;
75
- --secondary: 240 3.7% 15.9%;
76
- --secondary-foreground: 0 0% 92%;
77
- --muted: 240 3.7% 15.9%;
78
- --muted-foreground: 240 5% 64.9%;
79
- --accent: 240 3.7% 15.9%;
80
- --accent-foreground: 0 0% 92%;
81
- --destructive: 0 62.8% 30.6%;
82
- --destructive-foreground: 0 0% 92%;
83
- --border: 240 3.7% 15.9%;
84
- --input: 240 3.7% 15.9%;
85
- --ring: 240 4.9% 83.9%;
86
- --chart-1: 220 70% 50%;
87
- --chart-2: 160 60% 45%;
88
- --chart-3: 30 80% 55%;
89
- --chart-4: 280 65% 60%;
90
- --chart-5: 340 75% 55%;
91
- }
92
-
93
- *, *::before, *::after { box-sizing: border-box; }
94
-
95
- body {
96
- font-family: ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji";
97
- background-color: hsl(var(--background));
98
- color: hsl(var(--foreground));
99
- -webkit-font-smoothing: antialiased;
100
- }
101
-
102
- /* === Component Classes === */
103
-
104
- /* Navbar */
105
- .navbar {
106
- display: flex;
107
- align-items: center;
108
- gap: 1rem;
109
- padding: 0 1.5rem;
110
- height: 3.5rem;
111
- border-bottom: 1px solid hsl(var(--border));
112
- background-color: hsl(var(--card));
113
- }
114
- .navbar-section { display: flex; align-items: center; gap: 0.375rem; }
115
- .navbar-item {
116
- display: inline-flex;
117
- align-items: center;
118
- gap: 0.375rem;
119
- padding: 0.375rem 0.75rem;
120
- font-size: 0.875rem;
121
- font-weight: 500;
122
- color: hsl(var(--muted-foreground));
123
- text-decoration: none;
124
- border-radius: calc(var(--radius) - 2px);
125
- transition: color 150ms, background-color 150ms;
126
- white-space: nowrap;
127
- }
128
- .navbar-item:hover {
129
- color: hsl(var(--foreground));
130
- background-color: hsl(var(--accent));
131
- }
132
- .navbar-item-current {
133
- color: hsl(var(--primary-foreground));
134
- background-color: hsl(var(--primary));
135
- }
136
- .navbar-item-current:hover {
137
- color: hsl(var(--primary-foreground));
138
- background-color: hsl(var(--primary));
139
- opacity: 0.9;
140
- }
141
- .navbar-badge {
142
- padding: 0.125rem 0.375rem;
143
- font-size: 0.75rem;
144
- font-weight: 600;
145
- border-radius: calc(var(--radius) - 4px);
146
- background-color: hsl(var(--muted));
147
- color: hsl(var(--muted-foreground));
148
- }
149
- .navbar-item-current .navbar-badge {
150
- background-color: hsl(var(--primary-foreground) / 0.2);
151
- color: hsl(var(--primary-foreground));
152
- }
153
-
154
- /* Cards */
155
- .card {
156
- border-radius: var(--radius);
157
- border: 1px solid hsl(var(--border));
158
- background-color: hsl(var(--card));
159
- color: hsl(var(--card-foreground));
160
- box-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05);
161
- }
162
- .card-header {
163
- display: flex;
164
- flex-direction: column;
165
- gap: 0.375rem;
166
- padding: 1.5rem;
167
- }
168
- .card-title {
169
- margin: 0;
170
- font-size: 1rem;
171
- font-weight: 600;
172
- line-height: 1;
173
- letter-spacing: -0.025em;
174
- }
175
- .card-description {
176
- font-size: 0.875rem;
177
- color: hsl(var(--muted-foreground));
178
- }
179
- .card-content { padding: 1.5rem; padding-top: 0; }
180
- .card-footer { display: flex; align-items: center; padding: 1.5rem; padding-top: 0; }
181
-
182
- /* Badges */
183
- .badge {
184
- display: inline-flex;
185
- align-items: center;
186
- gap: 0.375rem;
187
- padding: 0.125rem 0.625rem;
188
- font-size: 0.75rem;
189
- font-weight: 500;
190
- border-radius: calc(var(--radius) - 2px);
191
- white-space: nowrap;
192
- transition: color 150ms, background-color 150ms;
193
- }
194
- .badge-primary { background-color: hsl(var(--primary)); color: hsl(var(--primary-foreground)); }
195
- .badge-secondary { background-color: hsl(var(--secondary)); color: hsl(var(--secondary-foreground)); }
196
- .badge-destructive { background-color: hsl(var(--destructive)); color: hsl(var(--destructive-foreground)); }
197
- .badge-outline { border: 1px solid hsl(var(--border)); color: hsl(var(--foreground)); background-color: transparent; }
198
-
199
- .badge-red { background-color: rgb(239 68 68 / 0.1); color: rgb(239 68 68); }
200
- .dark .badge-red { background-color: rgb(239 68 68 / 0.15); color: rgb(252 165 165); }
201
- .badge-amber { background-color: rgb(251 191 36 / 0.1); color: rgb(217 119 6); }
202
- .dark .badge-amber { background-color: rgb(251 191 36 / 0.15); color: rgb(252 211 77); }
203
- .badge-yellow { background-color: rgb(250 204 21 / 0.1); color: rgb(161 98 7); }
204
- .dark .badge-yellow { background-color: rgb(250 204 21 / 0.15); color: rgb(253 224 71); }
205
- .badge-green { background-color: rgb(34 197 94 / 0.1); color: rgb(22 163 74); }
206
- .dark .badge-green { background-color: rgb(34 197 94 / 0.15); color: rgb(134 239 172); }
207
- .badge-sky { background-color: rgb(14 165 233 / 0.1); color: rgb(2 132 199); }
208
- .dark .badge-sky { background-color: rgb(14 165 233 / 0.15); color: rgb(125 211 252); }
209
- .badge-zinc { background-color: rgb(82 82 91 / 0.1); color: rgb(82 82 91); }
210
- .dark .badge-zinc { background-color: rgb(82 82 91 / 0.2); color: rgb(161 161 170); }
211
-
212
- /* Status Circles */
213
- .circle { display: inline-block; width: 0.5rem; height: 0.5rem; border-radius: 9999px; flex-shrink: 0; }
214
- .circle-red { background-color: rgb(239 68 68); }
215
- .circle-amber { background-color: rgb(251 191 36); }
216
- .circle-green { background-color: rgb(34 197 94); }
217
- .circle-yellow { background-color: rgb(250 204 21); }
218
- .circle-sky { background-color: rgb(14 165 233); }
219
- .circle-zinc { background-color: rgb(82 82 91); }
220
-
221
- /* Buttons */
222
- .btn {
223
- display: inline-flex;
224
- align-items: center;
225
- justify-content: center;
226
- gap: 0.5rem;
227
- font-size: 0.875rem;
228
- font-weight: 500;
229
- white-space: nowrap;
230
- border-radius: calc(var(--radius) - 2px);
231
- border: none;
232
- cursor: pointer;
233
- transition: color 150ms, background-color 150ms, opacity 150ms;
234
- text-decoration: none;
235
- line-height: 1;
236
- }
237
- .btn:disabled { opacity: 0.5; cursor: not-allowed; }
238
- .btn-default {
239
- background-color: hsl(var(--primary));
240
- color: hsl(var(--primary-foreground));
241
- padding: 0.5rem 1rem;
242
- height: 2.5rem;
243
- }
244
- .btn-default:hover { opacity: 0.9; }
245
- .btn-secondary {
246
- background-color: hsl(var(--secondary));
247
- color: hsl(var(--secondary-foreground));
248
- border: 1px solid hsl(var(--border));
249
- padding: 0.5rem 1rem;
250
- height: 2.5rem;
251
- }
252
- .btn-secondary:hover { background-color: hsl(var(--accent)); }
253
- .btn-destructive {
254
- background-color: hsl(var(--destructive));
255
- color: hsl(var(--destructive-foreground));
256
- padding: 0.5rem 1rem;
257
- height: 2.5rem;
258
- }
259
- .btn-destructive:hover { opacity: 0.9; }
260
- .btn-outline {
261
- border: 1px solid hsl(var(--border));
262
- background-color: transparent;
263
- color: hsl(var(--foreground));
264
- padding: 0.5rem 1rem;
265
- height: 2.5rem;
266
- }
267
- .btn-outline:hover { background-color: hsl(var(--accent)); }
268
- .btn-ghost {
269
- background-color: transparent;
270
- color: hsl(var(--foreground));
271
- padding: 0.5rem 1rem;
272
- height: 2.5rem;
273
- }
274
- .btn-ghost:hover { background-color: hsl(var(--accent)); }
275
- .btn-icon {
276
- padding: 0;
277
- width: 2.5rem;
278
- height: 2.5rem;
279
- background-color: transparent;
280
- color: hsl(var(--foreground));
281
- border: 1px solid hsl(var(--border));
282
- }
283
- .btn-icon:hover { background-color: hsl(var(--accent)); }
284
- .btn-sm { height: 1.75rem; padding: 0.25rem 0.5rem; font-size: 0.75rem; }
285
- .btn-xs { height: 1.5rem; padding: 0.125rem 0.5rem; font-size: 0.75rem; }
286
-
287
- /* Select */
288
- .select {
289
- display: inline-flex;
290
- height: 2.25rem;
291
- width: 12rem;
292
- align-items: center;
293
- border-radius: calc(var(--radius) - 2px);
294
- border: 1px solid hsl(var(--input));
295
- background-color: hsl(var(--card));
296
- padding: 0.375rem 0.75rem;
297
- font-size: 0.875rem;
298
- color: hsl(var(--foreground));
299
- cursor: pointer;
300
- }
301
- .select:focus { outline: 2px solid hsl(var(--ring)); outline-offset: 2px; }
302
- .select option { background-color: hsl(var(--card)); color: hsl(var(--foreground)); }
303
-
304
- /* Tables */
305
- .table-wrapper { overflow-x: auto; }
306
- table.sqd-table { width: 100%; border-collapse: collapse; font-size: 0.875rem; caption-side: bottom; }
307
- table.sqd-table thead tr { border-bottom: 1px solid hsl(var(--border)); }
308
- table.sqd-table th {
309
- height: 2.5rem;
310
- padding: 0 1rem;
311
- text-align: left;
312
- vertical-align: middle;
313
- font-weight: 500;
314
- font-size: 0.75rem;
315
- text-transform: uppercase;
316
- letter-spacing: 0.05em;
317
- color: hsl(var(--muted-foreground));
318
- white-space: nowrap;
319
- }
320
- table.sqd-table tbody tr { border-bottom: 1px solid hsl(var(--border)); transition: background-color 150ms; }
321
- table.sqd-table tbody tr:hover { background-color: hsl(var(--muted) / 0.5); }
322
- table.sqd-table td { padding: 0.75rem 1rem; vertical-align: middle; }
323
-
324
- /* Info Lines */
325
- .info-line {
326
- display: flex;
327
- align-items: center;
328
- gap: 1rem;
329
- padding: 0.5rem 0;
330
- }
331
- .info-line-label {
332
- font-size: 0.875rem;
333
- color: hsl(var(--muted-foreground));
334
- white-space: nowrap;
335
- flex-shrink: 0;
336
- }
337
- .info-line-separator {
338
- flex: 1;
339
- height: 1px;
340
- background-color: hsl(var(--border));
341
- }
342
- .info-line-value {
343
- font-size: 0.875rem;
344
- font-weight: 500;
345
- color: hsl(var(--foreground));
346
- text-align: right;
347
- white-space: nowrap;
348
- }
349
-
350
- /* Alerts / Flash */
351
- .alert {
352
- position: relative;
353
- display: flex;
354
- align-items: center;
355
- gap: 0.75rem;
356
- padding: 1rem;
357
- border-radius: var(--radius);
358
- border: 1px solid;
359
- font-size: 0.875rem;
360
- }
361
- .alert-success {
362
- border-color: rgb(34 197 94 / 0.3);
363
- background-color: rgb(34 197 94 / 0.05);
364
- color: rgb(22 163 74);
365
- }
366
- .dark .alert-success { color: rgb(134 239 172); background-color: rgb(34 197 94 / 0.1); }
367
- .alert-error {
368
- border-color: rgb(239 68 68 / 0.3);
369
- background-color: rgb(239 68 68 / 0.05);
370
- color: rgb(220 38 38);
371
- }
372
- .dark .alert-error { color: rgb(252 165 165); background-color: rgb(239 68 68 / 0.1); }
373
- .alert-warning {
374
- border-color: rgb(251 191 36 / 0.3);
375
- background-color: rgb(251 191 36 / 0.05);
376
- color: rgb(217 119 6);
377
- }
378
- .dark .alert-warning { color: rgb(252 211 77); background-color: rgb(251 191 36 / 0.1); }
379
- .alert-info {
380
- border-color: rgb(14 165 233 / 0.3);
381
- background-color: rgb(14 165 233 / 0.05);
382
- color: rgb(2 132 199);
383
- }
384
- .dark .alert-info { color: rgb(125 211 252); background-color: rgb(14 165 233 / 0.1); }
385
-
386
- /* Pagination */
387
- .pagination-nav { display: flex; align-items: center; justify-content: center; gap: 0.25rem; }
388
- .pagination-link {
389
- display: inline-flex;
390
- align-items: center;
391
- justify-content: center;
392
- min-width: 2rem;
393
- height: 2rem;
394
- padding: 0 0.5rem;
395
- font-size: 0.875rem;
396
- font-weight: 500;
397
- color: hsl(var(--muted-foreground));
398
- text-decoration: none;
399
- border-radius: calc(var(--radius) - 2px);
400
- transition: background-color 150ms;
401
- }
402
- .pagination-link:hover { background-color: hsl(var(--accent)); color: hsl(var(--foreground)); }
403
- .pagination-active {
404
- background-color: hsl(var(--primary));
405
- color: hsl(var(--primary-foreground));
406
- border: 1px solid hsl(var(--primary));
407
- }
408
- .pagination-disabled { opacity: 0.5; cursor: not-allowed; }
409
-
410
- /* Link utility */
411
- .link { color: hsl(var(--foreground)); text-decoration: underline; text-underline-offset: 4px; }
412
- .link:hover { opacity: 0.75; }
413
-
414
- /* Chart */
415
- .sqd-chart { position: relative; width: 100%; height: 200px; }
416
- .sqd-chart canvas { width: 100% !important; height: 100% !important; }
417
-
418
- /* Code blocks */
419
- .code-block {
420
- font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
421
- font-size: 0.8125rem;
422
- background-color: hsl(var(--muted));
423
- padding: 0.75rem;
424
- border-radius: calc(var(--radius) - 2px);
425
- overflow-x: auto;
426
- white-space: pre-wrap;
427
- word-break: break-word;
428
- }
429
-
430
- /* Stat card */
431
- .stat-card {
432
- border-radius: var(--radius);
433
- border: 1px solid hsl(var(--border));
434
- background-color: hsl(var(--card));
435
- padding: 1.5rem;
436
- display: flex;
437
- flex-direction: column;
438
- gap: 0.25rem;
439
- text-decoration: none;
440
- transition: border-color 150ms;
441
- }
442
- a.stat-card:hover { border-color: hsl(var(--foreground) / 0.2); }
443
- .stat-card-label {
444
- font-size: 0.875rem;
445
- font-weight: 500;
446
- color: hsl(var(--muted-foreground));
447
- display: flex;
448
- align-items: center;
449
- gap: 0.5rem;
450
- }
451
- .stat-card-value {
452
- font-size: 2.25rem;
453
- font-weight: 700;
454
- line-height: 1;
455
- color: hsl(var(--foreground));
456
- }
457
-
458
- /* Utility: sqd-rate badge colors (reused from failure stats) */
459
- .sqd-rate-low { background-color: rgb(34 197 94 / 0.1); color: rgb(22 163 74); }
460
- .dark .sqd-rate-low { background-color: rgb(34 197 94 / 0.15); color: rgb(134 239 172); }
461
- .sqd-rate-medium { background-color: rgb(251 191 36 / 0.1); color: rgb(217 119 6); }
462
- .dark .sqd-rate-medium { background-color: rgb(251 191 36 / 0.15); color: rgb(252 211 77); }
463
- .sqd-rate-high { background-color: rgb(239 68 68 / 0.1); color: rgb(220 38 38); }
464
- .dark .sqd-rate-high { background-color: rgb(239 68 68 / 0.15); color: rgb(252 165 165); }
465
-
466
- /* Legacy compat: sqd- prefixed classes used by existing tests */
467
- .sqd-nav-link { /* NavLink uses this class */ }
468
- .sqd-nav-badge { /* NavLink badge class */ }
469
- .sqd-theme-toggle { /* ThemeToggle uses this class */ }
470
- .sqd-theme-icon-sun, .sqd-theme-icon-moon { /* Theme icons */ }
471
- .sqd-refresh-selector { /* RefreshSelector wrapper */ }
472
- .sqd-refresh-select { /* RefreshSelector select */ }
473
- .sqd-per-page-selector { /* PerPageSelector wrapper */ }
474
- .sqd-per-page-select { /* PerPageSelector select */ }
475
- .sqd-filters { /* JobFilters wrapper */ }
476
- .sqd-filter-select { /* JobFilters select */ }
477
- .sqd-failure-rates { /* FailureRates wrapper */ }
478
- .sqd-failure-table { /* FailureRates table */ }
479
- .sqd-retry-badge {
480
- padding: 0.125rem 0.375rem;
481
- font-size: 0.6875rem;
482
- font-weight: 600;
483
- border-radius: calc(var(--radius) - 4px);
484
- background-color: rgb(251 191 36 / 0.15);
485
- color: rgb(217 119 6);
486
- }
487
- .dark .sqd-retry-badge { color: rgb(252 211 77); }
488
-
489
- /* Dark theme icon visibility */
490
- html:not(.dark) .sqd-theme-icon-sun { display: none; }
491
- html:not(.dark) .sqd-theme-icon-moon { display: block; }
492
- html.dark .sqd-theme-icon-sun { display: block; }
493
- html.dark .sqd-theme-icon-moon { display: none; }
494
- </style>
9
+ <%= stylesheet_link_tag "job_harbor/application", media: "all" %>
495
10
  </head>
496
11
  <body class="min-h-screen">
497
12
  <%# === Top Navbar === %>
@@ -1,3 +1,3 @@
1
1
  module JobHarbor
2
- VERSION = "0.5.1"
2
+ VERSION = "0.6.0"
3
3
  end
@@ -0,0 +1,129 @@
1
+ namespace :job_harbor do
2
+ namespace :tailwind do
3
+ TAILWIND_VERSION = "v4.1.3"
4
+
5
+ def tailwind_platform
6
+ cpu = RbConfig::CONFIG["host_cpu"]
7
+ os = RbConfig::CONFIG["host_os"]
8
+
9
+ case os
10
+ when /darwin/i
11
+ cpu.match?(/arm|aarch64/) ? "macos-arm64" : "macos-x64"
12
+ when /linux/i
13
+ cpu.match?(/arm|aarch64/) ? "linux-arm64" : "linux-x64"
14
+ when /mingw|mswin/i
15
+ "windows-x64.exe"
16
+ else
17
+ abort "Unsupported platform: #{os} #{cpu}"
18
+ end
19
+ end
20
+
21
+ def vendor_dir
22
+ File.join(engine_root, "vendor/tailwindcss")
23
+ end
24
+
25
+ def tailwind_exe
26
+ exe = File.join(vendor_dir, "tailwindcss")
27
+ exe += ".exe" if Gem.win_platform?
28
+ exe
29
+ end
30
+
31
+ def engine_root
32
+ File.expand_path("../..", __dir__)
33
+ end
34
+
35
+ def input_css
36
+ File.join(engine_root, "app/assets/stylesheets/job_harbor/application.tailwind.css")
37
+ end
38
+
39
+ def output_css
40
+ File.join(engine_root, "app/assets/stylesheets/job_harbor/application.css")
41
+ end
42
+
43
+ desc "Download Tailwind CSS standalone CLI"
44
+ task :install do
45
+ require "fileutils"
46
+ require "net/http"
47
+ require "uri"
48
+
49
+ platform = tailwind_platform
50
+ filename = "tailwindcss-#{platform}"
51
+ url = "https://github.com/tailwindlabs/tailwindcss/releases/download/#{TAILWIND_VERSION}/#{filename}"
52
+
53
+ FileUtils.mkdir_p(vendor_dir)
54
+ target = tailwind_exe
55
+
56
+ puts "Downloading Tailwind CSS #{TAILWIND_VERSION} for #{platform}..."
57
+ puts " From: #{url}"
58
+ puts " To: #{target}"
59
+
60
+ download_with_redirects(url, target)
61
+
62
+ FileUtils.chmod(0o755, target)
63
+ puts "Tailwind CSS installed successfully!"
64
+ end
65
+
66
+ desc "Compile Tailwind CSS (minified)"
67
+ task :build do
68
+ ensure_cli_installed!
69
+
70
+ puts "Building Tailwind CSS..."
71
+ system(
72
+ tailwind_exe,
73
+ "--input", input_css,
74
+ "--output", output_css,
75
+ "--minify",
76
+ "--cwd", engine_root,
77
+ exception: true
78
+ )
79
+ puts "Built: #{output_css}"
80
+ end
81
+
82
+ desc "Watch and compile Tailwind CSS (development)"
83
+ task :watch do
84
+ ensure_cli_installed!
85
+
86
+ puts "Watching Tailwind CSS for changes..."
87
+ system(
88
+ tailwind_exe,
89
+ "--input", input_css,
90
+ "--output", output_css,
91
+ "--watch",
92
+ "--cwd", engine_root,
93
+ exception: true
94
+ )
95
+ end
96
+
97
+ private
98
+
99
+ def ensure_cli_installed!
100
+ return if File.executable?(tailwind_exe)
101
+
102
+ abort <<~MSG
103
+ Tailwind CSS CLI not found at #{tailwind_exe}
104
+ Run: rake job_harbor:tailwind:install
105
+ MSG
106
+ end
107
+
108
+ def download_with_redirects(url, target, limit = 5)
109
+ abort "Too many redirects" if limit == 0
110
+
111
+ uri = URI.parse(url)
112
+ Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == "https") do |http|
113
+ request = Net::HTTP::Get.new(uri)
114
+ http.request(request) do |response|
115
+ case response
116
+ when Net::HTTPRedirection
117
+ download_with_redirects(response["location"], target, limit - 1)
118
+ when Net::HTTPSuccess
119
+ File.open(target, "wb") do |file|
120
+ response.read_body { |chunk| file.write(chunk) }
121
+ end
122
+ else
123
+ abort "Download failed: #{response.code} #{response.message}"
124
+ end
125
+ end
126
+ end
127
+ end
128
+ end
129
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: job_harbor
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.1
4
+ version: 0.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Evan Sparkman
@@ -62,7 +62,8 @@ extra_rdoc_files: []
62
62
  files:
63
63
  - README.md
64
64
  - Rakefile
65
- - app/assets/stylesheets/solidqueue_dashboard/application.css
65
+ - app/assets/stylesheets/job_harbor/application.css
66
+ - app/assets/stylesheets/job_harbor/application.tailwind.css
66
67
  - app/components/job_harbor/application_component.rb
67
68
  - app/components/job_harbor/badge_component.rb
68
69
  - app/components/job_harbor/chart_component.rb
@@ -106,6 +107,7 @@ files:
106
107
  - lib/job_harbor/engine.rb
107
108
  - lib/job_harbor/version.rb
108
109
  - lib/tasks/solidqueue_dashboard_tasks.rake
110
+ - lib/tasks/tailwind.rake
109
111
  homepage: https://github.com/esparkman/job_harbor
110
112
  licenses:
111
113
  - MIT
@@ -1 +0,0 @@
1
- /* Solid Queue Dashboard styles are inlined in the layout for self-containment */