refactor(browser): split browser cores via build ARG, add core selector (#237)

* refactor(browser): split browser cores via build ARG, add core selector

- Replace playwright official image with ubuntu:noble base in both
  docker/Dockerfile.browser and devenv/Dockerfile.browser; install
  browsers at build time driven by ARG/ENV BROWSER_CORES
- Add GET /cores endpoint to Browser Gateway reporting available cores
- Proxy GET /browser-contexts/cores in Go handler to Browser Gateway
- Add `core` field to BrowserContextConfigModel and GatewayBrowserContext;
  context creation selects the appropriate browser instance by core
- Frontend context-setting page fetches available cores and renders a
  core selector; saves core as part of the config JSON
- install.sh prompts for browser core selection and writes BROWSER_CORES
  to .env; builds the browser image locally before docker compose up
- Regenerate OpenAPI spec and TypeScript SDK

* fix: lint
This commit is contained in:
Acbox Liu
2026-03-14 12:37:20 +08:00
committed by GitHub
parent 627b673a5c
commit c8728ffc2c
22 changed files with 364 additions and 33 deletions
+3
View File
@@ -393,6 +393,9 @@
"timezoneId": "Timezone",
"timezonePlaceholder": "e.g. America/New_York",
"ignoreHTTPSErrors": "Ignore HTTPS Errors",
"core": "Browser Core",
"chromium": "Chromium",
"firefox": "Firefox",
"config": "Configuration"
},
"mcp": {
+3
View File
@@ -389,6 +389,9 @@
"timezoneId": "时区",
"timezonePlaceholder": "例如 Asia/Shanghai",
"ignoreHTTPSErrors": "忽略 HTTPS 错误",
"core": "浏览器内核",
"chromium": "Chromium",
"firefox": "Firefox",
"config": "配置"
},
"mcp": {
@@ -41,6 +41,31 @@
{{ $t('browserContext.config') }}
</h3>
<FormField
v-slot="{ value, handleChange }"
name="core"
>
<FormItem>
<Label>{{ $t('browserContext.core') }}</Label>
<FormControl>
<div class="flex gap-3">
<button
v-for="c in availableCores"
:key="c"
type="button"
class="flex items-center gap-2 px-3 py-1.5 rounded-md border text-sm transition-colors"
:class="value === c
? 'border-primary bg-primary/10 text-primary font-medium'
: 'border-border bg-card text-muted-foreground hover:bg-accent'"
@click="handleChange(c)"
>
{{ $t(`browserContext.${c}`) }}
</button>
</div>
</FormControl>
</FormItem>
</FormField>
<div class="grid grid-cols-2 gap-4">
<FormField
v-slot="{ componentField }"
@@ -212,10 +237,11 @@ import {
import { toTypedSchema } from '@vee-validate/zod'
import z from 'zod'
import { useForm } from 'vee-validate'
import { useMutation, useQueryCache } from '@pinia/colada'
import { useMutation, useQuery, useQueryCache } from '@pinia/colada'
import { putBrowserContextsById, deleteBrowserContextsById } from '@memoh/sdk'
import { getBrowserContextsCoresQuery } from '@memoh/sdk/colada'
import type { BrowsercontextsBrowserContext } from '@memoh/sdk'
import { inject, watch, type Ref } from 'vue'
import { inject, watch, computed, type Ref } from 'vue'
import { useI18n } from 'vue-i18n'
import { toast } from 'vue-sonner'
import { useDialogMutation } from '@/composables/useDialogMutation'
@@ -228,7 +254,11 @@ const queryCache = useQueryCache()
const curContext = inject<Ref<BrowsercontextsBrowserContext | undefined>>('curBrowserContext')
const { data: coresData } = useQuery(getBrowserContextsCoresQuery())
const availableCores = computed(() => coresData.value?.cores ?? ['chromium'])
interface ConfigShape {
core?: string
viewport?: { width?: number; height?: number }
userAgent?: string
deviceScaleFactor?: number
@@ -251,6 +281,7 @@ function parseConfig(ctx: BrowsercontextsBrowserContext | undefined): ConfigShap
const schema = toTypedSchema(z.object({
name: z.string().min(1),
core: z.enum(['chromium', 'firefox']).optional(),
viewportWidth: z.coerce.number().optional(),
viewportHeight: z.coerce.number().optional(),
userAgent: z.string().optional(),
@@ -269,6 +300,7 @@ watch(() => curContext?.value, (ctx) => {
form.resetForm({
values: {
name: ctx.name || '',
core: (cfg.core as 'chromium' | 'firefox') ?? 'chromium',
viewportWidth: cfg.viewport?.width ?? 1280,
viewportHeight: cfg.viewport?.height ?? 720,
userAgent: cfg.userAgent ?? '',
@@ -307,7 +339,9 @@ const handleSave = form.handleSubmit(async (values) => {
const id = curContext?.value?.id
if (!id) return
const config: Record<string, any> = {}
const config: Record<string, any> = {
core: values.core ?? 'chromium',
}
if (values.viewportWidth || values.viewportHeight) {
config.viewport = {
width: values.viewportWidth || 1280,