|
2 | 2 |
|
3 | 3 | (function () {
|
4 | 4 |
|
| 5 | + class BreinifyDevConsole extends HTMLElement { |
| 6 | + constructor() { |
| 7 | + super(); |
| 8 | + |
| 9 | + this.attachShadow({mode: 'open'}); |
| 10 | + |
| 11 | + // SVG brein icon (16x16) |
| 12 | + this.isVisible = true; |
| 13 | + |
| 14 | + this.render(); |
| 15 | + this.hookConsole(); |
| 16 | + } |
| 17 | + |
| 18 | + render() { |
| 19 | + this.shadowRoot.innerHTML = ` |
| 20 | + <style> |
| 21 | + :host { all: initial; } |
| 22 | + div.title { display: flex; flex-flow: row; font-weight: bold; font-size: 14px; line-height: 14px; padding: 6px 10px; } |
| 23 | + button.close-btn { background: transparent; border: none; color: #ccc; font-size: 18px; cursor: pointer; padding: 0 6px; user-select: none; } |
| 24 | + button.close-btn:hover { color: white; } |
| 25 | + #panel { position: fixed; bottom: 0; right: 0; width: 400px; height: 80vh; max-height: 1000px; font-family: monospace; font-size: 12px; color: #fff; background: #1e1e1e; box-shadow: 0 0 10px rgba(0,0,0,0.5); border-top-left-radius: 6px; display: flex; flex-direction: column; z-index: 999999; transition: transform 0.2s ease-out, opacity 0.2s ease-out; overflow: hidden; } |
| 26 | + header { background: #111; padding: 6px 10px; display: flex; align-items: center; user-select: none; border-top-left-radius: 6px; color: #eee; } |
| 27 | + header > .tabs { display: flex; gap: 10px; flex-grow: 1; } |
| 28 | + header button.tab { background: transparent; border: none; color: #ccc; cursor: pointer; padding: 4px 8px; font-size: 12px; border-bottom: 2px solid transparent; transition: border-color 0.15s ease; } |
| 29 | + header button.tab.active { border-bottom-color: #fff; color: white; } |
| 30 | + header button.tab:hover:not(.active) { color: #fff; } |
| 31 | + #log-container { flex-grow: 1; background: #1e1e1e; padding: 10px; overflow-y: auto; white-space: pre-wrap; word-break: break-word; color: white; } |
| 32 | + #toggle-button { position: fixed; bottom: 10px; right: 10px; width: 32px; height: 32px; background: #333; border-radius: 50%; align-items: center; justify-content: center; cursor: pointer; z-index: 1000000; box-shadow: 0 0 5px rgba(0,0,0,0.3); transition: opacity 0.2s ease-out; display: none; } |
| 33 | + #toggle-button:hover svg path { fill: #ccc; } |
| 34 | + ::-webkit-scrollbar { width: 6px; } |
| 35 | + ::-webkit-scrollbar-thumb { background: #888; border-radius: 3px; } |
| 36 | + ::-webkit-scrollbar-thumb:hover { background: #555; } |
| 37 | + </style> |
| 38 | + <div id="panel"> |
| 39 | + <div class="title"> |
| 40 | + <div style="flex-grow: 1; align-content: center;">Breinify DevStudio</div> |
| 41 | + <button class="close-btn" title="Hide Breinify DevStudio">✕</button> |
| 42 | + </div> |
| 43 | + <header> |
| 44 | + <div class="tabs"> |
| 45 | + <button class="tab active" data-tab="console">Console</button> |
| 46 | + <button class="tab" data-tab="info">Info</button> |
| 47 | + </div> |
| 48 | + </header> |
| 49 | + <div id="log-container"></div> |
| 50 | + </div> |
| 51 | + <div id="toggle-button" title="Show Breinify DevStudio" role="button" tabindex="0"><svg xmlns="http://www.w3.org/2000/svg" fill="white" width="16" height="16" viewBox="0 0 24 24"><path d="M12 2C8.1 2 6 4.4 6 7v5c0 .5-.2.9-.5 1.3-.3.4-.5.9-.5 1.4v.3c.1.6.5 1.1 1 1.5.5.4.8 1 .8 1.6 0 .6.2 1.1.5 1.5s.7.7 1.2.9V21c0 .6.4 1 1 1s1-.4 1-1v-1h2v1c0 .6.4 1 1 1s1-.4 1-1v-1.5c.5-.2.9-.5 1.2-.9s.5-.9.5-1.5c0-.6.3-1.2.8-1.6.5-.4.9-.9 1-1.5v-.3c0-.5-.2-1-.5-1.4-.3-.4-.5-.9-.5-1.3V7c0-2.6-2.1-5-6-5z"/></svg></div>`; |
| 52 | + |
| 53 | + this.logContainer = this.shadowRoot.querySelector('#log-container'); |
| 54 | + this.toggleButton = this.shadowRoot.querySelector('#toggle-button'); |
| 55 | + this.panel = this.shadowRoot.querySelector('#panel'); |
| 56 | + this.closeBtn = this.shadowRoot.querySelector('button.close-btn'); |
| 57 | + this.tabs = this.shadowRoot.querySelectorAll('button.tab'); |
| 58 | + |
| 59 | + this.closeBtn.addEventListener('click', () => this.toggleConsole()); |
| 60 | + this.toggleButton.addEventListener('click', () => this.toggleConsole()); |
| 61 | + |
| 62 | + this.tabs.forEach(tab => tab.addEventListener('click', e => this.switchTab(e))); |
| 63 | + } |
| 64 | + |
| 65 | + toggleConsole() { |
| 66 | + this.isVisible = !this.isVisible; |
| 67 | + |
| 68 | + if (this.isVisible) { |
| 69 | + this.panel.style.transform = 'translateY(0)'; |
| 70 | + this.panel.style.opacity = '1'; |
| 71 | + this.toggleButton.style.display = 'none'; |
| 72 | + } else { |
| 73 | + this.panel.style.transform = 'translateY(100%)'; |
| 74 | + this.panel.style.opacity = '0'; |
| 75 | + this.toggleButton.style.display = 'flex'; |
| 76 | + } |
| 77 | + } |
| 78 | + |
| 79 | + switchTab(event) { |
| 80 | + const selectedTab = event.target.dataset.tab; |
| 81 | + this.tabs.forEach(tab => { |
| 82 | + tab.classList.toggle('active', tab.dataset.tab === selectedTab); |
| 83 | + }); |
| 84 | + |
| 85 | + // For now, just clear or keep logs on console tab, and show placeholder on info tab |
| 86 | + if (selectedTab === 'console') { |
| 87 | + this.logContainer.style.display = 'block'; |
| 88 | + if (this.infoDiv) this.infoDiv.style.display = 'none'; |
| 89 | + } else if (selectedTab === 'info') { |
| 90 | + |
| 91 | + // Lazy create info div |
| 92 | + if (!this.infoDiv) { |
| 93 | + this.infoDiv = document.createElement('div'); |
| 94 | + this.infoDiv.textContent = 'Information tab content goes here.'; |
| 95 | + this.infoDiv.style.padding = '10px'; |
| 96 | + this.infoDiv.style.color = '#ddd'; |
| 97 | + this.infoDiv.style.height = '100%'; |
| 98 | + this.infoDiv.style.overflowY = 'auto'; |
| 99 | + this.logContainer.after(this.infoDiv); |
| 100 | + } |
| 101 | + |
| 102 | + this.logContainer.style.display = 'none'; |
| 103 | + this.infoDiv.style.display = 'block'; |
| 104 | + } |
| 105 | + } |
| 106 | + |
| 107 | + hookConsole() { |
| 108 | + ['log', 'warn', 'error', 'info'].forEach(method => { |
| 109 | + const original = console[method]; |
| 110 | + console[method] = (...args) => { |
| 111 | + const msg = args.map(arg => { |
| 112 | + try { |
| 113 | + if (typeof arg === 'object' && arg !== null) { |
| 114 | + return JSON.stringify(arg, null, 2); |
| 115 | + } |
| 116 | + return String(arg); |
| 117 | + } catch { |
| 118 | + return '[object]'; |
| 119 | + } |
| 120 | + }).join(' '); |
| 121 | + |
| 122 | + const entry = document.createElement('div'); |
| 123 | + entry.textContent = `[${method.toUpperCase()}] ${msg}`; |
| 124 | + entry.style.color = { |
| 125 | + log: 'white', |
| 126 | + warn: 'orange', |
| 127 | + error: 'red', |
| 128 | + info: 'lightblue' |
| 129 | + }[method]; |
| 130 | + |
| 131 | + if (this.logContainer) { |
| 132 | + this.logContainer.appendChild(entry); |
| 133 | + this.logContainer.scrollTop = this.logContainer.scrollHeight; |
| 134 | + } |
| 135 | + |
| 136 | + original.apply(console, args); |
| 137 | + }; |
| 138 | + }); |
| 139 | + } |
| 140 | + } |
| 141 | + |
5 | 142 | const DevStudio = {
|
| 143 | + devStudio: null, |
6 | 144 |
|
7 | 145 | init: function () {
|
8 |
| - if (Breinify.UTL.internal.isDevMode() !== true) { |
| 146 | + // if (Breinify.UTL.internal.isDevMode() !== true) { |
| 147 | + // return; |
| 148 | + // } else |
| 149 | + if (this.devStudio !== null) { |
9 | 150 | return;
|
10 | 151 | }
|
11 | 152 |
|
12 |
| - console.log('DevStudio is being loaded'); |
| 153 | + const elementName = 'breinify-dev-console'; |
| 154 | + customElements.define(elementName, BreinifyDevConsole); |
| 155 | + |
| 156 | + this.devStudio = document.createElement(elementName); |
| 157 | + document.body.appendChild(this.devStudio); |
13 | 158 | }
|
14 | 159 | }
|
15 | 160 |
|
|
0 commit comments