Přeskočit obsah

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 jen super_admin, žádný manifest merge, žádné kolize global×firma. 3. Render: rozhodne autor specu (uživatel bez preference). Zvoleno: tk.Text per-bublina s image_create embeds (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

  1. Jako uživatel chatu chci napsat :rocket: a vidět v konverzaci obrázek, abych komunikoval rychleji a vizuálně.
  2. Jako uživatel offline chci, aby běžné emoji fungovaly i bez sítě (bundlovaná sada), a neznámé se ladně zobrazily jako :nazev:.
  3. Jako admin chci nahrát vlastní/firemní emoji (PNG, přiřadit :shortcode:) a rozdistribuovat ho všem klientům přes S3.
  4. Jako uživatel chci picker s kategoriemi a náhledy, vybrat emoji a vložit jeho shortcode do zprávy.
  5. (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.Text per-bublina (read-only) s image_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.Text state=disabled, wrap=word, výška dopočítána z obsahu, fg/text dle colors.*, emoji vloženy jako image_create na pozici tokenu; fallback token = obyčejný text run.
  • Zbývá ověřit spike-em (ne rozhodnutí, jen kalibrace): auto-výška tk.Text u víceřádkových zpráv, výkon při mnoha bublinách v CTkScrollableFrame, 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.json loká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.Text per-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/png vstup, 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é repo C:\avaxis\app-AiCenter\) — producent emoji/obrázkových assetů; kontrakt v jeho docs/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)