eleven / ocho Public

Clone
git clone https://amadan.net/eleven/ocho

Plain git — no account needed to clone.

Download

Download this file

πŸ€– Icons β€” one system across web and native

Three kinds of mark, three sources, one rule about emoji.

The three kinds

  1. 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.
  2. 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 needs fill-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.
  3. 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 its logo.

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 via innerHTML with these constants β€” never user content.
  • web/static/logos.js β€” the brand marks, same shape.
  • Apps carry logo: beside glyph: in defineApp; 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 as ICON_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 | | Reply in thread | ICON_THREAD (messages-square) | bubble.left.and.bubble.right | | Threads list | ICON_THREAD (messages-square) | bubble.left.and.bubble.right (iOS kebab; the Mac kebab's rows are text-only β€” see below) | | Convert to thread | ICON_FOLD_IN (fold-vertical) | arrow.down.right.and.arrow.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.

One row of the table is ahead of native: Reply in thread. The web strip and menus now draw ICON_THREAD, but the native message menus still label a second arrowshape.turn.up.left (StockChatFeed.swift, MessageBubble.swift) β€” tolerable there because those rows carry text, whereas the web hover strip is icon-only and two arrows read as one control drawn twice. Swap them to bubble.left.and.bubble.right on the next native touch.

The Threads list follows the same platform split as every other kebab row: the web's β–€ menu and the iOS β‹― menu draw the icon (iOS menu rows are Labels on one icon grid), while the Mac header kebab is text-only rows by construction β€” nothing else in it carries a glyph either.

Convert to thread carries a glyph on exactly two of its four surfaces: the web's picker (icon-only rows, so it needs one) and the iOS long-press sheet. The iOS context menu and the Mac NSMenu are text-only rows by construction, so there is nothing to map there.