| 1 | <html> |
| 2 | <head> |
| 3 | <title>Plugin Scanner</title> |
| 4 | <script type="module"> |
| 5 | import { store } from "/plugins/_plugin_scan/webui/plugin-scan-store.js"; |
| 6 | </script> |
| 7 | </head> |
| 8 | <body> |
| 9 | <div x-data> |
| 10 | <template x-if="$store.pluginScan"> |
| 11 | <div x-create="$store.pluginScan.onOpen()" x-destroy="$store.pluginScan.cleanup()" class="plugin-scan"> |
| 12 | |
| 13 | <!-- Git URL --> |
| 14 | <div class="scan-field"> |
| 15 | <label>Git Repository URL</label> |
| 16 | <input type="text" x-model="$store.pluginScan.gitUrl" |
| 17 | @input.debounce.300ms="$store.pluginScan.buildPrompt()" |
| 18 | placeholder="https://github.com/user/plugin-repo.git" /> |
| 19 | </div> |
| 20 | |
| 21 | <!-- Checks --> |
| 22 | <div class="scan-field"> |
| 23 | <label>Security Checks</label> |
| 24 | <div class="scan-checks"> |
| 25 | <template x-for="[key, meta] of Object.entries($store.pluginScan.checksMeta)" :key="key"> |
| 26 | <label> |
| 27 | <input type="checkbox" x-model="$store.pluginScan.checks[key]" |
| 28 | @change="$store.pluginScan.buildPrompt()" /> |
| 29 | <span x-text="meta.label"></span> |
| 30 | </label> |
| 31 | </template> |
| 32 | </div> |
| 33 | </div> |
| 34 | |
| 35 | <!-- Prompt --> |
| 36 | <div class="scan-field"> |
| 37 | <label>Agent Prompt <span style="font-weight:400; opacity:0.6">(editable)</span></label> |
| 38 | <textarea x-model="$store.pluginScan.prompt"></textarea> |
| 39 | </div> |
| 40 | <!-- Actions --> |
| 41 | <div class="scan-actions"> |
| 42 | <button class="button" @click="$store.pluginScan.copyPrompt()">Copy Prompt</button> |
| 43 | <button class="button confirm" @click="$store.pluginScan.runScan()"> |
| 44 | <span x-show="$store.pluginScan.scanning"><span class="scan-spinner"></span>Run Another Scan</span> |
| 45 | <span x-show="!$store.pluginScan.scanning">Run Scan</span> |
| 46 | </button> |
| 47 | <button class="button" @click="$store.pluginScan.openChatInNewWindow()" |
| 48 | x-show="$store.pluginScan.scanCtxId" |
| 49 | title="Open this scan's chat in a new tab"> |
| 50 | Open in Chat ↗ |
| 51 | </button> |
| 52 | </div> |
| 53 | |
| 54 | <!-- Output --> |
| 55 | <div x-show="$store.pluginScan.output" class="scan-output"> |
| 56 | <label style="font-size:0.85rem; font-weight:600; opacity:0.8;">Scan Results</label> |
| 57 | <div class="scan-output-html" x-html="$store.pluginScan.renderedOutput"></div> |
| 58 | </div> |
| 59 | |
| 60 | </div> |
| 61 | </template> |
| 62 | </div> |
| 63 | |
| 64 | <style> |
| 65 | .plugin-scan { display: flex; flex-direction: column; gap: 1rem; padding: 0.5rem; } |
| 66 | |
| 67 | .scan-field { display: flex; flex-direction: column; gap: 0.35rem; } |
| 68 | .scan-field label { font-size: 0.85rem; font-weight: 600; opacity: 0.8; } |
| 69 | .scan-field input[type="text"], |
| 70 | .scan-field textarea { |
| 71 | width: 100%; |
| 72 | border: 1px solid var(--color-border); |
| 73 | border-radius: 6px; |
| 74 | padding: 0.5rem 0.75rem; |
| 75 | font-family: inherit; |
| 76 | font-size: 0.875rem; |
| 77 | background: var(--color-panel); |
| 78 | color: var(--color-text); |
| 79 | box-sizing: border-box; |
| 80 | } |
| 81 | .scan-field textarea { min-height: 15rem; resize: none; font-family: monospace; font-size: 0.8rem; } |
| 82 | .scan-field input:focus, |
| 83 | .scan-field textarea:focus { outline: none; border-color: var(--color-primary); } |
| 84 | |
| 85 | .scan-checks { display: flex; flex-wrap: wrap; gap: 0.5rem 1.25rem; } |
| 86 | .scan-checks label { display: flex; align-items: center; gap: 0.35rem; font-size: 0.85rem; cursor: pointer; user-select: none; } |
| 87 | .scan-checks input[type="checkbox"] { accent-color: var(--color-primary); } |
| 88 | |
| 89 | .scan-actions { display: flex; gap: 0.5rem; flex-wrap: wrap; } |
| 90 | |
| 91 | .scan-output { border-top: 1px solid var(--color-border); padding-top: 1rem; } |
| 92 | .scan-output-html { line-height: 1.5; } |
| 93 | .scan-output-html table { border-collapse: collapse; width: 100%; margin: 0.75rem 0; } |
| 94 | .scan-output-html th, |
| 95 | .scan-output-html td { border: 1px solid var(--color-border); padding: 0.4rem 0.6rem; text-align: left; font-size: 0.85rem; } |
| 96 | .scan-output-html th { background: var(--color-panel); font-weight: 600; } |
| 97 | .scan-output-html hr { border: 1px solid var(--color-border); } |
| 98 | .scan-output-html pre { background: var(--color-panel); border: 1px solid var(--color-border); border-radius: 6px; padding: 0.75rem; overflow-x: auto; } |
| 99 | .scan-output-html code { font-size: 0.8rem; } |
| 100 | .scan-spinner { display: inline-block; width: 1em; height: 1em; border: 2px solid var(--color-border); |
| 101 | border-top-color: var(--color-primary); border-radius: 50%; animation: scan-spin 0.6s linear infinite; vertical-align: middle; margin-right: 0.4em; } |
| 102 | @keyframes scan-spin { to { transform: rotate(360deg); } } |
| 103 | </style> |
| 104 | </body> |
| 105 | </html> |