@drawnagency/primitives 0.1.0 → 0.2.0
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/auth/index.js +26 -3
- package/dist/chunk-2VTPWODA.js +60 -0
- package/dist/chunk-CS7F6IOY.js +39 -0
- package/dist/chunk-HOJAF4VD.js +264 -0
- package/dist/chunk-IP6ODLXX.js +341 -0
- package/dist/chunk-T4BJ6RSB.js +58 -0
- package/dist/chunk-UKEVUCIZ.js +200 -0
- package/dist/chunk-UMSFICAC.js +36 -0
- package/dist/index.js +156 -4
- package/dist/lib/index.js +62 -12
- package/dist/lib/sanitize.d.ts.map +1 -1
- package/dist/media/index.js +36 -9
- package/dist/schemas/index.js +52 -7
- package/package.json +5 -4
- package/src/lib/sanitize.ts +6 -2
- package/dist/auth/cookies.js +0 -44
- package/dist/auth/errors.js +0 -10
- package/dist/auth/security.js +0 -48
- package/dist/auth/types.js +0 -1
- package/dist/components/brandguide/ColorSwatchSettings.js +0 -10
- package/dist/components/brandguide/Colors.js +0 -79
- package/dist/components/brandguide/DoDontList.js +0 -22
- package/dist/components/brandguide/DoDontMediaGrid.js +0 -5
- package/dist/components/editor/AudiencePicker.js +0 -24
- package/dist/components/editor/DeleteButton.js +0 -6
- package/dist/components/editor/DragHandle.js +0 -8
- package/dist/components/editor/InsertButton.js +0 -7
- package/dist/components/editor/SectionWrapper.js +0 -135
- package/dist/components/editor/SettingsButton.js +0 -6
- package/dist/components/editor/SettingsForm.js +0 -64
- package/dist/components/editor/StatusBadge.js +0 -10
- package/dist/components/editor/StatusPicker.js +0 -30
- package/dist/components/editor/index.js +0 -7
- package/dist/components/primitives/CustomParagraph.js +0 -24
- package/dist/components/primitives/EditableGrid.js +0 -90
- package/dist/components/primitives/EditableList.js +0 -54
- package/dist/components/primitives/EditablePlainText.js +0 -52
- package/dist/components/primitives/EditableRichText.js +0 -86
- package/dist/components/primitives/HeadingSection.js +0 -7
- package/dist/components/primitives/IconPicker.js +0 -21
- package/dist/components/primitives/LinkPopover.js +0 -48
- package/dist/components/primitives/MediaSettingsForms.js +0 -42
- package/dist/components/primitives/ResolvedMedia.js +0 -9
- package/dist/components/primitives/RichTextToolbar.js +0 -26
- package/dist/components/primitives/tiptap-presets.js +0 -44
- package/dist/components/primitives/useEditableCollection.js +0 -61
- package/dist/components/primitives/useEditablePlainText.js +0 -27
- package/dist/components/primitives/useEditableRichText.js +0 -52
- package/dist/components/sections/Button/CTAButton.js +0 -18
- package/dist/components/sections/Button/index.js +0 -28
- package/dist/components/sections/Colors/index.js +0 -34
- package/dist/components/sections/DoDontList/index.js +0 -33
- package/dist/components/sections/DoDontMediaGrid/index.js +0 -41
- package/dist/components/sections/IconList/IconList.js +0 -131
- package/dist/components/sections/IconList/IconListSettings.js +0 -22
- package/dist/components/sections/IconList/index.js +0 -27
- package/dist/components/sections/LinkHeading/index.js +0 -15
- package/dist/components/sections/MediaGrid/MediaGrid.js +0 -62
- package/dist/components/sections/MediaGrid/index.js +0 -35
- package/dist/components/sections/Prose/Prose.js +0 -11
- package/dist/components/sections/Prose/index.js +0 -15
- package/dist/components/sections/SectionLayout.js +0 -15
- package/dist/components/sections/SplitContent/SplitContent.js +0 -31
- package/dist/components/sections/SplitContent/SplitContentSettings.js +0 -17
- package/dist/components/sections/SplitContent/index.js +0 -27
- package/dist/components/sections/SubHeading/index.js +0 -18
- package/dist/components/sections/SubSubHeading/index.js +0 -18
- package/dist/components/sections/ViewRenderer.js +0 -13
- package/dist/components/sections/register-schemas.js +0 -15
- package/dist/components/sections/register.js +0 -15
- package/dist/components/shared/Button.js +0 -27
- package/dist/components/shared/Checkbox.js +0 -10
- package/dist/components/shared/ColorPicker.js +0 -5
- package/dist/components/shared/ErrorBoundary.js +0 -30
- package/dist/components/shared/FontPicker.js +0 -190
- package/dist/components/shared/FormLabel.js +0 -5
- package/dist/components/shared/IconButton.js +0 -16
- package/dist/components/shared/Input.js +0 -8
- package/dist/components/shared/Navigation.js +0 -71
- package/dist/components/shared/PasswordInput.js +0 -11
- package/dist/components/shared/Popover.js +0 -33
- package/dist/components/shared/PopoverItem.js +0 -6
- package/dist/components/shared/Select.js +0 -9
- package/dist/components/shared/Textarea.js +0 -8
- package/dist/components/shared/Toggle.js +0 -5
- package/dist/components/shared/Tooltip.js +0 -8
- package/dist/components/shared/icons.js +0 -23
- package/dist/components/shell/AudienceAddForm.js +0 -43
- package/dist/components/shell/AudienceRow.js +0 -74
- package/dist/components/shell/EditorContext.js +0 -24
- package/dist/components/shell/EditorLoginForm.js +0 -46
- package/dist/components/shell/EditorModal.js +0 -43
- package/dist/components/shell/EditorModalContext.js +0 -20
- package/dist/components/shell/EditorShell.js +0 -483
- package/dist/components/shell/MediaLibraryContext.js +0 -5
- package/dist/components/shell/MediaLibraryModal.js +0 -145
- package/dist/components/shell/ProcessingIndicator.js +0 -15
- package/dist/components/shell/SectionSkeleton.js +0 -22
- package/dist/components/shell/SectionTypePicker.js +0 -15
- package/dist/components/shell/SiteSettingsDisplay.js +0 -28
- package/dist/components/shell/SiteSettingsModal.js +0 -40
- package/dist/components/shell/SiteSettingsUsers.js +0 -87
- package/dist/components/shell/SiteSettingsViewerAccess.js +0 -94
- package/dist/components/shell/ViewerLoginForm.js +0 -40
- package/dist/data/google-fonts.json +0 -7718
- package/dist/hooks/index.js +0 -6
- package/dist/hooks/useActiveHeadings.js +0 -99
- package/dist/hooks/useEditorPersistence.js +0 -73
- package/dist/hooks/useEditorPublish.js +0 -145
- package/dist/hooks/useFocusTrap.js +0 -51
- package/dist/hooks/useMediaPipeline.js +0 -253
- package/dist/hooks/useResolvedMedia.js +0 -39
- package/dist/lib/cn.js +0 -5
- package/dist/lib/contrast.js +0 -11
- package/dist/lib/dexie.js +0 -236
- package/dist/lib/events.js +0 -15
- package/dist/lib/google-fonts.js +0 -11
- package/dist/lib/grid.js +0 -7
- package/dist/lib/icons.js +0 -27
- package/dist/lib/loader.js +0 -57
- package/dist/lib/nav.js +0 -58
- package/dist/lib/registry.js +0 -64
- package/dist/lib/safeRedirect.js +0 -11
- package/dist/lib/sanitize.js +0 -6
- package/dist/lib/timestamp.js +0 -28
- package/dist/media/github.js +0 -60
- package/dist/media/queue.js +0 -116
- package/dist/media/resolve.js +0 -50
- package/dist/media/types.js +0 -1
- package/dist/media/utils.js +0 -41
- package/dist/media/videoPoster.js +0 -44
- package/dist/media/worker.js +0 -73
- package/dist/schemas/audience.js +0 -19
- package/dist/schemas/auth.js +0 -22
- package/dist/schemas/media-grid-options.js +0 -7
- package/dist/schemas/media.js +0 -28
- package/dist/schemas/sections.js +0 -12
- package/dist/schemas/shared.js +0 -71
- package/dist/schemas/site-config.js +0 -26
package/dist/lib/contrast.js
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
export function deriveContrast(hex) {
|
|
2
|
-
const h = hex.replace("#", "");
|
|
3
|
-
const expanded = h.length === 3
|
|
4
|
-
? h[0] + h[0] + h[1] + h[1] + h[2] + h[2]
|
|
5
|
-
: h;
|
|
6
|
-
const r = parseInt(expanded.substring(0, 2), 16);
|
|
7
|
-
const g = parseInt(expanded.substring(2, 4), 16);
|
|
8
|
-
const b = parseInt(expanded.substring(4, 6), 16);
|
|
9
|
-
const luminance = (0.299 * r + 0.587 * g + 0.114 * b) / 255;
|
|
10
|
-
return luminance >= 0.5 ? "#1a1a1a" : "#f0f0f0";
|
|
11
|
-
}
|
package/dist/lib/dexie.js
DELETED
|
@@ -1,236 +0,0 @@
|
|
|
1
|
-
import Dexie from "dexie";
|
|
2
|
-
class EditorDatabase extends Dexie {
|
|
3
|
-
sections;
|
|
4
|
-
siteIndex;
|
|
5
|
-
meta;
|
|
6
|
-
siteConfig;
|
|
7
|
-
contentCache;
|
|
8
|
-
mediaManifest;
|
|
9
|
-
pendingMedia;
|
|
10
|
-
pendingMediaDeletions;
|
|
11
|
-
constructor(siteId) {
|
|
12
|
-
super(`brand-portal-${siteId}`);
|
|
13
|
-
this.version(1).stores({
|
|
14
|
-
sections: "sectionId",
|
|
15
|
-
siteIndex: "key",
|
|
16
|
-
meta: "key",
|
|
17
|
-
});
|
|
18
|
-
this.version(2).stores({
|
|
19
|
-
sections: "sectionId",
|
|
20
|
-
siteIndex: "key",
|
|
21
|
-
meta: "key",
|
|
22
|
-
siteConfig: "key",
|
|
23
|
-
});
|
|
24
|
-
this.version(3).stores({
|
|
25
|
-
sections: "sectionId",
|
|
26
|
-
siteIndex: "key",
|
|
27
|
-
meta: "key",
|
|
28
|
-
siteConfig: "key",
|
|
29
|
-
contentCache: "key",
|
|
30
|
-
});
|
|
31
|
-
this.version(4).stores({
|
|
32
|
-
sections: "sectionId",
|
|
33
|
-
siteIndex: "key",
|
|
34
|
-
meta: "key",
|
|
35
|
-
siteConfig: "key",
|
|
36
|
-
contentCache: "key",
|
|
37
|
-
mediaManifest: "key",
|
|
38
|
-
pendingMedia: "id",
|
|
39
|
-
pendingMediaDeletions: "id",
|
|
40
|
-
});
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
let db = null;
|
|
44
|
-
function getDb() {
|
|
45
|
-
if (!db)
|
|
46
|
-
throw new Error("Editor store not initialized. Call initEditorStore first.");
|
|
47
|
-
return db;
|
|
48
|
-
}
|
|
49
|
-
export function initEditorStore(siteId) {
|
|
50
|
-
db = new EditorDatabase(siteId);
|
|
51
|
-
}
|
|
52
|
-
export async function checkForLocalChanges() {
|
|
53
|
-
const database = getDb();
|
|
54
|
-
const dirtyRows = await database.sections.filter((row) => row.dirty).toArray();
|
|
55
|
-
const indexRow = await database.siteIndex.get("current");
|
|
56
|
-
const configRow = await database.siteConfig.get("current");
|
|
57
|
-
if (dirtyRows.length === 0 && !indexRow && !configRow)
|
|
58
|
-
return null;
|
|
59
|
-
const timestamps = [];
|
|
60
|
-
for (const row of dirtyRows)
|
|
61
|
-
timestamps.push(row.updatedAt);
|
|
62
|
-
if (indexRow)
|
|
63
|
-
timestamps.push(indexRow.updatedAt);
|
|
64
|
-
if (configRow)
|
|
65
|
-
timestamps.push(configRow.updatedAt);
|
|
66
|
-
timestamps.sort();
|
|
67
|
-
return { latestTimestamp: timestamps[timestamps.length - 1] };
|
|
68
|
-
}
|
|
69
|
-
export async function restoreLocalChanges() {
|
|
70
|
-
const sectionRows = await getDb().sections.toArray();
|
|
71
|
-
const sections = {};
|
|
72
|
-
for (const row of sectionRows) {
|
|
73
|
-
sections[row.sectionId] = row.content;
|
|
74
|
-
}
|
|
75
|
-
const indexRow = await getDb().siteIndex.get("current");
|
|
76
|
-
const configRow = await getDb().siteConfig.get("current");
|
|
77
|
-
if (indexRow) {
|
|
78
|
-
const metaRow = await getDb().meta.get("current");
|
|
79
|
-
const siteId = metaRow?.siteId ?? "";
|
|
80
|
-
return {
|
|
81
|
-
sections,
|
|
82
|
-
siteIndex: { siteId, order: indexRow.order, sections: indexRow.sections },
|
|
83
|
-
siteConfig: configRow?.config,
|
|
84
|
-
};
|
|
85
|
-
}
|
|
86
|
-
return { sections, siteIndex: undefined, siteConfig: configRow?.config };
|
|
87
|
-
}
|
|
88
|
-
export async function discardLocalChanges() {
|
|
89
|
-
const database = getDb();
|
|
90
|
-
await database.transaction("rw", [database.sections, database.siteIndex, database.meta, database.siteConfig, database.pendingMedia, database.pendingMediaDeletions, database.mediaManifest], async () => {
|
|
91
|
-
await database.sections.clear();
|
|
92
|
-
await database.siteIndex.clear();
|
|
93
|
-
await database.meta.clear();
|
|
94
|
-
await database.siteConfig.clear();
|
|
95
|
-
await database.pendingMedia.clear();
|
|
96
|
-
await database.pendingMediaDeletions.clear();
|
|
97
|
-
await database.mediaManifest.clear();
|
|
98
|
-
});
|
|
99
|
-
}
|
|
100
|
-
export async function persistSiteIndex(index) {
|
|
101
|
-
const now = new Date().toISOString();
|
|
102
|
-
const database = getDb();
|
|
103
|
-
await database.transaction("rw", [database.siteIndex, database.meta], async () => {
|
|
104
|
-
await database.siteIndex.put({
|
|
105
|
-
key: "current",
|
|
106
|
-
order: index.order,
|
|
107
|
-
sections: index.sections,
|
|
108
|
-
updatedAt: now,
|
|
109
|
-
});
|
|
110
|
-
await database.meta.put({
|
|
111
|
-
key: "current",
|
|
112
|
-
lastSavedSha: null,
|
|
113
|
-
lastSavedAt: null,
|
|
114
|
-
siteId: index.siteId,
|
|
115
|
-
});
|
|
116
|
-
});
|
|
117
|
-
}
|
|
118
|
-
export async function persistSiteConfig(config) {
|
|
119
|
-
const now = new Date().toISOString();
|
|
120
|
-
const database = getDb();
|
|
121
|
-
await database.siteConfig.put({
|
|
122
|
-
key: "current",
|
|
123
|
-
config,
|
|
124
|
-
updatedAt: now,
|
|
125
|
-
});
|
|
126
|
-
}
|
|
127
|
-
export async function hasLocalChanges() {
|
|
128
|
-
const database = getDb();
|
|
129
|
-
const dirtyCount = await database.sections.filter((row) => row.dirty).count();
|
|
130
|
-
if (dirtyCount > 0)
|
|
131
|
-
return true;
|
|
132
|
-
const indexRow = await database.siteIndex.get("current");
|
|
133
|
-
if (indexRow != null)
|
|
134
|
-
return true;
|
|
135
|
-
const configRow = await database.siteConfig.get("current");
|
|
136
|
-
return configRow != null;
|
|
137
|
-
}
|
|
138
|
-
export async function getDirtySections() {
|
|
139
|
-
const rows = await getDb().sections.filter((row) => row.dirty).toArray();
|
|
140
|
-
return rows.map((row) => ({ sectionId: row.sectionId, content: row.content }));
|
|
141
|
-
}
|
|
142
|
-
export async function persistAll(sections, siteIndex, deletedSectionIds, siteConfig) {
|
|
143
|
-
const now = new Date().toISOString();
|
|
144
|
-
const database = getDb();
|
|
145
|
-
await database.transaction("rw", [database.sections, database.siteIndex, database.meta, database.siteConfig], async () => {
|
|
146
|
-
if (sections.length > 0) {
|
|
147
|
-
await database.sections.bulkPut(sections.map((e) => ({
|
|
148
|
-
sectionId: e.sectionId,
|
|
149
|
-
content: e.content,
|
|
150
|
-
dirty: true,
|
|
151
|
-
updatedAt: now,
|
|
152
|
-
})));
|
|
153
|
-
}
|
|
154
|
-
if (deletedSectionIds && deletedSectionIds.length > 0) {
|
|
155
|
-
await database.sections.bulkDelete(deletedSectionIds);
|
|
156
|
-
}
|
|
157
|
-
if (siteIndex) {
|
|
158
|
-
await database.siteIndex.put({
|
|
159
|
-
key: "current",
|
|
160
|
-
order: siteIndex.order,
|
|
161
|
-
sections: siteIndex.sections,
|
|
162
|
-
updatedAt: now,
|
|
163
|
-
});
|
|
164
|
-
await database.meta.put({
|
|
165
|
-
key: "current",
|
|
166
|
-
lastSavedSha: null,
|
|
167
|
-
lastSavedAt: null,
|
|
168
|
-
siteId: siteIndex.siteId,
|
|
169
|
-
});
|
|
170
|
-
}
|
|
171
|
-
if (siteConfig) {
|
|
172
|
-
await database.siteConfig.put({
|
|
173
|
-
key: "current",
|
|
174
|
-
config: siteConfig,
|
|
175
|
-
updatedAt: now,
|
|
176
|
-
});
|
|
177
|
-
}
|
|
178
|
-
});
|
|
179
|
-
}
|
|
180
|
-
export async function cacheContent(sha, sections, index, siteConfig) {
|
|
181
|
-
const now = new Date().toISOString();
|
|
182
|
-
await getDb().contentCache.put({
|
|
183
|
-
key: "current",
|
|
184
|
-
sha,
|
|
185
|
-
sections,
|
|
186
|
-
index,
|
|
187
|
-
siteConfig,
|
|
188
|
-
updatedAt: now,
|
|
189
|
-
});
|
|
190
|
-
}
|
|
191
|
-
export async function getCachedContent() {
|
|
192
|
-
const row = await getDb().contentCache.get("current");
|
|
193
|
-
if (!row)
|
|
194
|
-
return null;
|
|
195
|
-
return {
|
|
196
|
-
sha: row.sha,
|
|
197
|
-
sections: row.sections,
|
|
198
|
-
index: row.index,
|
|
199
|
-
siteConfig: row.siteConfig,
|
|
200
|
-
};
|
|
201
|
-
}
|
|
202
|
-
export async function persistMediaManifest(manifest) {
|
|
203
|
-
const now = new Date().toISOString();
|
|
204
|
-
await getDb().mediaManifest.put({ key: "current", manifest, updatedAt: now });
|
|
205
|
-
}
|
|
206
|
-
export async function getMediaManifest() {
|
|
207
|
-
const row = await getDb().mediaManifest.get("current");
|
|
208
|
-
return row?.manifest ?? null;
|
|
209
|
-
}
|
|
210
|
-
export async function addPendingMediaItem(item, localUrls = {}) {
|
|
211
|
-
const now = new Date().toISOString();
|
|
212
|
-
await getDb().pendingMedia.put({ id: item.id, item, localUrls, updatedAt: now });
|
|
213
|
-
}
|
|
214
|
-
export async function getPendingMediaItems() {
|
|
215
|
-
const rows = await getDb().pendingMedia.toArray();
|
|
216
|
-
return rows.map((r) => r.item);
|
|
217
|
-
}
|
|
218
|
-
export async function getPendingMediaLocalUrls(id) {
|
|
219
|
-
const row = await getDb().pendingMedia.get(id);
|
|
220
|
-
return row?.localUrls ?? null;
|
|
221
|
-
}
|
|
222
|
-
export async function markPendingMediaDeleted(id) {
|
|
223
|
-
const now = new Date().toISOString();
|
|
224
|
-
await getDb().pendingMediaDeletions.put({ id, deletedAt: now });
|
|
225
|
-
}
|
|
226
|
-
export async function getPendingMediaDeletions() {
|
|
227
|
-
const rows = await getDb().pendingMediaDeletions.toArray();
|
|
228
|
-
return rows.map((r) => r.id);
|
|
229
|
-
}
|
|
230
|
-
export async function clearPendingMedia() {
|
|
231
|
-
const database = getDb();
|
|
232
|
-
await database.transaction("rw", [database.pendingMedia, database.pendingMediaDeletions], async () => {
|
|
233
|
-
await database.pendingMedia.clear();
|
|
234
|
-
await database.pendingMediaDeletions.clear();
|
|
235
|
-
});
|
|
236
|
-
}
|
package/dist/lib/events.js
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
export function createEvent(name) {
|
|
2
|
-
return {
|
|
3
|
-
dispatch(detail) {
|
|
4
|
-
window.dispatchEvent(new CustomEvent(name, { detail }));
|
|
5
|
-
},
|
|
6
|
-
listen(callback) {
|
|
7
|
-
const handler = (e) => callback(e.detail);
|
|
8
|
-
window.addEventListener(name, handler);
|
|
9
|
-
return () => window.removeEventListener(name, handler);
|
|
10
|
-
},
|
|
11
|
-
};
|
|
12
|
-
}
|
|
13
|
-
export const editModeEvent = createEvent("editmodechange");
|
|
14
|
-
export const navChangeEvent = createEvent("sitenavchange");
|
|
15
|
-
export const darkModeEvent = createEvent("sitedarkmode");
|
package/dist/lib/google-fonts.js
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
export function buildGoogleFontsUrl(headingFont, bodyFont) {
|
|
2
|
-
const fonts = new Set();
|
|
3
|
-
if (headingFont !== "system-ui")
|
|
4
|
-
fonts.add(headingFont);
|
|
5
|
-
if (bodyFont !== "system-ui")
|
|
6
|
-
fonts.add(bodyFont);
|
|
7
|
-
if (fonts.size === 0)
|
|
8
|
-
return null;
|
|
9
|
-
const params = [...fonts].map((f) => `family=${f.replace(/ /g, "+")}:wght@400;700`).join("&");
|
|
10
|
-
return `https://fonts.googleapis.com/css2?${params}&display=swap`;
|
|
11
|
-
}
|
package/dist/lib/grid.js
DELETED
package/dist/lib/icons.js
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import { Check, X, ArrowRight, Star, Info, AlertTriangle, Heart, Lightbulb, Flag, Shield, Zap, Eye, Lock, Globe, Users, Target, Bookmark, Clock, ThumbsUp, ThumbsDown, } from "lucide-react";
|
|
2
|
-
export const curatedIcons = [
|
|
3
|
-
{ id: "check", label: "Check", icon: Check },
|
|
4
|
-
{ id: "x", label: "X", icon: X },
|
|
5
|
-
{ id: "arrow-right", label: "Arrow Right", icon: ArrowRight },
|
|
6
|
-
{ id: "star", label: "Star", icon: Star },
|
|
7
|
-
{ id: "info", label: "Info", icon: Info },
|
|
8
|
-
{ id: "alert-triangle", label: "Warning", icon: AlertTriangle },
|
|
9
|
-
{ id: "heart", label: "Heart", icon: Heart },
|
|
10
|
-
{ id: "lightbulb", label: "Lightbulb", icon: Lightbulb },
|
|
11
|
-
{ id: "flag", label: "Flag", icon: Flag },
|
|
12
|
-
{ id: "shield", label: "Shield", icon: Shield },
|
|
13
|
-
{ id: "zap", label: "Zap", icon: Zap },
|
|
14
|
-
{ id: "eye", label: "Eye", icon: Eye },
|
|
15
|
-
{ id: "lock", label: "Lock", icon: Lock },
|
|
16
|
-
{ id: "globe", label: "Globe", icon: Globe },
|
|
17
|
-
{ id: "users", label: "Users", icon: Users },
|
|
18
|
-
{ id: "target", label: "Target", icon: Target },
|
|
19
|
-
{ id: "bookmark", label: "Bookmark", icon: Bookmark },
|
|
20
|
-
{ id: "clock", label: "Clock", icon: Clock },
|
|
21
|
-
{ id: "thumbs-up", label: "Thumbs Up", icon: ThumbsUp },
|
|
22
|
-
{ id: "thumbs-down", label: "Thumbs Down", icon: ThumbsDown },
|
|
23
|
-
];
|
|
24
|
-
const iconMap = new Map(curatedIcons.map((e) => [e.id, e]));
|
|
25
|
-
export function getIcon(id) {
|
|
26
|
-
return iconMap.get(id);
|
|
27
|
-
}
|
package/dist/lib/loader.js
DELETED
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
import { getSectionSchema } from "../schemas/sections";
|
|
2
|
-
import { IndexSchema } from "../schemas/site-config";
|
|
3
|
-
/**
|
|
4
|
-
* Merge a validated index with raw section file contents.
|
|
5
|
-
* Validates each section file against its Zod schema.
|
|
6
|
-
* Returns sections in the order specified by index.order.
|
|
7
|
-
*/
|
|
8
|
-
export function mergeSiteContent(index, sectionFiles) {
|
|
9
|
-
const sections = [];
|
|
10
|
-
for (const id of index.order) {
|
|
11
|
-
const raw = sectionFiles[id];
|
|
12
|
-
if (!raw) {
|
|
13
|
-
console.warn(`Section file missing for id: ${id}, skipping`);
|
|
14
|
-
continue;
|
|
15
|
-
}
|
|
16
|
-
const result = getSectionSchema().safeParse(raw);
|
|
17
|
-
if (!result.success) {
|
|
18
|
-
const type = raw.type ?? "unknown";
|
|
19
|
-
console.warn(`Skipping section "${id}" (type: ${type}): invalid schema`);
|
|
20
|
-
continue;
|
|
21
|
-
}
|
|
22
|
-
const meta = index.sections[id];
|
|
23
|
-
sections.push({ section: result.data, meta });
|
|
24
|
-
}
|
|
25
|
-
return { sections, index };
|
|
26
|
-
}
|
|
27
|
-
/**
|
|
28
|
-
* Build a SiteContent from a Vite `import.meta.glob` result.
|
|
29
|
-
* Callers glob the section JSON files at build time, pass the parsed index, and
|
|
30
|
-
* receive sections merged in index.order.
|
|
31
|
-
*/
|
|
32
|
-
export function loadStaticSiteContent(index, sectionGlob) {
|
|
33
|
-
const sectionFiles = {};
|
|
34
|
-
for (const [path, data] of Object.entries(sectionGlob)) {
|
|
35
|
-
const filename = path.split("/").pop();
|
|
36
|
-
const id = filename.replace(".json", "");
|
|
37
|
-
sectionFiles[id] = data;
|
|
38
|
-
}
|
|
39
|
-
return mergeSiteContent(index, sectionFiles);
|
|
40
|
-
}
|
|
41
|
-
/**
|
|
42
|
-
* Load site content from the filesystem.
|
|
43
|
-
* For use in scripts and tests — Astro pages should use import.meta.glob instead.
|
|
44
|
-
*/
|
|
45
|
-
export async function loadSiteContent(contentDir) {
|
|
46
|
-
const fs = await import("node:fs/promises");
|
|
47
|
-
const path = await import("node:path");
|
|
48
|
-
const indexPath = path.join(contentDir, "index.json");
|
|
49
|
-
const indexRaw = JSON.parse(await fs.readFile(indexPath, "utf-8"));
|
|
50
|
-
const index = IndexSchema.parse(indexRaw);
|
|
51
|
-
const sectionFiles = {};
|
|
52
|
-
for (const id of index.order) {
|
|
53
|
-
const sectionPath = path.join(contentDir, "sections", `${id}.json`);
|
|
54
|
-
sectionFiles[id] = JSON.parse(await fs.readFile(sectionPath, "utf-8"));
|
|
55
|
-
}
|
|
56
|
-
return mergeSiteContent(index, sectionFiles);
|
|
57
|
-
}
|
package/dist/lib/nav.js
DELETED
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
export function toSectionId(text) {
|
|
2
|
-
return text
|
|
3
|
-
.toLowerCase()
|
|
4
|
-
.replace(/[^\w\s-]/g, "")
|
|
5
|
-
.trim()
|
|
6
|
-
.replace(/\s+/g, "-");
|
|
7
|
-
}
|
|
8
|
-
/**
|
|
9
|
-
* Generate a three-tier navigation structure from sections.
|
|
10
|
-
* link_heading → top-level, sub_heading → second-level, sub_sub_heading → third-level.
|
|
11
|
-
* Non-heading sections are skipped.
|
|
12
|
-
*/
|
|
13
|
-
export function generateNavLinks(sections) {
|
|
14
|
-
const nav = [];
|
|
15
|
-
let currentParent = null;
|
|
16
|
-
let currentChild = null;
|
|
17
|
-
for (const { section, meta } of sections) {
|
|
18
|
-
const content = section.content;
|
|
19
|
-
if (!content.heading)
|
|
20
|
-
continue;
|
|
21
|
-
if (section.type === "link_heading") {
|
|
22
|
-
currentParent = {
|
|
23
|
-
href: `#${toSectionId(content.heading)}`,
|
|
24
|
-
label: content.heading,
|
|
25
|
-
status: meta.status,
|
|
26
|
-
children: [],
|
|
27
|
-
};
|
|
28
|
-
currentChild = null;
|
|
29
|
-
nav.push(currentParent);
|
|
30
|
-
}
|
|
31
|
-
else if (section.type === "sub_heading") {
|
|
32
|
-
if (content.excludeFromNav)
|
|
33
|
-
continue;
|
|
34
|
-
if (!currentParent)
|
|
35
|
-
continue;
|
|
36
|
-
currentChild = {
|
|
37
|
-
href: `#${toSectionId(content.heading)}`,
|
|
38
|
-
label: content.heading,
|
|
39
|
-
status: meta.status,
|
|
40
|
-
children: [],
|
|
41
|
-
};
|
|
42
|
-
currentParent.children.push(currentChild);
|
|
43
|
-
}
|
|
44
|
-
else if (section.type === "sub_sub_heading") {
|
|
45
|
-
if (content.excludeFromNav)
|
|
46
|
-
continue;
|
|
47
|
-
if (!currentChild)
|
|
48
|
-
continue;
|
|
49
|
-
currentChild.children.push({
|
|
50
|
-
href: `#${toSectionId(content.heading)}`,
|
|
51
|
-
label: content.heading,
|
|
52
|
-
status: meta.status,
|
|
53
|
-
children: [],
|
|
54
|
-
});
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
return nav;
|
|
58
|
-
}
|
package/dist/lib/registry.js
DELETED
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
export function defineSection(def) {
|
|
2
|
-
return def;
|
|
3
|
-
}
|
|
4
|
-
export function createRegistry() {
|
|
5
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
6
|
-
const sections = new Map();
|
|
7
|
-
const schemas = new Map();
|
|
8
|
-
return {
|
|
9
|
-
registerSection(def) {
|
|
10
|
-
if (sections.has(def.type)) {
|
|
11
|
-
throw new Error(`Section type "${def.type}" is already registered`);
|
|
12
|
-
}
|
|
13
|
-
sections.set(def.type, def);
|
|
14
|
-
},
|
|
15
|
-
registerSchema(type, schema) {
|
|
16
|
-
schemas.set(type, schema);
|
|
17
|
-
},
|
|
18
|
-
getSection(type) {
|
|
19
|
-
return sections.get(type);
|
|
20
|
-
},
|
|
21
|
-
getSchema(type) {
|
|
22
|
-
return schemas.get(type) ?? sections.get(type)?.schema;
|
|
23
|
-
},
|
|
24
|
-
getAllSections() {
|
|
25
|
-
return Array.from(sections.values());
|
|
26
|
-
},
|
|
27
|
-
getAllSchemas() {
|
|
28
|
-
const merged = new Map(schemas);
|
|
29
|
-
for (const [type, def] of sections) {
|
|
30
|
-
if (!merged.has(type))
|
|
31
|
-
merged.set(type, def.schema);
|
|
32
|
-
}
|
|
33
|
-
return Array.from(merged.values());
|
|
34
|
-
},
|
|
35
|
-
clearRegistry() {
|
|
36
|
-
sections.clear();
|
|
37
|
-
schemas.clear();
|
|
38
|
-
},
|
|
39
|
-
};
|
|
40
|
-
}
|
|
41
|
-
// --- Default instance & module-level convenience functions ---
|
|
42
|
-
const defaultRegistry = createRegistry();
|
|
43
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
44
|
-
export function registerSection(def) {
|
|
45
|
-
defaultRegistry.registerSection(def);
|
|
46
|
-
}
|
|
47
|
-
export function registerSchema(type, schema) {
|
|
48
|
-
defaultRegistry.registerSchema(type, schema);
|
|
49
|
-
}
|
|
50
|
-
export function getSection(type) {
|
|
51
|
-
return defaultRegistry.getSection(type);
|
|
52
|
-
}
|
|
53
|
-
export function getSchema(type) {
|
|
54
|
-
return defaultRegistry.getSchema(type);
|
|
55
|
-
}
|
|
56
|
-
export function getAllSections() {
|
|
57
|
-
return defaultRegistry.getAllSections();
|
|
58
|
-
}
|
|
59
|
-
export function getAllSchemas() {
|
|
60
|
-
return defaultRegistry.getAllSchemas();
|
|
61
|
-
}
|
|
62
|
-
export function clearRegistry() {
|
|
63
|
-
defaultRegistry.clearRegistry();
|
|
64
|
-
}
|
package/dist/lib/safeRedirect.js
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import { safeNextPath } from "../auth/security";
|
|
2
|
-
/**
|
|
3
|
-
* Client-safe redirect validation (defense-in-depth).
|
|
4
|
-
* The server already validates via safeNextPath(), but this guards
|
|
5
|
-
* against open-redirect if a response is tampered with in transit.
|
|
6
|
-
*/
|
|
7
|
-
export function safeRedirect(url, fallback) {
|
|
8
|
-
if (!url)
|
|
9
|
-
return fallback;
|
|
10
|
-
return safeNextPath(url) ?? fallback;
|
|
11
|
-
}
|
package/dist/lib/sanitize.js
DELETED
package/dist/lib/timestamp.js
DELETED
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
export function formatTimestamp(iso) {
|
|
2
|
-
const date = new Date(iso);
|
|
3
|
-
const now = new Date();
|
|
4
|
-
const diffMs = now.getTime() - date.getTime();
|
|
5
|
-
const diffMinutes = Math.floor(diffMs / 60_000);
|
|
6
|
-
const diffHours = Math.floor(diffMs / 3_600_000);
|
|
7
|
-
const sameDay = date.getUTCFullYear() === now.getUTCFullYear() &&
|
|
8
|
-
date.getUTCMonth() === now.getUTCMonth() &&
|
|
9
|
-
date.getUTCDate() === now.getUTCDate();
|
|
10
|
-
if (diffMinutes < 1)
|
|
11
|
-
return "just now";
|
|
12
|
-
if (diffMinutes < 60) {
|
|
13
|
-
return `${diffMinutes} ${diffMinutes === 1 ? "minute" : "minutes"} ago`;
|
|
14
|
-
}
|
|
15
|
-
if (sameDay) {
|
|
16
|
-
return `${diffHours} ${diffHours === 1 ? "hour" : "hours"} ago`;
|
|
17
|
-
}
|
|
18
|
-
return date.toLocaleDateString("en-US", {
|
|
19
|
-
month: "short",
|
|
20
|
-
day: "numeric",
|
|
21
|
-
timeZone: "UTC",
|
|
22
|
-
}) + " at " + date.toLocaleTimeString("en-US", {
|
|
23
|
-
hour: "numeric",
|
|
24
|
-
minute: "2-digit",
|
|
25
|
-
hour12: true,
|
|
26
|
-
timeZone: "UTC",
|
|
27
|
-
});
|
|
28
|
-
}
|
package/dist/media/github.js
DELETED
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
import { sanitizeMediaName, mimeToExt, hashFileBuffer } from "./utils";
|
|
2
|
-
export function createGitHubMediaAdapter(manifest) {
|
|
3
|
-
return {
|
|
4
|
-
async hash(file) {
|
|
5
|
-
return hashFileBuffer(file.buffer);
|
|
6
|
-
},
|
|
7
|
-
async exists(hash) {
|
|
8
|
-
return hash in manifest.images;
|
|
9
|
-
},
|
|
10
|
-
async process(file, hash, _sizes) {
|
|
11
|
-
const name = sanitizeMediaName(file.originalName);
|
|
12
|
-
const folder = `${name}-${hash}`;
|
|
13
|
-
return {
|
|
14
|
-
id: hash, hash, kind: "image", originalName: name,
|
|
15
|
-
width: 0, height: 0, mimeType: "image/webp", size: 0, folder, variants: [], alt: "",
|
|
16
|
-
};
|
|
17
|
-
},
|
|
18
|
-
async delete(id) {
|
|
19
|
-
delete manifest.images[id];
|
|
20
|
-
},
|
|
21
|
-
resolve(id, sizes) {
|
|
22
|
-
const item = manifest.images[id];
|
|
23
|
-
if (!item)
|
|
24
|
-
return null;
|
|
25
|
-
if (item.kind === "video") {
|
|
26
|
-
return {
|
|
27
|
-
tag: "video",
|
|
28
|
-
src: `/api/media/${id}/original.${mimeToExt(item.mimeType)}`,
|
|
29
|
-
poster: `/api/media/${id}/poster.webp`,
|
|
30
|
-
width: item.width, height: item.height,
|
|
31
|
-
autoplay: true, loop: true, muted: true,
|
|
32
|
-
};
|
|
33
|
-
}
|
|
34
|
-
if (item.kind === "animated") {
|
|
35
|
-
return {
|
|
36
|
-
tag: "img",
|
|
37
|
-
src: `/api/media/${id}/original.${mimeToExt(item.mimeType)}`,
|
|
38
|
-
width: item.width, height: item.height,
|
|
39
|
-
};
|
|
40
|
-
}
|
|
41
|
-
// Static image with srcset
|
|
42
|
-
const availableVariants = item.variants.filter((v) => sizes.length === 0 || sizes.includes(v.width));
|
|
43
|
-
const sorted = [...availableVariants].sort((a, b) => a.width - b.width);
|
|
44
|
-
const midIndex = Math.floor(sorted.length / 2);
|
|
45
|
-
const defaultVariant = sorted[midIndex] ?? sorted[0];
|
|
46
|
-
if (!defaultVariant)
|
|
47
|
-
return null;
|
|
48
|
-
const srcSet = sorted.map((v) => `/api/media/${id}/${v.width}.webp ${v.width}w`).join(", ");
|
|
49
|
-
return {
|
|
50
|
-
tag: "img",
|
|
51
|
-
src: `/api/media/${id}/${defaultVariant.width}.webp`,
|
|
52
|
-
srcSet,
|
|
53
|
-
width: item.width, height: item.height,
|
|
54
|
-
};
|
|
55
|
-
},
|
|
56
|
-
async list() {
|
|
57
|
-
return Object.values(manifest.images);
|
|
58
|
-
},
|
|
59
|
-
};
|
|
60
|
-
}
|