From 76f1770286258097000ee18a9d11adc6b55ae08c Mon Sep 17 00:00:00 2001 From: Ku6epXBOCTuK Date: Mon, 24 Aug 2026 09:16:29 +0500 Subject: [PATCH] feat: add tool popularity and icons --- web/package.json | 3 + web/pnpm-lock.yaml | 13 ++++ web/src/lib/registry.test.ts | 16 +++++ web/src/lib/registry.ts | 10 +++ web/src/lib/tools/tool-icons.ts | 92 ++++++++++++++++++++++++++++ web/src/lib/tools/tool-popularity.ts | 53 ++++++++++++++++ web/vitest.config.ts | 2 + 7 files changed, 189 insertions(+) create mode 100644 web/src/lib/tools/tool-icons.ts create mode 100644 web/src/lib/tools/tool-popularity.ts diff --git a/web/package.json b/web/package.json index f9e32ef..a9b6ae5 100644 --- a/web/package.json +++ b/web/package.json @@ -21,5 +21,8 @@ "typescript": "^6.0.3", "vite": "^8.0.16", "vitest": "^4.1.11" + }, + "dependencies": { + "@lucide/svelte": "^1.33.0" } } diff --git a/web/pnpm-lock.yaml b/web/pnpm-lock.yaml index e386a17..96e0974 100644 --- a/web/pnpm-lock.yaml +++ b/web/pnpm-lock.yaml @@ -7,6 +7,10 @@ settings: importers: .: + dependencies: + '@lucide/svelte': + specifier: ^1.33.0 + version: 1.33.0(svelte@5.56.9) devDependencies: '@sveltejs/adapter-static': specifier: ^3.0.10 @@ -51,6 +55,11 @@ packages: '@jridgewell/trace-mapping@0.3.31': resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} + '@lucide/svelte@1.33.0': + resolution: {integrity: sha512-b+osTYG2V4dge5Lr7tnaTaahhy/4vH7Xb8zcqVjmctjwgkpXS0NNOJPkGzHHZoxtw/OO+2YAU28omeMfYCaawg==} + peerDependencies: + svelte: ^5 + '@oxc-project/types@0.144.0': resolution: {integrity: sha512-nuhZIOLuI6TFQ32I/WnUx+SCPY7SdSKwgnFHydAuoS1+Z4BRcaP+RRJmGzl9lw+0OFF7UmaESf7KQRXaNLHypg==} @@ -627,6 +636,10 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.5 + '@lucide/svelte@1.33.0(svelte@5.56.9)': + dependencies: + svelte: 5.56.9 + '@oxc-project/types@0.144.0': {} '@polka/url@1.0.0-next.29': {} diff --git a/web/src/lib/registry.test.ts b/web/src/lib/registry.test.ts index e7a645f..90e4076 100644 --- a/web/src/lib/registry.test.ts +++ b/web/src/lib/registry.test.ts @@ -1,5 +1,6 @@ import { describe, expect, it } from 'vitest'; import { CATEGORIES } from './categories'; +import { TOOL_ICONS } from './tools/tool-icons'; import { defaultParams, getTool, @@ -150,6 +151,21 @@ describe('реестр инструментов', () => { const resize = getToolOrThrow('resize-png'); expect(defaultParams(resize)).toEqual({ width: 0, height: 0, keepAspect: true }); }); + + it('словарь иконок покрывает все инструменты', () => { + for (const tool of TOOLS) { + expect(TOOL_ICONS[tool.id]).toBeDefined(); + } + }); + + it('у каждого инструмента популярность в диапазоне 0..100 и ключ иконки', () => { + for (const tool of TOOLS) { + expect(tool.popularity).toBeGreaterThanOrEqual(0); + expect(tool.popularity).toBeLessThanOrEqual(100); + expect(typeof tool.icon).toBe('string'); + expect(tool.icon!.length).toBeGreaterThan(0); + } + }); }); describe('sanitizeParams', () => { diff --git a/web/src/lib/registry.ts b/web/src/lib/registry.ts index 53e88aa..cebcd6a 100644 --- a/web/src/lib/registry.ts +++ b/web/src/lib/registry.ts @@ -88,6 +88,8 @@ export type ToolEntry = { img: PixelImage, params: Record ) => Promise | PixelImage; + popularity?: number; + icon?: string; resultType?: 'image' | 'info' | 'text'; output?: OutputFormat; }; @@ -803,6 +805,14 @@ export const TOOLS: ToolEntry[] = [ } ]; +import { TOOL_ICONS } from './tools/tool-icons'; +import { TOOL_POPULARITY } from './tools/tool-popularity'; + +for (const entry of TOOLS) { + entry.icon = entry.id; + entry.popularity = TOOL_POPULARITY[entry.id] ?? 50; +} + export function getTool(id: string): ToolEntry | undefined { return TOOLS.find((tool) => tool.id === id); } diff --git a/web/src/lib/tools/tool-icons.ts b/web/src/lib/tools/tool-icons.ts new file mode 100644 index 0000000..943b12e --- /dev/null +++ b/web/src/lib/tools/tool-icons.ts @@ -0,0 +1,92 @@ +import { + AppWindow, + Binary, + Blend, + ClipboardPaste, + Contrast, + Crop, + Crosshair, + Dices, + Droplet, + Droplets, + Eye, + FileImage, + FileOutput, + FilePlus2, + Film, + FlipHorizontal2, + Focus, + Frame, + Grid3x3, + Hash, + Image as ImageIcon, + Info, + Layers, + Link2, + Maximize2, + PaintBucket, + Palette, + Rainbow, + Repeat2, + RotateCw, + Ruler, + Scaling, + Scissors, + SearchCheck, + Square, + Sun +} from '@lucide/svelte'; + +export const TOOL_ICONS: Record = { + 'resize-png': Scaling, + 'crop-png': Crop, + 'rotate-png': RotateCw, + 'flip-png': FlipHorizontal2, + 'grayscale-png': Contrast, + 'invert-colors-png': Blend, + 'adjust-brightness-contrast-png': Sun, + 'convert-png-to-jpg': FileImage, + 'convert-png-to-webp': FileImage, + 'remove-color-from-png': Scissors, + 'png-info': Info, + 'jpg-to-png': FileImage, + 'webp-to-png': FileImage, + 'gif-to-png': Film, + 'bmp-to-png': FileImage, + 'ico-to-png': AppWindow, + 'png-to-bmp': FileOutput, + 'png-to-base64': Binary, + 'base64-to-png': ClipboardPaste, + 'png-to-data-uri': Link2, + 'data-uri-to-png': ClipboardPaste, + 'png-to-hex': Hash, + 'hex-to-png': Hash, + 'change-png-opacity': Droplet, + 'sepia-png': Palette, + 'change-png-hue': Rainbow, + 'extract-channel-png': Layers, + 'swap-channels-png': Repeat2, + 'black-and-white-png': Contrast, + 'posterize-png': Layers, + 'two-colors-png': Blend, + 'invert-alpha-png': Droplets, + 'remove-alpha-channel-png': Square, + 'set-alpha-channel-png': Droplet, + 'extract-alpha-mask-png': Layers, + 'round-corners-png': Frame, + 'add-padding-png': Maximize2, + 'add-border-png': Frame, + 'fit-on-background-png': ImageIcon, + 'tile-png': Grid3x3, + 'center-by-alpha-png': Crosshair, + 'create-empty-png': FilePlus2, + 'single-color-png': PaintBucket, + 'random-noise-png': Dices, + 'linear-gradient-png': Sun, + 'png-is-grayscale': SearchCheck, + 'png-is-transparent': Eye, + 'png-orientation': Ruler, + 'blur-png': Droplets, + 'sharpen-png': Focus, + 'remove-background-png': Scissors +}; diff --git a/web/src/lib/tools/tool-popularity.ts b/web/src/lib/tools/tool-popularity.ts new file mode 100644 index 0000000..db02204 --- /dev/null +++ b/web/src/lib/tools/tool-popularity.ts @@ -0,0 +1,53 @@ +export const TOOL_POPULARITY: Record = { + 'crop-png': 92, + 'resize-png': 90, + 'remove-background-png': 95, + 'blur-png': 85, + 'sharpen-png': 80, + 'convert-png-to-jpg': 82, + 'jpg-to-png': 78, + 'rotate-png': 75, + 'flip-png': 70, + 'convert-png-to-webp': 68, + 'webp-to-png': 62, + 'grayscale-png': 60, + 'adjust-brightness-contrast-png': 58, + 'invert-colors-png': 52, + 'remove-color-from-png': 65, + 'change-png-opacity': 45, + 'sepia-png': 40, + 'change-png-hue': 38, + 'add-padding-png': 42, + 'add-border-png': 38, + 'two-colors-png': 35, + 'black-and-white-png': 42, + 'posterize-png': 30, + 'round-corners-png': 36, + 'fit-on-background-png': 34, + 'extract-channel-png': 24, + 'swap-channels-png': 22, + 'invert-alpha-png': 26, + 'remove-alpha-channel-png': 30, + 'set-alpha-channel-png': 24, + 'extract-alpha-mask-png': 20, + 'tile-png': 28, + 'center-by-alpha-png': 26, + 'png-info': 44, + 'png-is-transparent': 18, + 'png-is-grayscale': 14, + 'png-orientation': 16, + 'png-to-base64': 40, + 'base64-to-png': 32, + 'png-to-data-uri': 34, + 'data-uri-to-png': 26, + 'png-to-hex': 10, + 'hex-to-png': 8, + 'gif-to-png': 30, + 'bmp-to-png': 18, + 'ico-to-png': 20, + 'png-to-bmp': 12, + 'create-empty-png': 25, + 'single-color-png': 20, + 'random-noise-png': 12, + 'linear-gradient-png': 22 +}; diff --git a/web/vitest.config.ts b/web/vitest.config.ts index b4cb01b..6a553d0 100644 --- a/web/vitest.config.ts +++ b/web/vitest.config.ts @@ -1,6 +1,8 @@ +import { svelte } from '@sveltejs/vite-plugin-svelte'; import { defineConfig } from 'vitest/config'; export default defineConfig({ + plugins: [svelte()], test: { include: ['src/**/*.test.ts'], environment: 'node'