main
html 242 lines 7.6 KB
Raw
1 <html>
2
3 <head>
4 <title>Skill Scanner</title>
5
6 <script type="module">
7 import { store } from "/components/settings/skills/skills-scan-store.js";
8 </script>
9 </head>
10
11 <body>
12 <div x-data>
13 <template x-if="$store.skillsScanStore">
14 <div class="skill-scan-modal" x-create="$store.skillsScanStore.onScanModalOpen()" x-destroy="$store.skillsScanStore.scanCleanup()">
15 <div class="scan-field">
16 <label>Target Skills</label>
17 <textarea class="scan-target" x-model="$store.skillsScanStore.targetText" @input.debounce.300ms="$store.skillsScanStore.buildScanPrompt()"></textarea>
18 </div>
19
20 <div class="scan-field">
21 <label>Security Checks</label>
22 <div class="scan-checks">
23 <template x-for="[key, meta] of Object.entries($store.skillsScanStore.scanChecksMeta)" :key="key">
24 <label>
25 <input type="checkbox" x-model="$store.skillsScanStore.scanChecks[key]" @change="$store.skillsScanStore.buildScanPrompt()" />
26 <span x-text="meta.label"></span>
27 </label>
28 </template>
29 </div>
30 </div>
31
32 <div class="scan-field">
33 <label>Scanner Options</label>
34 <div class="scan-checks">
35 <label>
36 <input type="checkbox" x-model="$store.skillsScanStore.scanOptions.useSnykAgentScan" @change="$store.skillsScanStore.buildScanPrompt()" />
37 <span>Use Snyk Agent Scan</span>
38 </label>
39 </div>
40 </div>
41
42 <div class="scan-field">
43 <label>Agent Prompt <span>(editable)</span></label>
44 <textarea class="scan-prompt" x-model="$store.skillsScanStore.scanPrompt"></textarea>
45 </div>
46
47 <div class="scan-actions">
48 <button type="button" class="button" @click="$store.skillsScanStore.copyScanPrompt()">
49 <x-icon aria-hidden="true" name="content_copy"></x-icon>
50 <span>Copy Prompt</span>
51 </button>
52 <button type="button" class="button confirm" :disabled="$store.skillsScanStore.agentScanning" @click="$store.skillsScanStore.runAgentScan()">
53 <span x-show="$store.skillsScanStore.agentScanning"><span class="scan-spinner"></span>Scanning</span>
54 <span x-show="!$store.skillsScanStore.agentScanning">Run Scan</span>
55 </button>
56 <button type="button" class="button" x-show="$store.skillsScanStore.scanCtxId" @click="$store.skillsScanStore.openScanChatInNewWindow()">
57 <x-icon aria-hidden="true" name="open_in_new"></x-icon>
58 <span>Open in Chat</span>
59 </button>
60 </div>
61
62 <div x-show="$store.skillsScanStore.scanOutput" class="scan-output">
63 <label>Scan Results</label>
64 <div class="scan-output-html" x-html="$store.skillsScanStore.renderedScanOutput"></div>
65 </div>
66 </div>
67 </template>
68 </div>
69
70 <style>
71 .skill-scan-modal {
72 display: flex;
73 flex-direction: column;
74 gap: 1rem;
75 padding: 0.5rem;
76 color: var(--color-text);
77 font-family: var(--font-family-main);
78 }
79
80 .scan-field {
81 display: flex;
82 flex-direction: column;
83 gap: 0.35rem;
84 }
85
86 .scan-field label,
87 .scan-output label {
88 color: var(--color-text-muted);
89 font-size: 0.85rem;
90 font-weight: 600;
91 }
92
93 .scan-field > label span {
94 font-weight: 400;
95 opacity: 0.7;
96 }
97
98 .scan-field textarea {
99 width: 100%;
100 box-sizing: border-box;
101 border: 1px solid var(--color-border);
102 border-radius: 6px;
103 background: var(--color-panel);
104 color: var(--color-text);
105 padding: 0.5rem 0.75rem;
106 font-family: var(--font-family-code);
107 font-size: 0.82rem;
108 line-height: 1.45;
109 resize: vertical;
110 }
111
112 .scan-field textarea:focus {
113 border-color: var(--color-highlight);
114 outline: none;
115 }
116
117 .scan-target {
118 min-height: 6rem;
119 }
120
121 .scan-prompt {
122 min-height: 18rem;
123 }
124
125 .scan-checks {
126 display: flex;
127 flex-wrap: wrap;
128 gap: 0.5rem 1.25rem;
129 }
130
131 .scan-checks label {
132 display: inline-flex;
133 align-items: center;
134 gap: 0.35rem;
135 color: var(--color-text);
136 cursor: pointer;
137 font-size: 0.85rem;
138 user-select: none;
139 }
140
141 .scan-checks input[type="checkbox"] {
142 accent-color: var(--color-highlight);
143 }
144
145 .scan-actions {
146 display: flex;
147 flex-wrap: wrap;
148 gap: 0.5rem;
149 }
150
151 .skill-scan-modal .button {
152 display: inline-flex;
153 align-items: center;
154 justify-content: center;
155 gap: 0.35rem;
156 min-height: 2rem;
157 padding: 0.35rem 0.75rem;
158 border: 1px solid var(--color-border);
159 border-radius: 6px;
160 background: var(--color-panel);
161 color: var(--color-text);
162 cursor: pointer;
163 }
164
165 .skill-scan-modal .button.confirm {
166 border-color: color-mix(in srgb, var(--color-highlight) 65%, var(--color-border));
167 background: var(--color-highlight);
168 color: #fff;
169 }
170
171 .skill-scan-modal .button:disabled {
172 opacity: 0.55;
173 cursor: not-allowed;
174 }
175
176 .skill-scan-modal .material-symbols-outlined {
177 font-size: 1.05rem;
178 line-height: 1;
179 }
180
181 .scan-output {
182 border-top: 1px solid var(--color-border);
183 padding-top: 1rem;
184 }
185
186 .scan-output-html {
187 line-height: 1.5;
188 }
189
190 .scan-output-html table {
191 width: 100%;
192 margin: 0.75rem 0;
193 border-collapse: collapse;
194 }
195
196 .scan-output-html th,
197 .scan-output-html td {
198 border: 1px solid var(--color-border);
199 padding: 0.4rem 0.6rem;
200 text-align: left;
201 font-size: 0.85rem;
202 }
203
204 .scan-output-html th {
205 background: var(--color-panel);
206 font-weight: 600;
207 }
208
209 .scan-output-html pre {
210 overflow-x: auto;
211 border: 1px solid var(--color-border);
212 border-radius: 6px;
213 background: var(--color-panel);
214 padding: 0.75rem;
215 }
216
217 .scan-output-html code {
218 font-size: 0.8rem;
219 }
220
221 .scan-spinner {
222 display: inline-block;
223 width: 1em;
224 height: 1em;
225 margin-right: 0.4em;
226 border: 2px solid var(--color-border);
227 border-top-color: currentColor;
228 border-radius: 50%;
229 vertical-align: middle;
230 animation: scan-spin 0.6s linear infinite;
231 }
232
233 @keyframes scan-spin {
234 to {
235 transform: rotate(360deg);
236 }
237 }
238 </style>
239
240 </body>
241
242 </html>