diff --git a/.agents/skills/impeccable/scripts/live-browser.js b/.agents/skills/impeccable/scripts/live-browser.js index ed9606409..1b8926333 100644 --- a/.agents/skills/impeccable/scripts/live-browser.js +++ b/.agents/skills/impeccable/scripts/live-browser.js @@ -8189,7 +8189,7 @@ void main() { let voiceInterimBase = ''; /** @type {{ mode: 'steer'|'configure', input: HTMLInputElement, submit: () => void, beforeStart?: () => void } | null} */ let voiceCtx = null; - const PAGE_CHAT_COLLAPSED_W = '88px'; + const PAGE_CHAT_COLLAPSED_W = '104px'; const PAGE_CHAT_PROCESSING_W = '76px'; const PAGE_CHAT_PLACEHOLDER_COLLAPSED = 'Steer…'; const PAGE_CHAT_PLACEHOLDER_EXPANDED = 'Steer the page…'; @@ -8200,7 +8200,7 @@ void main() { const GLOBAL_BAR_SECTION_GAP = 8; const GLOBAL_BAR_INNER_GAP = 2; const GLOBAL_BAR_INNER_PAD_LEFT = 2; - const PAGE_CHAT_EXPANDED_W = 'min(280px, 38vw)'; + const PAGE_CHAT_EXPANDED_MAX_W = 280; const ICON_PAGE_CHAT = ''; const ICON_PAGE_VOICE = @@ -8280,6 +8280,43 @@ void main() { return barPaletteForTheme(globalBarEl?.dataset.theme || detectPageTheme()); } + function globalBarModeToggles() { + return [ + uiGetById(PREFIX + '-pick-toggle'), + uiGetById(PREFIX + '-insert-toggle'), + uiGetById(PREFIX + '-detect-toggle'), + uiGetById(PREFIX + '-design-toggle'), + ].filter(Boolean); + } + + function syncGlobalBarExpandedLabels(expanded = globalBarEl?.matches(':hover')) { + globalBarModeToggles().forEach((toggle) => { + if (expanded && !pageChatExpanded) toggle._expandLabel?.(); + else if (toggle.dataset.active === 'true') toggle._expandLabel?.(); + else toggle._collapseLabel?.(); + }); + } + + function pageChatCollapsedWidthPx() { + const parsed = parseFloat(PAGE_CHAT_COLLAPSED_W); + return Number.isFinite(parsed) ? parsed : 104; + } + + function pageChatExpandedWidth() { + if (!pageChatEl || !globalBarEl) return PAGE_CHAT_EXPANDED_MAX_W + 'px'; + const currentChatWidth = pageChatEl.getBoundingClientRect().width || pageChatCollapsedWidthPx(); + const barWidth = globalBarEl.getBoundingClientRect().width || 0; + const nonChatWidth = Math.max(0, barWidth - currentChatWidth); + const available = window.innerWidth - 16 - nonChatWidth; + const next = Math.max(pageChatCollapsedWidthPx(), Math.min(PAGE_CHAT_EXPANDED_MAX_W, available)); + return Math.round(next) + 'px'; + } + + function syncPageChatExpandedWidth() { + if (!pageChatEl || !pageChatExpanded) return; + pageChatEl.style.width = pageChatExpandedWidth(); + } + function syncPageChatChrome() { if (!pageChatEl) return; const P = pageChatPalette(); @@ -8581,7 +8618,8 @@ void main() { if (!pageChatEl || !pageChatInput) return false; pageChatExpanded = true; pageChatEl.dataset.expanded = 'true'; - pageChatEl.style.width = PAGE_CHAT_EXPANDED_W; + syncGlobalBarExpandedLabels(false); + pageChatEl.style.width = pageChatExpandedWidth(); pageChatEl.style.cursor = steerLocked ? 'default' : 'text'; pageChatInput.placeholder = PAGE_CHAT_PLACEHOLDER_EXPANDED; if (pageChatHint) { @@ -8676,7 +8714,7 @@ void main() { pageChatEl.setAttribute('aria-label', 'Steer the page'); pageChatExpanded = keepExpanded; pageChatEl.dataset.expanded = keepExpanded ? 'true' : 'false'; - pageChatEl.style.width = keepExpanded ? PAGE_CHAT_EXPANDED_W : PAGE_CHAT_COLLAPSED_W; + pageChatEl.style.width = keepExpanded ? pageChatExpandedWidth() : PAGE_CHAT_COLLAPSED_W; pageChatEl.style.cursor = 'pointer'; if (pageChatInput) { pageChatInput.disabled = false; @@ -8990,6 +9028,7 @@ void main() { pageChatEl.dataset.expanded = 'false'; pageChatEl.style.width = PAGE_CHAT_COLLAPSED_W; pageChatEl.style.cursor = 'pointer'; + syncGlobalBarExpandedLabels(globalBarEl?.matches(':hover')); if (blur) { pageChatInput.blur(); pageChatInput.style.pointerEvents = 'none'; @@ -9296,6 +9335,8 @@ void main() { fontFamily: FONT, fontSize: '12px', lineHeight: '1', opacity: '0', overflow: 'hidden', // clip the full-bleed brand mark to the bar radius + maxWidth: 'calc(100vw - 16px)', + boxSizing: 'border-box', transition: 'opacity 0.3s ' + EASE + ', transform 0.3s ' + EASE, }); globalBarEl.id = PREFIX + '-global-bar'; @@ -9344,6 +9385,8 @@ void main() { const inner = el('div', { display: 'flex', alignItems: 'center', padding: '4px 5px 4px ' + GLOBAL_BAR_INNER_PAD_LEFT + 'px', gap: GLOBAL_BAR_INNER_GAP + 'px', + minWidth: '0', + flex: '1 1 auto', }); inner.id = PREFIX + '-global-bar-inner'; globalBarEl.appendChild(inner); @@ -9623,6 +9666,7 @@ void main() { width: '1px', height: '18px', background: P.hairline, margin: '0 4px 0 2px', + flexShrink: '0', }); inner.appendChild(divider); @@ -9639,6 +9683,7 @@ void main() { display: 'inline-flex', alignItems: 'center', justifyContent: 'center', padding: '0', boxSizing: 'border-box', width: '24px', height: '24px', borderRadius: '6px', + flexShrink: '0', border: 'none', background: 'transparent', color: P.textDim, fontFamily: FONT, fontSize: '0', lineHeight: '0', cursor: 'pointer', transition: 'color 0.12s ease, background 0.12s ease', @@ -9651,16 +9696,16 @@ void main() { exitBtn.addEventListener('click', () => { sendEvent({ type: 'exit' }); teardown(); }); inner.appendChild(exitBtn); - // Bar-level hover: expand every toggle's label at once; collapse on leave. + // Bar-level hover: expand mode labels unless Steer is using the space. // Buttons with dataset.active="true" ignore collapse (their label stays). - const toggles = [pickBtn, insertBtn, detectBtn, designBtn]; globalBarEl.addEventListener('mouseenter', () => { - toggles.forEach((t) => t._expandLabel && t._expandLabel()); + syncGlobalBarExpandedLabels(true); + syncPageChatExpandedWidth(); schedulePendingDockPosition(); setTimeout(schedulePendingDockPosition, 260); }); globalBarEl.addEventListener('mouseleave', () => { - toggles.forEach((t) => t._collapseLabel && t._collapseLabel()); + syncGlobalBarExpandedLabels(false); schedulePendingDockPosition(); setTimeout(schedulePendingDockPosition, 260); }); @@ -9678,6 +9723,7 @@ void main() { pendingDockResizeObserver.observe(globalBarEl); } window.addEventListener('resize', positionPendingDock); + window.addEventListener('resize', syncPageChatExpandedWidth); requestAnimationFrame(() => { globalBarEl.style.opacity = '1'; @@ -9724,9 +9770,7 @@ void main() { // If the bar is currently under the cursor, keep all labels expanded - // otherwise clicking a toggle that deactivates (e.g. closing DESIGN.md) // would collapse its label while the user's mouse is still on the bar. - if (globalBarEl && globalBarEl.matches(':hover')) { - [pickToggle, insertToggle, detectToggle, designToggle].forEach((t) => t?._expandLabel?.()); - } + syncGlobalBarExpandedLabels(globalBarEl && globalBarEl.matches(':hover')); if (detectBadge) { detectBadge.style.display = (detectActive && detectCount > 0) ? 'inline' : 'none'; diff --git a/.claude/skills/impeccable/scripts/live-browser.js b/.claude/skills/impeccable/scripts/live-browser.js index ed9606409..1b8926333 100644 --- a/.claude/skills/impeccable/scripts/live-browser.js +++ b/.claude/skills/impeccable/scripts/live-browser.js @@ -8189,7 +8189,7 @@ void main() { let voiceInterimBase = ''; /** @type {{ mode: 'steer'|'configure', input: HTMLInputElement, submit: () => void, beforeStart?: () => void } | null} */ let voiceCtx = null; - const PAGE_CHAT_COLLAPSED_W = '88px'; + const PAGE_CHAT_COLLAPSED_W = '104px'; const PAGE_CHAT_PROCESSING_W = '76px'; const PAGE_CHAT_PLACEHOLDER_COLLAPSED = 'Steer…'; const PAGE_CHAT_PLACEHOLDER_EXPANDED = 'Steer the page…'; @@ -8200,7 +8200,7 @@ void main() { const GLOBAL_BAR_SECTION_GAP = 8; const GLOBAL_BAR_INNER_GAP = 2; const GLOBAL_BAR_INNER_PAD_LEFT = 2; - const PAGE_CHAT_EXPANDED_W = 'min(280px, 38vw)'; + const PAGE_CHAT_EXPANDED_MAX_W = 280; const ICON_PAGE_CHAT = ''; const ICON_PAGE_VOICE = @@ -8280,6 +8280,43 @@ void main() { return barPaletteForTheme(globalBarEl?.dataset.theme || detectPageTheme()); } + function globalBarModeToggles() { + return [ + uiGetById(PREFIX + '-pick-toggle'), + uiGetById(PREFIX + '-insert-toggle'), + uiGetById(PREFIX + '-detect-toggle'), + uiGetById(PREFIX + '-design-toggle'), + ].filter(Boolean); + } + + function syncGlobalBarExpandedLabels(expanded = globalBarEl?.matches(':hover')) { + globalBarModeToggles().forEach((toggle) => { + if (expanded && !pageChatExpanded) toggle._expandLabel?.(); + else if (toggle.dataset.active === 'true') toggle._expandLabel?.(); + else toggle._collapseLabel?.(); + }); + } + + function pageChatCollapsedWidthPx() { + const parsed = parseFloat(PAGE_CHAT_COLLAPSED_W); + return Number.isFinite(parsed) ? parsed : 104; + } + + function pageChatExpandedWidth() { + if (!pageChatEl || !globalBarEl) return PAGE_CHAT_EXPANDED_MAX_W + 'px'; + const currentChatWidth = pageChatEl.getBoundingClientRect().width || pageChatCollapsedWidthPx(); + const barWidth = globalBarEl.getBoundingClientRect().width || 0; + const nonChatWidth = Math.max(0, barWidth - currentChatWidth); + const available = window.innerWidth - 16 - nonChatWidth; + const next = Math.max(pageChatCollapsedWidthPx(), Math.min(PAGE_CHAT_EXPANDED_MAX_W, available)); + return Math.round(next) + 'px'; + } + + function syncPageChatExpandedWidth() { + if (!pageChatEl || !pageChatExpanded) return; + pageChatEl.style.width = pageChatExpandedWidth(); + } + function syncPageChatChrome() { if (!pageChatEl) return; const P = pageChatPalette(); @@ -8581,7 +8618,8 @@ void main() { if (!pageChatEl || !pageChatInput) return false; pageChatExpanded = true; pageChatEl.dataset.expanded = 'true'; - pageChatEl.style.width = PAGE_CHAT_EXPANDED_W; + syncGlobalBarExpandedLabels(false); + pageChatEl.style.width = pageChatExpandedWidth(); pageChatEl.style.cursor = steerLocked ? 'default' : 'text'; pageChatInput.placeholder = PAGE_CHAT_PLACEHOLDER_EXPANDED; if (pageChatHint) { @@ -8676,7 +8714,7 @@ void main() { pageChatEl.setAttribute('aria-label', 'Steer the page'); pageChatExpanded = keepExpanded; pageChatEl.dataset.expanded = keepExpanded ? 'true' : 'false'; - pageChatEl.style.width = keepExpanded ? PAGE_CHAT_EXPANDED_W : PAGE_CHAT_COLLAPSED_W; + pageChatEl.style.width = keepExpanded ? pageChatExpandedWidth() : PAGE_CHAT_COLLAPSED_W; pageChatEl.style.cursor = 'pointer'; if (pageChatInput) { pageChatInput.disabled = false; @@ -8990,6 +9028,7 @@ void main() { pageChatEl.dataset.expanded = 'false'; pageChatEl.style.width = PAGE_CHAT_COLLAPSED_W; pageChatEl.style.cursor = 'pointer'; + syncGlobalBarExpandedLabels(globalBarEl?.matches(':hover')); if (blur) { pageChatInput.blur(); pageChatInput.style.pointerEvents = 'none'; @@ -9296,6 +9335,8 @@ void main() { fontFamily: FONT, fontSize: '12px', lineHeight: '1', opacity: '0', overflow: 'hidden', // clip the full-bleed brand mark to the bar radius + maxWidth: 'calc(100vw - 16px)', + boxSizing: 'border-box', transition: 'opacity 0.3s ' + EASE + ', transform 0.3s ' + EASE, }); globalBarEl.id = PREFIX + '-global-bar'; @@ -9344,6 +9385,8 @@ void main() { const inner = el('div', { display: 'flex', alignItems: 'center', padding: '4px 5px 4px ' + GLOBAL_BAR_INNER_PAD_LEFT + 'px', gap: GLOBAL_BAR_INNER_GAP + 'px', + minWidth: '0', + flex: '1 1 auto', }); inner.id = PREFIX + '-global-bar-inner'; globalBarEl.appendChild(inner); @@ -9623,6 +9666,7 @@ void main() { width: '1px', height: '18px', background: P.hairline, margin: '0 4px 0 2px', + flexShrink: '0', }); inner.appendChild(divider); @@ -9639,6 +9683,7 @@ void main() { display: 'inline-flex', alignItems: 'center', justifyContent: 'center', padding: '0', boxSizing: 'border-box', width: '24px', height: '24px', borderRadius: '6px', + flexShrink: '0', border: 'none', background: 'transparent', color: P.textDim, fontFamily: FONT, fontSize: '0', lineHeight: '0', cursor: 'pointer', transition: 'color 0.12s ease, background 0.12s ease', @@ -9651,16 +9696,16 @@ void main() { exitBtn.addEventListener('click', () => { sendEvent({ type: 'exit' }); teardown(); }); inner.appendChild(exitBtn); - // Bar-level hover: expand every toggle's label at once; collapse on leave. + // Bar-level hover: expand mode labels unless Steer is using the space. // Buttons with dataset.active="true" ignore collapse (their label stays). - const toggles = [pickBtn, insertBtn, detectBtn, designBtn]; globalBarEl.addEventListener('mouseenter', () => { - toggles.forEach((t) => t._expandLabel && t._expandLabel()); + syncGlobalBarExpandedLabels(true); + syncPageChatExpandedWidth(); schedulePendingDockPosition(); setTimeout(schedulePendingDockPosition, 260); }); globalBarEl.addEventListener('mouseleave', () => { - toggles.forEach((t) => t._collapseLabel && t._collapseLabel()); + syncGlobalBarExpandedLabels(false); schedulePendingDockPosition(); setTimeout(schedulePendingDockPosition, 260); }); @@ -9678,6 +9723,7 @@ void main() { pendingDockResizeObserver.observe(globalBarEl); } window.addEventListener('resize', positionPendingDock); + window.addEventListener('resize', syncPageChatExpandedWidth); requestAnimationFrame(() => { globalBarEl.style.opacity = '1'; @@ -9724,9 +9770,7 @@ void main() { // If the bar is currently under the cursor, keep all labels expanded - // otherwise clicking a toggle that deactivates (e.g. closing DESIGN.md) // would collapse its label while the user's mouse is still on the bar. - if (globalBarEl && globalBarEl.matches(':hover')) { - [pickToggle, insertToggle, detectToggle, designToggle].forEach((t) => t?._expandLabel?.()); - } + syncGlobalBarExpandedLabels(globalBarEl && globalBarEl.matches(':hover')); if (detectBadge) { detectBadge.style.display = (detectActive && detectCount > 0) ? 'inline' : 'none'; diff --git a/.cursor/skills/impeccable/scripts/live-browser.js b/.cursor/skills/impeccable/scripts/live-browser.js index ed9606409..1b8926333 100644 --- a/.cursor/skills/impeccable/scripts/live-browser.js +++ b/.cursor/skills/impeccable/scripts/live-browser.js @@ -8189,7 +8189,7 @@ void main() { let voiceInterimBase = ''; /** @type {{ mode: 'steer'|'configure', input: HTMLInputElement, submit: () => void, beforeStart?: () => void } | null} */ let voiceCtx = null; - const PAGE_CHAT_COLLAPSED_W = '88px'; + const PAGE_CHAT_COLLAPSED_W = '104px'; const PAGE_CHAT_PROCESSING_W = '76px'; const PAGE_CHAT_PLACEHOLDER_COLLAPSED = 'Steer…'; const PAGE_CHAT_PLACEHOLDER_EXPANDED = 'Steer the page…'; @@ -8200,7 +8200,7 @@ void main() { const GLOBAL_BAR_SECTION_GAP = 8; const GLOBAL_BAR_INNER_GAP = 2; const GLOBAL_BAR_INNER_PAD_LEFT = 2; - const PAGE_CHAT_EXPANDED_W = 'min(280px, 38vw)'; + const PAGE_CHAT_EXPANDED_MAX_W = 280; const ICON_PAGE_CHAT = ''; const ICON_PAGE_VOICE = @@ -8280,6 +8280,43 @@ void main() { return barPaletteForTheme(globalBarEl?.dataset.theme || detectPageTheme()); } + function globalBarModeToggles() { + return [ + uiGetById(PREFIX + '-pick-toggle'), + uiGetById(PREFIX + '-insert-toggle'), + uiGetById(PREFIX + '-detect-toggle'), + uiGetById(PREFIX + '-design-toggle'), + ].filter(Boolean); + } + + function syncGlobalBarExpandedLabels(expanded = globalBarEl?.matches(':hover')) { + globalBarModeToggles().forEach((toggle) => { + if (expanded && !pageChatExpanded) toggle._expandLabel?.(); + else if (toggle.dataset.active === 'true') toggle._expandLabel?.(); + else toggle._collapseLabel?.(); + }); + } + + function pageChatCollapsedWidthPx() { + const parsed = parseFloat(PAGE_CHAT_COLLAPSED_W); + return Number.isFinite(parsed) ? parsed : 104; + } + + function pageChatExpandedWidth() { + if (!pageChatEl || !globalBarEl) return PAGE_CHAT_EXPANDED_MAX_W + 'px'; + const currentChatWidth = pageChatEl.getBoundingClientRect().width || pageChatCollapsedWidthPx(); + const barWidth = globalBarEl.getBoundingClientRect().width || 0; + const nonChatWidth = Math.max(0, barWidth - currentChatWidth); + const available = window.innerWidth - 16 - nonChatWidth; + const next = Math.max(pageChatCollapsedWidthPx(), Math.min(PAGE_CHAT_EXPANDED_MAX_W, available)); + return Math.round(next) + 'px'; + } + + function syncPageChatExpandedWidth() { + if (!pageChatEl || !pageChatExpanded) return; + pageChatEl.style.width = pageChatExpandedWidth(); + } + function syncPageChatChrome() { if (!pageChatEl) return; const P = pageChatPalette(); @@ -8581,7 +8618,8 @@ void main() { if (!pageChatEl || !pageChatInput) return false; pageChatExpanded = true; pageChatEl.dataset.expanded = 'true'; - pageChatEl.style.width = PAGE_CHAT_EXPANDED_W; + syncGlobalBarExpandedLabels(false); + pageChatEl.style.width = pageChatExpandedWidth(); pageChatEl.style.cursor = steerLocked ? 'default' : 'text'; pageChatInput.placeholder = PAGE_CHAT_PLACEHOLDER_EXPANDED; if (pageChatHint) { @@ -8676,7 +8714,7 @@ void main() { pageChatEl.setAttribute('aria-label', 'Steer the page'); pageChatExpanded = keepExpanded; pageChatEl.dataset.expanded = keepExpanded ? 'true' : 'false'; - pageChatEl.style.width = keepExpanded ? PAGE_CHAT_EXPANDED_W : PAGE_CHAT_COLLAPSED_W; + pageChatEl.style.width = keepExpanded ? pageChatExpandedWidth() : PAGE_CHAT_COLLAPSED_W; pageChatEl.style.cursor = 'pointer'; if (pageChatInput) { pageChatInput.disabled = false; @@ -8990,6 +9028,7 @@ void main() { pageChatEl.dataset.expanded = 'false'; pageChatEl.style.width = PAGE_CHAT_COLLAPSED_W; pageChatEl.style.cursor = 'pointer'; + syncGlobalBarExpandedLabels(globalBarEl?.matches(':hover')); if (blur) { pageChatInput.blur(); pageChatInput.style.pointerEvents = 'none'; @@ -9296,6 +9335,8 @@ void main() { fontFamily: FONT, fontSize: '12px', lineHeight: '1', opacity: '0', overflow: 'hidden', // clip the full-bleed brand mark to the bar radius + maxWidth: 'calc(100vw - 16px)', + boxSizing: 'border-box', transition: 'opacity 0.3s ' + EASE + ', transform 0.3s ' + EASE, }); globalBarEl.id = PREFIX + '-global-bar'; @@ -9344,6 +9385,8 @@ void main() { const inner = el('div', { display: 'flex', alignItems: 'center', padding: '4px 5px 4px ' + GLOBAL_BAR_INNER_PAD_LEFT + 'px', gap: GLOBAL_BAR_INNER_GAP + 'px', + minWidth: '0', + flex: '1 1 auto', }); inner.id = PREFIX + '-global-bar-inner'; globalBarEl.appendChild(inner); @@ -9623,6 +9666,7 @@ void main() { width: '1px', height: '18px', background: P.hairline, margin: '0 4px 0 2px', + flexShrink: '0', }); inner.appendChild(divider); @@ -9639,6 +9683,7 @@ void main() { display: 'inline-flex', alignItems: 'center', justifyContent: 'center', padding: '0', boxSizing: 'border-box', width: '24px', height: '24px', borderRadius: '6px', + flexShrink: '0', border: 'none', background: 'transparent', color: P.textDim, fontFamily: FONT, fontSize: '0', lineHeight: '0', cursor: 'pointer', transition: 'color 0.12s ease, background 0.12s ease', @@ -9651,16 +9696,16 @@ void main() { exitBtn.addEventListener('click', () => { sendEvent({ type: 'exit' }); teardown(); }); inner.appendChild(exitBtn); - // Bar-level hover: expand every toggle's label at once; collapse on leave. + // Bar-level hover: expand mode labels unless Steer is using the space. // Buttons with dataset.active="true" ignore collapse (their label stays). - const toggles = [pickBtn, insertBtn, detectBtn, designBtn]; globalBarEl.addEventListener('mouseenter', () => { - toggles.forEach((t) => t._expandLabel && t._expandLabel()); + syncGlobalBarExpandedLabels(true); + syncPageChatExpandedWidth(); schedulePendingDockPosition(); setTimeout(schedulePendingDockPosition, 260); }); globalBarEl.addEventListener('mouseleave', () => { - toggles.forEach((t) => t._collapseLabel && t._collapseLabel()); + syncGlobalBarExpandedLabels(false); schedulePendingDockPosition(); setTimeout(schedulePendingDockPosition, 260); }); @@ -9678,6 +9723,7 @@ void main() { pendingDockResizeObserver.observe(globalBarEl); } window.addEventListener('resize', positionPendingDock); + window.addEventListener('resize', syncPageChatExpandedWidth); requestAnimationFrame(() => { globalBarEl.style.opacity = '1'; @@ -9724,9 +9770,7 @@ void main() { // If the bar is currently under the cursor, keep all labels expanded - // otherwise clicking a toggle that deactivates (e.g. closing DESIGN.md) // would collapse its label while the user's mouse is still on the bar. - if (globalBarEl && globalBarEl.matches(':hover')) { - [pickToggle, insertToggle, detectToggle, designToggle].forEach((t) => t?._expandLabel?.()); - } + syncGlobalBarExpandedLabels(globalBarEl && globalBarEl.matches(':hover')); if (detectBadge) { detectBadge.style.display = (detectActive && detectCount > 0) ? 'inline' : 'none'; diff --git a/.gemini/skills/impeccable/scripts/live-browser.js b/.gemini/skills/impeccable/scripts/live-browser.js index ed9606409..1b8926333 100644 --- a/.gemini/skills/impeccable/scripts/live-browser.js +++ b/.gemini/skills/impeccable/scripts/live-browser.js @@ -8189,7 +8189,7 @@ void main() { let voiceInterimBase = ''; /** @type {{ mode: 'steer'|'configure', input: HTMLInputElement, submit: () => void, beforeStart?: () => void } | null} */ let voiceCtx = null; - const PAGE_CHAT_COLLAPSED_W = '88px'; + const PAGE_CHAT_COLLAPSED_W = '104px'; const PAGE_CHAT_PROCESSING_W = '76px'; const PAGE_CHAT_PLACEHOLDER_COLLAPSED = 'Steer…'; const PAGE_CHAT_PLACEHOLDER_EXPANDED = 'Steer the page…'; @@ -8200,7 +8200,7 @@ void main() { const GLOBAL_BAR_SECTION_GAP = 8; const GLOBAL_BAR_INNER_GAP = 2; const GLOBAL_BAR_INNER_PAD_LEFT = 2; - const PAGE_CHAT_EXPANDED_W = 'min(280px, 38vw)'; + const PAGE_CHAT_EXPANDED_MAX_W = 280; const ICON_PAGE_CHAT = ''; const ICON_PAGE_VOICE = @@ -8280,6 +8280,43 @@ void main() { return barPaletteForTheme(globalBarEl?.dataset.theme || detectPageTheme()); } + function globalBarModeToggles() { + return [ + uiGetById(PREFIX + '-pick-toggle'), + uiGetById(PREFIX + '-insert-toggle'), + uiGetById(PREFIX + '-detect-toggle'), + uiGetById(PREFIX + '-design-toggle'), + ].filter(Boolean); + } + + function syncGlobalBarExpandedLabels(expanded = globalBarEl?.matches(':hover')) { + globalBarModeToggles().forEach((toggle) => { + if (expanded && !pageChatExpanded) toggle._expandLabel?.(); + else if (toggle.dataset.active === 'true') toggle._expandLabel?.(); + else toggle._collapseLabel?.(); + }); + } + + function pageChatCollapsedWidthPx() { + const parsed = parseFloat(PAGE_CHAT_COLLAPSED_W); + return Number.isFinite(parsed) ? parsed : 104; + } + + function pageChatExpandedWidth() { + if (!pageChatEl || !globalBarEl) return PAGE_CHAT_EXPANDED_MAX_W + 'px'; + const currentChatWidth = pageChatEl.getBoundingClientRect().width || pageChatCollapsedWidthPx(); + const barWidth = globalBarEl.getBoundingClientRect().width || 0; + const nonChatWidth = Math.max(0, barWidth - currentChatWidth); + const available = window.innerWidth - 16 - nonChatWidth; + const next = Math.max(pageChatCollapsedWidthPx(), Math.min(PAGE_CHAT_EXPANDED_MAX_W, available)); + return Math.round(next) + 'px'; + } + + function syncPageChatExpandedWidth() { + if (!pageChatEl || !pageChatExpanded) return; + pageChatEl.style.width = pageChatExpandedWidth(); + } + function syncPageChatChrome() { if (!pageChatEl) return; const P = pageChatPalette(); @@ -8581,7 +8618,8 @@ void main() { if (!pageChatEl || !pageChatInput) return false; pageChatExpanded = true; pageChatEl.dataset.expanded = 'true'; - pageChatEl.style.width = PAGE_CHAT_EXPANDED_W; + syncGlobalBarExpandedLabels(false); + pageChatEl.style.width = pageChatExpandedWidth(); pageChatEl.style.cursor = steerLocked ? 'default' : 'text'; pageChatInput.placeholder = PAGE_CHAT_PLACEHOLDER_EXPANDED; if (pageChatHint) { @@ -8676,7 +8714,7 @@ void main() { pageChatEl.setAttribute('aria-label', 'Steer the page'); pageChatExpanded = keepExpanded; pageChatEl.dataset.expanded = keepExpanded ? 'true' : 'false'; - pageChatEl.style.width = keepExpanded ? PAGE_CHAT_EXPANDED_W : PAGE_CHAT_COLLAPSED_W; + pageChatEl.style.width = keepExpanded ? pageChatExpandedWidth() : PAGE_CHAT_COLLAPSED_W; pageChatEl.style.cursor = 'pointer'; if (pageChatInput) { pageChatInput.disabled = false; @@ -8990,6 +9028,7 @@ void main() { pageChatEl.dataset.expanded = 'false'; pageChatEl.style.width = PAGE_CHAT_COLLAPSED_W; pageChatEl.style.cursor = 'pointer'; + syncGlobalBarExpandedLabels(globalBarEl?.matches(':hover')); if (blur) { pageChatInput.blur(); pageChatInput.style.pointerEvents = 'none'; @@ -9296,6 +9335,8 @@ void main() { fontFamily: FONT, fontSize: '12px', lineHeight: '1', opacity: '0', overflow: 'hidden', // clip the full-bleed brand mark to the bar radius + maxWidth: 'calc(100vw - 16px)', + boxSizing: 'border-box', transition: 'opacity 0.3s ' + EASE + ', transform 0.3s ' + EASE, }); globalBarEl.id = PREFIX + '-global-bar'; @@ -9344,6 +9385,8 @@ void main() { const inner = el('div', { display: 'flex', alignItems: 'center', padding: '4px 5px 4px ' + GLOBAL_BAR_INNER_PAD_LEFT + 'px', gap: GLOBAL_BAR_INNER_GAP + 'px', + minWidth: '0', + flex: '1 1 auto', }); inner.id = PREFIX + '-global-bar-inner'; globalBarEl.appendChild(inner); @@ -9623,6 +9666,7 @@ void main() { width: '1px', height: '18px', background: P.hairline, margin: '0 4px 0 2px', + flexShrink: '0', }); inner.appendChild(divider); @@ -9639,6 +9683,7 @@ void main() { display: 'inline-flex', alignItems: 'center', justifyContent: 'center', padding: '0', boxSizing: 'border-box', width: '24px', height: '24px', borderRadius: '6px', + flexShrink: '0', border: 'none', background: 'transparent', color: P.textDim, fontFamily: FONT, fontSize: '0', lineHeight: '0', cursor: 'pointer', transition: 'color 0.12s ease, background 0.12s ease', @@ -9651,16 +9696,16 @@ void main() { exitBtn.addEventListener('click', () => { sendEvent({ type: 'exit' }); teardown(); }); inner.appendChild(exitBtn); - // Bar-level hover: expand every toggle's label at once; collapse on leave. + // Bar-level hover: expand mode labels unless Steer is using the space. // Buttons with dataset.active="true" ignore collapse (their label stays). - const toggles = [pickBtn, insertBtn, detectBtn, designBtn]; globalBarEl.addEventListener('mouseenter', () => { - toggles.forEach((t) => t._expandLabel && t._expandLabel()); + syncGlobalBarExpandedLabels(true); + syncPageChatExpandedWidth(); schedulePendingDockPosition(); setTimeout(schedulePendingDockPosition, 260); }); globalBarEl.addEventListener('mouseleave', () => { - toggles.forEach((t) => t._collapseLabel && t._collapseLabel()); + syncGlobalBarExpandedLabels(false); schedulePendingDockPosition(); setTimeout(schedulePendingDockPosition, 260); }); @@ -9678,6 +9723,7 @@ void main() { pendingDockResizeObserver.observe(globalBarEl); } window.addEventListener('resize', positionPendingDock); + window.addEventListener('resize', syncPageChatExpandedWidth); requestAnimationFrame(() => { globalBarEl.style.opacity = '1'; @@ -9724,9 +9770,7 @@ void main() { // If the bar is currently under the cursor, keep all labels expanded - // otherwise clicking a toggle that deactivates (e.g. closing DESIGN.md) // would collapse its label while the user's mouse is still on the bar. - if (globalBarEl && globalBarEl.matches(':hover')) { - [pickToggle, insertToggle, detectToggle, designToggle].forEach((t) => t?._expandLabel?.()); - } + syncGlobalBarExpandedLabels(globalBarEl && globalBarEl.matches(':hover')); if (detectBadge) { detectBadge.style.display = (detectActive && detectCount > 0) ? 'inline' : 'none'; diff --git a/.github/skills/impeccable/scripts/live-browser.js b/.github/skills/impeccable/scripts/live-browser.js index ed9606409..1b8926333 100644 --- a/.github/skills/impeccable/scripts/live-browser.js +++ b/.github/skills/impeccable/scripts/live-browser.js @@ -8189,7 +8189,7 @@ void main() { let voiceInterimBase = ''; /** @type {{ mode: 'steer'|'configure', input: HTMLInputElement, submit: () => void, beforeStart?: () => void } | null} */ let voiceCtx = null; - const PAGE_CHAT_COLLAPSED_W = '88px'; + const PAGE_CHAT_COLLAPSED_W = '104px'; const PAGE_CHAT_PROCESSING_W = '76px'; const PAGE_CHAT_PLACEHOLDER_COLLAPSED = 'Steer…'; const PAGE_CHAT_PLACEHOLDER_EXPANDED = 'Steer the page…'; @@ -8200,7 +8200,7 @@ void main() { const GLOBAL_BAR_SECTION_GAP = 8; const GLOBAL_BAR_INNER_GAP = 2; const GLOBAL_BAR_INNER_PAD_LEFT = 2; - const PAGE_CHAT_EXPANDED_W = 'min(280px, 38vw)'; + const PAGE_CHAT_EXPANDED_MAX_W = 280; const ICON_PAGE_CHAT = ''; const ICON_PAGE_VOICE = @@ -8280,6 +8280,43 @@ void main() { return barPaletteForTheme(globalBarEl?.dataset.theme || detectPageTheme()); } + function globalBarModeToggles() { + return [ + uiGetById(PREFIX + '-pick-toggle'), + uiGetById(PREFIX + '-insert-toggle'), + uiGetById(PREFIX + '-detect-toggle'), + uiGetById(PREFIX + '-design-toggle'), + ].filter(Boolean); + } + + function syncGlobalBarExpandedLabels(expanded = globalBarEl?.matches(':hover')) { + globalBarModeToggles().forEach((toggle) => { + if (expanded && !pageChatExpanded) toggle._expandLabel?.(); + else if (toggle.dataset.active === 'true') toggle._expandLabel?.(); + else toggle._collapseLabel?.(); + }); + } + + function pageChatCollapsedWidthPx() { + const parsed = parseFloat(PAGE_CHAT_COLLAPSED_W); + return Number.isFinite(parsed) ? parsed : 104; + } + + function pageChatExpandedWidth() { + if (!pageChatEl || !globalBarEl) return PAGE_CHAT_EXPANDED_MAX_W + 'px'; + const currentChatWidth = pageChatEl.getBoundingClientRect().width || pageChatCollapsedWidthPx(); + const barWidth = globalBarEl.getBoundingClientRect().width || 0; + const nonChatWidth = Math.max(0, barWidth - currentChatWidth); + const available = window.innerWidth - 16 - nonChatWidth; + const next = Math.max(pageChatCollapsedWidthPx(), Math.min(PAGE_CHAT_EXPANDED_MAX_W, available)); + return Math.round(next) + 'px'; + } + + function syncPageChatExpandedWidth() { + if (!pageChatEl || !pageChatExpanded) return; + pageChatEl.style.width = pageChatExpandedWidth(); + } + function syncPageChatChrome() { if (!pageChatEl) return; const P = pageChatPalette(); @@ -8581,7 +8618,8 @@ void main() { if (!pageChatEl || !pageChatInput) return false; pageChatExpanded = true; pageChatEl.dataset.expanded = 'true'; - pageChatEl.style.width = PAGE_CHAT_EXPANDED_W; + syncGlobalBarExpandedLabels(false); + pageChatEl.style.width = pageChatExpandedWidth(); pageChatEl.style.cursor = steerLocked ? 'default' : 'text'; pageChatInput.placeholder = PAGE_CHAT_PLACEHOLDER_EXPANDED; if (pageChatHint) { @@ -8676,7 +8714,7 @@ void main() { pageChatEl.setAttribute('aria-label', 'Steer the page'); pageChatExpanded = keepExpanded; pageChatEl.dataset.expanded = keepExpanded ? 'true' : 'false'; - pageChatEl.style.width = keepExpanded ? PAGE_CHAT_EXPANDED_W : PAGE_CHAT_COLLAPSED_W; + pageChatEl.style.width = keepExpanded ? pageChatExpandedWidth() : PAGE_CHAT_COLLAPSED_W; pageChatEl.style.cursor = 'pointer'; if (pageChatInput) { pageChatInput.disabled = false; @@ -8990,6 +9028,7 @@ void main() { pageChatEl.dataset.expanded = 'false'; pageChatEl.style.width = PAGE_CHAT_COLLAPSED_W; pageChatEl.style.cursor = 'pointer'; + syncGlobalBarExpandedLabels(globalBarEl?.matches(':hover')); if (blur) { pageChatInput.blur(); pageChatInput.style.pointerEvents = 'none'; @@ -9296,6 +9335,8 @@ void main() { fontFamily: FONT, fontSize: '12px', lineHeight: '1', opacity: '0', overflow: 'hidden', // clip the full-bleed brand mark to the bar radius + maxWidth: 'calc(100vw - 16px)', + boxSizing: 'border-box', transition: 'opacity 0.3s ' + EASE + ', transform 0.3s ' + EASE, }); globalBarEl.id = PREFIX + '-global-bar'; @@ -9344,6 +9385,8 @@ void main() { const inner = el('div', { display: 'flex', alignItems: 'center', padding: '4px 5px 4px ' + GLOBAL_BAR_INNER_PAD_LEFT + 'px', gap: GLOBAL_BAR_INNER_GAP + 'px', + minWidth: '0', + flex: '1 1 auto', }); inner.id = PREFIX + '-global-bar-inner'; globalBarEl.appendChild(inner); @@ -9623,6 +9666,7 @@ void main() { width: '1px', height: '18px', background: P.hairline, margin: '0 4px 0 2px', + flexShrink: '0', }); inner.appendChild(divider); @@ -9639,6 +9683,7 @@ void main() { display: 'inline-flex', alignItems: 'center', justifyContent: 'center', padding: '0', boxSizing: 'border-box', width: '24px', height: '24px', borderRadius: '6px', + flexShrink: '0', border: 'none', background: 'transparent', color: P.textDim, fontFamily: FONT, fontSize: '0', lineHeight: '0', cursor: 'pointer', transition: 'color 0.12s ease, background 0.12s ease', @@ -9651,16 +9696,16 @@ void main() { exitBtn.addEventListener('click', () => { sendEvent({ type: 'exit' }); teardown(); }); inner.appendChild(exitBtn); - // Bar-level hover: expand every toggle's label at once; collapse on leave. + // Bar-level hover: expand mode labels unless Steer is using the space. // Buttons with dataset.active="true" ignore collapse (their label stays). - const toggles = [pickBtn, insertBtn, detectBtn, designBtn]; globalBarEl.addEventListener('mouseenter', () => { - toggles.forEach((t) => t._expandLabel && t._expandLabel()); + syncGlobalBarExpandedLabels(true); + syncPageChatExpandedWidth(); schedulePendingDockPosition(); setTimeout(schedulePendingDockPosition, 260); }); globalBarEl.addEventListener('mouseleave', () => { - toggles.forEach((t) => t._collapseLabel && t._collapseLabel()); + syncGlobalBarExpandedLabels(false); schedulePendingDockPosition(); setTimeout(schedulePendingDockPosition, 260); }); @@ -9678,6 +9723,7 @@ void main() { pendingDockResizeObserver.observe(globalBarEl); } window.addEventListener('resize', positionPendingDock); + window.addEventListener('resize', syncPageChatExpandedWidth); requestAnimationFrame(() => { globalBarEl.style.opacity = '1'; @@ -9724,9 +9770,7 @@ void main() { // If the bar is currently under the cursor, keep all labels expanded - // otherwise clicking a toggle that deactivates (e.g. closing DESIGN.md) // would collapse its label while the user's mouse is still on the bar. - if (globalBarEl && globalBarEl.matches(':hover')) { - [pickToggle, insertToggle, detectToggle, designToggle].forEach((t) => t?._expandLabel?.()); - } + syncGlobalBarExpandedLabels(globalBarEl && globalBarEl.matches(':hover')); if (detectBadge) { detectBadge.style.display = (detectActive && detectCount > 0) ? 'inline' : 'none'; diff --git a/.kiro/skills/impeccable/scripts/live-browser.js b/.kiro/skills/impeccable/scripts/live-browser.js index ed9606409..1b8926333 100644 --- a/.kiro/skills/impeccable/scripts/live-browser.js +++ b/.kiro/skills/impeccable/scripts/live-browser.js @@ -8189,7 +8189,7 @@ void main() { let voiceInterimBase = ''; /** @type {{ mode: 'steer'|'configure', input: HTMLInputElement, submit: () => void, beforeStart?: () => void } | null} */ let voiceCtx = null; - const PAGE_CHAT_COLLAPSED_W = '88px'; + const PAGE_CHAT_COLLAPSED_W = '104px'; const PAGE_CHAT_PROCESSING_W = '76px'; const PAGE_CHAT_PLACEHOLDER_COLLAPSED = 'Steer…'; const PAGE_CHAT_PLACEHOLDER_EXPANDED = 'Steer the page…'; @@ -8200,7 +8200,7 @@ void main() { const GLOBAL_BAR_SECTION_GAP = 8; const GLOBAL_BAR_INNER_GAP = 2; const GLOBAL_BAR_INNER_PAD_LEFT = 2; - const PAGE_CHAT_EXPANDED_W = 'min(280px, 38vw)'; + const PAGE_CHAT_EXPANDED_MAX_W = 280; const ICON_PAGE_CHAT = ''; const ICON_PAGE_VOICE = @@ -8280,6 +8280,43 @@ void main() { return barPaletteForTheme(globalBarEl?.dataset.theme || detectPageTheme()); } + function globalBarModeToggles() { + return [ + uiGetById(PREFIX + '-pick-toggle'), + uiGetById(PREFIX + '-insert-toggle'), + uiGetById(PREFIX + '-detect-toggle'), + uiGetById(PREFIX + '-design-toggle'), + ].filter(Boolean); + } + + function syncGlobalBarExpandedLabels(expanded = globalBarEl?.matches(':hover')) { + globalBarModeToggles().forEach((toggle) => { + if (expanded && !pageChatExpanded) toggle._expandLabel?.(); + else if (toggle.dataset.active === 'true') toggle._expandLabel?.(); + else toggle._collapseLabel?.(); + }); + } + + function pageChatCollapsedWidthPx() { + const parsed = parseFloat(PAGE_CHAT_COLLAPSED_W); + return Number.isFinite(parsed) ? parsed : 104; + } + + function pageChatExpandedWidth() { + if (!pageChatEl || !globalBarEl) return PAGE_CHAT_EXPANDED_MAX_W + 'px'; + const currentChatWidth = pageChatEl.getBoundingClientRect().width || pageChatCollapsedWidthPx(); + const barWidth = globalBarEl.getBoundingClientRect().width || 0; + const nonChatWidth = Math.max(0, barWidth - currentChatWidth); + const available = window.innerWidth - 16 - nonChatWidth; + const next = Math.max(pageChatCollapsedWidthPx(), Math.min(PAGE_CHAT_EXPANDED_MAX_W, available)); + return Math.round(next) + 'px'; + } + + function syncPageChatExpandedWidth() { + if (!pageChatEl || !pageChatExpanded) return; + pageChatEl.style.width = pageChatExpandedWidth(); + } + function syncPageChatChrome() { if (!pageChatEl) return; const P = pageChatPalette(); @@ -8581,7 +8618,8 @@ void main() { if (!pageChatEl || !pageChatInput) return false; pageChatExpanded = true; pageChatEl.dataset.expanded = 'true'; - pageChatEl.style.width = PAGE_CHAT_EXPANDED_W; + syncGlobalBarExpandedLabels(false); + pageChatEl.style.width = pageChatExpandedWidth(); pageChatEl.style.cursor = steerLocked ? 'default' : 'text'; pageChatInput.placeholder = PAGE_CHAT_PLACEHOLDER_EXPANDED; if (pageChatHint) { @@ -8676,7 +8714,7 @@ void main() { pageChatEl.setAttribute('aria-label', 'Steer the page'); pageChatExpanded = keepExpanded; pageChatEl.dataset.expanded = keepExpanded ? 'true' : 'false'; - pageChatEl.style.width = keepExpanded ? PAGE_CHAT_EXPANDED_W : PAGE_CHAT_COLLAPSED_W; + pageChatEl.style.width = keepExpanded ? pageChatExpandedWidth() : PAGE_CHAT_COLLAPSED_W; pageChatEl.style.cursor = 'pointer'; if (pageChatInput) { pageChatInput.disabled = false; @@ -8990,6 +9028,7 @@ void main() { pageChatEl.dataset.expanded = 'false'; pageChatEl.style.width = PAGE_CHAT_COLLAPSED_W; pageChatEl.style.cursor = 'pointer'; + syncGlobalBarExpandedLabels(globalBarEl?.matches(':hover')); if (blur) { pageChatInput.blur(); pageChatInput.style.pointerEvents = 'none'; @@ -9296,6 +9335,8 @@ void main() { fontFamily: FONT, fontSize: '12px', lineHeight: '1', opacity: '0', overflow: 'hidden', // clip the full-bleed brand mark to the bar radius + maxWidth: 'calc(100vw - 16px)', + boxSizing: 'border-box', transition: 'opacity 0.3s ' + EASE + ', transform 0.3s ' + EASE, }); globalBarEl.id = PREFIX + '-global-bar'; @@ -9344,6 +9385,8 @@ void main() { const inner = el('div', { display: 'flex', alignItems: 'center', padding: '4px 5px 4px ' + GLOBAL_BAR_INNER_PAD_LEFT + 'px', gap: GLOBAL_BAR_INNER_GAP + 'px', + minWidth: '0', + flex: '1 1 auto', }); inner.id = PREFIX + '-global-bar-inner'; globalBarEl.appendChild(inner); @@ -9623,6 +9666,7 @@ void main() { width: '1px', height: '18px', background: P.hairline, margin: '0 4px 0 2px', + flexShrink: '0', }); inner.appendChild(divider); @@ -9639,6 +9683,7 @@ void main() { display: 'inline-flex', alignItems: 'center', justifyContent: 'center', padding: '0', boxSizing: 'border-box', width: '24px', height: '24px', borderRadius: '6px', + flexShrink: '0', border: 'none', background: 'transparent', color: P.textDim, fontFamily: FONT, fontSize: '0', lineHeight: '0', cursor: 'pointer', transition: 'color 0.12s ease, background 0.12s ease', @@ -9651,16 +9696,16 @@ void main() { exitBtn.addEventListener('click', () => { sendEvent({ type: 'exit' }); teardown(); }); inner.appendChild(exitBtn); - // Bar-level hover: expand every toggle's label at once; collapse on leave. + // Bar-level hover: expand mode labels unless Steer is using the space. // Buttons with dataset.active="true" ignore collapse (their label stays). - const toggles = [pickBtn, insertBtn, detectBtn, designBtn]; globalBarEl.addEventListener('mouseenter', () => { - toggles.forEach((t) => t._expandLabel && t._expandLabel()); + syncGlobalBarExpandedLabels(true); + syncPageChatExpandedWidth(); schedulePendingDockPosition(); setTimeout(schedulePendingDockPosition, 260); }); globalBarEl.addEventListener('mouseleave', () => { - toggles.forEach((t) => t._collapseLabel && t._collapseLabel()); + syncGlobalBarExpandedLabels(false); schedulePendingDockPosition(); setTimeout(schedulePendingDockPosition, 260); }); @@ -9678,6 +9723,7 @@ void main() { pendingDockResizeObserver.observe(globalBarEl); } window.addEventListener('resize', positionPendingDock); + window.addEventListener('resize', syncPageChatExpandedWidth); requestAnimationFrame(() => { globalBarEl.style.opacity = '1'; @@ -9724,9 +9770,7 @@ void main() { // If the bar is currently under the cursor, keep all labels expanded - // otherwise clicking a toggle that deactivates (e.g. closing DESIGN.md) // would collapse its label while the user's mouse is still on the bar. - if (globalBarEl && globalBarEl.matches(':hover')) { - [pickToggle, insertToggle, detectToggle, designToggle].forEach((t) => t?._expandLabel?.()); - } + syncGlobalBarExpandedLabels(globalBarEl && globalBarEl.matches(':hover')); if (detectBadge) { detectBadge.style.display = (detectActive && detectCount > 0) ? 'inline' : 'none'; diff --git a/.opencode/skills/impeccable/scripts/live-browser.js b/.opencode/skills/impeccable/scripts/live-browser.js index ed9606409..1b8926333 100644 --- a/.opencode/skills/impeccable/scripts/live-browser.js +++ b/.opencode/skills/impeccable/scripts/live-browser.js @@ -8189,7 +8189,7 @@ void main() { let voiceInterimBase = ''; /** @type {{ mode: 'steer'|'configure', input: HTMLInputElement, submit: () => void, beforeStart?: () => void } | null} */ let voiceCtx = null; - const PAGE_CHAT_COLLAPSED_W = '88px'; + const PAGE_CHAT_COLLAPSED_W = '104px'; const PAGE_CHAT_PROCESSING_W = '76px'; const PAGE_CHAT_PLACEHOLDER_COLLAPSED = 'Steer…'; const PAGE_CHAT_PLACEHOLDER_EXPANDED = 'Steer the page…'; @@ -8200,7 +8200,7 @@ void main() { const GLOBAL_BAR_SECTION_GAP = 8; const GLOBAL_BAR_INNER_GAP = 2; const GLOBAL_BAR_INNER_PAD_LEFT = 2; - const PAGE_CHAT_EXPANDED_W = 'min(280px, 38vw)'; + const PAGE_CHAT_EXPANDED_MAX_W = 280; const ICON_PAGE_CHAT = ''; const ICON_PAGE_VOICE = @@ -8280,6 +8280,43 @@ void main() { return barPaletteForTheme(globalBarEl?.dataset.theme || detectPageTheme()); } + function globalBarModeToggles() { + return [ + uiGetById(PREFIX + '-pick-toggle'), + uiGetById(PREFIX + '-insert-toggle'), + uiGetById(PREFIX + '-detect-toggle'), + uiGetById(PREFIX + '-design-toggle'), + ].filter(Boolean); + } + + function syncGlobalBarExpandedLabels(expanded = globalBarEl?.matches(':hover')) { + globalBarModeToggles().forEach((toggle) => { + if (expanded && !pageChatExpanded) toggle._expandLabel?.(); + else if (toggle.dataset.active === 'true') toggle._expandLabel?.(); + else toggle._collapseLabel?.(); + }); + } + + function pageChatCollapsedWidthPx() { + const parsed = parseFloat(PAGE_CHAT_COLLAPSED_W); + return Number.isFinite(parsed) ? parsed : 104; + } + + function pageChatExpandedWidth() { + if (!pageChatEl || !globalBarEl) return PAGE_CHAT_EXPANDED_MAX_W + 'px'; + const currentChatWidth = pageChatEl.getBoundingClientRect().width || pageChatCollapsedWidthPx(); + const barWidth = globalBarEl.getBoundingClientRect().width || 0; + const nonChatWidth = Math.max(0, barWidth - currentChatWidth); + const available = window.innerWidth - 16 - nonChatWidth; + const next = Math.max(pageChatCollapsedWidthPx(), Math.min(PAGE_CHAT_EXPANDED_MAX_W, available)); + return Math.round(next) + 'px'; + } + + function syncPageChatExpandedWidth() { + if (!pageChatEl || !pageChatExpanded) return; + pageChatEl.style.width = pageChatExpandedWidth(); + } + function syncPageChatChrome() { if (!pageChatEl) return; const P = pageChatPalette(); @@ -8581,7 +8618,8 @@ void main() { if (!pageChatEl || !pageChatInput) return false; pageChatExpanded = true; pageChatEl.dataset.expanded = 'true'; - pageChatEl.style.width = PAGE_CHAT_EXPANDED_W; + syncGlobalBarExpandedLabels(false); + pageChatEl.style.width = pageChatExpandedWidth(); pageChatEl.style.cursor = steerLocked ? 'default' : 'text'; pageChatInput.placeholder = PAGE_CHAT_PLACEHOLDER_EXPANDED; if (pageChatHint) { @@ -8676,7 +8714,7 @@ void main() { pageChatEl.setAttribute('aria-label', 'Steer the page'); pageChatExpanded = keepExpanded; pageChatEl.dataset.expanded = keepExpanded ? 'true' : 'false'; - pageChatEl.style.width = keepExpanded ? PAGE_CHAT_EXPANDED_W : PAGE_CHAT_COLLAPSED_W; + pageChatEl.style.width = keepExpanded ? pageChatExpandedWidth() : PAGE_CHAT_COLLAPSED_W; pageChatEl.style.cursor = 'pointer'; if (pageChatInput) { pageChatInput.disabled = false; @@ -8990,6 +9028,7 @@ void main() { pageChatEl.dataset.expanded = 'false'; pageChatEl.style.width = PAGE_CHAT_COLLAPSED_W; pageChatEl.style.cursor = 'pointer'; + syncGlobalBarExpandedLabels(globalBarEl?.matches(':hover')); if (blur) { pageChatInput.blur(); pageChatInput.style.pointerEvents = 'none'; @@ -9296,6 +9335,8 @@ void main() { fontFamily: FONT, fontSize: '12px', lineHeight: '1', opacity: '0', overflow: 'hidden', // clip the full-bleed brand mark to the bar radius + maxWidth: 'calc(100vw - 16px)', + boxSizing: 'border-box', transition: 'opacity 0.3s ' + EASE + ', transform 0.3s ' + EASE, }); globalBarEl.id = PREFIX + '-global-bar'; @@ -9344,6 +9385,8 @@ void main() { const inner = el('div', { display: 'flex', alignItems: 'center', padding: '4px 5px 4px ' + GLOBAL_BAR_INNER_PAD_LEFT + 'px', gap: GLOBAL_BAR_INNER_GAP + 'px', + minWidth: '0', + flex: '1 1 auto', }); inner.id = PREFIX + '-global-bar-inner'; globalBarEl.appendChild(inner); @@ -9623,6 +9666,7 @@ void main() { width: '1px', height: '18px', background: P.hairline, margin: '0 4px 0 2px', + flexShrink: '0', }); inner.appendChild(divider); @@ -9639,6 +9683,7 @@ void main() { display: 'inline-flex', alignItems: 'center', justifyContent: 'center', padding: '0', boxSizing: 'border-box', width: '24px', height: '24px', borderRadius: '6px', + flexShrink: '0', border: 'none', background: 'transparent', color: P.textDim, fontFamily: FONT, fontSize: '0', lineHeight: '0', cursor: 'pointer', transition: 'color 0.12s ease, background 0.12s ease', @@ -9651,16 +9696,16 @@ void main() { exitBtn.addEventListener('click', () => { sendEvent({ type: 'exit' }); teardown(); }); inner.appendChild(exitBtn); - // Bar-level hover: expand every toggle's label at once; collapse on leave. + // Bar-level hover: expand mode labels unless Steer is using the space. // Buttons with dataset.active="true" ignore collapse (their label stays). - const toggles = [pickBtn, insertBtn, detectBtn, designBtn]; globalBarEl.addEventListener('mouseenter', () => { - toggles.forEach((t) => t._expandLabel && t._expandLabel()); + syncGlobalBarExpandedLabels(true); + syncPageChatExpandedWidth(); schedulePendingDockPosition(); setTimeout(schedulePendingDockPosition, 260); }); globalBarEl.addEventListener('mouseleave', () => { - toggles.forEach((t) => t._collapseLabel && t._collapseLabel()); + syncGlobalBarExpandedLabels(false); schedulePendingDockPosition(); setTimeout(schedulePendingDockPosition, 260); }); @@ -9678,6 +9723,7 @@ void main() { pendingDockResizeObserver.observe(globalBarEl); } window.addEventListener('resize', positionPendingDock); + window.addEventListener('resize', syncPageChatExpandedWidth); requestAnimationFrame(() => { globalBarEl.style.opacity = '1'; @@ -9724,9 +9770,7 @@ void main() { // If the bar is currently under the cursor, keep all labels expanded - // otherwise clicking a toggle that deactivates (e.g. closing DESIGN.md) // would collapse its label while the user's mouse is still on the bar. - if (globalBarEl && globalBarEl.matches(':hover')) { - [pickToggle, insertToggle, detectToggle, designToggle].forEach((t) => t?._expandLabel?.()); - } + syncGlobalBarExpandedLabels(globalBarEl && globalBarEl.matches(':hover')); if (detectBadge) { detectBadge.style.display = (detectActive && detectCount > 0) ? 'inline' : 'none'; diff --git a/.pi/skills/impeccable/scripts/live-browser.js b/.pi/skills/impeccable/scripts/live-browser.js index ed9606409..1b8926333 100644 --- a/.pi/skills/impeccable/scripts/live-browser.js +++ b/.pi/skills/impeccable/scripts/live-browser.js @@ -8189,7 +8189,7 @@ void main() { let voiceInterimBase = ''; /** @type {{ mode: 'steer'|'configure', input: HTMLInputElement, submit: () => void, beforeStart?: () => void } | null} */ let voiceCtx = null; - const PAGE_CHAT_COLLAPSED_W = '88px'; + const PAGE_CHAT_COLLAPSED_W = '104px'; const PAGE_CHAT_PROCESSING_W = '76px'; const PAGE_CHAT_PLACEHOLDER_COLLAPSED = 'Steer…'; const PAGE_CHAT_PLACEHOLDER_EXPANDED = 'Steer the page…'; @@ -8200,7 +8200,7 @@ void main() { const GLOBAL_BAR_SECTION_GAP = 8; const GLOBAL_BAR_INNER_GAP = 2; const GLOBAL_BAR_INNER_PAD_LEFT = 2; - const PAGE_CHAT_EXPANDED_W = 'min(280px, 38vw)'; + const PAGE_CHAT_EXPANDED_MAX_W = 280; const ICON_PAGE_CHAT = ''; const ICON_PAGE_VOICE = @@ -8280,6 +8280,43 @@ void main() { return barPaletteForTheme(globalBarEl?.dataset.theme || detectPageTheme()); } + function globalBarModeToggles() { + return [ + uiGetById(PREFIX + '-pick-toggle'), + uiGetById(PREFIX + '-insert-toggle'), + uiGetById(PREFIX + '-detect-toggle'), + uiGetById(PREFIX + '-design-toggle'), + ].filter(Boolean); + } + + function syncGlobalBarExpandedLabels(expanded = globalBarEl?.matches(':hover')) { + globalBarModeToggles().forEach((toggle) => { + if (expanded && !pageChatExpanded) toggle._expandLabel?.(); + else if (toggle.dataset.active === 'true') toggle._expandLabel?.(); + else toggle._collapseLabel?.(); + }); + } + + function pageChatCollapsedWidthPx() { + const parsed = parseFloat(PAGE_CHAT_COLLAPSED_W); + return Number.isFinite(parsed) ? parsed : 104; + } + + function pageChatExpandedWidth() { + if (!pageChatEl || !globalBarEl) return PAGE_CHAT_EXPANDED_MAX_W + 'px'; + const currentChatWidth = pageChatEl.getBoundingClientRect().width || pageChatCollapsedWidthPx(); + const barWidth = globalBarEl.getBoundingClientRect().width || 0; + const nonChatWidth = Math.max(0, barWidth - currentChatWidth); + const available = window.innerWidth - 16 - nonChatWidth; + const next = Math.max(pageChatCollapsedWidthPx(), Math.min(PAGE_CHAT_EXPANDED_MAX_W, available)); + return Math.round(next) + 'px'; + } + + function syncPageChatExpandedWidth() { + if (!pageChatEl || !pageChatExpanded) return; + pageChatEl.style.width = pageChatExpandedWidth(); + } + function syncPageChatChrome() { if (!pageChatEl) return; const P = pageChatPalette(); @@ -8581,7 +8618,8 @@ void main() { if (!pageChatEl || !pageChatInput) return false; pageChatExpanded = true; pageChatEl.dataset.expanded = 'true'; - pageChatEl.style.width = PAGE_CHAT_EXPANDED_W; + syncGlobalBarExpandedLabels(false); + pageChatEl.style.width = pageChatExpandedWidth(); pageChatEl.style.cursor = steerLocked ? 'default' : 'text'; pageChatInput.placeholder = PAGE_CHAT_PLACEHOLDER_EXPANDED; if (pageChatHint) { @@ -8676,7 +8714,7 @@ void main() { pageChatEl.setAttribute('aria-label', 'Steer the page'); pageChatExpanded = keepExpanded; pageChatEl.dataset.expanded = keepExpanded ? 'true' : 'false'; - pageChatEl.style.width = keepExpanded ? PAGE_CHAT_EXPANDED_W : PAGE_CHAT_COLLAPSED_W; + pageChatEl.style.width = keepExpanded ? pageChatExpandedWidth() : PAGE_CHAT_COLLAPSED_W; pageChatEl.style.cursor = 'pointer'; if (pageChatInput) { pageChatInput.disabled = false; @@ -8990,6 +9028,7 @@ void main() { pageChatEl.dataset.expanded = 'false'; pageChatEl.style.width = PAGE_CHAT_COLLAPSED_W; pageChatEl.style.cursor = 'pointer'; + syncGlobalBarExpandedLabels(globalBarEl?.matches(':hover')); if (blur) { pageChatInput.blur(); pageChatInput.style.pointerEvents = 'none'; @@ -9296,6 +9335,8 @@ void main() { fontFamily: FONT, fontSize: '12px', lineHeight: '1', opacity: '0', overflow: 'hidden', // clip the full-bleed brand mark to the bar radius + maxWidth: 'calc(100vw - 16px)', + boxSizing: 'border-box', transition: 'opacity 0.3s ' + EASE + ', transform 0.3s ' + EASE, }); globalBarEl.id = PREFIX + '-global-bar'; @@ -9344,6 +9385,8 @@ void main() { const inner = el('div', { display: 'flex', alignItems: 'center', padding: '4px 5px 4px ' + GLOBAL_BAR_INNER_PAD_LEFT + 'px', gap: GLOBAL_BAR_INNER_GAP + 'px', + minWidth: '0', + flex: '1 1 auto', }); inner.id = PREFIX + '-global-bar-inner'; globalBarEl.appendChild(inner); @@ -9623,6 +9666,7 @@ void main() { width: '1px', height: '18px', background: P.hairline, margin: '0 4px 0 2px', + flexShrink: '0', }); inner.appendChild(divider); @@ -9639,6 +9683,7 @@ void main() { display: 'inline-flex', alignItems: 'center', justifyContent: 'center', padding: '0', boxSizing: 'border-box', width: '24px', height: '24px', borderRadius: '6px', + flexShrink: '0', border: 'none', background: 'transparent', color: P.textDim, fontFamily: FONT, fontSize: '0', lineHeight: '0', cursor: 'pointer', transition: 'color 0.12s ease, background 0.12s ease', @@ -9651,16 +9696,16 @@ void main() { exitBtn.addEventListener('click', () => { sendEvent({ type: 'exit' }); teardown(); }); inner.appendChild(exitBtn); - // Bar-level hover: expand every toggle's label at once; collapse on leave. + // Bar-level hover: expand mode labels unless Steer is using the space. // Buttons with dataset.active="true" ignore collapse (their label stays). - const toggles = [pickBtn, insertBtn, detectBtn, designBtn]; globalBarEl.addEventListener('mouseenter', () => { - toggles.forEach((t) => t._expandLabel && t._expandLabel()); + syncGlobalBarExpandedLabels(true); + syncPageChatExpandedWidth(); schedulePendingDockPosition(); setTimeout(schedulePendingDockPosition, 260); }); globalBarEl.addEventListener('mouseleave', () => { - toggles.forEach((t) => t._collapseLabel && t._collapseLabel()); + syncGlobalBarExpandedLabels(false); schedulePendingDockPosition(); setTimeout(schedulePendingDockPosition, 260); }); @@ -9678,6 +9723,7 @@ void main() { pendingDockResizeObserver.observe(globalBarEl); } window.addEventListener('resize', positionPendingDock); + window.addEventListener('resize', syncPageChatExpandedWidth); requestAnimationFrame(() => { globalBarEl.style.opacity = '1'; @@ -9724,9 +9770,7 @@ void main() { // If the bar is currently under the cursor, keep all labels expanded - // otherwise clicking a toggle that deactivates (e.g. closing DESIGN.md) // would collapse its label while the user's mouse is still on the bar. - if (globalBarEl && globalBarEl.matches(':hover')) { - [pickToggle, insertToggle, detectToggle, designToggle].forEach((t) => t?._expandLabel?.()); - } + syncGlobalBarExpandedLabels(globalBarEl && globalBarEl.matches(':hover')); if (detectBadge) { detectBadge.style.display = (detectActive && detectCount > 0) ? 'inline' : 'none'; diff --git a/.qoder/skills/impeccable/scripts/live-browser.js b/.qoder/skills/impeccable/scripts/live-browser.js index ed9606409..1b8926333 100644 --- a/.qoder/skills/impeccable/scripts/live-browser.js +++ b/.qoder/skills/impeccable/scripts/live-browser.js @@ -8189,7 +8189,7 @@ void main() { let voiceInterimBase = ''; /** @type {{ mode: 'steer'|'configure', input: HTMLInputElement, submit: () => void, beforeStart?: () => void } | null} */ let voiceCtx = null; - const PAGE_CHAT_COLLAPSED_W = '88px'; + const PAGE_CHAT_COLLAPSED_W = '104px'; const PAGE_CHAT_PROCESSING_W = '76px'; const PAGE_CHAT_PLACEHOLDER_COLLAPSED = 'Steer…'; const PAGE_CHAT_PLACEHOLDER_EXPANDED = 'Steer the page…'; @@ -8200,7 +8200,7 @@ void main() { const GLOBAL_BAR_SECTION_GAP = 8; const GLOBAL_BAR_INNER_GAP = 2; const GLOBAL_BAR_INNER_PAD_LEFT = 2; - const PAGE_CHAT_EXPANDED_W = 'min(280px, 38vw)'; + const PAGE_CHAT_EXPANDED_MAX_W = 280; const ICON_PAGE_CHAT = ''; const ICON_PAGE_VOICE = @@ -8280,6 +8280,43 @@ void main() { return barPaletteForTheme(globalBarEl?.dataset.theme || detectPageTheme()); } + function globalBarModeToggles() { + return [ + uiGetById(PREFIX + '-pick-toggle'), + uiGetById(PREFIX + '-insert-toggle'), + uiGetById(PREFIX + '-detect-toggle'), + uiGetById(PREFIX + '-design-toggle'), + ].filter(Boolean); + } + + function syncGlobalBarExpandedLabels(expanded = globalBarEl?.matches(':hover')) { + globalBarModeToggles().forEach((toggle) => { + if (expanded && !pageChatExpanded) toggle._expandLabel?.(); + else if (toggle.dataset.active === 'true') toggle._expandLabel?.(); + else toggle._collapseLabel?.(); + }); + } + + function pageChatCollapsedWidthPx() { + const parsed = parseFloat(PAGE_CHAT_COLLAPSED_W); + return Number.isFinite(parsed) ? parsed : 104; + } + + function pageChatExpandedWidth() { + if (!pageChatEl || !globalBarEl) return PAGE_CHAT_EXPANDED_MAX_W + 'px'; + const currentChatWidth = pageChatEl.getBoundingClientRect().width || pageChatCollapsedWidthPx(); + const barWidth = globalBarEl.getBoundingClientRect().width || 0; + const nonChatWidth = Math.max(0, barWidth - currentChatWidth); + const available = window.innerWidth - 16 - nonChatWidth; + const next = Math.max(pageChatCollapsedWidthPx(), Math.min(PAGE_CHAT_EXPANDED_MAX_W, available)); + return Math.round(next) + 'px'; + } + + function syncPageChatExpandedWidth() { + if (!pageChatEl || !pageChatExpanded) return; + pageChatEl.style.width = pageChatExpandedWidth(); + } + function syncPageChatChrome() { if (!pageChatEl) return; const P = pageChatPalette(); @@ -8581,7 +8618,8 @@ void main() { if (!pageChatEl || !pageChatInput) return false; pageChatExpanded = true; pageChatEl.dataset.expanded = 'true'; - pageChatEl.style.width = PAGE_CHAT_EXPANDED_W; + syncGlobalBarExpandedLabels(false); + pageChatEl.style.width = pageChatExpandedWidth(); pageChatEl.style.cursor = steerLocked ? 'default' : 'text'; pageChatInput.placeholder = PAGE_CHAT_PLACEHOLDER_EXPANDED; if (pageChatHint) { @@ -8676,7 +8714,7 @@ void main() { pageChatEl.setAttribute('aria-label', 'Steer the page'); pageChatExpanded = keepExpanded; pageChatEl.dataset.expanded = keepExpanded ? 'true' : 'false'; - pageChatEl.style.width = keepExpanded ? PAGE_CHAT_EXPANDED_W : PAGE_CHAT_COLLAPSED_W; + pageChatEl.style.width = keepExpanded ? pageChatExpandedWidth() : PAGE_CHAT_COLLAPSED_W; pageChatEl.style.cursor = 'pointer'; if (pageChatInput) { pageChatInput.disabled = false; @@ -8990,6 +9028,7 @@ void main() { pageChatEl.dataset.expanded = 'false'; pageChatEl.style.width = PAGE_CHAT_COLLAPSED_W; pageChatEl.style.cursor = 'pointer'; + syncGlobalBarExpandedLabels(globalBarEl?.matches(':hover')); if (blur) { pageChatInput.blur(); pageChatInput.style.pointerEvents = 'none'; @@ -9296,6 +9335,8 @@ void main() { fontFamily: FONT, fontSize: '12px', lineHeight: '1', opacity: '0', overflow: 'hidden', // clip the full-bleed brand mark to the bar radius + maxWidth: 'calc(100vw - 16px)', + boxSizing: 'border-box', transition: 'opacity 0.3s ' + EASE + ', transform 0.3s ' + EASE, }); globalBarEl.id = PREFIX + '-global-bar'; @@ -9344,6 +9385,8 @@ void main() { const inner = el('div', { display: 'flex', alignItems: 'center', padding: '4px 5px 4px ' + GLOBAL_BAR_INNER_PAD_LEFT + 'px', gap: GLOBAL_BAR_INNER_GAP + 'px', + minWidth: '0', + flex: '1 1 auto', }); inner.id = PREFIX + '-global-bar-inner'; globalBarEl.appendChild(inner); @@ -9623,6 +9666,7 @@ void main() { width: '1px', height: '18px', background: P.hairline, margin: '0 4px 0 2px', + flexShrink: '0', }); inner.appendChild(divider); @@ -9639,6 +9683,7 @@ void main() { display: 'inline-flex', alignItems: 'center', justifyContent: 'center', padding: '0', boxSizing: 'border-box', width: '24px', height: '24px', borderRadius: '6px', + flexShrink: '0', border: 'none', background: 'transparent', color: P.textDim, fontFamily: FONT, fontSize: '0', lineHeight: '0', cursor: 'pointer', transition: 'color 0.12s ease, background 0.12s ease', @@ -9651,16 +9696,16 @@ void main() { exitBtn.addEventListener('click', () => { sendEvent({ type: 'exit' }); teardown(); }); inner.appendChild(exitBtn); - // Bar-level hover: expand every toggle's label at once; collapse on leave. + // Bar-level hover: expand mode labels unless Steer is using the space. // Buttons with dataset.active="true" ignore collapse (their label stays). - const toggles = [pickBtn, insertBtn, detectBtn, designBtn]; globalBarEl.addEventListener('mouseenter', () => { - toggles.forEach((t) => t._expandLabel && t._expandLabel()); + syncGlobalBarExpandedLabels(true); + syncPageChatExpandedWidth(); schedulePendingDockPosition(); setTimeout(schedulePendingDockPosition, 260); }); globalBarEl.addEventListener('mouseleave', () => { - toggles.forEach((t) => t._collapseLabel && t._collapseLabel()); + syncGlobalBarExpandedLabels(false); schedulePendingDockPosition(); setTimeout(schedulePendingDockPosition, 260); }); @@ -9678,6 +9723,7 @@ void main() { pendingDockResizeObserver.observe(globalBarEl); } window.addEventListener('resize', positionPendingDock); + window.addEventListener('resize', syncPageChatExpandedWidth); requestAnimationFrame(() => { globalBarEl.style.opacity = '1'; @@ -9724,9 +9770,7 @@ void main() { // If the bar is currently under the cursor, keep all labels expanded - // otherwise clicking a toggle that deactivates (e.g. closing DESIGN.md) // would collapse its label while the user's mouse is still on the bar. - if (globalBarEl && globalBarEl.matches(':hover')) { - [pickToggle, insertToggle, detectToggle, designToggle].forEach((t) => t?._expandLabel?.()); - } + syncGlobalBarExpandedLabels(globalBarEl && globalBarEl.matches(':hover')); if (detectBadge) { detectBadge.style.display = (detectActive && detectCount > 0) ? 'inline' : 'none'; diff --git a/.rovodev/skills/impeccable/scripts/live-browser.js b/.rovodev/skills/impeccable/scripts/live-browser.js index ed9606409..1b8926333 100644 --- a/.rovodev/skills/impeccable/scripts/live-browser.js +++ b/.rovodev/skills/impeccable/scripts/live-browser.js @@ -8189,7 +8189,7 @@ void main() { let voiceInterimBase = ''; /** @type {{ mode: 'steer'|'configure', input: HTMLInputElement, submit: () => void, beforeStart?: () => void } | null} */ let voiceCtx = null; - const PAGE_CHAT_COLLAPSED_W = '88px'; + const PAGE_CHAT_COLLAPSED_W = '104px'; const PAGE_CHAT_PROCESSING_W = '76px'; const PAGE_CHAT_PLACEHOLDER_COLLAPSED = 'Steer…'; const PAGE_CHAT_PLACEHOLDER_EXPANDED = 'Steer the page…'; @@ -8200,7 +8200,7 @@ void main() { const GLOBAL_BAR_SECTION_GAP = 8; const GLOBAL_BAR_INNER_GAP = 2; const GLOBAL_BAR_INNER_PAD_LEFT = 2; - const PAGE_CHAT_EXPANDED_W = 'min(280px, 38vw)'; + const PAGE_CHAT_EXPANDED_MAX_W = 280; const ICON_PAGE_CHAT = ''; const ICON_PAGE_VOICE = @@ -8280,6 +8280,43 @@ void main() { return barPaletteForTheme(globalBarEl?.dataset.theme || detectPageTheme()); } + function globalBarModeToggles() { + return [ + uiGetById(PREFIX + '-pick-toggle'), + uiGetById(PREFIX + '-insert-toggle'), + uiGetById(PREFIX + '-detect-toggle'), + uiGetById(PREFIX + '-design-toggle'), + ].filter(Boolean); + } + + function syncGlobalBarExpandedLabels(expanded = globalBarEl?.matches(':hover')) { + globalBarModeToggles().forEach((toggle) => { + if (expanded && !pageChatExpanded) toggle._expandLabel?.(); + else if (toggle.dataset.active === 'true') toggle._expandLabel?.(); + else toggle._collapseLabel?.(); + }); + } + + function pageChatCollapsedWidthPx() { + const parsed = parseFloat(PAGE_CHAT_COLLAPSED_W); + return Number.isFinite(parsed) ? parsed : 104; + } + + function pageChatExpandedWidth() { + if (!pageChatEl || !globalBarEl) return PAGE_CHAT_EXPANDED_MAX_W + 'px'; + const currentChatWidth = pageChatEl.getBoundingClientRect().width || pageChatCollapsedWidthPx(); + const barWidth = globalBarEl.getBoundingClientRect().width || 0; + const nonChatWidth = Math.max(0, barWidth - currentChatWidth); + const available = window.innerWidth - 16 - nonChatWidth; + const next = Math.max(pageChatCollapsedWidthPx(), Math.min(PAGE_CHAT_EXPANDED_MAX_W, available)); + return Math.round(next) + 'px'; + } + + function syncPageChatExpandedWidth() { + if (!pageChatEl || !pageChatExpanded) return; + pageChatEl.style.width = pageChatExpandedWidth(); + } + function syncPageChatChrome() { if (!pageChatEl) return; const P = pageChatPalette(); @@ -8581,7 +8618,8 @@ void main() { if (!pageChatEl || !pageChatInput) return false; pageChatExpanded = true; pageChatEl.dataset.expanded = 'true'; - pageChatEl.style.width = PAGE_CHAT_EXPANDED_W; + syncGlobalBarExpandedLabels(false); + pageChatEl.style.width = pageChatExpandedWidth(); pageChatEl.style.cursor = steerLocked ? 'default' : 'text'; pageChatInput.placeholder = PAGE_CHAT_PLACEHOLDER_EXPANDED; if (pageChatHint) { @@ -8676,7 +8714,7 @@ void main() { pageChatEl.setAttribute('aria-label', 'Steer the page'); pageChatExpanded = keepExpanded; pageChatEl.dataset.expanded = keepExpanded ? 'true' : 'false'; - pageChatEl.style.width = keepExpanded ? PAGE_CHAT_EXPANDED_W : PAGE_CHAT_COLLAPSED_W; + pageChatEl.style.width = keepExpanded ? pageChatExpandedWidth() : PAGE_CHAT_COLLAPSED_W; pageChatEl.style.cursor = 'pointer'; if (pageChatInput) { pageChatInput.disabled = false; @@ -8990,6 +9028,7 @@ void main() { pageChatEl.dataset.expanded = 'false'; pageChatEl.style.width = PAGE_CHAT_COLLAPSED_W; pageChatEl.style.cursor = 'pointer'; + syncGlobalBarExpandedLabels(globalBarEl?.matches(':hover')); if (blur) { pageChatInput.blur(); pageChatInput.style.pointerEvents = 'none'; @@ -9296,6 +9335,8 @@ void main() { fontFamily: FONT, fontSize: '12px', lineHeight: '1', opacity: '0', overflow: 'hidden', // clip the full-bleed brand mark to the bar radius + maxWidth: 'calc(100vw - 16px)', + boxSizing: 'border-box', transition: 'opacity 0.3s ' + EASE + ', transform 0.3s ' + EASE, }); globalBarEl.id = PREFIX + '-global-bar'; @@ -9344,6 +9385,8 @@ void main() { const inner = el('div', { display: 'flex', alignItems: 'center', padding: '4px 5px 4px ' + GLOBAL_BAR_INNER_PAD_LEFT + 'px', gap: GLOBAL_BAR_INNER_GAP + 'px', + minWidth: '0', + flex: '1 1 auto', }); inner.id = PREFIX + '-global-bar-inner'; globalBarEl.appendChild(inner); @@ -9623,6 +9666,7 @@ void main() { width: '1px', height: '18px', background: P.hairline, margin: '0 4px 0 2px', + flexShrink: '0', }); inner.appendChild(divider); @@ -9639,6 +9683,7 @@ void main() { display: 'inline-flex', alignItems: 'center', justifyContent: 'center', padding: '0', boxSizing: 'border-box', width: '24px', height: '24px', borderRadius: '6px', + flexShrink: '0', border: 'none', background: 'transparent', color: P.textDim, fontFamily: FONT, fontSize: '0', lineHeight: '0', cursor: 'pointer', transition: 'color 0.12s ease, background 0.12s ease', @@ -9651,16 +9696,16 @@ void main() { exitBtn.addEventListener('click', () => { sendEvent({ type: 'exit' }); teardown(); }); inner.appendChild(exitBtn); - // Bar-level hover: expand every toggle's label at once; collapse on leave. + // Bar-level hover: expand mode labels unless Steer is using the space. // Buttons with dataset.active="true" ignore collapse (their label stays). - const toggles = [pickBtn, insertBtn, detectBtn, designBtn]; globalBarEl.addEventListener('mouseenter', () => { - toggles.forEach((t) => t._expandLabel && t._expandLabel()); + syncGlobalBarExpandedLabels(true); + syncPageChatExpandedWidth(); schedulePendingDockPosition(); setTimeout(schedulePendingDockPosition, 260); }); globalBarEl.addEventListener('mouseleave', () => { - toggles.forEach((t) => t._collapseLabel && t._collapseLabel()); + syncGlobalBarExpandedLabels(false); schedulePendingDockPosition(); setTimeout(schedulePendingDockPosition, 260); }); @@ -9678,6 +9723,7 @@ void main() { pendingDockResizeObserver.observe(globalBarEl); } window.addEventListener('resize', positionPendingDock); + window.addEventListener('resize', syncPageChatExpandedWidth); requestAnimationFrame(() => { globalBarEl.style.opacity = '1'; @@ -9724,9 +9770,7 @@ void main() { // If the bar is currently under the cursor, keep all labels expanded - // otherwise clicking a toggle that deactivates (e.g. closing DESIGN.md) // would collapse its label while the user's mouse is still on the bar. - if (globalBarEl && globalBarEl.matches(':hover')) { - [pickToggle, insertToggle, detectToggle, designToggle].forEach((t) => t?._expandLabel?.()); - } + syncGlobalBarExpandedLabels(globalBarEl && globalBarEl.matches(':hover')); if (detectBadge) { detectBadge.style.display = (detectActive && detectCount > 0) ? 'inline' : 'none'; diff --git a/.trae-cn/skills/impeccable/scripts/live-browser.js b/.trae-cn/skills/impeccable/scripts/live-browser.js index ed9606409..1b8926333 100644 --- a/.trae-cn/skills/impeccable/scripts/live-browser.js +++ b/.trae-cn/skills/impeccable/scripts/live-browser.js @@ -8189,7 +8189,7 @@ void main() { let voiceInterimBase = ''; /** @type {{ mode: 'steer'|'configure', input: HTMLInputElement, submit: () => void, beforeStart?: () => void } | null} */ let voiceCtx = null; - const PAGE_CHAT_COLLAPSED_W = '88px'; + const PAGE_CHAT_COLLAPSED_W = '104px'; const PAGE_CHAT_PROCESSING_W = '76px'; const PAGE_CHAT_PLACEHOLDER_COLLAPSED = 'Steer…'; const PAGE_CHAT_PLACEHOLDER_EXPANDED = 'Steer the page…'; @@ -8200,7 +8200,7 @@ void main() { const GLOBAL_BAR_SECTION_GAP = 8; const GLOBAL_BAR_INNER_GAP = 2; const GLOBAL_BAR_INNER_PAD_LEFT = 2; - const PAGE_CHAT_EXPANDED_W = 'min(280px, 38vw)'; + const PAGE_CHAT_EXPANDED_MAX_W = 280; const ICON_PAGE_CHAT = ''; const ICON_PAGE_VOICE = @@ -8280,6 +8280,43 @@ void main() { return barPaletteForTheme(globalBarEl?.dataset.theme || detectPageTheme()); } + function globalBarModeToggles() { + return [ + uiGetById(PREFIX + '-pick-toggle'), + uiGetById(PREFIX + '-insert-toggle'), + uiGetById(PREFIX + '-detect-toggle'), + uiGetById(PREFIX + '-design-toggle'), + ].filter(Boolean); + } + + function syncGlobalBarExpandedLabels(expanded = globalBarEl?.matches(':hover')) { + globalBarModeToggles().forEach((toggle) => { + if (expanded && !pageChatExpanded) toggle._expandLabel?.(); + else if (toggle.dataset.active === 'true') toggle._expandLabel?.(); + else toggle._collapseLabel?.(); + }); + } + + function pageChatCollapsedWidthPx() { + const parsed = parseFloat(PAGE_CHAT_COLLAPSED_W); + return Number.isFinite(parsed) ? parsed : 104; + } + + function pageChatExpandedWidth() { + if (!pageChatEl || !globalBarEl) return PAGE_CHAT_EXPANDED_MAX_W + 'px'; + const currentChatWidth = pageChatEl.getBoundingClientRect().width || pageChatCollapsedWidthPx(); + const barWidth = globalBarEl.getBoundingClientRect().width || 0; + const nonChatWidth = Math.max(0, barWidth - currentChatWidth); + const available = window.innerWidth - 16 - nonChatWidth; + const next = Math.max(pageChatCollapsedWidthPx(), Math.min(PAGE_CHAT_EXPANDED_MAX_W, available)); + return Math.round(next) + 'px'; + } + + function syncPageChatExpandedWidth() { + if (!pageChatEl || !pageChatExpanded) return; + pageChatEl.style.width = pageChatExpandedWidth(); + } + function syncPageChatChrome() { if (!pageChatEl) return; const P = pageChatPalette(); @@ -8581,7 +8618,8 @@ void main() { if (!pageChatEl || !pageChatInput) return false; pageChatExpanded = true; pageChatEl.dataset.expanded = 'true'; - pageChatEl.style.width = PAGE_CHAT_EXPANDED_W; + syncGlobalBarExpandedLabels(false); + pageChatEl.style.width = pageChatExpandedWidth(); pageChatEl.style.cursor = steerLocked ? 'default' : 'text'; pageChatInput.placeholder = PAGE_CHAT_PLACEHOLDER_EXPANDED; if (pageChatHint) { @@ -8676,7 +8714,7 @@ void main() { pageChatEl.setAttribute('aria-label', 'Steer the page'); pageChatExpanded = keepExpanded; pageChatEl.dataset.expanded = keepExpanded ? 'true' : 'false'; - pageChatEl.style.width = keepExpanded ? PAGE_CHAT_EXPANDED_W : PAGE_CHAT_COLLAPSED_W; + pageChatEl.style.width = keepExpanded ? pageChatExpandedWidth() : PAGE_CHAT_COLLAPSED_W; pageChatEl.style.cursor = 'pointer'; if (pageChatInput) { pageChatInput.disabled = false; @@ -8990,6 +9028,7 @@ void main() { pageChatEl.dataset.expanded = 'false'; pageChatEl.style.width = PAGE_CHAT_COLLAPSED_W; pageChatEl.style.cursor = 'pointer'; + syncGlobalBarExpandedLabels(globalBarEl?.matches(':hover')); if (blur) { pageChatInput.blur(); pageChatInput.style.pointerEvents = 'none'; @@ -9296,6 +9335,8 @@ void main() { fontFamily: FONT, fontSize: '12px', lineHeight: '1', opacity: '0', overflow: 'hidden', // clip the full-bleed brand mark to the bar radius + maxWidth: 'calc(100vw - 16px)', + boxSizing: 'border-box', transition: 'opacity 0.3s ' + EASE + ', transform 0.3s ' + EASE, }); globalBarEl.id = PREFIX + '-global-bar'; @@ -9344,6 +9385,8 @@ void main() { const inner = el('div', { display: 'flex', alignItems: 'center', padding: '4px 5px 4px ' + GLOBAL_BAR_INNER_PAD_LEFT + 'px', gap: GLOBAL_BAR_INNER_GAP + 'px', + minWidth: '0', + flex: '1 1 auto', }); inner.id = PREFIX + '-global-bar-inner'; globalBarEl.appendChild(inner); @@ -9623,6 +9666,7 @@ void main() { width: '1px', height: '18px', background: P.hairline, margin: '0 4px 0 2px', + flexShrink: '0', }); inner.appendChild(divider); @@ -9639,6 +9683,7 @@ void main() { display: 'inline-flex', alignItems: 'center', justifyContent: 'center', padding: '0', boxSizing: 'border-box', width: '24px', height: '24px', borderRadius: '6px', + flexShrink: '0', border: 'none', background: 'transparent', color: P.textDim, fontFamily: FONT, fontSize: '0', lineHeight: '0', cursor: 'pointer', transition: 'color 0.12s ease, background 0.12s ease', @@ -9651,16 +9696,16 @@ void main() { exitBtn.addEventListener('click', () => { sendEvent({ type: 'exit' }); teardown(); }); inner.appendChild(exitBtn); - // Bar-level hover: expand every toggle's label at once; collapse on leave. + // Bar-level hover: expand mode labels unless Steer is using the space. // Buttons with dataset.active="true" ignore collapse (their label stays). - const toggles = [pickBtn, insertBtn, detectBtn, designBtn]; globalBarEl.addEventListener('mouseenter', () => { - toggles.forEach((t) => t._expandLabel && t._expandLabel()); + syncGlobalBarExpandedLabels(true); + syncPageChatExpandedWidth(); schedulePendingDockPosition(); setTimeout(schedulePendingDockPosition, 260); }); globalBarEl.addEventListener('mouseleave', () => { - toggles.forEach((t) => t._collapseLabel && t._collapseLabel()); + syncGlobalBarExpandedLabels(false); schedulePendingDockPosition(); setTimeout(schedulePendingDockPosition, 260); }); @@ -9678,6 +9723,7 @@ void main() { pendingDockResizeObserver.observe(globalBarEl); } window.addEventListener('resize', positionPendingDock); + window.addEventListener('resize', syncPageChatExpandedWidth); requestAnimationFrame(() => { globalBarEl.style.opacity = '1'; @@ -9724,9 +9770,7 @@ void main() { // If the bar is currently under the cursor, keep all labels expanded - // otherwise clicking a toggle that deactivates (e.g. closing DESIGN.md) // would collapse its label while the user's mouse is still on the bar. - if (globalBarEl && globalBarEl.matches(':hover')) { - [pickToggle, insertToggle, detectToggle, designToggle].forEach((t) => t?._expandLabel?.()); - } + syncGlobalBarExpandedLabels(globalBarEl && globalBarEl.matches(':hover')); if (detectBadge) { detectBadge.style.display = (detectActive && detectCount > 0) ? 'inline' : 'none'; diff --git a/.trae/skills/impeccable/scripts/live-browser.js b/.trae/skills/impeccable/scripts/live-browser.js index ed9606409..1b8926333 100644 --- a/.trae/skills/impeccable/scripts/live-browser.js +++ b/.trae/skills/impeccable/scripts/live-browser.js @@ -8189,7 +8189,7 @@ void main() { let voiceInterimBase = ''; /** @type {{ mode: 'steer'|'configure', input: HTMLInputElement, submit: () => void, beforeStart?: () => void } | null} */ let voiceCtx = null; - const PAGE_CHAT_COLLAPSED_W = '88px'; + const PAGE_CHAT_COLLAPSED_W = '104px'; const PAGE_CHAT_PROCESSING_W = '76px'; const PAGE_CHAT_PLACEHOLDER_COLLAPSED = 'Steer…'; const PAGE_CHAT_PLACEHOLDER_EXPANDED = 'Steer the page…'; @@ -8200,7 +8200,7 @@ void main() { const GLOBAL_BAR_SECTION_GAP = 8; const GLOBAL_BAR_INNER_GAP = 2; const GLOBAL_BAR_INNER_PAD_LEFT = 2; - const PAGE_CHAT_EXPANDED_W = 'min(280px, 38vw)'; + const PAGE_CHAT_EXPANDED_MAX_W = 280; const ICON_PAGE_CHAT = ''; const ICON_PAGE_VOICE = @@ -8280,6 +8280,43 @@ void main() { return barPaletteForTheme(globalBarEl?.dataset.theme || detectPageTheme()); } + function globalBarModeToggles() { + return [ + uiGetById(PREFIX + '-pick-toggle'), + uiGetById(PREFIX + '-insert-toggle'), + uiGetById(PREFIX + '-detect-toggle'), + uiGetById(PREFIX + '-design-toggle'), + ].filter(Boolean); + } + + function syncGlobalBarExpandedLabels(expanded = globalBarEl?.matches(':hover')) { + globalBarModeToggles().forEach((toggle) => { + if (expanded && !pageChatExpanded) toggle._expandLabel?.(); + else if (toggle.dataset.active === 'true') toggle._expandLabel?.(); + else toggle._collapseLabel?.(); + }); + } + + function pageChatCollapsedWidthPx() { + const parsed = parseFloat(PAGE_CHAT_COLLAPSED_W); + return Number.isFinite(parsed) ? parsed : 104; + } + + function pageChatExpandedWidth() { + if (!pageChatEl || !globalBarEl) return PAGE_CHAT_EXPANDED_MAX_W + 'px'; + const currentChatWidth = pageChatEl.getBoundingClientRect().width || pageChatCollapsedWidthPx(); + const barWidth = globalBarEl.getBoundingClientRect().width || 0; + const nonChatWidth = Math.max(0, barWidth - currentChatWidth); + const available = window.innerWidth - 16 - nonChatWidth; + const next = Math.max(pageChatCollapsedWidthPx(), Math.min(PAGE_CHAT_EXPANDED_MAX_W, available)); + return Math.round(next) + 'px'; + } + + function syncPageChatExpandedWidth() { + if (!pageChatEl || !pageChatExpanded) return; + pageChatEl.style.width = pageChatExpandedWidth(); + } + function syncPageChatChrome() { if (!pageChatEl) return; const P = pageChatPalette(); @@ -8581,7 +8618,8 @@ void main() { if (!pageChatEl || !pageChatInput) return false; pageChatExpanded = true; pageChatEl.dataset.expanded = 'true'; - pageChatEl.style.width = PAGE_CHAT_EXPANDED_W; + syncGlobalBarExpandedLabels(false); + pageChatEl.style.width = pageChatExpandedWidth(); pageChatEl.style.cursor = steerLocked ? 'default' : 'text'; pageChatInput.placeholder = PAGE_CHAT_PLACEHOLDER_EXPANDED; if (pageChatHint) { @@ -8676,7 +8714,7 @@ void main() { pageChatEl.setAttribute('aria-label', 'Steer the page'); pageChatExpanded = keepExpanded; pageChatEl.dataset.expanded = keepExpanded ? 'true' : 'false'; - pageChatEl.style.width = keepExpanded ? PAGE_CHAT_EXPANDED_W : PAGE_CHAT_COLLAPSED_W; + pageChatEl.style.width = keepExpanded ? pageChatExpandedWidth() : PAGE_CHAT_COLLAPSED_W; pageChatEl.style.cursor = 'pointer'; if (pageChatInput) { pageChatInput.disabled = false; @@ -8990,6 +9028,7 @@ void main() { pageChatEl.dataset.expanded = 'false'; pageChatEl.style.width = PAGE_CHAT_COLLAPSED_W; pageChatEl.style.cursor = 'pointer'; + syncGlobalBarExpandedLabels(globalBarEl?.matches(':hover')); if (blur) { pageChatInput.blur(); pageChatInput.style.pointerEvents = 'none'; @@ -9296,6 +9335,8 @@ void main() { fontFamily: FONT, fontSize: '12px', lineHeight: '1', opacity: '0', overflow: 'hidden', // clip the full-bleed brand mark to the bar radius + maxWidth: 'calc(100vw - 16px)', + boxSizing: 'border-box', transition: 'opacity 0.3s ' + EASE + ', transform 0.3s ' + EASE, }); globalBarEl.id = PREFIX + '-global-bar'; @@ -9344,6 +9385,8 @@ void main() { const inner = el('div', { display: 'flex', alignItems: 'center', padding: '4px 5px 4px ' + GLOBAL_BAR_INNER_PAD_LEFT + 'px', gap: GLOBAL_BAR_INNER_GAP + 'px', + minWidth: '0', + flex: '1 1 auto', }); inner.id = PREFIX + '-global-bar-inner'; globalBarEl.appendChild(inner); @@ -9623,6 +9666,7 @@ void main() { width: '1px', height: '18px', background: P.hairline, margin: '0 4px 0 2px', + flexShrink: '0', }); inner.appendChild(divider); @@ -9639,6 +9683,7 @@ void main() { display: 'inline-flex', alignItems: 'center', justifyContent: 'center', padding: '0', boxSizing: 'border-box', width: '24px', height: '24px', borderRadius: '6px', + flexShrink: '0', border: 'none', background: 'transparent', color: P.textDim, fontFamily: FONT, fontSize: '0', lineHeight: '0', cursor: 'pointer', transition: 'color 0.12s ease, background 0.12s ease', @@ -9651,16 +9696,16 @@ void main() { exitBtn.addEventListener('click', () => { sendEvent({ type: 'exit' }); teardown(); }); inner.appendChild(exitBtn); - // Bar-level hover: expand every toggle's label at once; collapse on leave. + // Bar-level hover: expand mode labels unless Steer is using the space. // Buttons with dataset.active="true" ignore collapse (their label stays). - const toggles = [pickBtn, insertBtn, detectBtn, designBtn]; globalBarEl.addEventListener('mouseenter', () => { - toggles.forEach((t) => t._expandLabel && t._expandLabel()); + syncGlobalBarExpandedLabels(true); + syncPageChatExpandedWidth(); schedulePendingDockPosition(); setTimeout(schedulePendingDockPosition, 260); }); globalBarEl.addEventListener('mouseleave', () => { - toggles.forEach((t) => t._collapseLabel && t._collapseLabel()); + syncGlobalBarExpandedLabels(false); schedulePendingDockPosition(); setTimeout(schedulePendingDockPosition, 260); }); @@ -9678,6 +9723,7 @@ void main() { pendingDockResizeObserver.observe(globalBarEl); } window.addEventListener('resize', positionPendingDock); + window.addEventListener('resize', syncPageChatExpandedWidth); requestAnimationFrame(() => { globalBarEl.style.opacity = '1'; @@ -9724,9 +9770,7 @@ void main() { // If the bar is currently under the cursor, keep all labels expanded - // otherwise clicking a toggle that deactivates (e.g. closing DESIGN.md) // would collapse its label while the user's mouse is still on the bar. - if (globalBarEl && globalBarEl.matches(':hover')) { - [pickToggle, insertToggle, detectToggle, designToggle].forEach((t) => t?._expandLabel?.()); - } + syncGlobalBarExpandedLabels(globalBarEl && globalBarEl.matches(':hover')); if (detectBadge) { detectBadge.style.display = (detectActive && detectCount > 0) ? 'inline' : 'none'; diff --git a/plugin/skills/impeccable/scripts/live-browser.js b/plugin/skills/impeccable/scripts/live-browser.js index ed9606409..1b8926333 100644 --- a/plugin/skills/impeccable/scripts/live-browser.js +++ b/plugin/skills/impeccable/scripts/live-browser.js @@ -8189,7 +8189,7 @@ void main() { let voiceInterimBase = ''; /** @type {{ mode: 'steer'|'configure', input: HTMLInputElement, submit: () => void, beforeStart?: () => void } | null} */ let voiceCtx = null; - const PAGE_CHAT_COLLAPSED_W = '88px'; + const PAGE_CHAT_COLLAPSED_W = '104px'; const PAGE_CHAT_PROCESSING_W = '76px'; const PAGE_CHAT_PLACEHOLDER_COLLAPSED = 'Steer…'; const PAGE_CHAT_PLACEHOLDER_EXPANDED = 'Steer the page…'; @@ -8200,7 +8200,7 @@ void main() { const GLOBAL_BAR_SECTION_GAP = 8; const GLOBAL_BAR_INNER_GAP = 2; const GLOBAL_BAR_INNER_PAD_LEFT = 2; - const PAGE_CHAT_EXPANDED_W = 'min(280px, 38vw)'; + const PAGE_CHAT_EXPANDED_MAX_W = 280; const ICON_PAGE_CHAT = ''; const ICON_PAGE_VOICE = @@ -8280,6 +8280,43 @@ void main() { return barPaletteForTheme(globalBarEl?.dataset.theme || detectPageTheme()); } + function globalBarModeToggles() { + return [ + uiGetById(PREFIX + '-pick-toggle'), + uiGetById(PREFIX + '-insert-toggle'), + uiGetById(PREFIX + '-detect-toggle'), + uiGetById(PREFIX + '-design-toggle'), + ].filter(Boolean); + } + + function syncGlobalBarExpandedLabels(expanded = globalBarEl?.matches(':hover')) { + globalBarModeToggles().forEach((toggle) => { + if (expanded && !pageChatExpanded) toggle._expandLabel?.(); + else if (toggle.dataset.active === 'true') toggle._expandLabel?.(); + else toggle._collapseLabel?.(); + }); + } + + function pageChatCollapsedWidthPx() { + const parsed = parseFloat(PAGE_CHAT_COLLAPSED_W); + return Number.isFinite(parsed) ? parsed : 104; + } + + function pageChatExpandedWidth() { + if (!pageChatEl || !globalBarEl) return PAGE_CHAT_EXPANDED_MAX_W + 'px'; + const currentChatWidth = pageChatEl.getBoundingClientRect().width || pageChatCollapsedWidthPx(); + const barWidth = globalBarEl.getBoundingClientRect().width || 0; + const nonChatWidth = Math.max(0, barWidth - currentChatWidth); + const available = window.innerWidth - 16 - nonChatWidth; + const next = Math.max(pageChatCollapsedWidthPx(), Math.min(PAGE_CHAT_EXPANDED_MAX_W, available)); + return Math.round(next) + 'px'; + } + + function syncPageChatExpandedWidth() { + if (!pageChatEl || !pageChatExpanded) return; + pageChatEl.style.width = pageChatExpandedWidth(); + } + function syncPageChatChrome() { if (!pageChatEl) return; const P = pageChatPalette(); @@ -8581,7 +8618,8 @@ void main() { if (!pageChatEl || !pageChatInput) return false; pageChatExpanded = true; pageChatEl.dataset.expanded = 'true'; - pageChatEl.style.width = PAGE_CHAT_EXPANDED_W; + syncGlobalBarExpandedLabels(false); + pageChatEl.style.width = pageChatExpandedWidth(); pageChatEl.style.cursor = steerLocked ? 'default' : 'text'; pageChatInput.placeholder = PAGE_CHAT_PLACEHOLDER_EXPANDED; if (pageChatHint) { @@ -8676,7 +8714,7 @@ void main() { pageChatEl.setAttribute('aria-label', 'Steer the page'); pageChatExpanded = keepExpanded; pageChatEl.dataset.expanded = keepExpanded ? 'true' : 'false'; - pageChatEl.style.width = keepExpanded ? PAGE_CHAT_EXPANDED_W : PAGE_CHAT_COLLAPSED_W; + pageChatEl.style.width = keepExpanded ? pageChatExpandedWidth() : PAGE_CHAT_COLLAPSED_W; pageChatEl.style.cursor = 'pointer'; if (pageChatInput) { pageChatInput.disabled = false; @@ -8990,6 +9028,7 @@ void main() { pageChatEl.dataset.expanded = 'false'; pageChatEl.style.width = PAGE_CHAT_COLLAPSED_W; pageChatEl.style.cursor = 'pointer'; + syncGlobalBarExpandedLabels(globalBarEl?.matches(':hover')); if (blur) { pageChatInput.blur(); pageChatInput.style.pointerEvents = 'none'; @@ -9296,6 +9335,8 @@ void main() { fontFamily: FONT, fontSize: '12px', lineHeight: '1', opacity: '0', overflow: 'hidden', // clip the full-bleed brand mark to the bar radius + maxWidth: 'calc(100vw - 16px)', + boxSizing: 'border-box', transition: 'opacity 0.3s ' + EASE + ', transform 0.3s ' + EASE, }); globalBarEl.id = PREFIX + '-global-bar'; @@ -9344,6 +9385,8 @@ void main() { const inner = el('div', { display: 'flex', alignItems: 'center', padding: '4px 5px 4px ' + GLOBAL_BAR_INNER_PAD_LEFT + 'px', gap: GLOBAL_BAR_INNER_GAP + 'px', + minWidth: '0', + flex: '1 1 auto', }); inner.id = PREFIX + '-global-bar-inner'; globalBarEl.appendChild(inner); @@ -9623,6 +9666,7 @@ void main() { width: '1px', height: '18px', background: P.hairline, margin: '0 4px 0 2px', + flexShrink: '0', }); inner.appendChild(divider); @@ -9639,6 +9683,7 @@ void main() { display: 'inline-flex', alignItems: 'center', justifyContent: 'center', padding: '0', boxSizing: 'border-box', width: '24px', height: '24px', borderRadius: '6px', + flexShrink: '0', border: 'none', background: 'transparent', color: P.textDim, fontFamily: FONT, fontSize: '0', lineHeight: '0', cursor: 'pointer', transition: 'color 0.12s ease, background 0.12s ease', @@ -9651,16 +9696,16 @@ void main() { exitBtn.addEventListener('click', () => { sendEvent({ type: 'exit' }); teardown(); }); inner.appendChild(exitBtn); - // Bar-level hover: expand every toggle's label at once; collapse on leave. + // Bar-level hover: expand mode labels unless Steer is using the space. // Buttons with dataset.active="true" ignore collapse (their label stays). - const toggles = [pickBtn, insertBtn, detectBtn, designBtn]; globalBarEl.addEventListener('mouseenter', () => { - toggles.forEach((t) => t._expandLabel && t._expandLabel()); + syncGlobalBarExpandedLabels(true); + syncPageChatExpandedWidth(); schedulePendingDockPosition(); setTimeout(schedulePendingDockPosition, 260); }); globalBarEl.addEventListener('mouseleave', () => { - toggles.forEach((t) => t._collapseLabel && t._collapseLabel()); + syncGlobalBarExpandedLabels(false); schedulePendingDockPosition(); setTimeout(schedulePendingDockPosition, 260); }); @@ -9678,6 +9723,7 @@ void main() { pendingDockResizeObserver.observe(globalBarEl); } window.addEventListener('resize', positionPendingDock); + window.addEventListener('resize', syncPageChatExpandedWidth); requestAnimationFrame(() => { globalBarEl.style.opacity = '1'; @@ -9724,9 +9770,7 @@ void main() { // If the bar is currently under the cursor, keep all labels expanded - // otherwise clicking a toggle that deactivates (e.g. closing DESIGN.md) // would collapse its label while the user's mouse is still on the bar. - if (globalBarEl && globalBarEl.matches(':hover')) { - [pickToggle, insertToggle, detectToggle, designToggle].forEach((t) => t?._expandLabel?.()); - } + syncGlobalBarExpandedLabels(globalBarEl && globalBarEl.matches(':hover')); if (detectBadge) { detectBadge.style.display = (detectActive && detectCount > 0) ? 'inline' : 'none'; diff --git a/site/styles/kinpaku-kit.css b/site/styles/kinpaku-kit.css index 2b2e6085f..050e6c2d9 100644 --- a/site/styles/kinpaku-kit.css +++ b/site/styles/kinpaku-kit.css @@ -1194,7 +1194,7 @@ display: inline-flex; align-items: center; height: 28px; - width: 88px; + width: 104px; margin: 0 4px 0 6px; border-radius: 5px; background: var(--ks-graphite); @@ -1202,12 +1202,12 @@ overflow: hidden; cursor: pointer; flex-shrink: 0; - transition: width 0.18s ease, border-color 0.15s ease; + transition: border-color 0.15s ease; } .home-kinpaku .live-demo-gbar-chat.is-expanded, .live-mode-kinpaku .live-demo-gbar-chat.is-expanded { - width: min(220px, 42vw); + width: min(220px, 34vw); cursor: text; } diff --git a/site/styles/live-mode.css b/site/styles/live-mode.css index f883ad3ad..ae942d6f7 100644 --- a/site/styles/live-mode.css +++ b/site/styles/live-mode.css @@ -292,7 +292,8 @@ display: flex; align-items: center; gap: 2px; - flex-shrink: 0; + flex: 1 1 auto; + min-width: 0; padding: 4px 6px 4px 2px; } .live-demo-gbar-btn { @@ -358,7 +359,7 @@ align-items: center; justify-content: center; box-sizing: border-box; - flex-shrink: 0; + flex: 0 0 24px; align-self: center; width: 24px; height: 24px; diff --git a/skill/scripts/live-browser.js b/skill/scripts/live-browser.js index ed9606409..1b8926333 100644 --- a/skill/scripts/live-browser.js +++ b/skill/scripts/live-browser.js @@ -8189,7 +8189,7 @@ void main() { let voiceInterimBase = ''; /** @type {{ mode: 'steer'|'configure', input: HTMLInputElement, submit: () => void, beforeStart?: () => void } | null} */ let voiceCtx = null; - const PAGE_CHAT_COLLAPSED_W = '88px'; + const PAGE_CHAT_COLLAPSED_W = '104px'; const PAGE_CHAT_PROCESSING_W = '76px'; const PAGE_CHAT_PLACEHOLDER_COLLAPSED = 'Steer…'; const PAGE_CHAT_PLACEHOLDER_EXPANDED = 'Steer the page…'; @@ -8200,7 +8200,7 @@ void main() { const GLOBAL_BAR_SECTION_GAP = 8; const GLOBAL_BAR_INNER_GAP = 2; const GLOBAL_BAR_INNER_PAD_LEFT = 2; - const PAGE_CHAT_EXPANDED_W = 'min(280px, 38vw)'; + const PAGE_CHAT_EXPANDED_MAX_W = 280; const ICON_PAGE_CHAT = ''; const ICON_PAGE_VOICE = @@ -8280,6 +8280,43 @@ void main() { return barPaletteForTheme(globalBarEl?.dataset.theme || detectPageTheme()); } + function globalBarModeToggles() { + return [ + uiGetById(PREFIX + '-pick-toggle'), + uiGetById(PREFIX + '-insert-toggle'), + uiGetById(PREFIX + '-detect-toggle'), + uiGetById(PREFIX + '-design-toggle'), + ].filter(Boolean); + } + + function syncGlobalBarExpandedLabels(expanded = globalBarEl?.matches(':hover')) { + globalBarModeToggles().forEach((toggle) => { + if (expanded && !pageChatExpanded) toggle._expandLabel?.(); + else if (toggle.dataset.active === 'true') toggle._expandLabel?.(); + else toggle._collapseLabel?.(); + }); + } + + function pageChatCollapsedWidthPx() { + const parsed = parseFloat(PAGE_CHAT_COLLAPSED_W); + return Number.isFinite(parsed) ? parsed : 104; + } + + function pageChatExpandedWidth() { + if (!pageChatEl || !globalBarEl) return PAGE_CHAT_EXPANDED_MAX_W + 'px'; + const currentChatWidth = pageChatEl.getBoundingClientRect().width || pageChatCollapsedWidthPx(); + const barWidth = globalBarEl.getBoundingClientRect().width || 0; + const nonChatWidth = Math.max(0, barWidth - currentChatWidth); + const available = window.innerWidth - 16 - nonChatWidth; + const next = Math.max(pageChatCollapsedWidthPx(), Math.min(PAGE_CHAT_EXPANDED_MAX_W, available)); + return Math.round(next) + 'px'; + } + + function syncPageChatExpandedWidth() { + if (!pageChatEl || !pageChatExpanded) return; + pageChatEl.style.width = pageChatExpandedWidth(); + } + function syncPageChatChrome() { if (!pageChatEl) return; const P = pageChatPalette(); @@ -8581,7 +8618,8 @@ void main() { if (!pageChatEl || !pageChatInput) return false; pageChatExpanded = true; pageChatEl.dataset.expanded = 'true'; - pageChatEl.style.width = PAGE_CHAT_EXPANDED_W; + syncGlobalBarExpandedLabels(false); + pageChatEl.style.width = pageChatExpandedWidth(); pageChatEl.style.cursor = steerLocked ? 'default' : 'text'; pageChatInput.placeholder = PAGE_CHAT_PLACEHOLDER_EXPANDED; if (pageChatHint) { @@ -8676,7 +8714,7 @@ void main() { pageChatEl.setAttribute('aria-label', 'Steer the page'); pageChatExpanded = keepExpanded; pageChatEl.dataset.expanded = keepExpanded ? 'true' : 'false'; - pageChatEl.style.width = keepExpanded ? PAGE_CHAT_EXPANDED_W : PAGE_CHAT_COLLAPSED_W; + pageChatEl.style.width = keepExpanded ? pageChatExpandedWidth() : PAGE_CHAT_COLLAPSED_W; pageChatEl.style.cursor = 'pointer'; if (pageChatInput) { pageChatInput.disabled = false; @@ -8990,6 +9028,7 @@ void main() { pageChatEl.dataset.expanded = 'false'; pageChatEl.style.width = PAGE_CHAT_COLLAPSED_W; pageChatEl.style.cursor = 'pointer'; + syncGlobalBarExpandedLabels(globalBarEl?.matches(':hover')); if (blur) { pageChatInput.blur(); pageChatInput.style.pointerEvents = 'none'; @@ -9296,6 +9335,8 @@ void main() { fontFamily: FONT, fontSize: '12px', lineHeight: '1', opacity: '0', overflow: 'hidden', // clip the full-bleed brand mark to the bar radius + maxWidth: 'calc(100vw - 16px)', + boxSizing: 'border-box', transition: 'opacity 0.3s ' + EASE + ', transform 0.3s ' + EASE, }); globalBarEl.id = PREFIX + '-global-bar'; @@ -9344,6 +9385,8 @@ void main() { const inner = el('div', { display: 'flex', alignItems: 'center', padding: '4px 5px 4px ' + GLOBAL_BAR_INNER_PAD_LEFT + 'px', gap: GLOBAL_BAR_INNER_GAP + 'px', + minWidth: '0', + flex: '1 1 auto', }); inner.id = PREFIX + '-global-bar-inner'; globalBarEl.appendChild(inner); @@ -9623,6 +9666,7 @@ void main() { width: '1px', height: '18px', background: P.hairline, margin: '0 4px 0 2px', + flexShrink: '0', }); inner.appendChild(divider); @@ -9639,6 +9683,7 @@ void main() { display: 'inline-flex', alignItems: 'center', justifyContent: 'center', padding: '0', boxSizing: 'border-box', width: '24px', height: '24px', borderRadius: '6px', + flexShrink: '0', border: 'none', background: 'transparent', color: P.textDim, fontFamily: FONT, fontSize: '0', lineHeight: '0', cursor: 'pointer', transition: 'color 0.12s ease, background 0.12s ease', @@ -9651,16 +9696,16 @@ void main() { exitBtn.addEventListener('click', () => { sendEvent({ type: 'exit' }); teardown(); }); inner.appendChild(exitBtn); - // Bar-level hover: expand every toggle's label at once; collapse on leave. + // Bar-level hover: expand mode labels unless Steer is using the space. // Buttons with dataset.active="true" ignore collapse (their label stays). - const toggles = [pickBtn, insertBtn, detectBtn, designBtn]; globalBarEl.addEventListener('mouseenter', () => { - toggles.forEach((t) => t._expandLabel && t._expandLabel()); + syncGlobalBarExpandedLabels(true); + syncPageChatExpandedWidth(); schedulePendingDockPosition(); setTimeout(schedulePendingDockPosition, 260); }); globalBarEl.addEventListener('mouseleave', () => { - toggles.forEach((t) => t._collapseLabel && t._collapseLabel()); + syncGlobalBarExpandedLabels(false); schedulePendingDockPosition(); setTimeout(schedulePendingDockPosition, 260); }); @@ -9678,6 +9723,7 @@ void main() { pendingDockResizeObserver.observe(globalBarEl); } window.addEventListener('resize', positionPendingDock); + window.addEventListener('resize', syncPageChatExpandedWidth); requestAnimationFrame(() => { globalBarEl.style.opacity = '1'; @@ -9724,9 +9770,7 @@ void main() { // If the bar is currently under the cursor, keep all labels expanded - // otherwise clicking a toggle that deactivates (e.g. closing DESIGN.md) // would collapse its label while the user's mouse is still on the bar. - if (globalBarEl && globalBarEl.matches(':hover')) { - [pickToggle, insertToggle, detectToggle, designToggle].forEach((t) => t?._expandLabel?.()); - } + syncGlobalBarExpandedLabels(globalBarEl && globalBarEl.matches(':hover')); if (detectBadge) { detectBadge.style.display = (detectActive && detectCount > 0) ? 'inline' : 'none'; diff --git a/tests/live-browser-regression.test.mjs b/tests/live-browser-regression.test.mjs index 5b82a0491..d4ad8eebf 100644 --- a/tests/live-browser-regression.test.mjs +++ b/tests/live-browser-regression.test.mjs @@ -154,6 +154,29 @@ describe('live-browser.js regression guards', () => { ); }); + it('keeps the Steer pill from clipping its label or the global bar exit control', () => { + assert.match( + SOURCE, + /const PAGE_CHAT_COLLAPSED_W = '104px';/, + 'collapsed Steer pill should reserve enough room for icon, "Steer", and voice button', + ); + assert.match( + SOURCE, + /function pageChatExpandedWidth\(\)[\s\S]{0,520}?window\.innerWidth - 16 - nonChatWidth[\s\S]{0,220}?Math\.max\(pageChatCollapsedWidthPx\(\), Math\.min\(PAGE_CHAT_EXPANDED_MAX_W, available\)\)/, + 'expanded Steer width should reserve viewport room for the rest of the global bar', + ); + assert.match( + SOURCE, + /syncGlobalBarExpandedLabels\(false\);[\s\S]{0,80}?pageChatEl\.style\.width = pageChatExpandedWidth\(\);/, + 'opening Steer should collapse inactive mode labels before measuring input width', + ); + assert.match( + SOURCE, + /maxWidth: 'calc\(100vw - 16px\)'[\s\S]{0,80}?boxSizing: 'border-box'/, + 'global bar should be constrained to the viewport instead of clipping the exit control offscreen', + ); + }); + it('does not autofocus the steering chat while a page editable is focused', () => { assert.match( SOURCE,