anentrypoint-design 0.0.414 → 0.0.415
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.
- package/dist/247420.js +62 -12
- package/dist/app-shell.css +40 -594
- package/dist/colors_and_type.css +607 -47
- package/dist/index.html +227 -277
- package/dist/preview/buttons.html +39 -28
- package/dist/preview/colors-core.html +18 -8
- package/dist/preview/colors-lore.html +26 -17
- package/dist/preview/colors-semantic.html +24 -18
- package/dist/preview/dateline.html +6 -5
- package/dist/preview/dropzone.html +4 -3
- package/dist/preview/file-grid.html +10 -9
- package/dist/preview/file-row.html +14 -13
- package/dist/preview/file-toolbar.html +4 -3
- package/dist/preview/file-viewer.html +4 -3
- package/dist/preview/header.html +26 -24
- package/dist/preview/icons-unicode.html +8 -5
- package/dist/preview/index-row.html +17 -9
- package/dist/preview/inputs.html +4 -3
- package/dist/preview/manifesto.html +34 -17
- package/dist/preview/motion-default.js +77 -57
- package/dist/preview/rules.html +4 -3
- package/dist/preview/spacing.html +13 -12
- package/dist/preview/stamps-lore.html +5 -4
- package/dist/preview/stamps.html +4 -3
- package/dist/preview/theme-ink.html +7 -6
- package/dist/preview/type-display.html +14 -5
- package/dist/preview/type-mono.html +6 -5
- package/dist/preview/type-prose.html +12 -10
- package/dist/preview/type-scale.html +4 -3
- package/dist/preview/wordmarks.html +10 -7
- package/dist/site/content/globals/site.yaml +5 -3
- package/dist/site/content/pages/freddie.yaml +2 -2
- package/dist/site/content/pages/home.yaml +55 -55
- package/dist/site/theme.mjs +96 -348
- package/dist/slides/index.html +13 -13
- package/dist/src/bootstrap.js +10 -1
- package/dist/src/components/chat.js +43 -195
- package/dist/src/components/community.js +18 -166
- package/dist/src/components/content.js +30 -210
- package/dist/src/components/files-modals.js +14 -106
- package/dist/src/components/files.js +15 -116
- package/dist/src/components/freddie/helpers.js +53 -8
- package/dist/src/components/freddie.js +35 -25
- package/dist/src/components/shell.js +16 -114
- package/dist/src/components/theme-toggle.js +69 -25
- package/dist/src/components.js +92 -13
- package/dist/src/deck-stage.js +6 -4
- package/dist/src/highlight.js +51 -7
- package/dist/src/index.js +58 -12
- package/dist/src/kits/os/about-app.js +2 -3
- package/dist/src/kits/os/app-panes.css +46 -70
- package/dist/src/kits/os/browser-app.js +28 -1
- package/dist/src/kits/os/files-app.js +7 -0
- package/dist/src/kits/os/freddie/pages-chat.js +90 -75
- package/dist/src/kits/os/freddie/pages-core.js +9 -9
- package/dist/src/kits/os/freddie/pages-os.js +8 -8
- package/dist/src/kits/os/freddie/pages-tools.js +14 -14
- package/dist/src/kits/os/freddie/routes.js +21 -19
- package/dist/src/kits/os/freddie-dashboard.css +42 -21
- package/dist/src/kits/os/freddie-dashboard.js +6 -6
- package/dist/src/kits/os/icons.js +38 -16
- package/dist/src/kits/os/index.js +11 -0
- package/dist/src/kits/os/launcher.css +26 -10
- package/dist/src/kits/os/launcher.js +3 -0
- package/dist/src/kits/os/shell.js +72 -108
- package/dist/src/kits/os/terminal-app.js +1 -1
- package/dist/src/kits/os/theme.css +1330 -150
- package/dist/src/kits/os/wm.css +68 -15
- package/dist/src/kits/os/wm.js +93 -17
- package/dist/src/markdown.js +121 -12
- package/dist/src/motion.js +18 -2
- package/dist/src/page-html.js +62 -134
- package/dist/src/theme.js +106 -13
- package/dist/src/web-components/ds-chat.js +80 -9
- package/dist/ui_kits/aicat/app.js +54 -6
- package/dist/ui_kits/aicat/index.html +2 -0
- package/dist/ui_kits/blog/index.html +33 -17
- package/dist/ui_kits/chat/app.js +90 -22
- package/dist/ui_kits/chat/index.html +2 -0
- package/dist/ui_kits/community/app.js +86 -24
- package/dist/ui_kits/community/index.html +1 -0
- package/dist/ui_kits/dashboard/app.js +123 -34
- package/dist/ui_kits/dashboard/index.html +1 -0
- package/dist/ui_kits/docs/index.html +43 -33
- package/dist/ui_kits/error_404/app.js +78 -23
- package/dist/ui_kits/file_browser/README.md +1 -1
- package/dist/ui_kits/file_browser/app.js +45 -10
- package/dist/ui_kits/file_browser/index.html +1 -0
- package/dist/ui_kits/gallery/app.js +130 -51
- package/dist/ui_kits/homepage/app.js +156 -138
- package/dist/ui_kits/project_page/app.js +142 -125
- package/dist/ui_kits/project_page/index.html +2 -1
- package/dist/ui_kits/search/app.js +52 -15
- package/dist/ui_kits/settings/app.js +166 -39
- package/dist/ui_kits/signin/app.js +175 -49
- package/dist/ui_kits/signin/index.html +1 -0
- package/dist/ui_kits/slide_deck/app.js +83 -49
- package/dist/ui_kits/system_primer/app.js +57 -40
- package/dist/ui_kits/terminal/app.js +160 -43
- package/package.json +1 -1
- package/src/page-html/client-script.js +40 -11
- package/src/page-html/page-styles.js +22 -1
|
@@ -1,199 +1,47 @@
|
|
|
1
1
|
// Chat surface — matches upstream signatures (parts, typing, reactions,
|
|
2
2
|
// receipts, aicat). Pure factories — props in, vnode out.
|
|
3
3
|
// Includes ChatMessage, ChatComposer, Chat, AICat, AICatPortrait.
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
//
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
const
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
const refSink = (el) => {
|
|
48
|
-
if (!el) return;
|
|
49
|
-
if (el.dataset.mdSrc === p.text) return;
|
|
50
|
-
el.dataset.mdSrc = p.text || '';
|
|
51
|
-
ensureMarkdownReady().then(() => renderMarkdown(p.text || '')).then((html) => { el.innerHTML = html; });
|
|
52
|
-
};
|
|
53
|
-
return h('div', { class: 'chat-bubble chat-md', ref: refSink });
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
function CodeNode(p) {
|
|
57
|
-
const refSink = (el) => {
|
|
58
|
-
if (!el) return;
|
|
59
|
-
if (el.dataset.codeKey === (p.lang || '') + '|' + (p.code || '').length) return;
|
|
60
|
-
el.dataset.codeKey = (p.lang || '') + '|' + (p.code || '').length;
|
|
61
|
-
ensurePrism().then(() => highlightAllUnder(el));
|
|
62
|
-
};
|
|
63
|
-
return h('div', { class: 'chat-bubble chat-code', ref: refSink },
|
|
64
|
-
h('div', { class: 'chat-code-head' },
|
|
65
|
-
h('span', { class: 'lang' }, p.lang || 'code'),
|
|
66
|
-
p.filename ? h('span', { class: 'name' }, p.filename) : null
|
|
67
|
-
),
|
|
68
|
-
h('pre', {}, h('code', { class: p.lang ? 'lang-' + p.lang + ' language-' + p.lang : '' }, p.code || ''))
|
|
69
|
-
);
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
const PART_RENDERERS = {
|
|
73
|
-
text: (p) => h('div', { class: 'chat-bubble' }, ...renderInline(p.text || '')),
|
|
74
|
-
md: (p) => MdNode(p),
|
|
75
|
-
code: (p) => CodeNode(p),
|
|
76
|
-
image: (p) => h('a', { class: 'chat-image', href: p.href || p.src, target: '_blank', rel: 'noopener' },
|
|
77
|
-
h('img', { src: p.src, alt: p.alt || '', loading: 'lazy' }),
|
|
78
|
-
p.caption ? h('span', { class: 'cap' }, p.caption) : null),
|
|
79
|
-
pdf: (p) => h('div', { class: 'chat-pdf' },
|
|
80
|
-
h('div', { class: 'chat-pdf-head' },
|
|
81
|
-
h('span', { class: 'glyph' }, '▤'),
|
|
82
|
-
h('span', { class: 'name' }, p.name || 'document.pdf'),
|
|
83
|
-
p.size != null ? h('span', { class: 'size' }, fmtBytes(p.size)) : null,
|
|
84
|
-
h('a', { class: 'open', href: p.src, target: '_blank', rel: 'noopener' }, 'open ↗')
|
|
85
|
-
),
|
|
86
|
-
h('embed', { src: p.src, type: 'application/pdf' })),
|
|
87
|
-
file: (p) => h('a', { class: 'chat-file', href: p.src, target: '_blank', rel: 'noopener', download: p.name || true },
|
|
88
|
-
h('span', { class: 'glyph' }, fileGlyph(p.name)),
|
|
89
|
-
h('span', { class: 'meta' },
|
|
90
|
-
h('span', { class: 'name' }, p.name || 'attachment'),
|
|
91
|
-
h('span', { class: 'size' }, [p.kindLabel || (p.name || '').split('.').pop().toUpperCase(), p.size != null ? fmtBytes(p.size) : null].filter(Boolean).join(' · '))
|
|
92
|
-
),
|
|
93
|
-
h('span', { class: 'go' }, '↓')),
|
|
94
|
-
link: (p) => h('a', { class: 'chat-link', href: p.href, target: '_blank', rel: 'noopener' },
|
|
95
|
-
p.thumb ? h('img', { class: 'thumb', src: p.thumb, alt: '' }) : null,
|
|
96
|
-
h('span', { class: 'meta' },
|
|
97
|
-
h('span', { class: 'host' }, p.host || (() => { try { return new URL(p.href).host; } catch { return ''; } })()),
|
|
98
|
-
h('span', { class: 'title' }, p.title || p.href),
|
|
99
|
-
p.desc ? h('span', { class: 'desc' }, p.desc) : null
|
|
100
|
-
))
|
|
4
|
+
//
|
|
5
|
+
// This module is a barrel: every component lives in a single-responsibility
|
|
6
|
+
// submodule under ./chat/, and the public export surface here is unchanged
|
|
7
|
+
// — no consumer import needs to move.
|
|
8
|
+
|
|
9
|
+
import { fmtFileSize } from './files.js';
|
|
10
|
+
import { safeUrl as sharedSafeUrl, renderInline as sharedRenderInline, injectCodeCopy as sharedInjectCodeCopy } from './chat-message-parts.js';
|
|
11
|
+
import { hasSelectionInside, makeThreadAutoScroll } from './chat/thread-scroll.js';
|
|
12
|
+
import { ChatMessage } from './chat/message.js';
|
|
13
|
+
import { ChatComposer } from './chat/composer.js';
|
|
14
|
+
import { flashComposerNote } from './chat/composer-affordances.js';
|
|
15
|
+
import { Chat, AICat, AICatPortrait, ChatSuggestions, AICAT_FACE } from './chat/threads.js';
|
|
16
|
+
// Imported for its side effect: registers the 'chat' snapshot into the single
|
|
17
|
+
// window.__debug registry at module load, exactly as this file did before the
|
|
18
|
+
// split. Nothing here consumes the symbol directly.
|
|
19
|
+
import './chat/stats.js';
|
|
20
|
+
|
|
21
|
+
// ONE byte format across the kit: fmtFileSize (files.js) is canonical; the old
|
|
22
|
+
// divergent fmtBytes ('0.0 KB' for zero, no B tier) is gone — this alias keeps
|
|
23
|
+
// existing imports working while rendering the same string as the Files grid.
|
|
24
|
+
export const fmtBytes = fmtFileSize;
|
|
25
|
+
|
|
26
|
+
// safeUrl / renderInline now live in chat-message-parts.js (the shared
|
|
27
|
+
// message-part renderer both this file and agent-chat.js render parts
|
|
28
|
+
// through) — re-exported here under their original names so every existing
|
|
29
|
+
// consumer of chat.js's public API (components.js barrel, any host importing
|
|
30
|
+
// directly from './components/chat.js') keeps working unchanged.
|
|
31
|
+
export const safeUrl = sharedSafeUrl;
|
|
32
|
+
export const renderInline = sharedRenderInline;
|
|
33
|
+
|
|
34
|
+
// injectCodeCopy / MdNode / CodeNode / ToolCallNode / ThinkingNode /
|
|
35
|
+
// PART_RENDERERS all now live in chat-message-parts.js — the one dispatch
|
|
36
|
+
// table this file, agent-chat.js, and any future chat surface render message
|
|
37
|
+
// parts through. injectCodeCopy stays exported here (re-exported, same
|
|
38
|
+
// signature) since it was part of this file's public surface before the
|
|
39
|
+
// extraction, even though nothing in-repo imports it directly today.
|
|
40
|
+
export const injectCodeCopy = sharedInjectCodeCopy;
|
|
41
|
+
|
|
42
|
+
export {
|
|
43
|
+
hasSelectionInside, makeThreadAutoScroll,
|
|
44
|
+
ChatMessage,
|
|
45
|
+
ChatComposer, flashComposerNote,
|
|
46
|
+
Chat, AICat, AICatPortrait, ChatSuggestions, AICAT_FACE,
|
|
101
47
|
};
|
|
102
|
-
|
|
103
|
-
function renderPart(p, key) {
|
|
104
|
-
const fn = PART_RENDERERS[p.kind] || PART_RENDERERS.text;
|
|
105
|
-
const node = fn(p);
|
|
106
|
-
if (node && typeof node === 'object') node.props = { ...(node.props || {}), key: 'p' + key };
|
|
107
|
-
_stats.lastKindCounts[p.kind] = (_stats.lastKindCounts[p.kind] || 0) + 1;
|
|
108
|
-
return node;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
export function ChatMessage({ who = 'them', avatar, text, parts, time, typing, key, aicat, reactions, receipt, name }) {
|
|
112
|
-
_stats.messages += 1;
|
|
113
|
-
const cls = 'chat-msg ' + who + (aicat && who === 'them' ? ' aicat' : '');
|
|
114
|
-
const av = h('span', { class: 'chat-avatar' }, avatar || (who === 'you' ? 'u' : '?'));
|
|
115
|
-
let bodyNodes;
|
|
116
|
-
if (typing) bodyNodes = [h('span', { class: 'chat-typing', key: 'typ' }, h('span'), h('span'), h('span'))];
|
|
117
|
-
else if (parts && parts.length) bodyNodes = parts.map((p, i) => renderPart(p, i));
|
|
118
|
-
else bodyNodes = [h('div', { class: 'chat-bubble', key: 't' }, ...renderInline(text || ''))];
|
|
119
|
-
const reactionRow = reactions && reactions.length
|
|
120
|
-
? h('div', { class: 'chat-reactions' },
|
|
121
|
-
...reactions.map((r, i) => h('span', { class: 'rxn' + (r.you ? ' you' : ''), key: 'r' + i },
|
|
122
|
-
h('span', { class: 'e' }, r.emoji), h('span', { class: 'n' }, String(r.count)))))
|
|
123
|
-
: null;
|
|
124
|
-
const tickNode = who === 'you' && receipt
|
|
125
|
-
? h('span', { class: 'tick' + (receipt === 'read' ? ' read' : '') }, receipt === 'read' ? '✓✓' : '✓')
|
|
126
|
-
: null;
|
|
127
|
-
const metaItems = [];
|
|
128
|
-
if (name && who === 'them') metaItems.push(h('span', { class: 'who', key: 'w' }, name));
|
|
129
|
-
if (time) metaItems.push(h('span', { class: 't', key: 'ti' }, time));
|
|
130
|
-
if (tickNode) metaItems.push(tickNode);
|
|
131
|
-
const meta = metaItems.length ? h('div', { class: 'chat-meta' }, ...metaItems) : null;
|
|
132
|
-
const stack = h('div', { class: 'chat-stack' }, ...bodyNodes, reactionRow, meta);
|
|
133
|
-
return h('div', { key, class: cls }, who === 'you' ? stack : av, who === 'you' ? av : stack);
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
export function ChatComposer({ value, onInput, onSend, placeholder = 'message…', disabled }) {
|
|
137
|
-
const send = () => {
|
|
138
|
-
const v = (value || '').trim();
|
|
139
|
-
if (!v || disabled) return;
|
|
140
|
-
if (onSend) onSend(v);
|
|
141
|
-
};
|
|
142
|
-
return h('div', { class: 'chat-composer' },
|
|
143
|
-
h('textarea', { value: value || '', placeholder, rows: 1,
|
|
144
|
-
oninput: (e) => onInput && onInput(e.target.value),
|
|
145
|
-
onkeydown: (e) => { if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); send(); } } }),
|
|
146
|
-
h('button', { class: 'send', disabled: disabled || !(value && value.trim()), onclick: send }, '↑')
|
|
147
|
-
);
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
export function Chat({ title = 'chat', sub, messages = [], composer, header } = {}) {
|
|
151
|
-
return h('div', { class: 'chat' },
|
|
152
|
-
header || h('div', { class: 'chat-head' },
|
|
153
|
-
h('span', { class: 'dot' }),
|
|
154
|
-
h('span', {}, title),
|
|
155
|
-
sub ? h('span', { class: 'sub' }, ' · ' + sub) : null,
|
|
156
|
-
h('span', { class: 'spread' }),
|
|
157
|
-
h('span', { class: 'sub' }, String(messages.length).padStart(2, '0') + ' msgs')
|
|
158
|
-
),
|
|
159
|
-
h('div', { class: 'chat-thread' },
|
|
160
|
-
...messages.map((m, i) => ChatMessage({ ...m, key: m.key != null ? m.key : i }))
|
|
161
|
-
),
|
|
162
|
-
composer || null
|
|
163
|
-
);
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
export const AICAT_FACE = ` /\\_/\\\n( o.o )\n > ^ <`;
|
|
167
|
-
|
|
168
|
-
export function AICatPortrait({ name = 'aicat', status = 'idle', face } = {}) {
|
|
169
|
-
return h('div', { class: 'aicat-portrait' },
|
|
170
|
-
h('pre', { class: 'aicat-face' }, face || AICAT_FACE),
|
|
171
|
-
h('div', { class: 'aicat-meta' },
|
|
172
|
-
h('span', { class: 'name' }, name),
|
|
173
|
-
h('span', { class: 'status' }, h('span', { class: 'dot' }, '● '), status)
|
|
174
|
-
)
|
|
175
|
-
);
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
export function AICat({ name = 'aicat', messages = [], thinking, composer, status = 'online · purring' } = {}) {
|
|
179
|
-
const annotated = messages.map((m) =>
|
|
180
|
-
m.who === 'them' ? { ...m, aicat: true, avatar: m.avatar || '=^.^=' } : m);
|
|
181
|
-
const all = thinking
|
|
182
|
-
? [...annotated, { who: 'them', aicat: true, avatar: '=^.^=', typing: true, key: '_thinking' }]
|
|
183
|
-
: annotated;
|
|
184
|
-
return h('div', { class: 'chat' },
|
|
185
|
-
h('div', { class: 'chat-head' },
|
|
186
|
-
h('span', { class: 'dot' }),
|
|
187
|
-
h('span', {}, name),
|
|
188
|
-
h('span', { class: 'sub' }, ' · ' + status),
|
|
189
|
-
h('span', { class: 'spread' }),
|
|
190
|
-
h('span', { class: 'sub' }, String(messages.length).padStart(2, '0') + ' turns')
|
|
191
|
-
),
|
|
192
|
-
h('div', { class: 'chat-thread' },
|
|
193
|
-
...all.map((m, i) => ChatMessage({ ...m, key: m.key != null ? m.key : i }))
|
|
194
|
-
),
|
|
195
|
-
composer || null
|
|
196
|
-
);
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
register('chat', () => ({ messages: _stats.messages, lastKindCounts: { ..._stats.lastKindCounts } }));
|
|
@@ -1,167 +1,19 @@
|
|
|
1
1
|
// Community surface — matches upstream signatures.
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
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, onClick, onContext } = {}) {
|
|
25
|
-
const icon = type === 'voice' ? '🔊' : type === 'forum' ? '◻' : '#';
|
|
26
|
-
return h('div', {
|
|
27
|
-
class: 'cm-channel-item' + (active ? ' active' : '') + (voiceActive ? ' voice-active' : ''),
|
|
28
|
-
'data-id': id,
|
|
29
|
-
onclick: onClick,
|
|
30
|
-
oncontextmenu: (e) => { e.preventDefault(); onContext && onContext(id, e.clientX, e.clientY); }
|
|
31
|
-
},
|
|
32
|
-
h('span', { class: 'cm-ch-icon' }, icon),
|
|
33
|
-
h('span', { class: 'cm-ch-name' }, name)
|
|
34
|
-
);
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
export function ChannelCategory({ id, name, channels = [], collapsed, activeId, onToggle, onAddChannel, onChannelClick, onChannelContext } = {}) {
|
|
38
|
-
return h('div', { class: 'cm-channel-category' },
|
|
39
|
-
h('div', {
|
|
40
|
-
class: 'cm-category-header' + (collapsed ? ' collapsed' : ''),
|
|
41
|
-
onclick: () => onToggle && onToggle(id)
|
|
42
|
-
},
|
|
43
|
-
h('svg', { class: 'cm-cat-arrow', viewBox: '0 0 24 24' }, h('path', { d: 'M7 10l5 5 5-5z' })),
|
|
44
|
-
h('span', { class: 'cm-cat-name' }, name),
|
|
45
|
-
onAddChannel ? h('button', { class: 'cm-cat-add', onclick: (e) => { e.stopPropagation(); onAddChannel(id); }, title: 'Add channel' }, '+') : null
|
|
46
|
-
),
|
|
47
|
-
collapsed ? null : h('div', { class: 'cm-cat-channels' },
|
|
48
|
-
...channels.map(c => ChannelItem({
|
|
49
|
-
...c,
|
|
50
|
-
active: c.id === activeId,
|
|
51
|
-
onClick: () => onChannelClick && onChannelClick(c),
|
|
52
|
-
onContext: onChannelContext
|
|
53
|
-
}))
|
|
54
|
-
)
|
|
55
|
-
);
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
export function VoiceUser({ identity, speaking, color } = {}) {
|
|
59
|
-
const initial = (identity || '?').slice(0, 1).toUpperCase();
|
|
60
|
-
return h('div', { class: 'cm-voice-user' + (speaking ? ' speaking' : '') },
|
|
61
|
-
h('div', { class: 'cm-voice-user-avatar', style: color ? `background:${color}` : '' }, initial),
|
|
62
|
-
h('span', { class: 'cm-voice-user-name' }, identity)
|
|
63
|
-
);
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
export function UserPanel({ name, tag, color, muted, deafened, onMute, onDeafen, onSettings } = {}) {
|
|
67
|
-
const initial = (name || '?').slice(0, 1).toUpperCase();
|
|
68
|
-
return h('div', { class: 'cm-user-panel' },
|
|
69
|
-
h('div', { class: 'cm-user-avatar', style: color ? `background:${color}` : '' },
|
|
70
|
-
h('span', { class: 'cm-user-status-dot' }),
|
|
71
|
-
initial
|
|
72
|
-
),
|
|
73
|
-
h('div', { class: 'cm-user-info' },
|
|
74
|
-
h('div', { class: 'cm-user-name' }, name || 'You'),
|
|
75
|
-
tag ? h('div', { class: 'cm-user-tag' }, tag) : null
|
|
76
|
-
),
|
|
77
|
-
h('div', { class: 'cm-user-controls' },
|
|
78
|
-
h('button', { class: 'cm-user-btn' + (muted ? ' muted' : ''), onclick: onMute, title: muted ? 'Unmute' : 'Mute' }, muted ? '🔇' : '🎤'),
|
|
79
|
-
h('button', { class: 'cm-user-btn' + (deafened ? ' deafened' : ''), onclick: onDeafen, title: deafened ? 'Undeafen' : 'Deafen' }, deafened ? '🔕' : '🎧'),
|
|
80
|
-
h('button', { class: 'cm-user-btn', onclick: onSettings, title: 'Settings' }, '⚙')
|
|
81
|
-
)
|
|
82
|
-
);
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
export function ChannelSidebar({ serverName, channels = [], categories = [], activeId, collapsedCats, onChannelClick, onCategoryToggle, onAddChannel, onChannelContext, userPanelProps } = {}) {
|
|
86
|
-
const collapsed = collapsedCats || new Set();
|
|
87
|
-
const uncategorized = channels.filter(c => !c.categoryId || !categories.find(cat => cat.id === c.categoryId));
|
|
88
|
-
const sorted = [...categories].sort((a, b) => (a.position || 0) - (b.position || 0));
|
|
89
|
-
return h('div', { class: 'cm-channel-sidebar' },
|
|
90
|
-
h('div', { class: 'cm-server-header' },
|
|
91
|
-
h('span', { class: 'cm-server-header-name' }, serverName || 'Server'),
|
|
92
|
-
),
|
|
93
|
-
h('div', { class: 'cm-channel-list' },
|
|
94
|
-
...sorted.map(cat => ChannelCategory({
|
|
95
|
-
id: cat.id,
|
|
96
|
-
name: cat.name,
|
|
97
|
-
channels: channels.filter(c => c.categoryId === cat.id).sort((a, b) => (a.position || 0) - (b.position || 0)),
|
|
98
|
-
collapsed: collapsed.has && collapsed.has(cat.id),
|
|
99
|
-
activeId,
|
|
100
|
-
onToggle: onCategoryToggle,
|
|
101
|
-
onAddChannel,
|
|
102
|
-
onChannelClick,
|
|
103
|
-
onChannelContext
|
|
104
|
-
})),
|
|
105
|
-
uncategorized.length ? ChannelCategory({
|
|
106
|
-
id: 'uncategorized',
|
|
107
|
-
name: 'CHANNELS',
|
|
108
|
-
channels: uncategorized,
|
|
109
|
-
activeId,
|
|
110
|
-
onChannelClick,
|
|
111
|
-
onChannelContext
|
|
112
|
-
}) : null
|
|
113
|
-
),
|
|
114
|
-
userPanelProps ? UserPanel(userPanelProps) : null
|
|
115
|
-
);
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
export function MemberItem({ identity, name, color, status = 'online' } = {}) {
|
|
119
|
-
const initial = (name || identity || '?').slice(0, 1).toUpperCase();
|
|
120
|
-
return h('div', { class: 'cm-member-item' },
|
|
121
|
-
h('div', { class: 'cm-member-avatar', style: color ? `background:${color}` : '' },
|
|
122
|
-
h('span', { class: 'cm-member-status' + (status === 'online' ? ' online' : '') }),
|
|
123
|
-
initial
|
|
124
|
-
),
|
|
125
|
-
h('span', { class: 'cm-member-name' }, name || identity)
|
|
126
|
-
);
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
export function MemberList({ categories = [], open } = {}) {
|
|
130
|
-
return h('div', { class: 'cm-member-list' + (open ? ' open' : '') },
|
|
131
|
-
...categories.flatMap(cat => [
|
|
132
|
-
h('div', { class: 'cm-member-category', key: cat.label }, `${cat.label} — ${cat.members.length}`),
|
|
133
|
-
...cat.members.map((m, i) => MemberItem({ ...m, key: m.identity || i }))
|
|
134
|
-
])
|
|
135
|
-
);
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
export function ChatHeader({ icon = '#', name, topic, toolbar = [] } = {}) {
|
|
139
|
-
return h('div', { class: 'cm-chat-header' },
|
|
140
|
-
h('span', { class: 'cm-chat-header-icon' }, icon),
|
|
141
|
-
h('span', { class: 'cm-chat-header-name' }, name),
|
|
142
|
-
topic ? h('span', { class: 'cm-chat-header-topic' }, topic) : null,
|
|
143
|
-
h('div', { class: 'cm-chat-header-toolbar' }, ...toolbar)
|
|
144
|
-
);
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
export function VoiceStrip({ channelName, status, muted, deafened, onMute, onDeafen, onLeave, open } = {}) {
|
|
148
|
-
return h('div', { class: 'cm-voice-strip' + (open ? ' open' : '') },
|
|
149
|
-
h('div', { class: 'cm-vs-label' },
|
|
150
|
-
h('span', { class: 'cm-vs-channel' }, '🔊 ' + (channelName || 'voice')),
|
|
151
|
-
h('span', { class: 'cm-vs-status' }, status || 'connected')
|
|
152
|
-
),
|
|
153
|
-
h('button', { class: 'cm-vs-btn', onclick: onMute, title: 'Mute' }, muted ? '🔇' : '🎤'),
|
|
154
|
-
h('button', { class: 'cm-vs-btn', onclick: onDeafen, title: 'Deafen' }, deafened ? '🔕' : '🎧'),
|
|
155
|
-
h('button', { class: 'cm-vs-btn danger', onclick: onLeave, title: 'Leave' }, '✕')
|
|
156
|
-
);
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
export function CommunityShell({ serverRailProps, sidebarProps, children, memberListProps, voiceStripProps } = {}) {
|
|
160
|
-
return h('div', { class: 'cm-shell' },
|
|
161
|
-
serverRailProps ? ServerRail(serverRailProps) : null,
|
|
162
|
-
sidebarProps ? ChannelSidebar(sidebarProps) : null,
|
|
163
|
-
h('div', { class: 'cm-main' }, ...(Array.isArray(children) ? children : [children])),
|
|
164
|
-
memberListProps ? MemberList(memberListProps) : null,
|
|
165
|
-
voiceStripProps ? VoiceStrip(voiceStripProps) : null
|
|
166
|
-
);
|
|
167
|
-
}
|
|
2
|
+
//
|
|
3
|
+
// This module is a barrel: every component lives in a single-responsibility
|
|
4
|
+
// submodule under ./community/, and the public export surface here is unchanged
|
|
5
|
+
// — no consumer import needs to move.
|
|
6
|
+
|
|
7
|
+
import { ServerIcon, ServerRail, ChannelItem, ChannelCategory, ChannelSidebar } from './community/navigation.js';
|
|
8
|
+
import { VoiceUser, UserPanel, MemberItem, MemberList, VoiceStrip } from './community/presence.js';
|
|
9
|
+
import { ChatHeader, MobileHeader, ReplyBar, Banner } from './community/chrome.js';
|
|
10
|
+
import { ThreadPanel, ForumView, PageView } from './community/views.js';
|
|
11
|
+
import { CommunityShell } from './community/shell.js';
|
|
12
|
+
|
|
13
|
+
export {
|
|
14
|
+
ServerIcon, ServerRail, ChannelItem, ChannelCategory, ChannelSidebar,
|
|
15
|
+
VoiceUser, UserPanel, MemberItem, MemberList, VoiceStrip,
|
|
16
|
+
ChatHeader, MobileHeader, ReplyBar, Banner,
|
|
17
|
+
ThreadPanel, ForumView, PageView,
|
|
18
|
+
CommunityShell,
|
|
19
|
+
};
|