feat(splash): refresh startup experience - #2048
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
📝 WalkthroughWalkthroughThe splash renderer replaces the previous activity-based loading view with animated logo and aurora compositions, distinguishes system and manual unlock states, preserves unlock callbacks, adds reduced-motion styling, and introduces focused renderer tests and feature documentation. ChangesSplash experience redesign
Estimated code review effort: 4 (Complex) | ~45 minutes Sequence Diagram(s)sequenceDiagram
participant DeepChatSplash
participant Loading
participant RendererDOM
DeepChatSplash->>Loading: onUnlockProgress(payload)
Loading->>RendererDOM: render system unlock panel
DeepChatSplash->>Loading: onUnlockRequest(payload)
Loading->>RendererDOM: render manual unlock panel
Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (9)
src/renderer/splash/loading.vue (7)
107-107: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value
animationStartedis a constant. It's initialized totrueand never mutated, so the binding always resolves to the same class. Either drop the ref and hardcodeloader-stage--orb loader-stage--animating, or flip it on mount (likely the intent, to avoid animations starting before first paint).♻️ Option: start on mount
-const animationStarted = ref(true) +const animationStarted = ref(false)onMounted(() => { + requestAnimationFrame(() => { + animationStarted.value = true + }) cleanupListeners.push(🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/renderer/splash/loading.vue` at line 107, Update animationStarted in the loading component: either remove the unused ref and always apply both loader-stage--orb and loader-stage--animating classes, or initialize it as inactive and set it to true during the component’s mount lifecycle so animation starts after first paint.
900-1033: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winNine unused keyframes.
card-settle,rim-breathe,wake-sweep,wake-idle,fish-swim,fish-idle,eye-arrive,speed-line, andwake-pulseare not referenced by any selector in this file — roughly 130 lines of dead CSS in the startup bundle. Thespeed-linekeyframe is especially confusing since the.speed-lineclass actually usesspeed-scan. Please drop them (or wire them up if they were intended for the loader).🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/renderer/splash/loading.vue` around lines 900 - 1033, Remove the unused keyframes card-settle, rim-breathe, wake-sweep, wake-idle, fish-swim, fish-idle, eye-arrive, speed-line, and wake-pulse from the stylesheet; preserve the existing animation referenced by the .speed-line selector, which uses speed-scan.
4-9: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueAurora background markup duplicated three times. Consider extracting a small
AuroraBackgroundcomponent (with an optionalcyanprop) to keep the three stages in sync.Also applies to: 44-49, 69-75
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/renderer/splash/loading.vue` around lines 4 - 9, Extract the repeated aurora markup into a reusable AuroraBackground component, supporting an optional cyan prop for the variant used by specific stages. Replace all three duplicated blocks in the loading view with this component so the background structure remains synchronized.
15-17: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚖️ Poor tradeoffSplash strings are hardcoded English.
DeepChat is starting,Local database is encrypted,SQLite password,Unlock/Quit, and the hint/subtitle text are user-facing. Guidelines require vue-i18n keys undersrc/renderer/src/i18n; if the splash entry intentionally runs without the i18n runtime (to keep startup cheap), please note that exception in the PR.As per coding guidelines: "All user-facing strings must use vue-i18n keys defined under
src/renderer/src/i18n".Also applies to: 35-37, 55-59, 67-67
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/renderer/splash/loading.vue` around lines 15 - 17, Replace all hardcoded user-facing splash text in the loading component—including the title, encryption message, password label, hint/subtitle, and Unlock/Quit controls—with vue-i18n keys defined under src/renderer/src/i18n, using the component’s existing i18n access pattern or adding the required setup. If this splash intentionally runs without the i18n runtime, document that exception in the PR instead.Source: Coding guidelines
63-68: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win
aria-labelon a plaindivisn't reliably exposed. Without a role, most AT ignores the label. Addrole="status"(orrole="img") so the starting state is announced.♿ Proposed fix
<div v-else class="loader-stage loader-stage--orb" :class="{ 'loader-stage--animating': animationStarted }" + role="status" aria-label="DeepChat is starting" >🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/renderer/splash/loading.vue` around lines 63 - 68, Add an explicit accessibility role to the loading-state div in the `loader-stage--orb` branch, using `role="status"` so assistive technology reliably announces its existing `aria-label` of “DeepChat is starting.”
506-529: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winBase
.unlock-panelstyling is dead. Both consumers (--system,--manual) resetborder,background,box-shadow, andbackdrop-filter, and the light-mode override at Lines 569–575 restyles properties that are then cleared. Collapse to just the layout rules.♻️ Proposed cleanup
.unlock-panel { position: relative; z-index: 1; display: flex; width: min(340px, 100%); flex-direction: column; gap: 11px; - border: 1px solid rgb(148 163 184 / 28%); - border-radius: 20px; - background: linear-gradient(145deg, rgb(10 30 71 / 84%), rgb(3 12 33 / 82%)); - box-shadow: - 0 18px 48px rgb(2 8 23 / 34%), - inset 0 1px rgb(255 255 255 / 8%); padding: 22px 24px 24px; - backdrop-filter: blur(22px); } -.unlock-panel--system, -.unlock-panel--manual { - border: 0; - background: transparent; - box-shadow: none; - backdrop-filter: none; -} - .unlock-panel--system {`@media` (prefers-color-scheme: light) { - .unlock-panel { - border-color: rgb(45 116 181 / 20%); - background: rgb(255 255 255 / 72%); - box-shadow: - 0 18px 48px rgb(67 125 178 / 18%), - inset 0 1px rgb(255 255 255 / 74%); - } - .unlock-title {Also applies to: 569-575
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/renderer/splash/loading.vue` around lines 506 - 529, Remove the unused visual properties from the base .unlock-panel rule, retaining only layout-related styling used by both .unlock-panel--system and .unlock-panel--manual. Remove the corresponding light-mode override declarations for the cleared border, background, box-shadow, and backdrop-filter properties, while preserving the shared layout and spacing behavior.
258-263: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueViewport-relative sizes inside a 340px orb.
135vmaxribbons andmin(70vmax, 820px)pools are clipped by the 340px circle, so the visual result depends heavily on window size rather than on the orb. Consider sizing these relative to the stage (e.g.%of the aurora container) so loading and system-unlock look identical across displays.Also applies to: 289-297
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/renderer/splash/loading.vue` around lines 258 - 263, Replace the viewport-relative dimensions used by .aurora-ribbon and the related pool sizing around the aurora container with stage-relative percentages or equivalent container-based sizing. Keep the elements contained within the 340px orb while ensuring loading and system-unlock visuals remain consistent across viewport sizes.test/renderer/splash/loading.test.ts (2)
44-62: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winAssertions are weak for the states this PR introduces. Line 47 is tautological (
.get('.splash-shell')then asserting it hassplash-shell), and the "circular shell" test never checks the orb class. Assertunlock-stage--orb/loader-stage--orbso a regression to a rectangular stage actually fails.💚 Proposed strengthening
it('starts with the circular splash shell loading state', () => { const wrapper = mountLoading() - expect(wrapper.get('.splash-shell').classes()).toContain('splash-shell') + expect(wrapper.get('.loader-stage').classes()).toContain('loader-stage--orb') expect(wrapper.get('.loader-stage').attributes('aria-label')).toBe('DeepChat is starting') expect(wrapper.classes()).not.toContain('splash-shell--manual-unlock') })- expect(wrapper.get('.splash-shell').classes()).toContain('splash-shell') + expect(wrapper.get('.unlock-stage').classes()).toContain('unlock-stage--orb') expect(wrapper.classes()).not.toContain('splash-shell--manual-unlock')🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@test/renderer/splash/loading.test.ts` around lines 44 - 62, Strengthen the two loading-state tests in the relevant describe block: replace the tautological splash-shell class assertion with an assertion that the wrapper contains unlock-stage--orb, and add an assertion that the loader stage contains loader-stage--orb. Preserve the existing accessibility, manual-unlock, and system-unlock assertions.
45-45: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueLocal
wrappershadows the module-level one. Cleanup still works becausemountLoadingassigns the outer binding, but the shadowing is fragile and OxLint'sno-shadowwill likely flag it. Drop theconst.♻️ Proposed fix
- const wrapper = mountLoading() + const view = mountLoading()Also applies to: 53-53, 65-65
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@test/renderer/splash/loading.test.ts` at line 45, Remove the local const declaration before each affected mountLoading call so the module-level wrapper binding is reused. Update all occurrences noted in the comment, including the calls around the existing tests, while preserving the current mounting and cleanup behavior.Source: Coding guidelines
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@docs/features/splash-experience/tasks.md`:
- Around line 6-7: Update the checklist in the splash experience tasks document
to mark focused renderer tests and required validation as completed, reflecting
the existing loading renderer test and reported format, i18n, lint, Vitest, and
typecheck validation.
In `@src/renderer/splash/loading.vue`:
- Around line 289-309: Wire the existing aurora animation keyframes to the
relevant `.aurora-pool` and sweep selectors, ensuring each pool receives the
appropriate animation with its centering `translate3d` transform and timing.
Remove or avoid the unused `will-change: transform` only if the animations are
not retained; keep the existing gradient styling unchanged.
- Around line 1035-1050: Update the prefers-reduced-motion block to disable
animations for .unlock-logo and .unlock-logo :deep(path:nth-of-type(3)),
covering the unlock-logo-float and eye-blink animations. Remove the nonexistent
.logo-loader__wake selector from the same block.
- Around line 628-640: Update the .unlock-input:focus rule to add a clearly
visible focus ring, while preserving the existing border-color change. Use an
accessible box-shadow or equivalent focus indicator that provides sufficient
contrast around the password input.
---
Nitpick comments:
In `@src/renderer/splash/loading.vue`:
- Line 107: Update animationStarted in the loading component: either remove the
unused ref and always apply both loader-stage--orb and loader-stage--animating
classes, or initialize it as inactive and set it to true during the component’s
mount lifecycle so animation starts after first paint.
- Around line 900-1033: Remove the unused keyframes card-settle, rim-breathe,
wake-sweep, wake-idle, fish-swim, fish-idle, eye-arrive, speed-line, and
wake-pulse from the stylesheet; preserve the existing animation referenced by
the .speed-line selector, which uses speed-scan.
- Around line 4-9: Extract the repeated aurora markup into a reusable
AuroraBackground component, supporting an optional cyan prop for the variant
used by specific stages. Replace all three duplicated blocks in the loading view
with this component so the background structure remains synchronized.
- Around line 15-17: Replace all hardcoded user-facing splash text in the
loading component—including the title, encryption message, password label,
hint/subtitle, and Unlock/Quit controls—with vue-i18n keys defined under
src/renderer/src/i18n, using the component’s existing i18n access pattern or
adding the required setup. If this splash intentionally runs without the i18n
runtime, document that exception in the PR instead.
- Around line 63-68: Add an explicit accessibility role to the loading-state div
in the `loader-stage--orb` branch, using `role="status"` so assistive technology
reliably announces its existing `aria-label` of “DeepChat is starting.”
- Around line 506-529: Remove the unused visual properties from the base
.unlock-panel rule, retaining only layout-related styling used by both
.unlock-panel--system and .unlock-panel--manual. Remove the corresponding
light-mode override declarations for the cleared border, background, box-shadow,
and backdrop-filter properties, while preserving the shared layout and spacing
behavior.
- Around line 258-263: Replace the viewport-relative dimensions used by
.aurora-ribbon and the related pool sizing around the aurora container with
stage-relative percentages or equivalent container-based sizing. Keep the
elements contained within the 340px orb while ensuring loading and system-unlock
visuals remain consistent across viewport sizes.
In `@test/renderer/splash/loading.test.ts`:
- Around line 44-62: Strengthen the two loading-state tests in the relevant
describe block: replace the tautological splash-shell class assertion with an
assertion that the wrapper contains unlock-stage--orb, and add an assertion that
the loader stage contains loader-stage--orb. Preserve the existing
accessibility, manual-unlock, and system-unlock assertions.
- Line 45: Remove the local const declaration before each affected mountLoading
call so the module-level wrapper binding is reused. Update all occurrences noted
in the comment, including the calls around the existing tests, while preserving
the current mounting and cleanup behavior.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: d183e141-305d-4fdf-8756-b5d2d86e189d
⛔ Files ignored due to path filters (2)
src/renderer/src/assets/splash/logo-v3-dark.svgis excluded by!**/*.svgsrc/renderer/src/assets/splash/logo-v3-light.svgis excluded by!**/*.svg
📒 Files selected for processing (5)
docs/features/splash-experience/plan.mddocs/features/splash-experience/spec.mddocs/features/splash-experience/tasks.mdsrc/renderer/splash/loading.vuetest/renderer/splash/loading.test.ts
# Conflicts: # test/main/provider/modelConfig.test.ts
Summary
UI layout
BEFORE
AFTER
Validation
pnpm run formatpnpm run i18npnpm run lintpnpm exec vitest run test/renderer/splash/loading.test.tspnpm run typecheck:webSummary by CodeRabbit