anentrypoint-design 0.0.121 → 0.0.124

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (202) hide show
  1. package/README.md +253 -253
  2. package/app-shell.css +931 -594
  3. package/colors_and_type.css +226 -226
  4. package/community.css +817 -1222
  5. package/dist/247420.css +2202 -2084
  6. package/dist/247420.js +13 -13
  7. package/package.json +80 -80
  8. package/src/bootstrap.js +25 -25
  9. package/src/components/chat.js +199 -199
  10. package/src/components/community.js +190 -208
  11. package/src/components/content.js +269 -269
  12. package/src/components/editor-primitives.js +100 -0
  13. package/src/components/files-modals.js +107 -107
  14. package/src/components/files.js +118 -118
  15. package/src/components/freddie/helpers.js +50 -50
  16. package/src/components/freddie.js +33 -33
  17. package/src/components/shell.js +117 -117
  18. package/src/components/theme-toggle.js +70 -70
  19. package/src/components.js +59 -57
  20. package/src/debug.js +30 -30
  21. package/src/deck-stage.js +21 -21
  22. package/src/highlight.js +65 -32
  23. package/src/index.js +86 -86
  24. package/src/kits/os/about-app.js +52 -52
  25. package/src/kits/os/app-panes.css +152 -152
  26. package/src/kits/os/browser-app.js +58 -58
  27. package/src/kits/os/files-app.js +44 -44
  28. package/src/kits/os/freddie/helpers.js +59 -59
  29. package/src/kits/os/freddie/pages-chat.js +143 -143
  30. package/src/kits/os/freddie/pages-core.js +101 -101
  31. package/src/kits/os/freddie/pages-os.js +51 -51
  32. package/src/kits/os/freddie/pages-tools.js +183 -183
  33. package/src/kits/os/freddie/routes.js +24 -24
  34. package/src/kits/os/freddie-dashboard.css +51 -51
  35. package/src/kits/os/freddie-dashboard.js +101 -101
  36. package/src/kits/os/icons.js +17 -17
  37. package/src/kits/os/index.js +17 -17
  38. package/src/kits/os/launcher.css +61 -61
  39. package/src/kits/os/launcher.js +79 -79
  40. package/src/kits/os/monitor-app.js +34 -34
  41. package/src/kits/os/shell.js +214 -214
  42. package/src/kits/os/terminal-app.js +45 -45
  43. package/src/kits/os/theme.css +450 -450
  44. package/src/kits/os/validate.css +19 -19
  45. package/src/kits/os/validator-app.js +55 -55
  46. package/src/kits/os/wm.css +115 -115
  47. package/src/kits/os/wm.js +111 -111
  48. package/src/markdown.js +39 -39
  49. package/src/motion.js +35 -35
  50. package/src/page-html.js +196 -196
  51. package/src/styles.js +25 -25
  52. package/src/theme.js +99 -99
  53. package/src/web-components/ds-chat.js +116 -116
  54. package/dist/.nojekyll +0 -0
  55. package/dist/app-shell.css +0 -594
  56. package/dist/colors_and_type.css +0 -197
  57. package/dist/favicon.svg +0 -1
  58. package/dist/index.html +0 -308
  59. package/dist/preview/buttons.html +0 -28
  60. package/dist/preview/colors-core.html +0 -45
  61. package/dist/preview/colors-lore.html +0 -28
  62. package/dist/preview/colors-semantic.html +0 -34
  63. package/dist/preview/dateline.html +0 -19
  64. package/dist/preview/dropzone.html +0 -30
  65. package/dist/preview/file-grid.html +0 -19
  66. package/dist/preview/file-row.html +0 -20
  67. package/dist/preview/file-toolbar.html +0 -40
  68. package/dist/preview/file-viewer.html +0 -31
  69. package/dist/preview/header.html +0 -24
  70. package/dist/preview/icons-unicode.html +0 -26
  71. package/dist/preview/index-row.html +0 -25
  72. package/dist/preview/inputs.html +0 -22
  73. package/dist/preview/manifesto.html +0 -52
  74. package/dist/preview/motion-default.js +0 -106
  75. package/dist/preview/rules.html +0 -16
  76. package/dist/preview/spacing.html +0 -18
  77. package/dist/preview/stamps-lore.html +0 -20
  78. package/dist/preview/stamps.html +0 -14
  79. package/dist/preview/theme-ink.html +0 -15
  80. package/dist/preview/type-display.html +0 -16
  81. package/dist/preview/type-mono.html +0 -15
  82. package/dist/preview/type-prose.html +0 -11
  83. package/dist/preview/type-scale.html +0 -20
  84. package/dist/preview/wordmarks.html +0 -28
  85. package/dist/robots.txt +0 -8
  86. package/dist/site/content/globals/navigation.yaml +0 -5
  87. package/dist/site/content/globals/site.yaml +0 -16
  88. package/dist/site/content/pages/freddie.yaml +0 -88
  89. package/dist/site/content/pages/home.yaml +0 -190
  90. package/dist/site/theme.mjs +0 -368
  91. package/dist/sitemap.xml +0 -31
  92. package/dist/slides/deck-stage-overlay.js +0 -63
  93. package/dist/slides/deck-stage-state.js +0 -81
  94. package/dist/slides/deck-stage-style.js +0 -117
  95. package/dist/slides/deck-stage.js +0 -159
  96. package/dist/slides/index.html +0 -276
  97. package/dist/src/bootstrap.js +0 -25
  98. package/dist/src/components/chat.js +0 -199
  99. package/dist/src/components/community.js +0 -167
  100. package/dist/src/components/content.js +0 -213
  101. package/dist/src/components/files-modals.js +0 -107
  102. package/dist/src/components/files.js +0 -118
  103. package/dist/src/components/freddie/helpers.js +0 -50
  104. package/dist/src/components/freddie.js +0 -33
  105. package/dist/src/components/shell.js +0 -117
  106. package/dist/src/components/theme-toggle.js +0 -70
  107. package/dist/src/components.js +0 -52
  108. package/dist/src/debug.js +0 -30
  109. package/dist/src/deck-stage.js +0 -21
  110. package/dist/src/highlight.js +0 -32
  111. package/dist/src/index.js +0 -86
  112. package/dist/src/kits/os/about-app.js +0 -52
  113. package/dist/src/kits/os/app-panes.css +0 -152
  114. package/dist/src/kits/os/browser-app.js +0 -58
  115. package/dist/src/kits/os/files-app.js +0 -44
  116. package/dist/src/kits/os/freddie/helpers.js +0 -59
  117. package/dist/src/kits/os/freddie/pages-chat.js +0 -143
  118. package/dist/src/kits/os/freddie/pages-core.js +0 -101
  119. package/dist/src/kits/os/freddie/pages-os.js +0 -51
  120. package/dist/src/kits/os/freddie/pages-tools.js +0 -183
  121. package/dist/src/kits/os/freddie/routes.js +0 -24
  122. package/dist/src/kits/os/freddie-dashboard.css +0 -51
  123. package/dist/src/kits/os/freddie-dashboard.js +0 -101
  124. package/dist/src/kits/os/icons.js +0 -17
  125. package/dist/src/kits/os/index.js +0 -5
  126. package/dist/src/kits/os/launcher.css +0 -61
  127. package/dist/src/kits/os/launcher.js +0 -79
  128. package/dist/src/kits/os/monitor-app.js +0 -34
  129. package/dist/src/kits/os/shell.js +0 -214
  130. package/dist/src/kits/os/terminal-app.js +0 -45
  131. package/dist/src/kits/os/theme.css +0 -412
  132. package/dist/src/kits/os/validate.css +0 -19
  133. package/dist/src/kits/os/validator-app.js +0 -55
  134. package/dist/src/kits/os/wm.css +0 -115
  135. package/dist/src/kits/os/wm.js +0 -111
  136. package/dist/src/markdown.js +0 -39
  137. package/dist/src/motion.js +0 -35
  138. package/dist/src/page-html.js +0 -196
  139. package/dist/src/styles.js +0 -25
  140. package/dist/src/theme.js +0 -99
  141. package/dist/src/web-components/ds-chat.js +0 -45
  142. package/dist/ui_kits/aicat/README.md +0 -7
  143. package/dist/ui_kits/aicat/app.js +0 -156
  144. package/dist/ui_kits/aicat/index.html +0 -26
  145. package/dist/ui_kits/aicat/sample-square.png +0 -0
  146. package/dist/ui_kits/aicat/sample-svg.svg +0 -1
  147. package/dist/ui_kits/aicat/sample.pdf +0 -32
  148. package/dist/ui_kits/blog/README.md +0 -3
  149. package/dist/ui_kits/blog/index.html +0 -90
  150. package/dist/ui_kits/chat/README.md +0 -5
  151. package/dist/ui_kits/chat/app.js +0 -110
  152. package/dist/ui_kits/chat/index.html +0 -26
  153. package/dist/ui_kits/chat/sample-square.png +0 -0
  154. package/dist/ui_kits/chat/sample-svg.svg +0 -1
  155. package/dist/ui_kits/chat/sample.pdf +0 -32
  156. package/dist/ui_kits/community/app.js +0 -134
  157. package/dist/ui_kits/community/index.html +0 -24
  158. package/dist/ui_kits/dashboard/app.js +0 -92
  159. package/dist/ui_kits/dashboard/index.html +0 -26
  160. package/dist/ui_kits/docs/README.md +0 -3
  161. package/dist/ui_kits/docs/index.html +0 -123
  162. package/dist/ui_kits/error_404/app.js +0 -56
  163. package/dist/ui_kits/error_404/index.html +0 -26
  164. package/dist/ui_kits/file_browser/README.md +0 -48
  165. package/dist/ui_kits/file_browser/app.js +0 -231
  166. package/dist/ui_kits/file_browser/index.html +0 -33
  167. package/dist/ui_kits/gallery/app.js +0 -121
  168. package/dist/ui_kits/gallery/index.html +0 -26
  169. package/dist/ui_kits/homepage/README.md +0 -7
  170. package/dist/ui_kits/homepage/app.js +0 -167
  171. package/dist/ui_kits/homepage/index.html +0 -46
  172. package/dist/ui_kits/project_page/README.md +0 -3
  173. package/dist/ui_kits/project_page/app.js +0 -154
  174. package/dist/ui_kits/project_page/index.html +0 -45
  175. package/dist/ui_kits/search/app.js +0 -107
  176. package/dist/ui_kits/search/index.html +0 -26
  177. package/dist/ui_kits/settings/app.js +0 -133
  178. package/dist/ui_kits/settings/index.html +0 -26
  179. package/dist/ui_kits/signin/app.js +0 -115
  180. package/dist/ui_kits/signin/index.html +0 -26
  181. package/dist/ui_kits/slide_deck/app.js +0 -174
  182. package/dist/ui_kits/slide_deck/index.html +0 -26
  183. package/dist/ui_kits/system_primer/app.js +0 -152
  184. package/dist/ui_kits/system_primer/index.html +0 -26
  185. package/dist/ui_kits/terminal/app.js +0 -150
  186. package/dist/ui_kits/terminal/index.html +0 -26
  187. package/dist/vendor/webjsx/applyDiff.js +0 -182
  188. package/dist/vendor/webjsx/attributes.js +0 -154
  189. package/dist/vendor/webjsx/constants.js +0 -4
  190. package/dist/vendor/webjsx/createDOMElement.js +0 -52
  191. package/dist/vendor/webjsx/createElement.js +0 -75
  192. package/dist/vendor/webjsx/elementTags.js +0 -115
  193. package/dist/vendor/webjsx/factory.js +0 -6
  194. package/dist/vendor/webjsx/index.js +0 -6
  195. package/dist/vendor/webjsx/jsx-dev-runtime.js +0 -2
  196. package/dist/vendor/webjsx/jsx-runtime.js +0 -30
  197. package/dist/vendor/webjsx/jsx.js +0 -2
  198. package/dist/vendor/webjsx/package.json +0 -39
  199. package/dist/vendor/webjsx/renderSuspension.js +0 -25
  200. package/dist/vendor/webjsx/types.js +0 -5
  201. package/dist/vendor/webjsx/utils.js +0 -84
  202. package/src/components/overlays.js +0 -151
@@ -1,208 +1,190 @@
1
- // Community surface — matches upstream signatures.
2
-
3
- import * as webjsx from '../../vendor/webjsx/index.js';
4
- const h = webjsx.createElement;
5
-
6
- export function ServerIcon({ id, name, icon, active, badge, onClick } = {}) {
7
- const initials = (name || '?').slice(0, 2).toUpperCase();
8
- return h('div', { class: 'cm-server-icon' + (active ? ' active' : ''), onclick: onClick, title: name, 'data-id': id },
9
- h('span', { class: 'cm-server-pill' }),
10
- icon ? h('img', { src: icon, alt: name }) : h('span', {}, initials),
11
- badge ? h('span', { class: 'cm-server-badge' }, badge > 99 ? '99+' : String(badge)) : null
12
- );
13
- }
14
-
15
- export function ServerRail({ servers = [], activeId, onSelect, onAdd } = {}) {
16
- return h('div', { class: 'cm-server-rail' },
17
- h('a', { class: 'cm-server-back', href: '../', title: 'Back' }, '◰'),
18
- h('div', { class: 'cm-server-sep' }),
19
- ...servers.map(s => ServerIcon({ ...s, active: s.id === activeId, onClick: () => onSelect && onSelect(s.id) })),
20
- onAdd ? h('button', { class: 'cm-server-add', onclick: onAdd, title: 'Add server' }, '+') : null
21
- );
22
- }
23
-
24
- export function ChannelItem({ id, name, type = 'text', active, voiceActive, voiceConnecting, badge, draggable, actions = [], participants = [], onClick, onContext } = {}) {
25
- const icon = type === 'voice' ? '🔊' : type === 'forum' ? '◻' : type === 'threaded' ? '◉' : type === 'announcement' ? '📣' : type === 'page' ? '📄' : type === 'thread' ? '🧵' : '#';
26
- return h('div', { class: 'cm-channel-item-wrap', 'data-channel-wrap': id },
27
- h('div', {
28
- class: 'cm-channel-item' + (active ? ' active' : '') + (voiceActive ? ' voice-active' : '') + (voiceConnecting ? ' voice-connecting' : ''),
29
- 'data-id': id,
30
- 'data-type': type,
31
- draggable: draggable ? 'true' : null,
32
- onclick: onClick,
33
- oncontextmenu: (e) => { e.preventDefault(); onContext && onContext(id, e.clientX, e.clientY); }
34
- },
35
- h('span', { class: 'cm-ch-icon' }, icon),
36
- voiceConnecting ? h('span', { class: 'cm-ch-spinner', title: 'Connecting…' }) : null,
37
- h('span', { class: 'cm-ch-name' }, name),
38
- badge ? h('span', { class: 'cm-ch-badge' }, badge > 99 ? '99+' : String(badge)) : null,
39
- actions.length ? h('div', { class: 'cm-ch-actions' },
40
- ...actions.map(a => h('button', {
41
- class: 'cm-ch-action-btn',
42
- title: a.title || '',
43
- 'data-action': a.id || '',
44
- onclick: (e) => { e.stopPropagation(); a.onClick && a.onClick(id, e); }
45
- }, a.icon || a.label || '⋯'))
46
- ) : null
47
- ),
48
- voiceActive && participants.length ? h('div', { class: 'cm-ch-voice-users' },
49
- ...participants.map(p => h('div', { class: 'cm-ch-voice-user' + (p.speaking ? ' speaking' : '') },
50
- h('div', { class: 'cm-ch-voice-user-avatar', style: p.color ? `background:${p.color}` : '' }, (p.identity || '?').slice(0, 1).toUpperCase()),
51
- h('span', { class: 'cm-ch-voice-user-name' }, p.identity)
52
- ))
53
- ) : null
54
- );
55
- }
56
-
57
- export function ChannelCategory({ id, name, channels = [], collapsed, activeId, onToggle, onAddChannel, onChannelClick, onChannelContext, onContextMenu, extraButton, channelDraggable } = {}) {
58
- return h('div', { class: 'cm-channel-category', 'data-category': id },
59
- h('div', {
60
- class: 'cm-category-header' + (collapsed ? ' collapsed' : ''),
61
- onclick: () => onToggle && onToggle(id),
62
- oncontextmenu: onContextMenu ? (e) => { e.preventDefault(); onContextMenu(id, e.clientX, e.clientY); } : null
63
- },
64
- h('svg', { class: 'cm-cat-arrow', viewBox: '0 0 24 24' }, h('path', { d: 'M7 10l5 5 5-5z' })),
65
- h('span', { class: 'cm-cat-name' }, name),
66
- extraButton ? h('button', { class: 'cm-cat-extra', onclick: (e) => { e.stopPropagation(); extraButton.onClick && extraButton.onClick(id, e); }, title: extraButton.title || '' }, extraButton.icon || extraButton.label || '+') : null,
67
- onAddChannel ? h('button', { class: 'cm-cat-add', onclick: (e) => { e.stopPropagation(); onAddChannel(id); }, title: 'Add channel' }, '+') : null
68
- ),
69
- collapsed ? null : h('div', { class: 'cm-cat-channels' },
70
- ...channels.map(c => ChannelItem({
71
- ...c,
72
- draggable: channelDraggable,
73
- active: c.id === activeId,
74
- onClick: () => onChannelClick && onChannelClick(c),
75
- onContext: onChannelContext
76
- }))
77
- )
78
- );
79
- }
80
-
81
- export function VoiceUser({ identity, speaking, color, quality, videoStream } = {}) {
82
- const initial = (identity || '?').slice(0, 1).toUpperCase();
83
- const q = quality || null;
84
- const videoRef = videoStream ? (el) => {
85
- if (el && el.srcObject !== videoStream) el.srcObject = videoStream;
86
- } : null;
87
- return h('div', { class: 'cm-voice-user' + (speaking ? ' speaking' : '') },
88
- h('div', { class: 'cm-voice-user-avatar', style: color ? `background:${color}` : '' },
89
- videoStream ? h('video', {
90
- class: 'cm-voice-user-video',
91
- autoplay: true,
92
- muted: true,
93
- playsinline: true,
94
- ref: videoRef
95
- }) : initial,
96
- q ? h('span', { class: `cm-voice-user-quality q-${q}`, title: `Connection: ${q}` }) : null
97
- ),
98
- h('span', { class: 'cm-voice-user-name' }, identity)
99
- );
100
- }
101
-
102
- export function UserPanel({ name, tag, color, muted, deafened, onMute, onDeafen, onSettings, extraButtons = [] } = {}) {
103
- const initial = (name || '?').slice(0, 1).toUpperCase();
104
- return h('div', { class: 'cm-user-panel' },
105
- h('div', { class: 'cm-user-avatar', style: color ? `background:${color}` : '' },
106
- h('span', { class: 'cm-user-status-dot' }),
107
- initial
108
- ),
109
- h('div', { class: 'cm-user-info' },
110
- h('div', { class: 'cm-user-name' }, name || 'You'),
111
- tag ? h('div', { class: 'cm-user-tag' }, tag) : null
112
- ),
113
- h('div', { class: 'cm-user-controls' },
114
- ...extraButtons.map(b => h('button', {
115
- class: 'cm-user-btn extra' + (b.active ? ' active' : ''),
116
- onclick: b.onClick,
117
- title: b.title || ''
118
- }, b.icon || '·')),
119
- h('button', { class: 'cm-user-btn' + (muted ? ' muted' : ''), onclick: onMute, title: muted ? 'Unmute' : 'Mute' }, muted ? '🔇' : '🎤'),
120
- h('button', { class: 'cm-user-btn' + (deafened ? ' deafened' : ''), onclick: onDeafen, title: deafened ? 'Undeafen' : 'Deafen' }, deafened ? '🔕' : '🎧'),
121
- h('button', { class: 'cm-user-btn', onclick: onSettings, title: 'Settings' }, '⚙')
122
- )
123
- );
124
- }
125
-
126
- export function ChannelSidebar({ serverName, channels = [], categories = [], activeId, collapsedCats, onChannelClick, onCategoryToggle, onAddChannel, onChannelContext, userPanelProps } = {}) {
127
- const collapsed = collapsedCats || new Set();
128
- const uncategorized = channels.filter(c => !c.categoryId || !categories.find(cat => cat.id === c.categoryId));
129
- const sorted = [...categories].sort((a, b) => (a.position || 0) - (b.position || 0));
130
- return h('div', { class: 'cm-channel-sidebar' },
131
- h('div', { class: 'cm-server-header' },
132
- h('span', { class: 'cm-server-header-name' }, serverName || 'Server'),
133
- ),
134
- h('div', { class: 'cm-channel-list' },
135
- ...sorted.map(cat => ChannelCategory({
136
- id: cat.id,
137
- name: cat.name,
138
- channels: channels.filter(c => c.categoryId === cat.id).sort((a, b) => (a.position || 0) - (b.position || 0)),
139
- collapsed: collapsed.has && collapsed.has(cat.id),
140
- activeId,
141
- onToggle: onCategoryToggle,
142
- onAddChannel,
143
- onChannelClick,
144
- onChannelContext
145
- })),
146
- uncategorized.length ? ChannelCategory({
147
- id: 'uncategorized',
148
- name: 'CHANNELS',
149
- channels: uncategorized,
150
- activeId,
151
- onChannelClick,
152
- onChannelContext
153
- }) : null
154
- ),
155
- userPanelProps ? UserPanel(userPanelProps) : null
156
- );
157
- }
158
-
159
- export function MemberItem({ identity, name, color, status = 'online' } = {}) {
160
- const initial = (name || identity || '?').slice(0, 1).toUpperCase();
161
- return h('div', { class: 'cm-member-item' },
162
- h('div', { class: 'cm-member-avatar', style: color ? `background:${color}` : '' },
163
- h('span', { class: 'cm-member-status' + (status === 'online' ? ' online' : '') }),
164
- initial
165
- ),
166
- h('span', { class: 'cm-member-name' }, name || identity)
167
- );
168
- }
169
-
170
- export function MemberList({ categories = [], open } = {}) {
171
- return h('div', { class: 'cm-member-list' + (open ? ' open' : '') },
172
- ...categories.flatMap(cat => [
173
- h('div', { class: 'cm-member-category', key: cat.label }, `${cat.label} ${cat.members.length}`),
174
- ...cat.members.map((m, i) => MemberItem({ ...m, key: m.identity || i }))
175
- ])
176
- );
177
- }
178
-
179
- export function ChatHeader({ icon = '#', name, topic, toolbar = [] } = {}) {
180
- return h('div', { class: 'cm-chat-header' },
181
- h('span', { class: 'cm-chat-header-icon' }, icon),
182
- h('span', { class: 'cm-chat-header-name' }, name),
183
- topic ? h('span', { class: 'cm-chat-header-topic' }, topic) : null,
184
- h('div', { class: 'cm-chat-header-toolbar' }, ...toolbar)
185
- );
186
- }
187
-
188
- export function VoiceStrip({ channelName, status, muted, deafened, onMute, onDeafen, onLeave, open } = {}) {
189
- return h('div', { class: 'cm-voice-strip' + (open ? ' open' : '') },
190
- h('div', { class: 'cm-vs-label' },
191
- h('span', { class: 'cm-vs-channel' }, '🔊 ' + (channelName || 'voice')),
192
- h('span', { class: 'cm-vs-status' }, status || 'connected')
193
- ),
194
- h('button', { class: 'cm-vs-btn', onclick: onMute, title: 'Mute' }, muted ? '🔇' : '🎤'),
195
- h('button', { class: 'cm-vs-btn', onclick: onDeafen, title: 'Deafen' }, deafened ? '🔕' : '🎧'),
196
- h('button', { class: 'cm-vs-btn danger', onclick: onLeave, title: 'Leave' }, '✕')
197
- );
198
- }
199
-
200
- export function CommunityShell({ serverRailProps, sidebarProps, children, memberListProps, voiceStripProps } = {}) {
201
- return h('div', { class: 'cm-shell' },
202
- serverRailProps ? ServerRail(serverRailProps) : null,
203
- sidebarProps ? ChannelSidebar(sidebarProps) : null,
204
- h('div', { class: 'cm-main' }, ...(Array.isArray(children) ? children : [children])),
205
- memberListProps ? MemberList(memberListProps) : null,
206
- voiceStripProps ? VoiceStrip(voiceStripProps) : null
207
- );
208
- }
1
+ // Community surface — matches upstream signatures.
2
+
3
+ import * as webjsx from '../../vendor/webjsx/index.js';
4
+ const h = webjsx.createElement;
5
+
6
+ export function ServerIcon({ id, name, icon, active, badge, onClick } = {}) {
7
+ const initials = (name || '?').slice(0, 2).toUpperCase();
8
+ return h('div', { class: 'cm-server-icon' + (active ? ' active' : ''), onclick: onClick, title: name, 'data-id': id },
9
+ h('span', { class: 'cm-server-pill' }),
10
+ icon ? h('img', { src: icon, alt: name }) : h('span', {}, initials),
11
+ badge ? h('span', { class: 'cm-server-badge' }, badge > 99 ? '99+' : String(badge)) : null
12
+ );
13
+ }
14
+
15
+ export function ServerRail({ servers = [], activeId, onSelect, onAdd } = {}) {
16
+ return h('div', { class: 'cm-server-rail' },
17
+ h('a', { class: 'cm-server-back', href: '../', title: 'Back' }, '◰'),
18
+ h('div', { class: 'cm-server-sep' }),
19
+ ...servers.map(s => ServerIcon({ ...s, active: s.id === activeId, onClick: () => onSelect && onSelect(s.id) })),
20
+ onAdd ? h('button', { class: 'cm-server-add', onclick: onAdd, title: 'Add server' }, '+') : null
21
+ );
22
+ }
23
+
24
+ export function ChannelItem({ id, name, type = 'text', active, voiceActive, voiceConnecting, badge, draggable, actions = [], participants = [], onClick, onContext } = {}) {
25
+ const icon = type === 'voice' ? '🔊' : type === 'forum' ? '◻' : type === 'threaded' ? '◉' : type === 'announcement' ? '📣' : type === 'page' ? '📄' : type === 'thread' ? '🧵' : '#';
26
+ return h('div', { class: 'cm-channel-item-wrap', 'data-channel-wrap': id },
27
+ h('div', {
28
+ class: 'cm-channel-item' + (active ? ' active' : '') + (voiceActive ? ' voice-active' : '') + (voiceConnecting ? ' voice-connecting' : ''),
29
+ 'data-id': id,
30
+ 'data-type': type,
31
+ draggable: draggable ? 'true' : null,
32
+ onclick: onClick,
33
+ oncontextmenu: (e) => { e.preventDefault(); onContext && onContext(id, e.clientX, e.clientY); }
34
+ },
35
+ h('span', { class: 'cm-ch-icon' }, icon),
36
+ voiceConnecting ? h('span', { class: 'cm-ch-spinner', title: 'Connecting…' }) : null,
37
+ h('span', { class: 'cm-ch-name' }, name),
38
+ badge ? h('span', { class: 'cm-ch-badge' }, badge > 99 ? '99+' : String(badge)) : null,
39
+ actions.length ? h('div', { class: 'cm-ch-actions' },
40
+ ...actions.map(a => h('button', {
41
+ class: 'cm-ch-action-btn',
42
+ title: a.title || '',
43
+ 'data-action': a.id || '',
44
+ onclick: (e) => { e.stopPropagation(); a.onClick && a.onClick(id, e); }
45
+ }, a.icon || a.label || '⋯'))
46
+ ) : null
47
+ ),
48
+ voiceActive && participants.length ? h('div', { class: 'cm-ch-voice-users' },
49
+ ...participants.map(p => h('div', { class: 'cm-ch-voice-user' + (p.speaking ? ' speaking' : '') },
50
+ h('div', { class: 'cm-ch-voice-user-avatar', style: p.color ? `background:${p.color}` : '' }, (p.identity || '?').slice(0, 1).toUpperCase()),
51
+ h('span', { class: 'cm-ch-voice-user-name' }, p.identity)
52
+ ))
53
+ ) : null
54
+ );
55
+ }
56
+
57
+ export function ChannelCategory({ id, name, channels = [], collapsed, activeId, onToggle, onAddChannel, onChannelClick, onChannelContext, onContextMenu, extraButton, channelDraggable } = {}) {
58
+ return h('div', { class: 'cm-channel-category', 'data-category': id },
59
+ h('div', {
60
+ class: 'cm-category-header' + (collapsed ? ' collapsed' : ''),
61
+ onclick: () => onToggle && onToggle(id),
62
+ oncontextmenu: onContextMenu ? (e) => { e.preventDefault(); onContextMenu(id, e.clientX, e.clientY); } : null
63
+ },
64
+ h('svg', { class: 'cm-cat-arrow', viewBox: '0 0 24 24' }, h('path', { d: 'M7 10l5 5 5-5z' })),
65
+ h('span', { class: 'cm-cat-name' }, name),
66
+ extraButton ? h('button', { class: 'cm-cat-extra', onclick: (e) => { e.stopPropagation(); extraButton.onClick && extraButton.onClick(id, e); }, title: extraButton.title || '' }, extraButton.icon || extraButton.label || '+') : null,
67
+ onAddChannel ? h('button', { class: 'cm-cat-add', onclick: (e) => { e.stopPropagation(); onAddChannel(id); }, title: 'Add channel' }, '+') : null
68
+ ),
69
+ collapsed ? null : h('div', { class: 'cm-cat-channels' },
70
+ ...channels.map(c => ChannelItem({
71
+ ...c,
72
+ draggable: channelDraggable,
73
+ active: c.id === activeId,
74
+ onClick: () => onChannelClick && onChannelClick(c),
75
+ onContext: onChannelContext
76
+ }))
77
+ )
78
+ );
79
+ }
80
+
81
+ export function VoiceUser({ identity, speaking, color } = {}) {
82
+ const initial = (identity || '?').slice(0, 1).toUpperCase();
83
+ return h('div', { class: 'cm-voice-user' + (speaking ? ' speaking' : '') },
84
+ h('div', { class: 'cm-voice-user-avatar', style: color ? `background:${color}` : '' }, initial),
85
+ h('span', { class: 'cm-voice-user-name' }, identity)
86
+ );
87
+ }
88
+
89
+ export function UserPanel({ name, tag, color, muted, deafened, onMute, onDeafen, onSettings } = {}) {
90
+ const initial = (name || '?').slice(0, 1).toUpperCase();
91
+ return h('div', { class: 'cm-user-panel' },
92
+ h('div', { class: 'cm-user-avatar', style: color ? `background:${color}` : '' },
93
+ h('span', { class: 'cm-user-status-dot' }),
94
+ initial
95
+ ),
96
+ h('div', { class: 'cm-user-info' },
97
+ h('div', { class: 'cm-user-name' }, name || 'You'),
98
+ tag ? h('div', { class: 'cm-user-tag' }, tag) : null
99
+ ),
100
+ h('div', { class: 'cm-user-controls' },
101
+ h('button', { class: 'cm-user-btn' + (muted ? ' muted' : ''), onclick: onMute, title: muted ? 'Unmute' : 'Mute' }, muted ? '🔇' : '🎤'),
102
+ h('button', { class: 'cm-user-btn' + (deafened ? ' deafened' : ''), onclick: onDeafen, title: deafened ? 'Undeafen' : 'Deafen' }, deafened ? '🔕' : '🎧'),
103
+ h('button', { class: 'cm-user-btn', onclick: onSettings, title: 'Settings' }, '⚙')
104
+ )
105
+ );
106
+ }
107
+
108
+ export function ChannelSidebar({ serverName, channels = [], categories = [], activeId, collapsedCats, onChannelClick, onCategoryToggle, onAddChannel, onChannelContext, userPanelProps } = {}) {
109
+ const collapsed = collapsedCats || new Set();
110
+ const uncategorized = channels.filter(c => !c.categoryId || !categories.find(cat => cat.id === c.categoryId));
111
+ const sorted = [...categories].sort((a, b) => (a.position || 0) - (b.position || 0));
112
+ return h('div', { class: 'cm-channel-sidebar' },
113
+ h('div', { class: 'cm-server-header' },
114
+ h('span', { class: 'cm-server-header-name' }, serverName || 'Server'),
115
+ ),
116
+ h('div', { class: 'cm-channel-list' },
117
+ ...sorted.map(cat => ChannelCategory({
118
+ id: cat.id,
119
+ name: cat.name,
120
+ channels: channels.filter(c => c.categoryId === cat.id).sort((a, b) => (a.position || 0) - (b.position || 0)),
121
+ collapsed: collapsed.has && collapsed.has(cat.id),
122
+ activeId,
123
+ onToggle: onCategoryToggle,
124
+ onAddChannel,
125
+ onChannelClick,
126
+ onChannelContext
127
+ })),
128
+ uncategorized.length ? ChannelCategory({
129
+ id: 'uncategorized',
130
+ name: 'CHANNELS',
131
+ channels: uncategorized,
132
+ activeId,
133
+ onChannelClick,
134
+ onChannelContext
135
+ }) : null
136
+ ),
137
+ userPanelProps ? UserPanel(userPanelProps) : null
138
+ );
139
+ }
140
+
141
+ export function MemberItem({ identity, name, color, status = 'online' } = {}) {
142
+ const initial = (name || identity || '?').slice(0, 1).toUpperCase();
143
+ return h('div', { class: 'cm-member-item' },
144
+ h('div', { class: 'cm-member-avatar', style: color ? `background:${color}` : '' },
145
+ h('span', { class: 'cm-member-status' + (status === 'online' ? ' online' : '') }),
146
+ initial
147
+ ),
148
+ h('span', { class: 'cm-member-name' }, name || identity)
149
+ );
150
+ }
151
+
152
+ export function MemberList({ categories = [], open } = {}) {
153
+ return h('div', { class: 'cm-member-list' + (open ? ' open' : '') },
154
+ ...categories.flatMap(cat => [
155
+ h('div', { class: 'cm-member-category', key: cat.label }, `${cat.label} — ${cat.members.length}`),
156
+ ...cat.members.map((m, i) => MemberItem({ ...m, key: m.identity || i }))
157
+ ])
158
+ );
159
+ }
160
+
161
+ export function ChatHeader({ icon = '#', name, topic, toolbar = [] } = {}) {
162
+ return h('div', { class: 'cm-chat-header' },
163
+ h('span', { class: 'cm-chat-header-icon' }, icon),
164
+ h('span', { class: 'cm-chat-header-name' }, name),
165
+ topic ? h('span', { class: 'cm-chat-header-topic' }, topic) : null,
166
+ h('div', { class: 'cm-chat-header-toolbar' }, ...toolbar)
167
+ );
168
+ }
169
+
170
+ export function VoiceStrip({ channelName, status, muted, deafened, onMute, onDeafen, onLeave, open } = {}) {
171
+ return h('div', { class: 'cm-voice-strip' + (open ? ' open' : '') },
172
+ h('div', { class: 'cm-vs-label' },
173
+ h('span', { class: 'cm-vs-channel' }, '🔊 ' + (channelName || 'voice')),
174
+ h('span', { class: 'cm-vs-status' }, status || 'connected')
175
+ ),
176
+ h('button', { class: 'cm-vs-btn', onclick: onMute, title: 'Mute' }, muted ? '🔇' : '🎤'),
177
+ h('button', { class: 'cm-vs-btn', onclick: onDeafen, title: 'Deafen' }, deafened ? '🔕' : '🎧'),
178
+ h('button', { class: 'cm-vs-btn danger', onclick: onLeave, title: 'Leave' }, '✕')
179
+ );
180
+ }
181
+
182
+ export function CommunityShell({ serverRailProps, sidebarProps, children, memberListProps, voiceStripProps } = {}) {
183
+ return h('div', { class: 'cm-shell' },
184
+ serverRailProps ? ServerRail(serverRailProps) : null,
185
+ sidebarProps ? ChannelSidebar(sidebarProps) : null,
186
+ h('div', { class: 'cm-main' }, ...(Array.isArray(children) ? children : [children])),
187
+ memberListProps ? MemberList(memberListProps) : null,
188
+ voiceStripProps ? VoiceStrip(voiceStripProps) : null
189
+ );
190
+ }