МИН 87444ee2c8 Initial public release: Rublox Player v1.0
Open-source web player for Rublox games, dual-licensed under
AGPL-3.0 + Commercial.

Highlights:
- Babylon.js 7 + React 18 + Vite 5 stack
- Self-contained engine (~46k lines): BlockManager, ModelManager,
  PlayerController, ScriptSandboxWorker, MultiplayerSync, 30+ GD
  gamemodes
- Configurable backend via VITE_API_BASE and friends — works against
  staging (dev-api.rublox.pro) out of the box
- Standalone mode (VITE_STANDALONE=true) loads a bundled sample game
  for first-run without any backend
- Full docs: README, ARCHITECTURE, CONTRIBUTING, SECURITY, CHANGELOG
- Lint + format scaffolding (ESLint + Prettier + EditorConfig)
- Legal: LICENSE (AGPL-3.0), LICENSE-COMMERCIAL.md, CLA.md, COPYRIGHT.md
- Issue templates: bug_report, feature_request, security_disclosure

Removed before public release:
- frontend_deploy.py (contained production SSH credentials)
- ~27 admin endpoints (kept in private repo)
- Hard-coded internal URLs and IPs
- All previous git history (clean repo init)
2026-05-27 23:04:04 +03:00

386 lines
38 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* Icon — единый компонент иконок для KubikonEditor и KubikonStudio.
*
* Иконки — САМОПИСНЫЕ inline-SVG (не эмодзи, не сторонняя библиотека).
* Единый стиль: viewBox 24×24, обводка currentColor, скруглённые концы.
* Цвет наследуется от текста родителя (currentColor).
*
* Два режима использования:
*
* 1. По имени: <Icon name="rename"/>
* Семантическое имя (rename / delete / eye / settings / sun ...).
*
* 2. По эмодзи: <Icon emoji="✏️"/>
* Маппит эмодзи на имя иконки. Используется когда иконка хранится
* строкой в данных — менять структуру данных не нужно.
*
* Размер по умолчанию — 16px. Перебивается пропом size.
*
* Неизвестное имя/эмодзи → фолбэк-точка (видно, что иконка не настроена).
*/
import React from 'react';
// Общие пресеты атрибутов SVG-фигур.
// Толщину обводки (stroke-width) задаём на самом <svg> — так её можно
// переопределить пропом strokeWidth. Здесь её НЕ указываем.
const S = { fill: 'none', stroke: 'currentColor', strokeLinecap: 'round', strokeLinejoin: 'round' };
const F = { fill: 'currentColor', stroke: 'none' };
// ----------------------------------------------------------------------------
// GLYPHS — реестр иконок. Ключ = имя, значение = функция, рисующая
// внутренности <svg> (примитивы path/circle/rect/...).
// ----------------------------------------------------------------------------
const GLYPHS = {
// ===== действия =====
rename: () => (<><path d="M4 20h16" {...S}/><path d="M14.5 4.5l5 5L9 20H4v-5z" {...S}/></>),
edit: () => (<><path d="M4 20h16" {...S}/><path d="M14.5 4.5l5 5L9 20H4v-5z" {...S}/></>),
delete: () => (<><path d="M4 7h16" {...S}/><path d="M9 7V4h6v3" {...S}/><path d="M6 7l1 13h10l1-13" {...S}/><path d="M10 11v6M14 11v6" {...S}/></>),
trash: () => (<><path d="M4 7h16" {...S}/><path d="M9 7V4h6v3" {...S}/><path d="M6 7l1 13h10l1-13" {...S}/><path d="M10 11v6M14 11v6" {...S}/></>),
duplicate: () => (<><rect x="8" y="8" width="12" height="12" rx="2" {...S}/><path d="M16 8V6a2 2 0 0 0-2-2H6a2 2 0 0 0-2 2v8a2 2 0 0 0 2 2h2" {...S}/></>),
copy: () => (<><rect x="8" y="8" width="12" height="12" rx="2" {...S}/><path d="M16 8V6a2 2 0 0 0-2-2H6a2 2 0 0 0-2 2v8a2 2 0 0 0 2 2h2" {...S}/></>),
visible: () => (<><path d="M2 12s4-7 10-7 10 7 10 7-4 7-10 7-10-7-10-7z" {...S}/><circle cx="12" cy="12" r="3" {...S}/></>),
hidden: () => (<><path d="M4 4l16 16" {...S}/><path d="M9.5 5.5A9.7 9.7 0 0 1 12 5c6 0 10 7 10 7a17 17 0 0 1-3.5 4" {...S}/><path d="M6.5 7.5A17 17 0 0 0 2 12s4 7 10 7a9.7 9.7 0 0 0 4-0.9" {...S}/><path d="M9.5 9.7a3 3 0 0 0 4.2 4.2" {...S}/></>),
eye: () => (<><path d="M2 12s4-7 10-7 10 7 10 7-4 7-10 7-10-7-10-7z" {...S}/><circle cx="12" cy="12" r="3" {...S}/></>),
'eye-off': () => (<><path d="M4 4l16 16" {...S}/><path d="M9.5 5.5A9.7 9.7 0 0 1 12 5c6 0 10 7 10 7a17 17 0 0 1-3.5 4" {...S}/><path d="M6.5 7.5A17 17 0 0 0 2 12s4 7 10 7a9.7 9.7 0 0 0 4-0.9" {...S}/><path d="M9.5 9.7a3 3 0 0 0 4.2 4.2" {...S}/></>),
locked: () => (<><rect x="5" y="11" width="14" height="9" rx="2" {...S}/><path d="M8 11V8a4 4 0 0 1 8 0v3" {...S}/><circle cx="12" cy="15.5" r="1.3" {...F}/></>),
unlocked: () => (<><rect x="5" y="11" width="14" height="9" rx="2" {...S}/><path d="M8 11V8a4 4 0 0 1 7.7-1.5" {...S}/><circle cx="12" cy="15.5" r="1.3" {...F}/></>),
select: () => (<><path d="M5 4l13 7-5.5 1.8L9.5 18z" {...S}/></>),
cursor: () => (<><path d="M5 4l13 7-5.5 1.8L9.5 18z" {...S}/></>),
move: () => (<><path d="M12 3v18M3 12h18" {...S}/><path d="M12 3l-2.5 2.5M12 3l2.5 2.5" {...S}/><path d="M12 21l-2.5-2.5M12 21l2.5-2.5" {...S}/><path d="M3 12l2.5-2.5M3 12l2.5 2.5" {...S}/><path d="M21 12l-2.5-2.5M21 12l-2.5 2.5" {...S}/></>),
scale: () => (<><path d="M4 14v6h6" {...S}/><path d="M20 10V4h-6" {...S}/><path d="M4 20L11 13" {...S}/><path d="M20 4l-7 7" {...S}/></>),
rotate: () => (<><path d="M20 12a8 8 0 1 1-2.3-5.6" {...S}/><path d="M20 4v4h-4" {...S}/></>),
// ===== навигация / UI =====
add: () => (<><path d="M12 5v14M5 12h14" {...S}/></>),
plus: () => (<><path d="M12 5v14M5 12h14" {...S}/></>),
close: () => (<><path d="M6 6l12 12M18 6L6 18" {...S}/></>),
cancel: () => (<><path d="M6 6l12 12M18 6L6 18" {...S}/></>),
check: () => (<><path d="M5 12.5l4.5 4.5L19 6.5" {...S}/></>),
save: () => (<><path d="M5 4h11l3 3v13H5z" {...S}/><path d="M8 4v5h7V4" {...S}/><rect x="9" y="13" width="6" height="5" {...S}/></>),
upload: () => (<><path d="M12 17V5" {...S}/><path d="M7 10l5-5 5 5" {...S}/><path d="M5 20h14" {...S}/></>),
download: () => (<><path d="M12 5v12" {...S}/><path d="M7 12l5 5 5-5" {...S}/><path d="M5 20h14" {...S}/></>),
publish: () => (<><path d="M12 17V5" {...S}/><path d="M7 10l5-5 5 5" {...S}/><path d="M5 20h14" {...S}/></>),
search: () => (<><circle cx="11" cy="11" r="6" {...S}/><path d="M20 20l-4.5-4.5" {...S}/></>),
settings: () => (<><circle cx="12" cy="12" r="3" {...S}/><path d="M12 3v3M12 18v3M3 12h3M18 12h3M5.6 5.6l2.1 2.1M16.3 16.3l2.1 2.1M18.4 5.6l-2.1 2.1M7.7 16.3l-2.1 2.1" {...S}/></>),
sliders: () => (<><path d="M5 6h9M18 6h1M5 12h3M12 12h7M5 18h11M19 18h0" {...S}/><circle cx="16" cy="6" r="2" {...S}/><circle cx="10" cy="12" r="2" {...S}/><circle cx="18" cy="18" r="2" {...S}/></>),
more: () => (<><circle cx="5" cy="12" r="1.6" {...F}/><circle cx="12" cy="12" r="1.6" {...F}/><circle cx="19" cy="12" r="1.6" {...F}/></>),
chevronDown: () => (<><path d="M6 9l6 6 6-6" {...S}/></>),
chevronRight: () => (<><path d="M9 6l6 6-6 6" {...S}/></>),
folder: () => (<><path d="M3 7a2 2 0 0 1 2-2h4l2 2h8a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z" {...S}/></>),
'folder-open': () => (<><path d="M3 7a2 2 0 0 1 2-2h4l2 2h8a2 2 0 0 1 2 2v1H5l-2 9z" {...S}/><path d="M3 19l2.2-8a1 1 0 0 1 1-0.8H22l-2.2 8a1 1 0 0 1-1 0.8z" {...S}/></>),
'folder-add': () => (<><path d="M3 7a2 2 0 0 1 2-2h4l2 2h8a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z" {...S}/><path d="M12 10v6M9 13h6" {...S}/></>),
'arrow-up': () => (<><path d="M12 19V5" {...S}/><path d="M6 11l6-6 6 6" {...S}/></>),
'arrow-down': () => (<><path d="M12 5v14" {...S}/><path d="M6 13l6 6 6-6" {...S}/></>),
'arrow-left': () => (<><path d="M19 12H5" {...S}/><path d="M11 6l-6 6 6 6" {...S}/></>),
'arrow-right': () => (<><path d="M5 12h14" {...S}/><path d="M13 6l6 6-6 6" {...S}/></>),
refresh: () => (<><path d="M20 11a8 8 0 0 0-14-4.5L4 9" {...S}/><path d="M4 4v5h5" {...S}/><path d="M4 13a8 8 0 0 0 14 4.5L20 15" {...S}/><path d="M20 20v-5h-5" {...S}/></>),
cycle: () => (<><path d="M20 11a8 8 0 0 0-14-4.5L4 9" {...S}/><path d="M4 4v5h5" {...S}/><path d="M4 13a8 8 0 0 0 14 4.5L20 15" {...S}/><path d="M20 20v-5h-5" {...S}/></>),
flag: () => (<><path d="M6 21V4" {...S}/><path d="M6 4h11l-2.5 4L17 12H6" {...S}/></>),
star: () => (<><path d="M12 3.5l2.6 5.3 5.9 0.8-4.3 4.1 1 5.8-5.2-2.7-5.2 2.7 1-5.8L4.5 9.6l5.9-0.8z" {...S}/></>),
award: () => (<><circle cx="12" cy="9" r="5" {...S}/><path d="M9 13.5L7.5 21l4.5-2.5L16.5 21 15 13.5" {...S}/></>),
warning: () => (<><path d="M12 4l9 16H3z" {...S}/><path d="M12 10v4.5" {...S}/><circle cx="12" cy="17.5" r="1" {...F}/></>),
error: () => (<><circle cx="12" cy="12" r="8.5" {...S}/><path d="M12 7.5v5.5" {...S}/><circle cx="12" cy="16.5" r="1" {...F}/></>),
info: () => (<><circle cx="12" cy="12" r="8.5" {...S}/><path d="M12 11v6" {...S}/><circle cx="12" cy="7.8" r="1" {...F}/></>),
loading: () => (<><path d="M12 3v4M12 17v4M3 12h4M17 12h4M5.6 5.6l2.8 2.8M15.6 15.6l2.8 2.8M18.4 5.6l-2.8 2.8M5.6 18.4l2.8-2.8" {...S}/></>),
bell: () => (<><path d="M6 16V11a6 6 0 0 1 12 0v5l2 2H4z" {...S}/><path d="M10 19a2 2 0 0 0 4 0" {...S}/></>),
'bell-off': () => (<><path d="M4 4l16 16" {...S}/><path d="M8 8v3a13 13 0 0 1-2 5h12" {...S}/><path d="M9 6a6 6 0 0 1 9 5v5" {...S}/><path d="M10 19a2 2 0 0 0 4 0" {...S}/></>),
image: () => (<><rect x="4" y="5" width="16" height="14" rx="2" {...S}/><circle cx="9" cy="10" r="1.6" {...S}/><path d="M5 17l5-5 4 4 3-3 4 4" {...S}/></>),
camera: () => (<><path d="M4 8h3.5L9 6h6l1.5 2H20a1 1 0 0 1 1 1v9a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1V9a1 1 0 0 1 1-1z" {...S}/><circle cx="12" cy="13" r="3.5" {...S}/></>),
// ===== сцена / окружение =====
sun: () => (<><circle cx="12" cy="12" r="4.5" {...S}/><path d="M12 2.5v2.5M12 19v2.5M2.5 12H5M19 12h2.5M5 5l1.8 1.8M17.2 17.2L19 19M19 5l-1.8 1.8M6.8 17.2L5 19" {...S}/></>),
moon: () => (<><path d="M20 13a8 8 0 0 1-10-10 8 8 0 1 0 10 10z" {...S}/></>),
sunrise: () => (<><path d="M3 19h18" {...S}/><path d="M8 19a4 4 0 0 1 8 0" {...S}/><path d="M12 3v5M5 11l1.8 1.8M19 11l-1.8 1.8M3 15h2M19 15h2" {...S}/><path d="M9 6l3-3 3 3" {...S}/></>),
sunset: () => (<><path d="M3 19h18" {...S}/><path d="M8 19a4 4 0 0 1 8 0" {...S}/><path d="M12 8V3M5 11l1.8 1.8M19 11l-1.8 1.8M3 15h2M19 15h2" {...S}/><path d="M9 5l3 3 3-3" {...S}/></>),
cloud: () => (<><path d="M7 18a4 4 0 0 1-0.5-8A6 6 0 0 1 18 9.5 3.5 3.5 0 0 1 17.5 18z" {...S}/></>),
fog: () => (<><path d="M7 12a4 4 0 0 1-0.5-8A6 6 0 0 1 18 5.5 3.5 3.5 0 0 1 17.5 12z" {...S}/><path d="M5 16h14M7 19h10" {...S}/></>),
rain: () => (<><path d="M7 13a4 4 0 0 1-0.5-8A6 6 0 0 1 18 6.5 3.5 3.5 0 0 1 17.5 13z" {...S}/><path d="M8 16l-1 3M12 16l-1 3M16 16l-1 3" {...S}/></>),
wind: () => (<><path d="M3 9h9a3 3 0 1 0-3-3" {...S}/><path d="M3 14h13a3 3 0 1 1-3 3" {...S}/><path d="M3 19h6a2.5 2.5 0 1 1-2.5 2.5" {...S}/></>),
water: () => (<><path d="M4 9c2 0 2-1.5 4-1.5S12 9 14 9s2-1.5 4-1.5S20 9 22 9" {...S}/><path d="M2 14c2 0 2-1.5 4-1.5S10 14 12 14s2-1.5 4-1.5S20 14 22 14" {...S}/><path d="M4 19c2 0 2-1.5 4-1.5S12 19 14 19s2-1.5 4-1.5" {...S}/></>),
droplet: () => (<><path d="M12 3.5C12 3.5 6 10 6 14.5a6 6 0 0 0 12 0C18 10 12 3.5 12 3.5z" {...S}/></>),
mountain: () => (<><path d="M3 19l6-11 4 7 2.5-4L21 19z" {...S}/></>),
trees: () => (<><path d="M12 3l4 5h-2.5l3 4h-2.5l3.5 4.5H6.5L10 12H7.5l3-4H8z" {...S}/><path d="M12 16.5V21" {...S}/><path d="M9.5 21h5" {...S}/></>),
tree: () => (<><path d="M12 3l5 7h-3l4 5H6l4-5H7z" {...S}/><path d="M12 15v6" {...S}/></>),
flower: () => (<><circle cx="12" cy="9" r="2.3" {...S}/><path d="M12 6.7a2.5 2.5 0 1 1 2.5 2.3M14.5 9a2.5 2.5 0 1 1-1 4.2M11 13.2a2.5 2.5 0 1 1-1.5-4.2M9.5 9A2.5 2.5 0 1 1 12 6.7" {...S}/><path d="M12 13.5V21" {...S}/><path d="M12 17c-1.5-1-3-1-4 0M12 19c1.5-1 3-1 4 0" {...S}/></>),
sprout: () => (<><path d="M12 20v-7" {...S}/><path d="M12 13C12 9 9 7 5 7c0 4 3 6 7 6z" {...S}/><path d="M12 11c0-3 2-5 6-5 0 3-2 5-6 5z" {...S}/></>),
leaf: () => (<><path d="M5 19C4 11 9 5 19 5c0 10-6 14-13 14a4 4 0 0 1-1-0z" {...S}/><path d="M6 18C9 14 13 11 17 9" {...S}/></>),
plant: () => (<><path d="M12 20v-7" {...S}/><path d="M12 13C12 9 9 7 5 7c0 4 3 6 7 6z" {...S}/><path d="M12 11c0-3 2-5 6-5 0 3-2 5-6 5z" {...S}/></>),
grass: () => (<><path d="M6 20c0-5-1-7-3-9 3 0 5 2 5 6" {...S}/><path d="M12 20c0-7-0-9 0-12 1 4 1 8 1 12" {...S}/><path d="M18 20c0-5 1-7 3-9-3 0-5 2-5 6" {...S}/></>),
bush: () => (<><path d="M6 18a3.5 3.5 0 0 1-0.5-7A5 5 0 0 1 15 8a3.5 3.5 0 0 1 3 6.5A3.5 3.5 0 0 1 16 18z" {...S}/><path d="M6 18h12" {...S}/></>),
mushroom: () => (<><path d="M4 12a8 8 0 0 1 16 0z" {...S}/><path d="M10 12v5a2 2 0 0 0 4 0v-5" {...S}/><circle cx="9" cy="9" r="1" {...F}/><circle cx="14" cy="8.5" r="1" {...F}/></>),
stone: () => (<><path d="M5 14l4-7h6l4 7-4 6H9z" {...S}/></>),
rock: () => (<><path d="M5 14l4-7h6l4 7-4 6H9z" {...S}/></>),
snowflake: () => (<><path d="M12 3v18M3.7 7.5l16.6 9M20.3 7.5l-16.6 9" {...S}/><path d="M12 6.5l2.5-2M12 6.5l-2.5-2M12 17.5l2.5 2M12 17.5l-2.5 2" {...S}/></>),
fire: () => (<><path d="M12 3c4 4 5 7 5 10a5 5 0 0 1-10 0c0-2 1-3.5 2.5-5 0.5 1.5 1.5 2 2.5 2 0-3-1-5-2-7z" {...S}/></>),
waves: () => (<><path d="M3 9c2 0 2-1.5 4-1.5S12 9 14 9s2-1.5 4-1.5S20 9 22 9" {...S}/><path d="M3 15c2 0 2-1.5 4-1.5S12 15 14 15s2-1.5 4-1.5S20 15 22 15" {...S}/></>),
sparkle: () => (<><path d="M12 3c0 4 1 6 6 6-5 0-6 2-6 6 0-4-1-6-6-6 5 0 6-2 6-6z" {...S}/></>),
sparkles: () => (<><path d="M11 3c0 3.5 1 5 5 5-4 0-5 1.5-5 5 0-3.5-1-5-5-5 4 0 5-1.5 5-5z" {...S}/><path d="M18 13c0 2 0.7 3 3 3-2.3 0-3 1-3 3 0-2-0.7-3-3-3 2.3 0 3-1 3-3z" {...S}/></>),
// ===== звук =====
music: () => (<><path d="M9 18V6l10-2v12" {...S}/><circle cx="6.5" cy="18" r="2.5" {...S}/><circle cx="16.5" cy="16" r="2.5" {...S}/></>),
music2: () => (<><path d="M11 17V5l8-1.5" {...S}/><circle cx="8" cy="17" r="3" {...S}/><circle cx="19" cy="15.5" r="2" {...S}/></>),
sound: () => (<><path d="M4 9v6h4l5 4V5L8 9z" {...S}/><path d="M16 9.5a3.5 3.5 0 0 1 0 5M18.5 7a7 7 0 0 1 0 10" {...S}/></>),
mute: () => (<><path d="M4 9v6h4l5 4V5L8 9z" {...S}/><path d="M16 10l5 5M21 10l-5 5" {...S}/></>),
mic: () => (<><rect x="9" y="3" width="6" height="11" rx="3" {...S}/><path d="M6 11a6 6 0 0 0 12 0" {...S}/><path d="M12 17v4M9 21h6" {...S}/></>),
// ===== мир / карта =====
globe: () => (<><circle cx="12" cy="12" r="8.5" {...S}/><path d="M3.5 12h17" {...S}/><path d="M12 3.5c2.5 2.5 3.8 5.5 3.8 8.5S14.5 18 12 20.5C9.5 18 8.2 15 8.2 12S9.5 6 12 3.5z" {...S}/></>),
map: () => (<><path d="M9 4L3 6.5v13L9 17l6 2.5 6-2.5v-13L15 7 9 4.5z" {...S}/><path d="M9 4.5V17M15 7v12.5" {...S}/></>),
pin: () => (<><path d="M12 21s7-5.5 7-11a7 7 0 0 0-14 0c0 5.5 7 11 7 11z" {...S}/><circle cx="12" cy="10" r="2.5" {...S}/></>),
compass: () => (<><circle cx="12" cy="12" r="8.5" {...S}/><path d="M15.5 8.5l-2 5-5 2 2-5z" {...S}/><circle cx="12" cy="12" r="1" {...F}/></>),
// ===== игрок / геймплей =====
user: () => (<><circle cx="12" cy="8" r="4" {...S}/><path d="M4.5 20a7.5 7.5 0 0 1 15 0z" {...S}/></>),
avatar: () => (<><circle cx="12" cy="8" r="4" {...S}/><path d="M4.5 20a7.5 7.5 0 0 1 15 0z" {...S}/></>),
users: () => (<><circle cx="9" cy="8" r="3.5" {...S}/><path d="M3 19a6 6 0 0 1 12 0z" {...S}/><path d="M16 5a3.5 3.5 0 0 1 0 7" {...S}/><path d="M17 13.5a6 6 0 0 1 4 5.5" {...S}/></>),
heart: () => (<><path d="M12 20S4 14.5 4 9a4.5 4.5 0 0 1 8-2.8A4.5 4.5 0 0 1 20 9c0 5.5-8 11-8 11z" {...S}/></>),
shield: () => (<><path d="M12 3l8 3v6c0 5-3.5 8-8 9.5C7.5 18 4 15 4 12V6z" {...S}/></>),
sword: () => (<><path d="M14 4h6v6L9 21l-3-3z" {...S}/><path d="M5 14l5 5M3 17l4 4" {...S}/></>),
crosshair: () => (<><circle cx="12" cy="12" r="8" {...S}/><path d="M12 3v4M12 17v4M3 12h4M17 12h4" {...S}/><circle cx="12" cy="12" r="1.3" {...F}/></>),
target: () => (<><circle cx="12" cy="12" r="8.5" {...S}/><circle cx="12" cy="12" r="5" {...S}/><circle cx="12" cy="12" r="1.5" {...F}/></>),
zap: () => (<><path d="M13 3L5 13h5l-1 8 8-11h-5z" {...S}/></>),
lightning: () => (<><path d="M13 3L5 13h5l-1 8 8-11h-5z" {...S}/></>),
skull: () => (<><path d="M5 11a7 7 0 0 1 14 0v3l-1.5 2H6.5L5 14z" {...S}/><circle cx="9" cy="11" r="1.6" {...F}/><circle cx="15" cy="11" r="1.6" {...F}/><path d="M9 16v3M12 16v3M15 16v3" {...S}/></>),
bomb: () => (<><circle cx="11" cy="14" r="7" {...S}/><path d="M15.5 8.5l2.5-2.5" {...S}/><path d="M18 6l1-2 2 1-1 2z" {...S}/><path d="M17 4.5C18 3 20 3 21 4" {...S}/></>),
crown: () => (<><path d="M4 8l4 4 4-6 4 6 4-4-1.5 11H5.5z" {...S}/><path d="M5.5 19h13" {...S}/></>),
trophy: () => (<><path d="M7 4h10v5a5 5 0 0 1-10 0z" {...S}/><path d="M7 6H4v2a3 3 0 0 0 3 3M17 6h3v2a3 3 0 0 1-3 3" {...S}/><path d="M12 14v4M8.5 21h7M10 21v-3h4v3" {...S}/></>),
joystick: () => (<><circle cx="12" cy="6" r="3" {...S}/><path d="M12 9v6" {...S}/><path d="M6 21a6 6 0 0 1 12 0z" {...S}/></>),
gamepad: () => (<><rect x="3" y="8" width="18" height="10" rx="4" {...S}/><path d="M7.5 11.5v3M6 13h3" {...S}/><circle cx="16" cy="12" r="1" {...F}/><circle cx="18.5" cy="14.5" r="1" {...F}/></>),
backpack: () => (<><path d="M6 9a6 6 0 0 1 12 0v11H6z" {...S}/><path d="M9 9V6a3 3 0 0 1 6 0v3" {...S}/><path d="M6 14h12" {...S}/><rect x="10" y="14" width="4" height="4" {...S}/></>),
box: () => (<><path d="M12 3l8 4.5v9L12 21l-8-4.5v-9z" {...S}/><path d="M4 7.5l8 4.5 8-4.5M12 12v9" {...S}/></>),
hammer: () => (<><path d="M14 6l4 4-3 3-4-4z" {...S}/><path d="M10 9l2 2-7 7-2-2z" {...S}/><path d="M13 5l4 4 3-2-4-4z" {...S}/></>),
wrench: () => (<><path d="M15 4a5 5 0 0 0-2 9.5L6 20.5l-2.5-2.5 7-7A5 5 0 0 0 19.5 6l-3 3-2.5-2.5 3-3A5 5 0 0 0 15 4z" {...S}/></>),
pickaxe: () => (<><path d="M4 6c4-2 9-1 13 3M20 6c-4-2-9-1-13 3" {...S}/><path d="M9 9l9 9" {...S}/><path d="M10 8L8 10" {...S}/></>),
bug: () => (<><rect x="8" y="8" width="8" height="11" rx="4" {...S}/><path d="M8 12H4M8 16H4M16 12h4M16 16h4M8 9L5 6M16 9l3-3" {...S}/><path d="M9 8a3 3 0 0 1 6 0" {...S}/></>),
ghost: () => (<><path d="M5 20V11a7 7 0 0 1 14 0v9l-2.3-2-2.3 2-2.4-2-2.4 2-2.3-2z" {...S}/><circle cx="9.5" cy="10.5" r="1.2" {...F}/><circle cx="14.5" cy="10.5" r="1.2" {...F}/></>),
zombie: () => (<><path d="M5 20V11a7 7 0 0 1 14 0v9l-2.3-2-2.3 2-2.4-2-2.4 2-2.3-2z" {...S}/><path d="M8 10.5l2.5 1M13.5 11.5l2.5-1" {...S}/><circle cx="9.5" cy="11" r="1" {...F}/><circle cx="14.5" cy="11" r="1" {...F}/></>),
spawner: () => (<><rect x="4" y="4" width="16" height="16" rx="2" {...S}/><circle cx="12" cy="12" r="4" {...S}/><path d="M12 4v2M12 18v2M4 12h2M18 12h2" {...S}/></>),
// ===== геометрия =====
cube: () => (<><path d="M12 3l8 4.5v9L12 21l-8-4.5v-9z" {...S}/><path d="M4 7.5l8 4.5 8-4.5M12 12v9" {...S}/></>),
square: () => (<><rect x="4.5" y="4.5" width="15" height="15" rx="1.5" {...S}/></>),
sphere: () => (<><circle cx="12" cy="12" r="8.5" {...S}/><ellipse cx="12" cy="12" rx="3.5" ry="8.5" {...S}/><path d="M3.5 12h17" {...S}/></>),
circle: () => (<><circle cx="12" cy="12" r="8.5" {...S}/></>),
triangle: () => (<><path d="M12 4l9 16H3z" {...S}/></>),
hexagon: () => (<><path d="M12 3l7.5 4.3v9.4L12 21l-7.5-4.3V7.3z" {...S}/></>),
cylinder: () => (<><ellipse cx="12" cy="6" rx="7" ry="3" {...S}/><path d="M5 6v12a7 3 0 0 0 14 0V6" {...S}/></>),
cone: () => (<><path d="M12 3l7 14H5z" {...S}/><ellipse cx="12" cy="17" rx="7" ry="3" {...S}/></>),
torus: () => (<><ellipse cx="12" cy="12" rx="9" ry="6" {...S}/><ellipse cx="12" cy="12" rx="3.5" ry="2" {...S}/></>),
boxes: () => (<><path d="M7 3l4 2.3v4.7L7 12.3 3 10V5.3z" {...S}/><path d="M17 3l4 2.3v4.7l-4 2.3-4-2.3V5.3z" {...S}/><path d="M12 13l4 2.3V20l-4 2.3L8 20v-4.7z" {...S}/></>),
layers: () => (<><path d="M12 3l9 5-9 5-9-5z" {...S}/><path d="M3 13l9 5 9-5" {...S}/></>),
grid: () => (<><rect x="4" y="4" width="16" height="16" rx="1.5" {...S}/><path d="M4 9.3h16M4 14.6h16M9.3 4v16M14.6 4v16" {...S}/></>),
archive: () => (<><rect x="3" y="4" width="18" height="4.5" rx="1" {...S}/><path d="M5 8.5V19a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1V8.5" {...S}/><path d="M10 12.5h4" {...S}/></>),
component: () => (<><path d="M12 3l3 3-3 3-3-3z" {...S}/><path d="M6 9l3 3-3 3-3-3z" {...S}/><path d="M18 9l3 3-3 3-3-3z" {...S}/><path d="M12 15l3 3-3 3-3-3z" {...S}/></>),
// ===== UI / художественные =====
palette: () => (<><path d="M12 3a9 9 0 0 0 0 18c1.6 0 2-1 1.4-2-0.8-1.2 0-2.5 1.6-2.5H17a4 4 0 0 0 4-4c0-5-4-9.5-9-9.5z" {...S}/><circle cx="8" cy="11" r="1.3" {...F}/><circle cx="12" cy="7.5" r="1.3" {...F}/><circle cx="16" cy="10" r="1.3" {...F}/></>),
type: () => (<><path d="M5 6V4.5h14V6" {...S}/><path d="M12 4.5V20M9 20h6" {...S}/></>),
'align-left': () => (<><path d="M4 6h16M4 11h10M4 16h13M4 20h7" {...S}/></>),
eraser: () => (<><path d="M9 19l-4-4a2 2 0 0 1 0-3l8-8a2 2 0 0 1 3 0l4 4a2 2 0 0 1 0 3l-8 8z" {...S}/><path d="M9 19h11M8.5 9.5l6 6" {...S}/></>),
paintbrush: () => (<><path d="M19 4c-3 1-6 4-8 7l2 2c3-2 6-5 7-8z" {...S}/><path d="M11 13l-3 3a3 3 0 0 1-5 1c2 0 2-3 4-4z" {...S}/></>),
brush: () => (<><path d="M19 4c-3 1-6 4-8 7l2 2c3-2 6-5 7-8z" {...S}/><path d="M11 13l-3 3a3 3 0 0 1-5 1c2 0 2-3 4-4z" {...S}/></>),
highlighter: () => (<><path d="M13 3l8 8-7 7H9l-3-3z" {...S}/><path d="M9 18l-2 2H4v-3l2-2" {...S}/><path d="M11 5l8 8" {...S}/></>),
ruler: () => (<><path d="M3 16L16 3l5 5L8 21z" {...S}/><path d="M8 8l2 2M11 5l2 2M5 11l2 2M14 14l2 2M11 17l2 2" {...S}/></>),
// ===== системные / медиа =====
play: () => (<><path d="M7 4.5l13 7.5-13 7.5z" {...S}/></>),
pause: () => (<><rect x="6.5" y="4.5" width="4" height="15" rx="1" {...S}/><rect x="13.5" y="4.5" width="4" height="15" rx="1" {...S}/></>),
stop: () => (<><rect x="5.5" y="5.5" width="13" height="13" rx="2" {...S}/></>),
forward: () => (<><path d="M4 5l8 7-8 7z" {...S}/><path d="M13 5l8 7-8 7z" {...S}/></>),
rewind: () => (<><path d="M20 5l-8 7 8 7z" {...S}/><path d="M11 5l-8 7 8 7z" {...S}/></>),
clock: () => (<><circle cx="12" cy="12" r="8.5" {...S}/><path d="M12 7v5.5l4 2.5" {...S}/></>),
hourglass: () => (<><path d="M6 4h12M6 20h12" {...S}/><path d="M7 4c0 5 5 6 5 8s-5 3-5 8M17 4c0 5-5 6-5 8s5 3 5 8" {...S}/></>),
chat: () => (<><path d="M4 5h16v11H9l-4 4z" {...S}/><path d="M8 9h8M8 12h5" {...S}/></>),
message: () => (<><path d="M4 5h16v11H9l-4 4z" {...S}/><path d="M8 9h8M8 12h5" {...S}/></>),
file: () => (<><path d="M6 3h8l5 5v13H6z" {...S}/><path d="M14 3v5h5" {...S}/></>),
'file-code': () => (<><path d="M6 3h8l5 5v13H6z" {...S}/><path d="M14 3v5h5" {...S}/><path d="M10 13l-2 2 2 2M14 13l2 2-2 2" {...S}/></>),
code: () => (<><path d="M8 8l-4 4 4 4M16 8l4 4-4 4" {...S}/><path d="M13.5 6l-3 12" {...S}/></>),
script: () => (<><path d="M6 3h8l5 5v13H6z" {...S}/><path d="M14 3v5h5" {...S}/><path d="M10 13l-2 2 2 2M14 13l2 2-2 2" {...S}/></>),
clipboard: () => (<><rect x="5" y="5" width="14" height="16" rx="2" {...S}/><rect x="9" y="3" width="6" height="4" rx="1" {...S}/><path d="M9 12h6M9 16h4" {...S}/></>),
tag: () => (<><path d="M3 11V4h7l11 11-7 7z" {...S}/><circle cx="7.5" cy="7.5" r="1.5" {...S}/></>),
anchor: () => (<><circle cx="12" cy="6" r="2.5" {...S}/><path d="M12 8.5V21" {...S}/><path d="M8 12H16" {...S}/><path d="M4 13a8 8 0 0 0 16 0" {...S}/><path d="M4 13h2M20 13h-2" {...S}/></>),
magnet: () => (<><path d="M6 3v8a6 6 0 0 0 12 0V3" {...S}/><path d="M6 3h4v8a2 2 0 0 0 4 0V3h4" {...S}/><path d="M6 7h4M14 7h4" {...S}/></>),
link: () => (<><path d="M9.5 14.5l5-5" {...S}/><path d="M8 12l-2 2a3.5 3.5 0 0 0 5 5l2-2" {...S}/><path d="M16 12l2-2a3.5 3.5 0 0 0-5-5l-2 2" {...S}/></>),
flask: () => (<><path d="M9 3h6M10 3v7l-5 8a1 1 0 0 0 1 1.5h12A1 1 0 0 0 19 18l-5-8V3" {...S}/><path d="M7.5 14h9" {...S}/></>),
beaker: () => (<><path d="M8 3h8M9 3v6l-3 9a1 1 0 0 0 1 1.5h10A1 1 0 0 0 18 18l-3-9V3" {...S}/><path d="M7 13h10" {...S}/></>),
atom: () => (<><circle cx="12" cy="12" r="1.6" {...F}/><ellipse cx="12" cy="12" rx="9" ry="4" {...S}/><ellipse cx="12" cy="12" rx="9" ry="4" transform="rotate(60 12 12)" {...S}/><ellipse cx="12" cy="12" rx="9" ry="4" transform="rotate(120 12 12)" {...S}/></>),
home: () => (<><path d="M3 11l9-7 9 7" {...S}/><path d="M5 10v10h14V10" {...S}/><path d="M10 20v-6h4v6" {...S}/></>),
building: () => (<><rect x="5" y="3" width="14" height="18" rx="1" {...S}/><path d="M9 7h2M13 7h2M9 11h2M13 11h2M9 15h2M13 15h2" {...S}/><path d="M10 21v-3h4v3" {...S}/></>),
door: () => (<><path d="M5 21V3h14v18" {...S}/><path d="M5 21h14" {...S}/><circle cx="15" cy="12" r="1" {...F}/></>),
construction: () => (<><rect x="3" y="9" width="18" height="10" rx="1" {...S}/><path d="M5 9l5 10M11 9l5 10M17 9l4 8" {...S}/><path d="M3 9V6h18v3" {...S}/></>),
ladder: () => (<><path d="M8 3v18M16 3v18" {...S}/><path d="M8 7h8M8 11h8M8 15h8M8 19h8" {...S}/></>),
package: () => (<><path d="M12 3l8 4.5v9L12 21l-8-4.5v-9z" {...S}/><path d="M4 7.5l8 4.5 8-4.5M12 12v9" {...S}/><path d="M8 5.2l8 4.6" {...S}/></>),
hash: () => (<><path d="M9 3l-2 18M17 3l-2 18M4 8.5h16M3 15.5h16" {...S}/></>),
// ===== прочее =====
flame: () => (<><path d="M12 3c4 4 5 7 5 10a5 5 0 0 1-10 0c0-2 1-3.5 2.5-5 0.5 1.5 1.5 2 2.5 2 0-3-1-5-2-7z" {...S}/></>),
'thumbs-up': () => (<><path d="M7 11l4-8a2.5 2.5 0 0 1 2.5 2.5V9h5a2 2 0 0 1 2 2.4l-1.5 7A2 2 0 0 1 17 20H7z" {...S}/><rect x="3" y="11" width="4" height="9" rx="1" {...S}/></>),
'thumbs-down': () => (<><path d="M7 13l4 8a2.5 2.5 0 0 0 2.5-2.5V15h5a2 2 0 0 0 2-2.4l-1.5-7A2 2 0 0 0 17 4H7z" {...S}/><rect x="3" y="4" width="4" height="9" rx="1" {...S}/></>),
graduation: () => (<><path d="M2 9l10-4 10 4-10 4z" {...S}/><path d="M6 11v5c0 1.5 3 3 6 3s6-1.5 6-3v-5" {...S}/><path d="M22 9v6" {...S}/></>),
puzzle: () => (<><path d="M9 4h6v3a2 2 0 0 0 4 0V4h1v6h-3a2 2 0 0 0 0 4h3v6h-6v-3a2 2 0 0 0-4 0v3H4v-6h3a2 2 0 0 0 0-4H4V4z" {...S}/></>),
smartphone: () => (<><rect x="6" y="3" width="12" height="18" rx="2.5" {...S}/><path d="M10.5 18h3" {...S}/></>),
activity: () => (<><path d="M3 12h4l3-7 4 14 3-7h4" {...S}/></>),
book: () => (<><path d="M5 4h12a2 2 0 0 1 2 2v14H7a2 2 0 0 0-2 2z" {...S}/><path d="M5 20a2 2 0 0 1 2-2h12" {...S}/></>),
'book-open': () => (<><path d="M12 6C10 4.5 7 4 4 4.5v13c3-0.5 6 0 8 1.5" {...S}/><path d="M12 6c2-1.5 5-2 8-1.5v13c-3-0.5-6 0-8 1.5z" {...S}/></>),
'book-text': () => (<><path d="M5 4h12a2 2 0 0 1 2 2v14H7a2 2 0 0 0-2 2z" {...S}/><path d="M5 20a2 2 0 0 1 2-2h12" {...S}/><path d="M9 8h6M9 11h5" {...S}/></>),
library: () => (<><path d="M5 4v15M9 4v15" {...S}/><path d="M5 19h4M5 4h4" {...S}/><path d="M12.5 5l4-1 3.5 14-4 1z" {...S}/></>),
trending: () => (<><path d="M3 17l6-6 4 4 8-8" {...S}/><path d="M16 7h5v5" {...S}/></>),
medal: () => (<><circle cx="12" cy="14" r="6" {...S}/><path d="M8 8.5L6 3h4l2 4M16 8.5L18 3h-4l-2 4" {...S}/><path d="M12 11.5l1.2 2.4 2.6 0.4-1.9 1.8 0.4 2.6-2.3-1.2-2.3 1.2 0.4-2.6-1.9-1.8 2.6-0.4z" {...S}/></>),
baby: () => (<><circle cx="12" cy="6" r="3" {...S}/><path d="M9 5.5c1 1 5 1 6 0" {...S}/><path d="M12 9v6M12 12l-4 2M12 12l4 2M12 15l-3 5M12 15l3 5" {...S}/></>),
'user-square': () => (<><rect x="4" y="4" width="16" height="16" rx="3" {...S}/><circle cx="12" cy="10" r="3" {...S}/><path d="M6.5 18a5.5 5.5 0 0 1 11 0" {...S}/></>),
smile: () => (<><circle cx="12" cy="12" r="8.5" {...S}/><path d="M8.5 14a4.5 4.5 0 0 0 7 0" {...S}/><circle cx="9" cy="9.5" r="1" {...F}/><circle cx="15" cy="9.5" r="1" {...F}/></>),
bulb: () => (<><path d="M8 14a6 6 0 1 1 8 0c-1 1-1.3 1.7-1.5 3h-5c-0.2-1.3-0.5-2-1.5-3z" {...S}/><path d="M9.5 20h5M10 22h4" {...S}/></>),
keyboard: () => (<><rect x="3" y="6" width="18" height="12" rx="2" {...S}/><path d="M8 16h8" {...S}/><circle cx="6" cy="9.5" r="0.6" {...F}/><circle cx="9.5" cy="9.5" r="0.6" {...F}/><circle cx="13" cy="9.5" r="0.6" {...F}/><circle cx="16.5" cy="9.5" r="0.6" {...F}/><circle cx="7.5" cy="13" r="0.6" {...F}/><circle cx="11" cy="13" r="0.6" {...F}/><circle cx="14.5" cy="13" r="0.6" {...F}/></>),
stethoscope: () => (<><path d="M6 3v6a4 4 0 0 0 8 0V3" {...S}/><path d="M10 13v3a4 4 0 0 0 8 0v-1" {...S}/><circle cx="18" cy="9" r="2.5" {...S}/></>),
monitor: () => (<><rect x="3" y="4" width="18" height="12" rx="2" {...S}/><path d="M9 20h6M12 16v4" {...S}/></>),
rocket: () => (<><path d="M12 3c3.5 2 5 6 5 9l-3 3h-4l-3-3c0-3 1.5-7 5-9z" {...S}/><circle cx="12" cy="9" r="1.8" {...S}/><path d="M9 15c-2 1-2.5 3-2.5 5 2 0 4-0.5 5-2.5M15 15c2 1 2.5 3 2.5 5-2 0-4-0.5-5-2.5" {...S}/></>),
chart: () => (<><path d="M4 4v16h16" {...S}/><path d="M8 16v-4M12 16v-8M16 16v-6" {...S}/></>),
'bar-chart': () => (<><path d="M4 4v16h16" {...S}/><path d="M8 16v-4M12 16v-8M16 16v-6" {...S}/></>),
radio: () => (<><circle cx="12" cy="12" r="2.5" {...S}/><path d="M7.5 7.5a6.5 6.5 0 0 0 0 9M16.5 7.5a6.5 6.5 0 0 1 0 9" {...S}/><path d="M4.5 4.5a11 11 0 0 0 0 15M19.5 4.5a11 11 0 0 1 0 15" {...S}/></>),
dice: () => (<><rect x="4" y="4" width="16" height="16" rx="3" {...S}/><circle cx="9" cy="9" r="1.3" {...F}/><circle cx="15" cy="9" r="1.3" {...F}/><circle cx="12" cy="12" r="1.3" {...F}/><circle cx="9" cy="15" r="1.3" {...F}/><circle cx="15" cy="15" r="1.3" {...F}/></>),
swords: () => (<><path d="M14 4h6v6L9 21l-3-3z" {...S}/><path d="M5 14l5 5M3 17l4 4" {...S}/><path d="M10 4H4v6l11 11 3-3z" {...S}/><path d="M19 14l-5 5M21 17l-4 4" {...S}/></>),
footprints: () => (<><path d="M7 5c1.5 0 2.5 1.5 2.5 4S8.5 14 7 14s-2.5-2.5-2.5-5S5.5 5 7 5z" {...S}/><path d="M5 16h4v2a2 2 0 0 1-4 0z" {...S}/><path d="M17 8c1.5 0 2.5 1.5 2.5 4S18.5 17 17 17s-2.5-2.5-2.5-5S15.5 8 17 8z" {...S}/><path d="M15 19h4v2a2 2 0 0 1-4 0z" {...S}/></>),
send: () => (<><path d="M21 3L3 10.5l7 3 3 7z" {...S}/><path d="M21 3l-11 10.5" {...S}/></>),
// ===== примитивы редактора 3D =====
'prim-cube': () => (<><path d="M12 3l8 4.5v9L12 21l-8-4.5v-9z" {...S}/><path d="M4 7.5l8 4.5 8-4.5M12 12v9" {...S}/></>),
'prim-sphere': () => (<><circle cx="12" cy="12" r="8.5" {...S}/><ellipse cx="12" cy="12" rx="3.5" ry="8.5" {...S}/><path d="M3.5 12h17" {...S}/></>),
'prim-cylinder': () => (<><ellipse cx="12" cy="6" rx="7" ry="3" {...S}/><path d="M5 6v12a7 3 0 0 0 14 0V6" {...S}/></>),
'prim-cone': () => (<><path d="M12 3l7 14H5z" {...S}/><ellipse cx="12" cy="17" rx="7" ry="3" {...S}/></>),
'prim-plane': () => (<><path d="M3 15l6-6h12l-6 6z" {...S}/></>),
'prim-torus': () => (<><ellipse cx="12" cy="12" rx="9" ry="6" {...S}/><ellipse cx="12" cy="12" rx="3.5" ry="2" {...S}/></>),
'prim-wedge': () => (<><path d="M4 19V8l8-3v14z" {...S}/><path d="M4 19h16l-8-3" {...S}/><path d="M12 5l8 11" {...S}/></>),
'prim-cornerwedge': () => (<><path d="M4 19V6l8 3v10z" {...S}/><path d="M4 19h12l-4-1.5" {...S}/><path d="M4 6l12 11" {...S}/></>),
'prim-trigger': () => (<><path d="M12 3l8 4.5v9L12 21l-8-4.5v-9z" {...S} strokeDasharray="3 3"/><path d="M4 7.5l8 4.5 8-4.5M12 12v9" {...S} strokeDasharray="3 3"/></>),
'prim-checkpoint': () => (<><path d="M6 21V4" {...S}/><path d="M6 4h6v3h6v3h-6V7H6" {...S}/><path d="M6 12h12v3h-6v-3" {...S}/></>),
'prim-light': () => (<><path d="M8 14a6 6 0 1 1 8 0c-1 1-1.3 1.7-1.5 3h-5c-0.2-1.3-0.5-2-1.5-3z" {...S}/><path d="M9.5 20h5M10 22h4" {...S}/></>),
'prim-emitter': () => (<><circle cx="12" cy="12" r="3" {...S}/><path d="M12 3v3M12 18v3M3 12h3M18 12h3M5.5 5.5l2 2M16.5 16.5l2 2M18.5 5.5l-2 2M5.5 18.5l2-2" {...S}/></>),
'prim-portal': () => (<><ellipse cx="12" cy="12" rx="5" ry="8.5" {...S}/><ellipse cx="12" cy="12" rx="2" ry="4" {...S}/></>),
'prim-spike': () => (<><path d="M3 19l3-9 3 9M9 19l3-11 3 11M15 19l3-9 3 9" {...S}/><path d="M3 19h18" {...S}/></>),
'prim-finish': () => (<><path d="M6 21V4" {...S}/><path d="M6 4h12v10H6" {...S}/><path d="M6 7h4v3.5h4V7h4M6 10.5h4M10 14v-3.5h4V14" {...S}/></>),
'prim-coin': () => (<><circle cx="12" cy="12" r="8.5" {...S}/><circle cx="12" cy="12" r="5" {...S}/><path d="M12 9.5v5" {...S}/></>),
// ===== тайлы редактора 3D =====
'tile-floor': () => (<><rect x="3" y="9" width="18" height="6" rx="1" {...S}/><path d="M9 9v6M15 9v6" {...S}/></>),
'tile-spike': () => (<><path d="M3 18l3-9 3 9M9 18l3-9 3 9M15 18l3-9 3 9" {...S}/><path d="M3 18h18" {...S}/></>),
'tile-orb': () => (<><circle cx="12" cy="12" r="6" {...S}/><circle cx="12" cy="12" r="2" {...F}/></>),
'tile-pad': () => (<><rect x="4" y="16" width="16" height="4" rx="1" {...S}/><path d="M12 13V4" {...S}/><path d="M7 9l5-5 5 5" {...S}/></>),
'tile-gravity': () => (<><path d="M9 3v18M9 3l-3 3M9 3l3 3" {...S}/><path d="M15 21V3M15 21l-3-3M15 21l3-3" {...S}/></>),
'tile-moving': () => (<><rect x="3" y="10" width="18" height="5" rx="1" {...S}/><path d="M6 7L3 12.5 6 18M18 7l3 5.5L18 18" {...S}/></>),
'tile-portal-square': () => (<><rect x="6" y="3" width="12" height="18" rx="2" {...S}/><rect x="10" y="9" width="4" height="6" rx="1.5" {...S}/></>),
'tile-step': () => (<><path d="M3 20v-4h6v-4h6V8h6" {...S}/><path d="M3 20h18" {...S}/></>),
// ===== шаблоны =====
'tpl-platformer': () => (<><path d="M3 19h4M10 14h5M18 9h3" {...S}/><rect x="6" y="6" width="4" height="4" rx="0.5" {...S}/><path d="M8 14V10" {...S}/></>),
'tpl-shooter': () => (<><circle cx="12" cy="12" r="8" {...S}/><path d="M12 3v4M12 17v4M3 12h4M17 12h4" {...S}/><circle cx="12" cy="12" r="1.5" {...F}/></>),
'tpl-racing': () => (<><path d="M5 16h14M5 16l2-9h10l2 9" {...S}/><circle cx="8" cy="19" r="2" {...S}/><circle cx="16" cy="19" r="2" {...S}/></>),
'tpl-plain': () => (<><path d="M3 16h18" {...S}/><path d="M3 16c0-2 9-2 18 0" {...S}/><path d="M7 16v-2M12 16v-3M17 16v-2" {...S}/></>),
'tpl-hills': () => (<><path d="M3 18c3-6 6-6 9 0s6 6 9 0" {...S}/><path d="M3 21h18" {...S}/><circle cx="18" cy="6" r="2.5" {...S}/></>),
'tpl-island': () => (<><path d="M3 17c2 0 2-1.5 4-1.5S12 17 14 17s2-1.5 4-1.5S20 17 22 17" {...S}/><path d="M6 16c1-4 3-6 6-6s5 2 6 6" {...S}/><path d="M12 10V6" {...S}/><path d="M12 6c-1.5-1.5-3.5-1.5-5 0M12 6c1.5-1.5 3.5-1.5 5 0" {...S}/></>),
'tpl-city': () => (<><path d="M3 21V11l5-3v13M8 21V6l6-3v18M14 21V9l6 3v9" {...S}/><path d="M3 21h18" {...S}/><path d="M10.5 8h1M10.5 12h1M16.5 14h1" {...S}/></>),
'tpl-village': () => (<><path d="M3 20l4-4 4 4M5 20v-3M5 17l2-1.5L9 17v3" {...S}/><path d="M12 20l5-6 5 6M14.5 20v-4M14.5 16l2.5-2 2.5 2v4" {...S}/><path d="M3 20h18" {...S}/></>),
};
// ----------------------------------------------------------------------------
// EMOJI → имя иконки. Для legacy-данных, где иконка приходит строкой-эмодзи.
// ----------------------------------------------------------------------------
const EMOJI_TO_NAME = {
// действия
'✏️': 'rename', '✏': 'rename', '🗑️': 'delete', '🗑': 'delete',
'📋': 'clipboard', '👁': 'visible', '👁️': 'visible', '🚫': 'hidden',
'🔒': 'locked', '🔓': 'unlocked', '🎯': 'target',
'✓': 'check', '✔': 'check', '✗': 'cancel', '✕': 'close', '❌': 'error',
'⚠': 'warning', '⚠️': 'warning', '': 'add',
// сцена / окружение
'🌞': 'sun', '☀️': 'sun', '☀': 'sun', '🌙': 'moon', '🌑': 'moon',
'🌅': 'sunset', '🌄': 'sunrise', '🌥️': 'cloud', '🌫️': 'fog',
'💧': 'water', '💡': 'bulb', '✨': 'sparkles', '🌍': 'globe', '🌐': 'globe',
'📍': 'pin', '🌲': 'tree', '🌳': 'trees', '🌸': 'flower', '🌼': 'flower',
'🌱': 'sprout', '🌿': 'leaf', '🌾': 'grass', '🍄': 'mushroom', '🪨': 'rock',
'🍩': 'torus', '⛰️': 'mountain', '⛰': 'mountain',
// игрок / предметы
'👤': 'user', '🧍': 'user', '🧟': 'zombie', '🌀': 'spawner', '❤️': 'heart',
'🛡': 'shield', '🛡️': 'shield', '⚔️': 'swords', '⚔': 'swords', '🔱': 'sword',
'🏹': 'target', '🔫': 'crosshair', '⚡': 'zap', '💣': 'bomb', '🏆': 'trophy',
'⭐': 'star', '🎒': 'backpack', '🧪': 'flask', '🛠️': 'wrench', '🛠': 'wrench',
'🩸': 'droplet',
// геометрия
'⬛': 'cube', '⬜': 'cube', '⚪': 'sphere', '🛢': 'cylinder', '🛢️': 'cylinder',
'🔺': 'cone', '🔻': 'triangle', '▭': 'square',
'🟦': 'square', '🟧': 'square', '🟢': 'circle', '🟥': 'square',
'🟨': 'square', '🟩': 'square', '🟡': 'circle', '🟠': 'circle',
'🔵': 'circle', '🟣': 'circle', '🟪': 'square', '🧱': 'boxes',
'◣': 'prim-wedge', '◢': 'prim-cornerwedge', '〰': 'waves',
// UI / художественные
'🎨': 'palette', '📺': 'monitor', '🖼': 'image', '🖼️': 'image',
'🔤': 'type', '🟧': 'square',
// звук
'🎵': 'music', '🎼': 'music2', '🔊': 'sound',
// навигация
'🔍': 'search', '📁': 'folder', '📂': 'folder-open', '⚙': 'settings',
'⚙️': 'settings', '🔄': 'refresh', '↺': 'refresh', '🚩': 'flag', '🏁': 'flag',
'🖱️': 'cursor', '🖱': 'cursor', '📐': 'ruler', '📏': 'ruler', '📤': 'upload',
'📥': 'download', '📷': 'camera', '💾': 'save', '📝': 'rename', '📜': 'script',
'🚪': 'door', '🚧': 'construction', '🪜': 'ladder', '🏠': 'home',
'🏛️': 'building', '🏛': 'building', '⏱': 'clock', '⏳': 'hourglass',
'🕒': 'clock', '⏹': 'stop', '▶': 'play', '▶️': 'play', '⏸': 'pause',
'🎮': 'gamepad', '🎬': 'image', '📨': 'send', '📅': 'clock', '👶': 'baby',
// прочее
'🦘': 'zap', '⚓': 'anchor', '⚖️': 'sliders',
'🔥': 'flame', '💨': 'wind', '🔮': 'sparkle',
'👍': 'thumbs-up', '👎': 'thumbs-down', '🎓': 'graduation', '🧩': 'puzzle',
'📱': 'smartphone', '📈': 'trending', '📊': 'chart', '📚': 'library',
'📖': 'book-open', '📔': 'book-text', '🥇': 'trophy', '🥈': 'medal',
'🥉': 'medal', '👑': 'crown', '🧒': 'baby', '👦': 'user', '🧑': 'user-square',
'🧓': 'user-square', '👥': 'users', '🙂': 'smile', '💬': 'message',
'💭': 'message', '⌨️': 'keyboard', '⌨': 'keyboard', '🩺': 'stethoscope',
'🖥️': 'monitor', '🖥': 'monitor', '💻': 'monitor', '🚀': 'rocket',
'🔴': 'radio', '💀': 'skull', '🎲': 'dice', '🏃': 'footprints',
'🆕': 'sparkles', '🔞': 'shield', '🎉': 'sparkles', '🎂': 'star',
'⬇': 'arrow-down', '←': 'arrow-left', '↓': 'arrow-down', '→': 'arrow-right',
// транспорт / категории тулбокса
'🚗': 'box', '⛵': 'box', '🛣': 'map', '🛸': 'prim-portal', '🤖': 'gamepad',
'🏰': 'building', '🏡': 'home', '🏝️': 'tpl-island', '🏙️': 'tpl-city',
'🪑': 'box', '🍕': 'box', '🎄': 'tree', '🪙': 'prim-coin',
};
// ----------------------------------------------------------------------------
// FALLBACK — точка для неизвестных имён/эмодзи.
// ----------------------------------------------------------------------------
function Fallback({ size = 16 }) {
return (
<svg width={size} height={size} viewBox="0 0 24 24" aria-hidden="true">
<circle cx="12" cy="12" r="3" fill="currentColor" />
</svg>
);
}
/**
* <Icon name="rename" size={14} className="..."/>
* <Icon emoji="✏️"/>
* <Icon emoji={block.icon}/>
*
* strokeWidth — необязательный: задаёт толщину обводки (через CSS на svg,
* переопределяет stroke-width дочерних фигур). Без него — 1.8 из пресета.
*/
export default function Icon({ name, emoji, size = 16, strokeWidth = 1.8, className, style }) {
let key = name;
if (!key && emoji) key = EMOJI_TO_NAME[emoji];
const draw = key && GLYPHS[key];
if (!draw) return <Fallback size={size} />;
return (
<svg
width={size}
height={size}
viewBox="0 0 24 24"
className={className}
style={style}
strokeWidth={strokeWidth}
role="img"
aria-hidden="true"
>
{draw()}
</svg>
);
}