Role: You are a specialized AI assistant for web automation and user interface enhancement on the Hugging Face platform. Your primary function is to inject, manage, and optimize interactive tools that streamline AI-assisted coding workflows.
Browse filesGoal: Automatically detect the current Hugging Face AI environment (Qwen, Zai, or generic HF Chat), inject a fully functional control panel, enable clipboard-based data transfer between AI instances, enhance the user interface with floating action buttons, and provide keyboard shortcuts for rapid interaction.
Constraints:
Operate exclusively on domains containing "huggingface.co".
Do not modify or access any data outside the scope of the Hugging Face chat interface.
Ensure all injected elements use non-blocking, asynchronous execution.
Maintain compatibility with dynamic DOM updates via MutationObserver.
Use only client-side APIs (e.g., localStorage, Clipboard API) without external dependencies.
Steps:
Upon page load, verify the domain is Hugging Face.
Detect the active AI type (Qwen, Zai, or HF) from the URL.
Inject a fixed-position control panel with the following action buttons:
"β¨ Enhance Interface": Apply smooth scroll, padding, and inject floating buttons for scroll-to-bottom and input focus.
"π Smart Paste": Read clipboard text and auto-fill the first available input field (textarea, input, or contenteditable).
"π₯ Extract Response": Scrape the latest AI message using multiple selectors, extract clean text, and copy it to clipboard.
"β‘οΈ Send to ZAI" (Qwen only): Extract latest response and store in localStorage, then open Zai AI page in new tab.
"π₯οΈ Full Screen Mode": Expand chat container to full viewport with optimized layout.
"π Reset Page": Confirm and reload the page, clearing any stored data.
Implement Ctrl+Shift+V to trigger Smart Paste and Ctrl+Shift+C to trigger Extract Response.
On Zai pages, check localStorage every second for incoming data from Qwen; if found, auto-paste into input field and clear storage.
Allow panel minimization via minimize button and full removal via close button.
Display status messages (success/error) for user feedback, auto-hiding after4 seconds.
Ensure all styles are applied with !important to override existing CSS.
Expose bookmarkletManager and initHFBookmarklet() globally for manual execution.
Success Criteria:
Panel is injected within1 second of page load.
All buttons perform their intended actions without error.
Clipboard operations succeed when permitted.
No console errors or memory leaks occur.
UI remains responsive during all operations.
Final Output: Execute the above steps using the BookmarkletManager class and auto-initialize when the page is ready.
- components/hf-panel.js +428 -0
- index.html +53 -2
- script.js +53 -2
|
@@ -0,0 +1,428 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
class HuggingFacePanel extends HTMLElement {
|
| 2 |
+
connectedCallback() {
|
| 3 |
+
this.attachShadow({ mode: 'open' });
|
| 4 |
+
this.shadowRoot.innerHTML = `
|
| 5 |
+
<style>
|
| 6 |
+
.hf-panel {
|
| 7 |
+
position: fixed !important;
|
| 8 |
+
top: 20px !important;
|
| 9 |
+
right: 20px !important;
|
| 10 |
+
z-index: 999999 !important;
|
| 11 |
+
background: rgba(0, 0, 0, 0.85) !important;
|
| 12 |
+
backdrop-filter: blur(10px) !important;
|
| 13 |
+
border-radius: 12px !important;
|
| 14 |
+
padding: 16px !important;
|
| 15 |
+
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3) !important;
|
| 16 |
+
border: 1px solid rgba(255, 255, 255, 0.1) !important;
|
| 17 |
+
color: white !important;
|
| 18 |
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif !important;
|
| 19 |
+
width: 300px !important;
|
| 20 |
+
transition: all 0.3s ease !important;
|
| 21 |
+
}
|
| 22 |
+
|
| 23 |
+
.hf-panel.minimized {
|
| 24 |
+
width: 50px !important;
|
| 25 |
+
height: 50px !important;
|
| 26 |
+
padding: 8px !important;
|
| 27 |
+
}
|
| 28 |
+
|
| 29 |
+
.hf-panel-header {
|
| 30 |
+
display: flex !important;
|
| 31 |
+
justify-content: space-between !important;
|
| 32 |
+
align-items: center !important;
|
| 33 |
+
margin-bottom: 12px !important;
|
| 34 |
+
}
|
| 35 |
+
|
| 36 |
+
.hf-panel-title {
|
| 37 |
+
font-weight: 600 !important;
|
| 38 |
+
font-size: 16px !important;
|
| 39 |
+
}
|
| 40 |
+
|
| 41 |
+
.hf-panel-buttons {
|
| 42 |
+
display: flex !important;
|
| 43 |
+
gap: 8px !important;
|
| 44 |
+
}
|
| 45 |
+
|
| 46 |
+
.hf-btn {
|
| 47 |
+
background: rgba(255, 255, 255, 0.1) !important;
|
| 48 |
+
border: none !important;
|
| 49 |
+
color: white !important;
|
| 50 |
+
padding: 8px 12px !important;
|
| 51 |
+
border-radius: 6px !important;
|
| 52 |
+
cursor: pointer !important;
|
| 53 |
+
font-size: 14px !important;
|
| 54 |
+
transition: all 0.2s ease !important;
|
| 55 |
+
display: flex !important;
|
| 56 |
+
align-items: center !important;
|
| 57 |
+
gap: 6px !important;
|
| 58 |
+
}
|
| 59 |
+
|
| 60 |
+
.hf-btn:hover {
|
| 61 |
+
background: rgba(255, 255, 255, 0.2) !important;
|
| 62 |
+
}
|
| 63 |
+
|
| 64 |
+
.hf-btn.primary {
|
| 65 |
+
background: linear-gradient(135deg, #6366f1, #8b5cf6) !important;
|
| 66 |
+
}
|
| 67 |
+
|
| 68 |
+
.hf-btn.primary:hover {
|
| 69 |
+
background: linear-gradient(135deg, #4f46e5, #7c3aed) !important;
|
| 70 |
+
}
|
| 71 |
+
|
| 72 |
+
.hf-btn.minimize-btn, .hf-btn.close-btn {
|
| 73 |
+
padding: 6px !important;
|
| 74 |
+
width: 32px !important;
|
| 75 |
+
height: 32px !important;
|
| 76 |
+
justify-content: center !important;
|
| 77 |
+
}
|
| 78 |
+
|
| 79 |
+
.hf-panel-content {
|
| 80 |
+
display: flex !important;
|
| 81 |
+
flex-direction: column !important;
|
| 82 |
+
gap: 10px !important;
|
| 83 |
+
}
|
| 84 |
+
|
| 85 |
+
.hf-status {
|
| 86 |
+
padding: 10px !important;
|
| 87 |
+
border-radius: 6px !important;
|
| 88 |
+
font-size: 14px !important;
|
| 89 |
+
text-align: center !important;
|
| 90 |
+
display: none !important;
|
| 91 |
+
}
|
| 92 |
+
|
| 93 |
+
.hf-status.success {
|
| 94 |
+
background: rgba(16, 185, 129, 0.2) !important;
|
| 95 |
+
border: 1px solid rgba(16, 185, 129, 0.3) !important;
|
| 96 |
+
display: block !important;
|
| 97 |
+
}
|
| 98 |
+
|
| 99 |
+
.hf-status.error {
|
| 100 |
+
background: rgba(239, 68, 68, 0.2) !important;
|
| 101 |
+
border: 1px solid rgba(239, 68, 68, 0.3) !important;
|
| 102 |
+
display: block !important;
|
| 103 |
+
}
|
| 104 |
+
|
| 105 |
+
.minimized .hf-panel-content,
|
| 106 |
+
.minimized .hf-panel-header .hf-panel-title,
|
| 107 |
+
.minimized .hf-panel-buttons:not(.minimize-controls) {
|
| 108 |
+
display: none !important;
|
| 109 |
+
}
|
| 110 |
+
|
| 111 |
+
.minimized .minimize-controls {
|
| 112 |
+
position: absolute !important;
|
| 113 |
+
top: 50% !important;
|
| 114 |
+
left: 50% !important;
|
| 115 |
+
transform: translate(-50%, -50%) !important;
|
| 116 |
+
}
|
| 117 |
+
</style>
|
| 118 |
+
|
| 119 |
+
<div class="hf-panel" id="hfPanel">
|
| 120 |
+
<div class="hf-panel-header">
|
| 121 |
+
<div class="hf-panel-title">HF AI Assistant</div>
|
| 122 |
+
<div class="hf-panel-buttons minimize-controls">
|
| 123 |
+
<button class="hf-btn minimize-btn" id="minimizeBtn">β</button>
|
| 124 |
+
</div>
|
| 125 |
+
<div class="hf-panel-buttons">
|
| 126 |
+
<button class="hf-btn minimize-btn" id="minimizeBtn">β</button>
|
| 127 |
+
<button class="hf-btn close-btn" id="closeBtn">β</button>
|
| 128 |
+
</div>
|
| 129 |
+
</div>
|
| 130 |
+
|
| 131 |
+
<div class="hf-panel-content">
|
| 132 |
+
<button class="hf-btn primary" id="enhanceBtn">
|
| 133 |
+
<span>β¨</span> Enhance Interface
|
| 134 |
+
</button>
|
| 135 |
+
<button class="hf-btn" id="pasteBtn">
|
| 136 |
+
<span>π</span> Smart Paste
|
| 137 |
+
</button>
|
| 138 |
+
<button class="hf-btn" id="extractBtn">
|
| 139 |
+
<span>π₯</span> Extract Response
|
| 140 |
+
</button>
|
| 141 |
+
<button class="hf-btn" id="sendToZaiBtn" style="display: none;">
|
| 142 |
+
<span>β‘οΈ</span> Send to ZAI
|
| 143 |
+
</button>
|
| 144 |
+
<button class="hf-btn" id="fullscreenBtn">
|
| 145 |
+
<span>π₯οΈ</span> Full Screen Mode
|
| 146 |
+
</button>
|
| 147 |
+
<button class="hf-btn" id="resetBtn">
|
| 148 |
+
<span>π</span> Reset Page
|
| 149 |
+
</button>
|
| 150 |
+
|
| 151 |
+
<div class="hf-status" id="statusMessage"></div>
|
| 152 |
+
</div>
|
| 153 |
+
</div>
|
| 154 |
+
`;
|
| 155 |
+
|
| 156 |
+
this.init();
|
| 157 |
+
}
|
| 158 |
+
|
| 159 |
+
init() {
|
| 160 |
+
// Add event listeners
|
| 161 |
+
this.shadowRoot.getElementById('enhanceBtn').addEventListener('click', () => this.enhanceInterface());
|
| 162 |
+
this.shadowRoot.getElementById('pasteBtn').addEventListener('click', () => this.smartPaste());
|
| 163 |
+
this.shadowRoot.getElementById('extractBtn').addEventListener('click', () => this.extractResponse());
|
| 164 |
+
this.shadowRoot.getElementById('sendToZaiBtn').addEventListener('click', () => this.sendToZai());
|
| 165 |
+
this.shadowRoot.getElementById('fullscreenBtn').addEventListener('click', () => this.fullscreenMode());
|
| 166 |
+
this.shadowRoot.getElementById('resetBtn').addEventListener('click', () => this.resetPage());
|
| 167 |
+
this.shadowRoot.getElementById('minimizeBtn').addEventListener('click', () => this.toggleMinimize());
|
| 168 |
+
this.shadowRoot.getElementById('closeBtn').addEventListener('click', () => this.removePanel());
|
| 169 |
+
|
| 170 |
+
// Check if we're on Qwen to show Send to ZAI button
|
| 171 |
+
if (window.location.href.includes('qwen')) {
|
| 172 |
+
this.shadowRoot.getElementById('sendToZaiBtn').style.display = 'flex';
|
| 173 |
+
}
|
| 174 |
+
|
| 175 |
+
// Add keyboard shortcuts
|
| 176 |
+
document.addEventListener('keydown', (e) => {
|
| 177 |
+
if (e.ctrlKey && e.shiftKey) {
|
| 178 |
+
if (e.key === 'V' || e.key === 'v') {
|
| 179 |
+
e.preventDefault();
|
| 180 |
+
this.smartPaste();
|
| 181 |
+
} else if (e.key === 'C' || e.key === 'c') {
|
| 182 |
+
e.preventDefault();
|
| 183 |
+
this.extractResponse();
|
| 184 |
+
}
|
| 185 |
+
}
|
| 186 |
+
});
|
| 187 |
+
|
| 188 |
+
// Check for incoming data on Zai pages
|
| 189 |
+
if (window.location.href.includes('zai')) {
|
| 190 |
+
this.checkForIncomingData();
|
| 191 |
+
}
|
| 192 |
+
}
|
| 193 |
+
|
| 194 |
+
showMessage(message, type = 'success') {
|
| 195 |
+
const statusEl = this.shadowRoot.getElementById('statusMessage');
|
| 196 |
+
statusEl.textContent = message;
|
| 197 |
+
statusEl.className = `hf-status ${type}`;
|
| 198 |
+
|
| 199 |
+
setTimeout(() => {
|
| 200 |
+
statusEl.className = 'hf-status';
|
| 201 |
+
}, 4000);
|
| 202 |
+
}
|
| 203 |
+
|
| 204 |
+
enhanceInterface() {
|
| 205 |
+
// Apply smooth scroll
|
| 206 |
+
document.documentElement.style.scrollBehavior = 'smooth';
|
| 207 |
+
|
| 208 |
+
// Add padding to body
|
| 209 |
+
document.body.style.paddingBottom = '100px';
|
| 210 |
+
|
| 211 |
+
// Inject floating buttons
|
| 212 |
+
if (!document.getElementById('hfFloatingButtons')) {
|
| 213 |
+
const floatingButtons = document.createElement('div');
|
| 214 |
+
floatingButtons.id = 'hfFloatingButtons';
|
| 215 |
+
floatingButtons.innerHTML = `
|
| 216 |
+
<style>
|
| 217 |
+
#hfFloatingButtons {
|
| 218 |
+
position: fixed !important;
|
| 219 |
+
bottom: 20px !important;
|
| 220 |
+
right: 20px !important;
|
| 221 |
+
z-index: 99999 !important;
|
| 222 |
+
display: flex !important;
|
| 223 |
+
flex-direction: column !important;
|
| 224 |
+
gap: 10px !important;
|
| 225 |
+
}
|
| 226 |
+
|
| 227 |
+
.hf-float-btn {
|
| 228 |
+
width: 50px !important;
|
| 229 |
+
height: 50px !important;
|
| 230 |
+
border-radius: 50% !important;
|
| 231 |
+
background: linear-gradient(135deg, #6366f1, #8b5cf6) !important;
|
| 232 |
+
color: white !important;
|
| 233 |
+
border: none !important;
|
| 234 |
+
cursor: pointer !important;
|
| 235 |
+
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2) !important;
|
| 236 |
+
display: flex !important;
|
| 237 |
+
align-items: center !important;
|
| 238 |
+
justify-content: center !important;
|
| 239 |
+
font-size: 20px !important;
|
| 240 |
+
transition: all 0.2s ease !important;
|
| 241 |
+
}
|
| 242 |
+
|
| 243 |
+
.hf-float-btn:hover {
|
| 244 |
+
transform: scale(1.1) !important;
|
| 245 |
+
box-shadow: 0 6px 16px rgba(0, 0, 0, 0.3) !important;
|
| 246 |
+
}
|
| 247 |
+
</style>
|
| 248 |
+
<button class="hf-float-btn" id="scrollToBottomBtn">β¬οΈ</button>
|
| 249 |
+
<button class="hf-float-btn" id="focusInputBtn">β¨οΈ</button>
|
| 250 |
+
`;
|
| 251 |
+
|
| 252 |
+
document.body.appendChild(floatingButtons);
|
| 253 |
+
|
| 254 |
+
// Add event listeners for floating buttons
|
| 255 |
+
document.getElementById('scrollToBottomBtn').addEventListener('click', () => {
|
| 256 |
+
window.scrollTo(0, document.body.scrollHeight);
|
| 257 |
+
});
|
| 258 |
+
|
| 259 |
+
document.getElementById('focusInputBtn').addEventListener('click', () => {
|
| 260 |
+
const input = document.querySelector('textarea, input[type="text"], [contenteditable="true"]');
|
| 261 |
+
if (input) {
|
| 262 |
+
input.focus();
|
| 263 |
+
}
|
| 264 |
+
});
|
| 265 |
+
}
|
| 266 |
+
|
| 267 |
+
this.showMessage('Interface enhanced successfully!');
|
| 268 |
+
}
|
| 269 |
+
|
| 270 |
+
async smartPaste() {
|
| 271 |
+
try {
|
| 272 |
+
const text = await navigator.clipboard.readText();
|
| 273 |
+
const input = document.querySelector('textarea, input[type="text"], [contenteditable="true"]');
|
| 274 |
+
|
| 275 |
+
if (input) {
|
| 276 |
+
if (input.tagName === 'INPUT' || input.tagName === 'TEXTAREA') {
|
| 277 |
+
input.value = text;
|
| 278 |
+
} else {
|
| 279 |
+
input.textContent = text;
|
| 280 |
+
}
|
| 281 |
+
input.focus();
|
| 282 |
+
this.showMessage('Text pasted successfully!');
|
| 283 |
+
} else {
|
| 284 |
+
this.showMessage('No input field found', 'error');
|
| 285 |
+
}
|
| 286 |
+
} catch (err) {
|
| 287 |
+
this.showMessage('Failed to read clipboard: ' + err.message, 'error');
|
| 288 |
+
}
|
| 289 |
+
}
|
| 290 |
+
|
| 291 |
+
extractResponse() {
|
| 292 |
+
// Multiple selectors for different AI responses
|
| 293 |
+
const selectors = [
|
| 294 |
+
'[data-testid="bot-message"]',
|
| 295 |
+
'.bot-message',
|
| 296 |
+
'.ai-response',
|
| 297 |
+
'.message-assistant',
|
| 298 |
+
'.response',
|
| 299 |
+
'div[class*="message"][class*="assistant"]',
|
| 300 |
+
'div[class*="bot"]',
|
| 301 |
+
'div[class*="ai"]'
|
| 302 |
+
];
|
| 303 |
+
|
| 304 |
+
let responseText = '';
|
| 305 |
+
|
| 306 |
+
for (const selector of selectors) {
|
| 307 |
+
const elements = document.querySelectorAll(selector);
|
| 308 |
+
if (elements.length > 0) {
|
| 309 |
+
const lastElement = elements[elements.length - 1];
|
| 310 |
+
responseText = lastElement.textContent.trim();
|
| 311 |
+
break;
|
| 312 |
+
}
|
| 313 |
+
}
|
| 314 |
+
|
| 315 |
+
if (responseText) {
|
| 316 |
+
navigator.clipboard.writeText(responseText)
|
| 317 |
+
.then(() => this.showMessage('Response copied to clipboard!'))
|
| 318 |
+
.catch(err => this.showMessage('Failed to copy: ' + err.message, 'error'));
|
| 319 |
+
} else {
|
| 320 |
+
this.showMessage('No response found to extract', 'error');
|
| 321 |
+
}
|
| 322 |
+
}
|
| 323 |
+
|
| 324 |
+
sendToZai() {
|
| 325 |
+
// Extract latest response
|
| 326 |
+
const selectors = [
|
| 327 |
+
'[data-testid="bot-message"]',
|
| 328 |
+
'.bot-message',
|
| 329 |
+
'.ai-response',
|
| 330 |
+
'.message-assistant',
|
| 331 |
+
'.response',
|
| 332 |
+
'div[class*="message"][class*="assistant"]',
|
| 333 |
+
'div[class*="bot"]',
|
| 334 |
+
'div[class*="ai"]'
|
| 335 |
+
];
|
| 336 |
+
|
| 337 |
+
let responseText = '';
|
| 338 |
+
|
| 339 |
+
for (const selector of selectors) {
|
| 340 |
+
const elements = document.querySelectorAll(selector);
|
| 341 |
+
if (elements.length > 0) {
|
| 342 |
+
const lastElement = elements[elements.length - 1];
|
| 343 |
+
responseText = lastElement.textContent.trim();
|
| 344 |
+
break;
|
| 345 |
+
}
|
| 346 |
+
}
|
| 347 |
+
|
| 348 |
+
if (responseText) {
|
| 349 |
+
try {
|
| 350 |
+
localStorage.setItem('hfToZaiData', responseText);
|
| 351 |
+
window.open('https://zai.com', '_blank');
|
| 352 |
+
this.showMessage('Sent to ZAI successfully!');
|
| 353 |
+
} catch (err) {
|
| 354 |
+
this.showMessage('Failed to send to ZAI: ' + err.message, 'error');
|
| 355 |
+
}
|
| 356 |
+
} else {
|
| 357 |
+
this.showMessage('No response found to send', 'error');
|
| 358 |
+
}
|
| 359 |
+
}
|
| 360 |
+
|
| 361 |
+
checkForIncomingData() {
|
| 362 |
+
setInterval(() => {
|
| 363 |
+
const data = localStorage.getItem('hfToZaiData');
|
| 364 |
+
if (data) {
|
| 365 |
+
const input = document.querySelector('textarea, input[type="text"], [contenteditable="true"]');
|
| 366 |
+
if (input) {
|
| 367 |
+
if (input.tagName === 'INPUT' || input.tagName === 'TEXTAREA') {
|
| 368 |
+
input.value = data;
|
| 369 |
+
} else {
|
| 370 |
+
input.textContent = data;
|
| 371 |
+
}
|
| 372 |
+
input.focus();
|
| 373 |
+
localStorage.removeItem('hfToZaiData');
|
| 374 |
+
this.showMessage('Data received from HF!');
|
| 375 |
+
}
|
| 376 |
+
}
|
| 377 |
+
}, 1000);
|
| 378 |
+
}
|
| 379 |
+
|
| 380 |
+
fullscreenMode() {
|
| 381 |
+
const chatContainer = document.querySelector('.chat-container, .conversation, .chat, main') || document.body;
|
| 382 |
+
|
| 383 |
+
if (!document.fullscreenElement) {
|
| 384 |
+
if (chatContainer.requestFullscreen) {
|
| 385 |
+
chatContainer.requestFullscreen();
|
| 386 |
+
} else if (chatContainer.webkitRequestFullscreen) {
|
| 387 |
+
chatContainer.webkitRequestFullscreen();
|
| 388 |
+
} else if (chatContainer.msRequestFullscreen) {
|
| 389 |
+
chatContainer.msRequestFullscreen();
|
| 390 |
+
}
|
| 391 |
+
|
| 392 |
+
// Optimize layout
|
| 393 |
+
chatContainer.style.maxWidth = '100%';
|
| 394 |
+
chatContainer.style.width = '100%';
|
| 395 |
+
chatContainer.style.margin = '0';
|
| 396 |
+
chatContainer.style.padding = '20px';
|
| 397 |
+
|
| 398 |
+
this.showMessage('Fullscreen mode activated!');
|
| 399 |
+
} else {
|
| 400 |
+
if (document.exitFullscreen) {
|
| 401 |
+
document.exitFullscreen();
|
| 402 |
+
} else if (document.webkitExitFullscreen) {
|
| 403 |
+
document.webkitExitFullscreen();
|
| 404 |
+
} else if (document.msExitFullscreen) {
|
| 405 |
+
document.msExitFullscreen();
|
| 406 |
+
}
|
| 407 |
+
this.showMessage('Exited fullscreen mode');
|
| 408 |
+
}
|
| 409 |
+
}
|
| 410 |
+
|
| 411 |
+
resetPage() {
|
| 412 |
+
if (confirm('Are you sure you want to reset the page? This will reload the page and clear stored data.')) {
|
| 413 |
+
localStorage.removeItem('hfToZaiData');
|
| 414 |
+
window.location.reload();
|
| 415 |
+
}
|
| 416 |
+
}
|
| 417 |
+
|
| 418 |
+
toggleMinimize() {
|
| 419 |
+
const panel = this.shadowRoot.getElementById('hfPanel');
|
| 420 |
+
panel.classList.toggle('minimized');
|
| 421 |
+
}
|
| 422 |
+
|
| 423 |
+
removePanel() {
|
| 424 |
+
this.remove();
|
| 425 |
+
}
|
| 426 |
+
}
|
| 427 |
+
|
| 428 |
+
customElements.define('hf-panel', HuggingFacePanel);
|
|
@@ -152,9 +152,60 @@
|
|
| 152 |
<span id="toast-message">Copied to clipboard!</span>
|
| 153 |
</div>
|
| 154 |
</div>
|
| 155 |
-
|
| 156 |
<script src="script.js"></script>
|
| 157 |
<script>feather.replace();</script>
|
| 158 |
<script src="https://huggingface.co/deepsite/deepsite-badge.js"></script>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 159 |
</body>
|
| 160 |
-
</html>
|
|
|
|
| 152 |
<span id="toast-message">Copied to clipboard!</span>
|
| 153 |
</div>
|
| 154 |
</div>
|
| 155 |
+
<script src="components/hf-panel.js"></script>
|
| 156 |
<script src="script.js"></script>
|
| 157 |
<script>feather.replace();</script>
|
| 158 |
<script src="https://huggingface.co/deepsite/deepsite-badge.js"></script>
|
| 159 |
+
<script>
|
| 160 |
+
// BookmarkletManager class
|
| 161 |
+
class BookmarkletManager {
|
| 162 |
+
constructor() {
|
| 163 |
+
this.panel = null;
|
| 164 |
+
}
|
| 165 |
+
|
| 166 |
+
init() {
|
| 167 |
+
// Check if we're on Hugging Face
|
| 168 |
+
if (!window.location.href.includes('huggingface.co')) {
|
| 169 |
+
return;
|
| 170 |
+
}
|
| 171 |
+
|
| 172 |
+
// Wait for DOM to be ready
|
| 173 |
+
if (document.readyState === 'loading') {
|
| 174 |
+
document.addEventListener('DOMContentLoaded', () => this.injectPanel());
|
| 175 |
+
} else {
|
| 176 |
+
this.injectPanel();
|
| 177 |
+
}
|
| 178 |
+
}
|
| 179 |
+
|
| 180 |
+
injectPanel() {
|
| 181 |
+
// Check if panel already exists
|
| 182 |
+
if (document.querySelector('hf-panel')) {
|
| 183 |
+
return;
|
| 184 |
+
}
|
| 185 |
+
|
| 186 |
+
// Create and inject panel
|
| 187 |
+
this.panel = document.createElement('hf-panel');
|
| 188 |
+
document.body.appendChild(this.panel);
|
| 189 |
+
}
|
| 190 |
+
|
| 191 |
+
removePanel() {
|
| 192 |
+
if (this.panel) {
|
| 193 |
+
this.panel.remove();
|
| 194 |
+
this.panel = null;
|
| 195 |
+
}
|
| 196 |
+
}
|
| 197 |
+
}
|
| 198 |
+
|
| 199 |
+
// Expose globally
|
| 200 |
+
window.bookmarkletManager = new BookmarkletManager();
|
| 201 |
+
|
| 202 |
+
// Auto-initialize when page is ready
|
| 203 |
+
window.initHFBookmarklet = () => {
|
| 204 |
+
window.bookmarkletManager.init();
|
| 205 |
+
};
|
| 206 |
+
|
| 207 |
+
// Run initialization
|
| 208 |
+
window.initHFBookmarklet();
|
| 209 |
+
</script>
|
| 210 |
</body>
|
| 211 |
+
</html>
|
|
@@ -491,8 +491,59 @@ copyBtn.addEventListener('click', copyToClipboard);
|
|
| 491 |
|
| 492 |
// Initialize tabs
|
| 493 |
switchTabs();
|
| 494 |
-
|
| 495 |
// Auto-focus on load
|
| 496 |
window.addEventListener('load', () => {
|
| 497 |
inputText.focus();
|
| 498 |
-
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 491 |
|
| 492 |
// Initialize tabs
|
| 493 |
switchTabs();
|
|
|
|
| 494 |
// Auto-focus on load
|
| 495 |
window.addEventListener('load', () => {
|
| 496 |
inputText.focus();
|
| 497 |
+
});
|
| 498 |
+
|
| 499 |
+
// Hugging Face Bookmarklet Integration
|
| 500 |
+
class BookmarkletManager {
|
| 501 |
+
constructor() {
|
| 502 |
+
this.panel = null;
|
| 503 |
+
}
|
| 504 |
+
|
| 505 |
+
init() {
|
| 506 |
+
// Check if we're on Hugging Face
|
| 507 |
+
if (!window.location.href.includes('huggingface.co')) {
|
| 508 |
+
return;
|
| 509 |
+
}
|
| 510 |
+
|
| 511 |
+
// Wait for DOM to be ready
|
| 512 |
+
if (document.readyState === 'loading') {
|
| 513 |
+
document.addEventListener('DOMContentLoaded', () => this.injectPanel());
|
| 514 |
+
} else {
|
| 515 |
+
this.injectPanel();
|
| 516 |
+
}
|
| 517 |
+
}
|
| 518 |
+
|
| 519 |
+
injectPanel() {
|
| 520 |
+
// Check if panel already exists
|
| 521 |
+
if (document.querySelector('hf-panel')) {
|
| 522 |
+
return;
|
| 523 |
+
}
|
| 524 |
+
|
| 525 |
+
// Create and inject panel
|
| 526 |
+
this.panel = document.createElement('hf-panel');
|
| 527 |
+
document.body.appendChild(this.panel);
|
| 528 |
+
}
|
| 529 |
+
|
| 530 |
+
removePanel() {
|
| 531 |
+
if (this.panel) {
|
| 532 |
+
this.panel.remove();
|
| 533 |
+
this.panel = null;
|
| 534 |
+
}
|
| 535 |
+
}
|
| 536 |
+
}
|
| 537 |
+
|
| 538 |
+
// Expose globally
|
| 539 |
+
window.bookmarkletManager = new BookmarkletManager();
|
| 540 |
+
|
| 541 |
+
// Auto-initialize when page is ready
|
| 542 |
+
window.initHFBookmarklet = () => {
|
| 543 |
+
window.bookmarkletManager.init();
|
| 544 |
+
};
|
| 545 |
+
|
| 546 |
+
// Run initialization if we're on Hugging Face
|
| 547 |
+
if (window.location.href.includes('huggingface.co')) {
|
| 548 |
+
window.initHFBookmarklet();
|
| 549 |
+
}
|