trackops 2.0.2 → 2.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/README.md +238 -0
  2. package/lib/init.js +2 -2
  3. package/lib/locale.js +41 -17
  4. package/lib/opera-bootstrap.js +68 -7
  5. package/lib/opera.js +10 -2
  6. package/lib/registry.js +18 -0
  7. package/lib/server.js +312 -207
  8. package/locales/en.json +4 -0
  9. package/locales/es.json +4 -0
  10. package/package.json +1 -1
  11. package/skills/trackops/SKILL.md +39 -4
  12. package/skills/trackops/agents/openai.yaml +2 -2
  13. package/skills/trackops/locales/en/SKILL.md +39 -4
  14. package/skills/trackops/locales/en/references/activation.md +15 -0
  15. package/skills/trackops/locales/en/references/troubleshooting.md +12 -0
  16. package/skills/trackops/references/activation.md +15 -0
  17. package/skills/trackops/references/troubleshooting.md +12 -0
  18. package/skills/trackops/skill.json +4 -4
  19. package/ui/css/base.css +19 -1
  20. package/ui/css/charts.css +106 -8
  21. package/ui/css/components.css +554 -17
  22. package/ui/css/onboarding.css +133 -0
  23. package/ui/css/panels.css +345 -406
  24. package/ui/css/terminal.css +125 -0
  25. package/ui/css/timeline.css +58 -0
  26. package/ui/css/tokens.css +170 -113
  27. package/ui/index.html +3 -0
  28. package/ui/js/api.js +49 -13
  29. package/ui/js/app.js +28 -32
  30. package/ui/js/charts.js +526 -0
  31. package/ui/js/filters.js +247 -0
  32. package/ui/js/icons.js +82 -57
  33. package/ui/js/keyboard.js +229 -0
  34. package/ui/js/onboarding.js +33 -42
  35. package/ui/js/router.js +20 -3
  36. package/ui/js/views/board.js +84 -114
  37. package/ui/js/views/dashboard.js +870 -0
  38. package/ui/js/views/projects.js +745 -0
  39. package/ui/js/views/scrum.js +476 -0
  40. package/ui/js/views/settings.js +197 -247
  41. package/ui/js/views/sidebar.js +37 -31
  42. package/ui/js/views/tasks.js +218 -101
  43. package/ui/js/views/timeline.js +265 -0
  44. package/ui/js/views/topbar.js +94 -107
  45. package/ui/app.js +0 -950
  46. package/ui/js/views/insights.js +0 -340
  47. package/ui/js/views/overview.js +0 -369
  48. package/ui/styles.css +0 -688
@@ -0,0 +1,247 @@
1
+ /**
2
+ * filters.js — Filtros persistentes para vistas de tareas
3
+ * Persiste en localStorage + sincroniza con state.
4
+ */
5
+
6
+ import * as state from './state.js';
7
+ import { esc } from './utils.js';
8
+ import { icon } from './icons.js';
9
+ import { t } from './i18n.js';
10
+
11
+ const STORAGE_PREFIX = 'ops-filters-';
12
+
13
+ /** Claves de filtro soportadas */
14
+ const FILTER_KEYS = ['status', 'priority', 'phase', 'stream', 'search'];
15
+
16
+ // ─────────────────────────────── PERSISTENCE ─────────────────────────────────
17
+
18
+ /**
19
+ * Guardar filtros de una vista en localStorage y state
20
+ * @param {string} viewId — ID de la vista (board, tasks)
21
+ * @param {Object} filters — Mapa de filtros activos
22
+ */
23
+ export function save(viewId, filters) {
24
+ // Limpiar filtros vacios
25
+ const clean = {};
26
+ for (const key of FILTER_KEYS) {
27
+ if (filters[key]) clean[key] = filters[key];
28
+ }
29
+ localStorage.setItem(`${STORAGE_PREFIX}${viewId}`, JSON.stringify(clean));
30
+ state.update('activeFilters', clean);
31
+ }
32
+
33
+ /**
34
+ * Cargar filtros guardados de una vista
35
+ * @param {string} viewId
36
+ * @returns {Object} Filtros guardados o {}
37
+ */
38
+ export function load(viewId) {
39
+ try {
40
+ const raw = localStorage.getItem(`${STORAGE_PREFIX}${viewId}`);
41
+ return raw ? JSON.parse(raw) : {};
42
+ } catch {
43
+ return {};
44
+ }
45
+ }
46
+
47
+ /**
48
+ * Limpiar filtros de una vista
49
+ * @param {string} viewId
50
+ */
51
+ export function clear(viewId) {
52
+ localStorage.removeItem(`${STORAGE_PREFIX}${viewId}`);
53
+ state.update('activeFilters', {});
54
+ }
55
+
56
+ // ─────────────────────────────── APLICAR ─────────────────────────────────────
57
+
58
+ /**
59
+ * Aplicar filtros a una lista de tareas
60
+ * @param {Array} tasks — Lista de tareas
61
+ * @param {Object} filters — Filtros activos
62
+ * @returns {Array} Tareas filtradas
63
+ */
64
+ export function apply(tasks, filters) {
65
+ if (!filters || Object.keys(filters).length === 0) return tasks;
66
+
67
+ return tasks.filter(task => {
68
+ if (filters.status && task.status !== filters.status) return false;
69
+ if (filters.priority && task.priority !== filters.priority) return false;
70
+ if (filters.phase && task.phase !== filters.phase) return false;
71
+ if (filters.stream && task.stream !== filters.stream) return false;
72
+ if (filters.search) {
73
+ const q = filters.search.toLowerCase();
74
+ const haystack = `${task.title} ${task.id} ${task.summary || ''} ${task.stream || ''}`.toLowerCase();
75
+ if (!haystack.includes(q)) return false;
76
+ }
77
+ return true;
78
+ });
79
+ }
80
+
81
+ // ─────────────────────────────── CONTEO ──────────────────────────────────────
82
+
83
+ /**
84
+ * Contar filtros activos
85
+ * @param {Object} filters
86
+ * @returns {number}
87
+ */
88
+ export function count(filters) {
89
+ if (!filters) return 0;
90
+ return FILTER_KEYS.filter(k => filters[k]).length;
91
+ }
92
+
93
+ // ─────────────────────────────── UI ──────────────────────────────────────────
94
+
95
+ /**
96
+ * Renderizar la barra de filtros (chips + selectores)
97
+ * @param {string} viewId — ID de la vista
98
+ * @param {Object} filters — Filtros actuales
99
+ * @param {Object} options — Opciones de renderizado
100
+ * @param {Array} [options.statuses] — Estados disponibles
101
+ * @param {Array} [options.priorities] — Prioridades disponibles
102
+ * @param {Array} [options.phases] — Fases disponibles
103
+ * @param {Array} [options.streams] — Streams disponibles
104
+ * @returns {string} HTML
105
+ */
106
+ export function renderBar(viewId, filters, options = {}) {
107
+ const statusLabels = state.getStatusLabels?.() || {};
108
+ const phases = options.phases || state.getPhases?.() || [];
109
+ const statuses = options.statuses || ['pending', 'in_progress', 'in_review', 'blocked', 'completed', 'cancelled'];
110
+ const priorities = options.priorities || ['P0', 'P1', 'P2', 'P3'];
111
+ const streams = options.streams || [];
112
+
113
+ return `
114
+ <div class="filter-bar" role="search" aria-label="${t('ui.filters.label', {}, 'Filter tasks')}">
115
+ <div class="filter-controls">
116
+ ${_renderSelect('status', filters.status, statuses.map(s => ({
117
+ value: s, label: statusLabels[s] || s
118
+ })), t('ui.filters.status', {}, 'Status'))}
119
+
120
+ ${_renderSelect('priority', filters.priority, priorities.map(p => ({
121
+ value: p, label: p
122
+ })), t('ui.filters.priority', {}, 'Priority'))}
123
+
124
+ ${phases.length ? _renderSelect('phase', filters.phase, phases.map(p => ({
125
+ value: p.id, label: `${p.id} · ${p.label}`
126
+ })), t('ui.filters.phase', {}, 'Phase')) : ''}
127
+
128
+ ${streams.length ? _renderSelect('stream', filters.stream, streams.map(s => ({
129
+ value: s, label: s
130
+ })), t('ui.filters.stream', {}, 'Stream')) : ''}
131
+
132
+ <div class="filter-search">
133
+ <input
134
+ type="search"
135
+ class="form-input filter-search-input"
136
+ placeholder="${t('ui.filters.searchPlaceholder', {}, 'Search tasks...')}"
137
+ value="${esc(filters.search || '')}"
138
+ data-filter="search"
139
+ aria-label="${t('ui.filters.search', {}, 'Search')}"
140
+ />
141
+ </div>
142
+ </div>
143
+
144
+ ${count(filters) > 0 ? `
145
+ <div class="filter-active-bar">
146
+ ${_renderActiveChips(filters, statusLabels, phases)}
147
+ <button class="btn btn-ghost btn-sm filter-clear-all" type="button" data-filter-action="clear">
148
+ ${icon('x', 14)} ${t('ui.filters.clearAll', {}, 'Clear all')}
149
+ </button>
150
+ </div>
151
+ ` : ''}
152
+ </div>
153
+ `;
154
+ }
155
+
156
+ function _renderSelect(key, current, options, placeholder) {
157
+ return `
158
+ <select class="form-select filter-select" data-filter="${key}" aria-label="${placeholder}">
159
+ <option value="">${esc(placeholder)}</option>
160
+ ${options.map(o => `
161
+ <option value="${esc(o.value)}" ${current === o.value ? 'selected' : ''}>${esc(o.label)}</option>
162
+ `).join('')}
163
+ </select>
164
+ `;
165
+ }
166
+
167
+ function _renderActiveChips(filters, statusLabels, phases) {
168
+ const chips = [];
169
+
170
+ if (filters.status) {
171
+ chips.push({ key: 'status', label: statusLabels[filters.status] || filters.status });
172
+ }
173
+ if (filters.priority) {
174
+ chips.push({ key: 'priority', label: filters.priority });
175
+ }
176
+ if (filters.phase) {
177
+ const phase = phases.find(p => p.id === filters.phase);
178
+ chips.push({ key: 'phase', label: phase ? `${phase.id} · ${phase.label}` : filters.phase });
179
+ }
180
+ if (filters.stream) {
181
+ chips.push({ key: 'stream', label: filters.stream });
182
+ }
183
+ if (filters.search) {
184
+ chips.push({ key: 'search', label: `"${filters.search}"` });
185
+ }
186
+
187
+ return chips.map(c => `
188
+ <span class="chip chip-active" data-filter-remove="${c.key}">
189
+ ${esc(c.label)}
190
+ <button class="chip-remove" type="button" aria-label="Remove ${c.key} filter">${icon('x', 12)}</button>
191
+ </span>
192
+ `).join('');
193
+ }
194
+
195
+ /**
196
+ * Vincular eventos de la barra de filtros
197
+ * @param {string} viewId — ID de la vista
198
+ * @param {Function} onFilter — Callback cuando cambian los filtros
199
+ */
200
+ export function bindBar(viewId, onFilter) {
201
+ const filters = load(viewId);
202
+
203
+ // Selectores
204
+ document.querySelectorAll('.filter-select').forEach(select => {
205
+ select.addEventListener('change', () => {
206
+ const key = select.dataset.filter;
207
+ if (key) {
208
+ filters[key] = select.value || undefined;
209
+ if (!select.value) delete filters[key];
210
+ save(viewId, filters);
211
+ onFilter(filters);
212
+ }
213
+ });
214
+ });
215
+
216
+ // Busqueda con debounce
217
+ const searchInput = document.querySelector('.filter-search-input');
218
+ let searchTimer = null;
219
+ searchInput?.addEventListener('input', () => {
220
+ clearTimeout(searchTimer);
221
+ searchTimer = setTimeout(() => {
222
+ const val = searchInput.value.trim();
223
+ if (val) filters.search = val;
224
+ else delete filters.search;
225
+ save(viewId, filters);
226
+ onFilter(filters);
227
+ }, 300);
228
+ });
229
+
230
+ // Eliminar chip individual
231
+ document.querySelectorAll('[data-filter-remove]').forEach(chip => {
232
+ chip.addEventListener('click', () => {
233
+ const key = chip.dataset.filterRemove;
234
+ delete filters[key];
235
+ save(viewId, filters);
236
+ onFilter(filters);
237
+ });
238
+ });
239
+
240
+ // Limpiar todos
241
+ document.querySelector('.filter-clear-all')?.addEventListener('click', () => {
242
+ clear(viewId);
243
+ onFilter({});
244
+ });
245
+ }
246
+
247
+ export default { save, load, clear, apply, count, renderBar, bindBar };
package/ui/js/icons.js CHANGED
@@ -1,80 +1,105 @@
1
1
  /**
2
- * icons.js — SVG paths de React Icons (Flat / Line) como constantes
2
+ * icons.js — SVG paths de Remix Icon (Fill) como constantes
3
3
  * Stack vanilla: sin React, sin dependencias externas.
4
- * Se usan los paths SVG de heroicons + lucide-style wrappers.
5
- * Uso: icons.render('dashboard', 20) <svg>...</svg>
4
+ * Paths SVG tomados de Remix Icon (fill variants), licencia MIT.
5
+ * viewBox 0 0 24 24, fill="currentColor", sin stroke.
6
+ * Uso: icon('dashboard', 20) → <svg>...</svg>
6
7
  */
7
8
 
8
9
  const PATHS = {
9
10
  // ── Navegación ──
10
- dashboard: `<rect x="3" y="3" width="7" height="7" rx="1"/><rect x="14" y="3" width="7" height="7" rx="1"/><rect x="3" y="14" width="7" height="7" rx="1"/><rect x="14" y="14" width="7" height="7" rx="1"/>`,
11
- tasks: `<path d="M9 5H7a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V7a2 2 0 0 0-2-2h-2"/><rect x="9" y="3" width="6" height="4" rx="1"/><path d="m9 12 2 2 4-4"/>`,
12
- board: `<rect x="3" y="3" width="5" height="18" rx="1"/><rect x="11" y="3" width="5" height="12" rx="1"/><rect x="19" y="3" width="5" height="15" rx="1"/>`, // adjusted
13
- execution: `<polyline points="4 17 10 11 4 5"/><line x1="12" y1="19" x2="20" y2="19"/>`,
14
- insights: `<line x1="18" y1="20" x2="18" y2="10"/><line x1="12" y1="20" x2="12" y2="4"/><line x1="6" y1="20" x2="6" y2="14"/>`,
15
- settings: `<circle cx="12" cy="12" r="3"/><path d="M19.1 6.9a2.83 2.83 0 0 1 0 4L17 13l-1.4-1.4 2.1-2.1a.83.83 0 0 0 0-1.2L16.4 7l-2.1 2.1L13 7.8l2.1-2.1a.83.83 0 0 0-1.2 0L12.7 6l-1.4 1.4L9.2 5.3a2.83 2.83 0 0 1 4-4L15.3 3.4l1.4 1.4 2.1-2.1L19.1 3a2.83 2.83 0 0 1 4 4l-2.1 2.1 1.4 1.4L24.5 8.8a2.83 2.83 0 0 1-4 4l-2.1-2.1-1.4 1.4 2.1 2.1a.83.83 0 0 1 0 1.2L17.4 17l2.1 2.1a.83.83 0 0 1-1.2 0L16.2 17l-1.4 1.4 2.1 2.1a2.83 2.83 0 0 1-4 4l-2.1-2.1-1.4 1.4 2.1 2.1a.83.83 0 0 1 0 1.2L10.7 26l2.1 2.1a.83.83 0 0 1-1.2 0L9.5 26l-1.4-1.4L5.9 26.7a2.83 2.83 0 0 1-4-4L4 20.6l1.4-1.4-2.1-2.1A2.83 2.83 0 0 1 7.3 13l2.1 2.1 1.4-1.4L8.7 11.6A2.83 2.83 0 0 1 13 7.6l2.1 2.1 1.4-1.4L14.4 6.2A2.83 2.83 0 0 1 19.1 6.9Z"/>`,
11
+ dashboard: `<path d="M3 13h8V3H3v10zm0 8h8v-6H3v6zm10 0h8V11h-8v10zm0-18v6h8V3h-8z"/>`,
12
+ tasks: `<path d="M21 2H3c-1.1 0-2 .9-2 2v16c0 1.1.9 2 2 2h18c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm-9 13l-5-5 1.41-1.41L12 12.17l5.59-5.59L19 8l-7 7z"/>`,
13
+ board: `<path d="M3 3h6v18H3V3zm8 0h6v12h-6V3zm8 0h2v15h-2V3z"/>`,
14
+ execution: `<path d="M8.59 16.59L13.17 12 8.59 7.41 10 6l6 6-6 6-1.41-1.41z"/><path d="M13 19h7v2h-7z"/>`,
15
+ insights: `<path d="M5 9.2h3v10.8H5V9.2zm5.6-5.2h3v16h-3V4zm5.6 8h3v8h-3v-8z"/>`,
16
+ settings: `<path d="M19.14 12.94c.04-.31.06-.63.06-.94 0-.31-.02-.63-.06-.94l2.03-1.58a.49.49 0 00.12-.61l-1.92-3.32a.49.49 0 00-.59-.22l-2.39.96a7.03 7.03 0 00-1.62-.94l-.36-2.54a.48.48 0 00-.48-.41h-3.84a.48.48 0 00-.48.41l-.36 2.54c-.59.24-1.13.57-1.62.94l-2.39-.96a.49.49 0 00-.59.22L2.74 8.87a.48.48 0 00.12.61l2.03 1.58c-.04.31-.06.63-.06.94s.02.63.06.94l-2.03 1.58a.49.49 0 00-.12.61l1.92 3.32c.12.22.37.29.59.22l2.39-.96c.5.38 1.03.7 1.62.94l.36 2.54c.05.24.24.41.48.41h3.84c.24 0 .44-.17.48-.41l.36-2.54c.59-.24 1.13-.56 1.62-.94l2.39.96c.22.08.47 0 .59-.22l1.92-3.32c.12-.22.07-.47-.12-.61l-2.01-1.58zM12 15.6A3.6 3.6 0 1112 8.4a3.6 3.6 0 010 7.2z"/>`,
16
17
 
17
18
  // ── General ──
18
- help: `<circle cx="12" cy="12" r="10"/><path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3"/><line x1="12" y1="17" x2="12.01" y2="17"/>`,
19
- logout: `<path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"/><polyline points="16 17 21 12 16 7"/><line x1="21" y1="12" x2="9" y2="12"/>`,
20
- console: `<polyline points="4 17 10 11 4 5"/><line x1="12" y1="19" x2="20" y2="19"/>`,
21
- infinity: `<path d="M12 12c-2-2.5-4-4-6-4a4 4 0 0 0 0 8c2 0 4-1.5 6-4z"/><path d="M12 12c2 2.5 4 4 6 4a4 4 0 0 0 0-8c-2 0-4 1.5-6 4z"/>`,
19
+ help: `<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 17h-2v-2h2v2zm2.07-7.75l-.9.92C13.45 12.9 13 13.5 13 15h-2v-.5c0-1.1.45-2.1 1.17-2.83l1.24-1.26c.37-.36.59-.86.59-1.41 0-1.1-.9-2-2-2s-2 .9-2 2H8c0-2.21 1.79-4 4-4s4 1.79 4 4c0 .88-.36 1.68-.93 2.25z"/>`,
20
+ logout: `<path d="M5 2h14a1 1 0 011 1v18a1 1 0 01-1 1H5a1 1 0 01-1-1v-4h2v3h12V4H7v3H4V3a1 1 0 011-1zm6 7V7l5 5-5 5v-2H4v-6h7z"/>`,
21
+ console: `<path d="M2 4a1 1 0 011-1h18a1 1 0 011 1v16a1 1 0 01-1 1H3a1 1 0 01-1-1V4zm4 13v-2l4-3-4-3V7l6 5-6 5zm6 0h6v-2h-6v2z"/>`,
22
+ infinity: `<path d="M18.6 6.62c-1.44 0-2.8.56-3.77 1.53L12 10.66 9.17 8.15C8.2 7.18 6.84 6.62 5.4 6.62 2.42 6.62 0 9.04 0 12s2.42 5.38 5.4 5.38c1.44 0 2.8-.56 3.77-1.53L12 13.34l2.83 2.51c.97.97 2.33 1.53 3.77 1.53C21.58 17.38 24 14.96 24 12s-2.42-5.38-5.4-5.38zm-13.2 8.56C3.63 15.18 2.2 13.75 2.2 12s1.43-3.18 3.2-3.18c.86 0 1.68.34 2.27.94l2.21 1.96-2.21 2.52c-.59.6-1.41.94-2.27.94zm13.2 0c-.86 0-1.68-.34-2.27-.94l-2.21-1.96 2.21-2.52c.59-.6 1.41-.94 2.27-.94 1.77 0 3.2 1.43 3.2 3.18s-1.43 3.18-3.2 3.18z"/>`,
22
23
 
23
24
  // ── Acciones ──
24
- plus: `<line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/>`,
25
- check: `<polyline points="20 6 9 17 4 12"/>`,
26
- x: `<line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/>`,
27
- edit: `<path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"/><path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"/>`,
28
- trash: `<polyline points="3 6 5 6 21 6"/><path d="M19 6l-1 14a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2L5 6"/><path d="M10 11v6"/><path d="M14 11v6"/><path d="M9 6V4a1 1 0 0 1 1-1h4a1 1 0 0 1 1 1v2"/>`,
29
- copy: `<rect x="9" y="9" width="13" height="13" rx="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/>`,
30
- refresh: `<polyline points="23 4 23 10 17 10"/><path d="M20.49 15a9 9 0 1 1-2.12-9.36L23 10"/>`,
31
- sync: `<path d="M21 2v6h-6"/><path d="M3 12a9 9 0 0 1 15-6.7L21 8"/><path d="M3 22v-6h6"/><path d="M21 12a9 9 0 0 1-15 6.7L3 16"/>`,
32
- search: `<circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/>`,
33
- filter: `<polygon points="22 3 2 3 10 12.46 10 19 14 21 14 12.46 22 3"/>`,
34
- sort: `<line x1="21" y1="10" x2="7" y2="10"/><line x1="21" y1="6" x2="3" y2="6"/><line x1="21" y1="14" x2="3" y2="14"/><line x1="21" y1="18" x2="7" y2="18"/>`,
35
- menu: `<line x1="3" y1="12" x2="21" y2="12"/><line x1="3" y1="6" x2="21" y2="6"/><line x1="3" y1="18" x2="21" y2="18"/>`,
36
- arrowUp: `<line x1="12" y1="19" x2="12" y2="5"/><polyline points="5 12 12 5 19 12"/>`,
37
- arrowDown: `<line x1="12" y1="5" x2="12" y2="19"/><polyline points="19 12 12 19 5 12"/>`,
38
- arrowRight:`<line x1="5" y1="12" x2="19" y2="12"/><polyline points="12 5 19 12 12 19"/>`,
39
- chevronDown:`<polyline points="6 9 12 15 18 9"/>`,
40
- externalLink:`<path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/><polyline points="15 3 21 3 21 9"/><line x1="10" y1="14" x2="21" y2="3"/>`,
25
+ plus: `<path d="M11 11V5h2v6h6v2h-6v6h-2v-6H5v-2h6z"/>`,
26
+ check: `<path d="M9.86 18a1 1 0 01-.73-.32l-4.86-5.17a1 1 0 111.46-1.37l4.12 4.39 8.41-9.2a1 1 0 111.48 1.34l-9.14 10a1 1 0 01-.73.33h-.01z"/>`,
27
+ x: `<path d="M12 10.586l4.95-4.95 1.414 1.414L13.414 12l4.95 4.95-1.414 1.414L12 13.414l-4.95 4.95-1.414-1.414L10.586 12 5.636 7.05l1.414-1.414L12 10.586z"/>`,
28
+ edit: `<path d="M3 17.25V21h3.75L17.81 9.94l-3.75-3.75L3 17.25zM20.71 7.04a1 1 0 000-1.41l-2.34-2.34a1 1 0 00-1.41 0l-1.83 1.83 3.75 3.75 1.83-1.83z"/>`,
29
+ trash: `<path d="M6 19c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7H6v12zM8 9h8v10H8V9zm7.5-5l-1-1h-5l-1 1H5v2h14V4h-3.5z"/>`,
30
+ copy: `<path d="M16 1H4c-1.1 0-2 .9-2 2v14h2V3h12V1zm3 4H8c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h11c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zm0 16H8V7h11v14z"/>`,
31
+ refresh: `<path d="M17.65 6.35A7.958 7.958 0 0012 4c-4.42 0-7.99 3.58-7.99 8s3.57 8 7.99 8c3.73 0 6.84-2.55 7.73-6h-2.08A5.99 5.99 0 0112 18c-3.31 0-6-2.69-6-6s2.69-6 6-6c1.66 0 3.14.69 4.22 1.78L13 11h7V4l-2.35 2.35z"/>`,
32
+ sync: `<path d="M12 4V1L8 5l4 4V6c3.31 0 6 2.69 6 6 0 1.01-.25 1.97-.7 2.8l1.46 1.46A7.93 7.93 0 0020 12c0-4.42-3.58-8-8-8zm0 14c-3.31 0-6-2.69-6-6 0-1.01.25-1.97.7-2.8L5.24 7.74A7.93 7.93 0 004 12c0 4.42 3.58 8 8 8v3l4-4-4-4v3z"/>`,
33
+ search: `<path d="M15.5 14h-.79l-.28-.27A6.47 6.47 0 0016 9.5 6.5 6.5 0 109.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"/>`,
34
+ filter: `<path d="M10 18h4v-2h-4v2zM3 6v2h18V6H3zm3 7h12v-2H6v2z"/>`,
35
+ sort: `<path d="M3 18h6v-2H3v2zM3 6v2h18V6H3zm0 7h12v-2H3v2z"/>`,
36
+ menu: `<path d="M3 18h18v-2H3v2zm0-5h18v-2H3v2zm0-7v2h18V6H3z"/>`,
37
+ arrowUp: `<path d="M4 12l1.41 1.41L11 7.83V20h2V7.83l5.58 5.59L20 12l-8-8-8 8z"/>`,
38
+ arrowDown: `<path d="M20 12l-1.41-1.41L13 16.17V4h-2v12.17l-5.58-5.59L4 12l8 8 8-8z"/>`,
39
+ arrowRight: `<path d="M12 4l-1.41 1.41L16.17 11H4v2h12.17l-5.58 5.59L12 20l8-8-8-8z"/>`,
40
+ chevronDown: `<path d="M7.41 8.59L12 13.17l4.59-4.58L18 10l-6 6-6-6 1.41-1.41z"/>`,
41
+ externalLink: `<path d="M19 19H5V5h7V3H5a2 2 0 00-2 2v14a2 2 0 002 2h14a2 2 0 002-2v-7h-2v7zM14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3h-7z"/>`,
41
42
 
42
43
  // ── Tiempo / cronómetro ──
43
- clock: `<circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/>`,
44
- play: `<polygon points="5 3 19 12 5 21 5 3"/>`,
45
- pause: `<rect x="6" y="4" width="4" height="16"/><rect x="14" y="4" width="4" height="16"/>`,
46
- stop: `<rect x="3" y="3" width="18" height="18" rx="2"/>`,
47
- timer: `<circle cx="12" cy="13" r="8"/><path d="M12 9v4l2 2"/><path d="M5 3 2 6"/><path d="m22 6-3-3"/><path d="M6.38 18.7 4 21"/><path d="M17.64 18.67 20 21"/>`,
44
+ clock: `<path d="M12 2C6.5 2 2 6.5 2 12s4.5 10 10 10 10-4.5 10-10S17.5 2 12 2zm4.2 14.2L11 13V7h1.5v5.2l4.5 2.7-.8 1.3z"/>`,
45
+ play: `<path d="M8 5v14l11-7L8 5z"/>`,
46
+ pause: `<path d="M6 19h4V5H6v14zm8-14v14h4V5h-4z"/>`,
47
+ stop: `<path d="M6 6h12v12H6z"/>`,
48
+ timer: `<path d="M15 1H9v2h6V1zm-4 13h2V8h-2v6zm8.03-6.61l1.42-1.42c-.43-.51-.9-.99-1.41-1.41l-1.42 1.42A8.962 8.962 0 0012 4c-4.97 0-9 4.03-9 9s4.03 9 9 9 9-4.03 9-9c0-2.12-.74-4.07-1.97-5.61zM12 20c-3.87 0-7-3.13-7-7s3.13-7 7-7 7 3.13 7 7-3.13 7-7 7z"/>`,
48
49
 
49
50
  // ── Git / Repo ──
50
- gitBranch: `<line x1="6" y1="3" x2="6" y2="15"/><circle cx="18" cy="6" r="3"/><circle cx="6" cy="18" r="3"/><path d="M18 9a9 9 0 0 1-9 9"/>`,
51
- gitCommit: `<circle cx="12" cy="12" r="4"/><line x1="1.05" y1="12" x2="7" y2="12"/><line x1="17.01" y1="12" x2="22.96" y2="12"/>`,
52
- gitMerge: `<circle cx="18" cy="18" r="3"/><circle cx="6" cy="6" r="3"/><path d="M6 21V9a9 9 0 0 0 9 9"/>`,
51
+ gitBranch: `<path d="M7 5a2 2 0 100 4 2 2 0 000-4zM7 15a2 2 0 100 4 2 2 0 000-4zM17 5a2 2 0 100 4 2 2 0 000-4zM7 11a4 4 0 00-4 4v0a4 4 0 004 4h0V11zm0 0V9m10-2v2a4 4 0 01-4 4H7"/>`,
52
+ gitCommit: `<path d="M12 16a4 4 0 100-8 4 4 0 000 8zm0-10a6.01 6.01 0 015.917 5H22v2h-4.083A6.01 6.01 0 0112 18a6.01 6.01 0 01-5.917-5H2v-2h4.083A6.01 6.01 0 0112 6z"/>`,
53
+ gitMerge: `<path d="M7.105 8.79A3.001 3.001 0 005 12a3 3 0 002.105 2.862V18a3.001 3.001 0 101.99.001v-3.526A8.01 8.01 0 0015 18h1v-2h-1a6.006 6.006 0 01-5.893-4.876A3.001 3.001 0 007.105 8.79zM8 6a1 1 0 11-2 0 1 1 0 012 0zm0 12a1 1 0 11-2 0 1 1 0 012 0zm0-6a1 1 0 11-2 0 1 1 0 012 0z"/>`,
53
54
 
54
55
  // ── Información / estado ──
55
- alertCircle: `<circle cx="12" cy="12" r="10"/><line x1="12" y1="8" x2="12" y2="12"/><line x1="12" y1="16" x2="12.01" y2="16"/>`,
56
- alertTriangle:`<path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"/><line x1="12" y1="9" x2="12" y2="13"/><line x1="12" y1="17" x2="12.01" y2="17"/>`,
57
- checkCircle: `<path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"/><polyline points="22 4 12 14.01 9 11.01"/>`,
58
- info: `<circle cx="12" cy="12" r="10"/><line x1="12" y1="16" x2="12" y2="12"/><line x1="12" y1="8" x2="12.01" y2="8"/>`,
59
- shield: `<path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/>`,
60
- zap: `<polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2"/>`,
61
- trending: `<polyline points="23 6 13.5 15.5 8.5 10.5 1 18"/><polyline points="17 6 23 6 23 12"/>`,
62
- target: `<circle cx="12" cy="12" r="10"/><circle cx="12" cy="12" r="6"/><circle cx="12" cy="12" r="2"/>`,
56
+ alertCircle: `<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-2h2v2zm0-4h-2V7h2v6z"/>`,
57
+ alertTriangle:`<path d="M1 21h22L12 2 1 21zm12-3h-2v-2h2v2zm0-4h-2v-4h2v4z"/>`,
58
+ checkCircle: `<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z"/>`,
59
+ info: `<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-6h2v6zm0-8h-2V7h2v2z"/>`,
60
+ shield: `<path d="M12 1l-9 4v6c0 5.55 3.84 10.74 9 12 5.16-1.26 9-6.45 9-12V5l-9-4z"/>`,
61
+ zap: `<path d="M11 21h-1l1-7H7.5c-.88 0-.33-.75-.31-.78C8.48 10.94 10.42 7.54 13.01 3h1l-1 7h3.51c.4 0 .62.19.4.66C12.97 17.55 11 21 11 21z"/>`,
62
+ trending: `<path d="M16 6l2.29 2.29-4.88 4.88-4-4L2 16.59 3.41 18l6-6 4 4 6.3-6.29L22 12V6h-6z"/>`,
63
+ target: `<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8zm0-14c-3.31 0-6 2.69-6 6s2.69 6 6 6 6-2.69 6-6-2.69-6-6-6zm0 10c-2.21 0-4-1.79-4-4s1.79-4 4-4 4 1.79 4 4-1.79 4-4 4zm0-6c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2z"/>`,
63
64
 
64
65
  // ── Ficheros / docs ──
65
- fileText: `<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/><line x1="16" y1="13" x2="8" y2="13"/><line x1="16" y1="17" x2="8" y2="17"/><polyline points="10 9 9 9 8 9"/>`,
66
- folder: `<path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"/>`,
67
- download: `<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/>`,
66
+ fileText: `<path d="M14 2H6c-1.1 0-2 .9-2 2v16c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V8l-6-6zm2 16H8v-2h8v2zm0-4H8v-2h8v2zm-3-5V3.5L18.5 9H13z"/>`,
67
+ folder: `<path d="M10 4H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V8c0-1.1-.9-2-2-2h-8l-2-2z"/>`,
68
+ download: `<path d="M19 9h-4V3H9v6H5l7 7 7-7zM5 18v2h14v-2H5z"/>`,
68
69
 
69
70
  // ── Layout ──
70
- maximize: `<path d="M8 3H5a2 2 0 0 0-2 2v3m18 0V5a2 2 0 0 0-2-2h-3m0 18h3a2 2 0 0 0 2-2v-3M3 16v3a2 2 0 0 0 2 2h3"/>`,
71
- columns: `<rect x="3" y="3" width="18" height="18" rx="2"/><line x1="12" y1="3" x2="12" y2="21"/>`,
72
- terminal2: `<polyline points="4 17 10 11 4 5"/><line x1="12" y1="19" x2="20" y2="19"/>`,
73
- package: `<line x1="16.5" y1="9.4" x2="7.5" y2="4.21"/><path d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z"/><polyline points="3.27 6.96 12 12.01 20.73 6.96"/><line x1="12" y1="22.08" x2="12" y2="12"/>`,
71
+ maximize: `<path d="M7 14H5v5h5v-2H7v-3zm-2-4h2V7h3V5H5v5zm12 7h-3v2h5v-5h-2v3zM14 5v2h3v3h2V5h-5z"/>`,
72
+ columns: `<path d="M10 18h5V5h-5v13zm-6 0h5V5H4v13zM16 5v13h5V5h-5z"/>`,
73
+ terminal2: `<path d="M2 4a1 1 0 011-1h18a1 1 0 011 1v16a1 1 0 01-1 1H3a1 1 0 01-1-1V4zm4 13v-2l4-3-4-3V7l6 5-6 5zm6 0h6v-2h-6v2z"/>`,
74
+ package: `<path d="M20 12V6.5L12 2 4 6.5V12l8 4.5L20 12zM12 4.31l5.74 3.22L12 10.75 6.26 7.53 12 4.31zM6 9.17l5 2.81v5.64l-5-2.81V9.17zm7 8.45v-5.64l5-2.81v5.64l-5 2.81z"/>`,
75
+
76
+ // ── Nuevos iconos ──
77
+ projects: `<path d="M20 6h-8l-2-2H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V8c0-1.1-.9-2-2-2zm-2 10H6v-2h12v2zm0-4H6v-2h12v2z"/>`,
78
+ timeline: `<path d="M2 5h6v4H2V5zm0 10h8v4H2v-4zm0-5h10v4H2v-4zm14-5h6v4h-6V5zm-4 10h10v4H12v-4zm2-5h8v4h-8v-4z"/>`,
79
+ velocity: `<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8zm1-8.41l2.54 2.53 1.41-1.41-3.54-3.54L12 3v6.59z"/>`,
80
+ calendar: `<path d="M19 3h-1V1h-2v2H8V1H6v2H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 16H5V8h14v11zM9 10H7v2h2v-2zm4 0h-2v2h2v-2zm4 0h-2v2h2v-2z"/>`,
81
+ heatmap: `<path d="M4 4h4v4H4V4zm6 0h4v4h-4V4zm6 0h4v4h-4V4zM4 10h4v4H4v-4zm6 0h4v4h-4v-4zm6 0h4v4h-4v-4zM4 16h4v4H4v-4zm6 0h4v4h-4v-4zm6 0h4v4h-4v-4z"/>`,
82
+ keyboard: `<path d="M20 5H4c-1.1 0-2 .9-2 2v10c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zm-9 3h2v2h-2V8zm0 3h2v2h-2v-2zM8 8h2v2H8V8zm0 3h2v2H8v-2zm-1 2H5v-2h2v2zm0-3H5V8h2v2zm9 7H8v-2h8v2zm0-4h-2v-2h2v2zm0-3h-2V8h2v2zm3 3h-2v-2h2v2zm0-3h-2V8h2v2z"/>`,
83
+ moon: `<path d="M12.03 4.5C11.44 4.5 10.87 4.56 10.31 4.68 13.31 5.88 15.5 8.88 15.5 12.38c0 3.51-2.19 6.5-5.19 7.7.56.12 1.13.18 1.72.18 4.97 0 9-4.03 9-9s-4.03-9.76-9-9.76z"/>`,
84
+ sun: `<path d="M6.76 4.84l-1.8-1.79-1.41 1.41 1.79 1.79 1.42-1.41zM4 10.5H1v2h3v-2zm9-9.95h-2V3.5h2V.55zm7.45 3.91l-1.41-1.41-1.79 1.79 1.41 1.41 1.79-1.79zm-3.21 13.7l1.79 1.8 1.41-1.41-1.8-1.79-1.4 1.4zM20 10.5v2h3v-2h-3zm-8-5c-3.31 0-6 2.69-6 6s2.69 6 6 6 6-2.69 6-6-2.69-6-6-6zm-1 16.95h2V19.5h-2v2.95zm-7.45-3.91l1.41 1.41 1.79-1.8-1.41-1.41-1.79 1.8z"/>`,
85
+ user: `<path d="M12 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm0 2c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z"/>`,
86
+ lock: `<path d="M18 8h-1V6c0-2.76-2.24-5-5-5S7 3.24 7 6v2H6c-1.1 0-2 .9-2 2v10c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V10c0-1.1-.9-2-2-2zm-6 9c-1.1 0-2-.9-2-2s.9-2 2-2 2 .9 2 2-.9 2-2 2zm3.1-9H8.9V6c0-1.71 1.39-3.1 3.1-3.1 1.71 0 3.1 1.39 3.1 3.1v2z"/>`,
87
+ unlock: `<path d="M12 17c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2zm6-9h-1V6c0-2.76-2.24-5-5-5S7 3.24 7 6h1.9c0-1.71 1.39-3.1 3.1-3.1 1.71 0 3.1 1.39 3.1 3.1v2H6c-1.1 0-2 .9-2 2v10c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V10c0-1.1-.9-2-2-2zm0 12H6V10h12v10z"/>`,
88
+ eye: `<path d="M12 4.5C7 4.5 2.73 7.61 1 12c1.73 4.39 6 7.5 11 7.5s9.27-3.11 11-7.5c-1.73-4.39-6-7.5-11-7.5zM12 17c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5-2.24 5-5 5zm0-8c-1.66 0-3 1.34-3 3s1.34 3 3 3 3-1.34 3-3-1.34-3-3-3z"/>`,
89
+ eyeOff: `<path d="M12 7c2.76 0 5 2.24 5 5 0 .65-.13 1.26-.36 1.83l2.92 2.92c1.51-1.26 2.7-2.89 3.43-4.75-1.73-4.39-6-7.5-11-7.5-1.4 0-2.74.25-3.98.7l2.16 2.16C10.74 7.13 11.35 7 12 7zM2 4.27l2.28 2.28.46.46A11.8 11.8 0 001 12c1.73 4.39 6 7.5 11 7.5 1.55 0 3.03-.3 4.38-.84l.42.42L19.73 22 21 20.73 3.27 3 2 4.27zM7.53 9.8l1.55 1.55c-.05.21-.08.43-.08.65 0 1.66 1.34 3 3 3 .22 0 .44-.03.65-.08l1.55 1.55c-.67.33-1.41.53-2.2.53-2.76 0-5-2.24-5-5 0-.79.2-1.53.53-2.2zm4.31-.78l3.15 3.15.02-.16c0-1.66-1.34-3-3-3l-.17.01z"/>`,
90
+ grip: `<path d="M9 3h2v2H9V3zm4 0h2v2h-2V3zM9 7h2v2H9V7zm4 0h2v2h-2V7zM9 11h2v2H9v-2zm4 0h2v2h-2v-2zM9 15h2v2H9v-2zm4 0h2v2h-2v-2zM9 19h2v2H9v-2zm4 0h2v2h-2v-2z"/>`,
91
+ moreVertical: `<path d="M12 8c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2zm0 2c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm0 6c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2z"/>`,
92
+ moreHorizontal:`<path d="M6 10c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm12 0c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm-6 0c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2z"/>`,
93
+ archive: `<path d="M20.54 5.23l-1.39-1.68C18.88 3.21 18.47 3 18 3H6c-.47 0-.88.21-1.16.55L3.46 5.23C3.17 5.57 3 6.02 3 6.5V19c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V6.5c0-.48-.17-.93-.46-1.27zM12 17.5L6.5 12H10v-2h4v2h3.5L12 17.5zM5.12 5l.81-1h12l.94 1H5.12z"/>`,
94
+ star: `<path d="M12 17.27L18.18 21l-1.64-7.03L22 9.24l-7.19-.61L12 2 9.19 8.63 2 9.24l5.46 4.73L5.82 21z"/>`,
95
+ bookmark: `<path d="M17 3H7c-1.1 0-2 .9-2 2v16l7-3 7 3V5c0-1.1-.9-2-2-2z"/>`,
96
+ link: `<path d="M3.9 12c0-1.71 1.39-3.1 3.1-3.1h4V7H7c-2.76 0-5 2.24-5 5s2.24 5 5 5h4v-1.9H7c-1.71 0-3.1-1.39-3.1-3.1zM8 13h8v-2H8v2zm9-6h-4v1.9h4c1.71 0 3.1 1.39 3.1 3.1s-1.39 3.1-3.1 3.1h-4V17h4c2.76 0 5-2.24 5-5s-2.24-5-5-5z"/>`,
97
+ globe: `<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-1 17.93c-3.95-.49-7-3.85-7-7.93 0-.62.08-1.21.21-1.79L9 15v1c0 1.1.9 2 2 2v1.93zm6.9-2.54c-.26-.81-1-1.39-1.9-1.39h-1v-3c0-.55-.45-1-1-1H8v-2h2c.55 0 1-.45 1-1V7h2c1.1 0 2-.9 2-2v-.41c2.93 1.19 5 4.06 5 7.41 0 2.08-.8 3.97-2.1 5.39z"/>`,
98
+ flag: `<path d="M14.4 6L14 4H5v17h2v-7h5.6l.4 2h7V6h-5.6z"/>`,
74
99
  };
75
100
 
76
101
  /**
77
- * Renderiza un SVG icon
102
+ * Renderiza un SVG icon (fill-based, Remix Icon style)
78
103
  * @param {string} name - nombre del icono
79
104
  * @param {number} [size=18] - tamaño en px
80
105
  * @param {string} [className=''] - clases adicionales
@@ -83,10 +108,10 @@ const PATHS = {
83
108
  export function icon(name, size = 18, className = '') {
84
109
  const paths = PATHS[name];
85
110
  if (!paths) {
86
- // Icono fallback: círculo vacío
87
- return `<svg width="${size}" height="${size}" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="${className}" aria-hidden="true"><circle cx="12" cy="12" r="10" opacity="0.3"/></svg>`;
111
+ // Icono fallback: círculo semitransparente
112
+ return `<svg width="${size}" height="${size}" viewBox="0 0 24 24" fill="currentColor" class="${className}" aria-hidden="true"><circle cx="12" cy="12" r="10" opacity="0.3"/></svg>`;
88
113
  }
89
- return `<svg width="${size}" height="${size}" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="${className}" aria-hidden="true">${paths}</svg>`;
114
+ return `<svg width="${size}" height="${size}" viewBox="0 0 24 24" fill="currentColor" class="${className}" aria-hidden="true">${paths}</svg>`;
90
115
  }
91
116
 
92
117
  /**