@mevdragon/vidfarm-devcli 0.20.6 → 0.20.8

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 (154) 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 +111 -0
  8. package/.agents/skills/vidfarm-director/references/automation-and-local-dev.md +243 -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 +265 -0
  13. package/SKILL.director.md +476 -260
  14. package/dist/src/cli.js +362 -96
  15. package/dist/src/devcli/clips.js +15 -10
  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 +10 -0
  20. package/package.json +30 -10
  21. package/SKILL.platform.md +0 -432
  22. package/demo/README.md +0 -28
  23. package/demo/dist/app.css +0 -1
  24. package/demo/dist/app.js +0 -1850
  25. package/demo/dist/chunks/chunk-DXB73IDG.js +0 -1
  26. package/demo/dist/chunks/chunk-S7OWAJDS.js +0 -36
  27. package/demo/dist/chunks/chunk-VTIBZ6AN.js +0 -1
  28. package/demo/dist/chunks/dist-ADSJKBVE.js +0 -332
  29. package/demo/dist/chunks/domEditingLayers-VZMLL4AP-SGHWPND4.js +0 -1
  30. package/demo/dist/chunks/hyperframes-player-XB65TCD6.js +0 -425
  31. package/demo/dist/chunks/lib-XAQ37YOE.js +0 -1
  32. package/demo/dist/chunks/src-TJ2QYA4U.js +0 -207
  33. package/demo/dist/favicon.ico +0 -0
  34. package/demo/dist/icons/timeline/audio.svg +0 -7
  35. package/demo/dist/icons/timeline/captions.svg +0 -5
  36. package/demo/dist/icons/timeline/composition.svg +0 -12
  37. package/demo/dist/icons/timeline/image.svg +0 -18
  38. package/demo/dist/icons/timeline/music.svg +0 -10
  39. package/demo/dist/icons/timeline/text.svg +0 -3
  40. package/demo/dist/index.html +0 -15
  41. package/dist/src/account-pages-legacy.js +0 -9396
  42. package/dist/src/account-pages.js +0 -61
  43. package/dist/src/app.js +0 -21603
  44. package/dist/src/composition-runtime.js +0 -1053
  45. package/dist/src/config.js +0 -217
  46. package/dist/src/context.js +0 -447
  47. package/dist/src/dev-app-legacy.js +0 -739
  48. package/dist/src/dev-app.js +0 -6
  49. package/dist/src/devcli/migrate-local.js +0 -140
  50. package/dist/src/devcli/sync.js +0 -368
  51. package/dist/src/domain.js +0 -5
  52. package/dist/src/editor-chat-history.js +0 -82
  53. package/dist/src/editor-chat.js +0 -828
  54. package/dist/src/editor-dark-theme.js +0 -1128
  55. package/dist/src/frontend/debug.js +0 -71
  56. package/dist/src/frontend/discover-client.js +0 -130
  57. package/dist/src/frontend/discover-store.js +0 -23
  58. package/dist/src/frontend/file-directory.js +0 -1018
  59. package/dist/src/frontend/flockposter-cache-store.js +0 -124
  60. package/dist/src/frontend/homepage-client.js +0 -446
  61. package/dist/src/frontend/homepage-shared.js +0 -201
  62. package/dist/src/frontend/homepage-store.js +0 -66
  63. package/dist/src/frontend/homepage-view.js +0 -705
  64. package/dist/src/frontend/page-runtime-client.js +0 -132
  65. package/dist/src/frontend/page-runtime-store.js +0 -9
  66. package/dist/src/frontend/sentry.js +0 -42
  67. package/dist/src/frontend/template-editor-chat.js +0 -4181
  68. package/dist/src/help-page.js +0 -346
  69. package/dist/src/homepage.js +0 -1458
  70. package/dist/src/index.js +0 -16
  71. package/dist/src/instrument.js +0 -30
  72. package/dist/src/landing-page.js +0 -384
  73. package/dist/src/page-runtime.js +0 -2
  74. package/dist/src/page-shell.js +0 -1452
  75. package/dist/src/primitive-context.js +0 -416
  76. package/dist/src/primitive-registry.js +0 -3940
  77. package/dist/src/primitive-sdk.js +0 -4
  78. package/dist/src/primitives/hyperframes-media.js +0 -108
  79. package/dist/src/react-page-shell.js +0 -35
  80. package/dist/src/ready-post-schedule-component.js +0 -1540
  81. package/dist/src/registry.js +0 -296
  82. package/dist/src/reskin/agency-page.js +0 -299
  83. package/dist/src/reskin/calendar-page.js +0 -568
  84. package/dist/src/reskin/chat-page.js +0 -942
  85. package/dist/src/reskin/discover-page.js +0 -1788
  86. package/dist/src/reskin/document.js +0 -1587
  87. package/dist/src/reskin/help-page.js +0 -357
  88. package/dist/src/reskin/index-page.js +0 -62
  89. package/dist/src/reskin/inpaint-clipper-page.js +0 -890
  90. package/dist/src/reskin/inpaint-page.js +0 -2554
  91. package/dist/src/reskin/inpaint-video-page.js +0 -1339
  92. package/dist/src/reskin/job-runs-page.js +0 -477
  93. package/dist/src/reskin/library-page.js +0 -1634
  94. package/dist/src/reskin/login-page.js +0 -262
  95. package/dist/src/reskin/portfolio-page.js +0 -687
  96. package/dist/src/reskin/pricing-page.js +0 -390
  97. package/dist/src/reskin/settings-page.js +0 -732
  98. package/dist/src/reskin/theme.js +0 -711
  99. package/dist/src/runtime.js +0 -35
  100. package/dist/src/services/api-call-history.js +0 -249
  101. package/dist/src/services/auth.js +0 -152
  102. package/dist/src/services/billing-pricing.js +0 -39
  103. package/dist/src/services/billing.js +0 -241
  104. package/dist/src/services/cast.js +0 -127
  105. package/dist/src/services/chat-threads.js +0 -92
  106. package/dist/src/services/clip-records.js +0 -250
  107. package/dist/src/services/clip-search.js +0 -77
  108. package/dist/src/services/clip-vectors.js +0 -125
  109. package/dist/src/services/composition-sanitize.js +0 -124
  110. package/dist/src/services/composition-watch.js +0 -79
  111. package/dist/src/services/elevenlabs.js +0 -222
  112. package/dist/src/services/file-directory.js +0 -117
  113. package/dist/src/services/fork-access.js +0 -93
  114. package/dist/src/services/fork-manifest.js +0 -42
  115. package/dist/src/services/ghostcut.js +0 -179
  116. package/dist/src/services/hyperframes.js +0 -3654
  117. package/dist/src/services/job-capacity.js +0 -14
  118. package/dist/src/services/job-logs.js +0 -197
  119. package/dist/src/services/jobs.js +0 -136
  120. package/dist/src/services/local-dynamo.js +0 -0
  121. package/dist/src/services/media-processing.js +0 -766
  122. package/dist/src/services/primitive-media-lambda.js +0 -280
  123. package/dist/src/services/providers.js +0 -2748
  124. package/dist/src/services/rate-limits.js +0 -262
  125. package/dist/src/services/scene-annotations.js +0 -32
  126. package/dist/src/services/serverless-auth.js +0 -382
  127. package/dist/src/services/serverless-jobs.js +0 -1084
  128. package/dist/src/services/serverless-provider-keys.js +0 -409
  129. package/dist/src/services/serverless-records.js +0 -1515
  130. package/dist/src/services/serverless-template-configs.js +0 -75
  131. package/dist/src/services/storage.js +0 -461
  132. package/dist/src/services/swipe-customize.js +0 -437
  133. package/dist/src/services/template-certification.js +0 -413
  134. package/dist/src/services/template-loader.js +0 -99
  135. package/dist/src/services/template-runtime-bundles.js +0 -217
  136. package/dist/src/services/template-sources.js +0 -1017
  137. package/dist/src/services/upstream.js +0 -248
  138. package/dist/src/services/video-normalization.js +0 -2
  139. package/dist/src/services/webhooks.js +0 -62
  140. package/dist/src/template-editor-pages.js +0 -2576
  141. package/dist/src/template-editor-shell.js +0 -2893
  142. package/dist/src/template-sdk.js +0 -4
  143. package/dist/src/worker.js +0 -17
  144. package/public/assets/discover-client-app.js +0 -1
  145. package/public/assets/file-directory-app.js +0 -3
  146. package/public/assets/homepage-app.js +0 -54
  147. package/public/assets/homepage-client-app.js +0 -80
  148. package/public/assets/page-runtime-client-app.js +0 -94
  149. package/public/assets/placeholders/scene-placeholder.png +0 -0
  150. package/src/assets/SELLING_AWARENESS_STAGES.md +0 -579
  151. package/src/assets/SELLING_WITH_HOOKS.md +0 -377
  152. package/src/assets/SELLING_WITH_VSLS.md +0 -606
  153. package/src/assets/favicon.ico +0 -0
  154. 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