main
html 227 lines 8.47 KB
Raw
1 <html>
2 <head>
3 <title>Import Skills</title>
4 <script type="module">
5 import { store } from "/components/settings/skills/skills-import-store.js";
6 </script>
7 </head>
8 <body>
9 <div x-data>
10 <template x-if="$store.skillsImportStore">
11 <div x-init="$store.skillsImportStore.init()" x-destroy="$store.skillsImportStore.onClose()">
12
13 <div class="section-title">Import Skills</div>
14 <div class="section-description">
15 Add a zipped skill pack that contains one or more <code>SKILL.md</code> folders.
16 </div>
17
18 <div class="upload-section">
19 <label for="skills-file" class="upload-label">
20 Select Skills Pack (.zip)
21 </label>
22 <input type="file" id="skills-file" accept=".zip"
23 @change="$store.skillsImportStore.handleFileUpload($event)">
24 <div class="upload-hint">
25 Upload a repository/archive that contains SKILL.md skill folders (see agentskills.io).
26 </div>
27 </div>
28
29 <div class="options" x-show="$store.skillsImportStore.skillsFile">
30 <label class="policy-label">
31 <span class="policy-label-text">Limit to project:</span>
32 <select x-model="$store.skillsImportStore.projectKey" class="policy-dropdown"
33 @change="$store.skillsImportStore.previewImport()">
34 <option value="">All</option>
35 <template x-for="project in $store.skillsImportStore.projects" :key="project.key">
36 <option :value="project.key" x-text="project.label"></option>
37 </template>
38 </select>
39 </label>
40
41 <label class="policy-label">
42 <span class="policy-label-text">Namespace:</span>
43 <input class="text-input" type="text" placeholder="e.g. my-pack"
44 x-model="$store.skillsImportStore.namespace"
45 @change="$store.skillsImportStore.previewImport()">
46 </label>
47
48 <label class="policy-label">
49 <span class="policy-label-text">Conflict policy:</span>
50 <select x-model="$store.skillsImportStore.conflict" class="policy-dropdown"
51 @change="$store.skillsImportStore.previewImport()">
52 <option value="skip">Skip existing</option>
53 <option value="rename">Rename (add _2, _3...)</option>
54 <option value="overwrite">Overwrite existing</option>
55 </select>
56 </label>
57
58 <div class="buttons">
59 <button class="btn slim" @click="$store.skillsImportStore.scanSelectedFile()"
60 :disabled="$store.skillsImportStore.loading || $store.skillsScanStore?.preparingUpload">Scan Skills</button>
61 <button class="btn slim" @click="$store.skillsImportStore.previewImport()"
62 :disabled="$store.skillsImportStore.loading">Preview</button>
63 <button class="btn slim primary" @click="$store.skillsImportStore.performImport()"
64 :disabled="$store.skillsImportStore.loading">Import</button>
65 </div>
66 </div>
67
68 <div x-show="$store.skillsImportStore.loading" class="loading">
69 <span x-text="$store.skillsImportStore.loadingMessage || 'Processing...'"></span>
70 </div>
71
72 <div x-show="$store.skillsImportStore.error" class="error">
73 <span x-text="$store.skillsImportStore.error"></span>
74 </div>
75
76 <div x-show="$store.skillsImportStore.preview" class="preview">
77 <h4>Preview</h4>
78 <div class="preview-meta">
79 <div>Namespace: <code x-text="$store.skillsImportStore.preview?.namespace"></code></div>
80 <div>Would import: <span x-text="$store.skillsImportStore.preview?.imported_count || 0"></span></div>
81 <div>Would skip: <span x-text="$store.skillsImportStore.preview?.skipped_count || 0"></span></div>
82 </div>
83
84 <textarea class="preview-list" readonly
85 x-text="($store.skillsImportStore.preview?.imported || []).join('\n')"></textarea>
86 </div>
87
88 <div x-show="$store.skillsImportStore.result" class="result">
89 <h4>Import Complete</h4>
90 <div class="preview-meta">
91 <div>Imported: <span x-text="$store.skillsImportStore.result?.imported_count || 0"></span></div>
92 <div>Skipped: <span x-text="$store.skillsImportStore.result?.skipped_count || 0"></span></div>
93 </div>
94 <div class="note">
95 Skills are indexed automatically. If you don’t see them immediately, use the Restart button in the left pane.
96 </div>
97 </div>
98
99 </div>
100 </template>
101 </div>
102
103 <style>
104 .upload-section {
105 margin-bottom: 1rem;
106 padding: 1rem;
107 border: 2px dashed var(--color-border);
108 border-radius: 4px;
109 text-align: center;
110 }
111
112 .upload-label {
113 display: block;
114 margin-bottom: 0.5rem;
115 font-weight: 600;
116 }
117
118 .upload-hint {
119 margin-top: 0.5rem;
120 font-size: 0.85rem;
121 color: var(--color-secondary);
122 }
123
124 .options {
125 margin: 1rem 0;
126 padding: 0.75rem;
127 background: var(--color-input);
128 border: 1px solid var(--color-border);
129 border-radius: 4px;
130 }
131
132 .policy-label {
133 display: grid;
134 grid-template-columns: minmax(10rem, 14rem) 1fr;
135 align-items: center;
136 column-gap: 0.75rem;
137 row-gap: 0.25rem;
138 margin: 0.5rem 0;
139 }
140
141 .policy-label-text {
142 font-weight: 600;
143 white-space: normal;
144 width: auto;
145 }
146
147 .policy-dropdown, .text-input {
148 flex: 1;
149 padding: 0.5rem;
150 border: 1px solid var(--color-border);
151 border-radius: 4px;
152 background: var(--color-bg-primary);
153 color: var(--color-text-primary);
154 font-size: 0.9rem;
155 }
156
157 .buttons {
158 margin-top: 0.75rem;
159 display: flex;
160 gap: 0.5rem;
161 }
162
163 .loading {
164 width: 100%;
165 text-align: center;
166 margin-top: 1rem;
167 margin-bottom: 1rem;
168 color: var(--color-secondary);
169 }
170
171 .error {
172 color: var(--color-error);
173 margin: 0.5rem 0;
174 padding: 0.5rem;
175 background: var(--color-error-bg);
176 border-radius: 4px;
177 }
178
179 .preview, .result {
180 margin-top: 1rem;
181 padding: 0.75rem;
182 background: var(--color-bg-primary);
183 border: 1px solid var(--color-border);
184 border-radius: 4px;
185 }
186
187 .preview-meta {
188 display: grid;
189 grid-template-columns: 1fr 1fr;
190 gap: 0.25rem 1rem;
191 margin-bottom: 0.5rem;
192 color: var(--color-text-secondary);
193 font-size: 0.9rem;
194 }
195
196 .preview-list {
197 width: 100%;
198 height: 12em;
199 font-family: monospace;
200 font-size: 0.85em;
201 background: var(--color-bg-primary);
202 color: var(--color-text-primary);
203 border: 1px solid var(--color-border);
204 border-radius: 4px;
205 padding: 0.5em;
206 resize: vertical;
207 }
208
209 .note {
210 margin-top: 0.5rem;
211 font-size: 0.9rem;
212 color: var(--color-text-secondary);
213 }
214
215 @media (max-width: 700px) {
216 .policy-label {
217 grid-template-columns: 1fr;
218 }
219
220 .policy-label-text {
221 width: auto;
222 white-space: normal;
223 }
224 }
225 </style>
226 </body>
227 </html>