@mevdragon/vidfarm-devcli 0.16.0 → 0.17.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/.agents/skills/farmville-saas-ux/SKILL.md +156 -0
- package/.agents/skills/farmville-saas-ux/assets/starter.html +294 -0
- package/.agents/skills/farmville-saas-ux/references/components.md +340 -0
- package/.agents/skills/farmville-saas-ux/references/porting-guide.md +121 -0
- package/.agents/skills/farmville-saas-ux/references/tokens.md +271 -0
- package/.agents/skills/vidfarm-media/SKILL.md +2 -1
- package/.agents/skills/vidfarm-media/references/tts.md +20 -1
- package/dist/src/account-pages-legacy.js +2 -2
- package/dist/src/app.js +441 -77
- package/dist/src/editor-chat.js +2 -2
- package/dist/src/frontend/homepage-client.js +162 -2
- package/dist/src/frontend/homepage-store.js +30 -1
- package/dist/src/frontend/homepage-view.js +111 -4
- package/dist/src/homepage.js +184 -1
- package/dist/src/landing-page.js +367 -0
- package/dist/src/primitive-registry.js +278 -2
- package/dist/src/reskin/agency-page.js +255 -0
- package/dist/src/reskin/calendar-page.js +252 -0
- package/dist/src/reskin/chat-page.js +414 -0
- package/dist/src/reskin/discover-page.js +380 -0
- package/dist/src/reskin/document.js +74 -0
- package/dist/src/reskin/help-page.js +318 -0
- package/dist/src/reskin/index-page.js +62 -0
- package/dist/src/reskin/job-runs-page.js +249 -0
- package/dist/src/reskin/library-page.js +515 -0
- package/dist/src/reskin/login-page.js +225 -0
- package/dist/src/reskin/pricing-page.js +359 -0
- package/dist/src/reskin/settings-page.js +353 -0
- package/dist/src/reskin/theme.js +265 -0
- package/dist/src/services/serverless-records.js +54 -0
- package/dist/src/services/swipe-customize.js +434 -0
- package/package.json +1 -1
- package/public/assets/homepage-client-app.js +22 -22
|
@@ -0,0 +1,515 @@
|
|
|
1
|
+
// /reskin/library — the director's own studio, ported into the farmville design
|
|
2
|
+
// system. Mirrors src/account-pages-legacy.ts → renderLibraryPage + the clip
|
|
3
|
+
// gallery (renderClipsPage). This is now ONE page with two server-routed tabs:
|
|
4
|
+
// /reskin/library/approved → your projects, renders & ready-to-post cuts
|
|
5
|
+
// /reskin/library/clips → long-form videos mined into tagged short clips
|
|
6
|
+
//
|
|
7
|
+
// Renders with representative SAMPLE data so it can be viewed without auth. The
|
|
8
|
+
// tab toggle is real <a> links (each tab is its own URL); card actions point at
|
|
9
|
+
// the real routes (/editor, /job-runs, /calendar, /discover, /approved/posts/…).
|
|
10
|
+
// Everything visual is built on the shared rk-* primitives + a page-scoped
|
|
11
|
+
// rk-library-* / rk-clips-* CSS block.
|
|
12
|
+
import { reskinDocument, rkEscape } from "./document.js";
|
|
13
|
+
const PROJECTS = [
|
|
14
|
+
{ id: "proj_9c41f0", title: "Swipe-mode ad — DTC skincare", emoji: "🧴", grad: "coral", status: "ready", originKind: "inspiration", originLabel: "Punchy Hook 15s", duration: "0:15", when: "Rendered 2h ago" },
|
|
15
|
+
{ id: "proj_7a12bd", title: "Founder VO — “ship a week in an afternoon”", emoji: "🎙️", grad: "honey", status: "rendering", originKind: "raw", originLabel: "Raw upload", duration: "0:42", when: "Started 4m ago", progress: 64 },
|
|
16
|
+
{ id: "proj_3fd80a", title: "UGC unboxing recut #3", emoji: "📦", grad: "sky", status: "draft", originKind: "inspiration", originLabel: "Unboxing Energy", duration: "0:22", when: "Edited yesterday" },
|
|
17
|
+
{ id: "proj_c19e44", title: "Honey-gold pricing explainer", emoji: "💳", grad: "violet", status: "scheduled", originKind: "inspiration", originLabel: "Explainer Grid", duration: "0:38", when: "Posts Jul 9, 9:00 AM" },
|
|
18
|
+
{ id: "proj_66b2c1", title: "TikTok hook A/B — captions pop", emoji: "💬", grad: "green", status: "ready", originKind: "inspiration", originLabel: "Caption Punch", duration: "0:12", when: "Rendered Jul 6" },
|
|
19
|
+
{ id: "proj_15aa09", title: "Clip mine → 15s teaser", emoji: "✂️", grad: "sky", status: "draft", originKind: "raw", originLabel: "Raw clips", duration: "0:15", when: "Edited 3d ago" },
|
|
20
|
+
{ id: "proj_ken042", title: "Ken Burns product hero", emoji: "🖼️", grad: "honey", status: "posted", originKind: "inspiration", originLabel: "Still-to-Motion", duration: "0:18", when: "Posted Jul 4 · @vidfarm", href: "/approved/posts/proj_ken042" },
|
|
21
|
+
{ id: "proj_news77", title: "Newsletter drop cut", emoji: "📮", grad: "coral", status: "posted", originKind: "raw", originLabel: "Raw upload", duration: "0:30", when: "Posted Jul 1 · Email", href: "/approved/posts/proj_news77" },
|
|
22
|
+
{ id: "proj_sizzle", title: "Sunny SaaS sizzle reel", emoji: "☀️", grad: "ink", status: "rendering", originKind: "inspiration", originLabel: "Brand Sizzle", duration: "1:04", when: "Started 12m ago", progress: 31 },
|
|
23
|
+
{ id: "proj_prompt5", title: "Prompt-gen: “costco for business apps”", emoji: "✨", grad: "violet", status: "draft", originKind: "raw", originLabel: "Prompt-gen", duration: "0:20", when: "Created 5d ago" }
|
|
24
|
+
];
|
|
25
|
+
const GRAD = {
|
|
26
|
+
honey: "radial-gradient(120% 78% at 22% 4%,rgba(255,255,255,.6),transparent 55%),linear-gradient(158deg,#ffe9cc,#ffc738 96%)",
|
|
27
|
+
coral: "radial-gradient(120% 78% at 22% 4%,rgba(255,255,255,.5),transparent 55%),linear-gradient(158deg,#ffe1c2,#f97316 98%)",
|
|
28
|
+
sky: "radial-gradient(120% 78% at 22% 4%,rgba(255,255,255,.58),transparent 55%),linear-gradient(158deg,#e2effd,#3f9fe0 98%)",
|
|
29
|
+
violet: "radial-gradient(120% 78% at 22% 4%,rgba(255,255,255,.5),transparent 55%),linear-gradient(158deg,#ece4fd,#9f5bde 98%)",
|
|
30
|
+
green: "radial-gradient(120% 78% at 22% 4%,rgba(255,255,255,.5),transparent 55%),linear-gradient(158deg,#dcfce7,#22c55e 98%)",
|
|
31
|
+
ink: "radial-gradient(120% 78% at 22% 4%,rgba(255,255,255,.18),transparent 55%),linear-gradient(158deg,#3f3f46,#171717 98%)"
|
|
32
|
+
};
|
|
33
|
+
const STATUS = {
|
|
34
|
+
draft: { short: "Draft", pill: "rk-pill" },
|
|
35
|
+
rendering: { short: "Rendering", pill: "rk-pill-sky" },
|
|
36
|
+
ready: { short: "Ready", pill: "rk-pill-green" },
|
|
37
|
+
scheduled: { short: "Scheduled", pill: "rk-pill-violet" },
|
|
38
|
+
posted: { short: "Posted", pill: "rk-pill-gold" }
|
|
39
|
+
};
|
|
40
|
+
const CHIPS = [
|
|
41
|
+
["", "All", ""],
|
|
42
|
+
["draft", "Drafts", "draft"],
|
|
43
|
+
["rendering", "Rendering", "rendering"],
|
|
44
|
+
["ready", "Ready to post", "ready"],
|
|
45
|
+
["scheduled", "Scheduled", "scheduled"],
|
|
46
|
+
["posted", "Posted", "posted"]
|
|
47
|
+
];
|
|
48
|
+
const PLAY = `<svg width="18" height="18" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M8 5.5v13l11-6.5-11-6.5Z"/></svg>`;
|
|
49
|
+
function count(status) {
|
|
50
|
+
return status === "" ? PROJECTS.length : PROJECTS.filter((p) => p.status === status).length;
|
|
51
|
+
}
|
|
52
|
+
function cardAction(p) {
|
|
53
|
+
switch (p.status) {
|
|
54
|
+
case "draft":
|
|
55
|
+
return `<a class="rk-btn rk-btn-ink rk-btn-sm" href="/editor">Open <span class="rk-arrow">→</span></a>`;
|
|
56
|
+
case "rendering":
|
|
57
|
+
return `<a class="rk-btn rk-btn-ghost rk-btn-sm" href="/job-runs">Track render</a>`;
|
|
58
|
+
case "ready":
|
|
59
|
+
return `<a class="rk-btn rk-btn-gold rk-btn-sm" href="/calendar">Schedule <span class="rk-arrow">→</span></a>`;
|
|
60
|
+
case "scheduled":
|
|
61
|
+
return `<a class="rk-btn rk-btn-ghost rk-btn-sm" href="/calendar">Reschedule</a>`;
|
|
62
|
+
case "posted":
|
|
63
|
+
return `<a class="rk-btn rk-btn-ghost rk-btn-sm" href="${rkEscape(p.href || "#")}" target="_blank" rel="noreferrer">View <span class="rk-arrow">→</span></a>`;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
function originRow(p) {
|
|
67
|
+
if (p.originKind === "inspiration") {
|
|
68
|
+
return `<div class="rk-library-origin">
|
|
69
|
+
<span class="rk-library-origin-txt">From “${rkEscape(p.originLabel)}”</span>
|
|
70
|
+
</div>`;
|
|
71
|
+
}
|
|
72
|
+
return `<div class="rk-library-origin">
|
|
73
|
+
<span class="rk-library-origin-txt">${rkEscape(p.originLabel)} · Your project</span>
|
|
74
|
+
</div>`;
|
|
75
|
+
}
|
|
76
|
+
function projectCard(p) {
|
|
77
|
+
const s = STATUS[p.status];
|
|
78
|
+
const dotCls = p.status === "rendering" ? " is-pulse" : "";
|
|
79
|
+
const progress = p.status === "rendering"
|
|
80
|
+
? `<span class="rk-library-prog"><span class="rk-library-prog-fill" style="width:${Math.max(6, Math.min(100, p.progress ?? 40))}%"></span></span>`
|
|
81
|
+
: "";
|
|
82
|
+
return `<article class="rk-library-card rk-card-hover" data-status="${p.status}" data-title="${rkEscape(p.title.toLowerCase())}">
|
|
83
|
+
<a class="rk-library-poster" style="background:${GRAD[p.grad]}" href="/editor" aria-label="${rkEscape(p.title)}">
|
|
84
|
+
<span class="rk-library-emoji">${p.emoji}</span>
|
|
85
|
+
<span class="rk-pill ${s.pill} rk-library-pstatus"><span class="rk-library-dot${dotCls}"></span>${rkEscape(s.short)}</span>
|
|
86
|
+
<span class="rk-library-dur">${rkEscape(p.duration)} · 9:16</span>
|
|
87
|
+
<span class="rk-library-play">${PLAY}</span>
|
|
88
|
+
${progress}
|
|
89
|
+
</a>
|
|
90
|
+
<div class="rk-library-meta">
|
|
91
|
+
<h3 class="rk-library-title">${rkEscape(p.title)}</h3>
|
|
92
|
+
${originRow(p)}
|
|
93
|
+
<div class="rk-spread rk-library-foot">
|
|
94
|
+
<span class="rk-library-time">${rkEscape(p.when)}</span>
|
|
95
|
+
${cardAction(p)}
|
|
96
|
+
</div>
|
|
97
|
+
</div>
|
|
98
|
+
</article>`;
|
|
99
|
+
}
|
|
100
|
+
function statTile(emoji, num, label) {
|
|
101
|
+
return `<div class="rk-library-stat">
|
|
102
|
+
<span class="rk-tile rk-tile-lg rk-tile-gold">${emoji}</span>
|
|
103
|
+
<div class="rk-stat">
|
|
104
|
+
<div class="rk-library-stat-num">${rkEscape(num)}</div>
|
|
105
|
+
<div class="rk-library-stat-label">${rkEscape(label)}</div>
|
|
106
|
+
</div>
|
|
107
|
+
</div>`;
|
|
108
|
+
}
|
|
109
|
+
function approvedPanel() {
|
|
110
|
+
const chips = CHIPS.map(([value, label], i) => `<button type="button" class="rk-library-chip${i === 0 ? " is-active" : ""}" data-rk-filter="${value}">${rkEscape(label)}</button>`).join("");
|
|
111
|
+
const newTile = `<a class="rk-library-new" data-rk-newtile href="/discover">
|
|
112
|
+
<span class="rk-tile rk-tile-lg rk-tile-gold rk-library-new-plus">+</span>
|
|
113
|
+
<div>
|
|
114
|
+
<div class="rk-library-new-title">New project</div>
|
|
115
|
+
<div class="rk-library-new-sub">Fork a template or start from a prompt</div>
|
|
116
|
+
</div>
|
|
117
|
+
</a>`;
|
|
118
|
+
const cards = PROJECTS.map(projectCard).join("");
|
|
119
|
+
return `
|
|
120
|
+
<div class="rk-library-summary"><b>${PROJECTS.length}</b> projects · <b>${count("rendering")}</b> rendering · <b>${count("ready")}</b> ready to post · <b>${count("posted")}</b> posted this week</div>
|
|
121
|
+
|
|
122
|
+
<div class="rk-library-toolbar">
|
|
123
|
+
<div class="rk-library-chips" role="tablist" aria-label="Filter by status">${chips}</div>
|
|
124
|
+
<input class="rk-input rk-library-search" data-rk-search type="search" placeholder="Search projects…" autocomplete="off" aria-label="Search projects">
|
|
125
|
+
</div>
|
|
126
|
+
|
|
127
|
+
<div class="rk-library-grid" data-rk-grid>
|
|
128
|
+
${newTile}
|
|
129
|
+
${cards}
|
|
130
|
+
</div>
|
|
131
|
+
|
|
132
|
+
<div class="rk-library-empty" data-rk-empty>
|
|
133
|
+
<h3>Nothing here yet</h3>
|
|
134
|
+
<p class="rk-muted">No projects match this filter. Try another status or spin up a fresh cut.</p>
|
|
135
|
+
<a class="rk-btn rk-btn-gold" href="/discover">Start a project <span class="rk-arrow">→</span></a>
|
|
136
|
+
</div>`;
|
|
137
|
+
}
|
|
138
|
+
const CLIP_DATA = {
|
|
139
|
+
stats: { clips: 128, sources: 14, minutes: 42, taggedPct: 100 },
|
|
140
|
+
filters: [
|
|
141
|
+
{ slug: "all", label: "All clips" },
|
|
142
|
+
{ slug: "reactions", label: "Reactions" },
|
|
143
|
+
{ slug: "hooks", label: "Hooks & hot takes" },
|
|
144
|
+
{ slug: "money", label: "Money shots" },
|
|
145
|
+
{ slug: "talking", label: "Talking head" },
|
|
146
|
+
{ slug: "broll", label: "B-roll" },
|
|
147
|
+
{ slug: "establishing", label: "Establishing" }
|
|
148
|
+
],
|
|
149
|
+
clips: [
|
|
150
|
+
{ title: "Guest freezes mid-sentence after the host's cold-read stat", durationSec: 18, aspect: "9:16", score: 94, source: "The Founder Hour — Ep. 212", tags: ["reaction", "close-up", "high energy"], glyph: "😮", tone: "gold", cat: "reactions" },
|
|
151
|
+
{ title: "“That number can't be real” — investor's slow double-take", durationSec: 19, aspect: "9:16", score: 93, source: "Contrarian Capital — Ep. 11", tags: ["reaction", "surprise", "close-up"], glyph: "😳", tone: "coral", cat: "reactions" },
|
|
152
|
+
{ title: "The “nobody talks about this” hook that opened the episode", durationSec: 22, aspect: "9:16", score: 96, source: "Contrarian Capital — Ep. 09", tags: ["hook", "hot take", "talking"], glyph: "🎤", tone: "ink", cat: "hooks" },
|
|
153
|
+
{ title: "Hands counting a stack of hundreds on the desk", durationSec: 15, aspect: "9:16", score: 88, source: "Cashflow Diaries — Ep. 17", tags: ["money", "hands", "macro"], glyph: "💵", tone: "green", cat: "money" },
|
|
154
|
+
{ title: "Founder tears up recalling the very first sale", durationSec: 31, aspect: "9:16", score: 90, source: "The Founder Hour — Ep. 205", tags: ["emotion", "close-up", "calm"], glyph: "🥹", tone: "violet", cat: "talking" },
|
|
155
|
+
{ title: "Slow push-in on the neon “SOLD OUT” window sign", durationSec: 12, aspect: "16:9", score: 78, source: "Retail Nerds — Ep. 40", tags: ["b-roll", "establishing", "calm"], glyph: "🪧", tone: "sky", cat: "broll" },
|
|
156
|
+
{ title: "City skyline timelapse at golden hour", durationSec: 14, aspect: "16:9", score: 74, source: "Built Different — Ep. 51", tags: ["establishing", "b-roll", "wide"], glyph: "🌇", tone: "coral", cat: "establishing" },
|
|
157
|
+
{ title: "Whiteboard reveal: the four-step funnel, drawn live", durationSec: 27, aspect: "16:9", score: 82, source: "Growth Loops — Ep. 63", tags: ["explainer", "talking", "medium"], glyph: "📊", tone: "sky", cat: "talking" },
|
|
158
|
+
{ title: "Barista latte-art pour in extreme slow-mo", durationSec: 16, aspect: "9:16", score: 80, source: "Small Shop Big Dreams — Ep. 30", tags: ["b-roll", "macro", "calm"], glyph: "☕", tone: "gold", cat: "broll" },
|
|
159
|
+
{ title: "Coins dropping into a glass jar, tight macro", durationSec: 13, aspect: "9:16", score: 85, source: "Cashflow Diaries — Ep. 21", tags: ["money", "macro", "sfx"], glyph: "🪙", tone: "green", cat: "money" },
|
|
160
|
+
{ title: "He didn't realize the camera was still rolling", durationSec: 24, aspect: "9:16", score: 91, source: "Lightbulb Pod — Ep. 88", tags: ["reaction", "candid", "high energy"], glyph: "😅", tone: "violet", cat: "reactions" },
|
|
161
|
+
{ title: "The mic-drop closing line, right before the outro", durationSec: 21, aspect: "16:9", score: 89, source: "Lightbulb Pod — Ep. 90", tags: ["hook", "hot take", "talking"], glyph: "🎙", tone: "ink", cat: "hooks" }
|
|
162
|
+
]
|
|
163
|
+
};
|
|
164
|
+
const SEARCH_ICON = `<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="7"/><path d="m20 20-3.5-3.5"/></svg>`;
|
|
165
|
+
function fmtClock(sec) {
|
|
166
|
+
const m = Math.floor(sec / 60);
|
|
167
|
+
const s = Math.floor(sec % 60);
|
|
168
|
+
return `${m}:${s < 10 ? "0" : ""}${s}`;
|
|
169
|
+
}
|
|
170
|
+
function clipCard(clip) {
|
|
171
|
+
const tagLine = clip.tags.slice(0, 3).map((t) => rkEscape(t)).join(" · ");
|
|
172
|
+
return `
|
|
173
|
+
<article class="rk-clips-card rk-card-hover" data-cat="${rkEscape(clip.cat)}">
|
|
174
|
+
<div class="rk-clips-thumb rk-clips-g-${clip.tone}">
|
|
175
|
+
<span class="rk-clips-glyph" aria-hidden="true">${clip.glyph}</span>
|
|
176
|
+
<span class="rk-clips-dur">${fmtClock(clip.durationSec)}</span>
|
|
177
|
+
<button type="button" class="rk-clips-play" aria-label="Preview clip">▶</button>
|
|
178
|
+
</div>
|
|
179
|
+
<div class="rk-clips-body">
|
|
180
|
+
<h3 class="rk-clips-title">${rkEscape(clip.title)}</h3>
|
|
181
|
+
<div class="rk-clips-tagline">${tagLine}</div>
|
|
182
|
+
<div class="rk-clips-foot">
|
|
183
|
+
<span class="rk-clips-src" title="${rkEscape(clip.source)}">${rkEscape(clip.source)}</span>
|
|
184
|
+
<a class="rk-clips-dl" href="#">Download</a>
|
|
185
|
+
</div>
|
|
186
|
+
</div>
|
|
187
|
+
</article>`;
|
|
188
|
+
}
|
|
189
|
+
function clipsPanel() {
|
|
190
|
+
const s = CLIP_DATA;
|
|
191
|
+
const filterChips = s.filters.map((f, i) => `<button type="button" class="rk-clips-chip${i === 0 ? " is-active" : ""}" data-filter="${rkEscape(f.slug)}">${rkEscape(f.label)}</button>`).join("");
|
|
192
|
+
const cards = s.clips.map(clipCard).join("");
|
|
193
|
+
return `
|
|
194
|
+
<div class="rk-library-summary"><b>${s.stats.clips}</b> clips · <b>${s.stats.sources}</b> sources · <b>${s.stats.minutes}</b> min mined · <b>${s.stats.taggedPct}%</b> tagged</div>
|
|
195
|
+
|
|
196
|
+
<div class="rk-clips-toolbar">
|
|
197
|
+
<div class="rk-clips-toolbar-top">
|
|
198
|
+
<form class="rk-clips-search" id="rkClipsSearch">
|
|
199
|
+
<span class="rk-clips-search-ic">${SEARCH_ICON}</span>
|
|
200
|
+
<input class="rk-input rk-clips-search-input" id="rkClipsQuery" type="search"
|
|
201
|
+
placeholder="Search clips — e.g. "someone looks confused after reading a message"" autocomplete="off" />
|
|
202
|
+
<button class="rk-btn rk-btn-ink rk-btn-sm rk-clips-search-btn" type="submit">Search</button>
|
|
203
|
+
</form>
|
|
204
|
+
<button type="button" class="rk-btn rk-btn-gold rk-clips-scan" id="rkClipsScanBtn">Scan a source</button>
|
|
205
|
+
</div>
|
|
206
|
+
<div class="rk-clips-chips" id="rkClipsChips">${filterChips}</div>
|
|
207
|
+
<div class="rk-clips-status" id="rkClipsStatus">Showing all ${s.clips.length} clips from ${s.stats.sources} sources</div>
|
|
208
|
+
</div>
|
|
209
|
+
|
|
210
|
+
<div class="rk-clips-grid" id="rkClipsGrid">${cards}</div>
|
|
211
|
+
|
|
212
|
+
<div class="rk-clips-empty" id="rkClipsEmpty" hidden>
|
|
213
|
+
<h3>No clips match that filter yet</h3>
|
|
214
|
+
<p class="rk-muted">Try another tag, or scan a new source to grow your library.</p>
|
|
215
|
+
</div>`;
|
|
216
|
+
}
|
|
217
|
+
const CLIPS_DIALOG = `
|
|
218
|
+
<dialog class="rk-clips-dialog" id="rkClipsDialog">
|
|
219
|
+
<form class="rk-clips-dialog-form" id="rkClipsScanForm" method="dialog">
|
|
220
|
+
<div class="rk-card-head">
|
|
221
|
+
<span class="rk-eyebrow">Import source</span>
|
|
222
|
+
<h2>Mine a video into clips</h2>
|
|
223
|
+
<p class="rk-muted">Paste a video URL — TikTok, YouTube, Instagram, X, or a direct link. We'll scan it and add the matching moments to your clip library.</p>
|
|
224
|
+
</div>
|
|
225
|
+
<div class="rk-field">
|
|
226
|
+
<label class="rk-label" for="rkScanUrl">Video URL</label>
|
|
227
|
+
<input class="rk-input" id="rkScanUrl" name="url" type="url" placeholder="https://youtube.com/watch?v=… or https://…/video.mp4" autocomplete="off" />
|
|
228
|
+
</div>
|
|
229
|
+
<div class="rk-field">
|
|
230
|
+
<label class="rk-label" for="rkScanPrompt">What to clip for</label>
|
|
231
|
+
<textarea class="rk-textarea" id="rkScanPrompt" name="prompt" placeholder="Clip scenes of people reacting, no text on screen, vertical, between 5–10 secs long"></textarea>
|
|
232
|
+
<span class="rk-hint">Guides the AI tagger toward the moments you actually want.</span>
|
|
233
|
+
</div>
|
|
234
|
+
<div class="rk-grid rk-grid-2 rk-clips-dialog-grid">
|
|
235
|
+
<div class="rk-field">
|
|
236
|
+
<label class="rk-label" for="rkScanProvider">AI provider</label>
|
|
237
|
+
<select class="rk-select" id="rkScanProvider" name="provider">
|
|
238
|
+
<option value="">Auto — use my saved keys</option>
|
|
239
|
+
<option value="gemini">Gemini</option>
|
|
240
|
+
<option value="openai">OpenAI</option>
|
|
241
|
+
<option value="openrouter">OpenRouter</option>
|
|
242
|
+
</select>
|
|
243
|
+
</div>
|
|
244
|
+
<div class="rk-field">
|
|
245
|
+
<label class="rk-label" for="rkScanTracer">Tracer <span class="rk-hint">optional</span></label>
|
|
246
|
+
<input class="rk-input" id="rkScanTracer" name="tracer" type="text" placeholder="my-campaign-001" autocomplete="off" />
|
|
247
|
+
</div>
|
|
248
|
+
</div>
|
|
249
|
+
<p class="rk-hint rk-clips-dialog-note">AI tagging runs on your own key (BYOK) — vidfarm only bills the clip-scan compute.</p>
|
|
250
|
+
<div class="rk-clips-dialog-actions">
|
|
251
|
+
<button type="button" class="rk-btn rk-btn-ghost" id="rkClipsScanCancel">Cancel</button>
|
|
252
|
+
<button type="submit" class="rk-btn rk-btn-gold">Start scan <span class="rk-arrow">→</span></button>
|
|
253
|
+
</div>
|
|
254
|
+
</form>
|
|
255
|
+
</dialog>`;
|
|
256
|
+
/* ──────────────────────────────── Page shell ──────────────────────────────── */
|
|
257
|
+
const APPROVED_SCRIPT = `
|
|
258
|
+
(function(){
|
|
259
|
+
var root=document;
|
|
260
|
+
var grid=root.querySelector('[data-rk-grid]');
|
|
261
|
+
var empty=root.querySelector('[data-rk-empty]');
|
|
262
|
+
var chips=Array.prototype.slice.call(root.querySelectorAll('[data-rk-filter]'));
|
|
263
|
+
var newTile=root.querySelector('[data-rk-newtile]');
|
|
264
|
+
var search=root.querySelector('[data-rk-search]');
|
|
265
|
+
var cards=Array.prototype.slice.call(root.querySelectorAll('.rk-library-card'));
|
|
266
|
+
var activeFilter='';
|
|
267
|
+
function apply(){
|
|
268
|
+
var q=(search&&search.value||'').trim().toLowerCase();
|
|
269
|
+
var shown=0;
|
|
270
|
+
cards.forEach(function(card){
|
|
271
|
+
var okStatus=activeFilter===''||card.getAttribute('data-status')===activeFilter;
|
|
272
|
+
var okSearch=!q||(card.getAttribute('data-title')||'').indexOf(q)!==-1;
|
|
273
|
+
var visible=okStatus&&okSearch;
|
|
274
|
+
card.hidden=!visible;
|
|
275
|
+
if(visible)shown++;
|
|
276
|
+
});
|
|
277
|
+
if(newTile)newTile.hidden=!(activeFilter===''&&!q);
|
|
278
|
+
if(empty)empty.classList.toggle('is-visible',shown===0);
|
|
279
|
+
}
|
|
280
|
+
chips.forEach(function(btn){
|
|
281
|
+
btn.addEventListener('click',function(){
|
|
282
|
+
activeFilter=btn.getAttribute('data-rk-filter')||'';
|
|
283
|
+
chips.forEach(function(b){b.classList.toggle('is-active',b===btn)});
|
|
284
|
+
apply();
|
|
285
|
+
});
|
|
286
|
+
});
|
|
287
|
+
if(search)search.addEventListener('input',apply);
|
|
288
|
+
apply();
|
|
289
|
+
})();`;
|
|
290
|
+
const CLIPS_SCRIPT = `
|
|
291
|
+
(function(){
|
|
292
|
+
var root=document;
|
|
293
|
+
var chips=root.getElementById('rkClipsChips');
|
|
294
|
+
var grid=root.getElementById('rkClipsGrid');
|
|
295
|
+
var statusEl=root.getElementById('rkClipsStatus');
|
|
296
|
+
var empty=root.getElementById('rkClipsEmpty');
|
|
297
|
+
var cards=grid?Array.prototype.slice.call(grid.querySelectorAll('.rk-clips-card')):[];
|
|
298
|
+
var total=cards.length;
|
|
299
|
+
function labelFor(slug){
|
|
300
|
+
var btn=chips&&chips.querySelector('[data-filter="'+slug+'"]');
|
|
301
|
+
return btn?btn.textContent.replace(/^[^A-Za-z0-9]+/,'').trim():'';
|
|
302
|
+
}
|
|
303
|
+
function applyFilter(slug){
|
|
304
|
+
var shown=0;
|
|
305
|
+
cards.forEach(function(card){
|
|
306
|
+
var on=slug==='all'||card.getAttribute('data-cat')===slug;
|
|
307
|
+
card.style.display=on?'':'none';
|
|
308
|
+
if(on)shown++;
|
|
309
|
+
});
|
|
310
|
+
if(empty)empty.hidden=shown!==0;
|
|
311
|
+
if(grid)grid.style.display=shown===0?'none':'';
|
|
312
|
+
if(statusEl){ statusEl.textContent=slug==='all'?'Showing all '+total+' clips':'Showing '+shown+' '+labelFor(slug)+' clip'+(shown===1?'':'s'); }
|
|
313
|
+
}
|
|
314
|
+
if(chips){
|
|
315
|
+
chips.addEventListener('click',function(e){
|
|
316
|
+
var btn=e.target.closest('.rk-clips-chip');
|
|
317
|
+
if(!btn)return;
|
|
318
|
+
chips.querySelectorAll('.rk-clips-chip').forEach(function(b){b.classList.toggle('is-active',b===btn);});
|
|
319
|
+
applyFilter(btn.getAttribute('data-filter'));
|
|
320
|
+
});
|
|
321
|
+
}
|
|
322
|
+
var searchForm=root.getElementById('rkClipsSearch');
|
|
323
|
+
var query=root.getElementById('rkClipsQuery');
|
|
324
|
+
if(searchForm){
|
|
325
|
+
searchForm.addEventListener('submit',function(e){
|
|
326
|
+
e.preventDefault();
|
|
327
|
+
var q=(query&&query.value||'').trim().toLowerCase();
|
|
328
|
+
if(!q){ if(chips){var all=chips.querySelector('[data-filter="all"]'); if(all)all.click();} return; }
|
|
329
|
+
chips&&chips.querySelectorAll('.rk-clips-chip').forEach(function(b){b.classList.remove('is-active');});
|
|
330
|
+
var shown=0;
|
|
331
|
+
cards.forEach(function(card){
|
|
332
|
+
var hay=(card.textContent||'').toLowerCase();
|
|
333
|
+
var on=q.split(/\\s+/).every(function(t){return hay.indexOf(t)>=0;});
|
|
334
|
+
card.style.display=on?'':'none';
|
|
335
|
+
if(on)shown++;
|
|
336
|
+
});
|
|
337
|
+
if(empty)empty.hidden=shown!==0;
|
|
338
|
+
if(grid)grid.style.display=shown===0?'none':'';
|
|
339
|
+
if(statusEl)statusEl.textContent='semantic results for \\u201c'+q+'\\u201d — '+shown+' clip'+(shown===1?'':'s');
|
|
340
|
+
});
|
|
341
|
+
}
|
|
342
|
+
var dialog=root.getElementById('rkClipsDialog');
|
|
343
|
+
var openBtn=root.getElementById('rkClipsScanBtn');
|
|
344
|
+
var cancelBtn=root.getElementById('rkClipsScanCancel');
|
|
345
|
+
var scanForm=root.getElementById('rkClipsScanForm');
|
|
346
|
+
if(openBtn&&dialog){ openBtn.addEventListener('click',function(){ if(typeof dialog.showModal==='function'&&!dialog.open)dialog.showModal(); }); }
|
|
347
|
+
if(cancelBtn&&dialog){ cancelBtn.addEventListener('click',function(){ if(dialog.open)dialog.close(); }); }
|
|
348
|
+
if(dialog){
|
|
349
|
+
dialog.addEventListener('click',function(e){
|
|
350
|
+
var r=dialog.getBoundingClientRect();
|
|
351
|
+
if(e.clientX<r.left||e.clientX>r.right||e.clientY<r.top||e.clientY>r.bottom)dialog.close();
|
|
352
|
+
});
|
|
353
|
+
}
|
|
354
|
+
if(scanForm){ scanForm.addEventListener('submit',function(){ if(statusEl)statusEl.textContent='Scanning source… this can take a few minutes.'; }); }
|
|
355
|
+
})();`;
|
|
356
|
+
export function renderReskinLibrary(tab = "approved") {
|
|
357
|
+
const isClips = tab === "clips";
|
|
358
|
+
const tabs = `<div class="rk-library-tabs" role="tablist" aria-label="Library view">
|
|
359
|
+
<a class="rk-library-tab${isClips ? "" : " is-active"}" role="tab" ${isClips ? "" : `aria-current="page"`} href="/reskin/library/approved">Approved</a>
|
|
360
|
+
<a class="rk-library-tab${isClips ? " is-active" : ""}" role="tab" ${isClips ? `aria-current="page"` : ""} href="/reskin/library/clips">Clips</a>
|
|
361
|
+
</div>`;
|
|
362
|
+
const body = `
|
|
363
|
+
<main class="rk-container-wide rk-page rk-library-page">
|
|
364
|
+
<div class="rk-spread rk-wrap rk-page-head rk-library-head">
|
|
365
|
+
<div class="rk-stack rk-library-head-copy">
|
|
366
|
+
<span class="rk-badge">Your studio</span>
|
|
367
|
+
<h1 class="rk-h1">Library</h1>
|
|
368
|
+
<p class="rk-sub">${isClips
|
|
369
|
+
? "Long-form videos mined into tagged, searchable short clips — every reaction, hook, and money shot, ready to drop into an edit."
|
|
370
|
+
: "Every project you own — forked templates, in-progress edits, active renders, and ready-to-post cuts."}</p>
|
|
371
|
+
</div>
|
|
372
|
+
<div class="rk-row rk-wrap rk-library-head-cta">
|
|
373
|
+
<a class="rk-btn rk-btn-gold rk-btn-lg" href="/discover">New project <span class="rk-arrow">→</span></a>
|
|
374
|
+
</div>
|
|
375
|
+
</div>
|
|
376
|
+
|
|
377
|
+
${tabs}
|
|
378
|
+
|
|
379
|
+
${isClips ? clipsPanel() : approvedPanel()}
|
|
380
|
+
</main>
|
|
381
|
+
${isClips ? CLIPS_DIALOG : ""}`;
|
|
382
|
+
const pageCss = `
|
|
383
|
+
/* .rk-page zeroes horizontal padding; the wide container == viewport at 1280 so
|
|
384
|
+
margin:auto adds no gutter. Restore side gutters (longhand beats the shorthand). */
|
|
385
|
+
.rk-library-page{padding-left:var(--rk-gutter);padding-right:var(--rk-gutter)}
|
|
386
|
+
.rk-library-head{align-items:flex-end}
|
|
387
|
+
.rk-library-head-copy{gap:8px;margin:0}
|
|
388
|
+
.rk-library-head-copy .rk-badge{justify-self:start}
|
|
389
|
+
.rk-library-head-cta{margin-bottom:4px}
|
|
390
|
+
@media(max-width:620px){.rk-library-head-cta .rk-btn-lg{flex:1}}
|
|
391
|
+
|
|
392
|
+
/* primary tab toggle (Approved | Clips) */
|
|
393
|
+
.rk-library-tabs{display:inline-flex;padding:5px;gap:3px;background:var(--rk-n-100);border:1px solid var(--rk-border);border-radius:var(--rk-r-full);margin-bottom:26px}
|
|
394
|
+
.rk-library-tab{display:inline-flex;align-items:center;gap:7px;padding:9px 20px;border-radius:var(--rk-r-full);font-size:14px;font-weight:600;color:var(--rk-n-600);transition:background var(--rk-dur) var(--rk-ease),color var(--rk-dur) var(--rk-ease)}
|
|
395
|
+
.rk-library-tab span{font-size:15px}
|
|
396
|
+
.rk-library-tab:hover{color:var(--rk-ink)}
|
|
397
|
+
.rk-library-tab.is-active{background:var(--rk-ink);color:#fff;box-shadow:var(--rk-shadow-sm)}
|
|
398
|
+
|
|
399
|
+
/* stats */
|
|
400
|
+
.rk-library-summary{font-size:13.5px;color:var(--rk-text-muted);font-weight:500;margin-bottom:22px;line-height:1.7}
|
|
401
|
+
.rk-library-summary b{color:var(--rk-ink);font-weight:800;font-family:var(--rk-font-display);font-size:15px;letter-spacing:-.01em}
|
|
402
|
+
|
|
403
|
+
/* toolbar */
|
|
404
|
+
.rk-library-toolbar{display:flex;flex-wrap:wrap;align-items:center;gap:12px;margin-bottom:24px}
|
|
405
|
+
.rk-library-chips{display:flex;flex-wrap:wrap;gap:4px}
|
|
406
|
+
.rk-library-chip{display:inline-flex;align-items:center;padding:6px 13px;border-radius:var(--rk-r-full);border:1px solid transparent;background:transparent;color:var(--rk-text-muted);font-size:13px;font-weight:600;cursor:pointer;transition:background var(--rk-dur) var(--rk-ease),color var(--rk-dur) var(--rk-ease)}
|
|
407
|
+
.rk-library-chip:hover{background:var(--rk-n-100);color:var(--rk-ink)}
|
|
408
|
+
.rk-library-chip.is-active{background:var(--rk-ink);color:#fff}
|
|
409
|
+
.rk-library-search{max-width:230px;margin-left:auto}
|
|
410
|
+
@media(max-width:720px){.rk-library-search{max-width:none;margin-left:0;width:100%;order:3}}
|
|
411
|
+
|
|
412
|
+
/* grid + cards */
|
|
413
|
+
.rk-library-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(250px,1fr));gap:20px}
|
|
414
|
+
.rk-library-card{display:flex;flex-direction:column;background:var(--rk-surface);border:1px solid var(--rk-border);border-radius:var(--rk-r-3xl);overflow:hidden;box-shadow:var(--rk-shadow-card)}
|
|
415
|
+
.rk-library-card[hidden]{display:none}
|
|
416
|
+
.rk-library-poster{position:relative;display:block;aspect-ratio:9/16;overflow:hidden}
|
|
417
|
+
.rk-library-poster::after{content:"";position:absolute;inset:0;background:linear-gradient(180deg,rgba(0,0,0,0) 58%,rgba(0,0,0,.22));pointer-events:none}
|
|
418
|
+
.rk-library-emoji{position:absolute;inset:0;display:grid;place-items:center;font-size:60px;filter:drop-shadow(0 8px 14px rgba(0,0,0,.2))}
|
|
419
|
+
.rk-library-pstatus{position:absolute;top:12px;left:12px;z-index:2;box-shadow:var(--rk-shadow-sm)}
|
|
420
|
+
.rk-library-dot{width:6px;height:6px;border-radius:var(--rk-r-full);background:currentColor;opacity:.9}
|
|
421
|
+
.rk-library-dot.is-pulse{animation:rk-library-pulse 1.2s var(--rk-ease) infinite}
|
|
422
|
+
.rk-library-dur{position:absolute;bottom:12px;right:12px;z-index:2;padding:4px 9px;border-radius:var(--rk-r-full);background:rgba(23,23,23,.72);color:#fff;font-size:11px;font-weight:600;letter-spacing:.01em;-webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px)}
|
|
423
|
+
.rk-library-play{position:absolute;top:50%;left:50%;z-index:2;width:52px;height:52px;border-radius:var(--rk-r-full);background:rgba(255,255,255,.94);display:grid;place-items:center;color:var(--rk-ink);box-shadow:var(--rk-shadow-md);opacity:0;transform:translate(-50%,-50%) scale(.82);transition:opacity var(--rk-dur) var(--rk-ease),transform var(--rk-dur) var(--rk-ease)}
|
|
424
|
+
.rk-library-card:hover .rk-library-play{opacity:1;transform:translate(-50%,-50%) scale(1)}
|
|
425
|
+
.rk-library-play svg{margin-left:2px}
|
|
426
|
+
.rk-library-prog{position:absolute;left:0;right:0;bottom:0;z-index:2;height:5px;background:rgba(255,255,255,.4)}
|
|
427
|
+
.rk-library-prog-fill{display:block;height:100%;background:var(--rk-gold-500);border-radius:0 var(--rk-r-full) var(--rk-r-full) 0;box-shadow:0 0 8px rgba(252,185,0,.6);animation:rk-library-pulse 1.6s var(--rk-ease) infinite}
|
|
428
|
+
.rk-library-meta{display:grid;gap:10px;padding:16px 16px 18px}
|
|
429
|
+
.rk-library-title{font-family:var(--rk-font-display);font-size:15px;font-weight:700;color:var(--rk-ink);line-height:1.24;letter-spacing:-.01em;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;min-height:2.48em}
|
|
430
|
+
.rk-library-origin{display:flex;align-items:center;gap:8px;min-width:0;font-size:12.5px;font-weight:500;color:var(--rk-text-muted)}
|
|
431
|
+
.rk-library-origin-txt{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0}
|
|
432
|
+
.rk-library-foot{margin-top:2px;gap:10px}
|
|
433
|
+
.rk-library-foot .rk-btn{flex:none}
|
|
434
|
+
.rk-library-time{font-size:12px;font-weight:500;color:var(--rk-text-faint);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;min-width:0}
|
|
435
|
+
|
|
436
|
+
/* new-project tile */
|
|
437
|
+
.rk-library-new{display:grid;grid-auto-flow:row;place-content:center;justify-items:center;gap:14px;text-align:center;padding:28px 22px;border:1.5px dashed var(--rk-border-strong);border-radius:var(--rk-r-3xl);background:var(--rk-n-50);color:var(--rk-text-muted);transition:border-color var(--rk-dur) var(--rk-ease),background var(--rk-dur) var(--rk-ease),transform var(--rk-dur) var(--rk-ease)}
|
|
438
|
+
.rk-library-new:hover{border-color:var(--rk-gold-600);background:var(--rk-gold-tint);transform:translateY(-2px)}
|
|
439
|
+
.rk-library-new-plus{font-family:var(--rk-font-display);font-weight:800}
|
|
440
|
+
.rk-library-new-title{font-family:var(--rk-font-display);font-weight:800;font-size:var(--rk-text-lg);color:var(--rk-ink);letter-spacing:-.01em}
|
|
441
|
+
.rk-library-new-sub{font-size:12.5px;color:var(--rk-text-muted);margin-top:3px;max-width:15ch}
|
|
442
|
+
.rk-library-new[hidden]{display:none}
|
|
443
|
+
|
|
444
|
+
/* empty state */
|
|
445
|
+
.rk-library-empty{display:none;place-items:center;gap:12px;text-align:center;padding:64px 24px;border:1.5px dashed var(--rk-border-strong);border-radius:var(--rk-r-3xl);background:var(--rk-n-50)}
|
|
446
|
+
.rk-library-empty.is-visible{display:grid}
|
|
447
|
+
.rk-library-empty h3{font-size:var(--rk-text-xl)}
|
|
448
|
+
.rk-library-empty p{max-width:42ch}
|
|
449
|
+
|
|
450
|
+
@keyframes rk-library-pulse{0%,100%{opacity:.5}50%{opacity:1}}
|
|
451
|
+
@media(prefers-reduced-motion:reduce){.rk-library-dot.is-pulse,.rk-library-prog-fill{animation:none}}
|
|
452
|
+
|
|
453
|
+
/* ── clips tab ── */
|
|
454
|
+
.rk-clips-toolbar{display:grid;gap:16px;margin-bottom:24px}
|
|
455
|
+
.rk-clips-toolbar-top{display:flex;flex-wrap:wrap;align-items:center;gap:12px}
|
|
456
|
+
.rk-clips-search{position:relative;display:flex;align-items:center;gap:8px;flex:1 1 340px;min-width:240px;margin:0}
|
|
457
|
+
.rk-clips-search-ic{position:absolute;left:14px;top:50%;transform:translateY(-50%);color:var(--rk-n-400);pointer-events:none;display:flex}
|
|
458
|
+
.rk-clips-search-input{flex:1;padding-left:40px;border-radius:var(--rk-r-full)}
|
|
459
|
+
.rk-clips-search-btn{flex:none}
|
|
460
|
+
.rk-clips-scan{flex:none}
|
|
461
|
+
.rk-clips-chips{display:flex;flex-wrap:wrap;gap:4px}
|
|
462
|
+
.rk-clips-chip{display:inline-flex;align-items:center;gap:6px;padding:6px 13px;border-radius:var(--rk-r-full);border:1px solid transparent;background:transparent;font-family:var(--rk-font-body);font-size:13px;font-weight:600;color:var(--rk-text-muted);cursor:pointer;white-space:nowrap;transition:background var(--rk-dur) var(--rk-ease),color var(--rk-dur) var(--rk-ease)}
|
|
463
|
+
.rk-clips-chip:hover{background:var(--rk-n-100);color:var(--rk-ink)}
|
|
464
|
+
.rk-clips-chip.is-active{background:var(--rk-ink);color:#fff}
|
|
465
|
+
.rk-clips-status{font-size:13px;color:var(--rk-text-muted);font-weight:500}
|
|
466
|
+
.rk-clips-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(232px,1fr));gap:22px}
|
|
467
|
+
.rk-clips-card{display:flex;flex-direction:column;overflow:hidden;background:var(--rk-surface);border:1px solid var(--rk-border);border-radius:var(--rk-r-2xl);box-shadow:var(--rk-shadow-card)}
|
|
468
|
+
.rk-clips-thumb{position:relative;aspect-ratio:3/4;overflow:hidden;display:grid;place-items:center}
|
|
469
|
+
.rk-clips-glyph{font-size:56px;filter:saturate(.9) drop-shadow(0 6px 14px rgba(0,0,0,.12));opacity:.92;transition:transform var(--rk-dur) var(--rk-ease)}
|
|
470
|
+
.rk-clips-card:hover .rk-clips-glyph{transform:scale(1.06)}
|
|
471
|
+
.rk-clips-dur{position:absolute;left:10px;bottom:10px;padding:3px 9px;border-radius:var(--rk-r-full);background:rgba(255,255,255,.92);color:var(--rk-ink);font-size:11px;font-weight:700;font-variant-numeric:tabular-nums;box-shadow:var(--rk-shadow-xs)}
|
|
472
|
+
.rk-clips-play{position:absolute;right:10px;top:10px;width:34px;height:34px;display:grid;place-items:center;padding:0 0 0 2px;border:0;border-radius:var(--rk-r-full);background:rgba(255,255,255,.92);color:var(--rk-ink);font-size:13px;cursor:pointer;opacity:0;transform:scale(.85);box-shadow:var(--rk-shadow-sm);transition:opacity var(--rk-dur) var(--rk-ease),transform var(--rk-dur) var(--rk-ease),background var(--rk-dur) var(--rk-ease)}
|
|
473
|
+
.rk-clips-card:hover .rk-clips-play{opacity:1;transform:scale(1)}
|
|
474
|
+
.rk-clips-play:hover{background:var(--rk-gold-500)}
|
|
475
|
+
.rk-clips-body{display:flex;flex-direction:column;gap:10px;flex:1;padding:16px}
|
|
476
|
+
.rk-clips-title{font-family:var(--rk-font-display);font-size:15px;font-weight:700;line-height:1.32;color:var(--rk-ink);letter-spacing:-.01em;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
|
|
477
|
+
.rk-clips-tagline{font-size:12px;font-weight:500;color:var(--rk-text-muted);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0}
|
|
478
|
+
.rk-clips-foot{display:flex;align-items:center;justify-content:space-between;gap:8px;margin-top:auto;border-top:1px solid var(--rk-border);padding-top:12px}
|
|
479
|
+
.rk-clips-src{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--rk-text-muted);font-size:12px;font-weight:500}
|
|
480
|
+
.rk-clips-dl{flex:none;color:var(--rk-gold-700);font-size:12px;font-weight:700}
|
|
481
|
+
.rk-clips-dl:hover{color:var(--rk-gold-500)}
|
|
482
|
+
.rk-clips-g-gold{background:radial-gradient(120% 90% at 25% 15%,#fff5e4,transparent 60%),linear-gradient(150deg,#ffe9cc,#ffd98a)}
|
|
483
|
+
.rk-clips-g-sky{background:radial-gradient(120% 90% at 25% 15%,#f2f8ff,transparent 60%),linear-gradient(150deg,#e2effd,#b6d8f7)}
|
|
484
|
+
.rk-clips-g-violet{background:radial-gradient(120% 90% at 25% 15%,#f6f1ff,transparent 60%),linear-gradient(150deg,#e4ddfd,#cbbdf5)}
|
|
485
|
+
.rk-clips-g-green{background:radial-gradient(120% 90% at 25% 15%,#f0fdf4,transparent 60%),linear-gradient(150deg,#dcfce7,#b3ebc7)}
|
|
486
|
+
.rk-clips-g-coral{background:radial-gradient(120% 90% at 25% 15%,#fff3ea,transparent 60%),linear-gradient(150deg,#ffe0d0,#ffc4a3)}
|
|
487
|
+
.rk-clips-g-ink{background:radial-gradient(130% 100% at 78% 12%,rgba(255,199,56,.28),transparent 55%),linear-gradient(150deg,#2c2c2c,#171717)}
|
|
488
|
+
.rk-clips-g-ink .rk-clips-glyph{filter:drop-shadow(0 6px 14px rgba(0,0,0,.4))}
|
|
489
|
+
.rk-clips-empty{display:grid;place-items:center;gap:10px;text-align:center;padding:64px 20px;background:#fff;border:1.5px dashed var(--rk-border-strong);border-radius:var(--rk-r-3xl)}
|
|
490
|
+
.rk-clips-empty[hidden]{display:none}
|
|
491
|
+
.rk-clips-empty h3{font-size:var(--rk-text-xl)}
|
|
492
|
+
.rk-clips-dialog{width:min(34rem,calc(100vw - 2rem));padding:0;border:1px solid var(--rk-border);border-radius:var(--rk-r-3xl);background:var(--rk-surface);box-shadow:var(--rk-shadow-xl)}
|
|
493
|
+
.rk-clips-dialog::backdrop{background:rgba(23,23,23,.34);backdrop-filter:blur(6px)}
|
|
494
|
+
.rk-clips-dialog-form{display:grid;gap:16px;margin:0;padding:28px}
|
|
495
|
+
.rk-clips-dialog-grid{gap:14px}
|
|
496
|
+
.rk-clips-dialog-note{background:var(--rk-gold-tint);border-radius:var(--rk-r-xl);padding:10px 13px;color:var(--rk-n-700)}
|
|
497
|
+
.rk-clips-dialog-actions{display:flex;justify-content:flex-end;gap:10px;margin-top:4px}
|
|
498
|
+
@media(max-width:620px){
|
|
499
|
+
.rk-clips-search{flex:1 1 100%}
|
|
500
|
+
.rk-clips-scan{flex:1 1 100%}
|
|
501
|
+
.rk-clips-search-input{font-size:16px}
|
|
502
|
+
.rk-clips-grid{grid-template-columns:repeat(2,minmax(0,1fr));gap:14px}
|
|
503
|
+
.rk-clips-glyph{font-size:42px}
|
|
504
|
+
}
|
|
505
|
+
`;
|
|
506
|
+
return reskinDocument({
|
|
507
|
+
title: `vidfarm reskin — Library${isClips ? " · Clips" : ""}`,
|
|
508
|
+
description: "Reskinned vidfarm library: your projects, renders & ready-to-post cuts, plus mined clips.",
|
|
509
|
+
activeSlug: "library",
|
|
510
|
+
pageCss,
|
|
511
|
+
body,
|
|
512
|
+
script: isClips ? CLIPS_SCRIPT : APPROVED_SCRIPT
|
|
513
|
+
});
|
|
514
|
+
}
|
|
515
|
+
//# sourceMappingURL=library-page.js.map
|