mirror of
https://github.com/agent0ai/agent-zero.git
synced 2026-07-09 17:08:29 +00:00
Group mobile browser toolbar controls
Move Annotate and Browser settings into the Browser toolbar so narrow views keep navigation and controls on one row above a full-width address bar. Make the Browser panel itself the container-query boundary so mobile modals and narrow right-canvas panels share the same polished toolbar layout, with regression and DOX coverage.
This commit is contained in:
parent
0028473ec9
commit
6ccd48e81f
3 changed files with 139 additions and 126 deletions
|
|
@ -19,7 +19,7 @@
|
|||
- Preserve Playwright lifecycle cleanup and WebSocket viewer compatibility across regular host browsers and Electron WebContentsView embedding.
|
||||
- Keep the WebUI Browser inside its own modal/canvas affordance; do not replace it with page-level navigation.
|
||||
- Default the visible WebUI Browser to live CDP screencast for responsiveness. Keep lightweight CDP/DOM state snapshots as the fallback transport.
|
||||
- Keep narrow WebUI Browser controls usable by placing navigation buttons above a full-width address bar.
|
||||
- Keep narrow WebUI Browser controls usable by grouping navigation with Annotate/settings above a full-width address bar.
|
||||
- Prefer DOM/CDP browser actions with refs, selectors, frame-chain refs, and screenshots over viewport coordinate input. Coordinates remain a visual fallback.
|
||||
- Do not hardcode user-specific browser paths or secrets.
|
||||
|
||||
|
|
|
|||
|
|
@ -42,118 +42,6 @@
|
|||
</button>
|
||||
</div>
|
||||
|
||||
<div class="browser-session-controls">
|
||||
<button type="button" class="btn browser-annotate-toggle" title="Annotate" aria-label="Annotate"
|
||||
:aria-pressed="$store.browserPage.annotating.toString()"
|
||||
:class="{ 'is-active': $store.browserPage.annotating }"
|
||||
:disabled="!$store.browserPage.canAnnotate() && !$store.browserPage.annotating"
|
||||
@click="$store.browserPage.toggleAnnotationMode()">
|
||||
<span class="material-symbols-outlined" aria-hidden="true"
|
||||
x-text="$store.browserPage.annotating ? 'rate_review' : 'edit_note'"></span>
|
||||
<span x-text="$store.browserPage.annotating ? 'Annotating' : 'Annotate'"></span>
|
||||
</button>
|
||||
<div class="browser-extension-menu" @click.outside="$store.browserPage.closeExtensionsMenu()"
|
||||
@keydown.escape.window="$store.browserPage.closeExtensionsMenu()">
|
||||
<button type="button" class="btn btn-icon-action browser-extensions" title="Browser settings"
|
||||
aria-label="Browser settings" @click.stop="$store.browserPage.toggleExtensionsMenu()"
|
||||
:aria-expanded="$store.browserPage.extensionMenuOpen.toString()"
|
||||
:class="{ 'is-active': $store.browserPage.status?.extensions?.active }">
|
||||
<span class="material-symbols-outlined">tune</span>
|
||||
</button>
|
||||
<div class="browser-extension-dropdown" x-show="$store.browserPage.extensionMenuOpen" x-transition
|
||||
style="display: none;">
|
||||
<div class="browser-extension-preset">
|
||||
<label for="browser-model-preset">Browser LLM Preset</label>
|
||||
<select id="browser-model-preset" x-model="$store.browserPage.modelPreset"
|
||||
@change="$store.browserPage.setBrowserModelPreset($event.target.value)"
|
||||
:disabled="$store.browserPage.modelPresetSaving">
|
||||
<option value="">Default Main Model</option>
|
||||
<template x-for="preset in $store.browserPage.modelPresetOptions" :key="preset.name">
|
||||
<option :value="preset.name" x-text="preset.label"></option>
|
||||
</template>
|
||||
</select>
|
||||
<div class="browser-extension-preset-summary" x-text="$store.browserPage.modelPresetSummary()">
|
||||
</div>
|
||||
</div>
|
||||
<div class="browser-extension-section-title">Chrome Extensions</div>
|
||||
<button type="button" class="dropdown-item" @click="$store.browserPage.createExtensionWithAgent()">
|
||||
<span class="material-symbols-outlined">add_circle</span>
|
||||
<span>Create New Extension with A0</span>
|
||||
</button>
|
||||
<div class="browser-extension-url">
|
||||
<label for="browser-extension-url">Input a Chrome Web Store URL</label>
|
||||
<input id="browser-extension-url" type="url" x-model="$store.browserPage.extensionInstallUrl"
|
||||
@keydown.enter.prevent="$store.browserPage.installExtensionFromUrl()"
|
||||
placeholder="https://chromewebstore.google.com/detail/..." />
|
||||
<div class="browser-extension-url-actions">
|
||||
<button type="button" class="btn btn-ok" @click="$store.browserPage.installExtensionFromUrl()"
|
||||
:disabled="$store.browserPage.extensionActionLoading">
|
||||
<span class="material-symbols-outlined"
|
||||
:class="{ spinning: $store.browserPage.extensionActionLoading }"
|
||||
x-text="$store.browserPage.extensionActionLoading ? 'progress_activity' : 'download'"></span>
|
||||
<span>Install URL</span>
|
||||
</button>
|
||||
<button type="button" class="btn btn-field"
|
||||
@click="$store.browserPage.askAgentInstallExtension()">
|
||||
<span class="material-symbols-outlined">psychology_alt</span>
|
||||
<span x-text="$store.browserPage.extensionAssistantActionLabel()">Scan with A0</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="browser-extension-warning" x-show="$store.browserPage.hasExtensionInstallUrl()"
|
||||
x-transition style="display: none;">
|
||||
<span class="material-symbols-outlined">warning</span>
|
||||
<span>
|
||||
Extensions run inside the Docker browser sandbox, but malicious or buggy extensions can still
|
||||
damage that environment. Review what you install.
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="browser-extension-list"
|
||||
x-show="$store.browserPage.extensionsListLoading || $store.browserPage.extensionsList.length"
|
||||
style="display: none;">
|
||||
<div class="browser-extension-list-header">
|
||||
<span>Installed extensions</span>
|
||||
<span class="material-symbols-outlined spinning"
|
||||
x-show="$store.browserPage.extensionsListLoading">progress_activity</span>
|
||||
</div>
|
||||
<template x-for="extension in $store.browserPage.extensionsList" :key="extension.path">
|
||||
<div class="browser-extension-row" :title="extension.path">
|
||||
<span class="browser-extension-row-text">
|
||||
<span class="browser-extension-name" x-text="extension.name || 'Unnamed extension'"></span>
|
||||
<span class="browser-extension-meta"
|
||||
x-text="$store.browserPage.extensionVersionLabel(extension)"></span>
|
||||
</span>
|
||||
<span class="browser-extension-row-actions">
|
||||
<button type="button" class="browser-extension-open"
|
||||
x-show="$store.browserPage.extensionHasOpenUi(extension)"
|
||||
:title="$store.browserPage.extensionOpenTitle(extension)"
|
||||
:aria-label="$store.browserPage.extensionOpenTitle(extension)"
|
||||
:disabled="!extension.enabled || $store.browserPage.isBusy()"
|
||||
@click.stop="$store.browserPage.openExtensionUi(extension)">
|
||||
<span class="material-symbols-outlined">open_in_new</span>
|
||||
<span>Open</span>
|
||||
</button>
|
||||
<label class="browser-extension-toggle">
|
||||
<input type="checkbox" :checked="extension.enabled"
|
||||
:disabled="$store.browserPage.extensionToggleLoadingPath === extension.path"
|
||||
@change="$store.browserPage.setExtensionEnabled(extension, $event.target.checked, $event.target)" />
|
||||
<span class="browser-extension-switch"></span>
|
||||
</label>
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
<button type="button" class="dropdown-item" @click="$store.browserPage.openExtensionsSettings()">
|
||||
<span class="material-symbols-outlined">tune</span>
|
||||
<span>Settings</span>
|
||||
</button>
|
||||
<div class="browser-extension-message" x-show="$store.browserPage.extensionActionMessage"
|
||||
x-text="$store.browserPage.extensionActionMessage"></div>
|
||||
<div class="browser-extension-error" x-show="$store.browserPage.extensionActionError"
|
||||
x-text="$store.browserPage.extensionActionError"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
@ -173,6 +61,119 @@
|
|||
</button>
|
||||
</div>
|
||||
|
||||
<div class="browser-session-controls">
|
||||
<button type="button" class="btn browser-annotate-toggle" title="Annotate" aria-label="Annotate"
|
||||
:aria-pressed="$store.browserPage.annotating.toString()"
|
||||
:class="{ 'is-active': $store.browserPage.annotating }"
|
||||
:disabled="!$store.browserPage.canAnnotate() && !$store.browserPage.annotating"
|
||||
@click="$store.browserPage.toggleAnnotationMode()">
|
||||
<span class="material-symbols-outlined" aria-hidden="true"
|
||||
x-text="$store.browserPage.annotating ? 'rate_review' : 'edit_note'"></span>
|
||||
<span x-text="$store.browserPage.annotating ? 'Annotating' : 'Annotate'"></span>
|
||||
</button>
|
||||
<div class="browser-extension-menu" @click.outside="$store.browserPage.closeExtensionsMenu()"
|
||||
@keydown.escape.window="$store.browserPage.closeExtensionsMenu()">
|
||||
<button type="button" class="btn btn-icon-action browser-extensions" title="Browser settings"
|
||||
aria-label="Browser settings" @click.stop="$store.browserPage.toggleExtensionsMenu()"
|
||||
:aria-expanded="$store.browserPage.extensionMenuOpen.toString()"
|
||||
:class="{ 'is-active': $store.browserPage.status?.extensions?.active }">
|
||||
<span class="material-symbols-outlined">tune</span>
|
||||
</button>
|
||||
<div class="browser-extension-dropdown" x-show="$store.browserPage.extensionMenuOpen" x-transition
|
||||
style="display: none;">
|
||||
<div class="browser-extension-preset">
|
||||
<label for="browser-model-preset">Browser LLM Preset</label>
|
||||
<select id="browser-model-preset" x-model="$store.browserPage.modelPreset"
|
||||
@change="$store.browserPage.setBrowserModelPreset($event.target.value)"
|
||||
:disabled="$store.browserPage.modelPresetSaving">
|
||||
<option value="">Default Main Model</option>
|
||||
<template x-for="preset in $store.browserPage.modelPresetOptions" :key="preset.name">
|
||||
<option :value="preset.name" x-text="preset.label"></option>
|
||||
</template>
|
||||
</select>
|
||||
<div class="browser-extension-preset-summary" x-text="$store.browserPage.modelPresetSummary()">
|
||||
</div>
|
||||
</div>
|
||||
<div class="browser-extension-section-title">Chrome Extensions</div>
|
||||
<button type="button" class="dropdown-item" @click="$store.browserPage.createExtensionWithAgent()">
|
||||
<span class="material-symbols-outlined">add_circle</span>
|
||||
<span>Create New Extension with A0</span>
|
||||
</button>
|
||||
<div class="browser-extension-url">
|
||||
<label for="browser-extension-url">Input a Chrome Web Store URL</label>
|
||||
<input id="browser-extension-url" type="url" x-model="$store.browserPage.extensionInstallUrl"
|
||||
@keydown.enter.prevent="$store.browserPage.installExtensionFromUrl()"
|
||||
placeholder="https://chromewebstore.google.com/detail/..." />
|
||||
<div class="browser-extension-url-actions">
|
||||
<button type="button" class="btn btn-ok" @click="$store.browserPage.installExtensionFromUrl()"
|
||||
:disabled="$store.browserPage.extensionActionLoading">
|
||||
<span class="material-symbols-outlined"
|
||||
:class="{ spinning: $store.browserPage.extensionActionLoading }"
|
||||
x-text="$store.browserPage.extensionActionLoading ? 'progress_activity' : 'download'"></span>
|
||||
<span>Install URL</span>
|
||||
</button>
|
||||
<button type="button" class="btn btn-field"
|
||||
@click="$store.browserPage.askAgentInstallExtension()">
|
||||
<span class="material-symbols-outlined">psychology_alt</span>
|
||||
<span x-text="$store.browserPage.extensionAssistantActionLabel()">Scan with A0</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="browser-extension-warning" x-show="$store.browserPage.hasExtensionInstallUrl()"
|
||||
x-transition style="display: none;">
|
||||
<span class="material-symbols-outlined">warning</span>
|
||||
<span>
|
||||
Extensions run inside the Docker browser sandbox, but malicious or buggy extensions can still
|
||||
damage that environment. Review what you install.
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="browser-extension-list"
|
||||
x-show="$store.browserPage.extensionsListLoading || $store.browserPage.extensionsList.length"
|
||||
style="display: none;">
|
||||
<div class="browser-extension-list-header">
|
||||
<span>Installed extensions</span>
|
||||
<span class="material-symbols-outlined spinning"
|
||||
x-show="$store.browserPage.extensionsListLoading">progress_activity</span>
|
||||
</div>
|
||||
<template x-for="extension in $store.browserPage.extensionsList" :key="extension.path">
|
||||
<div class="browser-extension-row" :title="extension.path">
|
||||
<span class="browser-extension-row-text">
|
||||
<span class="browser-extension-name" x-text="extension.name || 'Unnamed extension'"></span>
|
||||
<span class="browser-extension-meta"
|
||||
x-text="$store.browserPage.extensionVersionLabel(extension)"></span>
|
||||
</span>
|
||||
<span class="browser-extension-row-actions">
|
||||
<button type="button" class="browser-extension-open"
|
||||
x-show="$store.browserPage.extensionHasOpenUi(extension)"
|
||||
:title="$store.browserPage.extensionOpenTitle(extension)"
|
||||
:aria-label="$store.browserPage.extensionOpenTitle(extension)"
|
||||
:disabled="!extension.enabled || $store.browserPage.isBusy()"
|
||||
@click.stop="$store.browserPage.openExtensionUi(extension)">
|
||||
<span class="material-symbols-outlined">open_in_new</span>
|
||||
<span>Open</span>
|
||||
</button>
|
||||
<label class="browser-extension-toggle">
|
||||
<input type="checkbox" :checked="extension.enabled"
|
||||
:disabled="$store.browserPage.extensionToggleLoadingPath === extension.path"
|
||||
@change="$store.browserPage.setExtensionEnabled(extension, $event.target.checked, $event.target)" />
|
||||
<span class="browser-extension-switch"></span>
|
||||
</label>
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
<button type="button" class="dropdown-item" @click="$store.browserPage.openExtensionsSettings()">
|
||||
<span class="material-symbols-outlined">tune</span>
|
||||
<span>Settings</span>
|
||||
</button>
|
||||
<div class="browser-extension-message" x-show="$store.browserPage.extensionActionMessage"
|
||||
x-text="$store.browserPage.extensionActionMessage"></div>
|
||||
<div class="browser-extension-error" x-show="$store.browserPage.extensionActionError"
|
||||
x-text="$store.browserPage.extensionActionError"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form class="browser-address-form" aria-label="Browser navigation" @submit.prevent="$store.browserPage.go()">
|
||||
<span class="material-symbols-outlined browser-address-icon">language</span>
|
||||
<input class="browser-address" aria-label="Browser address" name="browser_address" x-model="$store.browserPage.address"
|
||||
|
|
@ -384,6 +385,7 @@
|
|||
--browser-tab-close-size: 32px;
|
||||
--browser-control-radius: 0.55rem;
|
||||
box-sizing: border-box;
|
||||
container-type: inline-size;
|
||||
display: flex;
|
||||
flex: 1 1 auto;
|
||||
flex-direction: column;
|
||||
|
|
@ -395,8 +397,8 @@
|
|||
|
||||
.browser-toolbar {
|
||||
display: grid;
|
||||
grid-template-columns: auto minmax(0, 1fr);
|
||||
grid-template-areas: "nav address";
|
||||
grid-template-columns: auto minmax(0, 1fr) auto;
|
||||
grid-template-areas: "nav address controls";
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
padding: 8px 10px 9px;
|
||||
|
|
@ -475,7 +477,7 @@
|
|||
|
||||
.browser-meta-top {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1fr) auto;
|
||||
grid-template-columns: minmax(0, 1fr);
|
||||
gap: 10px;
|
||||
align-items: end;
|
||||
}
|
||||
|
|
@ -647,11 +649,12 @@
|
|||
}
|
||||
|
||||
.browser-session-controls {
|
||||
grid-area: controls;
|
||||
display: flex;
|
||||
align-items: end;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
gap: 6px;
|
||||
min-width: 0;
|
||||
padding-bottom: 7px;
|
||||
}
|
||||
|
||||
.browser-annotate-toggle {
|
||||
|
|
@ -1279,11 +1282,11 @@
|
|||
|
||||
@container (max-width: 460px) {
|
||||
.browser-toolbar {
|
||||
grid-template-columns: minmax(0, 1fr);
|
||||
grid-template-columns: auto minmax(0, 1fr) auto;
|
||||
grid-template-areas:
|
||||
"nav"
|
||||
"address";
|
||||
align-items: stretch;
|
||||
"nav . controls"
|
||||
"address address address";
|
||||
align-items: center;
|
||||
gap: 7px;
|
||||
}
|
||||
|
||||
|
|
@ -1300,7 +1303,7 @@
|
|||
}
|
||||
|
||||
.browser-session-controls {
|
||||
width: 100%;
|
||||
width: auto;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
|
|
@ -1332,6 +1335,7 @@
|
|||
.browser-extension-dropdown {
|
||||
right: 0;
|
||||
left: auto;
|
||||
top: calc(var(--browser-control-size) + var(--browser-address-height) + 13px);
|
||||
width: min(296px, calc(100vw - 72px));
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue