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.
- package/README.md +253 -253
- package/app-shell.css +931 -594
- package/colors_and_type.css +226 -226
- package/community.css +817 -1222
- package/dist/247420.css +2202 -2084
- package/dist/247420.js +13 -13
- package/package.json +80 -80
- package/src/bootstrap.js +25 -25
- package/src/components/chat.js +199 -199
- package/src/components/community.js +190 -208
- package/src/components/content.js +269 -269
- package/src/components/editor-primitives.js +100 -0
- package/src/components/files-modals.js +107 -107
- package/src/components/files.js +118 -118
- package/src/components/freddie/helpers.js +50 -50
- package/src/components/freddie.js +33 -33
- package/src/components/shell.js +117 -117
- package/src/components/theme-toggle.js +70 -70
- package/src/components.js +59 -57
- package/src/debug.js +30 -30
- package/src/deck-stage.js +21 -21
- package/src/highlight.js +65 -32
- package/src/index.js +86 -86
- package/src/kits/os/about-app.js +52 -52
- package/src/kits/os/app-panes.css +152 -152
- package/src/kits/os/browser-app.js +58 -58
- package/src/kits/os/files-app.js +44 -44
- package/src/kits/os/freddie/helpers.js +59 -59
- package/src/kits/os/freddie/pages-chat.js +143 -143
- package/src/kits/os/freddie/pages-core.js +101 -101
- package/src/kits/os/freddie/pages-os.js +51 -51
- package/src/kits/os/freddie/pages-tools.js +183 -183
- package/src/kits/os/freddie/routes.js +24 -24
- package/src/kits/os/freddie-dashboard.css +51 -51
- package/src/kits/os/freddie-dashboard.js +101 -101
- package/src/kits/os/icons.js +17 -17
- package/src/kits/os/index.js +17 -17
- package/src/kits/os/launcher.css +61 -61
- package/src/kits/os/launcher.js +79 -79
- package/src/kits/os/monitor-app.js +34 -34
- package/src/kits/os/shell.js +214 -214
- package/src/kits/os/terminal-app.js +45 -45
- package/src/kits/os/theme.css +450 -450
- package/src/kits/os/validate.css +19 -19
- package/src/kits/os/validator-app.js +55 -55
- package/src/kits/os/wm.css +115 -115
- package/src/kits/os/wm.js +111 -111
- package/src/markdown.js +39 -39
- package/src/motion.js +35 -35
- package/src/page-html.js +196 -196
- package/src/styles.js +25 -25
- package/src/theme.js +99 -99
- package/src/web-components/ds-chat.js +116 -116
- package/dist/.nojekyll +0 -0
- package/dist/app-shell.css +0 -594
- package/dist/colors_and_type.css +0 -197
- package/dist/favicon.svg +0 -1
- package/dist/index.html +0 -308
- package/dist/preview/buttons.html +0 -28
- package/dist/preview/colors-core.html +0 -45
- package/dist/preview/colors-lore.html +0 -28
- package/dist/preview/colors-semantic.html +0 -34
- package/dist/preview/dateline.html +0 -19
- package/dist/preview/dropzone.html +0 -30
- package/dist/preview/file-grid.html +0 -19
- package/dist/preview/file-row.html +0 -20
- package/dist/preview/file-toolbar.html +0 -40
- package/dist/preview/file-viewer.html +0 -31
- package/dist/preview/header.html +0 -24
- package/dist/preview/icons-unicode.html +0 -26
- package/dist/preview/index-row.html +0 -25
- package/dist/preview/inputs.html +0 -22
- package/dist/preview/manifesto.html +0 -52
- package/dist/preview/motion-default.js +0 -106
- package/dist/preview/rules.html +0 -16
- package/dist/preview/spacing.html +0 -18
- package/dist/preview/stamps-lore.html +0 -20
- package/dist/preview/stamps.html +0 -14
- package/dist/preview/theme-ink.html +0 -15
- package/dist/preview/type-display.html +0 -16
- package/dist/preview/type-mono.html +0 -15
- package/dist/preview/type-prose.html +0 -11
- package/dist/preview/type-scale.html +0 -20
- package/dist/preview/wordmarks.html +0 -28
- package/dist/robots.txt +0 -8
- package/dist/site/content/globals/navigation.yaml +0 -5
- package/dist/site/content/globals/site.yaml +0 -16
- package/dist/site/content/pages/freddie.yaml +0 -88
- package/dist/site/content/pages/home.yaml +0 -190
- package/dist/site/theme.mjs +0 -368
- package/dist/sitemap.xml +0 -31
- package/dist/slides/deck-stage-overlay.js +0 -63
- package/dist/slides/deck-stage-state.js +0 -81
- package/dist/slides/deck-stage-style.js +0 -117
- package/dist/slides/deck-stage.js +0 -159
- package/dist/slides/index.html +0 -276
- package/dist/src/bootstrap.js +0 -25
- package/dist/src/components/chat.js +0 -199
- package/dist/src/components/community.js +0 -167
- package/dist/src/components/content.js +0 -213
- package/dist/src/components/files-modals.js +0 -107
- package/dist/src/components/files.js +0 -118
- package/dist/src/components/freddie/helpers.js +0 -50
- package/dist/src/components/freddie.js +0 -33
- package/dist/src/components/shell.js +0 -117
- package/dist/src/components/theme-toggle.js +0 -70
- package/dist/src/components.js +0 -52
- package/dist/src/debug.js +0 -30
- package/dist/src/deck-stage.js +0 -21
- package/dist/src/highlight.js +0 -32
- package/dist/src/index.js +0 -86
- package/dist/src/kits/os/about-app.js +0 -52
- package/dist/src/kits/os/app-panes.css +0 -152
- package/dist/src/kits/os/browser-app.js +0 -58
- package/dist/src/kits/os/files-app.js +0 -44
- package/dist/src/kits/os/freddie/helpers.js +0 -59
- package/dist/src/kits/os/freddie/pages-chat.js +0 -143
- package/dist/src/kits/os/freddie/pages-core.js +0 -101
- package/dist/src/kits/os/freddie/pages-os.js +0 -51
- package/dist/src/kits/os/freddie/pages-tools.js +0 -183
- package/dist/src/kits/os/freddie/routes.js +0 -24
- package/dist/src/kits/os/freddie-dashboard.css +0 -51
- package/dist/src/kits/os/freddie-dashboard.js +0 -101
- package/dist/src/kits/os/icons.js +0 -17
- package/dist/src/kits/os/index.js +0 -5
- package/dist/src/kits/os/launcher.css +0 -61
- package/dist/src/kits/os/launcher.js +0 -79
- package/dist/src/kits/os/monitor-app.js +0 -34
- package/dist/src/kits/os/shell.js +0 -214
- package/dist/src/kits/os/terminal-app.js +0 -45
- package/dist/src/kits/os/theme.css +0 -412
- package/dist/src/kits/os/validate.css +0 -19
- package/dist/src/kits/os/validator-app.js +0 -55
- package/dist/src/kits/os/wm.css +0 -115
- package/dist/src/kits/os/wm.js +0 -111
- package/dist/src/markdown.js +0 -39
- package/dist/src/motion.js +0 -35
- package/dist/src/page-html.js +0 -196
- package/dist/src/styles.js +0 -25
- package/dist/src/theme.js +0 -99
- package/dist/src/web-components/ds-chat.js +0 -45
- package/dist/ui_kits/aicat/README.md +0 -7
- package/dist/ui_kits/aicat/app.js +0 -156
- package/dist/ui_kits/aicat/index.html +0 -26
- package/dist/ui_kits/aicat/sample-square.png +0 -0
- package/dist/ui_kits/aicat/sample-svg.svg +0 -1
- package/dist/ui_kits/aicat/sample.pdf +0 -32
- package/dist/ui_kits/blog/README.md +0 -3
- package/dist/ui_kits/blog/index.html +0 -90
- package/dist/ui_kits/chat/README.md +0 -5
- package/dist/ui_kits/chat/app.js +0 -110
- package/dist/ui_kits/chat/index.html +0 -26
- package/dist/ui_kits/chat/sample-square.png +0 -0
- package/dist/ui_kits/chat/sample-svg.svg +0 -1
- package/dist/ui_kits/chat/sample.pdf +0 -32
- package/dist/ui_kits/community/app.js +0 -134
- package/dist/ui_kits/community/index.html +0 -24
- package/dist/ui_kits/dashboard/app.js +0 -92
- package/dist/ui_kits/dashboard/index.html +0 -26
- package/dist/ui_kits/docs/README.md +0 -3
- package/dist/ui_kits/docs/index.html +0 -123
- package/dist/ui_kits/error_404/app.js +0 -56
- package/dist/ui_kits/error_404/index.html +0 -26
- package/dist/ui_kits/file_browser/README.md +0 -48
- package/dist/ui_kits/file_browser/app.js +0 -231
- package/dist/ui_kits/file_browser/index.html +0 -33
- package/dist/ui_kits/gallery/app.js +0 -121
- package/dist/ui_kits/gallery/index.html +0 -26
- package/dist/ui_kits/homepage/README.md +0 -7
- package/dist/ui_kits/homepage/app.js +0 -167
- package/dist/ui_kits/homepage/index.html +0 -46
- package/dist/ui_kits/project_page/README.md +0 -3
- package/dist/ui_kits/project_page/app.js +0 -154
- package/dist/ui_kits/project_page/index.html +0 -45
- package/dist/ui_kits/search/app.js +0 -107
- package/dist/ui_kits/search/index.html +0 -26
- package/dist/ui_kits/settings/app.js +0 -133
- package/dist/ui_kits/settings/index.html +0 -26
- package/dist/ui_kits/signin/app.js +0 -115
- package/dist/ui_kits/signin/index.html +0 -26
- package/dist/ui_kits/slide_deck/app.js +0 -174
- package/dist/ui_kits/slide_deck/index.html +0 -26
- package/dist/ui_kits/system_primer/app.js +0 -152
- package/dist/ui_kits/system_primer/index.html +0 -26
- package/dist/ui_kits/terminal/app.js +0 -150
- package/dist/ui_kits/terminal/index.html +0 -26
- package/dist/vendor/webjsx/applyDiff.js +0 -182
- package/dist/vendor/webjsx/attributes.js +0 -154
- package/dist/vendor/webjsx/constants.js +0 -4
- package/dist/vendor/webjsx/createDOMElement.js +0 -52
- package/dist/vendor/webjsx/createElement.js +0 -75
- package/dist/vendor/webjsx/elementTags.js +0 -115
- package/dist/vendor/webjsx/factory.js +0 -6
- package/dist/vendor/webjsx/index.js +0 -6
- package/dist/vendor/webjsx/jsx-dev-runtime.js +0 -2
- package/dist/vendor/webjsx/jsx-runtime.js +0 -30
- package/dist/vendor/webjsx/jsx.js +0 -2
- package/dist/vendor/webjsx/package.json +0 -39
- package/dist/vendor/webjsx/renderSuspension.js +0 -25
- package/dist/vendor/webjsx/types.js +0 -5
- package/dist/vendor/webjsx/utils.js +0 -84
- package/src/components/overlays.js +0 -151
|
@@ -1,167 +0,0 @@
|
|
|
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, 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
|
-
}
|
|
@@ -1,213 +0,0 @@
|
|
|
1
|
-
// Content blocks: Panel, Row, RowLink, Section, Hero, Install, Receipt,
|
|
2
|
-
// Changelog, WorksList, WritingList, Manifesto, Kpi, Table, HomeView,
|
|
3
|
-
// ProjectView, Form. Pure factories.
|
|
4
|
-
|
|
5
|
-
import * as webjsx from '../../vendor/webjsx/index.js';
|
|
6
|
-
import { Btn, Heading, Lede, Dot } from './shell.js';
|
|
7
|
-
const h = webjsx.createElement;
|
|
8
|
-
|
|
9
|
-
export function Panel({ title, count, right, style = '', children, kind }) {
|
|
10
|
-
const cls = 'panel' + (kind ? ' panel-' + kind : '');
|
|
11
|
-
return h('div', { class: cls, style },
|
|
12
|
-
title != null ? h('div', { class: 'panel-head' },
|
|
13
|
-
h('span', {}, title),
|
|
14
|
-
right != null ? right : (count != null ? h('span', {}, String(count)) : null)
|
|
15
|
-
) : null,
|
|
16
|
-
h('div', { class: 'panel-body' }, ...(Array.isArray(children) ? children : [children]))
|
|
17
|
-
);
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export function Row({ code, title, sub, meta, active, onClick, key, style, href, kind, cols, leading, trailing, target }) {
|
|
21
|
-
const isLink = kind === 'link' || (href != null && !onClick);
|
|
22
|
-
const cls = 'row' + (active ? ' active' : '') + (cols ? ' row-grid' : '');
|
|
23
|
-
const props = { key, class: cls, style: cols ? `${style ? style + ';' : ''}grid-template-columns:${cols}` : style };
|
|
24
|
-
if (isLink) { props.href = href || '#'; if (target) props.target = target; }
|
|
25
|
-
else if (onClick) { props.onclick = onClick; }
|
|
26
|
-
return h(isLink ? 'a' : 'div', props,
|
|
27
|
-
leading != null ? leading : (code != null ? h('span', { class: 'code' }, code) : null),
|
|
28
|
-
h('span', { class: 'title' }, title, sub ? h('span', { class: 'sub' }, sub) : null),
|
|
29
|
-
trailing != null ? trailing : (meta != null ? h('span', { class: 'meta' }, meta) : null));
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export function RowLink({ code, title, sub, meta, href = '#', key, target }) {
|
|
33
|
-
return Row({ code, title, sub, meta, href, kind: 'link', key, target });
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
export function Section({ title, eyebrow, children }) {
|
|
37
|
-
return h('section', { class: 'ds-section' },
|
|
38
|
-
eyebrow ? h('span', { class: 'eyebrow' }, eyebrow) : null,
|
|
39
|
-
title ? h('h3', {}, title) : null,
|
|
40
|
-
...(Array.isArray(children) ? children : [children])
|
|
41
|
-
);
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
export function Hero({ eyebrow, title, body, accent, badge, badgeCount, actions }) {
|
|
45
|
-
return h('div', { class: 'ds-hero' },
|
|
46
|
-
eyebrow ? h('span', { class: 'eyebrow' }, eyebrow) : null,
|
|
47
|
-
h('h1', { class: 'ds-hero-title' }, title),
|
|
48
|
-
body ? h('p', { class: 'ds-hero-body' },
|
|
49
|
-
body,
|
|
50
|
-
accent ? h('span', { class: 'ds-hero-accent' }, ' ' + accent) : null
|
|
51
|
-
) : null,
|
|
52
|
-
actions ? h('div', { class: 'ds-hero-actions', style: 'display:flex;gap:10px;flex-wrap:wrap;margin-top:8px' }, ...(Array.isArray(actions) ? actions : [actions])) : null,
|
|
53
|
-
badge ? Panel({ title: badge, count: badgeCount, kind: 'inline', children: [] }) : null
|
|
54
|
-
);
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
export function Install({ cmd, copied, onCopy }) {
|
|
58
|
-
return h('div', { class: 'cli' },
|
|
59
|
-
h('span', { class: 'prompt' }, '$'),
|
|
60
|
-
h('span', { class: 'cmd' }, cmd),
|
|
61
|
-
h('span', { class: 'copy', onclick: () => onCopy && onCopy(cmd) }, copied ? 'copied' : 'copy')
|
|
62
|
-
);
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
export function Receipt({ rows = [] }) {
|
|
66
|
-
return h('table', { class: 'kv' },
|
|
67
|
-
h('tbody', {}, ...rows.map(([k, v], i) =>
|
|
68
|
-
h('tr', { key: i }, h('td', {}, k), h('td', {}, v))
|
|
69
|
-
))
|
|
70
|
-
);
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
export function Changelog({ entries = [] }) {
|
|
74
|
-
return Panel({
|
|
75
|
-
kind: 'wide',
|
|
76
|
-
children: entries.map((e, i) =>
|
|
77
|
-
h('div', { key: i, class: 'row ds-changelog-row' },
|
|
78
|
-
h('span', { class: 'code' }, e.date),
|
|
79
|
-
h('span', { class: 'ds-changelog-ver' }, e.ver),
|
|
80
|
-
h('span', { class: 'title' }, e.msg)
|
|
81
|
-
)
|
|
82
|
-
)
|
|
83
|
-
});
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
export function WorksList({ works = [], openedIndex = -1, onToggle }) {
|
|
87
|
-
return Panel({
|
|
88
|
-
children: works.map((w, i) => {
|
|
89
|
-
const isOpen = openedIndex === i;
|
|
90
|
-
return h('div', { key: i },
|
|
91
|
-
Row({
|
|
92
|
-
code: w.code,
|
|
93
|
-
title: w.title, sub: w.sub,
|
|
94
|
-
meta: w.meta + ' ' + (isOpen ? '−' : '+'),
|
|
95
|
-
active: isOpen,
|
|
96
|
-
onClick: () => onToggle && onToggle(isOpen ? -1 : i)
|
|
97
|
-
}),
|
|
98
|
-
isOpen ? h('div', { class: 'work-detail', 'data-work-index': String(i) },
|
|
99
|
-
h('div', { class: 'ds-prose' },
|
|
100
|
-
h('p', { class: 'ds-work-body' }, w.body)
|
|
101
|
-
),
|
|
102
|
-
h('div', { class: 'ds-work-actions' },
|
|
103
|
-
Btn({ primary: true, href: w.href || '#', children: 'open ↗' }),
|
|
104
|
-
Btn({ href: w.source || '#', children: 'source' })
|
|
105
|
-
)
|
|
106
|
-
) : null
|
|
107
|
-
);
|
|
108
|
-
})
|
|
109
|
-
});
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
export function WritingList({ posts = [] }) {
|
|
113
|
-
return Panel({
|
|
114
|
-
children: posts.map((p, i) =>
|
|
115
|
-
RowLink({ key: i, code: p.date, title: p.title, meta: p.tag, href: p.href || '#' })
|
|
116
|
-
)
|
|
117
|
-
});
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
export function Manifesto({ paragraphs = [], maxWidth }) {
|
|
121
|
-
return h('div', {
|
|
122
|
-
class: 'ds-prose ds-manifesto',
|
|
123
|
-
'data-max-width': maxWidth ? String(maxWidth) : null
|
|
124
|
-
},
|
|
125
|
-
...paragraphs.map((p, i) => h('p', {
|
|
126
|
-
key: i,
|
|
127
|
-
class: 'ds-manifesto-para' + (p.dim ? ' dim' : '')
|
|
128
|
-
}, p.text || p))
|
|
129
|
-
);
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
export function Kpi({ items = [] }) {
|
|
133
|
-
return h('div', { class: 'kpi' }, ...items.map(([n, l], i) =>
|
|
134
|
-
h('div', { key: i, class: 'kpi-card' },
|
|
135
|
-
h('div', { class: 'num' }, String(n)),
|
|
136
|
-
h('div', { class: 'lbl' }, l))));
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
export function Table({ headers = [], rows = [], onRowClick, emptyText = 'nothing here yet' }) {
|
|
140
|
-
if (!rows || rows.length === 0) return h('div', { class: 'empty' }, emptyText);
|
|
141
|
-
return h('table', {},
|
|
142
|
-
h('thead', {}, h('tr', {}, ...headers.map((hd, i) => h('th', { key: i }, hd)))),
|
|
143
|
-
h('tbody', {}, ...rows.map((row, i) => h('tr', {
|
|
144
|
-
key: i,
|
|
145
|
-
class: onRowClick ? 'clickable' : '',
|
|
146
|
-
onclick: onRowClick ? () => onRowClick(i) : null
|
|
147
|
-
}, ...row.map((c, j) => h('td', { key: j }, c == null ? '' : (typeof c === 'object' ? c : String(c))))))));
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
export function HomeView({ state = {}, onNav, onToggleWork, works = [], posts = [], manifesto = [], currentlyShipping } = {}) {
|
|
151
|
-
return [
|
|
152
|
-
Hero({
|
|
153
|
-
eyebrow: 'an entrypoint',
|
|
154
|
-
title: 'Small, weird, useful tools — built in public.',
|
|
155
|
-
body: '247420 is a creative collective of eight, scattered across three timezones. We have been shipping open-source tools for the web since 2018.',
|
|
156
|
-
accent: 'Some become the future. Most don\'t. That\'s the deal.'
|
|
157
|
-
}),
|
|
158
|
-
currentlyShipping ? Section({
|
|
159
|
-
eyebrow: 'currently shipping',
|
|
160
|
-
children: Panel({
|
|
161
|
-
kind: 'wide',
|
|
162
|
-
children: currentlyShipping.map((row, i) =>
|
|
163
|
-
Row({
|
|
164
|
-
key: i,
|
|
165
|
-
code: Dot({ tone: row.live ? 'live' : 'idle' }),
|
|
166
|
-
title: row.title, sub: row.sub, meta: row.meta
|
|
167
|
-
})
|
|
168
|
-
)
|
|
169
|
-
})
|
|
170
|
-
}) : null,
|
|
171
|
-
works.length ? Section({
|
|
172
|
-
eyebrow: 'works', title: 'Everything else.',
|
|
173
|
-
children: WorksList({ works, openedIndex: state.opened ?? -1, onToggle: onToggleWork })
|
|
174
|
-
}) : null,
|
|
175
|
-
posts.length ? Section({
|
|
176
|
-
eyebrow: 'writing', title: 'When we have something to say.',
|
|
177
|
-
children: WritingList({ posts })
|
|
178
|
-
}) : null,
|
|
179
|
-
manifesto.length ? Section({
|
|
180
|
-
eyebrow: 'who\'s here', title: 'Eight people, three timezones, one ongoing conversation.',
|
|
181
|
-
children: Manifesto({ paragraphs: manifesto })
|
|
182
|
-
}) : null
|
|
183
|
-
].filter(Boolean);
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
export function ProjectView({ project = {}, copied, onCopy } = {}) {
|
|
187
|
-
return [
|
|
188
|
-
h('div', { class: 'ds-prose' },
|
|
189
|
-
Heading({ level: 1, children: project.name }),
|
|
190
|
-
Lede({ children: project.tagline })
|
|
191
|
-
),
|
|
192
|
-
project.install ? [
|
|
193
|
-
Heading({ level: 3, children: 'install' }),
|
|
194
|
-
Install({ cmd: project.install, copied, onCopy }),
|
|
195
|
-
] : null,
|
|
196
|
-
project.receipt ? [
|
|
197
|
-
Heading({ level: 3, children: 'by the numbers' }),
|
|
198
|
-
Receipt({ rows: project.receipt }),
|
|
199
|
-
] : null,
|
|
200
|
-
project.changelog ? [
|
|
201
|
-
Heading({ level: 3, children: 'recent releases' }),
|
|
202
|
-
Changelog({ entries: project.changelog })
|
|
203
|
-
] : null
|
|
204
|
-
].filter(Boolean).flat();
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
export function Form({ fields = [], submit = 'submit', onSubmit }) {
|
|
208
|
-
return h('form', { class: 'row-form', onsubmit: (ev) => { ev.preventDefault(); onSubmit && onSubmit(ev); } },
|
|
209
|
-
...fields.map((f, i) => f.kind === 'textarea'
|
|
210
|
-
? h('textarea', { key: i, name: f.name, placeholder: f.placeholder || '', rows: f.rows || 4 })
|
|
211
|
-
: h('input', { key: i, name: f.name, type: f.type || 'text', placeholder: f.placeholder || '', value: f.value || '', required: f.required ? 'true' : null })),
|
|
212
|
-
h('button', { type: 'submit', class: 'btn-primary' }, submit));
|
|
213
|
-
}
|
|
@@ -1,107 +0,0 @@
|
|
|
1
|
-
// File modals — matches upstream signatures + class names.
|
|
2
|
-
|
|
3
|
-
import * as webjsx from '../../vendor/webjsx/index.js';
|
|
4
|
-
import { Btn } from './shell.js';
|
|
5
|
-
import { fileGlyph, fmtFileSize } from './files.js';
|
|
6
|
-
const h = webjsx.createElement;
|
|
7
|
-
|
|
8
|
-
function Backdrop({ onClose, children, kind = '' } = {}) {
|
|
9
|
-
return h('div', {
|
|
10
|
-
class: 'ds-modal-backdrop',
|
|
11
|
-
onclick: (e) => { if (e.target === e.currentTarget && onClose) onClose(); }
|
|
12
|
-
},
|
|
13
|
-
h('div', { class: 'ds-modal' + (kind ? ' ds-modal-' + kind : '') }, ...(Array.isArray(children) ? children : [children]))
|
|
14
|
-
);
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export function ConfirmDialog({ title = 'confirm', message, confirmLabel = 'confirm', cancelLabel = 'cancel', destructive, onConfirm, onCancel } = {}) {
|
|
18
|
-
return Backdrop({
|
|
19
|
-
onClose: onCancel,
|
|
20
|
-
kind: 'small',
|
|
21
|
-
children: [
|
|
22
|
-
h('div', { class: 'ds-modal-head' }, title),
|
|
23
|
-
h('div', { class: 'ds-modal-body' }, message || ''),
|
|
24
|
-
h('div', { class: 'ds-modal-actions' },
|
|
25
|
-
Btn({ onClick: onCancel, children: cancelLabel }),
|
|
26
|
-
h('button', {
|
|
27
|
-
class: destructive ? 'btn-primary danger' : 'btn-primary',
|
|
28
|
-
onclick: onConfirm
|
|
29
|
-
}, confirmLabel)
|
|
30
|
-
)
|
|
31
|
-
]
|
|
32
|
-
});
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
export function PromptDialog({ title = 'name', value = '', placeholder = '', confirmLabel = 'ok', cancelLabel = 'cancel', onConfirm, onCancel, onInput } = {}) {
|
|
36
|
-
return Backdrop({
|
|
37
|
-
onClose: onCancel,
|
|
38
|
-
kind: 'small',
|
|
39
|
-
children: [
|
|
40
|
-
h('div', { class: 'ds-modal-head' }, title),
|
|
41
|
-
h('div', { class: 'ds-modal-body' },
|
|
42
|
-
h('input', {
|
|
43
|
-
class: 'input ds-modal-input',
|
|
44
|
-
type: 'text',
|
|
45
|
-
value,
|
|
46
|
-
placeholder,
|
|
47
|
-
autofocus: true,
|
|
48
|
-
oninput: (e) => onInput && onInput(e.target.value),
|
|
49
|
-
onkeydown: (e) => {
|
|
50
|
-
if (e.key === 'Enter') { e.preventDefault(); onConfirm && onConfirm(e.target.value); }
|
|
51
|
-
if (e.key === 'Escape') { e.preventDefault(); onCancel && onCancel(); }
|
|
52
|
-
}
|
|
53
|
-
})
|
|
54
|
-
),
|
|
55
|
-
h('div', { class: 'ds-modal-actions' },
|
|
56
|
-
Btn({ onClick: onCancel, children: cancelLabel }),
|
|
57
|
-
h('button', { class: 'btn-primary', onclick: () => onConfirm && onConfirm(value) }, confirmLabel)
|
|
58
|
-
)
|
|
59
|
-
]
|
|
60
|
-
});
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
export function FilePreviewMedia({ src, type = 'other', name } = {}) {
|
|
64
|
-
if (type === 'image') return h('img', { class: 'ds-preview-media', src, alt: name || '' });
|
|
65
|
-
if (type === 'video') return h('video', { class: 'ds-preview-media', src, controls: true });
|
|
66
|
-
if (type === 'audio') return h('audio', { class: 'ds-preview-audio', src, controls: true });
|
|
67
|
-
return h('div', { class: 'ds-preview-fallback' },
|
|
68
|
-
h('span', { class: 'ds-preview-glyph' }, fileGlyph(type)),
|
|
69
|
-
h('span', {}, 'no inline preview for ' + (type || 'this file'))
|
|
70
|
-
);
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
export function FilePreviewCode({ content = '', lang } = {}) {
|
|
74
|
-
return h('pre', { class: 'ds-preview-code' + (lang ? ' lang-' + lang : '') },
|
|
75
|
-
h('code', { class: lang ? 'language-' + lang : '' }, content)
|
|
76
|
-
);
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
export function FilePreviewText({ content = '', truncated } = {}) {
|
|
80
|
-
return h('pre', { class: 'ds-preview-text' },
|
|
81
|
-
h('code', {}, content),
|
|
82
|
-
truncated ? h('div', { class: 'ds-preview-truncated' }, '… (truncated)') : null
|
|
83
|
-
);
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
export function FileViewer({ file, body, onClose, onAction } = {}) {
|
|
87
|
-
if (!file) return null;
|
|
88
|
-
const meta = [file.type, file.size != null ? fmtFileSize(file.size) : null, file.modified || null]
|
|
89
|
-
.filter(Boolean).join(' · ');
|
|
90
|
-
return Backdrop({
|
|
91
|
-
onClose,
|
|
92
|
-
kind: 'preview',
|
|
93
|
-
children: [
|
|
94
|
-
h('div', { class: 'ds-modal-head ds-preview-head', 'data-file-type': file.type || 'other' },
|
|
95
|
-
h('span', { class: 'ds-preview-name' }, file.name || ''),
|
|
96
|
-
h('span', { class: 'ds-preview-meta' }, meta),
|
|
97
|
-
h('span', { class: 'ds-preview-actions' },
|
|
98
|
-
onAction ? h('button', { class: 'ds-file-act', title: 'download', onclick: () => onAction('download') }, '↓') : null,
|
|
99
|
-
h('button', { class: 'ds-file-act', title: 'close', onclick: onClose }, '✕')
|
|
100
|
-
)
|
|
101
|
-
),
|
|
102
|
-
h('div', { class: 'ds-preview-body', 'data-file-type': file.type || 'other' },
|
|
103
|
-
...(Array.isArray(body) ? body : [body])
|
|
104
|
-
)
|
|
105
|
-
]
|
|
106
|
-
});
|
|
107
|
-
}
|
|
@@ -1,118 +0,0 @@
|
|
|
1
|
-
// File primitives — matches upstream signatures.
|
|
2
|
-
|
|
3
|
-
import * as webjsx from '../../vendor/webjsx/index.js';
|
|
4
|
-
import { Btn } from './shell.js';
|
|
5
|
-
const h = webjsx.createElement;
|
|
6
|
-
|
|
7
|
-
const FILE_TYPES = ['dir', 'image', 'video', 'audio', 'code', 'text', 'archive', 'document', 'symlink', 'other'];
|
|
8
|
-
const TYPE_GLYPH = {
|
|
9
|
-
dir: '▣', image: '◰', video: '▰', audio: '◎', code: '⌘',
|
|
10
|
-
text: '§', archive: '◐', document: '▢', symlink: '↗', other: '◌'
|
|
11
|
-
};
|
|
12
|
-
|
|
13
|
-
export function fileGlyph(type) {
|
|
14
|
-
return TYPE_GLYPH[type] || TYPE_GLYPH.other;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export function fmtFileSize(bytes) {
|
|
18
|
-
if (bytes == null || bytes === 0) return '—';
|
|
19
|
-
const u = ['B', 'KB', 'MB', 'GB', 'TB'];
|
|
20
|
-
let i = 0, n = bytes;
|
|
21
|
-
while (n >= 1024 && i < u.length - 1) { n /= 1024; i++; }
|
|
22
|
-
return n.toFixed(i === 0 ? 0 : 1) + ' ' + u[i];
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export function FileIcon({ type = 'other' } = {}) {
|
|
26
|
-
return h('span', { class: 'ds-file-icon', 'data-file-type': type }, fileGlyph(type));
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export function FileRow({ name, type = 'other', size, modified, code, onOpen, onAction, active, key } = {}) {
|
|
30
|
-
const meta = [type === 'dir' ? null : fmtFileSize(size), modified || null].filter(Boolean).join(' · ');
|
|
31
|
-
return h('div', {
|
|
32
|
-
key,
|
|
33
|
-
class: 'ds-file-row row' + (active ? ' active' : ''),
|
|
34
|
-
'data-file-type': type,
|
|
35
|
-
onclick: onOpen
|
|
36
|
-
},
|
|
37
|
-
code != null ? h('span', { class: 'code' }, code) : null,
|
|
38
|
-
FileIcon({ type }),
|
|
39
|
-
h('span', { class: 'title' }, name),
|
|
40
|
-
h('span', { class: 'ds-file-meta meta' }, meta || '—'),
|
|
41
|
-
onAction ? h('span', { class: 'ds-file-actions', onclick: (e) => e.stopPropagation() },
|
|
42
|
-
h('button', { class: 'ds-file-act', title: 'download', onclick: () => onAction('download') }, '↓'),
|
|
43
|
-
h('button', { class: 'ds-file-act', title: 'rename', onclick: () => onAction('rename') }, '✎'),
|
|
44
|
-
h('button', { class: 'ds-file-act ds-file-act-warn', title: 'delete', onclick: () => onAction('delete') }, '✕')
|
|
45
|
-
) : null
|
|
46
|
-
);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
export function FileGrid({ files = [], onOpen, onAction, emptyText = 'no files here yet' } = {}) {
|
|
50
|
-
if (!files.length) return EmptyState({ text: emptyText });
|
|
51
|
-
return h('div', { class: 'ds-file-grid' },
|
|
52
|
-
...files.map((f, i) => FileRow({
|
|
53
|
-
key: f.path || f.name + i,
|
|
54
|
-
name: f.name, type: f.type, size: f.size, modified: f.modified, code: f.code, active: f.active,
|
|
55
|
-
onOpen: onOpen ? () => onOpen(f) : null,
|
|
56
|
-
onAction: onAction ? (act) => onAction(act, f) : null
|
|
57
|
-
}))
|
|
58
|
-
);
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
export function FileToolbar({ left = [], right = [] } = {}) {
|
|
62
|
-
return h('div', { class: 'ds-file-toolbar' },
|
|
63
|
-
h('div', { class: 'ds-file-toolbar-left' }, ...left),
|
|
64
|
-
h('div', { class: 'ds-file-toolbar-right' }, ...right)
|
|
65
|
-
);
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
export function DropZone({ children, dragover, onDrop, onDragOver, onDragLeave, label = 'drop files here', onPick } = {}) {
|
|
69
|
-
return h('div', {
|
|
70
|
-
class: 'ds-dropzone' + (dragover ? ' dragover' : ''),
|
|
71
|
-
ondragover: (e) => { e.preventDefault(); onDragOver && onDragOver(e); },
|
|
72
|
-
ondragleave: (e) => { onDragLeave && onDragLeave(e); },
|
|
73
|
-
ondrop: (e) => { e.preventDefault(); onDrop && onDrop(e.dataTransfer.files); }
|
|
74
|
-
},
|
|
75
|
-
h('div', { class: 'ds-dropzone-inner' },
|
|
76
|
-
h('span', { class: 'ds-dropzone-glyph' }, '⇪'),
|
|
77
|
-
h('span', { class: 'ds-dropzone-label' }, label),
|
|
78
|
-
onPick ? Btn({ onClick: onPick, children: 'pick files' }) : null
|
|
79
|
-
),
|
|
80
|
-
...(Array.isArray(children) ? children : children ? [children] : [])
|
|
81
|
-
);
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
export function UploadProgress({ items = [] } = {}) {
|
|
85
|
-
if (!items.length) return null;
|
|
86
|
-
return h('div', { class: 'ds-upload-progress' },
|
|
87
|
-
...items.map((it, i) => h('div', {
|
|
88
|
-
key: it.name + i,
|
|
89
|
-
class: 'ds-upload-item' + (it.done ? ' done' : '') + (it.error ? ' error' : '')
|
|
90
|
-
},
|
|
91
|
-
h('span', { class: 'ds-upload-name' }, it.name),
|
|
92
|
-
h('span', { class: 'ds-upload-bar' },
|
|
93
|
-
h('span', { class: 'ds-upload-fill', 'data-pct': String(Math.max(0, Math.min(100, it.pct || 0))) })
|
|
94
|
-
),
|
|
95
|
-
h('span', { class: 'ds-upload-pct' }, (it.error ? 'err' : (it.done ? 'ok' : (it.pct || 0) + '%')))
|
|
96
|
-
))
|
|
97
|
-
);
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
export function EmptyState({ text = 'nothing here', glyph = '◌' } = {}) {
|
|
101
|
-
return h('div', { class: 'ds-file-empty' },
|
|
102
|
-
h('span', { class: 'ds-file-empty-glyph' }, glyph),
|
|
103
|
-
h('span', { class: 'ds-file-empty-text' }, text)
|
|
104
|
-
);
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
export function BreadcrumbPath({ segments = [], onNav, root = 'root' } = {}) {
|
|
108
|
-
const parts = [h('button', { key: 'root', class: 'ds-crumb-seg', onclick: () => onNav && onNav(0) }, root)];
|
|
109
|
-
segments.forEach((seg, i) => {
|
|
110
|
-
parts.push(h('span', { key: 'sep' + i, class: 'ds-crumb-sep' }, '›'));
|
|
111
|
-
parts.push(h('button', {
|
|
112
|
-
key: 'seg' + i,
|
|
113
|
-
class: 'ds-crumb-seg' + (i === segments.length - 1 ? ' leaf' : ''),
|
|
114
|
-
onclick: () => onNav && onNav(i + 1)
|
|
115
|
-
}, seg));
|
|
116
|
-
});
|
|
117
|
-
return h('div', { class: 'ds-crumb-path' }, ...parts);
|
|
118
|
-
}
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
// Freddie helpers — minimal stubs. Real freddie surfaces ship downstream
|
|
2
|
-
// (gm-cc, foph, hermes-fork). The SDK exposes the registry shape and
|
|
3
|
-
// localStorage helpers so consumer pages can wire to existing patterns.
|
|
4
|
-
|
|
5
|
-
import * as webjsx from '../../../vendor/webjsx/index.js';
|
|
6
|
-
const h = webjsx.createElement;
|
|
7
|
-
|
|
8
|
-
export function renderPageStub({ id, title }) {
|
|
9
|
-
return h('div', { class: 'ds-freddie-stub' },
|
|
10
|
-
h('span', { class: 'eyebrow' }, 'freddie · ' + id),
|
|
11
|
-
h('h2', {}, title || id),
|
|
12
|
-
h('p', { class: 'dim' }, 'this page renderer is a stub. consumers override it on their own freddie router.')
|
|
13
|
-
);
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
const SKILL_LABELS = {
|
|
17
|
-
transcribe: 'transcribe',
|
|
18
|
-
summarize: 'summarize',
|
|
19
|
-
translate: 'translate',
|
|
20
|
-
extract: 'extract',
|
|
21
|
-
classify: 'classify',
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
export function skillLabel(slug) {
|
|
25
|
-
return SKILL_LABELS[slug] || slug;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
const RECENT_KEY = 'ds247420.recent.paths';
|
|
29
|
-
|
|
30
|
-
export function getRecentPaths() {
|
|
31
|
-
if (typeof localStorage === 'undefined') return [];
|
|
32
|
-
try { return JSON.parse(localStorage.getItem(RECENT_KEY) || '[]'); }
|
|
33
|
-
catch { return []; }
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
export function saveRecentPath(path) {
|
|
37
|
-
if (typeof localStorage === 'undefined' || !path) return;
|
|
38
|
-
const list = getRecentPaths().filter(p => p !== path);
|
|
39
|
-
list.unshift(path);
|
|
40
|
-
try { localStorage.setItem(RECENT_KEY, JSON.stringify(list.slice(0, 10))); } catch {}
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
// Helper used by consumers that want to render chat-message arrays with
|
|
44
|
-
// our ChatMessage factory but pre-formatted for freddie's data shape.
|
|
45
|
-
export function renderChatMessages(messages = [], opts = {}) {
|
|
46
|
-
// Import lazily to keep this module light.
|
|
47
|
-
return import('../chat.js').then(({ ChatMessage }) =>
|
|
48
|
-
messages.map((m, i) => ChatMessage({ ...m, key: m.key != null ? m.key : i, ...opts }))
|
|
49
|
-
);
|
|
50
|
-
}
|