CRITICAL: NOT STANDALONE
Web apps and components do NOT work outside Wippy. They run in iframes with host-injected configuration. Direct browser testing will fail - the
$Wglobal andgetWippyApi()only exist when loaded within the Wippy host application.
To integrate with Wippy, use the getWippyApi() promise or the $W global variable.
// Using getWippyApi()
const { config, host, api, on } = await getWippyApi()
// Or use the $W global for individual access
const config = await $W.config()
const host = await $W.host()
const api = await $W.api()
const on = await $W.on()
const instance = await $W.instance() // Full ProxyApiInstanceThe initialization returns the following components:
config- Application configuration (path, artifactId, auth, customization)host- Host application communication methodsapi- Authenticated axios instance with automatic auth token injectionon- Subscription to real-time events from WebSocket layerloadWebComponent- Function to dynamically load other web components
The host object provides methods to communicate with the Wippy host application.
host.startChat(start_token: string, options?: { sidebar?: boolean }): voidInitiates a new chat session using the provided token.
start_token- Token to start the chat sessionoptions.sidebar- Whentrue, opens chat in the right sidebar panel; whenfalse(default), opens in main content area
host.startChat('my-start-token') // Opens in main area
host.startChat('my-start-token', { sidebar: true }) // Opens in sidebarhost.openSession(sessionUUID: string, options?: { sidebar?: boolean }): voidNavigates to an existing chat session.
sessionUUID- UUID of the chat session to openoptions.sidebar- Whentrue, opens in sidebar; whenfalse(default), opens in main area
host.openSession('abc-123-uuid', { sidebar: false })host.openArtifact(artifactUUID: string, options?: { target: 'modal' | 'sidebar' }): voidOpens an artifact in a modal or sidebar.
artifactUUID- UUID of the artifact to openoptions.target- Where to display the artifact:'modal'or'sidebar'(default)
host.openArtifact('artifact-uuid', { target: 'modal' })host.setContext(
context: Record<string, unknown>,
sessionUUID?: string,
source?: { type: 'page' | 'artifact'; uuid: string; instanceUUID?: string }
): voidSets the context for a chat session. This context can include any arbitrary data that you want to pass along with the chat session.
Parameters:
context- Arbitrary data object to send as contextsessionUUID- Optional UUID of a specific session to attach context tosource- Optional identifier for where the context originated
Behavior:
- If
sessionUUIDis provided, sends context to that specific session - If
sessionUUIDis omitted, sends to the last session opened viastartChatoropenSession - If no session is open yet, context is queued and replayed when a session starts
- The
sourceparameter identifies which page/artifact sent the context (useful for tracking)
// Send to last opened session
host.setContext({ currentPage: 'dashboard', selectedItems: [1, 2, 3] })
// Send to specific session
host.setContext(
{ documentId: 'doc-123' },
'session-uuid-456',
{ type: 'page', uuid: 'page-uuid-789' }
)host.navigate(url: string): voidRequests navigation to a specific URL. Supported URL patterns:
/c/<page-id>- Navigates to a dynamic page/c/<page-id>/<sub-path>- Navigates to a dynamic page with sub-path/chat/<session-id>- Navigates to a chat session
host.navigate('/c/my-page-id')
host.navigate('/chat/session-uuid')host.onRouteChanged(path: string): voidNotifies the host application of internal route changes within the iframe. This updates the host browser URL to reflect the current iframe route.
Usage in Vue Router:
router.afterEach((to) => {
host.onRouteChanged(to.fullPath)
})host.confirm(options: LimitedConfirmationOptions): Promise<boolean>Shows a PrimeVue confirmation dialog. Returns a promise that resolves to true if accepted, false if rejected.
const result = await host.confirm({
message: 'Are you sure you want to delete this item?',
header: 'Confirm Delete',
icon: 'tabler:trash',
acceptLabel: 'Delete',
rejectLabel: 'Cancel',
acceptClass: 'p-button-danger',
})
if (result) {
// User confirmed
}host.toast(options: ToastMessageOptions): voidShows a PrimeVue toast notification.
host.toast({
severity: 'success',
summary: 'Success',
detail: 'Operation completed successfully',
life: 3000
})Common severities: 'success', 'info', 'warn', 'error'
host.handleError(code: 'auth-expired' | 'other', error: Record<string, unknown>): voidReports errors to the host application for handling.
'auth-expired'- Triggers re-authentication flow'other'- General error handling
try {
await api.get('/protected-endpoint')
} catch (error) {
if (error.response?.status === 401) {
host.handleError('auth-expired', error)
} else {
host.handleError('other', error)
}
}host.formatUrl(relativeUrl: string): stringConverts a relative URL to an absolute URL using the host's base URL. Useful when generating links that work correctly within the Wippy environment.
const absoluteUrl = host.formatUrl('/api/downloads/file.pdf')
// Returns: https://app.wippy.ai/api/downloads/file.pdfhost.logout(): voidLogs the user out and ends their session.
The api object is an axios instance pre-configured with:
- Base URL from environment
- Content-Type header
- Automatic token injection for authenticated requests
// GET request
const response = await api.get('/api/users')
// POST request with data
const result = await api.post('/api/users', {
name: 'John',
email: 'john@example.com'
})The on function subscribes to events from the host application and WebSocket layer.
Emitted when the host URL changes (SPA navigation).
on('@history', ({ path }) => {
console.log('Host URL changed:', path)
// Update internal router if needed
})Emitted when iframe visibility changes (tab switch, hide/show). The host watches a visibility ref and sends visibility update messages.
on('@visibility', (visible: boolean) => {
if (visible) {
console.log('Page is now visible')
// Resume animations, refresh data, restart polling
} else {
console.log('Page is now hidden')
// Pause animations, stop polling, reduce resource usage
}
})Subscribe to ALL WebSocket messages (wildcard). Internally subscribes to patterns: *, *:*, *:*:*, *:*:*:*
on('@message', (message) => {
console.log('Received WebSocket message:', message)
})Colon-separated parts with * wildcard matching:
session:*matchessession:uuid-1,session:uuid-2session:*:message:*matchessession:uuid:message:msgId- Pattern must have the same number of parts as the topic
// Listen to all session updates
on('session:*', (event) => {
console.log('Session update:', event)
})
// Listen to specific session messages
on('session:abc-123:message:*', (event) => {
console.log('Message in session abc-123:', event)
})
// If topic parts contain ":" characters, encode them
on('session:' + encodeURIComponent('id:with:colons') + ':message:*', (event) => {
console.log('Message event:', event)
})URLs to host-provided CSS (Vite ?url imports). Use for reducing bundle size by leveraging host styles:
import { hostCss } from '@wippy-fe/proxy'
hostCss.fontCssUrl // Google Fonts
hostCss.themeConfigUrl // Theme CSS variables
hostCss.primeVueCssUrl // PrimeVue styles
hostCss.markdownCssUrl // Markdown content styles
hostCss.iframeCssUrl // Iframe sandbox stylesloadCss(url: string): Promise<string>Fetches CSS content via authenticated API. Returns raw CSS string. Useful for embedding styles in Shadow DOM:
import { hostCss, loadCss } from '@wippy-fe/proxy'
const css = await loadCss(hostCss.primeVueCssUrl)
shadowRoot.innerHTML = `<style>${css}</style>` + shadowRoot.innerHTMLloadWebComponent(componentId: string, tagName?: string): Promise<void>Dynamically loads another web component by artifact UUID:
- Fetches artifact metadata (must be
application/json) - Validates it's a Wippy ESM module (package.json with
specification: "wippy-component-1.0") - Creates
<script type="module" src="...?declare-tag=tagName"> - Component auto-registers via
define()
// Load and register a component
await loadWebComponent('artifact-uuid-123', 'my-custom-chart')
// Now you can use <my-custom-chart> in your HTMLRegister custom Iconify icons from config:
import { addCollection } from '@iconify/vue'
import { addIcons } from '@wippy-fe/proxy'
addIcons(addCollection) // Adds wippy icons + custom icons from configFor TypeScript support, install the types package:
"devDependencies": {
"@wippy-fe/types-global-proxy": "^0.0.1"
}Derive types from the $W global:
export type HostApi = Awaited<ReturnType<typeof window.$W.host>>
export type ProxyApiInstance = Awaited<ReturnType<typeof window.$W.instance>>
export type WippyConfig = Awaited<ReturnType<typeof window.$W.config>>The following are deprecated and should not be used in new code:
iframeobject - 100% deprecated alias forhost. Usehostinstead.formobject - Existed for form state management. Use direct API calls instead.tailwindConfig- Only for runtime Tailwind CDN script (<script src="https://cdn.tailwindcss.com">), not for modern Vite builds with Tailwind as a dependency.
The config object contains application configuration:
interface AppConfig {
artifactId?: string // Current artifact ID (if applicable)
path?: string // Sub-path after page ID (e.g., /c/page-id/sub-path)
auth: AppAuthConfig // Authentication details
feature?: AppFeatures // App features (history mode, session type, etc.)
customization?: AppCustomization // Custom CSS, i18n, icons
}For dynamic pages, if the URL is /c/123/something/else?foo=1:
- Page ID:
123 config.path:/something/else?foo=1
Use Iconify with the tabler prefix:
<script src="https://code.iconify.design/iconify-icon/2.3.0/iconify-icon.min.js"></script>
<iconify-icon icon="tabler:user" width="24" height="24"></iconify-icon>Or with @iconify/vue:
<script setup>
import { Icon } from '@iconify/vue'
</script>
<template>
<Icon icon="tabler:user" width="24" height="24" />
</template>Common icons: tabler:user, tabler:alert-circle, tabler:circle-check, tabler:loader, tabler:calendar, tabler:settings, tabler:plus
Browse all icons at tabler-icons.io
The proxy exposes a native Web Component called <w-artifact> that lets you embed pages or artifacts directly inside any dynamic page.
<!-- Render an artifact by UUID -->
<w-artifact id="38fb..." type="artifact"></w-artifact>
<!-- Render a page with auto-resize -->
<w-artifact id="123e456..." type="page" auto-height></w-artifact>| Attribute | Required | Values | Default | Description |
|---|---|---|---|---|
id |
Yes | Artifact / Page UUID | – | Which content to load |
type |
No | artifact | page |
artifact |
Which REST endpoint to call |
auto-height |
No | (boolean flag) | false |
Listen for CmdBodySize events and adjust height |
url |
No | Any URL | – | Fetch content directly from URL, ignoring id/type |
sub-path |
No | Any path string | – | Overrides config.path for embedded content |
| Event name | When it fires | detail payload |
|---|---|---|
loading |
Before remote content starts fetching | – |
load |
After iframe is inserted successfully | – |
error |
When fetch fails | Original error object |
size |
When auto-height updates iframe size | { width: number, height: number } |
document.querySelector('w-artifact')?.addEventListener('error', (e) => {
console.error('Failed to load artifact:', e.detail)
})<w-artifact> exposes its state to CSS:
Host status attribute - always one of loading, ready, error:
w-artifact[status="loading"] { opacity: 0.5; }
w-artifact[status="error"] { border: 1px solid red; }Shadow parts - internal nodes are labelled:
| Part name | Node |
|---|---|
loader |
The loading placeholder |
error |
The error placeholder |
frame |
The underlying <iframe> |
w-artifact::part(loader) { font-size: 1rem; }
w-artifact::part(frame) { border: 0; }- Fetches raw HTML using the proxy's authenticated
apiinstance - When
urlis provided, that value is used directly; otherwise:type="page"→/api/public/pages/content/<id>type="artifact"→/api/artifact/<id>/content
- HTML is rendered inside a sandboxed iframe (
sandbox="allow-same-origin allow-scripts allow-forms allow-popups") - All iframe → host commands are bridged automatically
- If
sub-pathis supplied, its value is forwarded to the inner iframe asconfig.path - Markdown and inline-interactive artifacts are not supported - only plain iframe renders