@mevdragon/vidfarm-devcli 0.20.6 → 0.20.9

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 (157) hide show
  1. package/.agents/skills/editor-capabilities/SKILL.md +15 -1
  2. package/.agents/skills/vidfarm-director/SKILL.md +106 -0
  3. package/.agents/skills/vidfarm-director/recipes/find-and-fork-template.md +16 -0
  4. package/.agents/skills/vidfarm-director/recipes/local-edit-render-approve.md +13 -0
  5. package/.agents/skills/vidfarm-director/recipes/onboard-a-new-director.md +13 -0
  6. package/.agents/skills/vidfarm-director/recipes/retheme-template.md +17 -0
  7. package/.agents/skills/vidfarm-director/references/assets-and-sourcing.md +117 -0
  8. package/.agents/skills/vidfarm-director/references/automation-and-local-dev.md +245 -0
  9. package/.agents/skills/vidfarm-director/references/core-workflows.md +273 -0
  10. package/.agents/skills/vidfarm-director/references/editor-workflows.md +365 -0
  11. package/.agents/skills/vidfarm-director/references/onboarding.md +28 -0
  12. package/.agents/skills/vidfarm-director/references/primitives.md +308 -0
  13. package/SKILL.director.md +517 -250
  14. package/dist/src/cli.js +563 -99
  15. package/dist/src/devcli/clips.js +29 -12
  16. package/dist/src/devcli/doctor.js +2 -2
  17. package/dist/src/devcli/local-backend.js +46 -2
  18. package/dist/src/services/clip-curation/cost.js +5 -1
  19. package/dist/src/services/clip-curation/gemini.js +14 -2
  20. package/dist/src/services/clip-curation/hunt.js +56 -0
  21. package/dist/src/services/clip-curation/index.js +1 -1
  22. package/dist/src/services/clip-curation/scan.js +5 -3
  23. package/package.json +30 -10
  24. package/SKILL.platform.md +0 -432
  25. package/demo/README.md +0 -28
  26. package/demo/dist/app.css +0 -1
  27. package/demo/dist/app.js +0 -1850
  28. package/demo/dist/chunks/chunk-DXB73IDG.js +0 -1
  29. package/demo/dist/chunks/chunk-S7OWAJDS.js +0 -36
  30. package/demo/dist/chunks/chunk-VTIBZ6AN.js +0 -1
  31. package/demo/dist/chunks/dist-ADSJKBVE.js +0 -332
  32. package/demo/dist/chunks/domEditingLayers-VZMLL4AP-SGHWPND4.js +0 -1
  33. package/demo/dist/chunks/hyperframes-player-XB65TCD6.js +0 -425
  34. package/demo/dist/chunks/lib-XAQ37YOE.js +0 -1
  35. package/demo/dist/chunks/src-TJ2QYA4U.js +0 -207
  36. package/demo/dist/favicon.ico +0 -0
  37. package/demo/dist/icons/timeline/audio.svg +0 -7
  38. package/demo/dist/icons/timeline/captions.svg +0 -5
  39. package/demo/dist/icons/timeline/composition.svg +0 -12
  40. package/demo/dist/icons/timeline/image.svg +0 -18
  41. package/demo/dist/icons/timeline/music.svg +0 -10
  42. package/demo/dist/icons/timeline/text.svg +0 -3
  43. package/demo/dist/index.html +0 -15
  44. package/dist/src/account-pages-legacy.js +0 -9396
  45. package/dist/src/account-pages.js +0 -61
  46. package/dist/src/app.js +0 -21603
  47. package/dist/src/composition-runtime.js +0 -1053
  48. package/dist/src/config.js +0 -217
  49. package/dist/src/context.js +0 -447
  50. package/dist/src/dev-app-legacy.js +0 -739
  51. package/dist/src/dev-app.js +0 -6
  52. package/dist/src/devcli/migrate-local.js +0 -140
  53. package/dist/src/devcli/sync.js +0 -368
  54. package/dist/src/domain.js +0 -5
  55. package/dist/src/editor-chat-history.js +0 -82
  56. package/dist/src/editor-chat.js +0 -828
  57. package/dist/src/editor-dark-theme.js +0 -1128
  58. package/dist/src/frontend/debug.js +0 -71
  59. package/dist/src/frontend/discover-client.js +0 -130
  60. package/dist/src/frontend/discover-store.js +0 -23
  61. package/dist/src/frontend/file-directory.js +0 -1018
  62. package/dist/src/frontend/flockposter-cache-store.js +0 -124
  63. package/dist/src/frontend/homepage-client.js +0 -446
  64. package/dist/src/frontend/homepage-shared.js +0 -201
  65. package/dist/src/frontend/homepage-store.js +0 -66
  66. package/dist/src/frontend/homepage-view.js +0 -705
  67. package/dist/src/frontend/page-runtime-client.js +0 -132
  68. package/dist/src/frontend/page-runtime-store.js +0 -9
  69. package/dist/src/frontend/sentry.js +0 -42
  70. package/dist/src/frontend/template-editor-chat.js +0 -4181
  71. package/dist/src/help-page.js +0 -346
  72. package/dist/src/homepage.js +0 -1458
  73. package/dist/src/index.js +0 -16
  74. package/dist/src/instrument.js +0 -30
  75. package/dist/src/landing-page.js +0 -384
  76. package/dist/src/page-runtime.js +0 -2
  77. package/dist/src/page-shell.js +0 -1452
  78. package/dist/src/primitive-context.js +0 -416
  79. package/dist/src/primitive-registry.js +0 -3940
  80. package/dist/src/primitive-sdk.js +0 -4
  81. package/dist/src/primitives/hyperframes-media.js +0 -108
  82. package/dist/src/react-page-shell.js +0 -35
  83. package/dist/src/ready-post-schedule-component.js +0 -1540
  84. package/dist/src/registry.js +0 -296
  85. package/dist/src/reskin/agency-page.js +0 -299
  86. package/dist/src/reskin/calendar-page.js +0 -568
  87. package/dist/src/reskin/chat-page.js +0 -942
  88. package/dist/src/reskin/discover-page.js +0 -1788
  89. package/dist/src/reskin/document.js +0 -1587
  90. package/dist/src/reskin/help-page.js +0 -357
  91. package/dist/src/reskin/index-page.js +0 -62
  92. package/dist/src/reskin/inpaint-clipper-page.js +0 -890
  93. package/dist/src/reskin/inpaint-page.js +0 -2554
  94. package/dist/src/reskin/inpaint-video-page.js +0 -1339
  95. package/dist/src/reskin/job-runs-page.js +0 -477
  96. package/dist/src/reskin/library-page.js +0 -1634
  97. package/dist/src/reskin/login-page.js +0 -262
  98. package/dist/src/reskin/portfolio-page.js +0 -687
  99. package/dist/src/reskin/pricing-page.js +0 -390
  100. package/dist/src/reskin/settings-page.js +0 -732
  101. package/dist/src/reskin/theme.js +0 -711
  102. package/dist/src/runtime.js +0 -35
  103. package/dist/src/services/api-call-history.js +0 -249
  104. package/dist/src/services/auth.js +0 -152
  105. package/dist/src/services/billing-pricing.js +0 -39
  106. package/dist/src/services/billing.js +0 -241
  107. package/dist/src/services/cast.js +0 -127
  108. package/dist/src/services/chat-threads.js +0 -92
  109. package/dist/src/services/clip-records.js +0 -250
  110. package/dist/src/services/clip-search.js +0 -77
  111. package/dist/src/services/clip-vectors.js +0 -125
  112. package/dist/src/services/composition-sanitize.js +0 -124
  113. package/dist/src/services/composition-watch.js +0 -79
  114. package/dist/src/services/elevenlabs.js +0 -222
  115. package/dist/src/services/file-directory.js +0 -117
  116. package/dist/src/services/fork-access.js +0 -93
  117. package/dist/src/services/fork-manifest.js +0 -42
  118. package/dist/src/services/ghostcut.js +0 -179
  119. package/dist/src/services/hyperframes.js +0 -3654
  120. package/dist/src/services/job-capacity.js +0 -14
  121. package/dist/src/services/job-logs.js +0 -197
  122. package/dist/src/services/jobs.js +0 -136
  123. package/dist/src/services/local-dynamo.js +0 -0
  124. package/dist/src/services/media-processing.js +0 -766
  125. package/dist/src/services/primitive-media-lambda.js +0 -280
  126. package/dist/src/services/providers.js +0 -2748
  127. package/dist/src/services/rate-limits.js +0 -262
  128. package/dist/src/services/scene-annotations.js +0 -32
  129. package/dist/src/services/serverless-auth.js +0 -382
  130. package/dist/src/services/serverless-jobs.js +0 -1084
  131. package/dist/src/services/serverless-provider-keys.js +0 -409
  132. package/dist/src/services/serverless-records.js +0 -1515
  133. package/dist/src/services/serverless-template-configs.js +0 -75
  134. package/dist/src/services/storage.js +0 -461
  135. package/dist/src/services/swipe-customize.js +0 -437
  136. package/dist/src/services/template-certification.js +0 -413
  137. package/dist/src/services/template-loader.js +0 -99
  138. package/dist/src/services/template-runtime-bundles.js +0 -217
  139. package/dist/src/services/template-sources.js +0 -1017
  140. package/dist/src/services/upstream.js +0 -248
  141. package/dist/src/services/video-normalization.js +0 -2
  142. package/dist/src/services/webhooks.js +0 -62
  143. package/dist/src/template-editor-pages.js +0 -2576
  144. package/dist/src/template-editor-shell.js +0 -2893
  145. package/dist/src/template-sdk.js +0 -4
  146. package/dist/src/worker.js +0 -17
  147. package/public/assets/discover-client-app.js +0 -1
  148. package/public/assets/file-directory-app.js +0 -3
  149. package/public/assets/homepage-app.js +0 -54
  150. package/public/assets/homepage-client-app.js +0 -80
  151. package/public/assets/page-runtime-client-app.js +0 -94
  152. package/public/assets/placeholders/scene-placeholder.png +0 -0
  153. package/src/assets/SELLING_AWARENESS_STAGES.md +0 -579
  154. package/src/assets/SELLING_WITH_HOOKS.md +0 -377
  155. package/src/assets/SELLING_WITH_VSLS.md +0 -606
  156. package/src/assets/favicon.ico +0 -0
  157. package/src/assets/logo-vidfarm.png +0 -0
@@ -1,296 +0,0 @@
1
- import { existsSync, readdirSync } from "node:fs";
2
- import path from "node:path";
3
- import { z } from "zod";
4
- import { config } from "./config.js";
5
- import { devLog } from "./lib/dev-log.js";
6
- import { loadTemplateFromModule } from "./services/template-loader.js";
7
- import { loadTemplateFromRuntimeRelease } from "./services/template-runtime-bundles.js";
8
- import { serverlessRecords } from "./services/serverless-records.js";
9
- class TemplateRegistry {
10
- localTemplates = new Map();
11
- releaseTemplates = new Map();
12
- activeReleases = new Map();
13
- runtimeTemplates = new Map();
14
- initialized = false;
15
- initPromise = null;
16
- async ensureInitialized() {
17
- if (this.initialized) {
18
- return;
19
- }
20
- if (this.initPromise) {
21
- return this.initPromise;
22
- }
23
- this.initPromise = (async () => {
24
- await this.loadActiveReleaseSnapshots();
25
- if (shouldLoadLocalTemplates()) {
26
- await this.loadLocalTemplates();
27
- devLog("template_registry.local_loaded", {
28
- count: this.localTemplates.size,
29
- template_ids: [...this.localTemplates.keys()]
30
- });
31
- }
32
- this.initialized = true;
33
- this.initPromise = null;
34
- devLog("template_registry.initialized", {
35
- local_count: this.localTemplates.size,
36
- release_count: this.releaseTemplates.size,
37
- runtime_count: this.runtimeTemplates.size
38
- });
39
- })();
40
- return this.initPromise;
41
- }
42
- list() {
43
- const merged = new Map();
44
- for (const template of this.releaseTemplates.values()) {
45
- merged.set(template.id, template);
46
- }
47
- for (const template of this.localTemplates.values()) {
48
- merged.set(template.id, template);
49
- }
50
- for (const template of this.runtimeTemplates.values()) {
51
- merged.set(template.id, template);
52
- }
53
- return [...merged.values()].sort((a, b) => a.id.localeCompare(b.id));
54
- }
55
- get(templateId) {
56
- return this.findTemplate(templateId, this.runtimeTemplates)
57
- ?? this.findTemplate(templateId, this.localTemplates)
58
- ?? this.findTemplate(templateId, this.releaseTemplates);
59
- }
60
- async listMetadata() {
61
- try {
62
- const releases = await serverlessRecords.listActiveTemplateReleases?.() ?? [];
63
- for (const release of releases) {
64
- this.cacheReleaseSnapshot(release);
65
- }
66
- const releaseTemplates = releases
67
- .filter((release) => release.templateSnapshot)
68
- .map((release) => templateFromSnapshot(release.templateSnapshot, release));
69
- return await this.mergeLocalMetadata(releaseTemplates);
70
- }
71
- catch (error) {
72
- devLog("template_registry.metadata.list_failed", {
73
- message: error instanceof Error ? error.message : String(error)
74
- }, "warn");
75
- await this.ensureInitialized();
76
- return this.list();
77
- }
78
- }
79
- async listMetadataPage(input = {}) {
80
- try {
81
- const page = await serverlessRecords.listActiveTemplateReleasePage?.(input);
82
- if (!page) {
83
- return { templates: await this.listMetadata(), nextCursor: null };
84
- }
85
- for (const release of page.items) {
86
- this.cacheReleaseSnapshot(release);
87
- }
88
- const releaseTemplates = page.items
89
- .filter((release) => release.templateSnapshot)
90
- .map((release) => templateFromSnapshot(release.templateSnapshot, release));
91
- return {
92
- templates: await this.mergeLocalMetadata(releaseTemplates),
93
- nextCursor: page.nextCursor
94
- };
95
- }
96
- catch (error) {
97
- devLog("template_registry.metadata.page_failed", {
98
- message: error instanceof Error ? error.message : String(error)
99
- }, "warn");
100
- return { templates: await this.listMetadata(), nextCursor: null };
101
- }
102
- }
103
- async getMetadata(templateId) {
104
- const cached = this.get(templateId);
105
- if (cached) {
106
- return cached;
107
- }
108
- try {
109
- const release = await serverlessRecords.getActiveTemplateReleaseByTemplateId?.(templateId);
110
- if (release?.templateSnapshot) {
111
- return this.cacheReleaseSnapshot(release);
112
- }
113
- }
114
- catch (error) {
115
- devLog("template_registry.metadata.get_failed", {
116
- template_id: templateId,
117
- message: error instanceof Error ? error.message : String(error)
118
- }, "warn");
119
- }
120
- await this.ensureInitialized();
121
- return this.get(templateId);
122
- }
123
- async getExecutable(templateId) {
124
- const runtime = this.findTemplate(templateId, this.runtimeTemplates) ?? this.findTemplate(templateId, this.localTemplates);
125
- if (runtime) {
126
- return runtime;
127
- }
128
- let releaseTemplate = this.findTemplate(templateId, this.releaseTemplates);
129
- let release = releaseTemplate ? this.activeReleases.get(releaseTemplate.id) : null;
130
- if (!release) {
131
- try {
132
- const activeRelease = await serverlessRecords.getActiveTemplateReleaseByTemplateId?.(templateId);
133
- if (activeRelease?.templateSnapshot) {
134
- releaseTemplate = this.cacheReleaseSnapshot(activeRelease);
135
- release = activeRelease;
136
- }
137
- }
138
- catch (error) {
139
- devLog("template_registry.executable.active_release_lookup_failed", {
140
- template_id: templateId,
141
- message: error instanceof Error ? error.message : String(error)
142
- }, "warn");
143
- }
144
- }
145
- if (!release) {
146
- await this.ensureInitialized();
147
- const initializedRuntime = this.findTemplate(templateId, this.runtimeTemplates) ?? this.findTemplate(templateId, this.localTemplates);
148
- if (initializedRuntime) {
149
- return initializedRuntime;
150
- }
151
- releaseTemplate = this.findTemplate(templateId, this.releaseTemplates);
152
- release = releaseTemplate ? this.activeReleases.get(releaseTemplate.id) : null;
153
- }
154
- if (!release) {
155
- return null;
156
- }
157
- const template = await loadTemplateFromRuntimeRelease(release);
158
- this.runtimeTemplates.set(template.id, template);
159
- return template;
160
- }
161
- registerRuntimeTemplate(template) {
162
- this.runtimeTemplates.set(template.id, template);
163
- }
164
- cacheReleaseSnapshot(release) {
165
- if (!release.templateSnapshot) {
166
- return null;
167
- }
168
- const template = templateFromSnapshot(release.templateSnapshot, release);
169
- this.releaseTemplates.set(template.id, template);
170
- this.activeReleases.set(template.id, release);
171
- return template;
172
- }
173
- async loadLocalTemplates() {
174
- this.localTemplates.clear();
175
- for (const modulePath of discoverLocalTemplateModulePaths(process.cwd())) {
176
- const template = await loadTemplateFromModule(modulePath);
177
- this.localTemplates.set(template.id, template);
178
- devLog("template_registry.local_template.loaded", {
179
- template_id: template.id,
180
- slug_id: template.slugId,
181
- module_path: modulePath
182
- });
183
- }
184
- }
185
- async loadActiveReleaseSnapshots() {
186
- this.releaseTemplates.clear();
187
- this.activeReleases.clear();
188
- let releases = [];
189
- try {
190
- releases = await serverlessRecords.listActiveTemplateReleases?.() ?? [];
191
- }
192
- catch (error) {
193
- devLog("template_registry.active_releases.load_failed", {
194
- message: error instanceof Error ? error.message : String(error)
195
- }, "warn");
196
- return;
197
- }
198
- for (const release of releases) {
199
- if (!release.templateSnapshot) {
200
- continue;
201
- }
202
- this.cacheReleaseSnapshot(release);
203
- }
204
- devLog("template_registry.active_releases.loaded", {
205
- count: this.releaseTemplates.size,
206
- template_ids: [...this.releaseTemplates.keys()]
207
- });
208
- }
209
- async mergeLocalMetadata(templates) {
210
- if (shouldLoadLocalTemplates()) {
211
- await this.ensureInitialized();
212
- }
213
- const merged = new Map();
214
- for (const template of templates) {
215
- merged.set(template.id, template);
216
- }
217
- for (const template of this.localTemplates.values()) {
218
- merged.set(template.id, template);
219
- }
220
- for (const template of this.runtimeTemplates.values()) {
221
- merged.set(template.id, template);
222
- }
223
- return [...merged.values()].sort((a, b) => a.id.localeCompare(b.id));
224
- }
225
- findTemplate(templateKey, source) {
226
- return source.get(templateKey) ?? [...source.values()].find((template) => template.slugId === templateKey) ?? null;
227
- }
228
- }
229
- function templateFromSnapshot(snapshot, release) {
230
- const snapshotAbout = snapshot.about;
231
- const operations = Object.fromEntries(snapshot.operations.map((operation) => [
232
- operation.name,
233
- {
234
- description: operation.description,
235
- inputSchema: z.record(z.string(), z.unknown()),
236
- workflow: operation.workflow,
237
- providerHint: operation.providerHint ?? undefined,
238
- webhookSupport: operation.webhookSupport ?? undefined
239
- }
240
- ]));
241
- return {
242
- id: snapshot.id,
243
- slugId: snapshot.slugId,
244
- version: snapshot.version,
245
- about: {
246
- ...snapshot.about,
247
- links_to_inspiration: normalizeTemplateInspirationLinks(snapshotAbout.links_to_inspiration, snapshot.about.link_to_original),
248
- sound: normalizeTemplateSound(snapshotAbout.sound)
249
- },
250
- examples: snapshot.examples,
251
- skillPath: release.skillPath,
252
- configSchema: z.record(z.string(), z.unknown()),
253
- operations,
254
- jobs: {}
255
- };
256
- }
257
- function normalizeTemplateInspirationLinks(value, linkToOriginal) {
258
- if (Array.isArray(value)) {
259
- return value.filter((entry) => typeof entry === "string");
260
- }
261
- return linkToOriginal ? [linkToOriginal] : [];
262
- }
263
- function normalizeTemplateSound(value) {
264
- return {
265
- sound_title: typeof value?.sound_title === "string" ? value.sound_title : "",
266
- sound_url: typeof value?.sound_url === "string" ? value.sound_url : ""
267
- };
268
- }
269
- function shouldLoadLocalTemplates() {
270
- if (config.VIDFARM_LOAD_LOCAL_TEMPLATES === "true") {
271
- return true;
272
- }
273
- if (config.VIDFARM_LOAD_LOCAL_TEMPLATES === "false") {
274
- return false;
275
- }
276
- return !config.isProduction;
277
- }
278
- function discoverLocalTemplateModulePaths(rootDir) {
279
- const templatesDir = path.resolve(rootDir, "templates");
280
- if (!existsSync(templatesDir)) {
281
- return [];
282
- }
283
- return readdirSync(templatesDir, { withFileTypes: true })
284
- .filter((entry) => entry.isDirectory() && entry.name.startsWith("vidfarm_template_"))
285
- .filter((entry) => (entry.name !== "vidfarm_template_0000"
286
- || process.env.NODE_ENV !== "production"
287
- || process.env.VIDFARM_DEPLOYMENT_TARGET === "serverless-staging"))
288
- .map((entry) => [
289
- path.join(templatesDir, entry.name, "src", "template.js"),
290
- path.join(templatesDir, entry.name, "src", "template.ts")
291
- ].find((modulePath) => existsSync(modulePath)))
292
- .filter((modulePath) => Boolean(modulePath))
293
- .sort((a, b) => a.localeCompare(b));
294
- }
295
- export const templateRegistry = new TemplateRegistry();
296
- //# sourceMappingURL=registry.js.map
@@ -1,299 +0,0 @@
1
- // /reskin/agency — the Agency / client-workspaces screen, MIGRATED to real
2
- // production data + behavior (farmville design system).
3
- //
4
- // This mirrors src/account-pages-legacy.ts → renderAgencyPage: an agency
5
- // managing its client sub-accounts. The live page's client list is NOT
6
- // server-fetched — it is read CLIENT-SIDE from this browser's localStorage
7
- // account registry (the same cache the app writes when you sign into a client
8
- // account), so the server can only render an empty shell + template and the
9
- // real client cards are built in JS. This reskin replicates that exact read and
10
- // wires the real per-client actions:
11
- // • "Open workspace" → opens the client's /u/:id/library in a new tab
12
- // • "Copy login" → copies the client's autologin URL
13
- // • "Add client login"→ opens /agency-client-login
14
- // localStorage contract (identical keys/fields to the live page):
15
- // index key: "vidfarm:agency:accounts:index" → string[] of userIds
16
- // entry key: "vidfarm:agency:account_<userId>" → { apiKey, email,
17
- // displayName, lastSeenAt, autologinUrl? }
18
- // autologinUrl falls back to "/auto-login?api_key=<encodeURIComponent(apiKey)>"
19
- //
20
- // The old reskin rendered fabricated per-client stats (projects, scheduled,
21
- // wallet, seats, plan, status) and aggregate tiles — NONE of those live in the
22
- // cache, so they are dropped. Cards now show only real cached fields (name,
23
- // email, user id, last-seen). Avatars are text initials (no emoji).
24
- import { reskinDocument, rkEscape } from "./document.js";
25
- export function renderReskinAgency(input = { userId: null, currentAccountId: null, name: null, email: null, loggedIn: false }) {
26
- const loggedIn = input.loggedIn ?? Boolean(input.userId);
27
- const signedInHint = loggedIn && input.email
28
- ? `<span class="rk-agency-hint">Managing from ${rkEscape(input.email)}</span>`
29
- : "";
30
- const PLUS = `<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 5v14M5 12h14"/></svg>`;
31
- // The invite / "add a client workspace" card is a static, always-real CTA to
32
- // the /agency-client-login flow. It lives inside the grid alongside the
33
- // JS-injected client cards (which render into [data-rk-agency-cards]).
34
- const inviteCard = `<article class="rk-card rk-agency-invite" data-rk-agency-invite>
35
- <span class="rk-tile rk-tile-lg rk-tile-gold rk-agency-invite-ic">${PLUS}</span>
36
- <h3 class="rk-agency-invite-title">Add a client workspace</h3>
37
- <p class="rk-muted rk-agency-invite-copy">Link a client's account and get an autologin so your team can post on their behalf.</p>
38
- <a class="rk-btn rk-btn-gold" href="/agency-client-login" target="_blank" rel="noreferrer">Add client login <span class="rk-arrow">→</span></a>
39
- </article>`;
40
- const body = `
41
- <main class="rk-container rk-page">
42
- <div class="rk-page-head rk-agency-head">
43
- <div class="rk-agency-head-copy">
44
- <h1 class="rk-h1">Agency clients</h1>
45
- <p class="rk-sub">Every client workspace cached on this device — open their library, or copy a one-tap login to post on their behalf.</p>
46
- </div>
47
- <div class="rk-agency-head-cta">
48
- ${signedInHint}
49
- <a class="rk-btn rk-btn-gold rk-btn-lg" href="/agency-client-login" target="_blank" rel="noreferrer">Add client login <span class="rk-arrow">→</span></a>
50
- </div>
51
- </div>
52
-
53
- <section class="rk-agency-panel">
54
- <div class="rk-spread rk-wrap rk-agency-toolbar">
55
- <div class="rk-agency-toolbar-copy">
56
- <span class="rk-eyebrow">Clients</span>
57
- <h2 class="rk-agency-h2"><span data-rk-agency-count>0</span> client workspace<span data-rk-agency-plural>s</span></h2>
58
- </div>
59
- <div class="rk-agency-search-wrap">
60
- <span class="rk-agency-search-ic" aria-hidden="true"><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></span>
61
- <input class="rk-input rk-agency-search" type="search" placeholder="Search name, email, or user id" autocomplete="off" spellcheck="false" data-rk-agency-search />
62
- </div>
63
- </div>
64
- <div class="rk-grid rk-grid-3 rk-agency-grid" data-rk-agency-grid>
65
- <div class="rk-agency-cards" data-rk-agency-cards></div>
66
- ${inviteCard}
67
- </div>
68
- <div class="rk-agency-empty" data-rk-agency-empty hidden>
69
- <p class="rk-muted" data-rk-agency-empty-msg>No cached accounts found in this browser yet.</p>
70
- </div>
71
- </section>
72
- </main>`;
73
- const pageCss = `
74
- .rk-agency-head{grid-template-columns:minmax(0,1fr) auto;align-items:end;gap:20px}
75
- .rk-agency-head-copy{display:grid;gap:4px;min-width:0}
76
- .rk-agency-head-cta{display:flex;align-items:center;gap:12px;flex:none}
77
- .rk-agency-hint{font-size:13px;color:var(--rk-text-muted);white-space:normal;overflow-wrap:anywhere}
78
- @media(max-width:760px){.rk-agency-head{grid-template-columns:1fr}.rk-agency-head-cta{flex-wrap:wrap}}
79
-
80
- .rk-agency-panel{background:var(--rk-bg-section);border:1px solid var(--rk-border);border-radius:var(--rk-r-3xl);padding:24px}
81
- .rk-agency-toolbar{margin-bottom:20px}
82
- .rk-agency-toolbar-copy{display:grid;gap:4px}
83
- .rk-agency-h2{font-size:var(--rk-text-2xl);letter-spacing:-.02em;color:var(--rk-ink)}
84
- .rk-agency-search-wrap{position:relative;width:min(360px,100%)}
85
- .rk-agency-search-ic{position:absolute;left:15px;top:50%;transform:translateY(-50%);font-size:14px;opacity:.6;pointer-events:none}
86
- .rk-agency-search{padding-left:40px;background:#fff;border-radius:var(--rk-r-full)}
87
- @media(max-width:620px){.rk-agency-search-wrap{width:100%}}
88
-
89
- .rk-agency-grid{align-items:stretch}
90
- /* the JS card wrapper is transparent to the grid so cards + invite share tracks */
91
- .rk-agency-cards{display:contents}
92
- .rk-agency-card{padding:22px;display:flex;flex-direction:column;gap:16px}
93
- .rk-agency-card-top{display:grid;grid-template-columns:auto minmax(0,1fr) auto;gap:12px;align-items:start}
94
- .rk-agency-avatar{font-family:var(--rk-font-display);font-weight:800;font-size:22px}
95
- .rk-agency-card-id{min-width:0;display:grid;gap:2px}
96
- .rk-agency-name{font-size:var(--rk-text-lg);font-weight:800;font-family:var(--rk-font-display);color:var(--rk-ink);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
97
- .rk-agency-email{font-size:13px;color:var(--rk-text-muted);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;cursor:pointer;transition:color var(--rk-dur) var(--rk-ease)}
98
- .rk-agency-email:hover{color:var(--rk-gold-700)}
99
- .rk-agency-seen-pill{flex:none;align-self:start;white-space:nowrap}
100
- .rk-agency-idrow{display:flex}
101
- .rk-agency-user{font-family:var(--rk-font-mono);font-size:12px;color:var(--rk-text-faint);cursor:pointer;transition:color var(--rk-dur) var(--rk-ease);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:100%}
102
- .rk-agency-user:hover{color:var(--rk-n-600)}
103
- .rk-agency-card-foot{display:flex;align-items:center;flex-wrap:wrap;gap:8px;margin-top:auto}
104
- .rk-agency-open{font-weight:600}
105
-
106
- .rk-agency-invite{border-style:dashed;border-color:var(--rk-border-strong);background:linear-gradient(180deg,var(--rk-gold-tint),#fff 70%);display:flex;flex-direction:column;align-items:center;justify-content:center;text-align:center;gap:12px;min-height:240px}
107
- .rk-agency-invite-ic{display:grid;place-items:center}
108
- .rk-agency-invite-title{font-size:var(--rk-text-lg);font-family:var(--rk-font-display);font-weight:800;color:var(--rk-ink)}
109
- .rk-agency-invite-copy{font-size:13px;max-width:26ch}
110
-
111
- .rk-agency-empty{display:none;flex-direction:column;align-items:center;gap:12px;padding:56px 20px;text-align:center}
112
- .rk-agency-empty[data-rk-shown="1"]{display:flex}
113
- `;
114
- // Client-side: replicate the live localStorage account-registry read, build
115
- // real farmville client cards, and wire the real actions. Written with string
116
- // concatenation (not template literals) to avoid nested-`${}` escaping.
117
- const script = `
118
- (function(){
119
- var ACCOUNT_REGISTRY_INDEX_KEY = "vidfarm:agency:accounts:index";
120
- var ACCOUNT_REGISTRY_ENTRY_PREFIX = "vidfarm:agency:account_";
121
- var TINTS = ["gold","sky","violet","green","coral","ink"];
122
-
123
- var root=document;
124
- var cardsWrap=root.querySelector('[data-rk-agency-cards]');
125
- var invite=root.querySelector('[data-rk-agency-invite]');
126
- var search=root.querySelector('[data-rk-agency-search]');
127
- var empty=root.querySelector('[data-rk-agency-empty]');
128
- var emptyMsg=root.querySelector('[data-rk-agency-empty-msg]');
129
- var countEl=root.querySelector('[data-rk-agency-count]');
130
- var pluralEl=root.querySelector('[data-rk-agency-plural]');
131
- var searchQuery="";
132
-
133
- function escapeHtml(value){
134
- return String(value==null?"":value)
135
- .replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;")
136
- .replace(/"/g,"&quot;").replace(/'/g,"&#39;");
137
- }
138
-
139
- function initials(name){
140
- var letters=String(name||"").replace(/&/g," ").split(/\\s+/)
141
- .map(function(w){var m=w.match(/[a-z0-9]/i);return m?m[0]:"";})
142
- .filter(Boolean);
143
- var out=(letters[0]||"")+(letters[1]||"");
144
- return (out||"?").toUpperCase();
145
- }
146
-
147
- function tintFor(seed){
148
- var s=String(seed||""),h=0;
149
- for(var i=0;i<s.length;i++){h=(h*31+s.charCodeAt(i))>>>0;}
150
- return TINTS[h%TINTS.length];
151
- }
152
-
153
- function relTime(iso){
154
- var t=Date.parse(iso||"");
155
- if(isNaN(t))return "";
156
- var diff=Date.now()-t; if(diff<0)diff=0;
157
- var m=Math.floor(diff/60000);
158
- if(m<1)return "just now";
159
- if(m<60)return m+"m ago";
160
- var hr=Math.floor(m/60);
161
- if(hr<24)return hr+"h ago";
162
- var d=Math.floor(hr/24);
163
- if(d<7)return d+"d ago";
164
- try{return new Date(t).toLocaleDateString("en-US",{month:"short",day:"numeric"});}catch(_){return d+"d ago";}
165
- }
166
-
167
- function readAccountIds(){
168
- try{
169
- var raw=window.localStorage.getItem(ACCOUNT_REGISTRY_INDEX_KEY);
170
- var parsed=raw?JSON.parse(raw):[];
171
- return Array.isArray(parsed)?parsed.filter(function(v){return typeof v==="string"&&v;}):[];
172
- }catch(_){return [];}
173
- }
174
-
175
- function readAccounts(){
176
- return readAccountIds().map(function(userId){
177
- try{
178
- var raw=window.localStorage.getItem(ACCOUNT_REGISTRY_ENTRY_PREFIX+userId);
179
- if(!raw)return null;
180
- var parsed=JSON.parse(raw);
181
- if(!parsed||typeof parsed!=="object"||typeof parsed.apiKey!=="string"||!parsed.apiKey)return null;
182
- return {
183
- userId:userId,
184
- email:typeof parsed.email==="string"?parsed.email:"",
185
- displayName:typeof parsed.displayName==="string"?parsed.displayName:"",
186
- lastSeenAt:typeof parsed.lastSeenAt==="string"?parsed.lastSeenAt:"",
187
- autologinUrl:typeof parsed.autologinUrl==="string"?parsed.autologinUrl:"/auto-login?api_key="+encodeURIComponent(parsed.apiKey)
188
- };
189
- }catch(_){return null;}
190
- }).filter(Boolean).sort(function(a,b){return Date.parse(b.lastSeenAt||"")-Date.parse(a.lastSeenAt||"");});
191
- }
192
-
193
- function matchesSearch(account){
194
- var needle=searchQuery.trim().toLowerCase();
195
- if(!needle)return true;
196
- return [account.displayName,account.email,account.userId]
197
- .some(function(v){return String(v||"").toLowerCase().indexOf(needle)!==-1;});
198
- }
199
-
200
- function cardHtml(account){
201
- var title=account.displayName||account.email||account.userId;
202
- var tint=tintFor(account.userId);
203
- var seen=relTime(account.lastSeenAt);
204
- var seenPill=seen?'<span class="rk-pill rk-agency-seen-pill">Active '+escapeHtml(seen)+'</span>':'';
205
- var openTarget="/u/"+encodeURIComponent(account.userId)+"/library";
206
- return ''
207
- +'<article class="rk-card rk-card-hover rk-agency-card">'
208
- +'<div class="rk-agency-card-top">'
209
- +'<span class="rk-tile rk-tile-lg rk-tile-'+tint+' rk-agency-avatar">'+escapeHtml(initials(title))+'</span>'
210
- +'<div class="rk-agency-card-id">'
211
- +'<h3 class="rk-agency-name">'+escapeHtml(title)+'</h3>'
212
- +'<div class="rk-agency-email" role="button" tabindex="0" data-rk-copy-text="'+escapeHtml(account.email||"")+'" title="Copy email">'+escapeHtml(account.email||"No email")+'</div>'
213
- +'</div>'
214
- +seenPill
215
- +'</div>'
216
- +'<div class="rk-agency-idrow">'
217
- +'<span class="rk-agency-user" role="button" tabindex="0" data-rk-copy-text="'+escapeHtml(account.userId)+'" title="Copy user id">User '+escapeHtml(account.userId)+'</span>'
218
- +'</div>'
219
- +'<div class="rk-agency-card-foot">'
220
- +'<button class="rk-btn rk-btn-ink rk-btn-sm rk-agency-open" type="button" data-rk-open-tab="'+escapeHtml(openTarget)+'">Open workspace <span class="rk-arrow">→</span></button>'
221
- +'<button class="rk-btn rk-btn-ghost rk-btn-sm" type="button" data-rk-copy-login="'+escapeHtml(account.autologinUrl)+'">Copy login</button>'
222
- +'</div>'
223
- +'</article>';
224
- }
225
-
226
- function flash(el,label){
227
- if(!el)return;
228
- var prev=el.textContent;
229
- el.textContent=label;
230
- window.setTimeout(function(){el.textContent=prev;},1300);
231
- }
232
- function copy(value,el){
233
- if(!value)return;
234
- if(navigator.clipboard&&navigator.clipboard.writeText){
235
- navigator.clipboard.writeText(value).then(function(){flash(el,"Copied");},function(){flash(el,"Clipboard blocked");});
236
- }else{flash(el,"Clipboard blocked");}
237
- }
238
-
239
- function wire(){
240
- if(!cardsWrap)return;
241
- cardsWrap.querySelectorAll('[data-rk-copy-text]').forEach(function(el){
242
- var run=function(){copy(el.getAttribute('data-rk-copy-text'),el);};
243
- el.addEventListener('click',run);
244
- el.addEventListener('keydown',function(e){if(e.key==='Enter'||e.key===' '){e.preventDefault();run();}});
245
- });
246
- cardsWrap.querySelectorAll('[data-rk-copy-login]').forEach(function(btn){
247
- btn.addEventListener('click',function(){
248
- var rel=btn.getAttribute('data-rk-copy-login')||'';
249
- var abs=rel;try{abs=new URL(rel,window.location.origin).toString();}catch(_){}
250
- copy(abs,btn);
251
- });
252
- });
253
- cardsWrap.querySelectorAll('[data-rk-open-tab]').forEach(function(btn){
254
- btn.addEventListener('click',function(){
255
- var target=btn.getAttribute('data-rk-open-tab');
256
- if(target)window.open(target,'_blank','noopener,noreferrer');
257
- });
258
- });
259
- }
260
-
261
- function render(){
262
- if(!cardsWrap)return;
263
- var accounts=readAccounts();
264
- var total=accounts.length;
265
- if(countEl)countEl.textContent=String(total);
266
- if(pluralEl)pluralEl.textContent=total===1?'':'s';
267
-
268
- var filtered=accounts.filter(matchesSearch);
269
- cardsWrap.innerHTML=filtered.map(cardHtml).join('');
270
- wire();
271
-
272
- // hide the "add client" invite while actively searching (it isn't a result)
273
- if(invite)invite.hidden=!!searchQuery.trim();
274
-
275
- if(empty&&emptyMsg){
276
- var showEmpty=false,msg='';
277
- if(total===0){showEmpty=true;msg='No cached accounts found in this browser yet.';}
278
- else if(filtered.length===0){showEmpty=true;msg='No agency clients match that search.';}
279
- emptyMsg.textContent=msg;
280
- empty.hidden=!showEmpty;
281
- empty.setAttribute('data-rk-shown',showEmpty?'1':'0');
282
- }
283
- }
284
-
285
- if(search){
286
- search.addEventListener('input',function(){searchQuery=search.value||'';render();});
287
- }
288
- render();
289
- })();`;
290
- return reskinDocument({
291
- title: "vidfarm reskin — Agency",
292
- description: "Reskinned vidfarm agency page — cached client workspaces, open library, copy autologin.",
293
- activeSlug: "agency",
294
- pageCss,
295
- body,
296
- script
297
- });
298
- }
299
- //# sourceMappingURL=agency-page.js.map