Skip to main content

Vanilla JavaScript Integration

This guide covers integrating Diosc without any framework - pure HTML, CSS, and JavaScript.

Quick Start

CDN (Simplest)

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>My App with AI</title>

<!-- Load Diosc -->
<script type="module" src="https://unpkg.com/@diosc-ai/assistant-kit@latest/dist/assistant-kit/assistant-kit.esm.js"></script>
</head>
<body>
<h1>My Application</h1>

<!-- AI Chat Component -->
<diosc-chat></diosc-chat>

<script type="module">
import { diosc } from 'https://unpkg.com/@diosc-ai/assistant-kit@latest/dist/assistant-kit/assistant-kit.esm.js';

// Configure
diosc('config', {
backendUrl: 'https://your-hub.example.com',
apiKey: 'your-api-key',
autoConnect: true
});
</script>
</body>
</html>

NPM with Bundler

If using a bundler like Webpack, Rollup, or Parcel:

npm install @diosc-ai/assistant-kit
import { diosc } from '@diosc-ai/assistant-kit';

diosc('config', {
backendUrl: 'https://your-hub.example.com',
apiKey: 'your-api-key'
});

Configuration

Basic Configuration

diosc('config', {
// Required
backendUrl: 'https://your-hub.example.com',
apiKey: 'your-api-key',

// Optional
autoConnect: true, // Connect immediately (default: true)
verbose: false, // Debug logging (default: false)
reconnect: true, // Auto-reconnect on disconnect (default: true)
reconnectDelay: 1000 // Delay between reconnect attempts (ms)
});

Authentication

diosc('auth', async () => {
// Get current user's token
const token = localStorage.getItem('access_token');

// Optionally refresh if expired
if (isTokenExpired(token)) {
const newToken = await refreshToken();
localStorage.setItem('access_token', newToken);
return {
headers: { 'Authorization': `Bearer ${newToken}` }
};
}

return {
headers: { 'Authorization': `Bearer ${token}` },
userId: parseJwt(token).sub
};
});

function isTokenExpired(token) {
if (!token) return true;
const payload = parseJwt(token);
return payload.exp * 1000 < Date.now();
}

function parseJwt(token) {
const base64 = token.split('.')[1];
return JSON.parse(atob(base64));
}

Event Handling

Listening to Events

// Connection events
diosc('on', 'connected', () => {
console.log('Connected to Diosc Hub');
document.getElementById('status').textContent = 'Connected';
});

diosc('on', 'disconnected', () => {
console.log('Disconnected');
document.getElementById('status').textContent = 'Disconnected';
});

diosc('on', 'reconnecting', (attempt) => {
console.log(`Reconnecting... attempt ${attempt}`);
});

// Message events
diosc('on', 'message', (msg) => {
console.log('AI:', msg.content);
appendMessage('assistant', msg.content);
});

diosc('on', 'streaming', (chunk) => {
// Handle streaming text
appendToLastMessage(chunk);
});

diosc('on', 'streaming_end', () => {
// Streaming complete
finalizeLastMessage();
});

// Tool events
diosc('on', 'tool_start', (tool) => {
console.log(`Executing: ${tool.name}`);
showToolIndicator(tool.name);
});

diosc('on', 'tool_end', (result) => {
console.log('Tool result:', result);
hideToolIndicator();
});

// Error handling
diosc('on', 'error', (error) => {
console.error('Error:', error);
showError(error.message);
});

// Approval workflow
diosc('on', 'approval_required', (request) => {
showApprovalDialog(request);
});

Removing Listeners

// Store the unsubscribe function
const unsubscribe = diosc('on', 'message', handleMessage);

// Later, remove the listener
unsubscribe();

Sending Messages

Programmatic Messages

// Send a message
diosc('send', 'Hello, AI!');

// Send with context
diosc('send', 'Help me with this order', {
context: {
orderId: '12345',
page: 'order-details'
}
});

Form Integration

<form id="chat-form">
<input type="text" id="message-input" placeholder="Type a message...">
<button type="submit">Send</button>
</form>

<script>
document.getElementById('chat-form').addEventListener('submit', (e) => {
e.preventDefault();
const input = document.getElementById('message-input');
const message = input.value.trim();

if (message) {
diosc('send', message);
input.value = '';
}
});
</script>

Page Context

Update context when the page changes:

// Update page context
function updatePageContext() {
diosc('pageContext', {
path: window.location.pathname,
search: window.location.search,
pageType: document.body.dataset.pageType,
pageData: getPageData()
});
}

// Call on page load
updatePageContext();

// Call on navigation (for SPAs)
window.addEventListener('popstate', updatePageContext);

// Or hook into your router
function navigateTo(path) {
history.pushState(null, '', path);
loadPage(path);
updatePageContext();
}

Custom Chat UI

Build your own chat interface:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Custom Chat</title>
<style>
.chat-container {
max-width: 600px;
margin: 0 auto;
height: 80vh;
display: flex;
flex-direction: column;
border: 1px solid #e0e0e0;
border-radius: 8px;
}

.messages {
flex: 1;
overflow-y: auto;
padding: 1rem;
}

.message {
margin-bottom: 1rem;
padding: 0.75rem 1rem;
border-radius: 8px;
max-width: 80%;
}

.message.user {
background: #0066cc;
color: white;
margin-left: auto;
}

.message.assistant {
background: #f0f0f0;
}

.message.loading {
opacity: 0.7;
}

.input-area {
display: flex;
padding: 1rem;
border-top: 1px solid #e0e0e0;
}

.input-area input {
flex: 1;
padding: 0.75rem;
border: 1px solid #e0e0e0;
border-radius: 4px;
margin-right: 0.5rem;
}

.input-area button {
padding: 0.75rem 1.5rem;
background: #0066cc;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}

.input-area button:disabled {
opacity: 0.5;
cursor: not-allowed;
}

.status {
padding: 0.5rem 1rem;
font-size: 0.875rem;
color: #666;
border-bottom: 1px solid #e0e0e0;
}

.status.connected { color: green; }
.status.disconnected { color: red; }
</style>
</head>
<body>
<div class="chat-container">
<div id="status" class="status disconnected">Connecting...</div>
<div id="messages" class="messages"></div>
<div class="input-area">
<input type="text" id="input" placeholder="Type a message..." disabled>
<button id="send-btn" disabled>Send</button>
</div>
</div>

<!-- Headless agent for connection management -->
<diosc-agent></diosc-agent>

<script type="module">
import { diosc } from 'https://unpkg.com/@diosc-ai/assistant-kit@latest/dist/assistant-kit/assistant-kit.esm.js';

// Elements
const messagesEl = document.getElementById('messages');
const inputEl = document.getElementById('input');
const sendBtn = document.getElementById('send-btn');
const statusEl = document.getElementById('status');

// State
let isLoading = false;
let currentStreamingEl = null;

// Configure
diosc('config', {
backendUrl: 'https://your-hub.example.com',
apiKey: 'your-api-key',
autoConnect: true
});

// Connection events
diosc('on', 'connected', () => {
statusEl.textContent = 'Connected';
statusEl.className = 'status connected';
inputEl.disabled = false;
sendBtn.disabled = false;
});

diosc('on', 'disconnected', () => {
statusEl.textContent = 'Disconnected';
statusEl.className = 'status disconnected';
inputEl.disabled = true;
sendBtn.disabled = true;
});

// Message events
diosc('on', 'message', (msg) => {
if (currentStreamingEl) {
currentStreamingEl.classList.remove('loading');
currentStreamingEl = null;
}
appendMessage('assistant', msg.content);
isLoading = false;
updateSendButton();
});

diosc('on', 'streaming', (chunk) => {
if (!currentStreamingEl) {
currentStreamingEl = appendMessage('assistant', '', true);
}
currentStreamingEl.textContent += chunk;
scrollToBottom();
});

diosc('on', 'streaming_end', () => {
if (currentStreamingEl) {
currentStreamingEl.classList.remove('loading');
currentStreamingEl = null;
}
isLoading = false;
updateSendButton();
});

diosc('on', 'error', (error) => {
appendMessage('assistant', `Error: ${error.message}`);
isLoading = false;
updateSendButton();
});

// Helper functions
function appendMessage(role, content, isStreaming = false) {
const msgEl = document.createElement('div');
msgEl.className = `message ${role}${isStreaming ? ' loading' : ''}`;
msgEl.textContent = content;
messagesEl.appendChild(msgEl);
scrollToBottom();
return msgEl;
}

function scrollToBottom() {
messagesEl.scrollTop = messagesEl.scrollHeight;
}

function updateSendButton() {
sendBtn.disabled = isLoading || !inputEl.value.trim();
}

function sendMessage() {
const content = inputEl.value.trim();
if (!content || isLoading) return;

appendMessage('user', content);
inputEl.value = '';
isLoading = true;
updateSendButton();

diosc('send', content);
}

// Event listeners
sendBtn.addEventListener('click', sendMessage);

inputEl.addEventListener('keypress', (e) => {
if (e.key === 'Enter') {
sendMessage();
}
});

inputEl.addEventListener('input', updateSendButton);
</script>
</body>
</html>

Multiple Chat Instances

While you can only have one diosc configuration, you can have multiple UI instances:

<!-- Main chat -->
<diosc-chat id="main-chat"></diosc-chat>

<!-- Embedded in a modal -->
<div id="modal" class="modal">
<diosc-chat id="modal-chat"></diosc-chat>
</div>

Both will share the same connection and session.

Styling

CSS Custom Properties

diosc-chat {
/* Colors */
--diosc-primary-color: #0066cc;
--diosc-primary-hover: #0052a3;
--diosc-background: #ffffff;
--diosc-surface: #f5f5f5;
--diosc-text-color: #333333;
--diosc-text-secondary: #666666;
--diosc-border-color: #e0e0e0;

/* Typography */
--diosc-font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
--diosc-font-size: 14px;
--diosc-line-height: 1.5;

/* Spacing */
--diosc-spacing-xs: 4px;
--diosc-spacing-sm: 8px;
--diosc-spacing-md: 16px;
--diosc-spacing-lg: 24px;

/* Layout */
--diosc-border-radius: 8px;
--diosc-chat-width: 380px;
--diosc-chat-height: 600px;
--diosc-chat-max-height: 80vh;

/* Shadows */
--diosc-shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.05);
--diosc-shadow-md: 0 4px 6px rgba(0, 0, 0, 0.1);
--diosc-shadow-lg: 0 10px 15px rgba(0, 0, 0, 0.15);
}

Dark Mode

@media (prefers-color-scheme: dark) {
diosc-chat {
--diosc-background: #1a1a1a;
--diosc-surface: #2d2d2d;
--diosc-text-color: #ffffff;
--diosc-text-secondary: #a0a0a0;
--diosc-border-color: #404040;
}
}

/* Or with a class */
.dark-mode diosc-chat {
--diosc-background: #1a1a1a;
/* ... */
}

Positioning

/* Floating bottom-right (default) */
diosc-chat {
position: fixed;
bottom: 20px;
right: 20px;
z-index: 9999;
}

/* Full-width embed */
diosc-chat.embedded {
position: relative;
width: 100%;
height: 100%;
--diosc-chat-width: 100%;
--diosc-chat-height: 100%;
}

Browser Compatibility

Diosc web components work in all modern browsers:

BrowserMinimum Version
Chrome67+
Firefox63+
Safari12+
Edge79+

For older browsers, you may need polyfills:

<script src="https://unpkg.com/@webcomponents/webcomponentsjs@2/webcomponents-loader.js"></script>

Troubleshooting

Component Not Rendering

Check if the script loaded:

if (typeof diosc === 'undefined') {
console.error('Diosc not loaded');
}

Check browser console for errors.

CORS Errors

Your Diosc Hub needs to allow requests from your domain:

Access-Control-Allow-Origin: https://your-domain.com

WebSocket Connection Failed

Check if WebSocket is supported and not blocked:

if (!window.WebSocket) {
console.error('WebSocket not supported');
}

Check firewall/proxy settings.

Events Not Firing

Ensure you're subscribing before the event occurs:

// Subscribe first
diosc('on', 'connected', () => { ... });

// Then configure (which triggers connect)
diosc('config', { ... });

Next Steps