Spaces:
Running
Running
Download plugins/_whatsapp_integration/webui/config.html from Leon4gr45/openoperator: direct link, hf CLI and curl.
- Browser
- Download file 27 kB
-
https://huggingface.co/spaces/Leon4gr45/openoperator/resolve/main/plugins/_whatsapp_integration/webui/config.html
- Command line
-
hf download hf://spaces/Leon4gr45/openoperator/plugins/_whatsapp_integration/webui/config.html
-
curl -L -o config.html https://huggingface.co/spaces/Leon4gr45/openoperator/resolve/main/plugins/_whatsapp_integration/webui/config.html
27 kB
| <html> | |
| <head> | |
| <title>WhatsApp Integration</title> | |
| <script type="module"> | |
| import { store } from "/plugins/_whatsapp_integration/webui/whatsapp-config-store.js"; | |
| </script> | |
| </head> | |
| <body> | |
| <div x-data x-init="$store.whatsappConfig.init(config, context)" x-destroy="$store.whatsappConfig.cleanup()"> | |
| <template x-if="config"> | |
| <div class="wa-page"> | |
| <div class="section-title">WhatsApp</div> | |
| <template x-if="config.enabled && $store.whatsappConfig.hasMeaningfulConfig()"> | |
| <div class="wa-summary-card"> | |
| <div class="wa-summary-copy"> | |
| <div class="wa-summary-title">Current setup</div> | |
| <div class="wa-summary-subtitle" | |
| x-text="$store.whatsappConfig.modeSummary() + ' · ' + $store.whatsappConfig.projectSummary()"> | |
| </div> | |
| </div> | |
| <span class="wa-status-pill" :class="'tone-' + $store.whatsappConfig.statusTone()" | |
| x-text="$store.whatsappConfig.statusLabel()"></span> | |
| </div> | |
| </template> | |
| <div class="wa-wizard-card"> | |
| <div class="wa-step-header"> | |
| <div class="wa-step-copy"> | |
| <div class="wa-step-title" x-text="$store.whatsappConfig.currentStepMeta().title"></div> | |
| <div class="wa-step-description" | |
| x-text="$store.whatsappConfig.currentStepMeta().description"></div> | |
| </div> | |
| <div class="wa-step-dots" aria-hidden="true"> | |
| <template x-for="(step, idx) in $store.whatsappConfig.steps" :key="step.title"> | |
| <button type="button" class="wa-step-dot" | |
| :class="{ 'is-active': $store.whatsappConfig.currentStep === idx }" | |
| @click="$store.whatsappConfig.setStep(idx)"></button> | |
| </template> | |
| </div> | |
| </div> | |
| <template x-if="$store.whatsappConfig.currentStep === 0"> | |
| <div class="wa-step-panel"> | |
| <div class="field"> | |
| <div class="field-label"> | |
| <div class="field-title">Turn on WhatsApp</div> | |
| <div class="field-description">Enable the local bridge to start.</div> | |
| </div> | |
| <div class="field-control"> | |
| <label class="toggle"> | |
| <input type="checkbox" | |
| x-model="config.enabled" | |
| @change="$store.whatsappConfig.onEnabledChange()" /> | |
| <span class="toggler"></span> | |
| </label> | |
| </div> | |
| </div> | |
| <template x-if="config.enabled"> | |
| <div> | |
| <div class="field"> | |
| <div class="field-label"> | |
| <div class="field-title">WhatsApp account</div> | |
| <div class="field-description"> | |
| <span x-show="!$store.whatsappConfig.disconnectMessage">Click "Show QR code" to pair your WhatsApp account.</span> | |
| <span x-show="$store.whatsappConfig.disconnectMessage" | |
| x-text="$store.whatsappConfig.disconnectMessage"></span> | |
| </div> | |
| </div> | |
| <div class="field-control wa-inline-actions"> | |
| <button class="btn btn-field" @click="$store.whatsappConfig.showQr()"> | |
| Show QR code | |
| </button> | |
| <button class="btn btn-field" @click="$store.whatsappConfig.disconnectAccount()" | |
| :disabled="$store.whatsappConfig.disconnecting"> | |
| <span x-show="!$store.whatsappConfig.disconnecting">Disconnect</span> | |
| <span x-show="$store.whatsappConfig.disconnecting">Disconnecting...</span> | |
| </button> | |
| </div> | |
| </div> | |
| <template x-if="$store.whatsappConfig.qrVisible"> | |
| <div class="wa-qr-panel"> | |
| <template x-if="$store.whatsappConfig.qrStatus === 'connected'"> | |
| <div> | |
| <div class="wa-qr-status ok">Connected</div> | |
| <div class="wa-qr-message" x-text="$store.whatsappConfig.qrMessage"></div> | |
| <button class="btn btn-field" @click="$store.whatsappConfig.hideQr()" | |
| style="margin-top: 12px;"> | |
| Close | |
| </button> | |
| </div> | |
| </template> | |
| <template | |
| x-if="$store.whatsappConfig.qrStatus === 'waiting_scan' && $store.whatsappConfig.qrDataUrl"> | |
| <div> | |
| <div class="wa-qr-status">Scan with WhatsApp on your phone</div> | |
| <img :src="$store.whatsappConfig.qrDataUrl" alt="WhatsApp QR Code" | |
| class="wa-qr-image" /> | |
| <div class="wa-qr-help">The QR code refreshes automatically.</div> | |
| <button class="btn btn-field" @click="$store.whatsappConfig.hideQr()" | |
| style="margin-top: 12px;"> | |
| Cancel | |
| </button> | |
| </div> | |
| </template> | |
| <template | |
| x-if="$store.whatsappConfig.qrStatus !== 'connected' && !($store.whatsappConfig.qrStatus === 'waiting_scan' && $store.whatsappConfig.qrDataUrl)"> | |
| <div> | |
| <div class="wa-qr-status" | |
| x-text="$store.whatsappConfig.qrMessage || 'Connecting...'"></div> | |
| <div class="wa-qr-help" | |
| x-show="$store.whatsappConfig.qrStatus !== 'error'"> | |
| Please wait...</div> | |
| <div class="wa-qr-help error" | |
| x-show="$store.whatsappConfig.qrStatus === 'error'" | |
| x-text="$store.whatsappConfig.qrMessage"></div> | |
| <button class="btn btn-field" @click="$store.whatsappConfig.hideQr()" | |
| style="margin-top: 12px;"> | |
| Cancel | |
| </button> | |
| </div> | |
| </template> | |
| </div> | |
| </template> | |
| </div> | |
| </template> | |
| <div class="wa-note" x-show="!config.enabled"> | |
| Turn on WhatsApp to pair or switch your account. | |
| </div> | |
| <div class="field"> | |
| <div class="field-label"> | |
| <div class="field-title">Mode</div> | |
| <div class="field-description"> | |
| <span x-show="config.mode === 'self-chat'"> | |
| Use your own number. You can message yourself to talk to the agent. | |
| </span> | |
| <span x-show="config.mode !== 'self-chat'"> | |
| Use a separate number dedicated to Agent Zero conversations. | |
| </span> | |
| </div> | |
| </div> | |
| <div class="field-control"> | |
| <select x-model="config.mode"> | |
| <option value="self-chat">Personal number (self-chat)</option> | |
| <option value="dedicated">Separate number (dedicated)</option> | |
| </select> | |
| </div> | |
| </div> | |
| <div class="wa-mode-note"> | |
| <strong>Good to know:</strong> Self-chat uses your own number. Dedicated is better for | |
| shared or public access. You can pair now or come back to it later. | |
| </div> | |
| <div class="wa-warning" x-show="$store.whatsappConfig.accessWarning()"> | |
| <div class="wa-warning-title"> | |
| <span aria-hidden="true">⚠</span> | |
| <span>Warning</span> | |
| </div> | |
| <div class="wa-warning-body" x-text="$store.whatsappConfig.accessWarning()"></div> | |
| </div> | |
| <div class="field"> | |
| <div class="field-label"> | |
| <div class="field-title">Allowed numbers</div> | |
| <div class="field-description">Comma-separated phone numbers. Punctuation and + | |
| prefixes are okay. Leave empty only if you want open access.</div> | |
| </div> | |
| <div class="field-control"> | |
| <input type="text" :value="$store.whatsappConfig.allowedText()" | |
| @input="$store.whatsappConfig.setAllowed($event.target.value)" | |
| placeholder="+1 (415) 555-1234, +44 7911 123456" /> | |
| </div> | |
| </div> | |
| <div class="field"> | |
| <div class="field-label"> | |
| <div class="field-title">Allow groups</div> | |
| <div class="field-description">Reply in group chats when mentioned or replied to. | |
| </div> | |
| </div> | |
| <div class="field-control"> | |
| <label class="toggle"> | |
| <input type="checkbox" x-model="config.allow_group" /> | |
| <span class="toggler"></span> | |
| </label> | |
| </div> | |
| </div> | |
| </div> | |
| </template> | |
| <template x-if="$store.whatsappConfig.currentStep === 1"> | |
| <div class="wa-step-panel"> | |
| <div class="field"> | |
| <div class="field-label"> | |
| <div class="field-title">Project</div> | |
| <div class="field-description">Optional project to activate for WhatsApp | |
| conversations.</div> | |
| </div> | |
| <div class="field-control"> | |
| <select :value="config.project" @change="config.project = $event.target.value"> | |
| <option value="">No project</option> | |
| <template x-for="proj in $store.whatsappConfig.projects" :key="$store.whatsappConfig.projectOptionValue(proj)"> | |
| <option :value="$store.whatsappConfig.projectOptionValue(proj)" | |
| x-text="$store.whatsappConfig.projectOptionLabel(proj)" | |
| :selected="config.project === $store.whatsappConfig.projectOptionValue(proj)"> | |
| </option> | |
| </template> | |
| </select> | |
| </div> | |
| </div> | |
| <div class="field"> | |
| <div class="field-label"> | |
| <div class="field-title">Agent instructions</div> | |
| <div class="field-description">Extra guidance for how the agent should reply in | |
| WhatsApp chats.</div> | |
| </div> | |
| <div class="field-control"> | |
| <textarea x-model="config.agent_instructions" rows="3" | |
| placeholder="Reply briefly and naturally, like a mobile conversation."></textarea> | |
| </div> | |
| </div> | |
| </div> | |
| </template> | |
| <div class="wa-test-panel"> | |
| <div class="wa-test-copy"> | |
| <div class="wa-test-title">Check the connection</div> | |
| <div class="wa-test-description">We will check whether the local WhatsApp bridge is up and | |
| connected.</div> | |
| </div> | |
| <button class="btn btn-field" @click="$store.whatsappConfig.testConnection()" | |
| :disabled="$store.whatsappConfig.testing"> | |
| <span x-text="$store.whatsappConfig.testButtonLabel()"></span> | |
| </button> | |
| </div> | |
| <template x-if="$store.whatsappConfig.testResults"> | |
| <div class="wa-results" :class="{ 'is-error': !$store.whatsappConfig.testResults.success }"> | |
| <template x-for="result in $store.whatsappConfig.testResults.results" | |
| :key="result.test + result.message"> | |
| <div class="wa-result-row"> | |
| <span class="wa-result-icon" x-text="result.ok ? '✓' : '✗'"></span> | |
| <div class="wa-result-copy"> | |
| <div class="wa-result-title" x-text="result.test"></div> | |
| <div class="wa-result-message" x-text="result.message"></div> | |
| </div> | |
| </div> | |
| </template> | |
| </div> | |
| </template> | |
| <template x-if="$store.whatsappConfig.currentStep > 0"> | |
| <details class="wa-advanced"> | |
| <summary> | |
| <span>Advanced</span> | |
| <x-icon class="wa-advanced-chevron" | |
| aria-hidden="true" name="keyboard_arrow_down"></x-icon> | |
| </summary> | |
| <div class="wa-advanced-body"> | |
| <div class="wa-info-box"> | |
| These settings are here when you need them, but the defaults are fine for most | |
| setups. | |
| </div> | |
| <div class="field"> | |
| <div class="field-label"> | |
| <div class="field-title">Bridge port</div> | |
| <div class="field-description">Local port for the WhatsApp bridge HTTP server. | |
| </div> | |
| </div> | |
| <div class="field-control"> | |
| <input type="number" x-model.number="config.bridge_port" placeholder="3100" /> | |
| </div> | |
| </div> | |
| <div class="field"> | |
| <div class="field-label"> | |
| <div class="field-title">Poll interval (seconds)</div> | |
| <div class="field-description">How often to check for new messages. The minimum | |
| is 2 seconds.</div> | |
| </div> | |
| <div class="field-control"> | |
| <input type="number" x-model.number="config.poll_interval_seconds" min="2" | |
| placeholder="3" /> | |
| </div> | |
| </div> | |
| </div> | |
| </details> | |
| </template> | |
| </div> | |
| </div> | |
| </template> | |
| </div> | |
| <style> | |
| .wa-page { | |
| display: flex; | |
| flex-direction: column; | |
| gap: 0.9rem; | |
| } | |
| .wa-summary-card, | |
| .wa-wizard-card { | |
| border-radius: 0.5rem; | |
| } | |
| .wa-advanced summary { | |
| cursor: pointer; | |
| list-style: none; | |
| font-weight: 700; | |
| display: flex; | |
| align-items: center; | |
| gap: 0.75rem; | |
| } | |
| .wa-advanced summary::-webkit-details-marker { | |
| display: none; | |
| } | |
| .wa-summary-title, | |
| .wa-step-title, | |
| .wa-test-title { | |
| font-weight: 700; | |
| } | |
| .wa-summary-subtitle, | |
| .wa-step-description, | |
| .wa-note, | |
| .wa-mode-note, | |
| .wa-test-description, | |
| .wa-result-message { | |
| color: var(--color-text-secondary); | |
| line-height: 1.5; | |
| } | |
| .wa-summary-card { | |
| padding: 1rem; | |
| border: 1px solid var(--color-border); | |
| display: flex; | |
| justify-content: space-between; | |
| gap: 1rem; | |
| align-items: center; | |
| } | |
| .wa-step-header { | |
| display: flex; | |
| justify-content: space-between; | |
| gap: 1rem; | |
| align-items: flex-start; | |
| margin-bottom: 1rem; | |
| } | |
| .wa-step-dots { | |
| display: flex; | |
| gap: 0.5rem; | |
| flex-wrap: wrap; | |
| justify-content: flex-end; | |
| align-items: center; | |
| } | |
| .wa-step-dot { | |
| width: 0.85rem; | |
| height: 0.85rem; | |
| border-radius: 999px; | |
| border: 1px solid var(--color-border); | |
| background: transparent; | |
| cursor: pointer; | |
| padding: 0; | |
| } | |
| .wa-step-dot.is-active { | |
| background: rgba(59, 130, 246, 0.9); | |
| border-color: rgba(59, 130, 246, 0.9); | |
| } | |
| .wa-step-panel { | |
| display: flex; | |
| flex-direction: column; | |
| gap: 0.2rem; | |
| } | |
| .wa-info-box, | |
| .wa-note { | |
| margin-bottom: 0.9rem; | |
| padding: 0.8rem 0.9rem; | |
| border-radius: 12px; | |
| font-size: var(--font-size-small); | |
| } | |
| .wa-info-box { | |
| background: color-mix(in srgb, #3b82f6 12%, var(--color-background) 88%); | |
| } | |
| .wa-note { | |
| background: color-mix(in srgb, var(--color-background) 88%, white 12%); | |
| } | |
| .wa-warning { | |
| margin: 0.5rem 0 1.25rem; | |
| padding: 0.75rem 0.9rem; | |
| border-radius: 10px; | |
| border: 1px solid rgba(255, 170, 0, 0.45); | |
| background: rgba(255, 170, 0, 0.12); | |
| color: var(--color-warning-text); | |
| font-size: var(--font-size-small); | |
| } | |
| .wa-warning-title { | |
| font-weight: 600; | |
| display: flex; | |
| align-items: center; | |
| gap: 0.5rem; | |
| } | |
| .wa-warning-body { | |
| margin-top: 0.25rem; | |
| line-height: 1.45; | |
| } | |
| .wa-mode-note { | |
| margin-top: -0.2rem; | |
| margin-bottom: 0.9rem; | |
| font-size: var(--font-size-small); | |
| } | |
| .wa-inline-actions { | |
| display: flex; | |
| gap: 0.75rem; | |
| flex-wrap: wrap; | |
| } | |
| .wa-qr-panel { | |
| margin-top: 0.25rem; | |
| padding: 1rem; | |
| border-radius: 14px; | |
| border: 1px solid color-mix(in srgb, var(--color-border) 88%, white 12%); | |
| background: color-mix(in srgb, var(--color-background) 90%, white 10%); | |
| text-align: center; | |
| } | |
| .wa-qr-status { | |
| font-weight: 700; | |
| margin-bottom: 0.5rem; | |
| } | |
| .wa-qr-status.ok { | |
| color: #7ee7a4; | |
| } | |
| .wa-qr-message, | |
| .wa-qr-help { | |
| color: var(--color-text-secondary); | |
| line-height: 1.5; | |
| } | |
| .wa-qr-help { | |
| font-size: var(--font-size-small); | |
| margin-top: 0.4rem; | |
| } | |
| .wa-qr-help.error { | |
| color: #fca5a5; | |
| } | |
| .wa-qr-image { | |
| width: 256px; | |
| height: 256px; | |
| max-width: 100%; | |
| border-radius: 8px; | |
| background: white; | |
| padding: 4px; | |
| } | |
| .wa-advanced { | |
| margin-top: 1rem; | |
| border: 1px solid color-mix(in srgb, var(--color-border) 88%, white 12%); | |
| border-radius: 14px; | |
| overflow: hidden; | |
| } | |
| .wa-advanced summary { | |
| padding: 0.9rem 1rem; | |
| background: color-mix(in srgb, var(--color-background) 88%, white 12%); | |
| } | |
| .wa-advanced-chevron { | |
| margin-left: auto; | |
| flex: 0 0 auto; | |
| transition: transform 0.18s ease, opacity 0.18s ease; | |
| opacity: 0.72; | |
| } | |
| .wa-advanced[open] .wa-advanced-chevron { | |
| transform: rotate(180deg); | |
| opacity: 1; | |
| } | |
| .wa-advanced-body { | |
| padding: 1rem; | |
| display: flex; | |
| flex-direction: column; | |
| gap: 0.2rem; | |
| } | |
| .wa-test-panel { | |
| margin-top: 1rem; | |
| padding: var(--spacing-xs) 0; | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| gap: 1rem; | |
| } | |
| .wa-test-copy, | |
| .wa-result-copy { | |
| min-width: 0; | |
| } | |
| .wa-results { | |
| margin-top: 0.9rem; | |
| border-radius: 14px; | |
| border: 1px solid color-mix(in srgb, #22c55e 28%, var(--color-border) 72%); | |
| background: color-mix(in srgb, #22c55e 8%, var(--color-background) 92%); | |
| padding: 0.35rem 0.9rem; | |
| } | |
| .wa-results.is-error { | |
| border-color: color-mix(in srgb, #ef4444 28%, var(--color-border) 72%); | |
| background: color-mix(in srgb, #ef4444 8%, var(--color-background) 92%); | |
| } | |
| .wa-result-row { | |
| display: flex; | |
| align-items: flex-start; | |
| gap: 0.8rem; | |
| padding: 0.7rem 0; | |
| } | |
| .wa-result-row+.wa-result-row { | |
| border-top: 1px solid color-mix(in srgb, var(--color-border) 85%, white 15%); | |
| } | |
| .wa-result-icon { | |
| width: 1.25rem; | |
| font-weight: 800; | |
| line-height: 1.3; | |
| } | |
| .wa-result-title { | |
| font-weight: 700; | |
| margin-bottom: 0.18rem; | |
| } | |
| .wa-status-pill { | |
| display: inline-flex; | |
| align-items: center; | |
| justify-content: center; | |
| min-width: 5.25rem; | |
| padding: 0.35rem 0.7rem; | |
| border-radius: 999px; | |
| font-size: 0.8rem; | |
| font-weight: 700; | |
| } | |
| .wa-status-pill.tone-success { | |
| background: rgba(34, 197, 94, 0.14); | |
| color: #7ee7a4; | |
| } | |
| .wa-status-pill.tone-ready { | |
| background: rgba(59, 130, 246, 0.16); | |
| color: #93c5fd; | |
| } | |
| .wa-status-pill.tone-warning { | |
| background: rgba(245, 158, 11, 0.16); | |
| color: #fcd34d; | |
| } | |
| .wa-status-pill.tone-muted { | |
| background: rgba(148, 163, 184, 0.14); | |
| color: #cbd5e1; | |
| } | |
| @media (max-width: 640px) { | |
| .wa-summary-card, | |
| .wa-step-header, | |
| .wa-test-panel { | |
| flex-direction: column; | |
| align-items: stretch; | |
| } | |
| .wa-step-dots { | |
| width: 100%; | |
| justify-content: space-between; | |
| } | |
| .wa-status-pill { | |
| min-width: 0; | |
| align-self: flex-start; | |
| } | |
| } | |
| </style> | |
| </body> | |
| </html> | |