Custom emoji & sdílené obrázkové assety — specifikace¶
Status: draft (sladěno 2026-05-17, rozhodnuté otázky níže) Verze spec: 0.2 Aktualizováno: 2026-05-17 Autor specifikace: Claude (z konceptu uživatele)
Sladěno 2026-05-17: 1. S3 prefix =
s3://avaxis/system/emoji/(jen globální sada). 2. Per-firma custom emoji = NE (v1 ani později do odvolání) → jedna globální sada, zápis jensuper_admin, žádný manifest merge, žádné kolize global×firma. 3. Render: rozhodne autor specu (uživatel bez preference). Zvoleno:tk.Textper-bublina simage_createembeds (viz 7a) — jediný Tk přístup co zvládne inline text+obrázek + zalamování + výběr/copy. Navíc: kromě:name:se překládají i ASCII emotikony (:D,:), …) — viz 4a. 4. Master velikost assetu = 128×128 px RGBA PNG, transparentní pozadí (zdůvodnění v 7d). Sada se generuje na míru.
1. Cíl a motivace¶
Co to dělá: Vlastní emoji/obrázkové assety ve stylu Slack/Discord. Mezi
klienty se posílá jen text :nazev: (shortcode) jako součást běžné zprávy.
Chat při zobrazení shortcode přeloží na obrázek. Obrázky jsou centrálně v
S3, klient si je lazy stahuje a cachuje; když asset nezná a není ani na S3,
zobrazí doslovně :nazev: (graceful fallback).
Proč: vlastní/firemní emoji a sdílené obrázkové prvky, bez závislosti na systémových color-emoji fontech (Tk je stejně nerenderuje barevně).
Rozsah v1 (rozhodnuto s uživatelem 2026-05-17): - BEZ AI generování. v1 = admin upload / kurátovaná sada → S3 + bundlovaná default sada (offline fallback). AI generátor = v2. - Emoji + obecný image-asset systém zároveň — emoji je první konzument obecného mechanismu „pojmenovaný obrázkový asset" (jméno → obrázek, S3, cache, fallback). Stejná infra slouží i obecným custom obrázkům v appce. - Nejdřív tento spec → sladit s uživatelem → teprve implementace.
2. User stories¶
- Jako uživatel chatu chci napsat
:rocket:a vidět v konverzaci obrázek, abych komunikoval rychleji a vizuálně. - Jako uživatel offline chci, aby běžné emoji fungovaly i bez sítě
(bundlovaná sada), a neznámé se ladně zobrazily jako
:nazev:. - Jako admin chci nahrát vlastní/firemní emoji (PNG, přiřadit
:shortcode:) a rozdistribuovat ho všem klientům přes S3. - Jako uživatel chci picker s kategoriemi a náhledy, vybrat emoji a vložit jeho shortcode do zprávy.
- (v2) Jako admin chci vygenerovat emoji/obrázek AI modelem z promptu.
3. Koncept a tok dat¶
[odesílatel] zpráva text obsahuje "...hotovo :rocket: díky"
│ (žádná změna message schématu — shortcode je prostý text)
▼
[backend chat relay] beze změny — přenáší text
▼
[příjemce / render] parser rozseká text na segmenty:
"...hotovo " | EMOJI(rocket) | " díky"
│
lokální cache má rocket@hash?
├─ ano → vykresli obrázek
├─ ne → manifest zná rocket? → stáhni z S3 → cache → obrázek
└─ ne v manifestu / S3 miss → vykresli doslovně ":rocket:"
Klíč: wire = text, render = obrázek, chybí = doslovný shortcode.
Žádná migrace chatu, zpětně kompatibilní (starý klient uvidí :rocket:).
4. Shortcode formát¶
- Gramatika:
:[a-z0-9_+-]{1,32}:(lowercase, bez mezer). Regex návrh:(?<!\w):([a-z0-9_+-]{1,32}):(?!\w). - Case-insensitive lookup, kanonicky lowercase.
- Escape literálu:
\:nazev:se nevykreslí jako emoji (zobrazí:nazev:). - Neznámý/neplatný shortcode → ponechán jako text (žádná chyba).
4a. ASCII emotikony (alias vrstva)¶
Kromě :name: se na obrázek překládá i pevná konečná sada klasických
emotikonů → mapováno na kanonický emoji name (alias tabulka):
| Emotikon | → name | Emotikon | → name | |
|---|---|---|---|---|
:) :-) |
slight_smile |
:D :-D |
grin |
|
;) ;-) |
wink |
:( :-( |
frown |
|
:P :-P :p |
stuck_out_tongue |
:'( |
cry |
|
:O :-O :o |
open_mouth |
<3 |
heart |
|
:/ :-/ |
confused |
xD XD |
laughing |
|
:| :-\| |
neutral_face |
</3 |
broken_heart |
Anti-false-positive pravidla (emotikony jsou v textu/URL/časech
nebezpečné):
- Match jen když je emotikon ohraničený mezerou / začátkem / koncem
řádku / interpunkcí — NIKDY uvnitř slova/tokenu.
- : následované číslicí (časy 12:30), // (URL http://), nebo
písmenem mimo definovaný emotikon → NEmatch.
- Tabulka je uzavřená (žádný regex „cokoliv mezi dvojtečkami" pro
emotikony) — rozšiřuje se jen explicitně.
- \ před emotikonem = escape (literál).
- Priorita: nejdřív :shortcode:, pak emotikon-alias, na zbytku text.
- Emotikon → alias → stejný render/cache/fallback jako :name:
(fallback = ponech původní emotikon doslovně).
5. S3 layout & manifest¶
Rozhodnuto: jen globální sada, prefix s3://avaxis/system/emoji/
(žádná per-firma větev).
s3://avaxis/system/emoji/
manifest.json ← {name: {sha256, w, h, category, animated}}
img/<sha256>.png ← obsah adresovaný hashem (immutable, cache-friendly)
- Obsah adresovaný hashem (
img/<sha256>.png) → cache nikdy neinvaliduje špatně; změna obrázku = nový hash = nový záznam v manifestu. - Manifest = malý JSON, klient ho lazy/periodicky stahuje (ETag/If-None-Match).
- Asset typy v1: PNG (statické). Animované (GIF/APNG) = otevřená otázka.
6. Backend API (hlavní backend, NE per-app)¶
GET /chat/emoji/manifest → sloučený manifest (global [+ firma]); ETag
GET /chat/emoji/{sha256} → binárka (nebo přes storage token/redirect na S3)
POST /admin/emoji → upload (multipart: name, file, category) [admin]
DELETE /admin/emoji/{name} → odebrat [admin]
GET /admin/emoji → správa/list [admin]
- Auth: JWT hlavního backendu. Zápis (upload/delete/správa): jen
super_admin(globální platformní sada). Čtení manifestu/binárek: všichni přihlášení. - Upload validace (bezpečnost): povolený MIME (
image/png), max rozměr (např. 128×128 pro emoji), max velikost (např. 64 KB), strip EXIF/metadat, re-encode přes Pillow (sanitizace), normalizace názvu. - Manifest verze/hash pro klientskou cache invalidaci.
7. Klient (launcher2)¶
Stack: Python + customtkinter + Pillow (už dependency).
7a. Parser & render v transkriptu¶
emoji_render.tokenize(text) -> [("text", str) | ("emoji", name)]. Pořadí::shortcode:→ ASCII emotikony (4a) → zbytek text. Čistá funkce, unit-testovatelná bez GUI.- Render zvolen:
tk.Textper-bublina (read-only) simage_create. Důvod: jediný Tk přístup, který v jednom widgetu zvládne inline mix text+obrázek s korektním zalamováním + výběr/kopírování (flow z více labelů zalamování neřeší rozumně). Bublina zprávy =tk.Textstate=disabled,wrap=word, výška dopočítána z obsahu, fg/text dlecolors.*, emoji vloženy jakoimage_createna pozici tokenu; fallback token = obyčejný text run. - Zbývá ověřit spike-em (ne rozhodnutí, jen kalibrace): auto-výška
tk.Textu víceřádkových zpráv, výkon při mnoha bublinách vCTkScrollableFrame, baseline zarovnání emoji k textu. Pokud výkon u dlouhých konverzací nevyhoví → lazy render jen viditelných bublin. - Velikost emoji v textu ~20–22 px (dle
fs()), v pickeru ~26–28 px — downscale z master 128px (viz 7d), vysokokvalitní resample (LANCZOS).
7b. Cache¶
- Adresář:
%LOCALAPPDATA%/Avaxis/emoji/(mimo per-app data; sdílené). - Klíč =
sha256(immutable).manifest.jsonlokálně + ETag. - Lazy fetch: render narazí na neznámý name → z manifestu zjistí sha256 → není lokálně → stáhne (executor, neblokovat Tk) → ulož → překresli.
- Miss (name není v manifestu nebo download selže) → doslovný
:name:, zkусит znovu při příštím manifest refresh.
7c. Picker (přepracování EmojiPicker)¶
- Lišta kategorií (z manifestu
category): Smajlíci · Gesta · Srdce · Zvířata · Jídlo · Objekty · Symboly · Firemní. - Grid náhledů =
CTkButton(image=CTkImage(...), text=""), klik → vloží:name:do vstupu (NE obrázek — wire je text). - Lazy image cache pro náhledy.
7d. Master velikost assetu (rozhodnuto)¶
128×128 px, RGBA PNG, transparentní pozadí, 1 master na emoji.
Zdůvodnění:
- Jeden master pokryje všechna zobrazení: inline text ~20–22 px, picker
~26–28 px, případné 2× HiDPI (≈44–56 px) — vše downscale z 128
(LANCZOS), nikdy upscale → vždy ostré.
- 128 je dost i pro budoucí větší použití (náhled detailu) bez nové sady.
- Optimalizovaný 128px RGBA PNG ≈ 3–12 KB → 300–400 ks ≈ 1–4 MB
(bundled rozpočet repo cíl < ~3 MB; lze oxipng/pngquant).
- Klient si drží odvozené velikosti v paměti cache (resize 1× per
velikost), na disk ukládá jen master (hash-addressed).
- NE větší (256+) — zbytečný objem; NE menší (64) — neostré na HiDPI.
Animované (GIF/APNG) = v2, mimo v1 (viz Otevřené otázky).
7e. Bundled default sada (offline)¶
- Kurátovaná podmnožina (~300–400) 128px master PNG v repu:
desktop/launcher2/assets/emoji/<name>.png+assets/emoji/index.json(name → category, + alias map z 4a). - Fallback když S3/manifest nedostupný; S3 manifest má přednost (override stejného name novějším hashem).
- Zdroj = generuje se na míru (rozhodnuto — vlastní sada, ne CDN/twemoji). Styl/paleta = dodá uživatel/design; spec jen určuje rozměr a formát.
8. Obecný image-asset režim¶
Stejný mechanismus (jméno → obrázek, S3, manifest, cache, fallback) je
zobecnitelný na libovolné pojmenované obrázky v appce (ne jen chat emoji):
- namespace v manifestu (kind: emoji | image), stejné API/cache.
- v1: admin upload/správa (žádné AI). UI = sekce v adminu „Obrázkové assety".
- Generování assetů (emoji/obrázky) = samostatná aplikace app-AiCenter
(repo C:\avaxis\app-AiCenter\, vyvíjí jiný Claude; hybrid lokální
Python orchestrátor nad ComfyUI/LM Studio/TRELLIS, deploy DEFERRED).
AVAX strana to negeneruje — je konzument. AiCenter produkuje
out/emoji/ bundle (128px RGBA <name>.png + index.json, příp.
hash-addressed manifest.json) dle kontraktu app-AiCenter/docs/
api-contract.md (zrcadlí §5/§7e tohoto specu).
- Dodací cesty: Path A (teď) bundle → desktop/launcher2/assets/
emoji/ přes PR; Path B (až bude backend §6) POST /admin/emoji
→ S3 system/emoji/. LM Studio = jen text LLM → AiCenter řeší
obrázkový backend (ComfyUI ap.) lokálně, mimo AVAX core.
9. Oprávnění¶
- Čtení (manifest, binárky, picker): všichni přihlášení.
- Zápis (upload/delete/správa) globální sady: jen
super_admin. - Per-firma sada: není (rozhodnuto — viz sladění).
10. Acceptance criteria¶
MVP (0.x)¶
-
tokenize()+ unit testy (escape, hranice, neplatné). - Render
:name:jako obrázek v transkriptu (segmentová sazba — spike OK). - Lazy fetch z S3 + lokální cache (hash-addressed) + fallback na
:name:. - Bundlovaná default sada funguje offline.
- Picker s kategoriemi vkládá shortcode.
v1.0¶
- Admin upload/správa emoji (validace, sanitizace Pillow) → S3 manifest.
- Manifest ETag cache + periodický refresh.
- Per-firma sada (pokud potvrzeno) + merge s globální.
- Obecný image-asset namespace (kind=image) přes stejné API.
v2.0¶
- AI generátor (prompt → obrázek) — po rozhodnutí o image backendu.
- Animované emoji (GIF/APNG) — pokud potřeba.
11. Otevřené otázky¶
Rozhodnuto (sladění 2026-05-17)¶
- S3 prefix =
s3://avaxis/system/emoji/(globální). - Per-firma custom emoji = NE.
- Transkript render =
tk.Textper-bublina +image_create(7a). - Master asset = 128×128 RGBA PNG, generuje se na míru (7d).
- ASCII emotikony se taky překládají (4a, uzavřená tabulka).
Zbývá (lze rozhodnout při implementaci, nízké riziko)¶
- Distribuce binárek: backend proxy
GET /chat/emoji/{sha256}vs přímý S3 + storage token. Návrh: backend proxy s dlouhým cache-control (immutable hash) — jednodušší klient, žádná S3 credential logika; sladit s existující storage architekturou. - Limity uploadu (návrh defaultů k potvrzení): formát
image/pngvstup, master ukládán 128×128 (re-encode Pillow), max vstup ≤ 1 MB, strip metadat. Animace (GIF/APNG) odloženo na v2. - Manifest refresh interval (návrh: při startu chatu + 1×/h + ETag 304 cheap-check; lazy dofetch při miss).
- Validace názvu (regex
[a-z0-9_+-]{1,32}, kolize = poslední upload přepíše + audit log).
12. Timeline a status¶
| Fáze | Status | Termín |
|---|---|---|
| Spec | ✅ sladěno v0.2 (čeká commit + go na MVP) | 2026-05-17 |
| MVP | ⏳ | TBD |
| v1.0 | ⏳ | TBD |
| v2.0 (AI) | ⏳ | TBD |
13. Související¶
app-AiCenter(samostatné repoC:\avaxis\app-AiCenter\) — producent emoji/obrázkových assetů; kontrakt v jehodocs/api-contract.md- chat.md — chat (render zpráv, S3 chat prefix)
- s3-architecture.md — izolace firem, S3 layout
- sync-backup.md — manifest/sync vzory
- auth-organization.md — role/oprávnění
- launcher2-gui.md — GUI launcher2 (chat, picker)
- ai-chat.md — LM Studio integrace (text-only; relevantní pro v2)