@mochabug/adapt-vue
v1.0.1-rc.30
Published
Vue component for Adapt automation platform
Readme
@mochabug/adapt-vue
Vue component for Adapt.
npm install @mochabug/adapt-vueRequires Vue 3.
Quickstart
<script setup>
import { AdaptAutomation } from '@mochabug/adapt-vue';
</script>
<template>
<AdaptAutomation automation-id="auto-123" :style="{ height: '600px' }" />
</template>With authentication:
<AdaptAutomation automation-id="auto-123" auth-token="your-token" :style="{ height: '600px' }" />With proof-of-work challenge:
<AdaptAutomation automation-id="auto-123" :requires-challenge="true" :style="{ height: '600px' }" />SSR (Nuxt)
Keep auth token on server:
<script setup>
import { AdaptAutomation } from '@mochabug/adapt-vue';
import { startSession } from '@mochabug/adapt-core';
const authToken = await getAuthTokenFromBackend();
const { token } = await startSession({ id: 'auto-123' }, authToken);
</script>
<template>
<AdaptAutomation automation-id="auto-123" :session-token="token" :style="{ height: '600px' }" />
</template>Session inheritance
<!-- direct -->
<AdaptAutomation automation-id="auto-123" inherit-token="token-from-parent" />
<!-- from URL hash: example.com#mb_session=xxx -->
<AdaptAutomation automation-id="auto-123" :inherit-from="{ hash: 'mb_session' }" />Fork display
<!-- side-by-side (default) -->
<AdaptAutomation automation-id="auto-123" :fork-display="{ mode: 'side-by-side', split: 60 }" />
<!-- dialog -->
<AdaptAutomation automation-id="auto-123" :fork-display="{ mode: 'dialog' }" />Events
<AdaptAutomation
automation-id="auto-123"
@session="(status, fork) => console.log(status, fork)"
@output="(output) => console.log(output)"
@fork-active="(active) => console.log(active)"
/>2. AdaptCap
Standalone proof-of-work challenge widget. Use when you manage the automation client yourself.
<script setup>
import { AdaptCap, createConnectClient } from '@mochabug/adapt-vue/cap';
const client = createConnectClient({ id: 'YOUR_ID' });
function onSolve(token: string, expires: Date) {
console.log('Solved:', token, expires);
}
</script>
<template>
<AdaptCap
automation-id="YOUR_ID"
:client="client"
@solve="onSolve"
@error="(err) => console.error(err)"
/>
</template>Props
| Prop | Type |
|------|------|
| automation-id | string (required) |
| client | AutomationClient (required) |
| worker-count | number |
| i18n | CapWidgetI18n |
| dark-mode | boolean |
Events
| Event | Payload |
|-------|---------|
| solve | (token: string, expires: Date) |
| error | (error: Error) |
Headless (no UI)
Use the lower-level API to create and redeem challenges yourself:
import { createChallenge, redeemChallenge, createConnectClient } from '@mochabug/adapt-vue/cap';
const client = createConnectClient({ id: 'YOUR_ID' });
const challenge = await createChallenge(client);
// ... solve with Cap.js or your own solver ...
const redeemed = await redeemChallenge(client, solutions);Styling
There are three ways to style the component, from simplest to most advanced. Use whichever fits your workflow.
1. theme prop (recommended)
Pass an AdaptTheme object for semantic, token-based theming without writing CSS:
<script setup>
import { AdaptAutomation } from '@mochabug/adapt-vue';
const myTheme = {
mode: 'light',
primary: '#4f46e5',
background: '#ffffff',
surface: '#f0f4ff',
text: '#1e293b',
textSecondary: '#64748b',
border: '#cbd5e1',
font: '"Inter", sans-serif',
};
</script>
<template>
<AdaptAutomation
automation-id="auto-123"
:theme="myTheme"
:style="{ height: '600px' }"
/>
</template>AdaptTheme tokens:
| Token | Type | What it controls |
|---|---|---|
| mode | 'light' \| 'dark' | Dark-mode class + shadow/border defaults |
| primary | string | Accent color — derives separator, drop targets, spinner, status icon |
| background | string | Root background, active-tab bg, status-card bg |
| surface | string | Panel content bg, toolbar / inactive-tab bg |
| text | string | Active-tab text, status text, cap text |
| textSecondary | string | Inactive-tab text |
| border | string | Tab/panel borders, status-card border, cap border |
| font | string | Font family for all panel UI and cap widget |
| vars | Record<string, string> | Direct CSS variable overrides (keys without --mb-adapt- prefix) |
The vars escape hatch lets you fine-tune any variable while keeping the rest of the theme intact:
<script setup>
const theme = {
mode: 'dark',
primary: '#818cf8',
background: '#0f172a',
surface: '#1e293b',
text: '#f1f5f9',
border: '#334155',
vars: {
'floating-radius': '16px',
'cap-border-radius': '24px',
'border-radius': '12px',
},
};
</script>
<template>
<AdaptAutomation automation-id="auto-123" :theme="theme" :style="{ height: '600px' }" />
</template>2. CSS custom properties
Set --mb-adapt-* variables on .mb-adapt in an unscoped <style> block or a global CSS file. In Vue SFCs, scoped styles cannot reach into the custom element's internals, so use one of these approaches:
Unscoped <style> block (in any component or App.vue):
<style>
.mb-adapt {
--mb-adapt-fork-bg: #ffffff;
--mb-adapt-fork-tab-bg: #f5f5f5;
--mb-adapt-fork-tab-active-bg: #ffffff;
--mb-adapt-fork-tab-color: #1a1a1a;
--mb-adapt-fork-tab-inactive-color: #888;
--mb-adapt-fork-separator: #e0e0e0;
}
/* Dark mode — active when :dark-mode="true" */
.mb-adapt--dark {
--mb-adapt-fork-bg: #1e1e1e;
--mb-adapt-fork-tab-bg: #2a2a2a;
--mb-adapt-fork-tab-active-bg: #1e1e1e;
--mb-adapt-fork-tab-color: #e0e0e0;
--mb-adapt-fork-tab-inactive-color: #777;
--mb-adapt-fork-separator: #3a3a3a;
}
</style>Global CSS import in main.ts:
// main.ts
import './adapt-theme.css';/* adapt-theme.css */
.mb-adapt {
--mb-adapt-fork-bg: #ffffff;
--mb-adapt-fork-separator: #e0e0e0;
}
.mb-adapt--dark {
--mb-adapt-fork-bg: #1e1e1e;
--mb-adapt-fork-separator: #3a3a3a;
}Typical mapping from your site's design system:
| Your site has | Maps to |
|---|---|
| Surface / card background | --mb-adapt-fork-bg |
| Secondary / muted background | --mb-adapt-fork-tab-bg |
| Primary text color | --mb-adapt-fork-tab-color |
| Muted / secondary text | --mb-adapt-fork-tab-inactive-color |
| Border / divider color | --mb-adapt-fork-separator |
| Accent color | --mb-adapt-separator-active (resize handle highlight) |
| Interactive hover tint | --mb-adapt-button-hover-bg |
3. Direct CSS on internal classes
For effects that go beyond variables (gradients, animations, pseudo-elements), target the component's internal class names directly. There is no Shadow DOM — all classes live in the light DOM.
Because Vue scoped styles hash selectors, internal classes like .mb-group-header will not match. Use an unscoped <style> block or :global() inside a scoped block:
Animated gradient toolbar:
<style>
/* Unscoped — reaches inside the custom element */
@keyframes mb-gradient-shift {
0% { background-position: 0% 50%; }
50% { background-position: 100% 50%; }
100% { background-position: 0% 50%; }
}
/* Light mode */
.mb-adapt .mb-group-header {
background: linear-gradient(135deg, #667eea, #764ba2, #f093fb, #667eea);
background-size: 300% 300%;
animation: mb-gradient-shift 8s ease infinite;
}
.mb-adapt .mb-group-header .mb-tab {
color: rgba(255, 255, 255, 0.7);
}
.mb-adapt .mb-group-header .mb-tab.mb-active {
color: #ffffff;
}
/* Dark mode */
.mb-adapt--dark .mb-group-header {
background: linear-gradient(135deg, #1e1b4b, #312e81, #4c1d95, #1e1b4b);
background-size: 300% 300%;
animation: mb-gradient-shift 8s ease infinite;
}
.mb-adapt--dark .mb-group-header .mb-tab {
color: rgba(199, 210, 254, 0.7);
}
.mb-adapt--dark .mb-group-header .mb-tab.mb-active {
color: #e0e7ff;
}
</style>Or with :global() inside a scoped block:
<style scoped>
:global(.mb-adapt .mb-group-header) {
background: linear-gradient(135deg, #667eea, #764ba2, #f093fb, #667eea);
background-size: 300% 300%;
animation: mb-gradient-shift 8s ease infinite;
}
</style>Key internal classes:
| Class | Element |
|---|---|
| .mb-adapt | Root container |
| .mb-adapt--dark | Root container in dark mode |
| .mb-group-header | Toolbar / tab bar |
| .mb-tab | Individual tab |
| .mb-tab.mb-active | Active tab |
| .mb-fork-panel | Panel content area |
| .mb-status-card | Status overlay card |
Note: Internal class names are not covered by semver. Pin your
@mochabug/adapt-vueversion if you rely on them.
Using the :class-names prop
Override internal element classes for deeper customization:
<AdaptAutomation
automation-id="auto-123"
:class-names="{
root: 'my-adapt-root',
iframe: 'my-adapt-iframe',
statusMessage: 'my-status-overlay',
statusCard: 'my-status-card',
}"
/>Using the :styles prop
Apply inline styles to the internal root element (useful for sizing):
<AdaptAutomation
automation-id="auto-123"
:styles="{ height: '600px', maxWidth: '1200px', margin: '0 auto' }"
/>Note:
:stylestargets the internal.mb-adaptroot. Use Vue's:stylefor styles on the outer<adapt-automation>host element.
General
| Variable | Light default | Dark default | Description |
|---|---|---|---|
| --mb-adapt-bg | transparent | | Root & group backgrounds |
| --mb-adapt-font | system-ui, -apple-system, sans-serif | | All panel UI text |
| --mb-adapt-button-hover-bg | rgba(128,128,128,0.2) | rgba(128,128,128,0.3) | Close/popout/action button hover |
| --mb-adapt-separator-active | rgba(59,130,246,0.5) | rgba(99,130,246,0.6) | Resize handle hover/active |
| --mb-adapt-border-radius | 8px | | Iframe border radius |
Toolbar and tabs
| Variable | Light default | Dark default | Description |
|---|---|---|---|
| --mb-adapt-fork-bg | #ffffff | #1e1e1e | Panel content background |
| --mb-adapt-fork-tab-bg | #f3f3f3 | #252526 | Toolbar / inactive tab bg |
| --mb-adapt-fork-tab-active-bg | #ffffff | #1e1e1e | Active tab background |
| --mb-adapt-fork-tab-color | rgb(51,51,51) | #ffffff | Active tab text |
| --mb-adapt-fork-tab-inactive-color | rgba(51,51,51,0.7) | #969696 | Inactive tab text |
| --mb-adapt-fork-separator | rgba(128,128,128,0.35) | rgb(68,68,68) | Tab/panel borders |
| --mb-adapt-tab-radius | 0 | | Tab border-radius (use 999px for pill shape) |
| --mb-adapt-tab-shadow | none | | Tab box-shadow |
| --mb-adapt-tab-active-shadow | none | | Active tab box-shadow |
| --mb-adapt-tab-gap | 0px | | Tab margin (spacing between tabs) |
| --mb-adapt-tab-padding | 0 14px | | Tab padding |
| --mb-adapt-tab-font-size | 13px | | Tab label font size |
| --mb-adapt-toolbar-height | 40px | | Toolbar / tab bar height |
| --mb-adapt-toolbar-padding | 0 | | Toolbar inner padding (standard CSS shorthand) |
| --mb-adapt-tab-min-width | 100px | | Tab minimum width |
| --mb-adapt-tab-spacing | 6px | | Gap between tab label and action buttons |
Floating panels (elevation)
| Variable | Light default | Dark default | Description |
|---|---|---|---|
| --mb-adapt-floating-shadow | 0 25px 50px -12px rgba(0,0,0,0.25), 0 12px 24px -8px rgba(0,0,0,0.15) | … rgba(0,0,0,0.5), … rgba(0,0,0,0.3) | Overlay box-shadow |
| --mb-adapt-floating-border | none | 1px solid rgba(255,255,255,0.06) | Overlay border |
| --mb-adapt-floating-backdrop | none | | Overlay backdrop-filter |
| --mb-adapt-floating-radius | 8px | | Overlay border-radius |
| --mb-adapt-status-card-shadow | 0 4px 24px rgba(0,0,0,0.08), 0 2px 8px rgba(0,0,0,0.04) | … rgba(0,0,0,0.25), … rgba(0,0,0,0.15) | Status card box-shadow |
| --mb-adapt-drag-ghost-shadow | 0 4px 12px rgba(0,0,0,0.15) | 0 4px 12px rgba(0,0,0,0.35) | Drag ghost box-shadow |
Drop targets
| Variable | Light default | Dark default |
|---|---|---|
| --mb-adapt-drop-header-bg | rgba(99,102,241,0.18) | rgba(129,140,248,0.22) |
| --mb-adapt-drop-center-bg | rgba(99,102,241,0.12) | rgba(129,140,248,0.15) |
| --mb-adapt-drop-split-bg | rgba(99,102,241,0.14) | rgba(129,140,248,0.18) |
| --mb-adapt-drop-border | rgba(99,102,241,0.55) | rgba(129,140,248,0.6) |
Status cards
| Variable | Light default | Dark default |
|---|---|---|
| --mb-adapt-status-card-bg | #ffffff | #1e293b |
| --mb-adapt-status-card-border | #e5e7eb | #334155 |
| --mb-adapt-status-icon-bg | #fef2f2 | #351c1c |
| --mb-adapt-status-text | #374151 | #e2e8f0 |
Cap widget
| Variable | Light default | Dark default |
|---|---|---|
| --mb-adapt-cap-background | #ffffff | #1e293b |
| --mb-adapt-cap-border-color | #e2e8f0 | #334155 |
| --mb-adapt-cap-border-radius | 16px | |
| --mb-adapt-cap-height | 72px | |
| --mb-adapt-cap-width | 380px | |
| --mb-adapt-cap-padding | 20px 28px | |
| --mb-adapt-cap-gap | 20px | |
| --mb-adapt-cap-color | #1e293b | #f1f5f9 |
| --mb-adapt-cap-checkbox-size | 36px | |
| --mb-adapt-cap-checkbox-border | 2px solid #cbd5e1 | 2px solid #475569 |
| --mb-adapt-cap-checkbox-radius | 10px | |
| --mb-adapt-cap-checkbox-background | #f8fafc | #0f172a |
| --mb-adapt-cap-spinner-color | #6366f1 | #818cf8 |
| --mb-adapt-cap-spinner-bg | #e2e8f0 | #334155 |
| --mb-adapt-cap-spinner-thickness | 3px | |
| --mb-adapt-cap-font | inherit | |
Z-index / stacking
| Variable | Default | Description |
|---|---|---|
| --mb-adapt-z-base | 0 | Base z-index offset — added to all internal z-index values |
Set --mb-adapt-z-base to shift all internal z-index values. Useful when embedding inside modals or drawers that have their own stacking context. Example: --mb-adapt-z-base: 10000 lifts all layers by 10000.
Internal stacking order from low to high: separators (1), resize handles (10), minimized tabs (100), floating panels (100000+), status/cap (200000), confirm dialog (300000), drop targets (999998), drag ghost (999999). All values are offset by --mb-adapt-z-base.
Props
| Prop | Type |
|------|------|
| automation-id | string (required) |
| session-token | string |
| auth-token | string |
| transmitter | string |
| signals | { [key: string]: SignalValue } |
| challenge-token | string |
| requires-challenge | boolean |
| cap-widget-options | { workerCount?: number; i18n?: CapWidgetI18n } |
| inherit-token | string |
| inherit-from | { hash: string } \| { param: string } |
| fork-display | { mode: 'side-by-side', split?: number } \| { mode: 'dialog' } |
| dark-mode | boolean |
| auto-resizing | boolean |
| allow-floating | boolean — hide pop-out buttons and block user-initiated floating (default true) |
| allow-docking | boolean — hide dock buttons and block user-initiated docking (default true) |
| allow-dialog-docking | boolean — allow tab splits inside floating dialog overlays (default true) |
| floating-auto-resize | boolean — floating overlays auto-resize from iframe content (default false) |
| confirm-on-close | boolean — show confirmation dialog before leaving page (default false) |
| persist | boolean \| PersistOptions |
| text | StatusText |
| theme | AdaptTheme |
| class-names | { root?: string; iframe?: string; statusMessage?: string; statusCard?: string } |
| styles | Partial<CSSStyleDeclaration> |
Events
| Event | Payload |
|-------|---------|
| session | (status, fork?) |
| output | (output) |
| fork-active | (active) |
License
ISC (c) mochabug AB
