diff --git a/src/ButtonModule.js b/src/ButtonModule.js index ada09e4ab9..4ed27da9b6 100644 --- a/src/ButtonModule.js +++ b/src/ButtonModule.js @@ -238,7 +238,7 @@ class ButtonModule { createMiniSettingsButton(container, position = 0) { const button = this.createIconButton({ id: 'saypi-settingsButton', - label: getMessage("extensionSettings"), + label: getMessage("voiceSettings"), icon: settingsIconSVG, onClick: () => openSettings(), className: 'settings-button' diff --git a/src/chatbots/Claude.ts b/src/chatbots/Claude.ts index 30d36d4b28..63e21d14b5 100644 --- a/src/chatbots/Claude.ts +++ b/src/chatbots/Claude.ts @@ -220,56 +220,53 @@ class ClaudeChatbot extends AbstractChatbot { } getSidebarConfig(sidebar: HTMLElement): SidebarConfig | null { - const actionSelectors = [ - '[data-testid="new-chat-button"]', - '[data-testid="navigation-link-new-chat"]', - '[data-testid="create-new-chat-button"]', - '[aria-label="New chat"]', - 'a[href="/new"]', - 'a[href="/chat/new"]', - ]; - - const newChatButton = actionSelectors - .map((selector) => sidebar.querySelector(selector) as HTMLElement | null) - .find((element) => element !== null) as HTMLElement | null; - - const collectCandidateContainers = () => { - const candidates: HTMLElement[] = []; - if (newChatButton) { - let ancestor = newChatButton.parentElement as HTMLElement | null; - while (ancestor && ancestor !== sidebar) { - candidates.push(ancestor); - ancestor = ancestor.parentElement as HTMLElement | null; + // Claude's sidebar structure (Jan 2025): + // - "New chat" is in its own container above the scrollable area + // - "Chats, Projects, Artifacts, Code" are in a flex column inside the scrollable area + // We want to insert our button after "Code" in that menu container + + // Strategy: Find the container that has menu items like "Chats", "Projects", "Artifacts", "Code" + // These are direct children divs that contain anchor or button elements with those labels + const findMenuContainer = (): HTMLElement | null => { + // Look for a flex column container that has the navigation items + const candidates = Array.from(sidebar.querySelectorAll('div.flex.flex-col')); + + for (const candidate of candidates) { + // Check if this container has children that look like menu items + const children = Array.from(candidate.children); + if (children.length < 3) continue; + + // Look for recognizable menu items (Chats, Projects, Artifacts, Code) + const menuLabels = ['chats', 'projects', 'artifacts', 'code']; + const foundLabels = children.filter(child => { + const text = child.textContent?.toLowerCase().trim() || ''; + return menuLabels.some(label => text === label); + }); + + // If we found at least 3 of the expected menu items, this is our container + if (foundLabels.length >= 3) { + return candidate as HTMLElement; } } - const allDivs = Array.from(sidebar.querySelectorAll('div')).map((div) => div as HTMLElement); - return [...candidates, ...allDivs]; + + return null; }; - const menuContainer = collectCandidateContainers().find((candidate) => { - const actions = Array.from(candidate.querySelectorAll('a, button, div[role="button"]')).filter((action) => !action.closest('[data-testid="user-profile"]')); - return actions.length >= 3; - }) || null; + const menuContainer = findMenuContainer(); if (!menuContainer) { - console.warn('[Claude] sidebar: Could not find menu container'); + console.warn('[Claude] sidebar: Could not find menu container with navigation items'); return null; } - let header: HTMLElement | null = menuContainer; - while (header && header.parentElement && header.parentElement !== sidebar) { - header = header.parentElement as HTMLElement; - } - - if (!header || header.parentElement !== sidebar) { - console.warn('[Claude] sidebar: Could not find header element'); - return null; - } + // Insert after "Code" which is typically the last item (position 4, 0-indexed) + // Count the actual children to determine insert position + const insertPosition = menuContainer.children.length; return { buttonContainer: menuContainer, buttonStyle: 'menu', - insertPosition: 3, + insertPosition: insertPosition, }; } } diff --git a/src/icons/settings.svg b/src/icons/settings.svg index 2f09f8815e..8f9ef1bf5c 100644 --- a/src/icons/settings.svg +++ b/src/icons/settings.svg @@ -1,7 +1,7 @@ - + diff --git a/src/styles/pi.scss b/src/styles/pi.scss index 13c172625d..fc8e17c1e0 100644 --- a/src/styles/pi.scss +++ b/src/styles/pi.scss @@ -13,12 +13,12 @@ html body.pi { } /* Pi-specific button sizing for control panel */ - /* Use 40x40 button containers to match Pi's native button sizing */ + /* Use 36x36 button containers to match Pi's native button sizing (size-9) */ #saypi-control-panel, .saypi-control-panel { .saypi-control-button.mini { - width: 40px; - height: 40px; + width: 36px; + height: 36px; } /* Match Pi's native brown icon color */ @@ -30,12 +30,28 @@ html body.pi { stroke: rgb(107, 98, 85); } } + + /* Hide outer circle ring on settings icon to match Pi's flat button style */ + #saypi-settingsButton svg .outer-circle { + display: none; + } + + /* Give settings button visible background to match enter button */ + #saypi-settingsButton { + background-color: rgb(237, 225, 209); /* bg-cream-550 */ + border-radius: 50%; + } + + /* Consistent spacing between SayPi buttons */ + #saypi-enter-button { + margin-right: 8px; + } } /* Enter focus mode button specific sizing */ #saypi-enter-button { - width: 40px; - height: 40px; + width: 36px; + height: 36px; svg { color: rgb(107, 98, 85); @@ -153,16 +169,22 @@ html body.pi { align-self: center; } - /* Mobile-specific call button sizing for new chat pages */ + /* Mobile-specific button sizing to match Pi's native buttons */ @media (max-width: 768px) { #saypi-callButton { - /* Ensure button is visible and tappable on mobile */ - min-width: 2.5rem; - min-height: 2.5rem; - width: 2.5rem; - height: 2.5rem; + /* Match Pi's native submit button size (size-9 = 36px = 2.25rem) */ + min-width: 2.25rem; + min-height: 2.25rem; + width: 2.25rem; + height: 2.25rem; flex-shrink: 0; /* Prevent button from shrinking in flex container */ } + + /* Ensure SayPi toolbar buttons have visible background to match native neighbors */ + #saypi-enter-button, + #saypi-settingsButton { + background-color: rgb(237, 225, 209); /* bg-cream-550 */ + } } /* Sidebar menu button styling to match Pi's native buttons */