| 1 | // The UI icon set — every piece of interface chrome, one module. Inline, |
| 2 | // static, stroke-drawn SVGs in the Feather/Lucide idiom (24x24 viewBox, |
| 3 | // currentColor, round caps), injected only via innerHTML with these in-code |
| 4 | // strings — never user content. The mapping to native SF Symbols, the emoji |
| 5 | // policy (emoji are CONTENT — a member bot's chosen glyph, a reaction — |
| 6 | // never chrome), and how to add an icon: docs/icons.md. |
| 7 | // |
| 8 | // The newer icons are vendored from Lucide (https://lucide.dev, ISC); the |
| 9 | // originals predate the adoption and are hand-drawn in the same style. |
| 10 | |
| 11 | // Curved reply arrow (hover affordance, swipe icon, composer banner). |
| 12 | export const ICON_REPLY = |
| 13 | '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" ' + |
| 14 | 'stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">' + |
| 15 | '<polyline points="9 17 4 12 9 7"></polyline>' + |
| 16 | '<path d="M20 18v-2a4 4 0 0 0-4-4H4"></path></svg>'; |
| 17 | // Optically-centered close cross (the reply banner's cancel button). |
| 18 | export const ICON_X = |
| 19 | '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" ' + |
| 20 | 'stroke-linecap="round" aria-hidden="true">' + |
| 21 | '<line x1="6" y1="6" x2="18" y2="18"></line>' + |
| 22 | '<line x1="18" y1="6" x2="6" y2="18"></line></svg>'; |
| 23 | // Pencil, for the desktop hover edit affordance on your own messages. |
| 24 | export const ICON_EDIT = |
| 25 | '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" ' + |
| 26 | 'stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">' + |
| 27 | '<path d="M12 20h9"></path>' + |
| 28 | '<path d="M16.5 3.5a2.1 2.1 0 0 1 3 3L7 19l-4 1 1-4Z"></path></svg>'; |
| 29 | // Smiley, for the desktop hover "react" affordance (opens the emoji picker). |
| 30 | export const ICON_REACT = |
| 31 | '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" ' + |
| 32 | 'stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">' + |
| 33 | '<circle cx="12" cy="12" r="9"></circle>' + |
| 34 | '<path d="M8 14s1.5 2 4 2 4-2 4-2"></path>' + |
| 35 | '<line x1="9" y1="9" x2="9.01" y2="9"></line>' + |
| 36 | '<line x1="15" y1="9" x2="15.01" y2="9"></line></svg>'; |
| 37 | // Trash, for the "Delete" action in the message picker. |
| 38 | export const ICON_TRASH = |
| 39 | '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" ' + |
| 40 | 'stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">' + |
| 41 | '<polyline points="3 6 5 6 21 6"></polyline>' + |
| 42 | '<path d="M19 6l-1 14a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2L5 6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"></path></svg>'; |
| 43 | // Filled play triangle, for the click-to-play YouTube card. |
| 44 | export const ICON_PLAY = |
| 45 | '<svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">' + |
| 46 | '<path d="M8 5.5v13a1 1 0 0 0 1.53.85l10.2-6.5a1 1 0 0 0 0-1.7L9.53 4.65A1 1 0 0 0 8 5.5z"></path></svg>'; |
| 47 | // Filled pushpin marking pinned chats in the thread list — solid so it stays |
| 48 | // crisp at ~12px, tilted via CSS for the classic "tacked on" look. |
| 49 | export const ICON_PIN = |
| 50 | '<svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">' + |
| 51 | '<path d="M16 9V4h1c.55 0 1-.45 1-1s-.45-1-1-1H7c-.55 0-1 .45-1 1s.45 1 1 1h1v5c0 1.66-1.34 ' + |
| 52 | '3-3 3v2h5.97v7l1 1 1-1v-7H19v-2c-1.66 0-3-1.34-3-3z"></path></svg>'; |
| 53 | // Slashed bell marking muted chats in the thread list — outline stroke like |
| 54 | // ICON_COPY so the slash reads clearly at ~12px. |
| 55 | export const ICON_MUTE = |
| 56 | '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" ' + |
| 57 | 'stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">' + |
| 58 | '<path d="M13.73 21a2 2 0 0 1-3.46 0"></path>' + |
| 59 | '<path d="M18.63 13A17.9 17.9 0 0 1 18 8"></path>' + |
| 60 | '<path d="M6.26 6.26A5.86 5.86 0 0 0 6 8c0 7-3 9-3 9h14"></path>' + |
| 61 | '<path d="M18 8a6 6 0 0 0-9.33-5"></path>' + |
| 62 | '<line x1="1" y1="1" x2="23" y2="23"></line></svg>'; |
| 63 | // A flag, for reporting a message or a person to the space's admins. |
| 64 | export const ICON_REPORT = |
| 65 | '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" ' + |
| 66 | 'stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">' + |
| 67 | '<path d="M4 15s1-1 4-1 5 2 8 2 4-1 4-1V3s-1 1-4 1-5-2-8-2-4 1-4 1z"></path>' + |
| 68 | '<line x1="4" y1="22" x2="4" y2="15"></line></svg>'; |
| 69 | // Overlapping pages, for the "Copy" action in the mobile long-press menu. |
| 70 | export const ICON_COPY = |
| 71 | '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" ' + |
| 72 | 'stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">' + |
| 73 | '<rect x="9" y="9" width="12" height="12" rx="2"></rect>' + |
| 74 | '<path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path></svg>'; |
| 75 | // Two speech bubbles, the second budding out from behind the first — "Reply in |
| 76 | // thread". Deliberately NOT a second reply arrow: it sits immediately beside |
| 77 | // Reply in the desktop hover strip, and two arrows a few pixels apart read as |
| 78 | // one control drawn twice. Bubble-begets-bubble says "a conversation branching |
| 79 | // off this message" before the tooltip has to. (Lucide: messages-square) |
| 80 | export const ICON_THREAD = |
| 81 | '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" ' + |
| 82 | 'stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">' + |
| 83 | '<path d="M16 10a2 2 0 0 1-2 2H6.828a2 2 0 0 0-1.414.586l-2.202 2.202A.71.71 0 0 1 2 14.286V4a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2z"></path>' + |
| 84 | '<path d="M20 9a2 2 0 0 1 2 2v10.286a.71.71 0 0 1-1.212.502l-2.202-2.202A2 2 0 0 0 17.172 19H10a2 2 0 0 1-2-2v-1"></path></svg>'; |
| 85 | // Arrows folding in toward a line — "Convert to thread": take the reply chain |
| 86 | // stacked under this message and collapse it out of the transcript. NOT the |
| 87 | // thread glyph again: that verb is about where the NEXT reply goes, this one is |
| 88 | // about tidying replies that already happened, and the picker's rows are |
| 89 | // icon-only. (Lucide: fold-vertical) |
| 90 | export const ICON_FOLD_IN = |
| 91 | '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" ' + |
| 92 | 'stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">' + |
| 93 | '<path d="M12 22v-6"></path><path d="M12 8V2"></path>' + |
| 94 | '<path d="M4 12H2"></path><path d="M10 12H8"></path>' + |
| 95 | '<path d="M16 12h-2"></path><path d="M22 12h-2"></path>' + |
| 96 | '<path d="m15 19-3-3-3 3"></path><path d="m15 5-3 3-3-3"></path></svg>'; |
| 97 | // Chain link, for the "Copy link" message-permalink action (issue #893). |
| 98 | export const ICON_LINK = |
| 99 | '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" ' + |
| 100 | 'stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">' + |
| 101 | '<path d="M9 17H7A5 5 0 0 1 7 7h2"></path>' + |
| 102 | '<path d="M15 7h2a5 5 0 1 1 0 10h-2"></path>' + |
| 103 | '<line x1="8" y1="12" x2="16" y2="12"></line></svg>'; |
| 104 | |
| 105 | // Dropdown caret (the Views button). (Lucide: chevron-down) |
| 106 | export const ICON_CHEVRON_DOWN = |
| 107 | '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" ' + |
| 108 | 'stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">' + |
| 109 | '<path d="m6 9 6 6 6-6"></path></svg>'; |
| 110 | // Back (mobile thread header). (Lucide: chevron-left) |
| 111 | export const ICON_CHEVRON_LEFT = |
| 112 | '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" ' + |
| 113 | 'stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">' + |
| 114 | '<path d="m15 18-6-6 6-6"></path></svg>'; |
| 115 | // The thread-header kebab. (Lucide: ellipsis-vertical) |
| 116 | export const ICON_MORE_VERTICAL = |
| 117 | '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" ' + |
| 118 | 'stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">' + |
| 119 | '<circle cx="12" cy="12" r="1"></circle><circle cx="12" cy="5" r="1"></circle><circle cx="12" cy="19" r="1"></circle></svg>'; |
| 120 | // Memory — the AI scratchpad (dropdown row + the memory app's mark). (Lucide: brain) |
| 121 | export const ICON_BRAIN = |
| 122 | '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" ' + |
| 123 | 'stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">' + |
| 124 | '<path d="M12 5a3 3 0 1 0-5.997.125 4 4 0 0 0-2.526 5.77 4 4 0 0 0 .556 6.588A4 4 0 1 0 12 18Z"></path><path d="M12 5a3 3 0 1 1 5.997.125 4 4 0 0 1 2.526 5.77 4 4 0 0 1-.556 6.588A4 4 0 1 1 12 18Z"></path><path d="M15 13a4.5 4.5 0 0 1-3-4 4.5 4.5 0 0 1-3 4"></path><path d="M17.599 6.5a3 3 0 0 0 .399-1.375"></path><path d="M6.003 5.125A3 3 0 0 0 6.401 6.5"></path><path d="M3.477 10.896a4 4 0 0 1 .585-.396"></path><path d="M19.938 10.5a4 4 0 0 1 .585.396"></path><path d="M6 18a4 4 0 0 1-1.967-.516"></path><path d="M19.967 17.484A4 4 0 0 1 18 18"></path></svg>'; |
| 125 | // To-do's mark. (Lucide: square-check) |
| 126 | export const ICON_SQUARE_CHECK = |
| 127 | '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" ' + |
| 128 | 'stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">' + |
| 129 | '<rect width="18" height="18" x="3" y="3" rx="2"></rect><path d="m9 12 2 2 4-4"></path></svg>'; |
| 130 | // Reminders' mark. (Lucide: alarm-clock) |
| 131 | export const ICON_ALARM_CLOCK = |
| 132 | '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" ' + |
| 133 | 'stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">' + |
| 134 | '<circle cx="12" cy="13" r="8"></circle><path d="M12 9v4l2 2"></path><path d="M5 3 2 6"></path><path d="m22 6-3-3"></path><path d="M6.38 18.7 4 21"></path><path d="M17.64 18.67 20 21"></path></svg>'; |
| 135 | // Bookmarks' mark. (Lucide: bookmark) |
| 136 | export const ICON_BOOKMARK = |
| 137 | '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" ' + |
| 138 | 'stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">' + |
| 139 | '<path d="m19 21-7-4-7 4V5a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2v16z"></path></svg>'; |
| 140 | // Poll's mark. (Lucide: chart-column) |
| 141 | export const ICON_CHART_COLUMN = |
| 142 | '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" ' + |
| 143 | 'stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">' + |
| 144 | '<path d="M3 3v16a2 2 0 0 0 2 2h16"></path><path d="M18 17V9"></path><path d="M13 17V5"></path><path d="M8 17v-3"></path></svg>'; |
| 145 | // Board's mark. (Lucide: presentation) |
| 146 | export const ICON_PRESENTATION = |
| 147 | '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" ' + |
| 148 | 'stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">' + |
| 149 | '<path d="M2 3h20"></path><path d="M21 3v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V3"></path><path d="m7 21 5-5 5 5"></path></svg>'; |
| 150 | // Vault's mark. (Lucide: lock) |
| 151 | export const ICON_LOCK = |
| 152 | '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" ' + |
| 153 | 'stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">' + |
| 154 | '<rect width="18" height="11" x="3" y="11" rx="2" ry="2"></rect><path d="M7 11V7a5 5 0 0 1 10 0v4"></path></svg>'; |
| 155 | // Emoji picker app's mark. (Lucide: smile) |
| 156 | export const ICON_SMILE = |
| 157 | '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" ' + |
| 158 | 'stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">' + |
| 159 | '<circle cx="12" cy="12" r="10"></circle><path d="M8 14s1.5 2 4 2 4-2 4-2"></path><line x1="9" x2="9.01" y1="9" y2="9"></line><line x1="15" x2="15.01" y1="9" y2="9"></line></svg>'; |
| 160 | // GIF app's mark. (Lucide: film) |
| 161 | export const ICON_FILM = |
| 162 | '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" ' + |
| 163 | 'stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">' + |
| 164 | '<rect width="18" height="18" x="3" y="3" rx="2"></rect><path d="M7 3v18"></path><path d="M3 7.5h4"></path><path d="M3 12h18"></path><path d="M3 16.5h4"></path><path d="M17 3v18"></path><path d="M17 7.5h4"></path><path d="M17 16.5h4"></path></svg>'; |
| 165 | // Admin's mark. (Lucide: wrench) |
| 166 | export const ICON_WRENCH = |
| 167 | '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" ' + |
| 168 | 'stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">' + |
| 169 | '<path d="M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z"></path></svg>'; |
| 170 | // App Studio's mark. (Lucide: puzzle) |
| 171 | export const ICON_PUZZLE = |
| 172 | '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" ' + |
| 173 | 'stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">' + |
| 174 | '<path d="M15.39 4.39a1 1 0 0 0 1.68-.474 2.5 2.5 0 1 1 3.014 3.015 1 1 0 0 0-.474 1.68l1.683 1.682a2.414 2.414 0 0 1 0 3.414L19.61 15.39a1 1 0 0 1-1.68-.474 2.5 2.5 0 1 0-3.014 3.015 1 1 0 0 1 .474 1.68l-1.683 1.682a2.414 2.414 0 0 1-3.414 0L8.61 19.61a1 1 0 0 0-1.68.474 2.5 2.5 0 1 1-3.014-3.015 1 1 0 0 0 .474-1.68l-1.683-1.682a2.414 2.414 0 0 1 0-3.414L4.39 8.61a1 1 0 0 1 1.68.474 2.5 2.5 0 1 0 3.014-3.015 1 1 0 0 1-.474-1.68l1.683-1.682a2.414 2.414 0 0 1 3.414 0z"></path></svg>'; |
| 175 | |