eleven / messenger Public
π€ Icons β one system across web and native
Three kinds of mark, three sources, one rule about emoji.
The three kinds
- UI chrome (carets, the kebab, message actions, app marks for first-party apps): Lucide on web (https://lucide.dev, ISC β vendored, never a CDN), SF Symbols on iOS/Mac (built into Apple platforms; their license keeps them off the web, which is why one literal library everywhere isn't possible). The table below keeps the two in step.
- Brand marks (the integration apps): real logos, vendored monochrome in
web/static/logos.jsβ Simple Icons (CC0) where available, an official asset otherwise (DNSimple's mask icon β note it needsfill-rule="evenodd"or the knockout fills solid). Brands have no SF Symbols; native gets the same SVGs as template images in the asset catalogs, tinted like SF. - Emoji β content, never chrome. An emoji is right where a person or a model chose it: a member bot's title ("RTE News Hourly πΊ"), a member app's glyph, a view's glyph, reactions. Interface furniture never uses emoji. Every first-party app still declares an emoji
glyphβ it is the cross-surface fallback (native command palette, string contexts) β but web surfaces render itslogo.
How it's wired (web)
web/static/icons.jsβ every chrome icon as a static in-code SVG string (24Γ24 viewBox,currentColor, stroke idiom). Only ever injected viainnerHTMLwith these constants β never user content.web/static/logos.jsβ the brand marks, same shape.- Apps carry
logo:besideglyph:indefineApp;appGlyphInto(el, app)(apps.js) renders logo-else-glyph everywhere app identity draws. - The Views button's caret is the one inline SVG in
web/pages/index.html(static markup; same chevron asICON_CHEVRON_DOWN).
Adding an icon = vendor the Lucide SVG into icons.js (keep the (Lucide: <slug>) comment), add a row here with its SF Symbol, use it.
The mapping
| Meaning | icons.js (Lucide slug) | SF Symbol (native) | |---|---|---| | Reply | ICON_REPLY (hand-drawn, pre-Lucide) | arrowshape.turn.up.left | | Close / dismiss | ICON_X | xmark | | Edit | ICON_EDIT | pencil | | React | ICON_REACT | face.smiling | | Delete | ICON_TRASH | trash | | Play | ICON_PLAY | play.fill | | Pinned chat | ICON_PIN | pin.fill | | Muted chat | ICON_MUTE | bell.slash | | Report | ICON_REPORT | flag | | Copy | ICON_COPY | doc.on.doc | | Dropdown caret | ICON_CHEVRON_DOWN (chevron-down) | chevron.down | | Back | ICON_CHEVRON_LEFT (chevron-left) | chevron.left | | Kebab | ICON_MORE_VERTICAL (ellipsis-vertical) | ellipsis | | Memory | ICON_BRAIN (brain) | brain | | To-do | ICON_SQUARE_CHECK (square-check) | checkmark.square | | Reminders | ICON_ALARM_CLOCK (alarm-clock) | alarm | | Bookmarks | ICON_BOOKMARK (bookmark) | bookmark | | Poll | ICON_CHART_COLUMN (chart-column) | chart.bar | | Board | ICON_PRESENTATION (presentation) | rectangle.on.rectangle | | Vault | ICON_LOCK (lock) | lock | | Emoji picker | ICON_SMILE (smile) | face.smiling | | GIF | ICON_FILM (film) | film | | Admin | ICON_WRENCH (wrench) | wrench.adjustable | | App Studio | ICON_PUZZLE (puzzle) | puzzlepiece |
Brand marks (logos.js): GitHub, DNSimple, Stripe, HubSpot, Linear (awaiting the linear app), plus Lucide's plug for the Integrations app itself (a capability, not a brand).
Native status
Built (ios/LChatCore/Sources/LChatCore/AppIdentity.swift): the appId β SF-Symbol map above plus the brand marks as vendored template PNGs in LChatCore/Marks/ (the same SVGs as logos.js, rasterized black-on-transparent at 96px, tinted at render β regenerate with resvg, remembering the vendored strings need an xmlns added). Wired into both platforms' avatar ladders (photo β mark β symbol β glyph β initials): the iOS thread list/search/share rows, the Mac sidebar, rail, feed header, search, βK palette, and both New-chat pickers. The old "β¦" placeholder is gone β unmapped apps get square.grid.2x2. Device verify + the release changelog ride the next native release mint.