Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "webbrain",
"version": "25.8.5",
"version": "25.8.6",
"description": "Open-source AI browser agent — chat with pages, automate tasks, multi-provider LLM support.",
"private": true,
"type": "module",
Expand Down
2 changes: 1 addition & 1 deletion src/chrome/ARCHITECTURE.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# WebBrain Chrome/Edge Extension — Architecture

> Version 25.8.5 · Manifest V3 · Service Worker background
> Version 25.8.6 · Manifest V3 · Service Worker background

## High-Level Overview

Expand Down
2 changes: 1 addition & 1 deletion src/chrome/manifest.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"manifest_version": 3,
"name": "WebBrain",
"version": "25.8.5",
"version": "25.8.6",
"description": "Open-source AI browser agent — chat with pages, automate tasks, multi-provider LLM support.",
"permissions": [
"sidePanel",
Expand Down
2 changes: 1 addition & 1 deletion src/chrome/src/ui/settings.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ import { ADDITIONAL_PROVIDER_UI } from '../providers/provider-catalog.js';

// Version shown in the subtitle. Kept here so it only needs one update per
// release; the subtitle string itself is translated.
const EXT_VERSION = '25.8.5';
const EXT_VERSION = '25.8.6';

const providersContainer = document.getElementById('providers');
const displaySettings = document.getElementById('display-settings');
Expand Down
40 changes: 40 additions & 0 deletions src/chrome/src/ui/sidepanel.js
Original file line number Diff line number Diff line change
Expand Up @@ -523,6 +523,14 @@ const activityText = document.getElementById('activity-text');
const modeAskBtn = document.getElementById('btn-mode-ask');
const modeActBtn = document.getElementById('btn-mode-act');
const modeDevBtn = document.getElementById('btn-mode-dev');
const modeToggleEl = document.getElementById('mode-toggle');
const modeToggleHighlight = (() => {
const el = document.createElement('div');
el.className = 'mode-toggle-highlight instant';
el.setAttribute('aria-hidden', 'true');
modeToggleEl?.prepend(el);
return el;
})();
const actWarning = document.getElementById('act-warning');
const inputArea = document.getElementById('input-area');
const slashCommandMenuEl = document.getElementById('slash-command-menu');
Expand Down Expand Up @@ -9503,6 +9511,14 @@ async function handleGlobalKeydown(e) {

// --- Mode Toggle ---

function positionModeHighlight(btn, { instant = false } = {}) {
if (!modeToggleHighlight || !btn) return;
if (instant) modeToggleHighlight.classList.add('instant');
modeToggleHighlight.style.width = `${btn.offsetWidth}px`;
modeToggleHighlight.style.transform = `translate3d(${btn.offsetLeft}px, 0, 0)`;
if (instant) requestAnimationFrame(() => modeToggleHighlight.classList.remove('instant'));
}

function setMode(mode) {
if (mode !== 'ask' && mode !== 'act' && mode !== 'dev') mode = 'ask';
const previousMode = agentMode;
Expand All @@ -9521,6 +9537,12 @@ function setMode(mode) {
modeDevBtn?.classList.toggle('active', mode === 'dev');
modeDevBtn?.classList.toggle('act', mode === 'dev');
inputArea.classList.toggle('act-mode', mode !== 'ask');

// Slide the highlight pill to the active button
const activeBtn = mode === 'ask' ? modeAskBtn : mode === 'act' ? modeActBtn : modeDevBtn;
modeToggleHighlight?.classList.toggle('act', mode !== 'ask');
positionModeHighlight(activeBtn);

updateActWarning();
resetInputPlaceholderRotation();
}
Expand Down Expand Up @@ -9559,6 +9581,24 @@ modeDevBtn?.addEventListener('click', async () => {
await ensureDevMode();
});

// Set initial highlight position without animation
requestAnimationFrame(() => positionModeHighlight(modeAskBtn, { instant: true }));

// Recalculate highlight when the toggle resizes (e.g. side panel drag)
if (modeToggleEl) {
new ResizeObserver(() => {
const activeBtn = modeToggleEl.querySelector('.mode-btn.active');
if (activeBtn) positionModeHighlight(activeBtn, { instant: true });
}).observe(modeToggleEl);
}

// Reposition highlight when document direction changes (RTL locale switch)
new MutationObserver(() => {
requestAnimationFrame(() => {
const activeBtn = modeToggleEl?.querySelector('.mode-btn.active');
if (activeBtn) positionModeHighlight(activeBtn, { instant: true });
});
}).observe(document.documentElement, { attributes: true, attributeFilter: ['dir'] });

// --- Stop / Abort ---

Expand Down
31 changes: 28 additions & 3 deletions src/chrome/styles/sidepanel.css
Original file line number Diff line number Diff line change
Expand Up @@ -2112,10 +2112,37 @@ body {
border: 1px solid var(--border);
border-radius: 8px;
padding: 2px;
position: relative;
}

.mode-toggle-highlight {
position: absolute;
top: 2px;
left: 0;
z-index: 0;
height: calc(100% - 4px);
border-radius: 6px;
background: var(--accent-dim);
pointer-events: none;
will-change: transform, width;
transition:
transform 170ms cubic-bezier(0.22, 1, 0.36, 1),
width 170ms cubic-bezier(0.22, 1, 0.36, 1),
background 170ms ease;
}

.mode-toggle-highlight.act {
background: rgba(255, 152, 0, 0.12);
}

.mode-toggle-highlight.instant {
transition: none;
}

.mode-btn {
flex: 1;
position: relative;
z-index: 1;
background: none;
border: none;
color: var(--text-muted);
Expand All @@ -2124,7 +2151,7 @@ body {
padding: 5px 0;
border-radius: 6px;
cursor: pointer;
transition: all 0.2s;
transition: color 0.2s;
text-align: center;
}

Expand All @@ -2133,12 +2160,10 @@ body {
}

.mode-btn.active {
background: var(--accent-dim);
color: var(--accent);
}

.mode-btn.active.act {
background: rgba(255, 152, 0, 0.12);
color: var(--warning);
}

Expand Down
2 changes: 1 addition & 1 deletion src/firefox/ARCHITECTURE.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# WebBrain Firefox Extension — Architecture

> Version 25.8.5 · Manifest V2 · Background Page
> Version 25.8.6 · Manifest V2 · Background Page

## How Firefox Differs from Chrome

Expand Down
2 changes: 1 addition & 1 deletion src/firefox/manifest.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"manifest_version": 2,
"name": "WebBrain",
"version": "25.8.5",
"version": "25.8.6",
"description": "Open-source AI browser agent — chat with pages, automate tasks, multi-provider LLM support.",
"permissions": [
"activeTab",
Expand Down
2 changes: 1 addition & 1 deletion src/firefox/src/ui/settings.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ import { ADDITIONAL_PROVIDER_UI } from '../providers/provider-catalog.js';

// Version shown in the subtitle. Kept here so it only needs one update per
// release; the subtitle string itself is translated.
const EXT_VERSION = '25.8.5';
const EXT_VERSION = '25.8.6';

const providersContainer = document.getElementById('providers');
const displaySettings = document.getElementById('display-settings');
Expand Down
40 changes: 40 additions & 0 deletions src/firefox/src/ui/sidepanel.js
Original file line number Diff line number Diff line change
Expand Up @@ -398,6 +398,14 @@ const activityText = document.getElementById('activity-text');
const modeAskBtn = document.getElementById('btn-mode-ask');
const modeActBtn = document.getElementById('btn-mode-act');
const modeDevBtn = document.getElementById('btn-mode-dev');
const modeToggleEl = document.getElementById('mode-toggle');
const modeToggleHighlight = (() => {
const el = document.createElement('div');
el.className = 'mode-toggle-highlight instant';
el.setAttribute('aria-hidden', 'true');
modeToggleEl?.prepend(el);
return el;
})();
const actWarning = document.getElementById('act-warning');
const inputArea = document.getElementById('input-area');
const slashCommandMenuEl = document.getElementById('slash-command-menu');
Expand Down Expand Up @@ -9073,6 +9081,14 @@ async function sendToBackground(action, data = {}) {

// --- Mode Toggle ---

function positionModeHighlight(btn, { instant = false } = {}) {
if (!modeToggleHighlight || !btn) return;
if (instant) modeToggleHighlight.classList.add('instant');
modeToggleHighlight.style.width = `${btn.offsetWidth}px`;
modeToggleHighlight.style.transform = `translate3d(${btn.offsetLeft}px, 0, 0)`;
if (instant) requestAnimationFrame(() => modeToggleHighlight.classList.remove('instant'));
}

function setMode(mode) {
if (mode !== 'ask' && mode !== 'act' && mode !== 'dev') mode = 'ask';
agentMode = mode;
Expand All @@ -9084,6 +9100,12 @@ function setMode(mode) {
modeDevBtn?.classList.toggle('active', mode === 'dev');
modeDevBtn?.classList.toggle('act', mode === 'dev');
inputArea.classList.toggle('act-mode', mode !== 'ask');

// Slide the highlight pill to the active button
const activeBtn = mode === 'ask' ? modeAskBtn : mode === 'act' ? modeActBtn : modeDevBtn;
modeToggleHighlight?.classList.toggle('act', mode !== 'ask');
positionModeHighlight(activeBtn);

updateActWarning();
resetInputPlaceholderRotation();
}
Expand Down Expand Up @@ -9122,6 +9144,24 @@ modeDevBtn?.addEventListener('click', async () => {
await ensureDevMode();
});

// Set initial highlight position without animation
requestAnimationFrame(() => positionModeHighlight(modeAskBtn, { instant: true }));

// Recalculate highlight when the toggle resizes (e.g. side panel drag)
if (modeToggleEl) {
new ResizeObserver(() => {
const activeBtn = modeToggleEl.querySelector('.mode-btn.active');
if (activeBtn) positionModeHighlight(activeBtn, { instant: true });
}).observe(modeToggleEl);
}

// Reposition highlight when document direction changes (RTL locale switch)
new MutationObserver(() => {
requestAnimationFrame(() => {
const activeBtn = modeToggleEl?.querySelector('.mode-btn.active');
if (activeBtn) positionModeHighlight(activeBtn, { instant: true });
});
}).observe(document.documentElement, { attributes: true, attributeFilter: ['dir'] });

// --- Stop / Abort ---

Expand Down
31 changes: 28 additions & 3 deletions src/firefox/styles/sidepanel.css
Original file line number Diff line number Diff line change
Expand Up @@ -1983,10 +1983,37 @@ body {
border: 1px solid var(--border);
border-radius: 8px;
padding: 2px;
position: relative;
}

.mode-toggle-highlight {
position: absolute;
top: 2px;
left: 0;
z-index: 0;
height: calc(100% - 4px);
border-radius: 6px;
background: var(--accent-dim);
pointer-events: none;
will-change: transform, width;
transition:
transform 170ms cubic-bezier(0.22, 1, 0.36, 1),
width 170ms cubic-bezier(0.22, 1, 0.36, 1),
background 170ms ease;
}

.mode-toggle-highlight.act {
background: rgba(255, 152, 0, 0.12);
}

.mode-toggle-highlight.instant {
transition: none;
}

.mode-btn {
flex: 1;
position: relative;
z-index: 1;
background: none;
border: none;
color: var(--text-muted);
Expand All @@ -1995,7 +2022,7 @@ body {
padding: 5px 0;
border-radius: 6px;
cursor: pointer;
transition: all 0.2s;
transition: color 0.2s;
text-align: center;
}

Expand All @@ -2004,12 +2031,10 @@ body {
}

.mode-btn.active {
background: var(--accent-dim);
color: var(--accent);
}

.mode-btn.active.act {
background: rgba(255, 152, 0, 0.12);
color: var(--warning);
}

Expand Down
Loading