anentrypoint-design 0.0.122 → 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 (204) 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 +933 -2255
  5. package/dist/247420.css +2276 -3075
  6. package/dist/247420.js +14 -14
  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 -67
  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
  203. package/src/components/surfaces.js +0 -399
  204. package/src/components/voice.js +0 -132
@@ -1,151 +0,0 @@
1
- // Overlay surface — toast, banner, mobile header, drawer, boot splash,
2
- // context menu, command palette, emoji picker, reply bar.
3
- // All factories: props → vnode. No internal state, no lifecycle (refs OK).
4
-
5
- import * as webjsx from '../../vendor/webjsx/index.js';
6
- const h = webjsx.createElement;
7
-
8
- export function Toast({ message, tone = 'info', visible } = {}) {
9
- if (!visible) return null;
10
- return h('div', { class: `cm-toast tone-${tone}`, role: 'status' },
11
- h('span', { class: 'cm-toast-msg' }, message || '')
12
- );
13
- }
14
-
15
- export function Banner({ tone = 'info', message, action, onAction, visible, dismissable, onDismiss } = {}) {
16
- if (!visible) return null;
17
- return h('div', { class: `cm-banner tone-${tone}`, role: 'alert' },
18
- h('span', { class: 'cm-banner-msg' }, message || ''),
19
- action ? h('button', {
20
- class: 'cm-banner-action',
21
- onclick: onAction,
22
- title: action.label || ''
23
- }, action.icon ? h('span', { class: 'cm-banner-action-icon' }, action.icon) : null, action.label || '') : null,
24
- dismissable ? h('button', { class: 'cm-banner-dismiss', onclick: onDismiss, title: 'Dismiss' }, '✕') : null
25
- );
26
- }
27
-
28
- export function MobileHeader({ title, onMenu, onMembers } = {}) {
29
- return h('div', { class: 'cm-mobile-header' },
30
- h('button', { class: 'cm-mh-btn', onclick: onMenu, title: 'Menu' }, '☰'),
31
- h('span', { class: 'cm-mh-title' }, title || ''),
32
- h('button', { class: 'cm-mh-btn', onclick: onMembers, title: 'Members' }, '👥')
33
- );
34
- }
35
-
36
- export function DrawerOverlay({ open, onClose, children, side = 'left' } = {}) {
37
- if (!open) return null;
38
- return h('div', { class: 'cm-drawer-overlay' },
39
- h('div', { class: 'cm-drawer-backdrop', onclick: onClose }),
40
- h('div', {
41
- class: `cm-drawer-content side-${side}`,
42
- onclick: (e) => e.stopPropagation()
43
- }, ...(Array.isArray(children) ? children : [children]))
44
- );
45
- }
46
-
47
- export function BootOverlay({ progress = 0, phase, error, visible } = {}) {
48
- if (!visible) return null;
49
- const pct = Math.max(0, Math.min(1, progress)) * 100;
50
- return h('div', { class: 'cm-boot-overlay', role: 'dialog' },
51
- h('div', { class: 'cm-boot-spinner' }),
52
- h('div', { class: 'cm-boot-phase' }, phase || 'Loading…'),
53
- h('div', { class: 'cm-boot-progress' },
54
- h('div', { class: 'cm-boot-bar', style: `width:${pct}%` })
55
- ),
56
- error ? h('div', { class: 'cm-boot-error' }, String(error)) : null
57
- );
58
- }
59
-
60
- export function ContextMenu({ items = [], x = 0, y = 0, open, onClose } = {}) {
61
- if (!open) return null;
62
- return h('div', {
63
- class: 'cm-context-menu',
64
- style: `left:${x}px;top:${y}px`,
65
- onclick: (e) => e.stopPropagation()
66
- },
67
- h('div', { class: 'cm-cm-list' },
68
- ...items.map(it => it.separator
69
- ? h('div', { class: 'cm-cm-sep', key: it.id || 'sep' })
70
- : h('button', {
71
- class: 'cm-cm-item' + (it.danger ? ' danger' : ''),
72
- 'data-id': it.id || '',
73
- onclick: (e) => { e.stopPropagation(); it.onClick && it.onClick(e); onClose && onClose(); }
74
- },
75
- it.icon ? h('span', { class: 'cm-cm-icon' }, it.icon) : null,
76
- h('span', { class: 'cm-cm-label' }, it.label || '')
77
- )
78
- )
79
- )
80
- );
81
- }
82
-
83
- export function CommandPalette({ items = [], query = '', onQuery, onSelect, open, onClose } = {}) {
84
- if (!open) return null;
85
- return h('div', { class: 'cm-command-palette-wrap' },
86
- h('div', { class: 'cm-command-palette-backdrop', onclick: onClose }),
87
- h('div', { class: 'cm-command-palette', onclick: (e) => e.stopPropagation() },
88
- h('input', {
89
- class: 'cm-cp-input',
90
- type: 'text',
91
- placeholder: 'Type a command…',
92
- value: query,
93
- oninput: (e) => onQuery && onQuery(e.target.value),
94
- autofocus: true
95
- }),
96
- h('div', { class: 'cm-cp-list' },
97
- ...items.map((it, i) => h('button', {
98
- class: 'cm-cp-item' + (it.selected ? ' selected' : ''),
99
- 'data-id': it.id || '',
100
- onclick: () => onSelect && onSelect(it, i)
101
- },
102
- it.icon ? h('span', { class: 'cm-cp-icon' }, it.icon) : null,
103
- h('span', { class: 'cm-cp-label' }, it.label || ''),
104
- it.hint ? h('span', { class: 'cm-cp-hint' }, it.hint) : null
105
- ))
106
- )
107
- )
108
- );
109
- }
110
-
111
- const DEFAULT_EMOJI_CATEGORIES = [
112
- { id: 'smileys', name: 'Smileys', emojis: ['😀','😃','😄','😁','😆','😅','🤣','😂','🙂','🙃','😉','😊','😇','🥰','😍','🤩','😘','😗','😚','😙','🥲','😋','😛','😜','🤪','😝','🤑','🤗','🤭','🤫'] },
113
- { id: 'hearts', name: 'Hearts', emojis: ['❤️','🧡','💛','💚','💙','💜','🖤','🤍','🤎','💔','❣️','💕','💞','💓','💗','💖','💘','💝','💟','♥️','💌','💋','💍','💎','🌹','🌷','🌻','🌸','🌺','💐'] },
114
- { id: 'animals', name: 'Animals', emojis: ['🐶','🐱','🐭','🐹','🐰','🦊','🐻','🐼','🐨','🐯','🦁','🐮','🐷','🐸','🐵','🐔','🐧','🐦','🐤','🦆','🦅','🦉','🦇','🐺','🐗','🐴','🦄','🐝','🐛','🦋'] },
115
- { id: 'food', name: 'Food', emojis: ['🍏','🍎','🍐','🍊','🍋','🍌','🍉','🍇','🍓','🫐','🍈','🍒','🍑','🥭','🍍','🥥','🥝','🍅','🍆','🥑','🥦','🥬','🥒','🌶️','🌽','🥕','🧄','🧅','🥔','🍞'] },
116
- { id: 'activities', name: 'Activities', emojis: ['⚽','🏀','🏈','⚾','🥎','🎾','🏐','🏉','🎱','🪀','🏓','🏸','🥅','🏒','🏑','🥍','🏏','🪃','⛳','🪁','🏹','🎣','🤿','🥊','🥋','🎽','🛹','🛼','🛷','⛸️'] },
117
- { id: 'symbols', name: 'Symbols', emojis: ['✅','❌','⭕','🚫','♻️','✳️','❇️','⚠️','🔱','⚜️','🔰','✴️','🆚','🆗','🆙','🆒','🆕','🆓','0️⃣','1️⃣','2️⃣','3️⃣','4️⃣','5️⃣','6️⃣','7️⃣','8️⃣','9️⃣','🔟','💯'] }
118
- ];
119
-
120
- export function EmojiPicker({ recent = [], categories, onSelect, onClose, open } = {}) {
121
- if (!open) return null;
122
- const cats = categories && categories.length ? categories : DEFAULT_EMOJI_CATEGORIES;
123
- return h('div', { class: 'cm-emoji-picker', onclick: (e) => e.stopPropagation() },
124
- h('input', { class: 'cm-ep-search', type: 'text', placeholder: 'Search emoji…' }),
125
- h('div', { class: 'cm-ep-grid' },
126
- recent.length ? h('div', { class: 'cm-ep-category' }, 'Recent') : null,
127
- ...recent.map((e, i) => h('button', {
128
- class: 'cm-ep-btn', key: 'r' + i,
129
- onclick: () => onSelect && onSelect(e)
130
- }, e)),
131
- ...cats.flatMap(cat => [
132
- h('div', { class: 'cm-ep-category', key: 'c-' + cat.id }, cat.name),
133
- ...cat.emojis.map((e, i) => h('button', {
134
- class: 'cm-ep-btn', key: cat.id + '-' + i,
135
- onclick: () => onSelect && onSelect(e)
136
- }, e))
137
- ])
138
- )
139
- );
140
- }
141
-
142
- export function ReplyBar({ quotedMessage, quotedAuthor, onCancel } = {}) {
143
- return h('div', { class: 'cm-reply-bar' },
144
- h('div', { class: 'cm-rb-quote' },
145
- h('span', { class: 'cm-rb-label' }, 'Replying to '),
146
- h('span', { class: 'cm-rb-author' }, quotedAuthor || ''),
147
- h('span', { class: 'cm-rb-msg' }, quotedMessage ? ': ' + String(quotedMessage).slice(0, 80) : '')
148
- ),
149
- h('button', { class: 'cm-rb-cancel', onclick: onCancel, title: 'Cancel reply' }, '✕')
150
- );
151
- }
@@ -1,399 +0,0 @@
1
- // Surfaces — thread panel, forum view, page view, auth modal, settings popover, voice settings modal.
2
- // Pure factories: props → vnode. Render nothing when modals are not open.
3
-
4
- import * as webjsx from '../../vendor/webjsx/index.js';
5
- const h = webjsx.createElement;
6
-
7
- function timeAgo(t) {
8
- if (!t) return '';
9
- const d = typeof t === 'number' ? t : Date.parse(t);
10
- if (!Number.isFinite(d)) return String(t);
11
- const s = Math.max(0, Math.floor((Date.now() - d) / 1000));
12
- if (s < 60) return s + 's';
13
- if (s < 3600) return Math.floor(s / 60) + 'm';
14
- if (s < 86400) return Math.floor(s / 3600) + 'h';
15
- return Math.floor(s / 86400) + 'd';
16
- }
17
-
18
- export function ThreadPanel({ threads = [], activeId, onSelect, onCreate, onClose, open, title = 'Threads' } = {}) {
19
- if (open !== true) return null;
20
- return h('aside', { class: 'cm-thread-panel', role: 'complementary' },
21
- h('div', { class: 'cm-tp-header' },
22
- h('span', { class: 'cm-tp-title' }, title),
23
- h('div', { class: 'cm-tp-header-actions' },
24
- onCreate ? h('button', { class: 'cm-tp-new', onclick: onCreate, title: 'New thread' }, '+') : null,
25
- onClose ? h('button', { class: 'cm-tp-close', onclick: onClose, title: 'Close' }, '✕') : null
26
- )
27
- ),
28
- h('div', { class: 'cm-tp-list' },
29
- ...threads.map(t => h('div', {
30
- class: 'cm-tp-item' + (t.id === activeId ? ' active' : '') + (t.unread ? ' unread' : ''),
31
- onclick: () => onSelect && onSelect(t.id),
32
- 'data-id': t.id
33
- },
34
- h('div', { class: 'cm-tp-item-row' },
35
- h('span', { class: 'cm-tp-item-title' }, t.title || '(untitled)'),
36
- h('span', { class: 'cm-tp-item-time' }, timeAgo(t.time))
37
- ),
38
- t.lastMessage ? h('div', { class: 'cm-tp-item-msg' },
39
- t.author ? h('span', { class: 'cm-tp-item-author' }, t.author + ': ') : null,
40
- t.lastMessage
41
- ) : null,
42
- t.unread ? h('span', { class: 'cm-tp-item-dot' }) : null
43
- ))
44
- )
45
- );
46
- }
47
-
48
- export function ForumView({ posts = [], onSelect, onSearch, onNewPost, query, sortBy = 'recent', onSort } = {}) {
49
- const sorts = [
50
- { id: 'recent', label: 'Recent' },
51
- { id: 'popular', label: 'Popular' },
52
- { id: 'unanswered', label: 'Unanswered' }
53
- ];
54
- return h('div', { class: 'cm-forum-view' },
55
- h('div', { class: 'cm-fv-toolbar' },
56
- h('input', {
57
- class: 'cm-fv-search',
58
- type: 'search',
59
- placeholder: 'Search posts…',
60
- value: query || '',
61
- oninput: (e) => onSearch && onSearch(e.target.value)
62
- }),
63
- h('select', {
64
- class: 'cm-fv-sort',
65
- value: sortBy,
66
- onchange: (e) => onSort && onSort(e.target.value)
67
- }, ...sorts.map(s => h('option', { value: s.id, selected: s.id === sortBy ? 'selected' : null }, s.label))),
68
- onNewPost ? h('button', { class: 'cm-fv-new', onclick: onNewPost, title: 'New post' }, '+ New') : null
69
- ),
70
- h('div', { class: 'cm-fv-list' },
71
- ...posts.map(p => h('div', {
72
- class: 'cm-fv-post',
73
- onclick: () => onSelect && onSelect(p.id),
74
- 'data-id': p.id
75
- },
76
- h('div', { class: 'cm-fv-post-head' },
77
- h('span', { class: 'cm-fv-post-title' }, p.title || '(untitled)'),
78
- h('span', { class: 'cm-fv-post-meta' },
79
- p.author ? h('span', { class: 'cm-fv-post-author' }, p.author) : null,
80
- h('span', { class: 'cm-fv-post-time' }, timeAgo(p.time)),
81
- h('span', { class: 'cm-fv-post-replies' }, (p.replyCount || 0) + ' replies')
82
- )
83
- ),
84
- p.snippet ? h('div', { class: 'cm-fv-post-snippet' }, p.snippet) : null,
85
- p.tags && p.tags.length ? h('div', { class: 'cm-fv-post-tags' },
86
- ...p.tags.map(tg => h('span', { class: 'cm-fv-post-tag' }, tg))
87
- ) : null
88
- ))
89
- )
90
- );
91
- }
92
-
93
- export function PageView({ content, html, title, onEdit, isAdmin } = {}) {
94
- const bodyAttrs = { class: 'cm-pv-content' };
95
- if (html != null) {
96
- bodyAttrs.ref = (el) => { if (el) el.innerHTML = String(html); };
97
- }
98
- return h('div', { class: 'cm-page-view' },
99
- h('div', { class: 'cm-pv-head' },
100
- title ? h('h1', { class: 'cm-pv-title' }, title) : null,
101
- isAdmin && onEdit ? h('button', { class: 'cm-pv-edit', onclick: onEdit, title: 'Edit' }, 'Edit') : null
102
- ),
103
- html != null
104
- ? h('div', bodyAttrs)
105
- : h('div', bodyAttrs, content || null)
106
- );
107
- }
108
-
109
- export function AuthModal({ mode = 'extension', error, onConnectExtension, onGenerate, onImport, onModeChange, open, onClose, busy } = {}) {
110
- if (open !== true) return null;
111
- const tabs = [
112
- { id: 'extension', label: 'Extension' },
113
- { id: 'generate', label: 'Generate' },
114
- { id: 'import', label: 'Import' }
115
- ];
116
- let importRef = null;
117
- return h('div', { class: 'cm-modal-backdrop', onclick: onClose },
118
- h('div', {
119
- class: 'cm-auth-modal',
120
- onclick: (e) => e.stopPropagation(),
121
- role: 'dialog'
122
- },
123
- h('div', { class: 'cm-am-head' },
124
- h('span', { class: 'cm-am-title' }, 'Sign in'),
125
- onClose ? h('button', { class: 'cm-am-close', onclick: onClose, title: 'Close' }, '✕') : null
126
- ),
127
- h('div', { class: 'cm-am-tabs', role: 'tablist' },
128
- ...tabs.map(t => h('button', {
129
- class: 'cm-am-tab' + (mode === t.id ? ' active' : ''),
130
- onclick: () => onModeChange && onModeChange(t.id),
131
- 'data-mode': t.id
132
- }, t.label))
133
- ),
134
- mode === 'extension' ? h('div', { class: 'cm-am-pane' },
135
- h('p', { class: 'cm-am-text' }, 'Connect using a NIP-07 browser extension (e.g. nos2x, Alby).'),
136
- h('button', {
137
- class: 'cm-am-btn',
138
- onclick: onConnectExtension,
139
- disabled: busy ? 'disabled' : null
140
- }, busy ? 'Connecting…' : 'Connect extension')
141
- ) : null,
142
- mode === 'generate' ? h('div', { class: 'cm-am-pane' },
143
- h('p', { class: 'cm-am-text' }, 'Generate a fresh keypair. Keep your secret key safe — losing it means losing your identity.'),
144
- h('div', { class: 'cm-am-warn' }, 'Warning: store the generated nsec somewhere safe before continuing.'),
145
- h('button', {
146
- class: 'cm-am-btn',
147
- onclick: onGenerate,
148
- disabled: busy ? 'disabled' : null
149
- }, busy ? 'Generating…' : 'Generate keypair')
150
- ) : null,
151
- mode === 'import' ? h('div', { class: 'cm-am-pane' },
152
- h('p', { class: 'cm-am-text' }, 'Paste your nsec secret key to import an existing identity.'),
153
- h('textarea', {
154
- class: 'cm-am-textarea',
155
- placeholder: 'nsec1…',
156
- rows: 3,
157
- ref: (el) => { importRef = el; }
158
- }),
159
- h('button', {
160
- class: 'cm-am-btn',
161
- onclick: () => onImport && onImport(importRef ? importRef.value.trim() : ''),
162
- disabled: busy ? 'disabled' : null
163
- }, busy ? 'Importing…' : 'Import')
164
- ) : null,
165
- error ? h('div', { class: 'cm-am-error', role: 'alert' }, String(error)) : null
166
- )
167
- );
168
- }
169
-
170
- function renderSettingsItem(item) {
171
- if (!item) return null;
172
- if (item.kind === 'header') return h('div', { class: 'cm-sp-header' }, item.label || '');
173
- if (item.kind === 'text') return h('div', { class: 'cm-sp-text' }, item.label || '');
174
- if (item.kind === 'toggle') {
175
- return h('label', { class: 'cm-sp-row cm-sp-toggle' },
176
- h('span', { class: 'cm-sp-row-label' }, item.label || ''),
177
- h('input', {
178
- type: 'checkbox',
179
- checked: item.value ? 'checked' : null,
180
- onchange: (e) => item.onChange && item.onChange(e.target.checked)
181
- })
182
- );
183
- }
184
- if (item.kind === 'select') {
185
- return h('label', { class: 'cm-sp-row cm-sp-select' },
186
- h('span', { class: 'cm-sp-row-label' }, item.label || ''),
187
- h('select', {
188
- value: item.value,
189
- onchange: (e) => item.onChange && item.onChange(e.target.value)
190
- }, ...(item.options || []).map(o => h('option', {
191
- value: o.value != null ? o.value : o.id,
192
- selected: (o.value != null ? o.value : o.id) === item.value ? 'selected' : null
193
- }, o.label)))
194
- );
195
- }
196
- if (item.kind === 'button') {
197
- return h('div', { class: 'cm-sp-row' },
198
- h('button', {
199
- class: 'cm-sp-btn' + (item.danger ? ' danger' : ''),
200
- onclick: item.onClick
201
- }, item.label || '')
202
- );
203
- }
204
- return null;
205
- }
206
-
207
- export function SettingsPopover({ sections = [], open, onClose, anchorX, anchorY, title = 'Settings' } = {}) {
208
- if (open !== true) return null;
209
- const style = (anchorX != null && anchorY != null)
210
- ? `left:${anchorX}px;top:${anchorY}px`
211
- : null;
212
- return h('div', { class: 'cm-modal-backdrop transparent', onclick: onClose },
213
- h('div', {
214
- class: 'cm-settings-popover',
215
- style,
216
- onclick: (e) => e.stopPropagation(),
217
- role: 'dialog'
218
- },
219
- h('div', { class: 'cm-sp-head' },
220
- h('span', { class: 'cm-sp-title' }, title),
221
- onClose ? h('button', { class: 'cm-sp-close', onclick: onClose, title: 'Close' }, '✕') : null
222
- ),
223
- ...sections.map(sec => h('div', { class: 'cm-sp-section', 'data-id': sec.id },
224
- sec.label ? h('div', { class: 'cm-sp-section-label' }, sec.label) : null,
225
- ...(sec.items || []).map(renderSettingsItem)
226
- ))
227
- )
228
- );
229
- }
230
-
231
- export function VoiceSettingsModal({
232
- mode = 'ptt',
233
- devices = { input: [], output: [] },
234
- inputId,
235
- outputId,
236
- volume = 0.7,
237
- bitrate = 64,
238
- vadThreshold = 0.15,
239
- rnnoise = true,
240
- autoGain = true,
241
- forceTurn = false,
242
- isAdmin,
243
- channelMode,
244
- allowedRoles,
245
- onModeChange,
246
- onInputChange,
247
- onOutputChange,
248
- onVolumeChange,
249
- onBitrateChange,
250
- onVadChange,
251
- onRnnoiseToggle,
252
- onAutoGainToggle,
253
- onForceTurnToggle,
254
- onChannelModeChange,
255
- onSave,
256
- onCancel,
257
- open
258
- } = {}) {
259
- if (open !== true) return null;
260
- const modes = [
261
- { id: 'ptt', label: 'Push-to-talk' },
262
- { id: 'realtime', label: 'Open mic (realtime)' }
263
- ];
264
- const channelModes = [
265
- { id: 'ptt', label: 'PTT only' },
266
- { id: 'realtime', label: 'Realtime' },
267
- { id: 'free', label: 'User choice' }
268
- ];
269
- return h('div', { class: 'cm-modal-backdrop', onclick: onCancel },
270
- h('div', {
271
- class: 'cm-voice-settings-modal',
272
- onclick: (e) => e.stopPropagation(),
273
- role: 'dialog'
274
- },
275
- h('div', { class: 'cm-vsm-head' },
276
- h('span', { class: 'cm-vsm-title' }, 'Voice settings'),
277
- onCancel ? h('button', { class: 'cm-vsm-close', onclick: onCancel, title: 'Close' }, '✕') : null
278
- ),
279
-
280
- h('div', { class: 'cm-vsm-section' },
281
- h('div', { class: 'cm-vsm-section-label' }, 'Audio devices'),
282
- h('label', { class: 'cm-vsm-row' },
283
- h('span', { class: 'cm-vsm-row-label' }, 'Input'),
284
- h('select', {
285
- value: inputId,
286
- onchange: (e) => onInputChange && onInputChange(e.target.value)
287
- }, ...(devices.input || []).map(d => h('option', {
288
- value: d.deviceId || d.id,
289
- selected: (d.deviceId || d.id) === inputId ? 'selected' : null
290
- }, d.label || d.name || 'Unknown')))
291
- ),
292
- h('label', { class: 'cm-vsm-row' },
293
- h('span', { class: 'cm-vsm-row-label' }, 'Output'),
294
- h('select', {
295
- value: outputId,
296
- onchange: (e) => onOutputChange && onOutputChange(e.target.value)
297
- }, ...(devices.output || []).map(d => h('option', {
298
- value: d.deviceId || d.id,
299
- selected: (d.deviceId || d.id) === outputId ? 'selected' : null
300
- }, d.label || d.name || 'Unknown')))
301
- ),
302
- h('label', { class: 'cm-vsm-row' },
303
- h('span', { class: 'cm-vsm-row-label' }, 'Output volume'),
304
- h('input', {
305
- type: 'range', min: '0', max: '1', step: '0.01', value: String(volume),
306
- oninput: (e) => onVolumeChange && onVolumeChange(parseFloat(e.target.value))
307
- }),
308
- h('span', { class: 'cm-vsm-row-val' }, Math.round(volume * 100) + '%')
309
- )
310
- ),
311
-
312
- h('div', { class: 'cm-vsm-section' },
313
- h('div', { class: 'cm-vsm-section-label' }, 'Voice mode'),
314
- h('div', { class: 'cm-vsm-radio' },
315
- ...modes.map(m => h('button', {
316
- class: 'cm-vsm-radio-btn' + (mode === m.id ? ' active' : ''),
317
- onclick: () => onModeChange && onModeChange(m.id),
318
- 'data-mode': m.id
319
- }, m.label))
320
- )
321
- ),
322
-
323
- h('div', { class: 'cm-vsm-section' },
324
- h('div', { class: 'cm-vsm-section-label' }, 'Audio processing'),
325
- h('label', { class: 'cm-vsm-row cm-vsm-toggle' },
326
- h('span', { class: 'cm-vsm-row-label' }, 'Noise suppression (RNNoise)'),
327
- h('input', {
328
- type: 'checkbox',
329
- checked: rnnoise ? 'checked' : null,
330
- onchange: (e) => onRnnoiseToggle && onRnnoiseToggle(e.target.checked)
331
- })
332
- ),
333
- h('label', { class: 'cm-vsm-row cm-vsm-toggle' },
334
- h('span', { class: 'cm-vsm-row-label' }, 'Auto gain control'),
335
- h('input', {
336
- type: 'checkbox',
337
- checked: autoGain ? 'checked' : null,
338
- onchange: (e) => onAutoGainToggle && onAutoGainToggle(e.target.checked)
339
- })
340
- ),
341
- h('label', { class: 'cm-vsm-row' },
342
- h('span', { class: 'cm-vsm-row-label' }, 'VAD threshold'),
343
- h('input', {
344
- type: 'range', min: '0', max: '1', step: '0.01', value: String(vadThreshold),
345
- oninput: (e) => onVadChange && onVadChange(parseFloat(e.target.value))
346
- }),
347
- h('span', { class: 'cm-vsm-row-val' }, vadThreshold.toFixed(2))
348
- )
349
- ),
350
-
351
- h('div', { class: 'cm-vsm-section' },
352
- h('div', { class: 'cm-vsm-section-label' }, 'Quality'),
353
- h('label', { class: 'cm-vsm-row' },
354
- h('span', { class: 'cm-vsm-row-label' }, 'Bitrate'),
355
- h('input', {
356
- type: 'range', min: '8', max: '128', step: '1', value: String(bitrate),
357
- oninput: (e) => onBitrateChange && onBitrateChange(parseInt(e.target.value, 10))
358
- }),
359
- h('span', { class: 'cm-vsm-row-val' }, bitrate + ' kbps')
360
- )
361
- ),
362
-
363
- h('div', { class: 'cm-vsm-section' },
364
- h('div', { class: 'cm-vsm-section-label' }, 'Network'),
365
- h('label', { class: 'cm-vsm-row cm-vsm-toggle' },
366
- h('span', { class: 'cm-vsm-row-label' }, 'Force TURN relay'),
367
- h('input', {
368
- type: 'checkbox',
369
- checked: forceTurn ? 'checked' : null,
370
- onchange: (e) => onForceTurnToggle && onForceTurnToggle(e.target.checked)
371
- })
372
- )
373
- ),
374
-
375
- isAdmin ? h('div', { class: 'cm-vsm-section cm-vsm-admin' },
376
- h('div', { class: 'cm-vsm-section-label' }, 'Channel admin'),
377
- h('div', { class: 'cm-vsm-row' },
378
- h('span', { class: 'cm-vsm-row-label' }, 'Channel mode'),
379
- h('div', { class: 'cm-vsm-radio' },
380
- ...channelModes.map(m => h('button', {
381
- class: 'cm-vsm-radio-btn' + (channelMode === m.id ? ' active' : ''),
382
- onclick: () => onChannelModeChange && onChannelModeChange(m.id),
383
- 'data-mode': m.id
384
- }, m.label))
385
- )
386
- ),
387
- allowedRoles != null ? h('div', { class: 'cm-vsm-row' },
388
- h('span', { class: 'cm-vsm-row-label' }, 'Allowed roles'),
389
- h('span', { class: 'cm-vsm-row-val' }, Array.isArray(allowedRoles) ? allowedRoles.join(', ') || '(any)' : String(allowedRoles))
390
- ) : null
391
- ) : null,
392
-
393
- h('div', { class: 'cm-vsm-actions' },
394
- h('button', { class: 'cm-vsm-cancel', onclick: onCancel }, 'Cancel'),
395
- h('button', { class: 'cm-vsm-save', onclick: onSave }, 'Save')
396
- )
397
- )
398
- );
399
- }