| 1 | <html> |
| 2 | |
| 3 | <head> |
| 4 | <title>Telegram Integration</title> |
| 5 | <script type="module"> |
| 6 | import { store } from "/plugins/_telegram_integration/webui/telegram-config-store.js"; |
| 7 | </script> |
| 8 | </head> |
| 9 | |
| 10 | <body> |
| 11 | <div x-data x-init="$store.telegramConfig.init(config, context)" x-destroy="$store.telegramConfig.cleanup()"> |
| 12 | <template x-if="config"> |
| 13 | <div class="tg-page"> |
| 14 | <div class="section-title">Telegram</div> |
| 15 | |
| 16 | <template x-if="$store.telegramConfig.didInit && $store.telegramConfig.bots.length === 0"> |
| 17 | <div class="tg-empty"> |
| 18 | <div class="tg-empty-title">Start with one bot</div> |
| 19 | <div class="tg-empty-copy"> |
| 20 | Most setups only need a bot token, one allowed user, and the default polling mode. |
| 21 | </div> |
| 22 | <button class="btn btn-field" @click="$store.telegramConfig.addBot()"> |
| 23 | Connect a bot |
| 24 | </button> |
| 25 | </div> |
| 26 | </template> |
| 27 | |
| 28 | <template x-for="(bot, idx) in $store.telegramConfig.bots" :key="idx"> |
| 29 | <div class="tg-card"> |
| 30 | <div class="tg-card-header" @click="$store.telegramConfig.toggleEditing(idx)"> |
| 31 | <div class="tg-card-heading"> |
| 32 | <div class="tg-card-title" x-text="$store.telegramConfig.botTitle(bot, idx)"></div> |
| 33 | <div class="tg-card-subtitle" x-text="$store.telegramConfig.botSubtitle(bot)"></div> |
| 34 | </div> |
| 35 | <div class="tg-card-actions"> |
| 36 | <span class="tg-status-pill" :class="'tone-' + $store.telegramConfig.botStatusTone(bot)" |
| 37 | x-text="$store.telegramConfig.botStatusLabel(bot)"></span> |
| 38 | <button class="btn btn-action delete" |
| 39 | @click.stop="$confirmClick($event, () => $store.telegramConfig.removeBot(idx))" title="Remove bot"> |
| 40 | <x-icon name="delete"></x-icon> |
| 41 | </button> |
| 42 | <x-icon class="tg-card-chevron" |
| 43 | :class="{ 'is-open': $store.telegramConfig.editing === idx }" name="expand_more"></x-icon> |
| 44 | </div> |
| 45 | </div> |
| 46 | |
| 47 | <template x-if="$store.telegramConfig.editing === idx"> |
| 48 | <div class="tg-card-body"> |
| 49 | <div class="tg-step-header"> |
| 50 | <div class="tg-step-copy"> |
| 51 | <div class="tg-step-title" x-text="$store.telegramConfig.currentStepMeta().title"></div> |
| 52 | <div class="tg-step-description" x-text="$store.telegramConfig.currentStepMeta().description"></div> |
| 53 | </div> |
| 54 | <div class="tg-step-dots" aria-hidden="true"> |
| 55 | <template x-for="(step, stepIdx) in $store.telegramConfig.steps" :key="step.title"> |
| 56 | <button type="button" class="tg-step-dot" |
| 57 | :class="{ 'is-active': $store.telegramConfig.currentStep === stepIdx }" |
| 58 | @click="$store.telegramConfig.setStep(stepIdx)"></button> |
| 59 | </template> |
| 60 | </div> |
| 61 | </div> |
| 62 | |
| 63 | <template x-if="$store.telegramConfig.currentStep === 0"> |
| 64 | <div class="tg-step-panel"> |
| 65 | <div class="tg-token-row"> |
| 66 | <div class="tg-qr-card"> |
| 67 | <div class="tg-qr-title">Scan to open @BotFather</div> |
| 68 | <img class="tg-qr-image" :src="$store.telegramConfig.botFatherQr" |
| 69 | alt="Scan to open @BotFather" /> |
| 70 | </div> |
| 71 | |
| 72 | <div class="tg-token-card"> |
| 73 | <div class="tg-token-steps"> |
| 74 | <div class="tg-token-steps-title">Create your bot</div> |
| 75 | <ol class="tg-token-steps-list"> |
| 76 | <li>Open <a href="https://t.me/BotFather" target="_blank" rel="noopener">@BotFather</a></li> |
| 77 | <li>Send <strong>/newbot</strong> and follow the prompts</li> |
| 78 | <li>Paste the token here</li> |
| 79 | </ol> |
| 80 | </div> |
| 81 | |
| 82 | <div class="field"> |
| 83 | <div class="field-label"> |
| 84 | <div class="field-title">Bot token</div> |
| 85 | <div class="field-description">Paste the token you got from @BotFather.</div> |
| 86 | </div> |
| 87 | <div class="field-control"> |
| 88 | <input type="text" class="masked-text-input" autocomplete="off" x-model="bot.token" |
| 89 | placeholder="123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11" /> |
| 90 | </div> |
| 91 | </div> |
| 92 | </div> |
| 93 | </div> |
| 94 | </div> |
| 95 | </template> |
| 96 | |
| 97 | <template x-if="$store.telegramConfig.currentStep === 1"> |
| 98 | <div class="tg-step-panel"> |
| 99 | <div class="field"> |
| 100 | <div class="field-label"> |
| 101 | <div class="field-title">Turn on this bot</div> |
| 102 | <div class="field-description">Enable message polling when you are ready for the bot to go live. |
| 103 | </div> |
| 104 | </div> |
| 105 | <div class="field-control"> |
| 106 | <label class="toggle"> |
| 107 | <input type="checkbox" x-model="bot.enabled" /> |
| 108 | <span class="toggler"></span> |
| 109 | </label> |
| 110 | </div> |
| 111 | </div> |
| 112 | |
| 113 | <div class="field"> |
| 114 | <div class="field-label"> |
| 115 | <div class="field-title">Bot name</div> |
| 116 | <div class="field-description">A friendly internal label so you can tell bots apart.</div> |
| 117 | </div> |
| 118 | <div class="field-control"> |
| 119 | <input type="text" x-model="bot.name" placeholder="support_bot" /> |
| 120 | </div> |
| 121 | </div> |
| 122 | |
| 123 | <div class="field"> |
| 124 | <div class="field-label"> |
| 125 | <div class="field-title">Delivery mode</div> |
| 126 | <div class="field-description">Polling is the simplest option. Webhook is for public HTTPS |
| 127 | setups.</div> |
| 128 | </div> |
| 129 | <div class="field-control"> |
| 130 | <select x-model="bot.mode"> |
| 131 | <option value="polling">Polling</option> |
| 132 | <option value="webhook">Webhook</option> |
| 133 | </select> |
| 134 | </div> |
| 135 | </div> |
| 136 | |
| 137 | <div class="field" x-show="bot.mode === 'webhook'"> |
| 138 | <div class="field-label"> |
| 139 | <div class="field-title">Webhook URL</div> |
| 140 | <div class="field-description">Your public Agent Zero base URL, for example |
| 141 | https://yourdomain.com.</div> |
| 142 | </div> |
| 143 | <div class="field-control"> |
| 144 | <input type="text" x-model="bot.webhook_url" placeholder="https://yourdomain.com" /> |
| 145 | </div> |
| 146 | </div> |
| 147 | |
| 148 | <div class="field"> |
| 149 | <div class="field-label"> |
| 150 | <div class="field-title">Allowed users</div> |
| 151 | <div class="field-description">Comma-separated @usernames or numeric IDs. Leave empty only if |
| 152 | you truly want open access.</div> |
| 153 | </div> |
| 154 | <div class="field-control"> |
| 155 | <input type="text" :value="$store.telegramConfig.whitelistText(bot)" |
| 156 | @input="$store.telegramConfig.setWhitelist(bot, $event.target.value)" |
| 157 | placeholder="@yourname, 123456789" /> |
| 158 | </div> |
| 159 | </div> |
| 160 | |
| 161 | <div class="tg-warning" x-show="$store.telegramConfig.accessWarning(bot)"> |
| 162 | <span x-text="$store.telegramConfig.accessWarning(bot)"></span> |
| 163 | </div> |
| 164 | |
| 165 | <div class="field"> |
| 166 | <div class="field-label"> |
| 167 | <div class="field-title">Default project</div> |
| 168 | <div class="field-description">Optional fallback project for conversations from this bot.</div> |
| 169 | </div> |
| 170 | <div class="field-control"> |
| 171 | <select :value="bot.default_project" @change="bot.default_project = $event.target.value"> |
| 172 | <option value="">No project</option> |
| 173 | <template x-for="proj in $store.telegramConfig.projects" :key="proj.name"> |
| 174 | <option :value="proj.name" x-text="proj.title || proj.name" |
| 175 | :selected="bot.default_project === proj.name"></option> |
| 176 | </template> |
| 177 | </select> |
| 178 | </div> |
| 179 | </div> |
| 180 | </div> |
| 181 | </template> |
| 182 | |
| 183 | <template x-if="$store.telegramConfig.currentStep === 2"> |
| 184 | <div class="tg-step-panel"> |
| 185 | <div class="field"> |
| 186 | <div class="field-label"> |
| 187 | <div class="field-title">Group behavior</div> |
| 188 | <div class="field-description">Choose whether the bot replies only when invited into the |
| 189 | conversation or to everything in a group.</div> |
| 190 | </div> |
| 191 | <div class="field-control"> |
| 192 | <select x-model="bot.group_mode"> |
| 193 | <option value="mention">Reply when @mentioned or replied to</option> |
| 194 | <option value="all">Reply to every group message</option> |
| 195 | <option value="off">Ignore group messages</option> |
| 196 | </select> |
| 197 | </div> |
| 198 | </div> |
| 199 | |
| 200 | <div class="field" x-show="bot.group_mode !== 'off'"> |
| 201 | <div class="field-label"> |
| 202 | <div class="field-title">Welcome new members</div> |
| 203 | <div class="field-description">Send a greeting when someone joins the group.</div> |
| 204 | </div> |
| 205 | <div class="field-control"> |
| 206 | <label class="toggle"> |
| 207 | <input type="checkbox" x-model="bot.welcome_enabled" /> |
| 208 | <span class="toggler"></span> |
| 209 | </label> |
| 210 | </div> |
| 211 | </div> |
| 212 | |
| 213 | <div class="field" x-show="bot.group_mode !== 'off' && bot.welcome_enabled"> |
| 214 | <div class="field-label"> |
| 215 | <div class="field-title">Welcome message</div> |
| 216 | <div class="field-description">Use {name} for the new member's name.</div> |
| 217 | </div> |
| 218 | <div class="field-control"> |
| 219 | <input type="text" x-model="bot.welcome_message" placeholder="Welcome, {name}!" /> |
| 220 | </div> |
| 221 | </div> |
| 222 | |
| 223 | <div class="field"> |
| 224 | <div class="field-label"> |
| 225 | <div class="field-title">Agent instructions</div> |
| 226 | <div class="field-description">Extra guidance for how the agent should reply in Telegram chats. |
| 227 | </div> |
| 228 | </div> |
| 229 | <div class="field-control"> |
| 230 | <textarea x-model="bot.agent_instructions" rows="3" |
| 231 | placeholder="Reply in a concise, mobile-friendly way."></textarea> |
| 232 | </div> |
| 233 | </div> |
| 234 | </div> |
| 235 | </template> |
| 236 | |
| 237 | <div class="tg-test-panel"> |
| 238 | <div class="tg-test-copy"> |
| 239 | <div class="tg-test-title">Check the connection</div> |
| 240 | <div class="tg-test-description" x-text="$store.telegramConfig.testIntro()"></div> |
| 241 | </div> |
| 242 | <button class="btn btn-field" @click.stop="$store.telegramConfig.testConnection(idx)" |
| 243 | :disabled="$store.telegramConfig.testing === idx || !$store.telegramConfig.canTest(bot)"> |
| 244 | <span x-text="$store.telegramConfig.testButtonLabel(bot, idx)"></span> |
| 245 | </button> |
| 246 | </div> |
| 247 | |
| 248 | <template x-if="$store.telegramConfig.testResults && $store.telegramConfig.testResultsFor === idx"> |
| 249 | <div class="tg-results" :class="{ 'is-error': !$store.telegramConfig.testResults.success }"> |
| 250 | <template x-for="result in $store.telegramConfig.testResults.results" |
| 251 | :key="result.test + result.message"> |
| 252 | <div class="tg-result-row"> |
| 253 | <span class="tg-result-icon" x-text="result.ok ? '✓' : '✗'"></span> |
| 254 | <div class="tg-result-copy"> |
| 255 | <div class="tg-result-title" x-text="$store.telegramConfig.resultTitle(result)"></div> |
| 256 | <div class="tg-result-message" x-text="$store.telegramConfig.resultMessage(result)"></div> |
| 257 | </div> |
| 258 | </div> |
| 259 | </template> |
| 260 | </div> |
| 261 | </template> |
| 262 | |
| 263 | <template x-if="$store.telegramConfig.currentStep > 0"> |
| 264 | <details class="tg-advanced"> |
| 265 | <summary> |
| 266 | <span>Advanced</span> |
| 267 | <x-icon class="tg-advanced-chevron" |
| 268 | aria-hidden="true" name="keyboard_arrow_down"></x-icon> |
| 269 | </summary> |
| 270 | <div class="tg-advanced-body"> |
| 271 | <div class="field" x-show="bot.mode === 'webhook'"> |
| 272 | <div class="field-label"> |
| 273 | <div class="field-title">Webhook secret</div> |
| 274 | <div class="field-description">Optional shared secret for webhook verification.</div> |
| 275 | </div> |
| 276 | <div class="field-control"> |
| 277 | <input type="text" class="masked-text-input" autocomplete="off" x-model="bot.webhook_secret" /> |
| 278 | </div> |
| 279 | </div> |
| 280 | |
| 281 | <div class="field"> |
| 282 | <div class="field-label"> |
| 283 | <div class="field-title">Message notifications</div> |
| 284 | <div class="field-description">Show a WebUI notification for each incoming Telegram message. |
| 285 | </div> |
| 286 | </div> |
| 287 | <div class="field-control"> |
| 288 | <label class="toggle"> |
| 289 | <input type="checkbox" x-model="bot.notify_messages" /> |
| 290 | <span class="toggler"></span> |
| 291 | </label> |
| 292 | </div> |
| 293 | </div> |
| 294 | |
| 295 | <div class="field"> |
| 296 | <div class="field-label"> |
| 297 | <div class="field-title">User project mapping</div> |
| 298 | <div class="field-description">Map specific Telegram user IDs to projects with user_id=project |
| 299 | entries.</div> |
| 300 | </div> |
| 301 | <div class="field-control"> |
| 302 | <input type="text" :value="$store.telegramConfig.userProjectsText(bot)" |
| 303 | @input="$store.telegramConfig.setUserProjects(bot, $event.target.value)" |
| 304 | placeholder="123456=my_project, 789012=other_project" /> |
| 305 | </div> |
| 306 | </div> |
| 307 | |
| 308 | <div class="field"> |
| 309 | <div class="field-label"> |
| 310 | <div class="field-title">Attachment max age</div> |
| 311 | <div class="field-description">How long to keep downloaded attachments. Use 0 to keep them |
| 312 | forever.</div> |
| 313 | </div> |
| 314 | <div class="field-control"> |
| 315 | <input type="number" x-model.number="bot.attachment_max_age_hours" min="0" step="1" |
| 316 | placeholder="0" /> |
| 317 | </div> |
| 318 | </div> |
| 319 | |
| 320 | </div> |
| 321 | </details> |
| 322 | </template> |
| 323 | </div> |
| 324 | </template> |
| 325 | </div> |
| 326 | </template> |
| 327 | |
| 328 | <template x-if="$store.telegramConfig.bots.length > 0"> |
| 329 | <button class="btn btn-field tg-add-btn" @click="$store.telegramConfig.addBot()"> |
| 330 | Connect another bot |
| 331 | </button> |
| 332 | </template> |
| 333 | </div> |
| 334 | </template> |
| 335 | </div> |
| 336 | |
| 337 | <style> |
| 338 | .tg-page { |
| 339 | display: flex; |
| 340 | flex-direction: column; |
| 341 | gap: 0.9rem; |
| 342 | } |
| 343 | |
| 344 | .tg-advanced summary { |
| 345 | cursor: pointer; |
| 346 | list-style: none; |
| 347 | font-weight: 700; |
| 348 | display: flex; |
| 349 | align-items: center; |
| 350 | gap: 0.75rem; |
| 351 | } |
| 352 | |
| 353 | .tg-advanced summary::-webkit-details-marker { |
| 354 | display: none; |
| 355 | } |
| 356 | |
| 357 | .tg-guide-card, |
| 358 | .tg-empty, |
| 359 | .tg-card { |
| 360 | border-radius: 0.5rem; |
| 361 | } |
| 362 | |
| 363 | .tg-empty-title, |
| 364 | .tg-step-title, |
| 365 | .tg-test-title, |
| 366 | .tg-qr-title { |
| 367 | font-weight: 700; |
| 368 | } |
| 369 | |
| 370 | .tg-empty-copy, |
| 371 | .tg-step-description, |
| 372 | .tg-info-box, |
| 373 | .tg-test-description, |
| 374 | .tg-result-message, |
| 375 | .tg-card-subtitle { |
| 376 | color: var(--color-text-secondary); |
| 377 | line-height: 1.5; |
| 378 | } |
| 379 | |
| 380 | .tg-empty { |
| 381 | padding: 1rem; |
| 382 | } |
| 383 | |
| 384 | .tg-empty-copy { |
| 385 | margin-top: 0.35rem; |
| 386 | margin-bottom: 0.9rem; |
| 387 | } |
| 388 | |
| 389 | .tg-card-header { |
| 390 | display: flex; |
| 391 | justify-content: space-between; |
| 392 | gap: 1rem; |
| 393 | align-items: flex-start; |
| 394 | padding: var(--spacing-sm) 0; |
| 395 | border-bottom: 1px solid var(--color-border); |
| 396 | cursor: pointer; |
| 397 | } |
| 398 | |
| 399 | .tg-card-heading { |
| 400 | min-width: 0; |
| 401 | flex: 1 1 auto; |
| 402 | } |
| 403 | |
| 404 | .tg-card-title { |
| 405 | font-weight: 700; |
| 406 | line-height: 1.35; |
| 407 | overflow-wrap: anywhere; |
| 408 | } |
| 409 | |
| 410 | .tg-card-subtitle { |
| 411 | margin-top: 0.3rem; |
| 412 | font-size: var(--font-size-small); |
| 413 | } |
| 414 | |
| 415 | .tg-card-actions { |
| 416 | display: flex; |
| 417 | align-items: center; |
| 418 | gap: 0.45rem; |
| 419 | flex: 0 0 auto; |
| 420 | } |
| 421 | |
| 422 | .tg-card-chevron { |
| 423 | transition: transform 0.18s ease; |
| 424 | opacity: 0.7; |
| 425 | } |
| 426 | |
| 427 | .tg-card-chevron.is-open { |
| 428 | transform: rotate(180deg); |
| 429 | } |
| 430 | |
| 431 | .tg-card-body { |
| 432 | border-top: 1px solid color-mix(in srgb, var(--color-border) 88%, white 12%); |
| 433 | } |
| 434 | |
| 435 | .tg-step-header { |
| 436 | display: flex; |
| 437 | justify-content: space-between; |
| 438 | gap: 1rem; |
| 439 | align-items: flex-start; |
| 440 | margin: 1rem 0; |
| 441 | } |
| 442 | |
| 443 | .tg-step-dots { |
| 444 | display: flex; |
| 445 | gap: 0.5rem; |
| 446 | flex-wrap: wrap; |
| 447 | justify-content: flex-end; |
| 448 | align-items: center; |
| 449 | } |
| 450 | |
| 451 | .tg-step-dot { |
| 452 | width: 0.85rem; |
| 453 | height: 0.85rem; |
| 454 | border-radius: 999px; |
| 455 | border: 1px solid var(--color-border); |
| 456 | background: transparent; |
| 457 | cursor: pointer; |
| 458 | padding: 0; |
| 459 | } |
| 460 | |
| 461 | .tg-step-dot.is-active { |
| 462 | background: rgba(59, 130, 246, 0.9); |
| 463 | border-color: rgba(59, 130, 246, 0.9); |
| 464 | } |
| 465 | |
| 466 | .tg-step-panel { |
| 467 | display: flex; |
| 468 | flex-direction: column; |
| 469 | gap: 0.2rem; |
| 470 | } |
| 471 | |
| 472 | .tg-token-row { |
| 473 | display: grid; |
| 474 | grid-template-columns: minmax(12rem, 16rem) minmax(0, 1fr); |
| 475 | gap: 1rem; |
| 476 | align-items: stretch; |
| 477 | } |
| 478 | |
| 479 | .tg-qr-card, |
| 480 | .tg-token-card { |
| 481 | border-radius: 0.5rem; |
| 482 | background: color-mix(in srgb, var(--color-background) 90%, white 10%); |
| 483 | padding: 1rem; |
| 484 | } |
| 485 | |
| 486 | .tg-qr-card { |
| 487 | display: flex; |
| 488 | flex-direction: column; |
| 489 | align-items: center; |
| 490 | text-align: center; |
| 491 | gap: 0.8rem; |
| 492 | } |
| 493 | |
| 494 | .tg-qr-image { |
| 495 | width: 150px; |
| 496 | height: 150px; |
| 497 | border-radius: 0.5rem; |
| 498 | image-rendering: pixelated; |
| 499 | } |
| 500 | |
| 501 | .tg-token-steps { |
| 502 | margin-bottom: 0.9rem; |
| 503 | } |
| 504 | |
| 505 | .tg-token-steps-title { |
| 506 | font-weight: 700; |
| 507 | margin-bottom: 0.45rem; |
| 508 | } |
| 509 | |
| 510 | .tg-token-steps-list { |
| 511 | margin: 0; |
| 512 | padding-left: 1.15rem; |
| 513 | color: var(--color-text-secondary); |
| 514 | line-height: 1.55; |
| 515 | } |
| 516 | |
| 517 | .tg-token-steps-list a { |
| 518 | color: var(--color-highlight); |
| 519 | } |
| 520 | |
| 521 | .tg-info-box, |
| 522 | .tg-warning { |
| 523 | margin-bottom: 0.9rem; |
| 524 | padding: 0.8rem 0.9rem; |
| 525 | border-radius: 12px; |
| 526 | font-size: var(--font-size-small); |
| 527 | } |
| 528 | |
| 529 | .tg-info-box { |
| 530 | background: color-mix(in srgb, #3b82f6 12%, var(--color-background) 88%); |
| 531 | } |
| 532 | |
| 533 | .tg-warning { |
| 534 | background: color-mix(in srgb, #f59e0b 14%, var(--color-background) 86%); |
| 535 | } |
| 536 | |
| 537 | .tg-advanced { |
| 538 | margin-top: 0.95rem; |
| 539 | border: 1px solid color-mix(in srgb, var(--color-border) 88%, white 12%); |
| 540 | border-radius: 14px; |
| 541 | overflow: hidden; |
| 542 | } |
| 543 | |
| 544 | .tg-advanced summary { |
| 545 | padding: 0.9rem 1rem; |
| 546 | background: color-mix(in srgb, var(--color-background) 88%, white 12%); |
| 547 | } |
| 548 | |
| 549 | .tg-advanced-chevron { |
| 550 | margin-left: auto; |
| 551 | flex: 0 0 auto; |
| 552 | transition: transform 0.18s ease, opacity 0.18s ease; |
| 553 | opacity: 0.72; |
| 554 | } |
| 555 | |
| 556 | .tg-advanced[open] .tg-advanced-chevron { |
| 557 | transform: rotate(180deg); |
| 558 | opacity: 1; |
| 559 | } |
| 560 | |
| 561 | .tg-advanced-body { |
| 562 | padding: 1rem; |
| 563 | display: flex; |
| 564 | flex-direction: column; |
| 565 | gap: 0.2rem; |
| 566 | } |
| 567 | |
| 568 | .tg-test-panel { |
| 569 | margin-top: 1rem; |
| 570 | padding: var(--spacing-xs) 0; |
| 571 | display: flex; |
| 572 | justify-content: space-between; |
| 573 | align-items: center; |
| 574 | gap: 1rem; |
| 575 | } |
| 576 | |
| 577 | .tg-test-copy, |
| 578 | .tg-result-copy { |
| 579 | min-width: 0; |
| 580 | } |
| 581 | |
| 582 | .tg-results { |
| 583 | margin-top: 0.9rem; |
| 584 | border-radius: 14px; |
| 585 | border: 1px solid color-mix(in srgb, #22c55e 28%, var(--color-border) 72%); |
| 586 | background: color-mix(in srgb, #22c55e 8%, var(--color-background) 92%); |
| 587 | padding: 0.35rem 0.9rem; |
| 588 | } |
| 589 | |
| 590 | .tg-results.is-error { |
| 591 | border-color: color-mix(in srgb, #ef4444 28%, var(--color-border) 72%); |
| 592 | background: color-mix(in srgb, #ef4444 8%, var(--color-background) 92%); |
| 593 | } |
| 594 | |
| 595 | .tg-result-row { |
| 596 | display: flex; |
| 597 | align-items: flex-start; |
| 598 | gap: 0.8rem; |
| 599 | padding: 0.7rem 0; |
| 600 | } |
| 601 | |
| 602 | .tg-result-row+.tg-result-row { |
| 603 | border-top: 1px solid color-mix(in srgb, var(--color-border) 85%, white 15%); |
| 604 | } |
| 605 | |
| 606 | .tg-result-icon { |
| 607 | width: 1.25rem; |
| 608 | font-weight: 800; |
| 609 | line-height: 1.3; |
| 610 | } |
| 611 | |
| 612 | .tg-result-title { |
| 613 | font-weight: 700; |
| 614 | margin-bottom: 0.18rem; |
| 615 | } |
| 616 | |
| 617 | .tg-status-pill { |
| 618 | display: inline-flex; |
| 619 | align-items: center; |
| 620 | justify-content: center; |
| 621 | min-width: 5.25rem; |
| 622 | padding: 0.35rem 0.7rem; |
| 623 | border-radius: 999px; |
| 624 | font-size: 0.8rem; |
| 625 | font-weight: 700; |
| 626 | } |
| 627 | |
| 628 | .tg-status-pill.tone-success { |
| 629 | background: rgba(34, 197, 94, 0.14); |
| 630 | color: #7ee7a4; |
| 631 | } |
| 632 | |
| 633 | .tg-status-pill.tone-ready { |
| 634 | background: rgba(59, 130, 246, 0.16); |
| 635 | color: #93c5fd; |
| 636 | } |
| 637 | |
| 638 | .tg-status-pill.tone-warning { |
| 639 | background: rgba(245, 158, 11, 0.16); |
| 640 | color: #fcd34d; |
| 641 | } |
| 642 | |
| 643 | .tg-status-pill.tone-muted { |
| 644 | background: rgba(148, 163, 184, 0.14); |
| 645 | color: #cbd5e1; |
| 646 | } |
| 647 | |
| 648 | .tg-add-btn { |
| 649 | align-self: flex-start; |
| 650 | } |
| 651 | |
| 652 | @media (max-width: 900px) { |
| 653 | .tg-token-row { |
| 654 | grid-template-columns: minmax(0, 1fr); |
| 655 | } |
| 656 | } |
| 657 | |
| 658 | @media (max-width: 640px) { |
| 659 | |
| 660 | .tg-card-header, |
| 661 | .tg-step-header, |
| 662 | .tg-test-panel { |
| 663 | flex-direction: column; |
| 664 | align-items: stretch; |
| 665 | } |
| 666 | |
| 667 | .tg-card-actions, |
| 668 | .tg-step-dots { |
| 669 | width: 100%; |
| 670 | justify-content: space-between; |
| 671 | } |
| 672 | |
| 673 | .tg-status-pill { |
| 674 | min-width: 0; |
| 675 | } |
| 676 | } |
| 677 | </style> |
| 678 | </body> |
| 679 | |
| 680 | </html> |