Last fix made the wave fill #1f4d52 in ultra-dark for both top-three
waves and the bottom wave, which gave visible motion but exposed a
hard horizontal line where the bottom wave's flat lower edge met the
page bg (#0f2d32). The user noticed it as "the wave at the bottom
not moving its like a line" — they were seeing the SVG's clipped
bottom edge, not the wave itself.
Solution: only the top three waves get the brighter fill (those carry
the visible motion). The bottom wave reverts to #0f2d32 = --bg-page,
so its flat bottom edge merges seamlessly into the page below. Net
effect: motion is still visible (from waves 2 and 3), and there's no
seam line at the bottom of the SVG.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Two related fixes:
1. Default-dark wave-header bg was wrong. I had #0a2227, but that's
the *ultra-dark* override; default dark uses --dark-color-background
= #0a1222. Now the dark-mode top half is the legacy purple-blue
instead of teal.
2. Ultra-dark wave fill is intentionally near-identical to its bg in
the legacy palette (#0f2d32 vs #0a2227, ~5/11/11 RGB delta), which
makes the wave look static even though the animation is running.
Bumped --wave-fill / --wave-fill-bottom to #1f4d52 in ultra-dark
only — far enough above the bg that the motion reads, while
staying within the same teal hue family.
Also corrected ultra-dark --bg-page back to #0f2d32 (was briefly
#0c0e12, which is the card color, not the page color).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Earlier I deferred the legacy headline word-cycling animation as
"purely aesthetic". Restored it: the title now alternates between
'Hello' and 'Welcome' every 2 seconds, matching the legacy panel.
The legacy implementation toggled .is-visible / .is-hidden classes on
two <b> elements via setTimeout chains and DOM querying. Replaced
with a reactive ref + Vue 3 <Transition mode="out-in"> so the fade
between words is declarative — no manual DOM manipulation, and the
interval is properly cleaned up in onBeforeUnmount.
The earlier "Welcome to 3x-ui" string was wrong on two counts: it
should be just "Welcome", and it should be one of two cycling words
with "Hello" preceding it.
Ultra-dark palette already matched legacy after the prior wave timing
fix; no additional changes needed there beyond the animation speeds
that now also apply to ultra-dark via the shared CSS rules.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Two reasons the bottom wave looked static in dark/ultra-dark:
1. Animation durations were 7s/10s/13s/20s. Legacy uses 4s/7s/10s/13s.
The 20s on the bottom wave was so slow that against the low dark-
mode contrast it read as motionless. Restored the legacy timings.
2. --bg-page in dark mode was #151f31 (card color / surface-100), but
the legacy .under uses surface-200 (#222d42) — that's the color of
the bottom half of the page, the same as the wave fill, so the
wave appears to flow into the page rather than meeting a hard edge.
Now it does.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Two layering bugs were hiding the wave entirely:
1. .ant-layout-content had background: var(--bg-page) which painted an
opaque rectangle covering the full content area — including the
fixed wave-header behind it. Made the layout/content transparent
and moved the bg paint up to .login-app (the outer ant-layout).
2. .waves-header had z-index: -1 which on its own was fine, but with
.ant-layout-content opaque on top it was doubly buried. Promoted
the wave-header to z-index: 0 and gave the form .login-row
z-index: 1, so the form sits above the wave and the wave sits
above the page-bg.
Also set --bg-page to the legacy mint (#c7ebe2) for light mode so the
bottom half of the page below the wave matches the legacy panel
(was white). Dark mode stays at the surface-100/login-wave palette.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The wave SVG had inline fill="#c7ebe2" (mint) on the bottom wave, so
in dark/ultra-dark mode it rendered as a pale-white blob against the
dark page. Stripped the inline fills, drove them off CSS variables
that swap with .is-dark / .is-ultra:
light: green tints + #c7ebe2 (mint) on the bottom wave
dark: #222d42 across all four waves
ultra-dark: #0f2d32
The wave was also positioned wrong — anchored to the top 200px of
the viewport with absolute positioning. Restored the legacy layout:
- .waves-header is fixed to the top of the viewport with z-index -1
so the form floats over it
- .waves-inner-header pushes the wave SVG down to ~50vh with a
50vh-tall solid block of the page color
- .waves SVG itself is 15vh tall, sitting at the bottom of that block
Net effect: top half is solid-colored, then a wavy edge transitions
into the rest of the page, with the form centered on top — matching
the legacy panel exactly.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Earlier dark mode used invented colors (#141a26 page bg, #1f2937 card)
that didn't match the rest of the panel. Replaced with the actual
values from web/assets/css/custom.min.css:
light dark ultra-dark
bg #c7ebe2 bg #222d42 bg #0f2d32
card #fff card #151f31 card #0c0e12
title #008771 title #fff/.92 title #fff/.92
Drove everything off CSS custom properties on .login-app so the
.is-dark / .is-ultra class swap is a few var overrides instead of
duplicating selectors. Also restored the legacy card metrics
(2rem radius, 4rem 3rem padding, 2rem title) so the new page
matches the old panel's geometry, not just its colors.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Two issues from running login.html against no Go backend:
1. Dark mode toggled the body class but didn't actually re-theme any
AD-Vue components. The legacy panel relied on custom.min.css which
we haven't ported. AD-Vue 4 ships its own dark algorithm — wrap
LoginPage in <a-config-provider :theme="{ algorithm }"> driven by
our useTheme state, and AD-Vue restyles every component for free.
Page chrome (background, card, title) gets explicit .is-dark CSS
since the algorithm only covers AD-Vue components.
2. Vite logged every failed proxy attempt loudly. When the Go panel
isn't running locally that's pure noise. Added a configure()
callback that swallows ECONNREFUSED specifically; real errors
(timeouts, 5xx, anything else) still surface.
Both fixes are dev-experience only — production build is unchanged.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Bumps Vite to 8.0.11 (npm install picked up 6.4.2 from the stale
lockfile; clean install resolves the new constraint). Bumps vue-i18n
to 11.1.4 since v10 was just EOL'd.
Migrates aThemeSwitch.html — the two-flavor theme picker + global
themeSwitcher object — into:
- composables/useTheme.js: single reactive `theme` state with
toggleTheme / toggleUltra. Boot side-effect applies the stored theme
to <body>/<html> before Vue renders; watchEffect persists changes
back to localStorage.
- components/ThemeSwitch.vue: full menu version for the main panel.
- components/ThemeSwitchLogin.vue: login-popover version.
AD-Vue 1 → 4 changes hit on this component:
- <a-icon type="bulb" :theme="filled|outlined"> dropped — replaced by
explicit BulbFilled / BulbOutlined imports from
@ant-design/icons-vue, swapped via <component :is="BulbIcon">
- Vue.component('a-theme-switch', { ... }) global registration → SFC
+ per-page import
- this.$message.config(...) (Vue 2 instance method) → message.config(...)
imported from ant-design-vue, called once in login.js at boot
Login page now surfaces a settings button → popover → theme picker.
Known gap: web/assets/css/custom.min.css isn't yet imported into the
new bundle, so toggling dark mode currently only re-themes AD-Vue's
own components, not the panel chrome. The body class is still toggled
so behavior is correct; visual fidelity returns when custom.css is
ported or directly imported.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
First real page in the new toolchain. Multi-page Vite: each migrated
page is its own entry. login.html now lives at frontend/login.html with
a thin entrypoint at frontend/src/login.js mounting LoginPage.vue.
Vite 6 → Vite 8.0.11 (per user request). Requires Node 20.19+ or 22.12+.
@vitejs/plugin-vue bumped to ^6.0.6 (peers vite ^8). Ant Design Vue
stays on 4.2.6 — there is no AD-Vue 6.
Vue 2 → Vue 3 / AD-Vue 1 → AD-Vue 4 syntax changes hit on this page:
- new Vue({ el, delimiters, data, methods }) → createApp + <script setup>
- mounted() → onMounted()
- <template slot="X"> → <template #X>
- <a-icon slot="prefix" type="user"> → <template #prefix><UserOutlined />
</template> with explicit @ant-design/icons-vue imports
- v-model.trim → v-model:value (AD-Vue 4 uses named v-model on inputs)
Three legacy features deferred so Phase 4 stays small:
- i18n (Phase 7 wires up vue-i18n)
- theme switcher (custom component pending Phase 5)
- headline word-cycle animation (purely aesthetic)
Run `cd frontend && npm install && npm run dev`, open
http://localhost:5173/login.html. With Go panel running on :2053 the
form submits real credentials via the configured proxy.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>