eleven / ocho Public

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

Plain git — no account needed to clone.

Download

Download this file

1# πŸ€– Icons β€” one system across web and native
2
3Three kinds of mark, three sources, one rule about emoji.
4
5## The three kinds
6
71. **UI chrome** (carets, the kebab, message actions, app marks for
8 first-party apps): **Lucide** on web (https://lucide.dev, ISC β€” vendored,
9 never a CDN), **SF Symbols** on iOS/Mac (built into Apple platforms;
10 their license keeps them off the web, which is why one literal library
11 everywhere isn't possible). The table below keeps the two in step.
122. **Brand marks** (the integration apps): real logos, vendored monochrome in
13 `web/static/logos.js` β€” Simple Icons (CC0) where available, an official
14 asset otherwise (DNSimple's mask icon β€” note it needs
15 `fill-rule="evenodd"` or the knockout fills solid). Brands have no SF
16 Symbols; native gets the same SVGs as template images in the asset
17 catalogs, tinted like SF.
183. **Emoji** β€” **content, never chrome.** An emoji is right where a person or
19 a model *chose* it: a member bot's title ("RTE News Hourly πŸ“Ί"), a member
20 app's glyph, a view's glyph, reactions. Interface furniture never uses
21 emoji. Every first-party app still declares an emoji `glyph` β€” it is the
22 cross-surface fallback (native command palette, string contexts) β€” but
23 web surfaces render its `logo`.
24
25## How it's wired (web)
26
27- `web/static/icons.js` β€” every chrome icon as a static in-code SVG string
28 (24Γ—24 viewBox, `currentColor`, stroke idiom). Only ever injected via
29 `innerHTML` with these constants β€” never user content.
30- `web/static/logos.js` β€” the brand marks, same shape.
31- Apps carry `logo:` beside `glyph:` in `defineApp`; `appGlyphInto(el, app)`
32 (apps.js) renders logo-else-glyph everywhere app identity draws.
33- The Views button's caret is the one inline SVG in `web/pages/index.html`
34 (static markup; same chevron as `ICON_CHEVRON_DOWN`).
35
36**Adding an icon** = vendor the Lucide SVG into `icons.js` (keep the
37`(Lucide: <slug>)` comment), add a row here with its SF Symbol, use it.
38
39## The mapping
40
41| Meaning | icons.js (Lucide slug) | SF Symbol (native) |
42|---|---|---|
43| Reply | `ICON_REPLY` (hand-drawn, pre-Lucide) | `arrowshape.turn.up.left` |
44| Reply in thread | `ICON_THREAD` (messages-square) | `bubble.left.and.bubble.right` |
45| Threads list | `ICON_THREAD` (messages-square) | `bubble.left.and.bubble.right` (iOS kebab; the Mac kebab's rows are text-only β€” see below) |
46| Convert to thread | `ICON_FOLD_IN` (fold-vertical) | `arrow.down.right.and.arrow.up.left` |
47| Close / dismiss | `ICON_X` | `xmark` |
48| Edit | `ICON_EDIT` | `pencil` |
49| React | `ICON_REACT` | `face.smiling` |
50| Delete | `ICON_TRASH` | `trash` |
51| Play | `ICON_PLAY` | `play.fill` |
52| Pinned chat | `ICON_PIN` | `pin.fill` |
53| Muted chat | `ICON_MUTE` | `bell.slash` |
54| Report | `ICON_REPORT` | `flag` |
55| Copy | `ICON_COPY` | `doc.on.doc` |
56| Dropdown caret | `ICON_CHEVRON_DOWN` (chevron-down) | `chevron.down` |
57| Back | `ICON_CHEVRON_LEFT` (chevron-left) | `chevron.left` |
58| Kebab | `ICON_MORE_VERTICAL` (ellipsis-vertical) | `ellipsis` |
59| Memory | `ICON_BRAIN` (brain) | `brain` |
60| To-do | `ICON_SQUARE_CHECK` (square-check) | `checkmark.square` |
61| Reminders | `ICON_ALARM_CLOCK` (alarm-clock) | `alarm` |
62| Bookmarks | `ICON_BOOKMARK` (bookmark) | `bookmark` |
63| Poll | `ICON_CHART_COLUMN` (chart-column) | `chart.bar` |
64| Board | `ICON_PRESENTATION` (presentation) | `rectangle.on.rectangle` |
65| Vault | `ICON_LOCK` (lock) | `lock` |
66| Emoji picker | `ICON_SMILE` (smile) | `face.smiling` |
67| GIF | `ICON_FILM` (film) | `film` |
68| Admin | `ICON_WRENCH` (wrench) | `wrench.adjustable` |
69| App Studio | `ICON_PUZZLE` (puzzle) | `puzzlepiece` |
70
71Brand marks (`logos.js`): GitHub, DNSimple, Stripe, HubSpot, Linear
72(awaiting the linear app), plus Lucide's `plug` for the Integrations app
73itself (a capability, not a brand).
74
75## Native status
76
77Built (`ios/LChatCore/Sources/LChatCore/AppIdentity.swift`): the appId β†’
78SF-Symbol map above plus the brand marks as vendored template PNGs in
79`LChatCore/Marks/` (the same SVGs as `logos.js`, rasterized
80black-on-transparent at 96px, tinted at render β€” regenerate with resvg,
81remembering the vendored strings need an `xmlns` added). Wired into both
82platforms' avatar ladders (photo β†’ mark β†’ symbol β†’ glyph β†’ initials): the
83iOS thread list/search/share rows, the Mac sidebar, rail, feed header,
84search, ⌘K palette, and both New-chat pickers. The old "β–¦" placeholder is
85gone β€” unmapped apps get `square.grid.2x2`. Device verify + the release
86changelog ride the next native release mint.
87
88One row of the table is ahead of native: **Reply in thread**. The web strip
89and menus now draw `ICON_THREAD`, but the native message menus still label a
90second `arrowshape.turn.up.left` (`StockChatFeed.swift`, `MessageBubble.swift`)
91β€” tolerable there because those rows carry text, whereas the web hover strip is
92icon-only and two arrows read as one control drawn twice. Swap them to
93`bubble.left.and.bubble.right` on the next native touch.
94
95The **Threads list** follows the same platform split as every other kebab
96row: the web's β–€ menu and the iOS β‹― menu draw the icon (iOS menu rows are
97`Label`s on one icon grid), while the Mac header kebab is text-only rows by
98construction β€” nothing else in it carries a glyph either.
99
100**Convert to thread** carries a glyph on exactly two of its four surfaces: the
101web's picker (icon-only rows, so it needs one) and the iOS long-press sheet.
102The iOS context menu and the Mac `NSMenu` are text-only rows by construction,
103so there is nothing to map there.
104