@mevdragon/vidfarm-devcli 0.2.8 → 0.2.10

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 (98) hide show
  1. package/.env.example +9 -0
  2. package/GETTING_STARTED.developers.md +1 -26
  3. package/README.md +3 -0
  4. package/SKILL.developer.md +419 -8
  5. package/auto-create-templates/AUTO_CREATE_TEMPLATES.md +475 -0
  6. package/auto-create-templates/extractor-system-prompt.md +113 -0
  7. package/auto-create-templates/production-graph.schema.json +276 -0
  8. package/auto-create-templates/runbook.md +140 -0
  9. package/auto-create-templates/template-plan.schema.json +230 -0
  10. package/auto-create-templates/template-planner-prompt.md +75 -0
  11. package/dist/src/account-pages-legacy.js +9064 -0
  12. package/dist/src/account-pages.js +41 -620
  13. package/dist/src/app.js +6627 -244
  14. package/dist/src/cli.js +386 -26
  15. package/dist/src/config.js +12 -2
  16. package/dist/src/context.js +35 -35
  17. package/dist/src/db.js +1016 -53
  18. package/dist/src/dev-app-legacy.js +693 -0
  19. package/dist/src/dev-app.js +4 -904
  20. package/dist/src/domain.js +1 -1
  21. package/dist/src/editor-chat-history.js +72 -0
  22. package/dist/src/editor-chat.js +202 -0
  23. package/dist/src/frontend/flockposter-cache-store.js +116 -0
  24. package/dist/src/frontend/homepage-client.js +114 -0
  25. package/dist/src/frontend/homepage-shared.js +3 -0
  26. package/dist/src/frontend/homepage-store.js +27 -0
  27. package/dist/src/frontend/homepage-view.js +147 -0
  28. package/dist/src/frontend/page-runtime-client.js +100 -0
  29. package/dist/src/frontend/page-runtime-store.js +8 -0
  30. package/dist/src/frontend/template-editor-chat.js +2261 -0
  31. package/dist/src/homepage.js +695 -371
  32. package/dist/src/lib/template-style-options.js +95 -15
  33. package/dist/src/page-runtime.js +1 -0
  34. package/dist/src/page-shell.js +941 -0
  35. package/dist/src/primitive-context.js +163 -0
  36. package/dist/src/primitive-registry.js +340 -0
  37. package/dist/src/primitive-sdk.js +3 -0
  38. package/dist/src/primitives/remotion/html-image.js +28 -0
  39. package/dist/src/react-page-shell.js +34 -0
  40. package/dist/src/ready-post-schedule-component.js +1514 -0
  41. package/dist/src/registry.js +36 -17
  42. package/dist/src/runtime.js +6 -1
  43. package/dist/src/services/api-call-history.js +245 -0
  44. package/dist/src/services/auth.js +25 -3
  45. package/dist/src/services/billing.js +248 -9
  46. package/dist/src/services/chat-threads.js +88 -0
  47. package/dist/src/services/job-logs.js +14 -7
  48. package/dist/src/services/jobs.js +13 -2
  49. package/dist/src/services/providers.js +552 -133
  50. package/dist/src/services/rate-limits.js +236 -0
  51. package/dist/src/services/remotion.js +143 -16
  52. package/dist/src/services/storage.js +10 -8
  53. package/dist/src/services/template-certification.js +11 -2
  54. package/dist/src/services/template-loader.js +3 -1
  55. package/dist/src/services/template-sources.js +68 -3
  56. package/dist/src/template-editor-pages.js +2309 -0
  57. package/dist/src/template-editor-shell.js +1507 -0
  58. package/dist/src/worker.js +120 -22
  59. package/package.json +27 -4
  60. package/public/assets/homepage-app.js +54 -0
  61. package/public/assets/homepage-client-app.js +54 -0
  62. package/public/assets/page-runtime-client-app.js +68 -0
  63. package/dist/templates/vidfarm_template_0000/src/lib/images.js +0 -202
  64. package/dist/templates/vidfarm_template_0000/src/remotion/Root.js +0 -34
  65. package/dist/templates/vidfarm_template_0000/src/remotion/index.js +0 -3
  66. package/dist/templates/vidfarm_template_0000/src/sdk.js +0 -3
  67. package/dist/templates/vidfarm_template_0000/src/style-options.js +0 -51
  68. package/dist/templates/vidfarm_template_0000/src/template-dna.js +0 -9
  69. package/dist/templates/vidfarm_template_0000/src/template.js +0 -1447
  70. package/templates/vidfarm_template_0000/README.md +0 -100
  71. package/templates/vidfarm_template_0000/SKILL.md +0 -225
  72. package/templates/vidfarm_template_0000/assets/Abel-Regular.ttf +0 -0
  73. package/templates/vidfarm_template_0000/assets/DMSerifDisplay-Regular.ttf +0 -0
  74. package/templates/vidfarm_template_0000/assets/Montserrat[wght].ttf +0 -0
  75. package/templates/vidfarm_template_0000/assets/SourceCodePro[wght].ttf +0 -0
  76. package/templates/vidfarm_template_0000/assets/TikTokSans-SemiBold.ttf +0 -0
  77. package/templates/vidfarm_template_0000/assets/Yesteryear-Regular.ttf +0 -0
  78. package/templates/vidfarm_template_0000/composition.json +0 -11
  79. package/templates/vidfarm_template_0000/package.json +0 -27
  80. package/templates/vidfarm_template_0000/research/preview/.gitkeep +0 -1
  81. package/templates/vidfarm_template_0000/research/source_notes.md +0 -7
  82. package/templates/vidfarm_template_0000/src/lib/images.js +0 -202
  83. package/templates/vidfarm_template_0000/src/lib/images.ts +0 -241
  84. package/templates/vidfarm_template_0000/src/remotion/Root.js +0 -33
  85. package/templates/vidfarm_template_0000/src/remotion/Root.tsx +0 -75
  86. package/templates/vidfarm_template_0000/src/remotion/index.js +0 -3
  87. package/templates/vidfarm_template_0000/src/remotion/index.tsx +0 -4
  88. package/templates/vidfarm_template_0000/src/sdk.js +0 -3
  89. package/templates/vidfarm_template_0000/src/sdk.ts +0 -140
  90. package/templates/vidfarm_template_0000/src/style-options.js +0 -51
  91. package/templates/vidfarm_template_0000/src/style-options.ts +0 -60
  92. package/templates/vidfarm_template_0000/src/template-dna.js +0 -9
  93. package/templates/vidfarm_template_0000/src/template-dna.ts +0 -15
  94. package/templates/vidfarm_template_0000/src/template.js +0 -1447
  95. package/templates/vidfarm_template_0000/src/template.ts +0 -2049
  96. package/templates/vidfarm_template_0000/template.config.json +0 -21
  97. package/templates/vidfarm_template_0000/tmp/solobacterium-moorei-slideshow.request.json +0 -31
  98. package/templates/vidfarm_template_0000/tsconfig.json +0 -19
@@ -0,0 +1,147 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useEffect, useRef, useState } from "react";
3
+ import { resolveAccountDisplayName, withAccountQuery } from "../page-shell.js";
4
+ import { isVideoPreview } from "./homepage-shared.js";
5
+ function DiscoverIcon() {
6
+ return _jsxs("svg", { viewBox: "0 0 20 20", fill: "none", stroke: "currentColor", strokeWidth: "1.7", strokeLinecap: "round", strokeLinejoin: "round", children: [_jsx("path", { d: "M10 3v4" }), _jsx("path", { d: "M10 13v4" }), _jsx("path", { d: "M3 10h4" }), _jsx("path", { d: "M13 10h4" }), _jsx("path", { d: "m5.8 5.8 2.3 2.3" }), _jsx("path", { d: "m11.9 11.9 2.3 2.3" }), _jsx("path", { d: "m14.2 5.8-2.3 2.3" }), _jsx("path", { d: "m8.1 11.9-2.3 2.3" })] });
7
+ }
8
+ function LibraryIcon() {
9
+ return _jsxs("svg", { viewBox: "0 0 20 20", fill: "none", stroke: "currentColor", strokeWidth: "1.7", strokeLinecap: "round", strokeLinejoin: "round", children: [_jsx("path", { d: "M4.5 4.5h3v11h-3z" }), _jsx("path", { d: "M8.7 4.5h3v11h-3z" }), _jsx("path", { d: "m13.4 5.1 2.3-.6 2.2 10.7-2.3.6z" })] });
10
+ }
11
+ function CalendarIcon() {
12
+ return _jsxs("svg", { viewBox: "0 0 20 20", fill: "none", stroke: "currentColor", strokeWidth: "1.7", strokeLinecap: "round", strokeLinejoin: "round", children: [_jsx("circle", { cx: "10", cy: "10", r: "7.2" }), _jsx("path", { d: "M10 6.3v4.1l2.8 1.7" })] });
13
+ }
14
+ function SettingsIcon() {
15
+ return _jsxs("svg", { viewBox: "0 0 20 20", fill: "none", stroke: "currentColor", strokeWidth: "1.7", strokeLinecap: "round", strokeLinejoin: "round", children: [_jsx("path", { d: "M14.8 15.2a5.4 5.4 0 1 0-9.6 0" }), _jsx("circle", { cx: "10", cy: "6.8", r: "3.1" })] });
16
+ }
17
+ function SearchIcon() {
18
+ return _jsxs("svg", { viewBox: "0 0 20 20", fill: "none", stroke: "currentColor", strokeWidth: "1.7", strokeLinecap: "round", strokeLinejoin: "round", children: [_jsx("circle", { cx: "8.5", cy: "8.5", r: "5.8" }), _jsx("path", { d: "m13 13 4.2 4.2" })] });
19
+ }
20
+ function BookmarkIcon() {
21
+ return _jsx("svg", { viewBox: "0 0 20 20", fill: "none", stroke: "currentColor", strokeWidth: "1.7", strokeLinecap: "round", strokeLinejoin: "round", children: _jsx("path", { d: "M6 3.8h8a1 1 0 0 1 1 1v11.4l-5-2.9-5 2.9V4.8a1 1 0 0 1 1-1Z" }) });
22
+ }
23
+ function PrimaryNav({ account }) {
24
+ const items = account.isLoggedIn
25
+ ? [
26
+ { id: "discover", href: withAccountQuery("/discover", account.userId), label: "Discover", icon: _jsx(DiscoverIcon, {}) },
27
+ { id: "library", href: withAccountQuery("/library", account.userId), label: "Library", icon: _jsx(LibraryIcon, {}) },
28
+ { id: "calendar", href: withAccountQuery("/calendar", account.userId), label: "Calendar", icon: _jsx(CalendarIcon, {}) },
29
+ { id: "settings", href: withAccountQuery("/settings", account.userId), label: "Settings", icon: _jsx(SettingsIcon, {}) }
30
+ ]
31
+ : [
32
+ { id: "discover", href: "/discover", label: "Discover", icon: _jsx(DiscoverIcon, {}) },
33
+ { id: "login", href: "/login", label: "Login", icon: _jsx(SettingsIcon, {}) }
34
+ ];
35
+ return (_jsx("nav", { className: "primary-nav", "aria-label": "Primary", children: items.map((item) => (_jsxs("a", { className: `nav-link${item.id === "discover" ? " is-active" : ""}`, href: item.href, children: [_jsx("span", { className: "nav-icon", "aria-hidden": "true", children: item.icon }), _jsx("span", { children: item.label })] }, item.id))) }));
36
+ }
37
+ function BrandLockup({ account }) {
38
+ const email = account.email?.trim() || "";
39
+ const derivedName = resolveAccountDisplayName({ displayName: account.displayName, email: account.email });
40
+ const href = withAccountQuery("/discover", account.userId);
41
+ return (_jsxs("a", { className: "brand-lockup", href: href, children: [_jsx("img", { className: "brand-logo-image", src: "/assets/logo-vidfarm.png", alt: "VidFarm AI" }), _jsxs("div", { className: "brand-copy", children: [_jsxs("div", { className: "brand-title brand-title-static", children: ["VidFarm", _jsx("span", { className: "brand-title-mobile-hidden", children: " AI" })] }), email ? (_jsxs("div", { className: "brand-account-meta", children: [derivedName, " ", _jsxs("span", { children: ["(", email, ")"] })] })) : null] })] }));
42
+ }
43
+ function CloseIcon() {
44
+ return _jsxs("svg", { viewBox: "0 0 20 20", fill: "none", stroke: "currentColor", strokeWidth: "1.8", strokeLinecap: "round", children: [_jsx("path", { d: "m5 5 10 10" }), _jsx("path", { d: "m15 5-10 10" })] });
45
+ }
46
+ function LazyVideo({ src, controls = true }) {
47
+ const ref = useRef(null);
48
+ useEffect(() => {
49
+ const video = ref.current;
50
+ if (!video || video.src) {
51
+ return;
52
+ }
53
+ const load = () => {
54
+ if (!video.dataset.src || video.getAttribute("src")) {
55
+ return;
56
+ }
57
+ video.setAttribute("src", video.dataset.src);
58
+ video.load();
59
+ };
60
+ if (!("IntersectionObserver" in window)) {
61
+ load();
62
+ return;
63
+ }
64
+ const observer = new IntersectionObserver((entries) => {
65
+ for (const entry of entries) {
66
+ if (!entry.isIntersecting) {
67
+ continue;
68
+ }
69
+ load();
70
+ observer.unobserve(entry.target);
71
+ }
72
+ }, {
73
+ rootMargin: "240px 0px"
74
+ });
75
+ observer.observe(video);
76
+ return () => observer.disconnect();
77
+ }, []);
78
+ return _jsx("video", { ref: ref, className: "preview", "data-src": src, preload: "metadata", muted: true, playsInline: true, controls: controls });
79
+ }
80
+ function CopyInlineButton({ text, label }) {
81
+ const [status, setStatus] = useState("");
82
+ const handleClick = async () => {
83
+ try {
84
+ await navigator.clipboard.writeText(text);
85
+ setStatus("done");
86
+ }
87
+ catch {
88
+ setStatus("error");
89
+ }
90
+ window.setTimeout(() => setStatus(""), 1600);
91
+ };
92
+ return (_jsxs("button", { className: "pill-tag copy-inline", type: "button", "data-state": status, "aria-label": status === "done" ? `${label} copied` : label, onClick: () => void handleClick(), children: [_jsx("span", { className: "copy-inline-label", children: label }), _jsx("span", { className: "copy-inline-feedback", "aria-hidden": "true", children: status === "error" ? "Couldn’t copy" : "Copied" })] }));
93
+ }
94
+ function CreateButton({ templateId, accountUserId }) {
95
+ const href = withAccountQuery(`/templates/${encodeURIComponent(templateId)}/editor/docs`, accountUserId);
96
+ return _jsx("a", { className: "cta-button", href: href, children: "Create" });
97
+ }
98
+ function BookmarkButton(input) {
99
+ const label = `${input.isBookmarked ? "Remove bookmark for " : "Bookmark "}${input.title}`;
100
+ return (_jsx("button", { className: "bookmark-button", type: "button", "data-active": input.isBookmarked ? "true" : "false", "aria-label": label, "aria-pressed": input.isBookmarked, title: label, onClick: () => input.onToggleBookmark?.(input.templateId), children: _jsx(BookmarkIcon, {}) }));
101
+ }
102
+ function TemplateCard(input) {
103
+ const { template } = input;
104
+ const media = template.previewUrl
105
+ ? isVideoPreview(template.previewUrl)
106
+ ? _jsx(LazyVideo, { src: template.previewUrl, controls: false })
107
+ : _jsx("img", { className: "preview", src: template.previewUrl, alt: `${template.title} preview`, loading: "lazy", decoding: "async" })
108
+ : _jsx("div", { className: "preview preview-empty", children: "No preview" });
109
+ return (_jsxs("li", { className: "discover-card row", "data-template-id": template.templateId, children: [template.previewUrl ? (_jsxs("button", { className: "discover-card-media discover-card-media-button", type: "button", "aria-label": `Open ${template.title} preview`, onClick: () => input.onOpenPreview?.(template), children: [media, _jsx("span", { className: "discover-card-media-affordance", "aria-hidden": "true", children: "View" })] })) : (_jsx("div", { className: "discover-card-media", children: media })), _jsxs("div", { className: "discover-card-copy", children: [_jsxs("div", { className: "discover-card-head", children: [_jsxs("div", { className: "discover-card-title-wrap", children: [_jsx("h2", { children: template.title }), _jsx("p", { className: "discover-card-meta", children: template.previewUrl ? "1 preview asset with editable handoff." : "Reference format with editable handoff." })] }), _jsxs("div", { className: "discover-card-actions", children: [_jsx(BookmarkButton, { templateId: template.templateId, title: template.title, isBookmarked: input.isBookmarked, onToggleBookmark: input.onToggleBookmark }), _jsx(CreateButton, { templateId: template.templateId, accountUserId: input.accountUserId })] })] }), _jsx("div", { className: "discover-card-body", children: _jsx("p", { children: String(template.viralDna || "").trim() || "No summary provided for this format yet." }) }), _jsxs("div", { className: "discover-card-tags", children: [_jsx("div", { className: "pill-tag", children: isVideoPreview(template.previewUrl) ? "Video" : "Slideshow" }), _jsx(CopyInlineButton, { text: template.templateId, label: template.templateId }), _jsx(CopyInlineButton, { text: template.slugId, label: template.slugId })] })] })] }));
110
+ }
111
+ function PreviewModal(input) {
112
+ const { onClose, template } = input;
113
+ const previewUrl = template.previewUrl || "";
114
+ const isVideo = isVideoPreview(previewUrl);
115
+ useEffect(() => {
116
+ const handleKeyDown = (event) => {
117
+ if (event.key === "Escape") {
118
+ onClose();
119
+ }
120
+ };
121
+ const previousOverflow = document.body.style.overflow;
122
+ document.body.style.overflow = "hidden";
123
+ window.addEventListener("keydown", handleKeyDown);
124
+ return () => {
125
+ document.body.style.overflow = previousOverflow;
126
+ window.removeEventListener("keydown", handleKeyDown);
127
+ };
128
+ }, [onClose]);
129
+ return (_jsx("div", { className: "discover-media-modal-backdrop", role: "presentation", onClick: onClose, children: _jsxs("div", { className: "discover-media-modal", role: "dialog", "aria-modal": "true", "aria-labelledby": "discover-media-modal-title", onClick: (event) => event.stopPropagation(), children: [_jsxs("div", { className: "discover-media-modal-head", children: [_jsx("h2", { id: "discover-media-modal-title", children: template.title }), _jsx("button", { className: "discover-media-modal-close", type: "button", "aria-label": "Close preview", onClick: onClose, children: _jsx(CloseIcon, {}) })] }), _jsx("div", { className: "discover-media-modal-stage", children: isVideo ? (_jsx("video", { src: previewUrl, controls: true, autoPlay: true, playsInline: true, preload: "metadata" })) : (_jsx("img", { src: previewUrl, alt: `${template.title} preview` })) })] }) }));
130
+ }
131
+ export function HomepageShell(input) {
132
+ const countLabel = input.error || `${input.visibleTemplates.length} visible`;
133
+ const showEmpty = !input.loading && !input.error && input.templates.length > 0 && input.visibleTemplates.length === 0;
134
+ const [previewTemplate, setPreviewTemplate] = useState(null);
135
+ useEffect(() => {
136
+ const phoneQuery = window.matchMedia("(max-width: 767px)");
137
+ const syncFilterPanelState = () => {
138
+ document.querySelectorAll(".discover-filter-panel").forEach((panel) => {
139
+ panel.open = !phoneQuery.matches;
140
+ });
141
+ };
142
+ syncFilterPanelState();
143
+ phoneQuery.addEventListener("change", syncFilterPanelState);
144
+ return () => phoneQuery.removeEventListener("change", syncFilterPanelState);
145
+ }, []);
146
+ return (_jsxs("section", { className: "frame discover-frame", children: [_jsxs("header", { className: "topbar", children: [_jsx(BrandLockup, { account: input.account }), _jsx("div", { className: "topbar-actions", children: _jsx(PrimaryNav, { account: input.account }) })] }), _jsx("div", { className: "frame-body", children: _jsxs("section", { className: "discover-shell", children: [_jsx("aside", { className: "discover-sidebar", children: _jsxs("details", { className: "discover-filter-panel", open: true, children: [_jsx("summary", { children: _jsx("span", { children: "Filters" }) }), _jsxs("div", { className: "discover-filter-panel-body", children: [_jsxs("div", { className: "discover-filter-block", children: [_jsx("div", { className: "label", children: "Filters" }), _jsx("p", { children: "Keep the controls pinned here and use the extra height to roam the catalog." })] }), _jsxs("div", { className: "discover-filter-group", children: [_jsx("label", { className: "label", htmlFor: "search", children: "Search formats" }), _jsxs("div", { className: "search-field", children: [_jsx("span", { className: "search-icon", "aria-hidden": "true", children: _jsx(SearchIcon, {}) }), _jsx("input", { id: "search", type: "search", placeholder: "Search templates, hooks, media styles", autoComplete: "off", value: input.searchQuery, onChange: (event) => input.onSearchQueryChange?.(event.target.value) })] })] }), _jsxs("div", { className: "discover-filter-group", children: [_jsx("label", { className: "label", htmlFor: "media-filter", children: "Media type" }), _jsx("select", { id: "media-filter", disabled: true, children: _jsx("option", { children: "All formats" }) })] }), _jsxs("div", { className: "discover-filter-group", children: [_jsx("label", { className: "label", children: "Shortlist" }), _jsxs("div", { className: "bookmarks-row", children: [_jsx("label", { className: "switch-label", htmlFor: "bookmarks-only", children: "Bookmarks only" }), _jsxs("label", { className: "switch-shell", htmlFor: "bookmarks-only", "data-active": input.bookmarksOnly ? "true" : "false", children: [_jsx("input", { id: "bookmarks-only", className: "switch-input", type: "checkbox", checked: input.bookmarksOnly, onChange: (event) => input.onBookmarksOnlyChange?.(event.target.checked) }), _jsx("span", { className: "switch-knob" })] })] })] })] })] }) }), _jsx("section", { className: "discover-results", children: _jsxs("div", { className: "discover-results-scroll", children: [_jsx("div", { className: "discover-count", id: "count", children: input.loading ? "Loading formats..." : countLabel }), _jsxs("div", { className: "discover-list-scroll", children: [_jsx("ul", { className: "discover-list", id: "template-list", children: input.loading ? (_jsx("li", { className: "empty-state discover-loading-card", children: "Loading formats..." })) : input.error ? (_jsx("li", { className: "empty-state discover-loading-card", children: "Unable to load formats." })) : input.templates.length === 0 ? (_jsx("li", { className: "empty-state discover-loading-card", children: "No formats available." })) : (input.visibleTemplates.map((template) => (_jsx(TemplateCard, { template: template, isBookmarked: input.bookmarks.has(template.templateId), onToggleBookmark: input.onToggleBookmark, onOpenPreview: setPreviewTemplate, accountUserId: input.account.userId }, template.templateId)))) }), _jsx("div", { className: "empty-state empty", id: "empty-state", "data-visible": showEmpty ? "true" : "false", children: "No formats match the current search and filter combination." })] })] }) })] }) }), previewTemplate ? _jsx(PreviewModal, { template: previewTemplate, onClose: () => setPreviewTemplate(null) }) : null] }));
147
+ }
@@ -0,0 +1,100 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { useEffect, useMemo, useState } from "react";
3
+ import { createPortal } from "react-dom";
4
+ import { createRoot } from "react-dom/client";
5
+ import { useStore } from "zustand";
6
+ import { createFlockPosterCacheStore, readFlockPosterCacheSnapshot } from "./flockposter-cache-store.js";
7
+ import { createPageRuntimeStore } from "./page-runtime-store.js";
8
+ import { TemplateEditorChat } from "./template-editor-chat.js";
9
+ function readBoot() {
10
+ const element = document.getElementById("page-runtime-boot");
11
+ if (!element?.textContent) {
12
+ return null;
13
+ }
14
+ return JSON.parse(element.textContent);
15
+ }
16
+ function isoDaysFromNow(days) {
17
+ const date = new Date();
18
+ date.setDate(date.getDate() + days);
19
+ return date.toISOString();
20
+ }
21
+ async function hydrateFlockPosterCacheAtStartup(input) {
22
+ if (!input.userId) {
23
+ return;
24
+ }
25
+ const current = input.store.getState();
26
+ if (!input.force && current.channels.length && current.channelsUpdatedAt) {
27
+ return;
28
+ }
29
+ const params = new URLSearchParams({
30
+ start_date: isoDaysFromNow(-7),
31
+ end_date: isoDaysFromNow(30)
32
+ });
33
+ const response = await fetch(`/u/${encodeURIComponent(input.userId)}/calendar/feed?${params.toString()}`, {
34
+ headers: { accept: "application/json" }
35
+ });
36
+ const payload = await response.json().catch(() => null);
37
+ if (!response.ok || !payload) {
38
+ return;
39
+ }
40
+ input.store.getState().setCalendarData({
41
+ channels: Array.isArray(payload.channels) ? payload.channels : [],
42
+ posts: Array.isArray(payload.posts) ? payload.posts : [],
43
+ rangeStart: payload.startDate || isoDaysFromNow(-7),
44
+ rangeEnd: payload.endDate || isoDaysFromNow(30),
45
+ replaceRange: true
46
+ });
47
+ window.dispatchEvent(new CustomEvent("vidfarm:flockposter-cache-updated"));
48
+ }
49
+ function PageRuntimeApp({ boot }) {
50
+ const store = useMemo(() => createPageRuntimeStore(boot), [boot]);
51
+ const title = useStore(store, (state) => state.boot.title);
52
+ const [chatRoot, setChatRoot] = useState(null);
53
+ useEffect(() => {
54
+ store.getState().setHydratedAt(new Date().toISOString());
55
+ document.documentElement.dataset.reactRuntime = "true";
56
+ document.documentElement.dataset.reactRuntimePage = boot.pageKind;
57
+ document.title = title;
58
+ }, [boot.pageKind, store, title]);
59
+ useEffect(() => {
60
+ if (!boot.editorChat) {
61
+ return;
62
+ }
63
+ const nextRoot = document.querySelector("[data-template-editor-chat-root]");
64
+ setChatRoot(nextRoot instanceof HTMLElement ? nextRoot : null);
65
+ }, [boot.editorChat]);
66
+ return boot.editorChat && chatRoot
67
+ ? createPortal(_jsx(TemplateEditorChat, { boot: boot.editorChat }), chatRoot)
68
+ : null;
69
+ }
70
+ const boot = readBoot();
71
+ const rootElement = document.getElementById("page-runtime-root");
72
+ const flockPosterCacheStore = createFlockPosterCacheStore(boot?.userId ?? null);
73
+ let flockPosterHydrationPromise = null;
74
+ function ensureFlockPosterCacheHydrated(options = {}) {
75
+ if (!options.force && flockPosterHydrationPromise) {
76
+ return flockPosterHydrationPromise;
77
+ }
78
+ flockPosterHydrationPromise = hydrateFlockPosterCacheAtStartup({
79
+ userId: boot?.userId ?? null,
80
+ store: flockPosterCacheStore,
81
+ force: options.force
82
+ }).catch(() => { });
83
+ return flockPosterHydrationPromise;
84
+ }
85
+ window.__vidfarmFlockPosterCache = {
86
+ readSnapshot: () => readFlockPosterCacheSnapshot(boot?.userId ?? null),
87
+ ensureHydrated: ensureFlockPosterCacheHydrated,
88
+ setChannelsData: (channels) => {
89
+ flockPosterCacheStore.getState().setChannelsData(channels);
90
+ window.dispatchEvent(new CustomEvent("vidfarm:flockposter-cache-updated"));
91
+ },
92
+ setCalendarData: (input) => {
93
+ flockPosterCacheStore.getState().setCalendarData(input);
94
+ window.dispatchEvent(new CustomEvent("vidfarm:flockposter-cache-updated"));
95
+ }
96
+ };
97
+ void ensureFlockPosterCacheHydrated();
98
+ if (boot && rootElement) {
99
+ createRoot(rootElement).render(_jsx(PageRuntimeApp, { boot: boot }));
100
+ }
@@ -0,0 +1,8 @@
1
+ import { createStore } from "zustand/vanilla";
2
+ export function createPageRuntimeStore(boot) {
3
+ return createStore((set) => ({
4
+ boot,
5
+ hydratedAt: null,
6
+ setHydratedAt: (hydratedAt) => set({ hydratedAt })
7
+ }));
8
+ }