agent-zero/plugins/_browser/webui/config.html
Alessandro cf67047ad3 Polish Browser chrome and extension management UX
Refine the Browser modal UI with more native-feeling tabs, consistent chrome controls, right-side tab close buttons, and a cleaner extension dropdown. Move the Browser LLM preset into the dropdown with the active Main Model summary, simplify extension settings, remove the global extension enable switch and legacy extension root behavior, and add per-extension enable toggles.

Also updates the Chrome extension install/review flow with contextual warning copy, “Scan with A0”, cleaner labels, hidden empty extension state, and regression coverage for the new Browser UX.
2026-04-26 00:09:16 +02:00

260 lines
7.8 KiB
HTML

<html>
<head>
<title>Browser Settings</title>
<script type="module">
import { store } from "/plugins/_browser/webui/browser-config-store.js";
</script>
</head>
<body>
<div x-data>
<template x-if="$store.browserConfig && config">
<div
class="browser-config-sections"
x-init="$store.browserConfig.init(config)"
x-effect="$store.browserConfig.bindConfig(config)"
x-destroy="$store.browserConfig.cleanup()"
>
<div class="browser-config-card">
<div class="section-title">Extensions</div>
<div class="section-description">
Choose which installed Chrome extensions Browser loads.
</div>
<div class="browser-config-warning">
<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. Keep only the extensions you trust enabled.
</span>
</div>
<div class="browser-config-extension-list">
<div class="browser-config-subtitle">
<span>Installed extensions</span>
<span class="material-symbols-outlined spinning" x-show="$store.browserConfig.extensionsLoading">progress_activity</span>
</div>
<template x-if="!$store.browserConfig.extensionsLoading && !$store.browserConfig.extensionsList.length">
<div class="browser-config-empty">No installed extensions found.</div>
</template>
<template x-for="extension in $store.browserConfig.extensionsList" :key="extension.path">
<label class="browser-config-extension-row" :title="extension.path">
<span class="browser-config-extension-text">
<span class="browser-config-extension-name" x-text="extension.name || 'Unnamed extension'"></span>
<span class="browser-config-extension-meta" x-text="$store.browserConfig.extensionVersionLabel(extension)"></span>
</span>
<span class="browser-config-toggle">
<input
type="checkbox"
:checked="$store.browserConfig.extensionEnabled(extension)"
@change="$store.browserConfig.setExtensionEnabled(extension, $event.target.checked)"
/>
<span class="browser-config-switch"></span>
</span>
</label>
</template>
<div class="browser-config-note" x-show="$store.browserConfig.extensionsError">
<span class="material-symbols-outlined">error</span>
<span x-text="$store.browserConfig.extensionsError"></span>
</div>
</div>
<div class="browser-config-pill-row">
<span class="browser-config-pill" x-text="$store.browserConfig.pathCountLabel()"></span>
<span class="browser-config-pill tone-active" x-show="$store.browserConfig.extensionModeReady()">
Ready
</span>
</div>
</div>
</div>
</template>
</div>
<style>
.browser-config-sections {
display: flex;
flex-direction: column;
gap: 16px;
}
.browser-config-card {
display: flex;
flex-direction: column;
gap: 14px;
padding: 16px;
border: 1px solid var(--color-border);
border-radius: 8px;
}
.browser-config-extension-list {
display: flex;
flex-direction: column;
gap: 8px;
padding: 12px;
border: 1px solid color-mix(in srgb, var(--color-border) 72%, transparent);
border-radius: 8px;
background: color-mix(in srgb, var(--color-panel) 78%, transparent);
}
.browser-config-subtitle {
display: flex;
align-items: center;
justify-content: space-between;
gap: 8px;
color: var(--color-text-secondary);
font-size: 0.82rem;
font-weight: 650;
}
.browser-config-subtitle .material-symbols-outlined {
font-size: 18px;
}
.browser-config-empty,
.browser-config-extension-meta {
color: var(--color-text-secondary);
font-size: 0.78rem;
}
.browser-config-extension-row {
display: grid;
grid-template-columns: minmax(0, 1fr) auto;
align-items: center;
gap: 14px;
min-height: 42px;
padding: 8px 0;
}
.browser-config-extension-row + .browser-config-extension-row {
border-top: 1px solid color-mix(in srgb, var(--color-border) 52%, transparent);
}
.browser-config-extension-text {
display: flex;
min-width: 0;
flex-direction: column;
gap: 2px;
}
.browser-config-extension-name {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
color: var(--color-text);
font-weight: 650;
}
.browser-config-toggle {
position: relative;
display: inline-flex;
flex: 0 0 auto;
align-items: center;
width: 42px;
height: 24px;
}
.browser-config-toggle input {
position: absolute;
inset: 0;
margin: 0;
opacity: 0;
cursor: pointer;
}
.browser-config-switch {
width: 100%;
height: 100%;
border-radius: 999px;
background: color-mix(in srgb, var(--color-border) 78%, transparent);
transition: background-color 0.18s cubic-bezier(0.4, 0, 0.2, 1);
}
.browser-config-switch::after {
content: "";
position: absolute;
top: 3px;
left: 3px;
width: 18px;
height: 18px;
border-radius: 50%;
background: var(--color-background);
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.24);
transition: transform 0.18s cubic-bezier(0.4, 0, 0.2, 1);
}
.browser-config-toggle input:checked + .browser-config-switch {
background: color-mix(in srgb, var(--color-primary) 76%, #16a34a);
}
.browser-config-toggle input:checked + .browser-config-switch::after {
transform: translateX(18px);
}
.browser-config-note {
display: flex;
align-items: flex-start;
gap: 8px;
padding: 10px 12px;
border-radius: 8px;
background: color-mix(in srgb, var(--color-panel) 82%, transparent);
color: var(--color-text-secondary);
font-size: var(--font-size-small);
}
.browser-config-warning {
display: flex;
align-items: flex-start;
gap: 9px;
padding: 11px 12px;
border: 1px solid color-mix(in srgb, #d97706 44%, var(--color-border));
border-radius: 8px;
background: color-mix(in srgb, #d97706 14%, var(--color-background));
color: color-mix(in srgb, var(--color-text) 86%, #92400e);
font-size: var(--font-size-small);
line-height: 1.4;
}
.browser-config-warning .material-symbols-outlined {
color: #b45309;
font-size: 20px;
}
.browser-config-sections .spinning {
display: inline-block;
transform-origin: center;
animation: browser-config-spin 0.8s linear infinite;
}
@keyframes browser-config-spin {
to {
transform: rotate(360deg);
}
}
.browser-config-pill-row {
display: flex;
flex-wrap: wrap;
gap: 8px;
}
.browser-config-pill {
display: inline-flex;
align-items: center;
gap: 6px;
min-height: 28px;
padding: 0 10px;
border-radius: 999px;
border: 1px solid color-mix(in srgb, var(--color-border) 70%, transparent);
background: color-mix(in srgb, var(--color-panel) 88%, transparent);
font-size: 0.78rem;
color: var(--color-text-secondary);
}
.browser-config-pill.tone-active {
color: #1b5e20;
border-color: rgba(27, 94, 32, 0.18);
background: rgba(46, 125, 50, 0.12);
}
</style>
</body>
</html>