Spaces:
Running
Running
| class AppHeader extends HTMLElement { | |
| constructor() { | |
| super(); | |
| this.attachShadow({ mode: 'open' }); | |
| } | |
| connectedCallback() { | |
| this.render(); | |
| this.setupEventListeners(); | |
| } | |
| render() { | |
| this.shadowRoot.innerHTML = ` | |
| <style> | |
| header { | |
| background: linear-gradient(135deg, #1e293b 0%, #0f172a 100%); | |
| color: var(--text-primary); | |
| padding: 1rem 0; | |
| box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3); | |
| border-bottom: 1px solid var(--border-color); | |
| } | |
| .header-container { | |
| max-width: 1200px; | |
| margin: 0 auto; | |
| padding: 0 1rem; | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| } | |
| .app-title { | |
| cursor: pointer; | |
| display: flex; | |
| align-items: center; | |
| gap: 0.5rem; | |
| transition: transform 0.2s; | |
| } | |
| .app-title:hover { | |
| transform: scale(1.02); | |
| } | |
| .app-title:active { | |
| transform: scale(0.98); | |
| } | |
| .app-name { | |
| font-size: 1.5rem; | |
| font-weight: 700; | |
| letter-spacing: -0.5px; | |
| } | |
| .app-subtitle { | |
| font-size: 0.75rem; | |
| opacity: 0.7; | |
| margin-top: 0.125rem; | |
| color: var(--text-secondary); | |
| } | |
| .model-selector { | |
| background: var(--bg-secondary); | |
| border: 1px solid var(--border-color); | |
| color: var(--text-primary); | |
| padding: 0.5rem 1rem; | |
| border-radius: 8px; | |
| font-size: 0.875rem; | |
| cursor: pointer; | |
| transition: all 0.2s; | |
| } | |
| .model-selector:hover { | |
| background: var(--bg-tertiary); | |
| border-color: var(--primary-color); | |
| } | |
| .model-selector:focus { | |
| outline: 2px solid var(--primary-color); | |
| outline-offset: 2px; | |
| } | |
| .model-selector:disabled { | |
| opacity: 0.5; | |
| cursor: not-allowed; | |
| } | |
| .model-selector option { | |
| background: var(--bg-secondary); | |
| color: var(--text-primary); | |
| border: 1px solid var(--border-color); | |
| } | |
| @media (max-width: 640px) { | |
| .header-container { | |
| flex-direction: column; | |
| gap: 1rem; | |
| } | |
| .app-name { | |
| font-size: 1.25rem; | |
| } | |
| .model-selector { | |
| width: 100%; | |
| } | |
| } | |
| </style> | |
| <header> | |
| <div class="header-container"> | |
| <div class="app-title" role="button" aria-label="New chat"> | |
| <div> | |
| <div class="app-name">Claude AI Chat</div> | |
| <div class="app-subtitle">Powered by Puter.js</div> | |
| </div> | |
| </div> | |
| <select class="model-selector" aria-label="Select AI model"> | |
| <option value="claude-sonnet-4-5">Claude Sonnet 4.5</option> | |
| <option value="claude-sonnet-4">Claude Sonnet 4</option> | |
| <option value="claude-opus-4-1">Claude Opus 4.1</option> | |
| <option value="claude-opus-4">Claude Opus 4</option> | |
| <option value="claude-haiku-4-5">Claude Haiku 4.5</option> | |
| </select> | |
| </div> | |
| </header> | |
| `; | |
| } | |
| setupEventListeners() { | |
| // App title click - new chat | |
| const appTitle = this.shadowRoot.querySelector('.app-title'); | |
| appTitle.addEventListener('click', () => { | |
| if (window.claudeApp) { | |
| window.claudeApp.newChat(); | |
| } | |
| }); | |
| // Model selector change | |
| const modelSelector = this.shadowRoot.querySelector('.model-selector'); | |
| // Set initial value | |
| if (window.claudeApp) { | |
| modelSelector.value = window.claudeApp.selectedModel; | |
| } | |
| modelSelector.addEventListener('change', (e) => { | |
| if (window.claudeApp) { | |
| window.claudeApp.changeModel(e.target.value); | |
| } | |
| }); | |
| // Update model selector when app state changes | |
| if (window.claudeApp) { | |
| const originalChangeModel = window.claudeApp.changeModel.bind(window.claudeApp); | |
| window.claudeApp.changeModel = (newModel) => { | |
| originalChangeModel(newModel); | |
| modelSelector.value = newModel; | |
| }; | |
| } | |
| } | |
| } | |
| customElements.define('app-header', AppHeader); |