main
html 298 lines 9.97 KB
Raw
1 <html>
2 <head>
3 <title>List Skills</title>
4 <script type="module">
5 import { store } from "/components/settings/skills/skills-list-store.js";
6 </script>
7 </head>
8 <body>
9 <div x-data>
10 <template x-if="$store.skillsListStore">
11 <div x-init="$store.skillsListStore.init()" x-destroy="$store.skillsListStore.onClose()">
12
13 <div class="section-title">List Skills</div>
14 <div class="section-description">
15 Browse skills across global and project scopes. Duplicates are shown individually.
16 </div>
17
18 <div class="skills-toolbar">
19 <div class="skills-toolbar-row">
20 <label class="skills-toolbar-item">
21 <span class="skills-toolbar-label">Project</span>
22 <select x-model="$store.skillsListStore.projectName"
23 @change="$store.skillsListStore.loadSkills()">
24 <option value="">All</option>
25 <template x-for="project in $store.skillsListStore.projects" :key="project.key">
26 <option :value="project.key" x-text="project.label"></option>
27 </template>
28 </select>
29 </label>
30
31 <label class="skills-search">
32 <x-icon aria-hidden="true" name="search"></x-icon>
33 <input type="search" x-model.debounce.150ms="$store.skillsListStore.skillSearch"
34 placeholder="Search skills" />
35 <button type="button" class="skills-search-clear"
36 x-show="$store.skillsListStore.skillSearchActive"
37 @click="$store.skillsListStore.clearSkillSearch()"
38 title="Clear search">
39 <x-icon aria-hidden="true" name="close"></x-icon>
40 </button>
41 </label>
42
43 <div class="skills-toolbar-actions">
44 <button type="button" class="button confirm" title="Refresh"
45 @click="$store.skillsListStore.loadSkills()"
46 :disabled="$store.skillsListStore.loading">
47 <x-icon class="icon" name="refresh"></x-icon> Refresh
48 </button>
49 </div>
50 </div>
51 </div>
52
53 <div x-show="$store.skillsListStore.loading" class="loading">
54 <span>Loading skills...</span>
55 </div>
56
57 <div x-show="$store.skillsListStore.error" class="error">
58 <span x-text="$store.skillsListStore.error"></span>
59 </div>
60
61 <template
62 x-if="!$store.skillsListStore.loading && !$store.skillsListStore.error && $store.skillsListStore.skills.length === 0">
63 <div class="skills-empty">No skills found.</div>
64 </template>
65
66 <template
67 x-if="!$store.skillsListStore.loading && !$store.skillsListStore.error && $store.skillsListStore.skills.length > 0 && $store.skillsListStore.filteredSkills.length === 0">
68 <div class="skills-empty">No skills match this search.</div>
69 </template>
70
71 <div class="skills-list">
72 <template x-for="skill in $store.skillsListStore.filteredSkills" :key="skill.path">
73 <div class="skill-card">
74 <div class="skill-header">
75 <div class="skill-heading">
76 <div class="skill-title" x-text="skill.name || '(unnamed skill)'"></div>
77 <code class="skill-path" x-text="skill.path"></code>
78 </div>
79 <div class="skill-actions">
80 <button type="button" class="button" title="Open in browser"
81 @click="$store.skillsListStore.openSkill(skill)">
82 <x-icon class="icon" name="folder_open"></x-icon> Open
83 </button>
84 <button type="button" class="button cancel icon-button" title="Delete"
85 @click="$confirmClick($event, () => $store.skillsListStore.deleteSkill(skill))">
86 <x-icon class="icon" name="delete"></x-icon>
87 </button>
88 </div>
89 </div>
90 <div class="skill-description" x-text="skill.description || 'No description provided.'"></div>
91 </div>
92 </template>
93 </div>
94
95 </div>
96 </template>
97 </div>
98
99 <style>
100 .skills-toolbar {
101 display: flex;
102 flex-direction: column;
103 gap: 0.35rem;
104 margin-top: 0.5rem;
105 }
106
107 .skills-toolbar-row {
108 display: flex;
109 align-items: center;
110 gap: 0.75rem;
111 flex-wrap: wrap;
112 }
113
114 .skills-toolbar-item {
115 display: flex;
116 align-items: center;
117 gap: 0.5rem;
118 flex: 1 1 18rem;
119 min-width: 12rem;
120 margin: 0;
121 }
122
123 .skills-toolbar-label {
124 font-weight: 600;
125 color: var(--color-text-secondary);
126 white-space: nowrap;
127 }
128
129 .skills-toolbar-item select {
130 flex: 1 1 auto;
131 min-width: 0;
132 }
133
134 .skills-toolbar-actions {
135 margin-left: auto;
136 flex: 0 0 auto;
137 }
138
139 .skills-search {
140 display: inline-flex;
141 align-items: center;
142 gap: 0.35rem;
143 min-height: 2.1rem;
144 flex: 1 1 18rem;
145 min-width: min(18rem, 100%);
146 padding: 0.25rem 0.45rem;
147 border: 1px solid var(--color-border);
148 border-radius: 7px;
149 background: var(--color-input);
150 color: var(--color-text-muted);
151 }
152
153 .skills-search input {
154 width: 100%;
155 min-width: 8rem;
156 border: 0;
157 background: transparent;
158 color: var(--color-text);
159 outline: none;
160 padding: 0.2rem;
161 }
162
163 .skills-search input::-webkit-search-cancel-button,
164 .skills-search input::-webkit-search-decoration {
165 -webkit-appearance: none;
166 appearance: none;
167 display: none;
168 }
169
170 .skills-search-clear {
171 display: inline-flex;
172 align-items: center;
173 justify-content: center;
174 width: 1.45rem;
175 height: 1.45rem;
176 border: 0;
177 border-radius: 5px;
178 background: transparent;
179 color: var(--color-text-muted);
180 cursor: pointer;
181 }
182
183 @media (max-width: 640px) {
184 .skills-toolbar-row {
185 gap: 0.5rem;
186 }
187
188 .skills-toolbar-item {
189 flex-basis: 100%;
190 min-width: 0;
191 }
192
193 .skills-search {
194 flex-basis: 100%;
195 min-width: 0;
196 }
197
198 .skills-toolbar-actions {
199 width: 100%;
200 margin-left: 0;
201 }
202
203 .skills-toolbar-actions .button {
204 width: 100%;
205 }
206 }
207
208 .skills-list {
209 margin-top: 1rem;
210 }
211
212 .skill-card {
213 border: 1px solid var(--color-border);
214 border-radius: 4px;
215 padding: 0.75rem;
216 margin-top: 0.75rem;
217 background: var(--color-bg-primary);
218 }
219
220 .skill-header {
221 display: flex;
222 align-items: flex-start;
223 justify-content: space-between;
224 gap: 1rem;
225 flex-wrap: nowrap;
226 min-width: 0;
227 }
228
229 .skill-heading {
230 display: flex;
231 flex-direction: column;
232 min-width: 0;
233 flex: 1 1 auto;
234 }
235
236 .skill-title {
237 font-weight: 600;
238 font-size: 1rem;
239 min-width: 0;
240 flex: 1 1 auto;
241 }
242
243 .skill-actions {
244 display: flex;
245 align-items: center;
246 gap: 0.5rem;
247 padding-bottom: var(--spacing-sm);
248 flex-wrap: wrap;
249 min-width: 0;
250 flex: 0 1 11.5rem;
251 max-width: 100%;
252 justify-content: flex-end;
253 }
254
255 .skill-description {
256 margin-top: 0.35rem;
257 color: var(--color-text-secondary);
258 font-size: var(--font-size-small);
259 }
260
261 .skill-path {
262 margin-top: 0.35rem;
263 font-size: 0.85rem;
264 color: var(--color-text-muted);
265 min-width: 0;
266 max-width: 100%;
267 white-space: pre-wrap;
268 word-break: break-word;
269 overflow-wrap: anywhere;
270 }
271
272 .skills-empty {
273 margin-top: 1rem;
274 padding: 0.75rem;
275 border: 1px dashed var(--color-border);
276 border-radius: 4px;
277 text-align: center;
278 color: var(--color-text-secondary);
279 }
280
281 .loading {
282 width: 100%;
283 text-align: center;
284 margin-top: 1rem;
285 margin-bottom: 1rem;
286 color: var(--color-secondary);
287 }
288
289 .error {
290 color: var(--color-error);
291 margin: 0.5rem 0;
292 padding: 0.5rem;
293 background: var(--color-error-bg);
294 border-radius: 4px;
295 }
296 </style>
297 </body>
298 </html>