diff --git a/pkg/console/assets/css/output.css b/pkg/console/assets/css/output.css index e28294cc..3f319537 100644 --- a/pkg/console/assets/css/output.css +++ b/pkg/console/assets/css/output.css @@ -1,4 +1,4 @@ -/*! tailwindcss v4.1.8 | MIT License | https://tailwindcss.com */ +/*! tailwindcss v4.1.14 | MIT License | https://tailwindcss.com */ @layer properties; @layer theme, base, components, utilities; @layer theme { @@ -12,7 +12,6 @@ --color-red-400: oklch(70.4% 0.191 22.216); --color-red-500: oklch(63.7% 0.237 25.331); --color-red-600: oklch(57.7% 0.245 27.325); - --color-red-700: oklch(50.5% 0.213 27.518); --color-red-800: oklch(44.4% 0.177 26.899); --color-red-900: oklch(39.6% 0.141 25.723); --color-orange-100: oklch(95.4% 0.038 75.164); @@ -61,7 +60,7 @@ --color-gray-700: oklch(37.3% 0.034 259.733); --color-gray-800: oklch(27.8% 0.033 256.848); --color-gray-900: oklch(21% 0.034 264.665); - --color-gray-950: oklch(13% 0.028 261.692); + --color-stone-700: oklch(37.4% 0.01 67.558); --color-black: #000; --color-white: #fff; --spacing: 0.25rem; @@ -88,7 +87,6 @@ --font-weight-semibold: 600; --font-weight-bold: 700; --tracking-wide: 0.025em; - --tracking-wider: 0.05em; --leading-relaxed: 1.625; --radius-sm: 0.25rem; --radius-md: 0.375rem; @@ -235,6 +233,9 @@ ::-webkit-datetime-edit, ::-webkit-datetime-edit-year-field, ::-webkit-datetime-edit-month-field, ::-webkit-datetime-edit-day-field, ::-webkit-datetime-edit-hour-field, ::-webkit-datetime-edit-minute-field, ::-webkit-datetime-edit-second-field, ::-webkit-datetime-edit-millisecond-field, ::-webkit-datetime-edit-meridiem-field { padding-block: 0; } + ::-webkit-calendar-picker-indicator { + line-height: 1; + } :-moz-ui-invalid { box-shadow: none; } @@ -258,9 +259,6 @@ .absolute { position: absolute; } - .fixed { - position: fixed; - } .relative { position: relative; } @@ -273,9 +271,6 @@ .top-0 { top: calc(var(--spacing) * 0); } - .top-1 { - top: calc(var(--spacing) * 1); - } .top-1\/2 { top: calc(1/2 * 100%); } @@ -300,9 +295,6 @@ .bottom-full { bottom: 100%; } - .left-1 { - left: calc(var(--spacing) * 1); - } .left-1\/2 { left: calc(1/2 * 100%); } @@ -399,6 +391,9 @@ .hidden { display: none; } + .inline { + display: inline; + } .inline-block { display: inline-block; } @@ -423,9 +418,6 @@ .h-5 { height: calc(var(--spacing) * 5); } - .h-6 { - height: calc(var(--spacing) * 6); - } .h-8 { height: calc(var(--spacing) * 8); } @@ -450,9 +442,6 @@ .w-0 { width: calc(var(--spacing) * 0); } - .w-1 { - width: calc(var(--spacing) * 1); - } .w-1\/2 { width: calc(1/2 * 100%); } @@ -510,9 +499,6 @@ .w-10 { width: calc(var(--spacing) * 10); } - .w-11 { - width: calc(var(--spacing) * 11); - } .w-11\/12 { width: calc(11/12 * 100%); } @@ -552,30 +538,16 @@ .flex-none { flex: none; } - .flex-shrink { - flex-shrink: 1; - } .flex-shrink-0 { flex-shrink: 0; } .table-auto { table-layout: auto; } - .border-collapse { - border-collapse: collapse; - } - .-translate-x-1 { - --tw-translate-x: calc(var(--spacing) * -1); - translate: var(--tw-translate-x) var(--tw-translate-y); - } .-translate-x-1\/2 { --tw-translate-x: calc(calc(1/2 * 100%) * -1); translate: var(--tw-translate-x) var(--tw-translate-y); } - .-translate-y-1 { - --tw-translate-y: calc(var(--spacing) * -1); - translate: var(--tw-translate-x) var(--tw-translate-y); - } .-translate-y-1\/2 { --tw-translate-y: calc(calc(1/2 * 100%) * -1); translate: var(--tw-translate-x) var(--tw-translate-y); @@ -604,9 +576,6 @@ .cursor-pointer { cursor: pointer; } - .resize { - resize: both; - } .grid-cols-1 { grid-template-columns: repeat(1, minmax(0, 1fr)); } @@ -634,6 +603,9 @@ .justify-center { justify-content: center; } + .justify-evenly { + justify-content: space-evenly; + } .gap-1 { gap: calc(var(--spacing) * 1); } @@ -698,13 +670,6 @@ margin-inline-end: calc(calc(var(--spacing) * 1) * calc(1 - var(--tw-space-x-reverse))); } } - .space-x-2 { - :where(& > :not(:last-child)) { - --tw-space-x-reverse: 0; - margin-inline-start: calc(calc(var(--spacing) * 2) * var(--tw-space-x-reverse)); - margin-inline-end: calc(calc(var(--spacing) * 2) * calc(1 - var(--tw-space-x-reverse))); - } - } .space-x-3 { :where(& > :not(:last-child)) { --tw-space-x-reverse: 0; @@ -719,13 +684,6 @@ margin-inline-end: calc(calc(var(--spacing) * 4) * calc(1 - var(--tw-space-x-reverse))); } } - .space-x-6 { - :where(& > :not(:last-child)) { - --tw-space-x-reverse: 0; - margin-inline-start: calc(calc(var(--spacing) * 6) * var(--tw-space-x-reverse)); - margin-inline-end: calc(calc(var(--spacing) * 6) * calc(1 - var(--tw-space-x-reverse))); - } - } .truncate { overflow: hidden; text-overflow: ellipsis; @@ -811,18 +769,12 @@ .border-gray-700 { border-color: var(--color-gray-700); } - .border-red-400 { - border-color: var(--color-red-400); - } .border-red-500 { border-color: var(--color-red-500); } .border-transparent { border-color: transparent; } - .border-white { - border-color: var(--color-white); - } .border-white\/20 { border-color: color-mix(in srgb, #fff 20%, transparent); @supports (color: color-mix(in lab, red, red)) { @@ -832,9 +784,6 @@ .border-t-gray-900 { border-top-color: var(--color-gray-900); } - .bg-black { - background-color: var(--color-black); - } .bg-black\/20 { background-color: color-mix(in srgb, #000 20%, transparent); @supports (color: color-mix(in lab, red, red)) { @@ -914,27 +863,15 @@ --tw-gradient-position: to left in oklab; background-image: linear-gradient(var(--tw-gradient-stops)); } - .bg-gradient-to-r { - --tw-gradient-position: to right in oklab; - background-image: linear-gradient(var(--tw-gradient-stops)); - } .from-gray-50 { --tw-gradient-from: var(--color-gray-50); --tw-gradient-stops: var(--tw-gradient-via-stops, var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position)); } - .from-white { - --tw-gradient-from: var(--color-white); - --tw-gradient-stops: var(--tw-gradient-via-stops, var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position)); - } .via-gray-50 { --tw-gradient-via: var(--color-gray-50); --tw-gradient-via-stops: var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-via) var(--tw-gradient-via-position), var(--tw-gradient-to) var(--tw-gradient-to-position); --tw-gradient-stops: var(--tw-gradient-via-stops); } - .to-gray-100 { - --tw-gradient-to: var(--color-gray-100); - --tw-gradient-stops: var(--tw-gradient-via-stops, var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position)); - } .to-transparent { --tw-gradient-to: transparent; --tw-gradient-stops: var(--tw-gradient-via-stops, var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position)); @@ -969,9 +906,6 @@ .px-30 { padding-inline: calc(var(--spacing) * 30); } - .py-0 { - padding-block: calc(var(--spacing) * 0); - } .py-0\.5 { padding-block: calc(var(--spacing) * 0.5); } @@ -1011,9 +945,6 @@ .pl-6 { padding-left: calc(var(--spacing) * 6); } - .pl-8 { - padding-left: calc(var(--spacing) * 8); - } .pl-12 { padding-left: calc(var(--spacing) * 12); } @@ -1142,9 +1073,6 @@ .text-red-600 { color: var(--color-red-600); } - .text-red-700 { - color: var(--color-red-700); - } .text-red-800 { color: var(--color-red-800); } @@ -1187,9 +1115,9 @@ --tw-shadow: 0 20px 25px -5px var(--tw-shadow-color, rgb(0 0 0 / 0.1)), 0 8px 10px -6px var(--tw-shadow-color, rgb(0 0 0 / 0.1)); box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow); } - .outline { - outline-style: var(--tw-outline-style); - outline-width: 1px; + .invert { + --tw-invert: invert(100%); + 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,); } .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,); @@ -1204,10 +1132,6 @@ -webkit-backdrop-filter: var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,); backdrop-filter: var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,); } - .backdrop-filter { - -webkit-backdrop-filter: var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,); - backdrop-filter: var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,); - } .transition-all { transition-property: all; transition-timing-function: var(--tw-ease, var(--default-transition-timing-function)); @@ -1423,17 +1347,17 @@ } } .dark\:border-gray-600 { - @media (prefers-color-scheme: dark) { + &:is(.dark, .dark *) { border-color: var(--color-gray-600); } } .dark\:border-gray-700 { - @media (prefers-color-scheme: dark) { + &:is(.dark, .dark *) { border-color: var(--color-gray-700); } } .dark\:border-gray-700\/50 { - @media (prefers-color-scheme: dark) { + &:is(.dark, .dark *) { border-color: color-mix(in srgb, oklch(37.3% 0.034 259.733) 50%, transparent); @supports (color: color-mix(in lab, red, red)) { border-color: color-mix(in oklab, var(--color-gray-700) 50%, transparent); @@ -1441,195 +1365,171 @@ } } .dark\:border-red-500 { - @media (prefers-color-scheme: dark) { + &:is(.dark, .dark *) { border-color: var(--color-red-500); } } .dark\:border-t-gray-700 { - @media (prefers-color-scheme: dark) { + &:is(.dark, .dark *) { border-top-color: var(--color-gray-700); } } - .dark\:bg-black { - @media (prefers-color-scheme: dark) { - background-color: var(--color-black); - } - } - .dark\:bg-blue-900 { - @media (prefers-color-scheme: dark) { - background-color: var(--color-blue-900); - } - } - .dark\:bg-gray-100 { - @media (prefers-color-scheme: dark) { - background-color: var(--color-gray-100); - } - } - .dark\:bg-gray-600 { - @media (prefers-color-scheme: dark) { - background-color: var(--color-gray-600); - } - } - .dark\:bg-gray-700 { - @media (prefers-color-scheme: dark) { - background-color: var(--color-gray-700); + .dark\:bg-\[\#000000\] { + &:is(.dark, .dark *) { + background-color: #000000; } } - .dark\:bg-gray-800 { - @media (prefers-color-scheme: dark) { - background-color: var(--color-gray-800); + .dark\:bg-\[\#141414\] { + &:is(.dark, .dark *) { + background-color: #141414; } } - .dark\:bg-gray-800\/90 { - @media (prefers-color-scheme: dark) { - background-color: color-mix(in srgb, oklch(27.8% 0.033 256.848) 90%, transparent); - @supports (color: color-mix(in lab, red, red)) { - background-color: color-mix(in oklab, var(--color-gray-800) 90%, transparent); - } + .dark\:bg-\[\#141414\]\/90 { + &:is(.dark, .dark *) { + background-color: color-mix(in oklab, #141414 90%, transparent); } } - .dark\:bg-gray-900 { - @media (prefers-color-scheme: dark) { - background-color: var(--color-gray-900); + .dark\:bg-blue-900 { + &:is(.dark, .dark *) { + background-color: var(--color-blue-900); } } - .dark\:bg-gray-950 { - @media (prefers-color-scheme: dark) { - background-color: var(--color-gray-950); + .dark\:bg-gray-100 { + &:is(.dark, .dark *) { + background-color: var(--color-gray-100); } } .dark\:bg-green-900 { - @media (prefers-color-scheme: dark) { + &:is(.dark, .dark *) { background-color: var(--color-green-900); } } .dark\:bg-orange-900 { - @media (prefers-color-scheme: dark) { + &:is(.dark, .dark *) { background-color: var(--color-orange-900); } } .dark\:bg-purple-900 { - @media (prefers-color-scheme: dark) { + &:is(.dark, .dark *) { background-color: var(--color-purple-900); } } .dark\:bg-red-900 { - @media (prefers-color-scheme: dark) { + &:is(.dark, .dark *) { background-color: var(--color-red-900); } } + .dark\:bg-stone-700 { + &:is(.dark, .dark *) { + background-color: var(--color-stone-700); + } + } .dark\:from-gray-800 { - @media (prefers-color-scheme: dark) { + &:is(.dark, .dark *) { --tw-gradient-from: var(--color-gray-800); --tw-gradient-stops: var(--tw-gradient-via-stops, var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position)); } } .dark\:via-gray-800 { - @media (prefers-color-scheme: dark) { + &:is(.dark, .dark *) { --tw-gradient-via: var(--color-gray-800); --tw-gradient-via-stops: var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-via) var(--tw-gradient-via-position), var(--tw-gradient-to) var(--tw-gradient-to-position); --tw-gradient-stops: var(--tw-gradient-via-stops); } } - .dark\:to-gray-900 { - @media (prefers-color-scheme: dark) { - --tw-gradient-to: var(--color-gray-900); - --tw-gradient-stops: var(--tw-gradient-via-stops, var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position)); - } - } .dark\:\!text-gray-400 { - @media (prefers-color-scheme: dark) { + &:is(.dark, .dark *) { color: var(--color-gray-400) !important; } } .dark\:text-blue-300 { - @media (prefers-color-scheme: dark) { + &:is(.dark, .dark *) { color: var(--color-blue-300); } } .dark\:text-gray-100 { - @media (prefers-color-scheme: dark) { + &:is(.dark, .dark *) { color: var(--color-gray-100); } } .dark\:text-gray-200 { - @media (prefers-color-scheme: dark) { + &:is(.dark, .dark *) { color: var(--color-gray-200); } } .dark\:text-gray-300 { - @media (prefers-color-scheme: dark) { + &:is(.dark, .dark *) { color: var(--color-gray-300); } } .dark\:text-gray-400 { - @media (prefers-color-scheme: dark) { + &:is(.dark, .dark *) { color: var(--color-gray-400); } } .dark\:text-gray-500 { - @media (prefers-color-scheme: dark) { + &:is(.dark, .dark *) { color: var(--color-gray-500); } } .dark\:text-gray-600 { - @media (prefers-color-scheme: dark) { + &:is(.dark, .dark *) { color: var(--color-gray-600); } } .dark\:text-gray-900 { - @media (prefers-color-scheme: dark) { + &:is(.dark, .dark *) { color: var(--color-gray-900); } } .dark\:text-green-200 { - @media (prefers-color-scheme: dark) { + &:is(.dark, .dark *) { color: var(--color-green-200); } } .dark\:text-green-400 { - @media (prefers-color-scheme: dark) { + &:is(.dark, .dark *) { color: var(--color-green-400); } } .dark\:text-orange-200 { - @media (prefers-color-scheme: dark) { + &:is(.dark, .dark *) { color: var(--color-orange-200); } } .dark\:text-purple-200 { - @media (prefers-color-scheme: dark) { + &:is(.dark, .dark *) { color: var(--color-purple-200); } } .dark\:text-purple-400 { - @media (prefers-color-scheme: dark) { + &:is(.dark, .dark *) { color: var(--color-purple-400); } } .dark\:text-red-200 { - @media (prefers-color-scheme: dark) { + &:is(.dark, .dark *) { color: var(--color-red-200); } } .dark\:text-red-400 { - @media (prefers-color-scheme: dark) { + &:is(.dark, .dark *) { color: var(--color-red-400); } } .dark\:text-yellow-400 { - @media (prefers-color-scheme: dark) { + &:is(.dark, .dark *) { color: var(--color-yellow-400); } } - .dark\:invert { - @media (prefers-color-scheme: dark) { - --tw-invert: invert(100%); + .dark\:invert-0 { + &:is(.dark, .dark *) { + --tw-invert: invert(0%); 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,); } } .dark\:hover\:\!bg-purple-700 { - @media (prefers-color-scheme: dark) { + &:is(.dark, .dark *) { &:hover { @media (hover: hover) { background-color: var(--color-purple-700) !important; @@ -1638,7 +1538,7 @@ } } .dark\:hover\:bg-gray-600 { - @media (prefers-color-scheme: dark) { + &:is(.dark, .dark *) { &:hover { @media (hover: hover) { background-color: var(--color-gray-600); @@ -1647,7 +1547,7 @@ } } .dark\:hover\:bg-gray-700 { - @media (prefers-color-scheme: dark) { + &:is(.dark, .dark *) { &:hover { @media (hover: hover) { background-color: var(--color-gray-700); @@ -1655,8 +1555,17 @@ } } } + .dark\:hover\:bg-gray-800 { + &:is(.dark, .dark *) { + &:hover { + @media (hover: hover) { + background-color: var(--color-gray-800); + } + } + } + } .dark\:hover\:bg-purple-700 { - @media (prefers-color-scheme: dark) { + &:is(.dark, .dark *) { &:hover { @media (hover: hover) { background-color: var(--color-purple-700); @@ -1664,8 +1573,17 @@ } } } + .dark\:hover\:bg-stone-700 { + &:is(.dark, .dark *) { + &:hover { + @media (hover: hover) { + background-color: var(--color-stone-700); + } + } + } + } .dark\:hover\:\!text-gray-300 { - @media (prefers-color-scheme: dark) { + &:is(.dark, .dark *) { &:hover { @media (hover: hover) { color: var(--color-gray-300) !important; @@ -1674,7 +1592,7 @@ } } .dark\:hover\:text-purple-300 { - @media (prefers-color-scheme: dark) { + &:is(.dark, .dark *) { &:hover { @media (hover: hover) { color: var(--color-purple-300); @@ -1683,14 +1601,14 @@ } } .dark\:focus\:border-purple-500 { - @media (prefers-color-scheme: dark) { + &:is(.dark, .dark *) { &:focus { border-color: var(--color-purple-500); } } } .dark\:focus\:border-red-500 { - @media (prefers-color-scheme: dark) { + &:is(.dark, .dark *) { &:focus { border-color: var(--color-red-500); } @@ -1927,11 +1845,6 @@ inherits: false; initial-value: 0 0 #0000; } -@property --tw-outline-style { - syntax: "*"; - inherits: false; - initial-value: solid; -} @property --tw-blur { syntax: "*"; inherits: false; @@ -2094,7 +2007,6 @@ --tw-ring-offset-width: 0px; --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; - --tw-outline-style: solid; --tw-blur: initial; --tw-brightness: initial; --tw-contrast: initial; diff --git a/pkg/console/assets/images/Glyph_Black.svg b/pkg/console/assets/images/Glyph_Black.svg deleted file mode 100755 index d2dd14a7..00000000 --- a/pkg/console/assets/images/Glyph_Black.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/pkg/console/assets/images/OAPLogo.svg b/pkg/console/assets/images/OAPLogo.svg new file mode 100644 index 00000000..7fe4c586 --- /dev/null +++ b/pkg/console/assets/images/OAPLogo.svg @@ -0,0 +1,3 @@ + + + diff --git a/pkg/console/assets/images/favicon.png b/pkg/console/assets/images/favicon.png new file mode 100644 index 00000000..0bdfdbc7 Binary files /dev/null and b/pkg/console/assets/images/favicon.png differ diff --git a/pkg/console/assets/input.css b/pkg/console/assets/input.css index 1f8f3b93..39e6667b 100644 --- a/pkg/console/assets/input.css +++ b/pkg/console/assets/input.css @@ -1,5 +1,7 @@ @import "tailwindcss"; +@variant dark (&:is(.dark, .dark *)); + /* JSON Syntax Highlighting */ .json-key { @apply text-blue-600 font-semibold; diff --git a/pkg/console/assets/js/theme.js b/pkg/console/assets/js/theme.js new file mode 100644 index 00000000..01d4b286 --- /dev/null +++ b/pkg/console/assets/js/theme.js @@ -0,0 +1,30 @@ +function themeToggle() { + return { + theme: 'light', + + init() { + console.log('Theme toggle initialized'); + // Read current state from document (set by inline script) + const isDark = document.documentElement.classList.contains('dark'); + this.theme = isDark ? 'dark' : 'light'; + console.log('Current theme:', this.theme); + }, + + toggleTheme() { + console.log('Toggle clicked, current theme:', this.theme); + this.theme = this.theme === 'light' ? 'dark' : 'light'; + console.log('New theme:', this.theme); + localStorage.setItem('theme', this.theme); + this.applyTheme(); + }, + + applyTheme() { + console.log('Applying theme:', this.theme); + if (this.theme === 'dark') { + document.documentElement.classList.add('dark'); + } else { + document.documentElement.classList.remove('dark'); + } + } + } +} diff --git a/pkg/console/cache.go b/pkg/console/cache.go new file mode 100644 index 00000000..c6c786ee --- /dev/null +++ b/pkg/console/cache.go @@ -0,0 +1,79 @@ +package console + +import ( + "context" + "sync" + "time" + + "github.com/AudiusProject/audiusd/pkg/common" +) + +// Cache is a generic thread-safe cache that periodically refreshes data in the background +type Cache[T any] struct { + mu sync.RWMutex + data T + lastUpdated time.Time + updateFunc func(context.Context) (T, error) + refreshRate time.Duration + logger *common.Logger +} + +// NewCache creates a new cache with the given update function and refresh rate +func NewCache[T any](updateFunc func(context.Context) (T, error), refreshRate time.Duration, logger *common.Logger) *Cache[T] { + return &Cache[T]{ + updateFunc: updateFunc, + refreshRate: refreshRate, + logger: logger, + } +} + +// Get returns the cached data (thread-safe read) +func (c *Cache[T]) Get() T { + c.mu.RLock() + defer c.mu.RUnlock() + return c.data +} + +// GetLastUpdated returns when the cache was last updated +func (c *Cache[T]) GetLastUpdated() time.Time { + c.mu.RLock() + defer c.mu.RUnlock() + return c.lastUpdated +} + +// StartRefresh begins the background refresh loop +// Should be called in a goroutine +func (c *Cache[T]) StartRefresh(ctx context.Context) { + ticker := time.NewTicker(c.refreshRate) + defer ticker.Stop() + + // Do initial refresh immediately (non-blocking) + c.refresh(ctx) + + for { + select { + case <-ctx.Done(): + c.logger.Info("Cache refresh stopped") + return + case <-ticker.C: + c.refresh(ctx) + } + } +} + +// refresh updates the cached data by calling the update function +func (c *Cache[T]) refresh(ctx context.Context) { + start := time.Now() + data, err := c.updateFunc(ctx) + if err != nil { + c.logger.Warn("Cache refresh failed", "error", err, "duration", time.Since(start)) + return + } + + c.mu.Lock() + c.data = data + c.lastUpdated = time.Now() + c.mu.Unlock() + + c.logger.Debug("Cache refreshed", "duration", time.Since(start)) +} diff --git a/pkg/console/cmd/main.go b/pkg/console/cmd/main.go index 5ad7d02d..1e40a01b 100644 --- a/pkg/console/cmd/main.go +++ b/pkg/console/cmd/main.go @@ -1,19 +1,13 @@ package main import ( - "context" - "fmt" "log" - "os" - "path/filepath" - "time" "github.com/AudiusProject/audiusd/pkg/common" "github.com/AudiusProject/audiusd/pkg/console" "github.com/AudiusProject/audiusd/pkg/etl" "github.com/AudiusProject/audiusd/pkg/sdk" - "github.com/testcontainers/testcontainers-go" - "github.com/testcontainers/testcontainers-go/wait" + "go.uber.org/zap" ) func main() { @@ -21,19 +15,14 @@ func main() { logger.Info("Starting Console") - // Start postgres container with volume - dbURL, err := startPostgres(context.Background()) - if err != nil { - log.Fatalf("Failed to start postgres: %s", err) - } + // Connect to postgres from Makefile (port 5444) + dbURL := "postgres://postgres:postgres@0.0.0.0:5444/audiusd?sslmode=disable" logger.Info("pgURL", "url", dbURL) - // wait for postgres to be ready - time.Sleep(5 * time.Second) - auds := sdk.NewAudiusdSDK("rpc.audius.engineering") + auds := sdk.NewAudiusdSDK("creatornode.audius.co") - etl := etl.NewETLService(auds.Core, nil) + etl := etl.NewETLService(auds.Core, zap.NewNop()) etl.SetDBURL(dbURL) etl.SetCheckReadiness(false) @@ -45,52 +34,3 @@ func main() { log.Fatal(err) } } - -func startPostgres(ctx context.Context) (string, error) { - pguser := "postgres" - pgpass := "postgres" - pgdb := "audiusd" - - // Get current working directory and create absolute path for postgres data - cwd, err := os.Getwd() - if err != nil { - return "", fmt.Errorf("failed to get current working directory: %w", err) - } - - postgresDataPath := filepath.Join(cwd, "tmp", "postgres-data") - - // Create the directory if it doesn't exist - if err := os.MkdirAll(postgresDataPath, 0755); err != nil { - return "", fmt.Errorf("failed to create postgres data directory: %w", err) - } - - req := testcontainers.ContainerRequest{ - Image: "postgres:15", - Name: "audiusd-console-postgres", - ExposedPorts: []string{"15432:5432/tcp"}, - Env: map[string]string{ - "POSTGRES_USER": pguser, - "POSTGRES_PASSWORD": pgpass, - "POSTGRES_DB": pgdb, - }, - WaitingFor: wait.ForListeningPort("5432/tcp"), - } - - postgresC, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{ - ContainerRequest: req, - Started: true, - Reuse: true, - }) - if err != nil { - return "", fmt.Errorf("failed to start container: %w", err) - } - - host, err := postgresC.Host(ctx) - if err != nil { - return "", fmt.Errorf("failed to get container host: %w", err) - } - - // Use fixed port 15432 for consistent TablePlus connections - dsn := fmt.Sprintf("postgres://%s:%s@%s:15432/%s?sslmode=disable", pguser, pgpass, host, pgdb) - return dsn, nil -} diff --git a/pkg/console/console.go b/pkg/console/console.go index 3fd08c3f..66640ba8 100644 --- a/pkg/console/console.go +++ b/pkg/console/console.go @@ -44,6 +44,7 @@ type Console struct { latestTrustedBlock atomic.Int64 lastRefreshTime atomic.Int64 // Unix timestamp of last refresh refreshInterval time.Duration // How often to refresh + dashboardCache *Cache[*pages.DashboardProps] } func NewConsole(etl *etl.ETLService, e *echo.Echo, env string) *Console { @@ -58,11 +59,11 @@ func NewConsole(etl *etl.ETLService, e *echo.Echo, env string) *Console { switch env { case "prod", "production", "mainnet": - trustedNodeURL = "rpc.audius.engineering" + trustedNodeURL = "creatornode.audius.co" case "staging", "stage", "testnet": - trustedNodeURL = "rpc.staging.audius.engineering" + trustedNodeURL = "creatornode11.staging.audius.co" case "dev": - trustedNodeURL = "rpc.dev.audius.engineering" + trustedNodeURL = "node2.oap.devnet" } return &Console{ @@ -76,7 +77,10 @@ func NewConsole(etl *etl.ETLService, e *echo.Echo, env string) *Console { } func (con *Console) Initialize() { - // start refresher + // Initialize dashboard cache with 5 second refresh rate + con.dashboardCache = NewCache(con.buildDashboardProps, 5*time.Second, con.logger.Child("dashboard-cache")) + + // Start background refreshers (but not the dashboard cache yet - that needs ETL to be ready) go con.refreshTrustedBlock() e := con.e @@ -153,7 +157,7 @@ func (con *Console) Initialize() { } func (con *Console) Run() error { - g, _ := errgroup.WithContext(context.Background()) + g, ctx := errgroup.WithContext(context.Background()) g.Go(func() error { info, err := con.trustedNode.Core.GetNodeInfo(context.Background(), &connect.Request[corev1.GetNodeInfoRequest]{}) @@ -189,6 +193,14 @@ func (con *Console) Run() error { return nil }) + // Start dashboard cache refresh after ETL is ready + g.Go(func() error { + // Wait a moment for ETL to initialize + time.Sleep(2 * time.Second) + con.dashboardCache.StartRefresh(ctx) + return nil + }) + g.Go(func() error { if err := con.e.Start(":3000"); err != nil { return err @@ -237,9 +249,9 @@ func (con *Console) Hello(c echo.Context) error { return p.Render(ctx, c.Response().Writer) } -func (con *Console) Dashboard(c echo.Context) error { - ctx := c.Request().Context() - +// buildDashboardProps builds the dashboard props by querying the database +// This is used by the cache to refresh dashboard data periodically +func (con *Console) buildDashboardProps(ctx context.Context) (*pages.DashboardProps, error) { // Get dashboard transaction stats from materialized view txStats, err := con.etl.GetDB().GetDashboardTransactionStats(ctx) if err != nil { @@ -271,20 +283,12 @@ func (con *Console) Dashboard(c echo.Context) error { var blockDelta int64 syncProgressPercentage := float64(100) // Default to fully synced - con.logger.Info("Latest block height", "height", latestBlockHeight) - con.logger.Info("Trusted block height", "height", trustedBlockHeight) - if trustedBlockHeight >= 0 && latestBlockHeight >= 0 { - blockDelta = trustedBlockHeight - latestBlockHeight isSyncing = blockDelta > syncThreshold - con.logger.Info("Block delta", "delta", blockDelta) - con.logger.Info("Is syncing", "isSyncing", isSyncing) - if isSyncing && trustedBlockHeight > 0 { syncProgressPercentage = (float64(latestBlockHeight) / float64(trustedBlockHeight)) * 100 - con.logger.Info("Sync progress percentage", "percentage", syncProgressPercentage) // Ensure percentage doesn't exceed 100 if syncProgressPercentage > 100 { syncProgressPercentage = 100 @@ -293,7 +297,6 @@ func (con *Console) Dashboard(c echo.Context) error { syncProgressPercentage = 100 // Fully synced } } else { - con.logger.Info("No trusted block info, assuming synced") // If we don't have trusted block info, assume synced isSyncing = false blockDelta = 0 @@ -335,7 +338,7 @@ func (con *Console) Dashboard(c echo.Context) error { }) if err != nil { con.logger.Warn("Failed to get blocks", "error", err) - return c.String(http.StatusInternalServerError, "Failed to get blocks") + blocks = []db.EtlBlock{} } blockPointers := make([]*db.EtlBlock, len(blocks)) @@ -398,14 +401,11 @@ func (con *Console) Dashboard(c echo.Context) error { slaRollupsData = []db.EtlSlaRollup{} } - con.logger.Info("SLA rollups data retrieved", "count", len(slaRollupsData)) - // Build SLA performance data points for chart - Initialize as empty slice, not nil slaPerformanceData := make([]*pages.SLAPerformanceDataPoint, 0) // Build chart data if we have any rollups if len(slaRollupsData) > 0 { - con.logger.Info("Building SLA performance chart data", "rollupCount", len(slaRollupsData)) // Extract rollup IDs for healthy validators query rollupIDs := make([]int32, len(slaRollupsData)) @@ -432,50 +432,17 @@ func (con *Console) Dashboard(c echo.Context) error { // Filter out invalid rollups and build valid data points validDataPoints := make([]*pages.SLAPerformanceDataPoint, 0, len(slaRollupsData)) - for i, rollup := range slaRollupsData { - // Log the first rollup to see what data we're getting - if i == 0 { - con.logger.Info("First rollup data sample", - "id", rollup.ID, - "blockHeight", rollup.BlockHeight, - "validatorCount", rollup.ValidatorCount, - "bps", rollup.Bps, - "tps", rollup.Tps, - "createdAtValid", rollup.CreatedAt.Valid, - "blockStart", rollup.BlockStart, - "blockEnd", rollup.BlockEnd) - } - - // Comprehensive validation of rollup data - if rollup.ID <= 0 { - con.logger.Debug("Skipping rollup with invalid ID", "index", i, "rollupId", rollup.ID) - continue - } - - if !rollup.CreatedAt.Valid { - con.logger.Debug("Skipping rollup with invalid timestamp", "rollupId", rollup.ID) - continue - } - - if rollup.BlockHeight <= 0 { - con.logger.Debug("Skipping rollup with invalid block height", "rollupId", rollup.ID, "blockHeight", rollup.BlockHeight) - continue - } - - if rollup.Bps < 0 || rollup.Tps < 0 { - con.logger.Debug("Skipping rollup with invalid performance data", "rollupId", rollup.ID, "bps", rollup.Bps, "tps", rollup.Tps) - continue - } - - if rollup.BlockStart < 0 || rollup.BlockEnd <= 0 || rollup.BlockStart > rollup.BlockEnd { - con.logger.Debug("Skipping rollup with invalid block range", "rollupId", rollup.ID, "start", rollup.BlockStart, "end", rollup.BlockEnd) + for _, rollup := range slaRollupsData { + // Skip invalid rollups + if rollup.ID <= 0 || !rollup.CreatedAt.Valid || rollup.BlockHeight <= 0 || + rollup.Bps < 0 || rollup.Tps < 0 || + rollup.BlockStart < 0 || rollup.BlockEnd <= 0 || rollup.BlockStart > rollup.BlockEnd { continue } // Use the validator count from the rollup data itself validatorCount := rollup.ValidatorCount if validatorCount <= 0 { - con.logger.Debug("Invalid validator count in rollup, using fallback", "rollupId", rollup.ID, "count", validatorCount) validatorCount = 1 // Minimum of 1 validator } @@ -507,25 +474,16 @@ func (con *Console) Dashboard(c echo.Context) error { // Use the data if we have any valid points after filtering if len(validDataPoints) > 0 { slaPerformanceData = validDataPoints - con.logger.Info("Successfully built SLA performance data", "validPoints", len(validDataPoints)) - } else { - con.logger.Warn("No valid rollup data points after filtering", "valid", len(validDataPoints), "total", len(slaRollupsData)) - // Keep the empty slice - don't set to nil } - } else { - con.logger.Info("No rollups available for chart", "rollupCount", len(slaRollupsData)) } - // Final debug of what we're passing to template - con.logger.Info("Final SLA performance data for template", "dataPoints", len(slaPerformanceData)) - // Convert rollups to pointers for template recentSLARollups := make([]*db.EtlSlaRollup, len(slaRollupsData)) for i := range slaRollupsData { recentSLARollups[i] = &slaRollupsData[i] } - props := pages.DashboardProps{ + props := &pages.DashboardProps{ Stats: stats, TransactionBreakdown: transactionBreakdown, RecentBlocks: blockPointers, @@ -536,10 +494,26 @@ func (con *Console) Dashboard(c echo.Context) error { SyncProgressPercentage: syncProgressPercentage, } - p := pages.Dashboard(props) + return props, nil +} - // Use context with environment - return p.Render(ctx, c.Response().Writer) +func (con *Console) Dashboard(c echo.Context) error { + // Get cached dashboard props + props := con.dashboardCache.Get() + + // If cache isn't ready yet, build props on-demand + if props == nil { + var err error + props, err = con.buildDashboardProps(c.Request().Context()) + if err != nil { + con.logger.Error("Failed to build dashboard props", "error", err) + return c.String(http.StatusInternalServerError, "Failed to load dashboard") + } + } + + // Render the dashboard + p := pages.Dashboard(*props) + return p.Render(c.Request().Context(), c.Response().Writer) } func (con *Console) Validators(c echo.Context) error { diff --git a/pkg/console/templates/layouts/base.templ b/pkg/console/templates/layouts/base.templ index 1ed38475..aadcbd53 100644 --- a/pkg/console/templates/layouts/base.templ +++ b/pkg/console/templates/layouts/base.templ @@ -4,18 +4,26 @@ import "fmt" templ Base(pageTitle string) { - { fmt.Sprintf("%s | Audius Explorer", pageTitle) } + { fmt.Sprintf("%s | Open Audio Explorer", pageTitle) } - + + + + - + @Frame(pageTitle) { { children... } } diff --git a/pkg/console/templates/layouts/base_templ.go b/pkg/console/templates/layouts/base_templ.go index 89ae1c1d..0f6c3182 100644 --- a/pkg/console/templates/layouts/base_templ.go +++ b/pkg/console/templates/layouts/base_templ.go @@ -36,15 +36,15 @@ func Base(pageTitle string) templ.Component { return templ_7745c5c3_Err } var templ_7745c5c3_Var2 string - templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%s | Audius Explorer", pageTitle)) + templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%s | Open Audio Explorer", pageTitle)) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/layouts/base.templ`, Line: 7, Col: 57} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/layouts/base.templ`, Line: 7, Col: 61} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var2)) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } diff --git a/pkg/console/templates/layouts/frame.templ b/pkg/console/templates/layouts/frame.templ index 2c15d422..596a0f5e 100644 --- a/pkg/console/templates/layouts/frame.templ +++ b/pkg/console/templates/layouts/frame.templ @@ -11,54 +11,64 @@ func getEnvFromContext(ctx context.Context) string { templ Frame(pageTitle string) {
+
- Audius Logo -
Audius Explorer
+ Audius Logo +
Open Audio Explorer
- -
-
{ pageTitle }
-
-
-
- -
-
-
-
- +
+
+ +
+
+
+ +
+
+
+
+ +
@@ -67,14 +77,22 @@ templ Frame(pageTitle string) { { children... }
-
+
- GitHub - Docs - API + + OpenAudio + GitHub + Docs
- Made with ⚡️ by the Protocol Team + Made with ⚡️ by the OpenAudio Team
diff --git a/pkg/console/templates/layouts/frame_templ.go b/pkg/console/templates/layouts/frame_templ.go index c871140a..db77da51 100644 --- a/pkg/console/templates/layouts/frame_templ.go +++ b/pkg/console/templates/layouts/frame_templ.go @@ -38,33 +38,20 @@ func Frame(pageTitle string) templ.Component { templ_7745c5c3_Var1 = templ.NopComponent } ctx = templ.ClearChildren(ctx) - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "
") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "
0\" @click.away=\"showSuggestions = false\">
") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "\">
0\" @click.away=\"showSuggestions = false\">
") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -72,7 +59,7 @@ func Frame(pageTitle string) templ.Component { if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "
Made with ⚡️ by the Protocol Team
") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "
Made with ⚡️ by the OpenAudio Team
") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } diff --git a/pkg/console/templates/pages/account.templ b/pkg/console/templates/pages/account.templ index 3526fc2b..bb397518 100644 --- a/pkg/console/templates/pages/account.templ +++ b/pkg/console/templates/pages/account.templ @@ -37,7 +37,7 @@ type AccountProps struct { templ Account(props AccountProps) { @layouts.Base(fmt.Sprintf("Account %s", props.Address[:6])) { -
+

Account @@ -49,7 +49,7 @@ templ Account(props AccountProps) { Page { fmt.Sprint(props.CurrentPage) } • { fmt.Sprint(props.PageSize) } transactions per page

-
+
Address
@@ -64,7 +64,7 @@ templ Account(props AccountProps) {
Filter by Relation
To Date
") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 18, "\" class=\"w-full px-3 py-2 text-sm border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-[#141414] text-gray-900 dark:text-gray-100 focus:ring-2 focus:ring-purple-500 focus:border-transparent\" onchange=\"updateFilters()\">
") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -264,7 +264,7 @@ func Account(props AccountProps) templ.Component { if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 22, "
Type
") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 22, "
Type
") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -334,12 +334,12 @@ func Account(props AccountProps) templ.Component { if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 29, "\" class=\"px-4 py-2 text-sm font-medium text-gray-700 dark:text-gray-300 bg-gray-100 dark:bg-gray-700 hover:bg-gray-200 dark:hover:bg-gray-600 rounded transition-colors\">← Previous ") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 29, "\" class=\"px-4 py-2 text-sm font-medium text-gray-700 dark:text-gray-300 bg-gray-100 dark:bg-[#141414] hover:bg-gray-200 dark:hover:bg-gray-600 rounded transition-colors\">← Previous ") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } } else { - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 30, "← Previous ") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 30, "← Previous ") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -358,12 +358,12 @@ func Account(props AccountProps) templ.Component { if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 32, "\" class=\"px-4 py-2 text-sm font-medium text-gray-700 dark:text-gray-300 bg-gray-100 dark:bg-gray-700 hover:bg-gray-200 dark:hover:bg-gray-600 rounded transition-colors\">Next →") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 32, "\" class=\"px-4 py-2 text-sm font-medium text-gray-700 dark:text-gray-300 bg-gray-100 dark:bg-[#141414] hover:bg-gray-200 dark:hover:bg-gray-600 rounded transition-colors\">Next →") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } } else { - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 33, "Next →") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 33, "Next →") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } diff --git a/pkg/console/templates/pages/block.templ b/pkg/console/templates/pages/block.templ index 52fe933b..f29e535a 100644 --- a/pkg/console/templates/pages/block.templ +++ b/pkg/console/templates/pages/block.templ @@ -16,7 +16,7 @@ templ Block(props BlockProps) { @layouts.Base(fmt.Sprintf("Block %d", props.Block.BlockHeight)) {
-
+

Block { fmt.Sprintf("%d", props.Block.BlockHeight) }

@@ -45,7 +45,7 @@ templ Block(props BlockProps) {
-
+

Transactions

@@ -65,7 +65,7 @@ templ Block(props BlockProps) {
Type
- + { tx.TxType }
diff --git a/pkg/console/templates/pages/block_templ.go b/pkg/console/templates/pages/block_templ.go index 5b952d02..6bb8d970 100644 --- a/pkg/console/templates/pages/block_templ.go +++ b/pkg/console/templates/pages/block_templ.go @@ -53,7 +53,7 @@ func Block(props BlockProps) templ.Component { }() } ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "

Block ") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "

Block ") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -126,7 +126,7 @@ func Block(props BlockProps) templ.Component { if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "

Transactions

") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "

Transactions

") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -175,7 +175,7 @@ func Block(props BlockProps) templ.Component { if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, "
Type
") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, "
Type
") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } diff --git a/pkg/console/templates/pages/blocks.templ b/pkg/console/templates/pages/blocks.templ index b9ca48aa..649250bb 100644 --- a/pkg/console/templates/pages/blocks.templ +++ b/pkg/console/templates/pages/blocks.templ @@ -18,7 +18,7 @@ type BlocksProps struct { templ Blocks(props BlocksProps) { @layouts.Base("Blocks") { -
+

Latest Blocks

@@ -63,20 +63,20 @@ templ Blocks(props BlocksProps) {
if props.HasPrev { - + ← Previous } else { - + ← Previous } if props.HasNext { - + Next → } else { - + Next → } diff --git a/pkg/console/templates/pages/blocks_templ.go b/pkg/console/templates/pages/blocks_templ.go index 58aad0d8..629939bc 100644 --- a/pkg/console/templates/pages/blocks_templ.go +++ b/pkg/console/templates/pages/blocks_templ.go @@ -57,7 +57,7 @@ func Blocks(props BlocksProps) templ.Component { }() } ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "

Latest Blocks

Page ") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "

Latest Blocks

Page ") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -176,12 +176,12 @@ func Blocks(props BlocksProps) templ.Component { if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, "\" class=\"px-4 py-2 text-sm font-medium text-gray-700 dark:text-gray-300 bg-gray-100 dark:bg-gray-700 hover:bg-gray-200 dark:hover:bg-gray-600 rounded transition-colors\">← Previous ") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, "\" class=\"px-4 py-2 text-sm font-medium text-gray-700 dark:text-gray-300 bg-gray-100 dark:bg-[#141414] hover:bg-gray-200 dark:hover:bg-gray-600 rounded transition-colors\">← Previous ") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } } else { - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 14, "← Previous ") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 14, "← Previous ") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -200,12 +200,12 @@ func Blocks(props BlocksProps) templ.Component { if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, "\" class=\"px-4 py-2 text-sm font-medium text-gray-700 dark:text-gray-300 bg-gray-100 dark:bg-gray-700 hover:bg-gray-200 dark:hover:bg-gray-600 rounded transition-colors\">Next →") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, "\" class=\"px-4 py-2 text-sm font-medium text-gray-700 dark:text-gray-300 bg-gray-100 dark:bg-[#141414] hover:bg-gray-200 dark:hover:bg-gray-600 rounded transition-colors\">Next →") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } } else { - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 17, "Next →") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 17, "Next →") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } diff --git a/pkg/console/templates/pages/dashboard.templ b/pkg/console/templates/pages/dashboard.templ index 6547ce2f..4300a9c5 100644 --- a/pkg/console/templates/pages/dashboard.templ +++ b/pkg/console/templates/pages/dashboard.templ @@ -150,18 +150,17 @@ templ Dashboard(props DashboardProps) { @layouts.Base("Dashboard") {
-
+
-
-

Block Height

+
+

Block Height

-
-

Chain ID

-

{ props.Stats.ChainID }

-
-
-

Block Time

+
if props.Stats.AvgBlockTime > 0 {

{ fmt.Sprintf("%.2fs", props.Stats.AvgBlockTime) }

} else if props.Stats.BPS > 0 { @@ -193,9 +188,9 @@ templ Dashboard(props DashboardProps) {

-

No data

} +

Block Time

-
-

Sync Status

+
if props.Stats.IsSyncing {
@@ -206,8 +201,8 @@ templ Dashboard(props DashboardProps) {

Synced

-

Up to date

} +

Sync Status

@@ -225,7 +220,7 @@ templ Dashboard(props DashboardProps) { ({ fmt.Sprintf("%.1f%%", props.SyncProgressPercentage) })
-
+
-
+
@@ -260,7 +255,7 @@ templ Dashboard(props DashboardProps) {
-
+

Transaction Analytics

@@ -269,26 +264,26 @@ templ Dashboard(props DashboardProps) {
-
- @TPSFragment(props.Stats) -
@TotalTransactionsFragment(props.Stats)
+
+ @TPSFragment(props.Stats) +
-
+
24h Volume
{ formatNumber(props.Stats.TotalTransactions24h) }
Daily rate
-
+
7d Volume
{ formatNumber(props.Stats.TotalTransactions7d) }
Avg: { formatNumber(props.Stats.TotalTransactions7d / 7) }/day
-
+
30d Volume
{ formatNumber(props.Stats.TotalTransactions30d) }
Avg: { formatNumber(props.Stats.TotalTransactions30d / 30) }/day
@@ -302,7 +297,7 @@ templ Dashboard(props DashboardProps) { if len(props.TransactionBreakdown) > 0 { for _, breakdown := range props.TransactionBreakdown {
- { breakdown.Type } + { breakdown.Type } { formatNumber(breakdown.Count) }
} @@ -315,8 +310,8 @@ templ Dashboard(props DashboardProps) {
if props.SLAPerformanceData != nil && len(props.SLAPerformanceData) > 0 { -
-
+
+
@@ -335,7 +330,7 @@ templ Dashboard(props DashboardProps) {
-
+
LIVE @@ -343,7 +338,7 @@ templ Dashboard(props DashboardProps) {
-
+
-
+

Latest Blocks

View all → @@ -365,14 +360,14 @@ templ Dashboard(props DashboardProps) { if len(props.RecentBlocks) > 0 {
for _, block := range props.RecentBlocks { -
+
-
+

Latest Transactions

View all → @@ -401,20 +396,20 @@ templ Dashboard(props DashboardProps) { if len(props.RecentTransactions) > 0 {
for _, tx := range props.RecentTransactions { -
+
- + if len(tx.TxHash) > 8 { - { tx.TxHash[:4] }...{ tx.TxHash[len(tx.TxHash)-4:] } + { tx.TxHash[:12] }...{ tx.TxHash[len(tx.TxHash)-4:] } } else { { tx.TxHash } } - + { tx.TxType } if blockHeight, exists := props.BlockHeights[tx.TxHash]; exists { - + { fmt.Sprintf("#%d", blockHeight) } } @@ -471,10 +466,6 @@ templ StatsHeaderFragment(stats *DashboardStats, syncProgressPercentage float64)
-
-

Chain ID

-

{ stats.ChainID }

-

Block Time

if stats.AvgBlockTime > 0 { @@ -518,7 +509,7 @@ templ StatsHeaderFragment(stats *DashboardStats, syncProgressPercentage float64) ({ fmt.Sprintf("%.1f%%", syncProgressPercentage) })
-
+
+
Transactions/sec
{ fmt.Sprintf("%.1f", stats.TPS) }
30d Avg: { fmt.Sprintf("%.1f", float64(stats.TotalTransactions30d)/(30*24*60*60)) } TPS
@@ -538,7 +529,7 @@ templ TPSFragment(stats *DashboardStats) { } templ TotalTransactionsFragment(stats *DashboardStats) { -
+
Total Transactions
{ formatNumber(stats.TotalTransactions) }
{ getPercentageChangeText(stats.TotalTransactions24h, stats.TotalTransactionsPrevious24h) }
@@ -1529,7 +1520,7 @@ templ SLAChartScript() { templ NetworkSidebarFragment(stats *DashboardStats) {
-
+

Network Info

@@ -1550,7 +1541,7 @@ templ NetworkSidebarFragment(stats *DashboardStats) {
-
+

Recent Proposers