Skip to content

Commit 80646bd

Browse files
committed
feat(ai-chat): video intro card on login / configure-claude / unavailable states
When the AI panel renders the not-signed-in (Sign In), Claude-needs- configuring (Setup Claude Code), CLI-not-installed (Claude Code Not Installed), or browser placeholder states, the existing icon stays in place but the title gets replaced by a richer intro: - Sign In and browser placeholder: "Design layouts, fix bugs, and build faster with AI" + a YouTube preview thumbnail. - Configure-Claude flows (Setup + Install): "Getting started with Claude Code" + "See this short video on how to set up AI" + the same thumbnail. Thumbnails and YouTube URLs come from a new ai_panel_config.json fetched once at boot from the same host as the onboarding iframe. Slot stays empty (display:none) when the fetch fails or the requested key isn't in the config — original UI then renders unchanged. Layout uses a flex column with consistent gap inside the centered .ai-unavailable container, max-width 320px, and overflow-wrap so the title wraps cleanly on narrow sidebars. The play overlay is the YouTube logo asset, scaling subtly on hover (no thumb glow — Phoenix doesn't generally do that). Sign In button switches to Phoenix's standard btn btn-primary using @bc-primary-btn-bg / @dark-bc-primary-btn-bg, defeating the AI panel's chat-area button reset with a more specific selector.
1 parent edfc511 commit 80646bd

2 files changed

Lines changed: 161 additions & 0 deletions

File tree

src/nls/root/strings.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2174,6 +2174,9 @@ define({
21742174
"AI_CHAT_LOGIN_TITLE": "Sign In to Use AI",
21752175
"AI_CHAT_LOGIN_MESSAGE": "Sign in to your {APP_NAME} account to access AI features.",
21762176
"AI_CHAT_LOGIN_BTN": "Sign In",
2177+
"AI_CHAT_INTRO_GUEST_TITLE": "Design layouts, fix bugs, and build faster with AI",
2178+
"AI_CHAT_INTRO_CONFIGURE_TITLE": "Getting started with Claude Code",
2179+
"AI_CHAT_INTRO_CONFIGURE_DESC": "See this short video on how to set up AI",
21772180
"AI_CHAT_UPSELL_TITLE": "Phoenix Pro + AI",
21782181
"AI_CHAT_UPSELL_MESSAGE": "AI features are available with Phoenix Pro.",
21792182
"AI_CHAT_UPSELL_BTN": "Get Phoenix Pro",

src/styles/Extn-AIChatPanel.less

Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2731,6 +2731,164 @@
27312731
}
27322732
}
27332733

2734+
/* ── Intro video card (login / unavailable / configure-claude states) ─
2735+
Renders inside the centered .ai-unavailable column. Vertical rhythm
2736+
uses a flex `gap` so spacing between the title, description (when
2737+
present), and the thumb stays consistent — no per-element bottom
2738+
margins to keep in sync. The slot itself adds bottom space before
2739+
the existing .ai-unavailable-message that follows it.
2740+
2741+
Width is capped at 320px so the block stays a tidy card even in a
2742+
wide sidebar, and the thumb keeps a 16:9 aspect so the YouTube
2743+
preview lands cleanly inside without cropping the play button.
2744+
2745+
Empty until ai_panel_config.json resolves — `.has-video` is added
2746+
when the populate function injects content. */
2747+
.ai-intro-video-slot {
2748+
display: none;
2749+
flex-direction: column;
2750+
align-items: stretch;
2751+
width: 100%;
2752+
max-width: 320px;
2753+
margin: 0 auto 1.25rem;
2754+
text-align: center;
2755+
gap: 14px;
2756+
2757+
&.has-video {
2758+
display: flex;
2759+
}
2760+
}
2761+
2762+
.ai-intro-video-title {
2763+
font-size: 17px;
2764+
font-weight: 600;
2765+
color: @project-panel-text-1;
2766+
line-height: 1.35;
2767+
letter-spacing: -0.005em;
2768+
margin: 0;
2769+
white-space: normal;
2770+
overflow-wrap: anywhere;
2771+
}
2772+
2773+
.ai-intro-video-desc {
2774+
font-size: 14px;
2775+
color: @project-panel-text-2;
2776+
opacity: 0.8;
2777+
line-height: 1.5;
2778+
margin: 0;
2779+
white-space: normal;
2780+
overflow-wrap: anywhere;
2781+
}
2782+
2783+
.ai-intro-video-thumb {
2784+
position: relative;
2785+
display: block;
2786+
width: 100%;
2787+
padding: 0;
2788+
margin: 0;
2789+
border: 1px solid rgba(255, 255, 255, 0.08);
2790+
border-radius: 8px;
2791+
overflow: hidden;
2792+
cursor: pointer;
2793+
background: rgba(0, 0, 0, 0.25);
2794+
aspect-ratio: 16 / 9;
2795+
2796+
/* Hover-on-thumb scale only the YouTube play icon — keep the thumb
2797+
border/shadow stable since Phoenix doesn't generally do glow-on-hover
2798+
on cards/thumbs. */
2799+
&:hover .ai-intro-video-play {
2800+
transform: translate(-50%, -50%) scale(1.08);
2801+
opacity: 1;
2802+
}
2803+
2804+
.ai-intro-video-img {
2805+
display: block;
2806+
width: 100%;
2807+
height: 100%;
2808+
object-fit: cover;
2809+
}
2810+
2811+
.ai-intro-video-play {
2812+
position: absolute;
2813+
left: 50%;
2814+
top: 50%;
2815+
transform: translate(-50%, -50%);
2816+
width: 64px;
2817+
height: auto;
2818+
pointer-events: none;
2819+
filter: drop-shadow(0 2px 6px rgba(0, 0, 0, 0.45));
2820+
opacity: 0.95;
2821+
transition: transform 0.15s ease, opacity 0.15s ease;
2822+
}
2823+
}
2824+
2825+
/* When the intro video card is showing, soften the existing icon and
2826+
give the surrounding column a touch more breathing room so the whole
2827+
block reads as a single composed card. */
2828+
.ai-unavailable:has(.ai-intro-video-slot.has-video) {
2829+
.ai-unavailable-icon,
2830+
.ai-install-icon {
2831+
margin-bottom: 1.25rem;
2832+
opacity: 0.5;
2833+
}
2834+
2835+
.ai-unavailable-message {
2836+
max-width: 320px;
2837+
font-size: 13.5px;
2838+
line-height: 1.5;
2839+
margin-bottom: 1rem;
2840+
}
2841+
}
2842+
2843+
/* Re-apply Phoenix's standard `.btn .btn-primary` look inside the
2844+
.ai-unavailable intro states. The blanket reset below (`.ai-chat-panel
2845+
.btn-primary`) blanks bootstrap's button styling so chat-area buttons
2846+
can use their own custom styles; here we want the regular primary
2847+
button so the Sign In CTA reads as the standard Phoenix action. The
2848+
selector is more specific than the reset, and `!important` belt-and-
2849+
braces against any later reset rule that might still apply. */
2850+
.ai-chat-panel .ai-unavailable .btn.btn-primary {
2851+
display: inline-block;
2852+
background-color: @bc-primary-btn-bg !important;
2853+
background-image: none !important;
2854+
color: #ffffff !important;
2855+
border: 1px solid darken(@bc-primary-btn-bg, 8%) !important;
2856+
box-shadow: none !important;
2857+
text-shadow: none !important;
2858+
font-weight: 600;
2859+
font-size: 13.5px;
2860+
padding: 6px 18px;
2861+
border-radius: 4px;
2862+
cursor: pointer;
2863+
transition: background-color 0.15s ease;
2864+
2865+
&:hover {
2866+
background-color: lighten(@bc-primary-btn-bg, 6%) !important;
2867+
}
2868+
2869+
&:active {
2870+
background-color: darken(@bc-primary-btn-bg, 8%) !important;
2871+
}
2872+
2873+
&:focus-visible {
2874+
outline: 2px solid fadeout(@bc-primary-btn-bg, 50%);
2875+
outline-offset: 2px;
2876+
}
2877+
}
2878+
2879+
.dark .ai-chat-panel .ai-unavailable .btn.btn-primary {
2880+
background-color: @dark-bc-primary-btn-bg !important;
2881+
border-color: darken(@dark-bc-primary-btn-bg, 8%) !important;
2882+
2883+
&:hover {
2884+
background-color: lighten(@dark-bc-primary-btn-bg, 8%) !important;
2885+
}
2886+
2887+
&:active {
2888+
background-color: darken(@dark-bc-primary-btn-bg, 8%) !important;
2889+
}
2890+
}
2891+
27342892
/* ── Unavailable / placeholder state ────────────────────────────────── */
27352893
.ai-unavailable {
27362894
display: flex;

0 commit comments

Comments
 (0)