anentrypoint-design 0.0.413 → 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 +63 -13
- 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
- package/src/page-html.js +10 -0
|
@@ -1,213 +1,33 @@
|
|
|
1
1
|
// Content blocks: Panel, Row, RowLink, Section, Hero, Install, Receipt,
|
|
2
2
|
// Changelog, WorksList, WritingList, Manifesto, Kpi, Table, HomeView,
|
|
3
3
|
// ProjectView, Form. Pure factories.
|
|
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
|
-
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
|
-
}
|
|
4
|
+
//
|
|
5
|
+
// This module is a barrel: every component lives in a single-responsibility
|
|
6
|
+
// submodule under ./content/, and the public export surface here is unchanged
|
|
7
|
+
// — no consumer import needs to move.
|
|
8
|
+
|
|
9
|
+
import { avatarInitial, Avatar } from './content/avatar.js';
|
|
10
|
+
import { Row, RowLink } from './content/row.js';
|
|
11
|
+
import { Panel, Card, PanelFromItems, Section, Receipt, Changelog } from './content/panel.js';
|
|
12
|
+
import { Hero, HeroFromPageData, Marquee, Manifesto, PageHeader } from './content/hero.js';
|
|
13
|
+
import { Install, CliBlock } from './content/cli.js';
|
|
14
|
+
import { WorksList, WritingList, EventList } from './content/lists.js';
|
|
15
|
+
import { Kpi, Sparkline, BarChart } from './content/charts.js';
|
|
16
|
+
import { Table, HealthTable, ProcessRegistryTable } from './content/table.js';
|
|
17
|
+
import { SearchInput, TextField, Select, Form } from './content/fields.js';
|
|
18
|
+
import { Spinner, Skeleton, Alert, FilterPills } from './content/feedback.js';
|
|
19
|
+
import { HomeView, ProjectView } from './content/views.js';
|
|
20
|
+
|
|
21
|
+
export {
|
|
22
|
+
avatarInitial, Avatar,
|
|
23
|
+
Row, RowLink,
|
|
24
|
+
Panel, Card, PanelFromItems, Section, Receipt, Changelog,
|
|
25
|
+
Hero, HeroFromPageData, Marquee, Manifesto, PageHeader,
|
|
26
|
+
Install, CliBlock,
|
|
27
|
+
WorksList, WritingList, EventList,
|
|
28
|
+
Kpi, Sparkline, BarChart,
|
|
29
|
+
Table, HealthTable, ProcessRegistryTable,
|
|
30
|
+
SearchInput, TextField, Select, Form,
|
|
31
|
+
Spinner, Skeleton, Alert, FilterPills,
|
|
32
|
+
HomeView, ProjectView,
|
|
33
|
+
};
|
|
@@ -1,107 +1,15 @@
|
|
|
1
1
|
// File modals — matches upstream signatures + class names.
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
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
|
-
}
|
|
2
|
+
//
|
|
3
|
+
// This module is a barrel: every component lives in a single-responsibility
|
|
4
|
+
// submodule under ./files-modals/, and the public export surface here is
|
|
5
|
+
// unchanged — no consumer import needs to move.
|
|
6
|
+
|
|
7
|
+
import { ConfirmDialog, PromptDialog, CountdownDialog } from './files-modals/dialogs.js';
|
|
8
|
+
import { FilePreviewMedia, FilePreviewCode, FilePreviewText } from './files-modals/preview-bodies.js';
|
|
9
|
+
import { FileViewer, FilePreviewPane } from './files-modals/preview-containers.js';
|
|
10
|
+
|
|
11
|
+
export {
|
|
12
|
+
ConfirmDialog, PromptDialog, CountdownDialog,
|
|
13
|
+
FilePreviewMedia, FilePreviewCode, FilePreviewText,
|
|
14
|
+
FileViewer, FilePreviewPane,
|
|
15
|
+
};
|
|
@@ -1,118 +1,17 @@
|
|
|
1
1
|
// File primitives — matches upstream signatures.
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
2
|
+
//
|
|
3
|
+
// This module is a barrel: every component lives in a single-responsibility
|
|
4
|
+
// submodule under ./files/, and the public export surface here is unchanged
|
|
5
|
+
// — no consumer import needs to move.
|
|
6
|
+
|
|
7
|
+
import { fileGlyph, fmtFileSize, FileIcon, sortFiles } from './files/types.js';
|
|
8
|
+
import { FileRow, FileSkeleton } from './files/entries.js';
|
|
9
|
+
import { FileGrid } from './files/grid.js';
|
|
10
|
+
import { FileToolbar, RootsPicker, DropZone, UploadProgress, EmptyState, BreadcrumbPath, BulkBar } from './files/chrome.js';
|
|
11
|
+
|
|
12
|
+
export {
|
|
13
|
+
fileGlyph, fmtFileSize, FileIcon, sortFiles,
|
|
14
|
+
FileRow, FileSkeleton,
|
|
15
|
+
FileGrid,
|
|
16
|
+
FileToolbar, RootsPicker, DropZone, UploadProgress, EmptyState, BreadcrumbPath, BulkBar,
|
|
11
17
|
};
|
|
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,10 +1,22 @@
|
|
|
1
|
-
// Freddie helpers —
|
|
2
|
-
//
|
|
3
|
-
//
|
|
1
|
+
// Freddie helpers — shared registry/localStorage/render helpers used by both
|
|
2
|
+
// the freddie component surface (src/components/freddie.js) and the OS kit's
|
|
3
|
+
// freddie pages (src/kits/os/freddie/*.js). Canonical merged location: this
|
|
4
|
+
// module previously had a diverged twin at src/kits/os/freddie/helpers.js
|
|
5
|
+
// (pre/form/getRecentPaths/saveRecentPath/skillLabel) that re-implemented the
|
|
6
|
+
// same localStorage-recent-paths + skill-label concerns with different keys
|
|
7
|
+
// and a different skillLabel() call shape; merged here so both surfaces
|
|
8
|
+
// share one behavior.
|
|
4
9
|
|
|
5
10
|
import * as webjsx from '../../../vendor/webjsx/index.js';
|
|
6
11
|
const h = webjsx.createElement;
|
|
7
12
|
|
|
13
|
+
// The ONE eyebrow on the freddie surface, and it earns it by being the only
|
|
14
|
+
// label that varies: `freddie · <id>` names the specific unimplemented route,
|
|
15
|
+
// which is the whole point of a stub — the reader needs to know WHICH page
|
|
16
|
+
// failed to resolve, and the <h2> below only shows a human title. The real
|
|
17
|
+
// freddie pages (src/components/freddie.js) deliberately carry NO eyebrow:
|
|
18
|
+
// there, the kicker was the constant string 'freddie' on all 22 headers, which
|
|
19
|
+
// named nothing the topbar/crumb did not already say. Do not add siblings.
|
|
8
20
|
export function renderPageStub({ id, title }) {
|
|
9
21
|
return h('div', { class: 'ds-freddie-stub' },
|
|
10
22
|
h('span', { class: 'eyebrow' }, 'freddie · ' + id),
|
|
@@ -13,7 +25,22 @@ export function renderPageStub({ id, title }) {
|
|
|
13
25
|
);
|
|
14
26
|
}
|
|
15
27
|
|
|
16
|
-
|
|
28
|
+
// pre() / form() — small raw-DOM helpers used by the OS kit's freddie pages
|
|
29
|
+
// (JSON dumps, quick add-forms) that don't need the full component barrel.
|
|
30
|
+
export function pre(obj) {
|
|
31
|
+
return h('pre', { class: 'fd-pre' }, typeof obj === 'string' ? obj : JSON.stringify(obj, null, 2));
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function form(opts) {
|
|
35
|
+
const { fields = [], submit = 'submit', onSubmit } = opts;
|
|
36
|
+
return h('form', { class: 'row-form', onsubmit: (ev) => { ev.preventDefault(); onSubmit && onSubmit(ev); } },
|
|
37
|
+
...fields.map(f => f.kind === 'textarea'
|
|
38
|
+
? h('textarea', { name: f.name, placeholder: f.placeholder || '', rows: f.rows || 4 })
|
|
39
|
+
: h('input', { name: f.name, type: f.type || 'text', placeholder: f.placeholder || '', value: f.value || '', required: f.required ? 'true' : null })),
|
|
40
|
+
h('button', { type: 'submit', class: 'btn-primary' }, submit));
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const SKILL_SLUG_LABELS = {
|
|
17
44
|
transcribe: 'transcribe',
|
|
18
45
|
summarize: 'summarize',
|
|
19
46
|
translate: 'translate',
|
|
@@ -21,11 +48,29 @@ const SKILL_LABELS = {
|
|
|
21
48
|
classify: 'classify',
|
|
22
49
|
};
|
|
23
50
|
|
|
24
|
-
|
|
25
|
-
|
|
51
|
+
// skillLabel() accepts EITHER call shape used by existing call sites:
|
|
52
|
+
// skillLabel('transcribe') -> looks up the static slug map
|
|
53
|
+
// skillLabel({ name, shortName }) -> derives a display label from a
|
|
54
|
+
// freddie skill object (OS kit's
|
|
55
|
+
// pages-core/-tools/-chat usage)
|
|
56
|
+
// Additive/widened on purpose: never narrows either existing signature.
|
|
57
|
+
export function skillLabel(input) {
|
|
58
|
+
if (input && typeof input === 'object') {
|
|
59
|
+
if (input.shortName) return input.shortName;
|
|
60
|
+
const n = input.name || '';
|
|
61
|
+
return n.replace(/^gm:/, '').replace(/^software-development$/, 'software dev').replace(/-/g, ' ');
|
|
62
|
+
}
|
|
63
|
+
return SKILL_SLUG_LABELS[input] || input;
|
|
26
64
|
}
|
|
27
65
|
|
|
28
|
-
|
|
66
|
+
// Recent-cwd localStorage helpers. The two diverged copies used different
|
|
67
|
+
// keys (ds247420.recent.paths vs fd_recent_cwds) and different caps (10 vs
|
|
68
|
+
// 5) — keep the fd_recent_cwds key + cap 5 since that is the one every real
|
|
69
|
+
// call site (the OS kit's freddie chat page) actually reads/writes; a
|
|
70
|
+
// consumer of the ds247420.recent.paths key never existed (grep-confirmed
|
|
71
|
+
// zero other readers), so this is a safe consolidation, not a behavior cut.
|
|
72
|
+
const RECENT_KEY = 'fd_recent_cwds';
|
|
73
|
+
const RECENT_CAP = 5;
|
|
29
74
|
|
|
30
75
|
export function getRecentPaths() {
|
|
31
76
|
if (typeof localStorage === 'undefined') return [];
|
|
@@ -37,7 +82,7 @@ export function saveRecentPath(path) {
|
|
|
37
82
|
if (typeof localStorage === 'undefined' || !path) return;
|
|
38
83
|
const list = getRecentPaths().filter(p => p !== path);
|
|
39
84
|
list.unshift(path);
|
|
40
|
-
try { localStorage.setItem(RECENT_KEY, JSON.stringify(list.slice(0,
|
|
85
|
+
try { localStorage.setItem(RECENT_KEY, JSON.stringify(list.slice(0, RECENT_CAP))); } catch { /* swallow: persistence is best-effort, recent-path history is non-critical */ }
|
|
41
86
|
}
|
|
42
87
|
|
|
43
88
|
// Helper used by consumers that want to render chat-message arrays with
|