Vue Integration
This guide covers integrating Diosc into Vue 3 applications using the Composition API, including Nuxt.js specifics.
Basic Setup
Installation
npm install @diosc-ai/client
Configure Vue for Web Components
Tell Vue to ignore diosc-* and ai-* elements in vite.config.ts:
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
export default defineConfig({
plugins: [
vue({
template: {
compilerOptions: {
isCustomElement: (tag) => tag.startsWith('diosc-') || tag.startsWith('ai-')
}
}
})
]
});
For Vue CLI (vue.config.js):
module.exports = {
chainWebpack: config => {
config.module
.rule('vue')
.use('vue-loader')
.tap(options => ({
...options,
compilerOptions: {
isCustomElement: tag => tag.startsWith('diosc-') || tag.startsWith('ai-')
}
}));
}
};
Basic Component
<template>
<div class="app">
<h1>My Application</h1>
<diosc-chat />
</div>
</template>
<script setup lang="ts">
import { onMounted, onUnmounted } from 'vue';
import { loadDiosc } from '@diosc-ai/client';
let diosc;
onMounted(async () => {
diosc = await loadDiosc({
backendUrl: import.meta.env.VITE_DIOSC_URL,
apiKey: import.meta.env.VITE_DIOSC_API_KEY,
});
// Push auth to connect
diosc('auth', {
headers: { Authorization: `Bearer ${getToken()}` },
userId: getUserId(),
});
});
onUnmounted(() => {
diosc?.('auth', null);
});
</script>
Composables
useDiosc Composable
// composables/useDiosc.ts
import { ref, onMounted, onUnmounted } from 'vue';
import { loadDiosc, type DioscFunction } from '@diosc-ai/client';
export function useDiosc(backendUrl: string, apiKey: string) {
const isConnected = ref(false);
const isStreaming = ref(false);
let diosc: DioscFunction;
let unsubscribers: (() => void)[] = [];
onMounted(async () => {
diosc = await loadDiosc({ backendUrl, apiKey });
unsubscribers = [
diosc('on', 'authenticated', () => {
isConnected.value = true;
}),
diosc('on', 'disconnected', () => {
isConnected.value = false;
}),
diosc('on', 'stream:start', () => {
isStreaming.value = true;
}),
diosc('on', 'stream:end', () => {
isStreaming.value = false;
}),
];
});
onUnmounted(() => {
unsubscribers.forEach(unsub => unsub());
diosc?.('auth', null);
});
function sendMessage(content: string) {
diosc?.('invoke', content);
}
function startNewSession() {
diosc?.('startNewSession');
}
return {
isConnected,
isStreaming,
sendMessage,
startNewSession,
getDiosc: () => diosc,
};
}
useDioscAuth Composable
Integrate with your existing auth system using reactive auth:
// composables/useDioscAuth.ts
import { watch } from 'vue';
import type { DioscFunction } from '@diosc-ai/client';
import { useAuth } from './useAuth'; // Your auth composable
export function useDioscAuth(diosc: DioscFunction) {
const { accessToken, user } = useAuth();
// Push auth whenever token or user changes
watch(
[accessToken, user],
([token, currentUser]) => {
if (token && currentUser) {
diosc('auth', {
headers: { Authorization: `Bearer ${token}` },
userId: currentUser.id,
tenantId: currentUser.tenantId,
});
} else {
diosc('auth', null);
}
},
{ immediate: true }
);
}
usePageContext Composable
Update page context on route changes:
// composables/usePageContext.ts
import { watch, onMounted } from 'vue';
import { useRoute } from 'vue-router';
import type { DioscFunction } from '@diosc-ai/client';
export function usePageContext(diosc: DioscFunction) {
const route = useRoute();
onMounted(() => {
diosc('observe', 'navigation', (notify) => {
const stopWatch = watch(
() => route.fullPath,
() => {
notify({
path: route.path,
search: route.query ? '?' + new URLSearchParams(route.query as any).toString() : '',
hash: route.hash,
});
},
{ immediate: true }
);
return stopWatch; // Return cleanup function
});
});
}
Plugin Pattern
Create a Vue plugin for app-wide configuration:
// plugins/diosc.ts
import type { App, Plugin } from 'vue';
import { loadDiosc, type DioscFunction } from '@diosc-ai/client';
export interface DioscPluginOptions {
backendUrl: string;
apiKey: string;
}
export const DioscPlugin: Plugin = {
install(app: App, options: DioscPluginOptions) {
const loaded = loadDiosc(options);
// Provide the diosc function for composition API
loaded.then(diosc => {
app.config.globalProperties.$diosc = diosc;
app.provide('diosc', diosc);
});
}
};
Usage:
// main.ts
import { createApp } from 'vue';
import App from './App.vue';
import { DioscPlugin } from './plugins/diosc';
const app = createApp(App);
app.use(DioscPlugin, {
backendUrl: import.meta.env.VITE_DIOSC_URL,
apiKey: import.meta.env.VITE_DIOSC_API_KEY
});
app.mount('#app');
<!-- Any component -->
<script setup lang="ts">
import { inject } from 'vue';
import type { DioscFunction } from '@diosc-ai/client';
const diosc = inject<DioscFunction>('diosc')!;
function askAI() {
diosc('invoke', 'Help me with this page');
}
</script>
Nuxt.js Integration
Nuxt 3 Plugin
// plugins/diosc.client.ts
import { loadDiosc } from '@diosc-ai/client';
export default defineNuxtPlugin(async (nuxtApp) => {
const config = useRuntimeConfig();
const diosc = await loadDiosc({
backendUrl: config.public.dioscUrl as string,
apiKey: config.public.dioscApiKey as string,
});
return {
provide: {
diosc
}
};
});
// nuxt.config.ts
export default defineNuxtConfig({
runtimeConfig: {
public: {
dioscUrl: process.env.DIOSC_URL,
dioscApiKey: process.env.DIOSC_API_KEY
}
},
vue: {
compilerOptions: {
isCustomElement: (tag) => tag.startsWith('diosc-') || tag.startsWith('ai-')
}
}
});
Client-Only Rendering
Wrap components in <ClientOnly>:
<template>
<div>
<h1>My Nuxt App</h1>
<ClientOnly>
<diosc-chat />
</ClientOnly>
</div>
</template>
Composable with Nuxt
// composables/useDiosc.ts
export function useDiosc() {
const { $diosc } = useNuxtApp();
const isConnected = ref(false);
if (import.meta.client) {
$diosc('on', 'authenticated', () => {
isConnected.value = true;
});
}
return {
isConnected,
invoke: (msg: string) => $diosc('invoke', msg),
auth: (ctx: any) => $diosc('auth', ctx),
};
}
Event Handling
Global Events
<script setup lang="ts">
import { onMounted, onUnmounted } from 'vue';
import type { DioscFunction } from '@diosc-ai/client';
const diosc = inject<DioscFunction>('diosc')!;
const unsubscribers: (() => void)[] = [];
onMounted(() => {
unsubscribers.push(
diosc('on', 'stream:chunk', ({ content }) => {
console.log('Streaming:', content);
}),
diosc('on', 'tool:started', ({ toolName }) => {
console.log('Tool executing:', toolName);
}),
diosc('on', 'session:error', ({ code, message }) => {
console.error(`Error [${code}]:`, message);
}),
diosc('on', 'approval:request', ({ toolCalls }) => {
console.log('Approval needed for:', toolCalls.map(t => t.name));
}),
);
});
onUnmounted(() => {
unsubscribers.forEach(fn => fn());
});
</script>
Pinia Store (Optional)
For complex state management:
// stores/diosc.ts
import { defineStore } from 'pinia';
import { loadDiosc, type DioscFunction } from '@diosc-ai/client';
export const useDioscStore = defineStore('diosc', {
state: () => ({
isConnected: false,
isStreaming: false,
error: null as string | null,
diosc: null as DioscFunction | null,
}),
actions: {
async initialize(backendUrl: string, apiKey: string) {
this.diosc = await loadDiosc({ backendUrl, apiKey });
this.diosc('on', 'authenticated', () => {
this.isConnected = true;
});
this.diosc('on', 'disconnected', () => {
this.isConnected = false;
});
this.diosc('on', 'stream:start', () => {
this.isStreaming = true;
});
this.diosc('on', 'stream:end', () => {
this.isStreaming = false;
});
this.diosc('on', 'session:error', ({ message }) => {
this.error = message;
});
},
pushAuth(token: string, userId: string) {
this.diosc?.('auth', {
headers: { Authorization: `Bearer ${token}` },
userId,
});
},
logout() {
this.diosc?.('auth', null);
},
sendMessage(content: string) {
this.error = null;
this.diosc?.('invoke', content);
},
}
});
Troubleshooting
Unknown Custom Element Warning
Ensure Vue compiler options are set correctly:
// vite.config.ts
vue({
template: {
compilerOptions: {
isCustomElement: (tag) => tag.startsWith('diosc-') || tag.startsWith('ai-')
}
}
})
SSR Hydration Mismatch
Use <ClientOnly> in Nuxt or conditional rendering:
<template>
<diosc-chat v-if="mounted" />
</template>
<script setup>
import { ref, onMounted } from 'vue';
const mounted = ref(false);
onMounted(() => { mounted.value = true; });
</script>
Reactivity with Web Components
Web components don't automatically respond to Vue reactivity:
<!-- This won't update reactively -->
<diosc-chat :backend-url="url" />
<!-- Use diosc() API instead -->
<script setup>
watch(url, (newUrl) => {
diosc('config', { backendUrl: newUrl });
});
</script>
Next Steps
- Angular Integration - Angular-specific patterns
- Common Integration Patterns - Framework-agnostic patterns
- Client SDK Reference - Complete API reference