@mevdragon/vidfarm-devcli 0.5.2 → 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.
Files changed (113) hide show
  1. package/README.md +3 -3
  2. package/demo/README.md +28 -0
  3. package/demo/dist/app.css +1 -0
  4. package/demo/dist/app.js +1184 -0
  5. package/demo/dist/chunks/chunk-DXB73IDG.js +1 -0
  6. package/demo/dist/chunks/chunk-S7OWAJDS.js +36 -0
  7. package/demo/dist/chunks/chunk-VTIBZ6AN.js +1 -0
  8. package/demo/dist/chunks/dist-ADSJKBVE.js +332 -0
  9. package/demo/dist/chunks/domEditingLayers-VZMLL4AP-SGHWPND4.js +1 -0
  10. package/demo/dist/chunks/hyperframes-player-XB65TCD6.js +425 -0
  11. package/demo/dist/chunks/lib-XAQ37YOE.js +1 -0
  12. package/demo/dist/chunks/src-TJ2QYA4U.js +207 -0
  13. package/demo/dist/favicon.ico +0 -0
  14. package/demo/dist/icons/timeline/audio.svg +7 -0
  15. package/demo/dist/icons/timeline/captions.svg +5 -0
  16. package/demo/dist/icons/timeline/composition.svg +12 -0
  17. package/demo/dist/icons/timeline/image.svg +18 -0
  18. package/demo/dist/icons/timeline/music.svg +10 -0
  19. package/demo/dist/icons/timeline/text.svg +3 -0
  20. package/demo/dist/index.html +15 -0
  21. package/dist/src/account-pages-legacy.js +9396 -0
  22. package/dist/src/account-pages.js +61 -0
  23. package/dist/src/app.js +14378 -0
  24. package/dist/src/cli.js +1 -1
  25. package/dist/src/composition-runtime.js +613 -0
  26. package/dist/src/config.js +166 -0
  27. package/dist/src/context.js +447 -0
  28. package/dist/src/dev-app-legacy.js +739 -0
  29. package/dist/src/dev-app.js +6 -0
  30. package/dist/src/domain.js +2 -0
  31. package/dist/src/editor-chat-history.js +82 -0
  32. package/dist/src/editor-chat.js +449 -0
  33. package/dist/src/editor-dark-theme.js +1128 -0
  34. package/dist/src/frontend/debug.js +71 -0
  35. package/dist/src/frontend/flockposter-cache-store.js +124 -0
  36. package/dist/src/frontend/homepage-client.js +182 -0
  37. package/dist/src/frontend/homepage-shared.js +4 -0
  38. package/dist/src/frontend/homepage-store.js +28 -0
  39. package/dist/src/frontend/homepage-view.js +547 -0
  40. package/dist/src/frontend/page-runtime-client.js +132 -0
  41. package/dist/src/frontend/page-runtime-store.js +9 -0
  42. package/dist/src/frontend/sentry.js +42 -0
  43. package/dist/src/frontend/template-editor-chat.js +3960 -0
  44. package/dist/src/help-page.js +346 -0
  45. package/dist/src/homepage.js +1235 -0
  46. package/dist/src/hyperframes/composition.js +180 -0
  47. package/dist/src/index.js +16 -0
  48. package/dist/src/instrument.js +30 -0
  49. package/dist/src/lib/crypto.js +45 -0
  50. package/dist/src/lib/dev-log.js +54 -0
  51. package/dist/src/lib/display-name.js +11 -0
  52. package/dist/src/lib/ids.js +24 -0
  53. package/dist/src/lib/images.js +19 -0
  54. package/dist/src/lib/json.js +15 -0
  55. package/dist/src/lib/package-root.js +47 -0
  56. package/dist/src/lib/template-paths.js +28 -0
  57. package/dist/src/lib/time.js +7 -0
  58. package/dist/src/lib/url-clean.js +85 -0
  59. package/dist/src/page-runtime.js +2 -0
  60. package/dist/src/page-shell.js +1381 -0
  61. package/dist/src/primitive-context.js +357 -0
  62. package/dist/src/primitive-registry.js +2436 -0
  63. package/dist/src/primitive-sdk.js +4 -0
  64. package/dist/src/primitives/hyperframes-media.js +108 -0
  65. package/dist/src/react-page-shell.js +35 -0
  66. package/dist/src/ready-post-schedule-component.js +1540 -0
  67. package/dist/src/registry.js +296 -0
  68. package/dist/src/runtime.js +35 -0
  69. package/dist/src/services/api-call-history.js +249 -0
  70. package/dist/src/services/auth.js +152 -0
  71. package/dist/src/services/billing-pricing.js +39 -0
  72. package/dist/src/services/billing.js +228 -0
  73. package/dist/src/services/cast.js +127 -0
  74. package/dist/src/services/chat-threads.js +92 -0
  75. package/dist/src/services/composition-sanitize.js +124 -0
  76. package/dist/src/services/composition-watch.js +79 -0
  77. package/dist/src/services/fork-access.js +93 -0
  78. package/dist/src/services/fork-manifest.js +42 -0
  79. package/dist/src/services/ghostcut.js +179 -0
  80. package/dist/src/services/hyperframes.js +2307 -0
  81. package/dist/src/services/job-capacity.js +14 -0
  82. package/dist/src/services/job-logs.js +197 -0
  83. package/dist/src/services/jobs.js +136 -0
  84. package/dist/src/services/local-dynamo.js +0 -0
  85. package/dist/src/services/media-processing.js +766 -0
  86. package/dist/src/services/primitive-media-lambda.js +280 -0
  87. package/dist/src/services/providers.js +2926 -0
  88. package/dist/src/services/rate-limits.js +262 -0
  89. package/dist/src/services/serverless-auth.js +382 -0
  90. package/dist/src/services/serverless-jobs.js +1082 -0
  91. package/dist/src/services/serverless-provider-keys.js +409 -0
  92. package/dist/src/services/serverless-records.js +1385 -0
  93. package/dist/src/services/serverless-template-configs.js +75 -0
  94. package/dist/src/services/storage.js +383 -0
  95. package/dist/src/services/template-certification.js +413 -0
  96. package/dist/src/services/template-loader.js +99 -0
  97. package/dist/src/services/template-runtime-bundles.js +217 -0
  98. package/dist/src/services/template-sources.js +1017 -0
  99. package/dist/src/services/video-normalization.js +2 -0
  100. package/dist/src/services/webhooks.js +62 -0
  101. package/dist/src/template-editor-pages.js +2576 -0
  102. package/dist/src/template-editor-shell.js +2840 -0
  103. package/dist/src/template-sdk.js +4 -0
  104. package/dist/src/worker.js +17 -0
  105. package/package.json +6 -4
  106. package/public/assets/homepage-app.js +54 -0
  107. package/public/assets/homepage-client-app.js +80 -0
  108. package/public/assets/page-runtime-client-app.js +94 -0
  109. package/src/assets/SELLING_AWARENESS_STAGES.md +579 -0
  110. package/src/assets/SELLING_WITH_HOOKS.md +377 -0
  111. package/src/assets/SELLING_WITH_VSLS.md +606 -0
  112. package/src/assets/favicon.ico +0 -0
  113. 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,4 @@
1
+ export function isVideoPreview(url) {
2
+ return Boolean(url && /\.(mp4|webm|ogg|mov)(\?|#|$)/i.test(url));
3
+ }
4
+ //# sourceMappingURL=homepage-shared.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