Spaces:
Running
Running
| class CustomCodeSnippet extends HTMLElement { | |
| constructor() { | |
| super(); | |
| this._title = this.getAttribute('title') || 'Code Snippet'; | |
| this._language = this.getAttribute('language') || 'javascript'; | |
| this._code = this.innerHTML.trim(); | |
| } | |
| static get observedAttributes() { | |
| return ['title', 'language', 'code']; | |
| } | |
| attributeChangedCallback(name, oldValue, newValue) { | |
| if (oldValue !== newValue) { | |
| this[`_${name}`] = newValue; | |
| this.render(); | |
| } | |
| } | |
| connectedCallback() { | |
| this.render(); | |
| } | |
| render() { | |
| this.attachShadow({ mode: 'open' }); | |
| this.shadowRoot.innerHTML = ` | |
| <style> | |
| :host { | |
| display: block; | |
| margin-bottom: 1.5rem; | |
| } | |
| .snippet-container { | |
| border-radius: 0.75rem; | |
| overflow: hidden; | |
| box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1); | |
| } | |
| .dark .snippet-container { | |
| box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.25); | |
| } | |
| .snippet-header { | |
| display: flex; | |
| align-items: center; | |
| justify-content: space-between; | |
| padding: 0.75rem 1rem; | |
| background-color: #f1f5f9; | |
| border-bottom: 1px solid #e2e8f0; | |
| } | |
| .dark .snippet-header { | |
| background-color: #1e293b; | |
| border-bottom-color: #334155; | |
| } | |
| .snippet-title { | |
| font-weight: 600; | |
| color: #334155; | |
| font-size: 0.875rem; | |
| } | |
| .dark .snippet-title { | |
| color: #e2e8f0; | |
| } | |
| .snippet-actions { | |
| display: flex; | |
| gap: 0.5rem; | |
| } | |
| .snippet-btn { | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| width: 1.75rem; | |
| height: 1.75rem; | |
| border-radius: 0.375rem; | |
| color: #64748b; | |
| background-color: transparent; | |
| border: none; | |
| cursor: pointer; | |
| transition: all 0.2s; | |
| } | |
| .snippet-btn:hover { | |
| background-color: #e2e8f0; | |
| color: #475569; | |
| } | |
| .dark .snippet-btn:hover { | |
| background-color: #334155; | |
| color: #94a3b8; | |
| } | |
| .snippet-btn-icon { | |
| width: 1rem; | |
| height: 1rem; | |
| } | |
| .snippet-content { | |
| position: relative; | |
| background-color: #f8fafc; | |
| font-family: 'Fira Code', monospace; | |
| font-size: 0.875rem; | |
| line-height: 1.5; | |
| color: #334155; | |
| padding: 1.25rem; | |
| overflow-x: auto; | |
| } | |
| .dark .snippet-content { | |
| background-color: #0f172a; | |
| color: #e2e8f0; | |
| } | |
| .language-tag { | |
| position: absolute; | |
| top: 0; | |
| right: 1rem; | |
| transform: translateY(-50%); | |
| background-color: #3b82f6; | |
| color: white; | |
| font-size: 0.75rem; | |
| font-weight: 500; | |
| padding: 0.25rem 0.75rem; | |
| border-radius: 9999px; | |
| } | |
| .code-block { | |
| margin: 0; | |
| white-space: pre; | |
| tab-size: 2; | |
| } | |
| /* Syntax highlighting */ | |
| .token.comment, | |
| .token.prolog, | |
| .token.doctype, | |
| .token.cdata { | |
| color: #64748b; | |
| } | |
| .token.punctuation { | |
| color: #94a3b8; | |
| } | |
| .token.property, | |
| .token.tag, | |
| .token.boolean, | |
| .token.number, | |
| .token.constant, | |
| .token.symbol, | |
| .token.deleted { | |
| color: #2563eb; | |
| } | |
| .token.selector, | |
| .token.attr-name, | |
| .token.string, | |
| .token.char, | |
| .token.builtin, | |
| .token.inserted { | |
| color: #059669; | |
| } | |
| .token.operator, | |
| .token.entity, | |
| .token.url, | |
| .language-css .token.string, | |
| .style .token.string { | |
| color: #f59e0b; | |
| } | |
| .token.atrule, | |
| .token.attr-value, | |
| .token.keyword { | |
| color: #7c3aed; | |
| } | |
| .token.function, | |
| .token.class-name { | |
| color: #3b82f6; | |
| } | |
| .token.regex, | |
| .token.important, | |
| .token.variable { | |
| color: #f97316; | |
| } | |
| </style> | |
| <div class="snippet-container"> | |
| <div class="snippet-header"> | |
| <span class="snippet-title">${this._title}</span> | |
| <div class="snippet-actions"> | |
| <button class="snippet-btn" data-copy-btn title="Copy code"> | |
| <i data-feather="copy" class="snippet-btn-icon"></i> | |
| </button> | |
| <button class="snippet-btn" title="Expand code"> | |
| <i data-feather="maximize" class="snippet-btn-icon"></i> | |
| </button> | |
| </div> | |
| </div> | |
| <div class="snippet-content"> | |
| <span class="language-tag">${this._language}</span> | |
| <pre class="code-block"><code>${this._code}</code></pre> | |
| </div> | |
| </div> | |
| `; | |
| // Initialize feather icons within the shadow DOM | |
| const featherScript = document.createElement('script'); | |
| featherScript.src = 'https://cdn.jsdelivr.net/npm/feather-icons/dist/feather.min.js'; | |
| this.shadowRoot.appendChild(featherScript); | |
| featherScript.onload = () => { | |
| if (window.feather) { | |
| window.feather.replace({ class: 'snippet-btn-icon' }); | |
| } | |
| }; | |
| } | |
| } | |
| customElements.define('custom-code-snippet', CustomCodeSnippet); |