quantumkv's picture
Hi Design Team,
20fda5d verified
class ClaudeHeader extends HTMLElement {
constructor() {
super();
}
connectedCallback() {
this.attachShadow({ mode: 'open' });
this.render();
this.setupEventListeners();
}
render() {
this.shadowRoot.innerHTML = `
<style>
.header {
background: rgba(255, 255, 255, 0.8);
backdrop-filter: blur(10px);
border-bottom: 1px solid #e2e8f0;
padding: 1rem 1.5rem;
position: sticky;
top: 0;
z-index: 40;
}
.header-content {
max-width: 1200px;
margin: 0 auto;
display: flex;
justify-content: space-between;
align-items: center;
}
.app-identity {
display: flex;
flex-direction: column;
gap: 0.25rem;
}
.app-name {
font-size: 1.5rem;
font-weight: 700;
color: #1e293b;
cursor: pointer;
transition: color 0.2s;
}
.app-name:hover {
color: var(--primary-blue);
}
.tagline {
font-size: 0.875rem;
color: #64748b;
}
.model-selector {
position: relative;
}
.model-button {
display: flex;
align-items: center;
gap: 0.5rem;
padding: 0.5rem 1rem;
background: white;
border: 1px solid #d1d5db;
border-radius: 0.75rem;
font-size: 0.875rem;
font-weight: 500;
color: #374151;
cursor: pointer;
transition: all 0.2s;
}
.model-button:hover {
border-color: var(--primary-blue);
background: #f8fafc;
}
.model-button:disabled {
opacity: 0.6;
cursor: not-allowed;
}
.dropdown {
position: absolute;
top: 100%;
right: 0;
margin-top: 0.5rem;
background: white;
border: 1px solid #e2e8f0;
border-radius: 0.75rem;
box-shadow: 0 10px 25px rgba(0, 0, 0, 0.1);
z-index: 50;
min-width: 200px;
display: none;
}
.dropdown.show {
display: block;
}
.dropdown-item {
padding: 0.75rem 1rem;
cursor: pointer;
transition: background 0.2s;
border-bottom: 1px solid #f1f5f9;
}
.dropdown-item:last-child {
border-bottom: none;
}
.dropdown-item:hover {
background: #f8fafc;
}
.dropdown-item.selected {
background: #eff6ff;
color: var(--primary-blue);
}
@media (max-width: 767px) {
.header {
padding: 0.75rem 1rem;
}
.app-name {
font-size: 1.25rem;
}
.tagline {
display: none;
}
}
</style>
<header class="header">
<div class="header-content">
<div class="app-identity">
<div class="app-name" id="appName">
ClaudeVerse AI
</div>
<div class="tagline">
Powered by Puter.js
</div>
</div>
<div class="model-selector">
<button class="model-button" id="modelButton">
<span id="currentModel">Claude Sonnet 4.5</span>
<i data-feather="chevron-down" width="16" height="16"></i>
</button>
<div class="dropdown" id="modelDropdown">
<div class="dropdown-item" data-model="claude-sonnet-4-5">Claude Sonnet 4.5</div>
<div class="dropdown-item" data-model="claude-sonnet-4">Claude Sonnet 4</div>
<div class="dropdown-item" data-model="claude-opus-4-1">Claude Opus 4.1</div>
<div class="dropdown-item" data-model="claude-opus-4">Claude Opus 4</div>
<div class="dropdown-item" data-model="claude-haiku-4-5">Claude Haiku 4.5</div>
</div>
</div>
</div>
</header>
`;
// Update feather icons
setTimeout(() => {
if (this.shadowRoot.querySelector('[data-feather]')) {
feather.replace();
}
}, 100);
}
setupEventListeners() {
const appName = this.shadowRoot.getElementById('appName');
const modelButton = this.shadowRoot.getElementById('modelButton');
const modelDropdown = this.shadowRoot.getElementById('modelDropdown');
const currentModel = this.shadowRoot.getElementById('currentModel');
// Reset conversation on app name click
appName.addEventListener('click', () => {
document.dispatchEvent(new CustomEvent('claude-new-chat'));
});
// Toggle dropdown
modelButton.addEventListener('click', () => {
if (window.claudeApp.isProcessing) return;
const isShowing = modelDropdown.classList.contains('show');
if (isShowing) {
modelDropdown.classList.remove('show');
} else {
modelDropdown.classList.add('show');
}
});
// Model selection
modelDropdown.addEventListener('click', (event) => {
const item = event.target.closest('.dropdown-item');
if (item) {
const model = item.dataset.model;
document.dispatchEvent(new CustomEvent('claude-change-model', {
detail: { model }
}));
// Update current model display
currentModel.textContent = this.getModelLabel(model);
// Close dropdown
modelDropdown.classList.remove('show');
// Show selection feedback
item.classList.add('selected');
setTimeout(() => item.classList.remove('selected'), 500);
}
});
// Close dropdown when clicking outside
document.addEventListener('click', (event) => {
if (!this.contains(event.target)) {
modelDropdown.classList.remove('show');
}
});
// Update model display when changed externally
document.addEventListener('claude-model-changed', (event) => {
const { model } = event.detail;
currentModel.textContent = this.getModelLabel(model);
});
}
getModelLabel(model) {
const modelLabels = {
'claude-sonnet-4-5': 'Claude Sonnet 4.5',
'claude-sonnet-4': 'Claude Sonnet 4',
'claude-opus-4-1': 'Claude Opus 4.1',
'claude-opus-4': 'Claude Opus 4',
'claude-haiku-4-5': 'Claude Haiku 4.5'
};
return modelLabels[model] || model;
}
}
customElements.define('claude-header', ClaudeHeader);