diff --git a/frontend/src/lib/plugin-host/PluginBundleHost.svelte b/frontend/src/lib/plugin-host/PluginBundleHost.svelte index 0f43182..834c30c 100644 --- a/frontend/src/lib/plugin-host/PluginBundleHost.svelte +++ b/frontend/src/lib/plugin-host/PluginBundleHost.svelte @@ -1,6 +1,7 @@ + + + {#each icon.paths as p} + + {/each} + diff --git a/frontend/src/lib/ui/icons.js b/frontend/src/lib/ui/icons.js new file mode 100644 index 0000000..8cc3c1e --- /dev/null +++ b/frontend/src/lib/ui/icons.js @@ -0,0 +1,128 @@ +/** + * icons.js — Centralised SVG icon set for Verstak. + * + * RULE: Icons MUST be SVG only. No emoji, no unicode symbols, no font-icons. + * The Wails WebKitGTK webview does NOT render colour emoji. + * + * Each icon is an object: { viewBox, paths } + * - viewBox: string, default '0 0 24 24' + * - paths: array of attributes or objects with { d, ...attrs } + * + * Usage in Svelte: + * import { iconPaths } from '../lib/ui/icons.js'; + * + * {#each iconPaths.puzzle.paths as p} + * + * {/each} + * + */ + +export const iconPaths = { + /** Plugin Manager — puzzle piece */ + puzzle: { + viewBox: '0 0 24 24', + paths: [ + { d: 'M4 5a3 3 0 0 1 3-3h4v2a2 2 0 0 0 4 0V2h4a2 2 0 0 1 2 2v4h-2a2 2 0 0 0 0 4h2v4a2 2 0 0 1-2 2h-4v-2a2 2 0 0 0-4 0v2H7a3 3 0 0 1-3-3v-3h2a2 2 0 0 0 0-4H4V5Z', + attrs: { fill: 'currentColor', stroke: 'none' }, + }, + ], + }, + + /** Platform Test / Diagnostics — flask/test-tube */ + flask: { + viewBox: '0 0 24 24', + paths: [ + { d: 'M9 2v5.586l-5.293 5.293A1 1 0 0 0 4 14v4a4 4 0 0 0 4 4h8a4 4 0 0 0 4-4v-4a1 1 0 0 0-.293-.707L15 7.586V2H9Zm2 0v6a1 1 0 0 0 .293.707l5 5V14a1 1 0 0 1-1 1h-1l-2-2-2 2H8a1 1 0 0 1-1-1v-.293l3-3V2h3Z', + attrs: { fill: 'currentColor', stroke: 'none' }, + }, + ], + }, + + /** Verstak logo — stack/tray */ + logo: { + viewBox: '0 0 24 24', + paths: [ + { d: 'M5 3h14a2 2 0 0 1 2 2v3a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2Zm0 2v3h14V5H5Z', + attrs: { fill: 'currentColor', stroke: 'none' }, + }, + { d: 'M5 12h14a2 2 0 0 1 2 2v3a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-3a2 2 0 0 1 2-2Zm0 2v3h14v-3H5Z', + attrs: { fill: 'currentColor', stroke: 'none' }, + }, + ], + }, + + /** Default fallback — circle */ + dot: { + viewBox: '0 0 24 24', + paths: [ + { d: 'M12 2C6.477 2 2 6.477 2 12s4.477 10 10 10 10-4.477 10-10S17.523 2 12 2Zm0 6a4 4 0 1 1 0 8 4 4 0 0 1 0-8Z', + attrs: { fill: 'currentColor', stroke: 'none' }, + }, + ], + }, + + /** Vault — safe / shield */ + vault: { + viewBox: '0 0 24 24', + paths: [ + { d: 'M12 1L3 5v6c0 5.55 3.84 10.74 9 12 5.16-1.26 9-6.45 9-12V5l-9-4Zm0 2.18L19 6.4v4.6c0 4.5-3.07 8.68-7 9.82-3.93-1.14-7-5.32-7-9.82V6.4l7-3.22ZM11 8v2H9v2h2v4h2v-4h2v-2h-2V8h-2Z', + attrs: { fill: 'currentColor', stroke: 'none' }, + }, + ], + }, + + /** Settings — gear */ + gear: { + viewBox: '0 0 24 24', + paths: [ + { d: 'M19.14 13l.57-1.43 1.79-.5-1-2.29-1.64.73-.29-.28-.73-1.64 2.29-1-1-2.29-1.79.5-.57 1.43-2.17.17-.57-1.43-1.79-.5-1 2.29 1.64.73-.29.28-.73 1.64-2.29-1-1 2.29 1.79.5.57 1.43-.57 1.43-1.79.5 1 2.29 1.64-.73.29.28.73 1.64-2.29 1 1 2.29 1.79-.5.57-1.43 2.17-.17.57 1.43 1.79.5 1-2.29-1.64-.73.29-.28.73-1.64 2.29 1 1-2.29-1.79-.5-.57-1.43ZM12 9a3 3 0 1 1 0 6 3 3 0 0 1 0-6Z', + attrs: { fill: 'currentColor', stroke: 'none' }, + }, + ], + }, + + /** Warning / error — triangle */ + warning: { + viewBox: '0 0 24 24', + paths: [ + { d: 'M1 21h22L12 2 1 21Zm12-3h-2v-2h2v2Zm0-4h-2v-4h2v4Z', + attrs: { fill: 'currentColor', stroke: 'none' }, + }, + ], + }, + + /** General plugin — extension/puzzle alternative */ + plugin: { + viewBox: '0 0 24 24', + paths: [ + { d: 'M11 2H5v6.172a3 3 0 0 0 0 5.656V20a1 1 0 0 0 1 1h4v-2H7v-4a1 1 0 0 0-1-1H5v-2h1a1 1 0 0 0 1-1V8h2V4h2a1 1 0 0 0 1-1V2h-1Z', + attrs: { fill: 'currentColor', stroke: 'none' }, + }, + { d: 'M17 2a3 3 0 0 1 3 3v1h-4V5a1 1 0 0 0-1-1h-2v2h2v2h-2v2h4V8h2v2a3 3 0 0 1-3 3h-1v2h1a2 2 0 0 1 2 2v2h2v2a2 2 0 0 1-2 2h-4a2 2 0 0 1-2-2v-2h2v-2h-2v-2h4a1 1 0 0 0 1-1V5a1 1 0 0 0-1-1h-2v2h-2V4a2 2 0 0 1 2-2h3Z', + attrs: { fill: 'currentColor', stroke: 'none' }, + }, + ], + }, +}; + +/** + * Render an SVG icon string inline. + * Returns an SVG string suitable for {@html } or innerHTML. + * @param {string} name - icon key + * @param {number|string} size - width/height + * @param {string} className - optional CSS class + */ +export function svgIcon(name, size = 16, className = '') { + const icon = iconPaths[name]; + if (!icon) return svgIcon('dot', size, className); + const paths = icon.paths + .map(p => { + const attrs = p.attrs + ? Object.entries(p.attrs).map(([k, v]) => `${k}="${v}"`).join(' ') + : ''; + return ``; + }) + .join(''); + const cls = className ? ` class="${className}"` : ''; + return `${paths}`; +}