@mevdragon/vidfarm-devcli 0.5.3 → 0.6.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/README.md +3 -3
- package/demo/README.md +28 -0
- package/demo/dist/app.css +1 -0
- package/demo/dist/app.js +1184 -0
- package/demo/dist/chunks/chunk-DXB73IDG.js +1 -0
- package/demo/dist/chunks/chunk-S7OWAJDS.js +36 -0
- package/demo/dist/chunks/chunk-VTIBZ6AN.js +1 -0
- package/demo/dist/chunks/dist-ADSJKBVE.js +332 -0
- package/demo/dist/chunks/domEditingLayers-VZMLL4AP-SGHWPND4.js +1 -0
- package/demo/dist/chunks/hyperframes-player-XB65TCD6.js +425 -0
- package/demo/dist/chunks/lib-XAQ37YOE.js +1 -0
- package/demo/dist/chunks/src-TJ2QYA4U.js +207 -0
- package/demo/dist/favicon.ico +0 -0
- package/demo/dist/icons/timeline/audio.svg +7 -0
- package/demo/dist/icons/timeline/captions.svg +5 -0
- package/demo/dist/icons/timeline/composition.svg +12 -0
- package/demo/dist/icons/timeline/image.svg +18 -0
- package/demo/dist/icons/timeline/music.svg +10 -0
- package/demo/dist/icons/timeline/text.svg +3 -0
- package/demo/dist/index.html +15 -0
- package/dist/src/account-pages-legacy.js +9396 -0
- package/dist/src/account-pages.js +61 -0
- package/dist/src/app.js +14378 -0
- package/dist/src/cli.js +1 -1
- package/dist/src/composition-runtime.js +613 -0
- package/dist/src/config.js +166 -0
- package/dist/src/context.js +447 -0
- package/dist/src/dev-app-legacy.js +739 -0
- package/dist/src/dev-app.js +6 -0
- package/dist/src/domain.js +2 -0
- package/dist/src/editor-chat-history.js +82 -0
- package/dist/src/editor-chat.js +449 -0
- package/dist/src/editor-dark-theme.js +1128 -0
- package/dist/src/frontend/debug.js +71 -0
- package/dist/src/frontend/flockposter-cache-store.js +124 -0
- package/dist/src/frontend/homepage-client.js +182 -0
- package/dist/src/frontend/homepage-shared.js +4 -0
- package/dist/src/frontend/homepage-store.js +28 -0
- package/dist/src/frontend/homepage-view.js +547 -0
- package/dist/src/frontend/page-runtime-client.js +132 -0
- package/dist/src/frontend/page-runtime-store.js +9 -0
- package/dist/src/frontend/sentry.js +42 -0
- package/dist/src/frontend/template-editor-chat.js +3960 -0
- package/dist/src/help-page.js +346 -0
- package/dist/src/homepage.js +1235 -0
- package/dist/src/hyperframes/composition.js +180 -0
- package/dist/src/index.js +16 -0
- package/dist/src/instrument.js +30 -0
- package/dist/src/lib/crypto.js +45 -0
- package/dist/src/lib/dev-log.js +54 -0
- package/dist/src/lib/display-name.js +11 -0
- package/dist/src/lib/ids.js +24 -0
- package/dist/src/lib/images.js +19 -0
- package/dist/src/lib/json.js +15 -0
- package/dist/src/lib/package-root.js +47 -0
- package/dist/src/lib/template-paths.js +28 -0
- package/dist/src/lib/time.js +7 -0
- package/dist/src/lib/url-clean.js +85 -0
- package/dist/src/page-runtime.js +2 -0
- package/dist/src/page-shell.js +1381 -0
- package/dist/src/primitive-context.js +357 -0
- package/dist/src/primitive-registry.js +2436 -0
- package/dist/src/primitive-sdk.js +4 -0
- package/dist/src/primitives/hyperframes-media.js +108 -0
- package/dist/src/react-page-shell.js +35 -0
- package/dist/src/ready-post-schedule-component.js +1540 -0
- package/dist/src/registry.js +296 -0
- package/dist/src/runtime.js +35 -0
- package/dist/src/services/api-call-history.js +249 -0
- package/dist/src/services/auth.js +152 -0
- package/dist/src/services/billing-pricing.js +39 -0
- package/dist/src/services/billing.js +228 -0
- package/dist/src/services/cast.js +127 -0
- package/dist/src/services/chat-threads.js +92 -0
- package/dist/src/services/composition-sanitize.js +124 -0
- package/dist/src/services/composition-watch.js +79 -0
- package/dist/src/services/fork-access.js +93 -0
- package/dist/src/services/fork-manifest.js +42 -0
- package/dist/src/services/ghostcut.js +179 -0
- package/dist/src/services/hyperframes.js +2307 -0
- package/dist/src/services/job-capacity.js +14 -0
- package/dist/src/services/job-logs.js +197 -0
- package/dist/src/services/jobs.js +136 -0
- package/dist/src/services/local-dynamo.js +0 -0
- package/dist/src/services/media-processing.js +766 -0
- package/dist/src/services/primitive-media-lambda.js +280 -0
- package/dist/src/services/providers.js +2926 -0
- package/dist/src/services/rate-limits.js +262 -0
- package/dist/src/services/serverless-auth.js +382 -0
- package/dist/src/services/serverless-jobs.js +1082 -0
- package/dist/src/services/serverless-provider-keys.js +409 -0
- package/dist/src/services/serverless-records.js +1385 -0
- package/dist/src/services/serverless-template-configs.js +75 -0
- package/dist/src/services/storage.js +383 -0
- package/dist/src/services/template-certification.js +413 -0
- package/dist/src/services/template-loader.js +99 -0
- package/dist/src/services/template-runtime-bundles.js +217 -0
- package/dist/src/services/template-sources.js +1017 -0
- package/dist/src/services/video-normalization.js +2 -0
- package/dist/src/services/webhooks.js +62 -0
- package/dist/src/template-editor-pages.js +2576 -0
- package/dist/src/template-editor-shell.js +2840 -0
- package/dist/src/template-sdk.js +4 -0
- package/dist/src/worker.js +17 -0
- package/package.json +6 -4
- package/public/assets/homepage-app.js +54 -0
- package/public/assets/homepage-client-app.js +80 -0
- package/public/assets/page-runtime-client-app.js +94 -0
- package/src/assets/SELLING_AWARENESS_STAGES.md +579 -0
- package/src/assets/SELLING_WITH_HOOKS.md +377 -0
- package/src/assets/SELLING_WITH_VSLS.md +606 -0
- package/src/assets/favicon.ico +0 -0
- package/src/assets/logo-vidfarm.png +0 -0
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
const prefix = "[vidfarm]";
|
|
2
|
+
const maxBufferedEntries = 1000;
|
|
3
|
+
function shouldWriteToConsole(level) {
|
|
4
|
+
if (level !== "debug")
|
|
5
|
+
return true;
|
|
6
|
+
if (typeof window === "undefined")
|
|
7
|
+
return false;
|
|
8
|
+
try {
|
|
9
|
+
const params = new URLSearchParams(window.location.search);
|
|
10
|
+
return params.has("debug") || window.localStorage?.getItem("VIDFARM_DEBUG") === "1";
|
|
11
|
+
}
|
|
12
|
+
catch {
|
|
13
|
+
return false;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
function write(level, event, detail) {
|
|
17
|
+
const entry = {
|
|
18
|
+
event,
|
|
19
|
+
detail,
|
|
20
|
+
timestamp: new Date().toISOString()
|
|
21
|
+
};
|
|
22
|
+
if (typeof window !== "undefined") {
|
|
23
|
+
window.__VIDFARM_DEBUG_LOGS__ = window.__VIDFARM_DEBUG_LOGS__ ?? [];
|
|
24
|
+
window.__VIDFARM_DEBUG_LOGS__.push(entry);
|
|
25
|
+
if (window.__VIDFARM_DEBUG_LOGS__.length > maxBufferedEntries) {
|
|
26
|
+
window.__VIDFARM_DEBUG_LOGS__.splice(0, window.__VIDFARM_DEBUG_LOGS__.length - maxBufferedEntries);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
if (shouldWriteToConsole(level)) {
|
|
30
|
+
const method = level === "debug" ? "log" : level;
|
|
31
|
+
console[method](prefix, event, detail ?? "");
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
export function debugLog(event, detail) {
|
|
35
|
+
write("info", event, detail);
|
|
36
|
+
}
|
|
37
|
+
export function debugDebug(event, detail) {
|
|
38
|
+
write("debug", event, detail);
|
|
39
|
+
}
|
|
40
|
+
export function debugWarn(event, detail) {
|
|
41
|
+
write("warn", event, detail);
|
|
42
|
+
}
|
|
43
|
+
export function debugError(event, detail) {
|
|
44
|
+
write("error", event, detail);
|
|
45
|
+
}
|
|
46
|
+
export function installGlobalDebugHooks() {
|
|
47
|
+
if (typeof window === "undefined" || window.__VIDFARM_DEBUG_HOOKS__) {
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
window.__VIDFARM_DEBUG_HOOKS__ = true;
|
|
51
|
+
debugLog("boot.hooks-installed", {
|
|
52
|
+
href: window.location.href,
|
|
53
|
+
readyState: document.readyState,
|
|
54
|
+
userAgent: navigator.userAgent
|
|
55
|
+
});
|
|
56
|
+
window.addEventListener("error", (event) => {
|
|
57
|
+
debugError("window.error", {
|
|
58
|
+
message: event.message,
|
|
59
|
+
filename: event.filename,
|
|
60
|
+
lineno: event.lineno,
|
|
61
|
+
colno: event.colno,
|
|
62
|
+
error: event.error instanceof Error ? event.error.stack : event.error
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
window.addEventListener("unhandledrejection", (event) => {
|
|
66
|
+
debugError("window.unhandledrejection", {
|
|
67
|
+
reason: event.reason instanceof Error ? event.reason.stack : event.reason
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
//# sourceMappingURL=debug.js.map
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { createStore } from "zustand/vanilla";
|
|
2
|
+
export const FLOCKPOSTER_CACHE_STORAGE_KEY = "vidfarm:flockposter-cache:v1";
|
|
3
|
+
const PUBLIC_SCOPE = "public";
|
|
4
|
+
const emptySnapshot = {
|
|
5
|
+
channels: [],
|
|
6
|
+
posts: [],
|
|
7
|
+
channelsUpdatedAt: null,
|
|
8
|
+
calendarUpdatedAt: null,
|
|
9
|
+
calendarRangeStart: null,
|
|
10
|
+
calendarRangeEnd: null
|
|
11
|
+
};
|
|
12
|
+
export function getFlockPosterCacheStorageKey(userId) {
|
|
13
|
+
return `${FLOCKPOSTER_CACHE_STORAGE_KEY}_${userId?.trim() || PUBLIC_SCOPE}`;
|
|
14
|
+
}
|
|
15
|
+
export function readFlockPosterCacheSnapshot(userId) {
|
|
16
|
+
if (typeof window === "undefined") {
|
|
17
|
+
return emptySnapshot;
|
|
18
|
+
}
|
|
19
|
+
try {
|
|
20
|
+
const raw = window.localStorage.getItem(getFlockPosterCacheStorageKey(userId));
|
|
21
|
+
if (!raw) {
|
|
22
|
+
return emptySnapshot;
|
|
23
|
+
}
|
|
24
|
+
const parsed = JSON.parse(raw);
|
|
25
|
+
if (!parsed || typeof parsed !== "object") {
|
|
26
|
+
return emptySnapshot;
|
|
27
|
+
}
|
|
28
|
+
return {
|
|
29
|
+
channels: Array.isArray(parsed.channels) ? parsed.channels : [],
|
|
30
|
+
posts: Array.isArray(parsed.posts) ? parsed.posts : [],
|
|
31
|
+
channelsUpdatedAt: typeof parsed.channelsUpdatedAt === "string" ? parsed.channelsUpdatedAt : null,
|
|
32
|
+
calendarUpdatedAt: typeof parsed.calendarUpdatedAt === "string" ? parsed.calendarUpdatedAt : null,
|
|
33
|
+
calendarRangeStart: typeof parsed.calendarRangeStart === "string" ? parsed.calendarRangeStart : null,
|
|
34
|
+
calendarRangeEnd: typeof parsed.calendarRangeEnd === "string" ? parsed.calendarRangeEnd : null
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
return emptySnapshot;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
function writeFlockPosterCacheSnapshot(snapshot, userId) {
|
|
42
|
+
if (typeof window === "undefined") {
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
try {
|
|
46
|
+
window.localStorage.setItem(getFlockPosterCacheStorageKey(userId), JSON.stringify(snapshot));
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
// Ignore storage failures and keep in-memory state only.
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
export function createFlockPosterCacheStore(userId) {
|
|
53
|
+
const initial = readFlockPosterCacheSnapshot(userId);
|
|
54
|
+
return createStore((set, get) => ({
|
|
55
|
+
...initial,
|
|
56
|
+
setChannelsData: (channels) => {
|
|
57
|
+
const current = get();
|
|
58
|
+
const next = {
|
|
59
|
+
...current,
|
|
60
|
+
channels: [
|
|
61
|
+
...current.channels.filter(isEmailChannel),
|
|
62
|
+
...channels.filter((channel) => !isEmailChannel(channel))
|
|
63
|
+
],
|
|
64
|
+
channelsUpdatedAt: new Date().toISOString()
|
|
65
|
+
};
|
|
66
|
+
writeFlockPosterCacheSnapshot(next, userId);
|
|
67
|
+
set(next);
|
|
68
|
+
},
|
|
69
|
+
setCalendarData: (input) => {
|
|
70
|
+
const current = get();
|
|
71
|
+
const mergedChannels = dedupeChannels([...current.channels, ...input.channels]);
|
|
72
|
+
const mergedPosts = input.replaceRange
|
|
73
|
+
? mergePostsForRange(current.posts, input.posts, input.rangeStart, input.rangeEnd)
|
|
74
|
+
: dedupePosts([...current.posts, ...input.posts]);
|
|
75
|
+
const next = {
|
|
76
|
+
...current,
|
|
77
|
+
channels: mergedChannels,
|
|
78
|
+
posts: mergedPosts,
|
|
79
|
+
channelsUpdatedAt: new Date().toISOString(),
|
|
80
|
+
calendarUpdatedAt: new Date().toISOString(),
|
|
81
|
+
calendarRangeStart: expandRangeStart(current.calendarRangeStart, input.rangeStart),
|
|
82
|
+
calendarRangeEnd: expandRangeEnd(current.calendarRangeEnd, input.rangeEnd)
|
|
83
|
+
};
|
|
84
|
+
writeFlockPosterCacheSnapshot(next, userId);
|
|
85
|
+
set(next);
|
|
86
|
+
}
|
|
87
|
+
}));
|
|
88
|
+
}
|
|
89
|
+
function dedupeChannels(channels) {
|
|
90
|
+
return [...new Map(channels.map((channel) => [channel.id, channel])).values()];
|
|
91
|
+
}
|
|
92
|
+
function isEmailChannel(channel) {
|
|
93
|
+
return channel.id.startsWith("email:") || channel.platform?.toLowerCase() === "email";
|
|
94
|
+
}
|
|
95
|
+
function dedupePosts(posts) {
|
|
96
|
+
return [...new Map(posts.map((post) => [post.id, post])).values()];
|
|
97
|
+
}
|
|
98
|
+
function mergePostsForRange(currentPosts, nextPosts, rangeStart, rangeEnd) {
|
|
99
|
+
const startTime = Date.parse(rangeStart);
|
|
100
|
+
const endTime = Date.parse(rangeEnd);
|
|
101
|
+
if (Number.isNaN(startTime) || Number.isNaN(endTime)) {
|
|
102
|
+
return dedupePosts([...currentPosts, ...nextPosts]);
|
|
103
|
+
}
|
|
104
|
+
return dedupePosts([
|
|
105
|
+
...currentPosts.filter((post) => {
|
|
106
|
+
const scheduledTime = Date.parse(post.scheduledAt);
|
|
107
|
+
return Number.isNaN(scheduledTime) || scheduledTime < startTime || scheduledTime > endTime;
|
|
108
|
+
}),
|
|
109
|
+
...nextPosts
|
|
110
|
+
]);
|
|
111
|
+
}
|
|
112
|
+
function expandRangeStart(current, next) {
|
|
113
|
+
if (!current) {
|
|
114
|
+
return next;
|
|
115
|
+
}
|
|
116
|
+
return Date.parse(next) < Date.parse(current) ? next : current;
|
|
117
|
+
}
|
|
118
|
+
function expandRangeEnd(current, next) {
|
|
119
|
+
if (!current) {
|
|
120
|
+
return next;
|
|
121
|
+
}
|
|
122
|
+
return Date.parse(next) > Date.parse(current) ? next : current;
|
|
123
|
+
}
|
|
124
|
+
//# sourceMappingURL=flockposter-cache-store.js.map
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import "./sentry.js";
|
|
3
|
+
import { useCallback, useEffect, useMemo, useState } from "react";
|
|
4
|
+
import { hydrateRoot } from "react-dom/client";
|
|
5
|
+
import * as Sentry from "@sentry/react";
|
|
6
|
+
import { useStore } from "zustand";
|
|
7
|
+
import { debugError, debugLog, installGlobalDebugHooks } from "./debug.js";
|
|
8
|
+
import { HomepageShell } from "./homepage-view.js";
|
|
9
|
+
import { createHomepageStore } from "./homepage-store.js";
|
|
10
|
+
installGlobalDebugHooks();
|
|
11
|
+
const bookmarkStorageKey = "vidfarm:discover-bookmarks:v1";
|
|
12
|
+
const publicBookmarkScope = "public";
|
|
13
|
+
function readBoot() {
|
|
14
|
+
const element = document.getElementById("homepage-boot");
|
|
15
|
+
if (!element?.textContent) {
|
|
16
|
+
return {
|
|
17
|
+
templates: [],
|
|
18
|
+
account: { isLoggedIn: false },
|
|
19
|
+
loadEndpoint: "/discover/feed",
|
|
20
|
+
skillEndpointPrefix: "/discover/skills/"
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
return JSON.parse(element.textContent);
|
|
24
|
+
}
|
|
25
|
+
function resolveBookmarkStorageKey(userId) {
|
|
26
|
+
return `${bookmarkStorageKey}_${userId?.trim() || publicBookmarkScope}`;
|
|
27
|
+
}
|
|
28
|
+
function readBookmarks(userId) {
|
|
29
|
+
try {
|
|
30
|
+
const raw = window.localStorage.getItem(resolveBookmarkStorageKey(userId));
|
|
31
|
+
if (!raw) {
|
|
32
|
+
return new Set();
|
|
33
|
+
}
|
|
34
|
+
const parsed = JSON.parse(raw);
|
|
35
|
+
if (!Array.isArray(parsed)) {
|
|
36
|
+
return new Set();
|
|
37
|
+
}
|
|
38
|
+
return new Set(parsed.filter((value) => typeof value === "string"));
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
return new Set();
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
function writeBookmarks(bookmarks, userId) {
|
|
45
|
+
try {
|
|
46
|
+
window.localStorage.setItem(resolveBookmarkStorageKey(userId), JSON.stringify(Array.from(bookmarks)));
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
// Ignore storage failures and keep in-memory state.
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
function filterTemplates(input) {
|
|
53
|
+
const query = input.searchQuery.trim().toLowerCase();
|
|
54
|
+
return input.templates.filter((template) => {
|
|
55
|
+
const matchesSearch = !query || `${template.templateId} ${template.title} ${template.slugId} ${template.difficulty}`.toLowerCase().includes(query);
|
|
56
|
+
const matchesBookmarks = !input.bookmarksOnly || input.bookmarks.has(template.templateId);
|
|
57
|
+
return matchesSearch && matchesBookmarks;
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
function HomepageClientApp({ boot }) {
|
|
61
|
+
const store = useMemo(() => createHomepageStore(boot), [boot]);
|
|
62
|
+
const templates = useStore(store, (state) => state.templates);
|
|
63
|
+
const searchQuery = useStore(store, (state) => state.searchQuery);
|
|
64
|
+
const bookmarksOnly = useStore(store, (state) => state.bookmarksOnly);
|
|
65
|
+
const bookmarks = useStore(store, (state) => state.bookmarks);
|
|
66
|
+
const loading = useStore(store, (state) => state.loading);
|
|
67
|
+
const error = useStore(store, (state) => state.error);
|
|
68
|
+
const [bookmarksReady, setBookmarksReady] = useState(false);
|
|
69
|
+
const visibleTemplates = useMemo(() => filterTemplates({ templates, searchQuery, bookmarksOnly, bookmarks }), [templates, searchQuery, bookmarksOnly, bookmarks]);
|
|
70
|
+
useEffect(() => {
|
|
71
|
+
store.getState().setBookmarks(readBookmarks(boot.account.userId));
|
|
72
|
+
setBookmarksReady(true);
|
|
73
|
+
}, [boot.account.userId, store]);
|
|
74
|
+
useEffect(() => {
|
|
75
|
+
if (!bookmarksReady) {
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
writeBookmarks(bookmarks, boot.account.userId);
|
|
79
|
+
}, [boot.account.userId, bookmarks, bookmarksReady]);
|
|
80
|
+
const refreshFeed = useCallback(async (options) => {
|
|
81
|
+
if (options?.showLoading) {
|
|
82
|
+
store.getState().setLoading(true);
|
|
83
|
+
}
|
|
84
|
+
try {
|
|
85
|
+
const response = await fetch(boot.loadEndpoint, {
|
|
86
|
+
credentials: "same-origin",
|
|
87
|
+
cache: "no-store",
|
|
88
|
+
headers: { Accept: "application/json" }
|
|
89
|
+
});
|
|
90
|
+
const payload = await response.json().catch(() => ({}));
|
|
91
|
+
if (!response.ok) {
|
|
92
|
+
throw new Error(typeof payload.error === "string" && payload.error ? payload.error : "Unable to load formats.");
|
|
93
|
+
}
|
|
94
|
+
store.getState().setTemplates(Array.isArray(payload.templates) ? payload.templates : []);
|
|
95
|
+
}
|
|
96
|
+
catch (fetchError) {
|
|
97
|
+
if (options?.showLoading) {
|
|
98
|
+
store.getState().setError(fetchError instanceof Error ? fetchError.message : "Unable to load formats.");
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
// Background refresh failure keeps the current list on screen.
|
|
102
|
+
debugError("homepage.feed_refresh_failed", { message: fetchError instanceof Error ? fetchError.message : String(fetchError) });
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}, [boot.loadEndpoint, store]);
|
|
106
|
+
useEffect(() => {
|
|
107
|
+
if (boot.templates.length > 0) {
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
void refreshFeed({ showLoading: true });
|
|
111
|
+
}, [boot.templates.length, refreshFeed]);
|
|
112
|
+
// While one of the user's submissions is still downloading, poll the feed so
|
|
113
|
+
// the "Processing" card flips to an openable template without a manual reload.
|
|
114
|
+
const hasProcessingTemplates = templates.some((template) => template.status === "processing");
|
|
115
|
+
useEffect(() => {
|
|
116
|
+
if (!hasProcessingTemplates) {
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
const timer = window.setInterval(() => {
|
|
120
|
+
void refreshFeed();
|
|
121
|
+
}, 8000);
|
|
122
|
+
return () => window.clearInterval(timer);
|
|
123
|
+
}, [hasProcessingTemplates, refreshFeed]);
|
|
124
|
+
const handleDeleteTemplate = useCallback(async (templateId) => {
|
|
125
|
+
try {
|
|
126
|
+
const response = await fetch(`/discover/templates/${encodeURIComponent(templateId)}`, {
|
|
127
|
+
method: "DELETE",
|
|
128
|
+
credentials: "same-origin",
|
|
129
|
+
headers: { Accept: "application/json" }
|
|
130
|
+
});
|
|
131
|
+
const payload = await response.json().catch(() => ({}));
|
|
132
|
+
if (!response.ok) {
|
|
133
|
+
return { ok: false, error: typeof payload.error === "string" && payload.error ? payload.error : "Unable to delete this template." };
|
|
134
|
+
}
|
|
135
|
+
debugLog("homepage.template_deleted", { template_id: templateId });
|
|
136
|
+
await refreshFeed();
|
|
137
|
+
return { ok: true };
|
|
138
|
+
}
|
|
139
|
+
catch (deleteError) {
|
|
140
|
+
return { ok: false, error: deleteError instanceof Error ? deleteError.message : "Unable to delete this template." };
|
|
141
|
+
}
|
|
142
|
+
}, [refreshFeed]);
|
|
143
|
+
const handleAddTemplate = useCallback(async (submission) => {
|
|
144
|
+
try {
|
|
145
|
+
const response = await fetch("/discover/templates", {
|
|
146
|
+
method: "POST",
|
|
147
|
+
credentials: "same-origin",
|
|
148
|
+
headers: { "content-type": "application/json", Accept: "application/json" },
|
|
149
|
+
body: JSON.stringify({
|
|
150
|
+
source_url: submission.sourceUrl,
|
|
151
|
+
tagline: submission.tagline || null,
|
|
152
|
+
notes: submission.notes || null
|
|
153
|
+
})
|
|
154
|
+
});
|
|
155
|
+
const payload = await response.json().catch(() => ({}));
|
|
156
|
+
if (!response.ok) {
|
|
157
|
+
return { ok: false, error: typeof payload.error === "string" && payload.error ? payload.error : "Unable to add that video." };
|
|
158
|
+
}
|
|
159
|
+
debugLog("homepage.template_added", { source_url: submission.sourceUrl });
|
|
160
|
+
await refreshFeed();
|
|
161
|
+
return { ok: true };
|
|
162
|
+
}
|
|
163
|
+
catch (submitError) {
|
|
164
|
+
return { ok: false, error: submitError instanceof Error ? submitError.message : "Unable to add that video." };
|
|
165
|
+
}
|
|
166
|
+
}, [refreshFeed]);
|
|
167
|
+
return (_jsx(HomepageShell, { account: boot.account, templates: templates, visibleTemplates: visibleTemplates, searchQuery: searchQuery, bookmarksOnly: bookmarksOnly, bookmarks: bookmarks, loading: loading, error: error, skillEndpointPrefix: boot.skillEndpointPrefix, onSearchQueryChange: (value) => store.getState().setSearchQuery(value), onBookmarksOnlyChange: (value) => store.getState().setBookmarksOnly(value), onToggleBookmark: (templateId) => store.getState().toggleBookmark(templateId), onAddTemplate: handleAddTemplate, onDeleteTemplate: handleDeleteTemplate }));
|
|
168
|
+
}
|
|
169
|
+
const boot = readBoot();
|
|
170
|
+
const root = document.getElementById("homepage-root");
|
|
171
|
+
debugLog("homepage.boot", {
|
|
172
|
+
is_logged_in: boot.account?.isLoggedIn ?? false,
|
|
173
|
+
template_count: boot.templates?.length ?? 0,
|
|
174
|
+
load_endpoint: boot.loadEndpoint
|
|
175
|
+
});
|
|
176
|
+
if (root) {
|
|
177
|
+
hydrateRoot(root, (_jsx(Sentry.ErrorBoundary, { fallback: _jsx(_Fragment, {}), onError: (error) => debugError("homepage.render_error", { message: error instanceof Error ? error.message : String(error) }), children: _jsx(HomepageClientApp, { boot: boot }) })));
|
|
178
|
+
}
|
|
179
|
+
else {
|
|
180
|
+
debugError("homepage.no_root_element", {});
|
|
181
|
+
}
|
|
182
|
+
//# sourceMappingURL=homepage-client.js.map
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { createStore } from "zustand/vanilla";
|
|
2
|
+
export function createHomepageStore(boot) {
|
|
3
|
+
return createStore((set) => ({
|
|
4
|
+
templates: boot.templates,
|
|
5
|
+
searchQuery: "",
|
|
6
|
+
bookmarksOnly: false,
|
|
7
|
+
bookmarks: new Set(),
|
|
8
|
+
loading: boot.templates.length === 0,
|
|
9
|
+
error: null,
|
|
10
|
+
setTemplates: (templates) => set({ templates, loading: false, error: null }),
|
|
11
|
+
setSearchQuery: (searchQuery) => set({ searchQuery }),
|
|
12
|
+
setBookmarksOnly: (bookmarksOnly) => set({ bookmarksOnly }),
|
|
13
|
+
setBookmarks: (bookmarks) => set({ bookmarks: new Set(bookmarks) }),
|
|
14
|
+
toggleBookmark: (templateId) => set((state) => {
|
|
15
|
+
const next = new Set(state.bookmarks);
|
|
16
|
+
if (next.has(templateId)) {
|
|
17
|
+
next.delete(templateId);
|
|
18
|
+
}
|
|
19
|
+
else {
|
|
20
|
+
next.add(templateId);
|
|
21
|
+
}
|
|
22
|
+
return { bookmarks: next };
|
|
23
|
+
}),
|
|
24
|
+
setLoading: (loading) => set({ loading }),
|
|
25
|
+
setError: (error) => set({ error, loading: false })
|
|
26
|
+
}));
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=homepage-store.js.map
|