@mevdragon/vidfarm-devcli 0.2.12 → 0.2.14

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 (205) hide show
  1. package/.env.example +37 -1
  2. package/README.md +27 -183
  3. package/SKILL.director.md +293 -0
  4. package/SKILL.platform.md +312 -0
  5. package/auto-create-hyperframe-templates/AUTO_CREATE_HYPERFRAME_TEMPLATES.md +275 -0
  6. package/auto-create-hyperframe-templates/extractor-system-prompt.md +128 -0
  7. package/auto-create-hyperframe-templates/input-manifest.schema.json +56 -0
  8. package/auto-create-hyperframe-templates/lambda-memory-estimator.md +118 -0
  9. package/auto-create-hyperframe-templates/production-graph.schema.json +314 -0
  10. package/auto-create-hyperframe-templates/runbook.md +198 -0
  11. package/auto-create-hyperframe-templates/scripts/create-hyperframe-template.mjs +368 -0
  12. package/auto-create-hyperframe-templates/template-plan.schema.json +311 -0
  13. package/auto-create-hyperframe-templates/template-planner-prompt.md +144 -0
  14. package/auto-create-templates/AUTO_CREATE_TEMPLATES.md +116 -0
  15. package/auto-create-templates/extractor-system-prompt.md +6 -1
  16. package/auto-create-templates/template-plan.schema.json +73 -2
  17. package/auto-create-templates/template-planner-prompt.md +62 -1
  18. package/dist/src/account-pages-legacy.js +363 -89
  19. package/dist/src/account-pages-legacy.js.map +1 -0
  20. package/dist/src/account-pages.js +3 -2
  21. package/dist/src/account-pages.js.map +1 -0
  22. package/dist/src/app.js +7096 -1294
  23. package/dist/src/app.js.map +1 -0
  24. package/dist/src/cli.js +115 -29
  25. package/dist/src/cli.js.map +1 -0
  26. package/dist/src/composition-runtime.js +581 -0
  27. package/dist/src/composition-runtime.js.map +1 -0
  28. package/dist/src/config.js +69 -11
  29. package/dist/src/config.js.map +1 -0
  30. package/dist/src/context.js +280 -21
  31. package/dist/src/context.js.map +1 -0
  32. package/dist/src/dev-app-legacy.js +18 -17
  33. package/dist/src/dev-app-legacy.js.map +1 -0
  34. package/dist/src/dev-app.js +1 -0
  35. package/dist/src/dev-app.js.map +1 -0
  36. package/dist/src/dev-serve.js +169 -0
  37. package/dist/src/dev-serve.js.map +1 -0
  38. package/dist/src/domain.js +2 -1
  39. package/dist/src/domain.js.map +1 -0
  40. package/dist/src/editor-chat-history.js +10 -0
  41. package/dist/src/editor-chat-history.js.map +1 -0
  42. package/dist/src/editor-chat.js +135 -25
  43. package/dist/src/editor-chat.js.map +1 -0
  44. package/dist/src/editor-dark-theme.js +1128 -0
  45. package/dist/src/editor-dark-theme.js.map +1 -0
  46. package/dist/src/frontend/flockposter-cache-store.js +10 -2
  47. package/dist/src/frontend/flockposter-cache-store.js.map +1 -0
  48. package/dist/src/frontend/homepage-client.js +6 -3
  49. package/dist/src/frontend/homepage-client.js.map +1 -0
  50. package/dist/src/frontend/homepage-shared.js +1 -0
  51. package/dist/src/frontend/homepage-shared.js.map +1 -0
  52. package/dist/src/frontend/homepage-store.js +1 -0
  53. package/dist/src/frontend/homepage-store.js.map +1 -0
  54. package/dist/src/frontend/homepage-view.js +131 -18
  55. package/dist/src/frontend/homepage-view.js.map +1 -0
  56. package/dist/src/frontend/page-runtime-client.js +27 -3
  57. package/dist/src/frontend/page-runtime-client.js.map +1 -0
  58. package/dist/src/frontend/page-runtime-store.js +1 -0
  59. package/dist/src/frontend/page-runtime-store.js.map +1 -0
  60. package/dist/src/frontend/sentry.js +42 -0
  61. package/dist/src/frontend/sentry.js.map +1 -0
  62. package/dist/src/frontend/template-editor-chat.js +1711 -212
  63. package/dist/src/frontend/template-editor-chat.js.map +1 -0
  64. package/dist/src/help-page.js +333 -0
  65. package/dist/src/help-page.js.map +1 -0
  66. package/dist/src/homepage.js +169 -47
  67. package/dist/src/homepage.js.map +1 -0
  68. package/dist/src/hyperframes/composition.js +180 -0
  69. package/dist/src/hyperframes/composition.js.map +1 -0
  70. package/dist/src/index.js +1 -0
  71. package/dist/src/index.js.map +1 -0
  72. package/dist/src/instrument.js +30 -0
  73. package/dist/src/instrument.js.map +1 -0
  74. package/dist/src/lib/crypto.js +1 -0
  75. package/dist/src/lib/crypto.js.map +1 -0
  76. package/dist/src/lib/dev-log.js +54 -0
  77. package/dist/src/lib/dev-log.js.map +1 -0
  78. package/dist/src/lib/display-name.js +11 -0
  79. package/dist/src/lib/display-name.js.map +1 -0
  80. package/dist/src/lib/ids.js +21 -1
  81. package/dist/src/lib/ids.js.map +1 -0
  82. package/dist/src/lib/images.js +1 -0
  83. package/dist/src/lib/images.js.map +1 -0
  84. package/dist/src/lib/json.js +1 -0
  85. package/dist/src/lib/json.js.map +1 -0
  86. package/dist/src/lib/template-dna.js +1 -0
  87. package/dist/src/lib/template-dna.js.map +1 -0
  88. package/dist/src/lib/template-paths.js +1 -0
  89. package/dist/src/lib/template-paths.js.map +1 -0
  90. package/dist/src/lib/template-style-options.js +29 -3
  91. package/dist/src/lib/template-style-options.js.map +1 -0
  92. package/dist/src/lib/time.js +1 -0
  93. package/dist/src/lib/time.js.map +1 -0
  94. package/dist/src/lib/video-quality-harness.js +60 -0
  95. package/dist/src/lib/video-quality-harness.js.map +1 -0
  96. package/dist/src/page-runtime.js +1 -0
  97. package/dist/src/page-runtime.js.map +1 -0
  98. package/dist/src/page-shell.js +403 -29
  99. package/dist/src/page-shell.js.map +1 -0
  100. package/dist/src/primitive-context.js +144 -8
  101. package/dist/src/primitive-context.js.map +1 -0
  102. package/dist/src/primitive-registry.js +1919 -78
  103. package/dist/src/primitive-registry.js.map +1 -0
  104. package/dist/src/primitive-sdk.js +1 -0
  105. package/dist/src/primitive-sdk.js.map +1 -0
  106. package/dist/src/primitives/remotion/html-image.js +3 -1
  107. package/dist/src/primitives/remotion/html-image.js.map +1 -0
  108. package/dist/src/primitives/remotion/media-slideshow.js +60 -0
  109. package/dist/src/primitives/remotion/media-slideshow.js.map +1 -0
  110. package/dist/src/react-page-shell.js +1 -0
  111. package/dist/src/react-page-shell.js.map +1 -0
  112. package/dist/src/ready-post-schedule-component.js +1 -0
  113. package/dist/src/ready-post-schedule-component.js.map +1 -0
  114. package/dist/src/registry.js +237 -24
  115. package/dist/src/registry.js.map +1 -0
  116. package/dist/src/runtime.js +3 -0
  117. package/dist/src/runtime.js.map +1 -0
  118. package/dist/src/services/api-call-history.js +4 -0
  119. package/dist/src/services/api-call-history.js.map +1 -0
  120. package/dist/src/services/auth.js +40 -50
  121. package/dist/src/services/auth.js.map +1 -0
  122. package/dist/src/services/billing.js +20 -44
  123. package/dist/src/services/billing.js.map +1 -0
  124. package/dist/src/services/chat-threads.js +10 -6
  125. package/dist/src/services/chat-threads.js.map +1 -0
  126. package/dist/src/services/fork-access.js +93 -0
  127. package/dist/src/services/fork-access.js.map +1 -0
  128. package/dist/src/services/fork-manifest.js +43 -0
  129. package/dist/src/services/fork-manifest.js.map +1 -0
  130. package/dist/src/services/ghostcut.js +132 -0
  131. package/dist/src/services/ghostcut.js.map +1 -0
  132. package/dist/src/services/hyperframes.js +1014 -0
  133. package/dist/src/services/hyperframes.js.map +1 -0
  134. package/dist/src/services/job-capacity.js +14 -0
  135. package/dist/src/services/job-capacity.js.map +1 -0
  136. package/dist/src/services/job-logs.js +4 -0
  137. package/dist/src/services/job-logs.js.map +1 -0
  138. package/dist/src/services/jobs.js +99 -91
  139. package/dist/src/services/jobs.js.map +1 -0
  140. package/dist/src/services/media-processing.js +743 -0
  141. package/dist/src/services/media-processing.js.map +1 -0
  142. package/dist/src/services/primitive-media-lambda.js +280 -0
  143. package/dist/src/services/primitive-media-lambda.js.map +1 -0
  144. package/dist/src/services/providers.js +1560 -178
  145. package/dist/src/services/providers.js.map +1 -0
  146. package/dist/src/services/rate-limits.js +3 -2
  147. package/dist/src/services/rate-limits.js.map +1 -0
  148. package/dist/src/services/remotion.js +495 -92
  149. package/dist/src/services/remotion.js.map +1 -0
  150. package/dist/src/services/serverless-auth.js +374 -0
  151. package/dist/src/services/serverless-auth.js.map +1 -0
  152. package/dist/src/services/serverless-jobs.js +1074 -0
  153. package/dist/src/services/serverless-jobs.js.map +1 -0
  154. package/dist/src/services/serverless-provider-keys.js +401 -0
  155. package/dist/src/services/serverless-provider-keys.js.map +1 -0
  156. package/dist/src/services/serverless-records.js +1088 -0
  157. package/dist/src/services/serverless-records.js.map +1 -0
  158. package/dist/src/services/serverless-template-configs.js +67 -0
  159. package/dist/src/services/serverless-template-configs.js.map +1 -0
  160. package/dist/src/services/storage.js +171 -35
  161. package/dist/src/services/storage.js.map +1 -0
  162. package/dist/src/services/template-certification.js +295 -3
  163. package/dist/src/services/template-certification.js.map +1 -0
  164. package/dist/src/services/template-loader.js +45 -1
  165. package/dist/src/services/template-loader.js.map +1 -0
  166. package/dist/src/services/template-runtime-bundles.js +217 -0
  167. package/dist/src/services/template-runtime-bundles.js.map +1 -0
  168. package/dist/src/services/template-sources.js +452 -87
  169. package/dist/src/services/template-sources.js.map +1 -0
  170. package/dist/src/services/video-normalization.js +2 -0
  171. package/dist/src/services/video-normalization.js.map +1 -0
  172. package/dist/src/services/webhooks.js +7 -6
  173. package/dist/src/services/webhooks.js.map +1 -0
  174. package/dist/src/template-editor-pages.js +2051 -1869
  175. package/dist/src/template-editor-pages.js.map +1 -0
  176. package/dist/src/template-editor-shell.js +1376 -181
  177. package/dist/src/template-editor-shell.js.map +1 -0
  178. package/dist/src/template-sdk.js +1 -0
  179. package/dist/src/template-sdk.js.map +1 -0
  180. package/dist/src/worker.js +11 -226
  181. package/dist/src/worker.js.map +1 -0
  182. package/package.json +47 -12
  183. package/public/assets/homepage-client-app.js +34 -8
  184. package/public/assets/page-runtime-client-app.js +44 -18
  185. package/readme.secret.md +89 -0
  186. package/templates/vidfarm_template_0000/README.md +106 -0
  187. package/templates/vidfarm_template_0000/SKILL.md +266 -0
  188. package/templates/vidfarm_template_0000/assets/Abel-Regular.ttf +0 -0
  189. package/templates/vidfarm_template_0000/assets/DMSerifDisplay-Regular.ttf +0 -0
  190. package/templates/vidfarm_template_0000/assets/Montserrat[wght].ttf +0 -0
  191. package/templates/vidfarm_template_0000/assets/SourceCodePro[wght].ttf +0 -0
  192. package/templates/vidfarm_template_0000/assets/TikTokSans-SemiBold.ttf +0 -0
  193. package/templates/vidfarm_template_0000/assets/Yesteryear-Regular.ttf +0 -0
  194. package/templates/vidfarm_template_0000/composition.json +11 -0
  195. package/templates/vidfarm_template_0000/src/lib/images.js +221 -0
  196. package/templates/vidfarm_template_0000/src/remotion/Root.js +33 -0
  197. package/templates/vidfarm_template_0000/src/remotion/index.js +3 -0
  198. package/templates/vidfarm_template_0000/src/sdk.js +3 -0
  199. package/templates/vidfarm_template_0000/src/style-options.js +200 -0
  200. package/templates/vidfarm_template_0000/src/template-dna.js +9 -0
  201. package/templates/vidfarm_template_0000/src/template.js +1566 -0
  202. package/templates/vidfarm_template_0000/template.config.json +21 -0
  203. package/GETTING_STARTED.developers.md +0 -87
  204. package/SKILL.developer.md +0 -1186
  205. package/dist/src/db.js +0 -2007
@@ -7,6 +7,7 @@ function shell(input) {
7
7
  body: input.body,
8
8
  shellClass: input.shellClass,
9
9
  mainClass: input.mainClass,
10
+ bodyClass: input.bodyClass,
10
11
  runtimeBoot: input.runtimeBoot,
11
12
  style: `
12
13
  .auth-layout {
@@ -549,8 +550,8 @@ export function renderSettingsPage(input) {
549
550
  if (event.entity_type === "wallet_funding_event") {
550
551
  return "Wallet top-up";
551
552
  }
552
- if (event.cost_center_slug === "remotion_lambda") {
553
- return "Cloud render usage";
553
+ if (event.cost_center_slug === "hyperframes_lambda" || event.cost_center_slug === "remotion_lambda") {
554
+ return "Timeline render usage";
554
555
  }
555
556
  if (event.cost_center_slug === "wallet_funding") {
556
557
  return "Wallet funding";
@@ -567,7 +568,7 @@ export function renderSettingsPage(input) {
567
568
  const renderWalletHistoryItem = (event) => {
568
569
  const amountClass = event.amount_usd >= 0 ? "is-credit" : "is-debit";
569
570
  const metaParts = [
570
- event.job_id ? `Job ${escapeHtml(event.job_id)}` : "",
571
+ event.job_id ? `Render ${escapeHtml(event.job_id)}` : "",
571
572
  event.tracer ? `Tracer ${escapeHtml(event.tracer)}` : "",
572
573
  event.client_reference_id ? `Client ref ${escapeHtml(event.client_reference_id)}` : "",
573
574
  event.event_id ? `Event ${escapeHtml(event.event_id)}` : ""
@@ -731,16 +732,6 @@ export function renderSettingsPage(input) {
731
732
  const attachments = input.attachments.length
732
733
  ? input.attachments.map((entry) => renderAttachmentCard(entry)).join("")
733
734
  : `<div class="empty-state" data-files-empty-state>No attachments uploaded yet.</div>`;
734
- const developerSkillButton = input.isDeveloper && input.developerSkill
735
- ? `
736
- <button
737
- type="button"
738
- class="secondary"
739
- data-copy-content='${escapeAttribute(input.developerSkill)}'
740
- data-copy-label="Developer SKILL.md"
741
- >Developer SKILL.md</button>
742
- `
743
- : "";
744
735
  const hasFlockPosterKey = Boolean(input.flockposterApiKey?.trim());
745
736
  const hasAnyChannel = hasFlockPosterKey || input.emailChannels.length > 0;
746
737
  const renderChannelMenu = (entry) => entry.type === "email"
@@ -756,7 +747,14 @@ export function renderSettingsPage(input) {
756
747
  </form>
757
748
  <form method="post" action="/settings/channels/email/${encodeURIComponent(entry.rawId ?? "")}/delete" data-email-delete-form>
758
749
  <input type="hidden" name="account" value="${escapeHtml(input.currentAccountId)}" />
759
- <button type="submit" class="danger-text">Delete</button>
750
+ <button type="submit" class="danger-text" data-email-delete-toggle aria-haspopup="dialog" aria-expanded="false">Delete</button>
751
+ <div class="channel-popconfirm" data-email-delete-confirm hidden>
752
+ <p>Delete this email channel?</p>
753
+ <div class="channel-popconfirm-actions">
754
+ <button type="button" class="secondary" data-email-delete-cancel>Cancel</button>
755
+ <button type="submit" class="danger" data-email-delete-confirm-button>Delete</button>
756
+ </div>
757
+ </div>
760
758
  </form>
761
759
  </div>
762
760
  </div>
@@ -920,7 +918,7 @@ export function renderSettingsPage(input) {
920
918
  <div class="settings-pane-head">
921
919
  <div class="label">Wallet</div>
922
920
  <h2>Wallet balance and usage billing</h2>
923
- <p>Your balance is denominated directly in USD. We do not use credits, and typical video costs can start from around $0.01 depending on template.</p>
921
+ <p>Your balance is denominated directly in USD. We do not use credits, and typical video costs depend on timeline length, layer count, and Lambda memory.</p>
924
922
  </div>
925
923
  <section class="developer-panel wallet-panel">
926
924
  <div class="wallet-hero">
@@ -1157,7 +1155,6 @@ export function renderSettingsPage(input) {
1157
1155
  data-copy-content='${escapeAttribute(input.directorSkill)}'
1158
1156
  data-copy-label="Director SKILL.md"
1159
1157
  >Director SKILL.md</button>
1160
- ${developerSkillButton}
1161
1158
  </div>
1162
1159
  </section>
1163
1160
  <section class="developer-panel">
@@ -1173,20 +1170,6 @@ export function renderSettingsPage(input) {
1173
1170
  ${providerKeyCreateDialog}
1174
1171
  </section>
1175
1172
  </section>
1176
- <dialog class="logout-dialog" data-logout-dialog>
1177
- <div class="logout-dialog-head">
1178
- <div class="label logout-dialog-kicker">Log out</div>
1179
- <h3>Log out of this browser?</h3>
1180
- <p>This clears the stored developer key, workspace access, and local session state for the current browser.</p>
1181
- </div>
1182
- <div class="toolbar logout-dialog-actions">
1183
- <button type="button" class="secondary" data-close-logout-dialog>Cancel</button>
1184
- <form method="post" action="/logout" data-logout-form>
1185
- <input type="hidden" name="account" value="${escapeHtml(input.currentAccountId)}" />
1186
- <button type="submit" class="danger">Yes, log out</button>
1187
- </form>
1188
- </div>
1189
- </dialog>
1190
1173
  </section>
1191
1174
  </div>
1192
1175
  </section>
@@ -1259,6 +1242,9 @@ export function renderSettingsPage(input) {
1259
1242
  function withAccountUrl(pathname) {
1260
1243
  const url = new URL(pathname, window.location.origin);
1261
1244
  if (ACCOUNT_USER_ID) {
1245
+ if (url.pathname === "/discover" || url.pathname.startsWith("/discover/") || url.pathname === "/help") {
1246
+ return url.pathname + url.search;
1247
+ }
1262
1248
  if (!url.pathname.startsWith("/api/v1") && !url.pathname.startsWith("/u/")) {
1263
1249
  return "/u/" + encodeURIComponent(ACCOUNT_USER_ID) + url.pathname + url.search;
1264
1250
  }
@@ -1299,8 +1285,9 @@ export function renderSettingsPage(input) {
1299
1285
  }
1300
1286
  try {
1301
1287
  const current = readFlockPosterCache();
1288
+ const emailChannels = Array.isArray(current.channels) ? current.channels.filter(isEmailCachedChannel) : [];
1302
1289
  window.localStorage.setItem(FLOCKPOSTER_CACHE_KEY, JSON.stringify({
1303
- channels,
1290
+ channels: [...emailChannels, ...channels.filter((channel) => !isEmailCachedChannel(channel))],
1304
1291
  posts: Array.isArray(current.posts) ? current.posts : [],
1305
1292
  channelsUpdatedAt: new Date().toISOString(),
1306
1293
  calendarUpdatedAt: current.calendarUpdatedAt || null,
@@ -1312,6 +1299,12 @@ export function renderSettingsPage(input) {
1312
1299
  }
1313
1300
  }
1314
1301
 
1302
+ function isEmailCachedChannel(channel) {
1303
+ const id = String(channel && channel.id || "");
1304
+ const platform = String(channel && channel.platform || "").toLowerCase();
1305
+ return id.startsWith("email:") || platform === "email";
1306
+ }
1307
+
1315
1308
  function renderLoadingInline(label) {
1316
1309
  return '<span class="loading-inline"><span class="loading-spinner" aria-hidden="true"></span><span>' + escapeHtml(label) + '</span></span>';
1317
1310
  }
@@ -1331,7 +1324,7 @@ export function renderSettingsPage(input) {
1331
1324
  currentTabLabel.textContent = tab.charAt(0).toUpperCase() + tab.slice(1);
1332
1325
  }
1333
1326
  const settingsNavPanel = document.querySelector(".settings-nav-panel");
1334
- if (settingsNavPanel instanceof HTMLDetailsElement && window.matchMedia("(max-width: 767px)").matches) {
1327
+ if (settingsNavPanel instanceof HTMLDetailsElement && window.matchMedia("(max-width: 1024px)").matches) {
1335
1328
  settingsNavPanel.open = false;
1336
1329
  }
1337
1330
  const url = new URL(window.location.href);
@@ -1360,7 +1353,7 @@ export function renderSettingsPage(input) {
1360
1353
  settingsNavPanel.open = !phoneSettingsQuery.matches;
1361
1354
  }
1362
1355
  }
1363
- const phoneSettingsQuery = window.matchMedia("(max-width: 767px)");
1356
+ const phoneSettingsQuery = window.matchMedia("(max-width: 1024px)");
1364
1357
  syncSettingsNavPanelState();
1365
1358
  phoneSettingsQuery.addEventListener("change", syncSettingsNavPanelState);
1366
1359
  document.querySelectorAll(".topbar-actions a, .brand-lockup").forEach((link) => {
@@ -1460,11 +1453,18 @@ export function renderSettingsPage(input) {
1460
1453
  const filesList = document.querySelector("[data-files-list]");
1461
1454
  const filesStatus = document.querySelector("[data-files-status]");
1462
1455
  const hasFlockPosterKey = ${hasFlockPosterKey ? "true" : "false"};
1456
+ const billingUrl = ${escapeJsonForHtml(input.billingUrl)};
1457
+ const vidfarmApiKey = ${escapeJsonForHtml(input.vidfarmApiKey)};
1458
+ const currentAccountId = ${escapeJsonForHtml(input.currentAccountId)};
1459
+ const walletCheckoutBaseUrl = "https://buy.stripe.com/3cIfZggERgLsfVzfKR53O0H";
1463
1460
  const shouldAutoOpenFlockPosterDialog = ${input.openFlockPosterDialog ? "true" : "false"};
1461
+ const shouldRefreshFlockPosterChannels = new URLSearchParams(window.location.search).get("refresh") === "channels";
1464
1462
  const initialEmailChannels = ${escapeJsonForHtml(input.emailChannels)};
1465
1463
  registerCurrentAccount();
1466
1464
  const cachedFlockPosterState = readFlockPosterCache();
1467
- const cachedFlockPosterChannels = Array.isArray(cachedFlockPosterState.channels) ? cachedFlockPosterState.channels : [];
1465
+ const cachedFlockPosterChannels = Array.isArray(cachedFlockPosterState.channels)
1466
+ ? cachedFlockPosterState.channels.filter((channel) => !isEmailCachedChannel(channel))
1467
+ : [];
1468
1468
  let flockPosterChannelsState = Array.isArray(initialEmailChannels) ? initialEmailChannels : [];
1469
1469
  let flockPosterSearchQuery = "";
1470
1470
  let hasRenderedCachedFlockPosterChannels = false;
@@ -1504,8 +1504,8 @@ export function renderSettingsPage(input) {
1504
1504
  if (event.entity_type === "wallet_funding_event") {
1505
1505
  return "Wallet top-up";
1506
1506
  }
1507
- if (event.cost_center_slug === "remotion_lambda") {
1508
- return "Cloud render usage";
1507
+ if (event.cost_center_slug === "hyperframes_lambda" || event.cost_center_slug === "remotion_lambda") {
1508
+ return "Timeline render usage";
1509
1509
  }
1510
1510
  if (event.cost_center_slug === "wallet_funding") {
1511
1511
  return "Wallet funding";
@@ -1515,7 +1515,7 @@ export function renderSettingsPage(input) {
1515
1515
  function renderWalletHistoryItem(event) {
1516
1516
  const amount = Number(event.amount_usd || 0);
1517
1517
  const meta = [
1518
- event.job_id ? "Job " + escapeHtml(event.job_id) : "",
1518
+ event.job_id ? "Render " + escapeHtml(event.job_id) : "",
1519
1519
  event.tracer ? "Tracer " + escapeHtml(event.tracer) : "",
1520
1520
  event.client_reference_id ? "Client ref " + escapeHtml(event.client_reference_id) : "",
1521
1521
  event.event_id ? "Event " + escapeHtml(event.event_id) : ""
@@ -1543,6 +1543,42 @@ export function renderSettingsPage(input) {
1543
1543
  anchor.click();
1544
1544
  anchor.remove();
1545
1545
  }
1546
+ function createWalletClientReferenceId() {
1547
+ if (window.crypto && typeof window.crypto.randomUUID === "function") {
1548
+ return "vidfarm_wallet_topup_" + window.crypto.randomUUID();
1549
+ }
1550
+ return "vidfarm_wallet_topup_" + "10000000-1000-4000-8000-" + String(Date.now()).padStart(12, "0").slice(-12);
1551
+ }
1552
+ async function createWalletCheckoutUrlDirect() {
1553
+ if (!billingUrl || !vidfarmApiKey) {
1554
+ throw new Error("Billing service is not configured.");
1555
+ }
1556
+ const clientReferenceId = createWalletClientReferenceId();
1557
+ const response = await fetch(new URL("/v1/wallet-funding-intents", billingUrl), {
1558
+ method: "POST",
1559
+ headers: {
1560
+ "content-type": "application/json",
1561
+ "vidfarm-api-key": vidfarmApiKey
1562
+ },
1563
+ body: JSON.stringify({
1564
+ customer_id: currentAccountId,
1565
+ client_reference_id: clientReferenceId,
1566
+ occurred_at_ms: Date.now(),
1567
+ metadata: {
1568
+ source: "settings_wallet_modal",
1569
+ customer_email: ${escapeJsonForHtml(input.email)}
1570
+ }
1571
+ })
1572
+ });
1573
+ const payload = await response.json().catch(() => ({}));
1574
+ if (!response.ok) {
1575
+ throw new Error(payload.error || "Unable to create payment link.");
1576
+ }
1577
+ const checkoutUrl = new URL(walletCheckoutBaseUrl);
1578
+ checkoutUrl.searchParams.set("client_reference_id", clientReferenceId);
1579
+ checkoutUrl.searchParams.set("prefilled_email", ${escapeJsonForHtml(input.email)});
1580
+ return checkoutUrl.toString();
1581
+ }
1546
1582
  openWalletFundingButton?.addEventListener("click", openWalletFundingDialog);
1547
1583
  closeWalletFundingButton?.addEventListener("click", closeWalletFundingDialog);
1548
1584
  walletFundingProceedButton?.addEventListener("click", async () => {
@@ -1551,18 +1587,22 @@ export function renderSettingsPage(input) {
1551
1587
  walletFundingProceedButton.disabled = true;
1552
1588
  walletFundingProceedButton.textContent = "Opening...";
1553
1589
  try {
1554
- const response = await fetch(withAccountUrl("/settings/wallet/funding-link"), {
1555
- method: "POST",
1556
- headers: {
1557
- "content-type": "application/json"
1558
- },
1559
- body: JSON.stringify({})
1560
- });
1561
- const payload = await response.json();
1562
- if (!response.ok || typeof payload.checkout_url !== "string" || !payload.checkout_url) {
1563
- throw new Error(payload.error || "Unable to create payment link.");
1564
- }
1565
- openWalletCheckoutInNewTab(payload.checkout_url, popup);
1590
+ const checkoutUrl = billingUrl
1591
+ ? await createWalletCheckoutUrlDirect()
1592
+ : await fetch(withAccountUrl("/settings/wallet/funding-link"), {
1593
+ method: "POST",
1594
+ headers: {
1595
+ "content-type": "application/json"
1596
+ },
1597
+ body: JSON.stringify({})
1598
+ }).then(async (response) => {
1599
+ const payload = await response.json();
1600
+ if (!response.ok || typeof payload.checkout_url !== "string" || !payload.checkout_url) {
1601
+ throw new Error(payload.error || "Unable to create payment link.");
1602
+ }
1603
+ return payload.checkout_url;
1604
+ });
1605
+ openWalletCheckoutInNewTab(checkoutUrl, popup);
1566
1606
  closeWalletFundingDialog();
1567
1607
  } catch (error) {
1568
1608
  if (popup && !popup.closed) {
@@ -1584,7 +1624,13 @@ export function renderSettingsPage(input) {
1584
1624
  walletHistoryLoadMoreButton.disabled = true;
1585
1625
  walletHistoryLoadMoreButton.textContent = "Loading...";
1586
1626
  try {
1587
- const response = await fetch(withAccountUrl("/settings/wallet/events?limit=50&cursor=" + encodeURIComponent(cursor)));
1627
+ const response = billingUrl && vidfarmApiKey
1628
+ ? await fetch(new URL("/v1/wallets/" + encodeURIComponent(currentAccountId) + "/events?limit=50&cursor=" + encodeURIComponent(cursor), billingUrl), {
1629
+ headers: {
1630
+ "vidfarm-api-key": vidfarmApiKey
1631
+ }
1632
+ })
1633
+ : await fetch(withAccountUrl("/settings/wallet/events?limit=50&cursor=" + encodeURIComponent(cursor)));
1588
1634
  const payload = await response.json();
1589
1635
  if (!response.ok || !Array.isArray(payload.events)) {
1590
1636
  throw new Error(payload.error || "Unable to load billing history.");
@@ -2305,15 +2351,64 @@ export function renderSettingsPage(input) {
2305
2351
  }
2306
2352
  });
2307
2353
  }
2308
- document.querySelectorAll("[data-email-delete-form]").forEach((form) => {
2309
- form.addEventListener("submit", (event) => {
2310
- if (!window.confirm("Delete this email channel?")) {
2311
- event.preventDefault();
2354
+ function closeEmailDeletePopconfirms(exceptForm) {
2355
+ document.querySelectorAll("[data-email-delete-form]").forEach((form) => {
2356
+ if (!(form instanceof HTMLFormElement) || form === exceptForm) {
2357
+ return;
2312
2358
  }
2359
+ const confirm = form.querySelector("[data-email-delete-confirm]");
2360
+ const toggle = form.querySelector("[data-email-delete-toggle]");
2361
+ if (confirm instanceof HTMLElement) {
2362
+ confirm.hidden = true;
2363
+ }
2364
+ if (toggle instanceof HTMLButtonElement) {
2365
+ toggle.setAttribute("aria-expanded", "false");
2366
+ }
2367
+ delete form.dataset.confirmed;
2313
2368
  });
2314
- });
2369
+ }
2370
+
2371
+ function bindEmailDeletePopconfirms(root) {
2372
+ if (!(root instanceof Document || root instanceof HTMLElement)) {
2373
+ return;
2374
+ }
2375
+ root.querySelectorAll("[data-email-delete-form]").forEach((form) => {
2376
+ if (!(form instanceof HTMLFormElement) || form.dataset.deletePopconfirmBound === "true") {
2377
+ return;
2378
+ }
2379
+ form.dataset.deletePopconfirmBound = "true";
2380
+ const confirm = form.querySelector("[data-email-delete-confirm]");
2381
+ const toggle = form.querySelector("[data-email-delete-toggle]");
2382
+ const cancel = form.querySelector("[data-email-delete-cancel]");
2383
+ const confirmButton = form.querySelector("[data-email-delete-confirm-button]");
2384
+ if (!(confirm instanceof HTMLElement) || !(toggle instanceof HTMLButtonElement) || !(cancel instanceof HTMLButtonElement) || !(confirmButton instanceof HTMLButtonElement)) {
2385
+ return;
2386
+ }
2387
+ cancel.addEventListener("click", () => {
2388
+ confirm.hidden = true;
2389
+ toggle.setAttribute("aria-expanded", "false");
2390
+ delete form.dataset.confirmed;
2391
+ });
2392
+ form.addEventListener("submit", (event) => {
2393
+ if (form.dataset.confirmed === "true") {
2394
+ return;
2395
+ }
2396
+ event.preventDefault();
2397
+ closeEmailDeletePopconfirms(form);
2398
+ confirm.hidden = false;
2399
+ toggle.setAttribute("aria-expanded", "true");
2400
+ });
2401
+ confirmButton.addEventListener("click", () => {
2402
+ form.dataset.confirmed = "true";
2403
+ });
2404
+ });
2405
+ }
2406
+ bindEmailDeletePopconfirms(document);
2407
+ if (!hasFlockPosterKey && shouldRefreshFlockPosterChannels) {
2408
+ writeFlockPosterChannelsToCache([]);
2409
+ }
2315
2410
  if (hasFlockPosterKey && flockPosterHeading instanceof HTMLElement && flockPosterChannelList instanceof HTMLElement && flockPosterSection instanceof HTMLElement) {
2316
- if (cachedFlockPosterChannels.length) {
2411
+ if (cachedFlockPosterChannels.length && !shouldRefreshFlockPosterChannels) {
2317
2412
  renderFlockPosterChannels([...(Array.isArray(initialEmailChannels) ? initialEmailChannels : []), ...cachedFlockPosterChannels.map((channel) => ({ ...channel, type: "flockposter" }))]);
2318
2413
  hasRenderedCachedFlockPosterChannels = true;
2319
2414
  } else {
@@ -2394,6 +2489,7 @@ export function renderSettingsPage(input) {
2394
2489
  button.setAttribute("aria-expanded", "false");
2395
2490
  }
2396
2491
  });
2492
+ closeEmailDeletePopconfirms();
2397
2493
  }
2398
2494
 
2399
2495
  function bindChannelMenus(root) {
@@ -2500,6 +2596,7 @@ export function renderSettingsPage(input) {
2500
2596
  '</article>'
2501
2597
  )).join("");
2502
2598
  bindChannelMenus(flockPosterChannelList);
2599
+ bindEmailDeletePopconfirms(flockPosterChannelList);
2503
2600
  }
2504
2601
 
2505
2602
  function filterFlockPosterChannels(channels, query) {
@@ -2559,7 +2656,7 @@ export function renderSettingsPage(input) {
2559
2656
  + '<button type="button" data-copy-content="' + escapeHtml(entry.handle || "") + '">Copy email</button>'
2560
2657
  + '<button type="button" data-email-edit-open data-email-id="' + escapeHtml(rawId) + '" data-email-title="' + escapeHtml(String(entry.title || "").replace(/^Unverified - /, "")) + '" data-email-note="' + escapeHtml(entry.note || "") + '" data-email-avatar-url="' + escapeHtml(entry.avatarUrl || "") + '">Edit</button>'
2561
2658
  + '<form method="post" action="/settings/channels/email/' + encodeURIComponent(rawId) + '/resend"><input type="hidden" name="account" value="' + escapeHtml(ACCOUNT_USER_ID) + '" /><button type="submit">Resend verification</button></form>'
2562
- + '<form method="post" action="/settings/channels/email/' + encodeURIComponent(rawId) + '/delete" data-email-delete-form><input type="hidden" name="account" value="' + escapeHtml(ACCOUNT_USER_ID) + '" /><button type="submit" class="danger-text">Delete</button></form>'
2659
+ + '<form method="post" action="/settings/channels/email/' + encodeURIComponent(rawId) + '/delete" data-email-delete-form><input type="hidden" name="account" value="' + escapeHtml(ACCOUNT_USER_ID) + '" /><button type="submit" class="danger-text" data-email-delete-toggle aria-haspopup="dialog" aria-expanded="false">Delete</button><div class="channel-popconfirm" data-email-delete-confirm hidden><p>Delete this email channel?</p><div class="channel-popconfirm-actions"><button type="button" class="secondary" data-email-delete-cancel>Cancel</button><button type="submit" class="danger" data-email-delete-confirm-button>Delete</button></div></div></form>'
2563
2660
  + '</div></div>';
2564
2661
  }
2565
2662
  return ''
@@ -2626,6 +2723,10 @@ export function renderSettingsPage(input) {
2626
2723
  min-height: 0;
2627
2724
  gap: 24px;
2628
2725
  padding: 28px 26px 30px;
2726
+ overflow-y: auto;
2727
+ overscroll-behavior: contain;
2728
+ scrollbar-gutter: stable;
2729
+ -webkit-overflow-scrolling: touch;
2629
2730
  border-right: 1px solid rgba(209, 219, 233, 0.8);
2630
2731
  background: linear-gradient(180deg, rgba(255, 251, 245, 0.96), rgba(255, 248, 239, 0.88));
2631
2732
  }
@@ -3311,6 +3412,52 @@ export function renderSettingsPage(input) {
3311
3412
  color: #b63d3d;
3312
3413
  }
3313
3414
 
3415
+ .channel-popconfirm {
3416
+ display: grid;
3417
+ gap: 10px;
3418
+ margin: 4px 0 6px;
3419
+ padding: 12px;
3420
+ border: 1px solid rgba(216, 91, 91, 0.22);
3421
+ border-radius: 12px;
3422
+ background: rgba(255, 247, 244, 0.92);
3423
+ }
3424
+
3425
+ .channel-popconfirm[hidden] {
3426
+ display: none;
3427
+ }
3428
+
3429
+ .channel-popconfirm p {
3430
+ margin: 0;
3431
+ color: #6f3434;
3432
+ font-size: 0.88rem;
3433
+ font-weight: 700;
3434
+ line-height: 1.3;
3435
+ }
3436
+
3437
+ .channel-popconfirm-actions {
3438
+ display: flex;
3439
+ justify-content: flex-end;
3440
+ gap: 8px;
3441
+ }
3442
+
3443
+ .channel-menu .channel-popconfirm-actions button {
3444
+ justify-content: center;
3445
+ width: auto;
3446
+ min-height: 34px;
3447
+ padding: 7px 10px;
3448
+ border-radius: 10px;
3449
+ font-size: 0.84rem;
3450
+ }
3451
+
3452
+ .channel-menu .channel-popconfirm-actions .secondary {
3453
+ background: rgba(255, 255, 255, 0.76);
3454
+ }
3455
+
3456
+ .channel-menu .channel-popconfirm-actions .danger {
3457
+ background: linear-gradient(180deg, #c94e4e, #b63d3d);
3458
+ color: #fff7f7;
3459
+ }
3460
+
3314
3461
  .channel-list {
3315
3462
  display: grid;
3316
3463
  gap: 12px;
@@ -3645,7 +3792,7 @@ export function renderSettingsPage(input) {
3645
3792
  }
3646
3793
  }
3647
3794
 
3648
- @media (max-width: 767px) {
3795
+ @media (max-width: 1024px) {
3649
3796
  .settings-sidebar,
3650
3797
  .settings-content {
3651
3798
  padding: 18px;
@@ -4163,15 +4310,16 @@ export function renderJobRunsPage(input) {
4163
4310
  storageDriver: input.storageDriver,
4164
4311
  loadEndpoint: input.loadEndpoint,
4165
4312
  accountId: input.currentAccountId,
4166
- headerTitle: input.headerTitle ?? "Job runs.",
4313
+ headerTitle: input.headerTitle ?? "Render runs.",
4167
4314
  initialGroupBy: input.initialGroupBy ?? "tracer-route",
4168
4315
  methodScope: input.methodScope ?? "post"
4169
4316
  };
4170
4317
  const usesEditorChrome = Boolean(input.editorChrome);
4171
4318
  const editorChromeMarkup = input.editorChrome ? renderTemplateEditorChrome(input.editorChrome) : "";
4172
4319
  return shell({
4173
- title: input.pageTitle ?? (resolveAccountDisplayName({ displayName: input.name, email: input.email }) || "Job Runs"),
4320
+ title: input.pageTitle ?? (resolveAccountDisplayName({ displayName: input.name, email: input.email }) || "Render Runs"),
4174
4321
  mainClass: "is-wide",
4322
+ bodyClass: usesEditorChrome ? "is-editor-theme" : undefined,
4175
4323
  style: `
4176
4324
  ${usesEditorChrome ? TEMPLATE_EDITOR_SHELL_STYLES : ""}
4177
4325
 
@@ -4890,11 +5038,31 @@ export function renderJobRunsPage(input) {
4890
5038
  }
4891
5039
 
4892
5040
  .history-media-modal-source {
5041
+ display: inline-flex;
5042
+ align-items: center;
5043
+ gap: 6px;
5044
+ border: 0;
5045
+ background: transparent;
4893
5046
  color: #617087;
4894
5047
  font-size: 0.82rem;
4895
5048
  font-weight: 800;
5049
+ font-family: inherit;
4896
5050
  text-decoration: underline;
4897
5051
  text-underline-offset: 0.14em;
5052
+ cursor: pointer;
5053
+ }
5054
+
5055
+ .history-media-modal-source svg {
5056
+ width: 16px;
5057
+ height: 16px;
5058
+ }
5059
+
5060
+ .history-media-modal-source[data-state="done"] {
5061
+ color: #2f7d4f;
5062
+ }
5063
+
5064
+ .history-media-modal-source[data-state="error"] {
5065
+ color: #a14444;
4898
5066
  }
4899
5067
 
4900
5068
  .history-media-modal-title {
@@ -5404,7 +5572,7 @@ export function renderJobRunsPage(input) {
5404
5572
  <div class="frame-body frame-body-start">
5405
5573
  <section class="job-runs-shell page-content-start">
5406
5574
  <section class="history-toolbar">
5407
- <h1 class="history-title">${escapeHtml(input.headerTitle ?? "Job runs.")}</h1>
5575
+ <h1 class="history-title">${escapeHtml(input.headerTitle ?? "Render runs.")}</h1>
5408
5576
  <div class="history-toolbar-shell">
5409
5577
  <div class="history-toolbar-group">
5410
5578
  <div class="label-group history-quick-filter">
@@ -5425,8 +5593,8 @@ export function renderJobRunsPage(input) {
5425
5593
  <input id="history-end-time" type="datetime-local" data-history-end-time />
5426
5594
  </div>
5427
5595
  <div class="label-group">
5428
- <label for="history-template-id">Template ID</label>
5429
- <input id="history-template-id" type="search" autocomplete="off" spellcheck="false" placeholder="vidfarm_template_character_cards" data-history-template-id />
5596
+ <label for="history-template-id">Composition ID</label>
5597
+ <input id="history-template-id" type="search" autocomplete="off" spellcheck="false" placeholder="primitive:timeline_render" data-history-template-id />
5430
5598
  </div>
5431
5599
  <div class="label-group">
5432
5600
  <label for="history-tracer-query">Tracer contains</label>
@@ -5453,6 +5621,10 @@ export function renderJobRunsPage(input) {
5453
5621
  <div class="history-media-modal-head">
5454
5622
  <p id="history-media-modal-title" class="history-media-modal-title">Output preview</p>
5455
5623
  <div class="history-media-modal-actions">
5624
+ <button type="button" class="history-media-modal-source" data-history-media-copy aria-label="Copy media URL" title="Copy link">
5625
+ <svg viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="M8.4 11.6a3.2 3.2 0 0 0 4.5 0l2.2-2.2a3.2 3.2 0 0 0-4.5-4.5L9.4 6.1"></path><path d="M11.6 8.4a3.2 3.2 0 0 0-4.5 0L4.9 10.6a3.2 3.2 0 0 0 4.5 4.5l1.2-1.2"></path></svg>
5626
+ <span data-history-media-copy-label>Copy link</span>
5627
+ </button>
5456
5628
  <a class="history-media-modal-source" data-history-media-source href="#" target="_blank" rel="noreferrer">Open source</a>
5457
5629
  <button type="button" class="history-filter-button" data-history-media-close>Close</button>
5458
5630
  </div>
@@ -5512,6 +5684,8 @@ export function renderJobRunsPage(input) {
5512
5684
  mediaModalBody: document.querySelector("[data-history-media-body]"),
5513
5685
  mediaModalTitle: document.getElementById("history-media-modal-title"),
5514
5686
  mediaModalSource: document.querySelector("[data-history-media-source]"),
5687
+ mediaModalCopy: document.querySelector("[data-history-media-copy]"),
5688
+ mediaModalCopyLabel: document.querySelector("[data-history-media-copy-label]"),
5515
5689
  httpModal: document.querySelector("[data-history-http-modal]"),
5516
5690
  httpModalHead: document.querySelector("[data-history-http-modal-head]"),
5517
5691
  httpModalBody: document.querySelector("[data-history-http-modal-body]"),
@@ -5664,8 +5838,8 @@ export function renderJobRunsPage(input) {
5664
5838
  '<input id="history-end-time" type="datetime-local" data-history-end-time />' +
5665
5839
  '</div>' +
5666
5840
  '<div class="label-group">' +
5667
- '<label for="history-template-id">Template ID</label>' +
5668
- '<input id="history-template-id" type="search" autocomplete="off" spellcheck="false" placeholder="vidfarm_template_character_cards" data-history-template-id />' +
5841
+ '<label for="history-template-id">Composition ID</label>' +
5842
+ '<input id="history-template-id" type="search" autocomplete="off" spellcheck="false" placeholder="primitive:timeline_render" data-history-template-id />' +
5669
5843
  '</div>' +
5670
5844
  '<div class="label-group">' +
5671
5845
  '<label for="history-tracer-query">Tracer contains</label>' +
@@ -5921,6 +6095,13 @@ export function renderJobRunsPage(input) {
5921
6095
  if (elements.mediaModalSource instanceof HTMLAnchorElement) {
5922
6096
  elements.mediaModalSource.href = url;
5923
6097
  }
6098
+ if (elements.mediaModalCopy instanceof HTMLButtonElement && elements.mediaModalCopyLabel instanceof HTMLElement) {
6099
+ elements.mediaModalCopy.dataset.url = url;
6100
+ elements.mediaModalCopy.removeAttribute("data-state");
6101
+ elements.mediaModalCopy.setAttribute("aria-label", "Copy media URL");
6102
+ elements.mediaModalCopy.setAttribute("title", "Copy link");
6103
+ elements.mediaModalCopyLabel.textContent = "Copy link";
6104
+ }
5924
6105
  if (kind === "image") {
5925
6106
  elements.mediaModalBody.innerHTML = '<img src="' + escapeHtml(url) + '" alt="' + escapeHtml(label || "Output preview") + '" />';
5926
6107
  } else if (kind === "video") {
@@ -5943,15 +6124,53 @@ export function renderJobRunsPage(input) {
5943
6124
  if (elements.mediaModalSource instanceof HTMLAnchorElement) {
5944
6125
  elements.mediaModalSource.href = "#";
5945
6126
  }
6127
+ if (elements.mediaModalCopy instanceof HTMLButtonElement && elements.mediaModalCopyLabel instanceof HTMLElement) {
6128
+ elements.mediaModalCopy.dataset.url = "";
6129
+ elements.mediaModalCopy.removeAttribute("data-state");
6130
+ elements.mediaModalCopy.setAttribute("aria-label", "Copy media URL");
6131
+ elements.mediaModalCopy.setAttribute("title", "Copy link");
6132
+ elements.mediaModalCopyLabel.textContent = "Copy link";
6133
+ }
5946
6134
  elements.mediaModalBody.innerHTML = "";
5947
6135
  }
5948
6136
 
6137
+ async function copyMediaModalUrl() {
6138
+ if (!(elements.mediaModalCopy instanceof HTMLButtonElement) || !(elements.mediaModalCopyLabel instanceof HTMLElement)) {
6139
+ return;
6140
+ }
6141
+ const url = elements.mediaModalCopy.dataset.url || "";
6142
+ if (!url) {
6143
+ return;
6144
+ }
6145
+ try {
6146
+ const copied = await copyText(url);
6147
+ elements.mediaModalCopy.setAttribute("data-state", copied ? "done" : "error");
6148
+ elements.mediaModalCopy.setAttribute("aria-label", copied ? "Media URL copied" : "Unable to copy media URL");
6149
+ elements.mediaModalCopy.setAttribute("title", copied ? "Copied" : "Copy failed");
6150
+ elements.mediaModalCopyLabel.textContent = copied ? "Copied" : "Copy failed";
6151
+ } catch {
6152
+ elements.mediaModalCopy.setAttribute("data-state", "error");
6153
+ elements.mediaModalCopy.setAttribute("aria-label", "Unable to copy media URL");
6154
+ elements.mediaModalCopy.setAttribute("title", "Copy failed");
6155
+ elements.mediaModalCopyLabel.textContent = "Copy failed";
6156
+ }
6157
+ window.setTimeout(() => {
6158
+ if (!(elements.mediaModalCopy instanceof HTMLButtonElement) || !(elements.mediaModalCopyLabel instanceof HTMLElement)) {
6159
+ return;
6160
+ }
6161
+ elements.mediaModalCopy.removeAttribute("data-state");
6162
+ elements.mediaModalCopy.setAttribute("aria-label", "Copy media URL");
6163
+ elements.mediaModalCopy.setAttribute("title", "Copy link");
6164
+ elements.mediaModalCopyLabel.textContent = "Copy link";
6165
+ }, 1400);
6166
+ }
6167
+
5949
6168
  function renderLinkedJob(entry) {
5950
6169
  if (!entry.linkedJob) {
5951
6170
  return '<section class="history-linked-job">' +
5952
6171
  '<div class="history-linked-job-copy">' +
5953
- '<h3 class="history-linked-job-title">Queued job</h3>' +
5954
- '<div class="history-linked-job-meta">No linked job record was found for this POST response.</div>' +
6172
+ '<h3 class="history-linked-job-title">Queued render</h3>' +
6173
+ '<div class="history-linked-job-meta">No linked render record was found for this POST response.</div>' +
5955
6174
  '</div>' +
5956
6175
  '</section>';
5957
6176
  }
@@ -5961,18 +6180,18 @@ export function renderJobRunsPage(input) {
5961
6180
  const timing = finishedAt
5962
6181
  ? 'Finished ' + escapeHtml(finishedAt)
5963
6182
  : 'Progress ' + escapeHtml(Math.round((Number(job.progress) || 0) * 100)) + '%';
5964
- const isPending = job.status === "queued" || job.status === "running" || job.status === "waiting_for_child" || job.status === "waiting_for_human";
6183
+ const isPending = ["queued", "running", "waiting_for_provider", "waiting_for_child", "waiting_for_human"].includes(job.status);
5965
6184
 
5966
6185
  return '<section class="history-linked-job">' +
5967
6186
  '<div class="history-linked-job-head">' +
5968
6187
  '<div class="history-linked-job-copy">' +
5969
- '<h3 class="history-linked-job-title">Linked job output</h3>' +
5970
- '<div class="history-linked-job-meta">' + escapeHtml(job.job_id) + ' · ' + escapeHtml(job.template_id) + ' / ' + escapeHtml(job.operation_name) + '</div>' +
6188
+ '<h3 class="history-linked-job-title">Linked render output</h3>' +
6189
+ '<div class="history-linked-job-meta">' + escapeHtml(job.job_id) + ' · composition ' + escapeHtml(job.template_id) + ' / action ' + escapeHtml(job.operation_name) + '</div>' +
5971
6190
  '</div>' +
5972
6191
  '<div class="history-status-actions">' +
5973
- (isPending ? '<button type="button" class="history-check-button" data-history-check="' + escapeHtml(job.job_id) + '">Check job status</button>' : '') +
6192
+ (isPending ? '<button type="button" class="history-check-button" data-history-check="' + escapeHtml(job.job_id) + '">Check render status</button>' : '') +
5974
6193
  '<div class="history-card-meta">' +
5975
- '<span class="history-chip">Job ' + escapeHtml(job.status) + '</span>' +
6194
+ '<span class="history-chip">Render ' + escapeHtml(job.status) + '</span>' +
5976
6195
  '<span class="history-chip">' + timing + '</span>' +
5977
6196
  '</div>' +
5978
6197
  '</div>' +
@@ -6085,10 +6304,10 @@ export function renderJobRunsPage(input) {
6085
6304
  function renderEntry(entry) {
6086
6305
  const tracer = entry.tracer ? '<span class="history-chip">Tracer: ' + escapeHtml(entry.tracer) + '</span>' : "";
6087
6306
  const linkedJob = entry.linkedJob
6088
- ? '<span class="history-chip">Job ' + escapeHtml(entry.linkedJob.status) + '</span>'
6089
- : (entry.jobId ? '<span class="history-chip">Job pending link</span>' : "");
6307
+ ? '<span class="history-chip">Render ' + escapeHtml(entry.linkedJob.status) + '</span>'
6308
+ : (entry.jobId ? '<span class="history-chip">Render pending link</span>' : "");
6090
6309
  const editorLink = editorHref(entry)
6091
- ? '<a class="history-action-link" href="' + escapeHtml(editorHref(entry)) + '" target="_blank" rel="noreferrer" data-history-editor-link>Editor</a>'
6310
+ ? '<a class="history-action-link" href="' + escapeHtml(editorHref(entry)) + '" target="_blank" rel="noreferrer" data-history-editor-link>Workspace</a>'
6092
6311
  : "";
6093
6312
  const focusedBody = state.isFocusedRecordView
6094
6313
  ? '<div class="history-card-body">' +
@@ -6139,7 +6358,7 @@ export function renderJobRunsPage(input) {
6139
6358
  syncToolbar();
6140
6359
 
6141
6360
  if (state.isLoading) {
6142
- elements.groups.innerHTML = '<div class="history-empty">Loading job runs...</div>';
6361
+ elements.groups.innerHTML = '<div class="history-empty">Loading render runs...</div>';
6143
6362
  return;
6144
6363
  }
6145
6364
 
@@ -6152,7 +6371,7 @@ export function renderJobRunsPage(input) {
6152
6371
  if (!visibleEntries.length) {
6153
6372
  elements.groups.innerHTML = '<div class="history-empty">' + (
6154
6373
  state.isFocusedRecordView
6155
- ? 'That log record could not be found. <button type="button" class="history-toolbar-back" data-history-back>Back to all job runs</button>'
6374
+ ? 'That log record could not be found. <button type="button" class="history-toolbar-back" data-history-back>Back to all render runs</button>'
6156
6375
  : (state.methodScope === "all" ? 'No API calls matched the current filter.' : 'No POST API calls matched the current filter.')
6157
6376
  ) + '</div>';
6158
6377
  return;
@@ -6275,7 +6494,7 @@ export function renderJobRunsPage(input) {
6275
6494
  } catch (error) {
6276
6495
  console.error(error);
6277
6496
  state.isLoading = false;
6278
- elements.groups.innerHTML = '<div class="history-empty">Unable to load job runs.</div>';
6497
+ elements.groups.innerHTML = '<div class="history-empty">Unable to load render runs.</div>';
6279
6498
  } finally {
6280
6499
  if (button) {
6281
6500
  button.disabled = false;
@@ -6359,6 +6578,12 @@ export function renderJobRunsPage(input) {
6359
6578
  closeMediaModal();
6360
6579
  return;
6361
6580
  }
6581
+ const modalCopy = target.closest("[data-history-media-copy]");
6582
+ if (modalCopy instanceof HTMLButtonElement) {
6583
+ event.preventDefault();
6584
+ void copyMediaModalUrl();
6585
+ return;
6586
+ }
6362
6587
  const httpModalClose = target.closest("[data-history-http-close]");
6363
6588
  if (httpModalClose instanceof HTMLButtonElement) {
6364
6589
  event.preventDefault();
@@ -6569,12 +6794,12 @@ export function renderLibraryPage(input) {
6569
6794
  userId: input.userId
6570
6795
  };
6571
6796
  return shell({
6572
- title: resolveAccountDisplayName({ displayName: input.name, email: input.email }) || "Library",
6797
+ title: resolveAccountDisplayName({ displayName: input.name, email: input.email }) || "Published renders",
6573
6798
  mainClass: "is-wide",
6574
6799
  style: `
6575
6800
  ${TEMPLATE_EDITOR_SHELL_STYLES}
6576
6801
 
6577
- .library-frame { padding-right: 432px; }
6802
+ .library-frame { padding-left: 432px; }
6578
6803
  .library-body, .library-shell, .library-list-shell { min-height: 0; }
6579
6804
  .library-shell { display: grid; grid-template-rows: auto minmax(0, 1fr); gap: 12px; }
6580
6805
  .library-toolbar {
@@ -6781,13 +7006,13 @@ export function renderLibraryPage(input) {
6781
7006
  .library-empty { display: grid; place-items: center; min-height: 260px; border: 1px dashed rgba(191, 164, 109, 0.36); border-radius: 18px; color: #617087; background: rgba(255,252,247,.62); }
6782
7007
  ${renderReadyPostScheduleStyles()}
6783
7008
  @media (max-width: 1180px) {
6784
- .library-frame { padding-right: 0; }
7009
+ .library-frame { padding-left: 0; }
6785
7010
  .editor-right-rail { position: relative; width: 100%; height: 560px; margin-top: 14px; border-radius: 24px; overflow: hidden; }
6786
7011
  .library-post { grid-template-columns: auto 132px minmax(0, 1fr); }
6787
7012
  .library-post-actions { grid-column: 3; justify-content: flex-start; }
6788
7013
  .library-post[data-collapsed="true"] .library-post-actions { grid-column: auto; }
6789
7014
  }
6790
- @media (max-width: 767px) {
7015
+ @media (max-width: 1024px) {
6791
7016
  .library-toolbar {
6792
7017
  display: flex !important;
6793
7018
  align-items: center;
@@ -6864,7 +7089,7 @@ export function renderLibraryPage(input) {
6864
7089
  grid-column: 3;
6865
7090
  grid-row: 2;
6866
7091
  }
6867
- .library-frame { padding-right: 0; }
7092
+ .library-frame { padding-left: 0; }
6868
7093
  .editor-right-rail {
6869
7094
  position: fixed !important;
6870
7095
  inset: 0;
@@ -6982,15 +7207,15 @@ export function renderLibraryPage(input) {
6982
7207
  <div class="frame-body library-body">
6983
7208
  <section class="library-shell">
6984
7209
  <div class="library-toolbar">
6985
- <input class="library-search" data-library-search type="search" placeholder="Search finished posts" autocomplete="off">
6986
- <a class="button secondary library-history-link" href="${escapeAttribute(withAccountQuery("/job-runs", input.currentAccountId))}"><span class="library-history-full">Job Run History</span><span class="library-history-short">Runs</span></a>
7210
+ <input class="library-search" data-library-search type="search" placeholder="Search published renders" autocomplete="off">
7211
+ <a class="button secondary library-history-link" href="${escapeAttribute(withAccountQuery("/job-runs", input.currentAccountId))}"><span class="library-history-full">Render History</span><span class="library-history-short">Runs</span></a>
6987
7212
  <details class="library-options">
6988
7213
  <summary>Options</summary>
6989
7214
  <div class="library-options-panel">
6990
7215
  <button type="button" class="secondary library-options-close" data-library-options-close aria-label="Close options">×</button>
6991
7216
  <div class="label-group"><label for="library-start">Start date</label><input id="library-start" data-library-start type="date"></div>
6992
7217
  <div class="label-group"><label for="library-end">End date</label><input id="library-end" data-library-end type="date"></div>
6993
- <div class="label-group"><label for="library-template">Template ID</label><input id="library-template" data-library-template type="text" placeholder="template_0000"></div>
7218
+ <div class="label-group"><label for="library-template">Composition ID</label><input id="library-template" data-library-template type="text" placeholder="primitive:timeline_render"></div>
6994
7219
  <div class="label-group"><label for="library-tracer">Tracer contains</label><input id="library-tracer" data-library-tracer type="text" placeholder="launch-run"></div>
6995
7220
  </div>
6996
7221
  </details>
@@ -6998,7 +7223,7 @@ export function renderLibraryPage(input) {
6998
7223
  </div>
6999
7224
  <div class="library-list-shell">
7000
7225
  <ul class="library-list" data-library-list></ul>
7001
- <div class="library-empty" data-library-empty hidden>No finished posts match these filters.</div>
7226
+ <div class="library-empty" data-library-empty hidden>No published renders match these filters.</div>
7002
7227
  </div>
7003
7228
  </section>
7004
7229
  </div>
@@ -7135,7 +7360,7 @@ export function renderLibraryPage(input) {
7135
7360
  return '<li class="library-post" data-library-post-id="' + escapeHtml(post.post_id) + '" data-selected="' + (state.selected.has(post.post_id) ? "true" : "false") + '" data-collapsed="' + (isCollapsed ? "true" : "false") + '">' +
7136
7361
  '<input class="library-check" type="checkbox" data-library-check data-index="' + index + '" aria-label="Select post" ' + (state.selected.has(post.post_id) ? "checked" : "") + '>' +
7137
7362
  '<div class="library-phone">' + media + '</div>' +
7138
- '<div class="library-post-main"><h2 class="library-post-title">' + escapeHtml(post.title || "Ready post") + '</h2><div class="library-post-meta"><span>' + escapeHtml(post.status || "ready") + '</span>' + (post.template_id ? '<span>' + escapeHtml(post.template_id) + '</span>' : "") + (post.tracer ? '<span>' + escapeHtml(post.tracer) + '</span>' : "") + '<span>' + escapeHtml(compactDate(post.created_at)) + '</span></div>' +
7363
+ '<div class="library-post-main"><h2 class="library-post-title">' + escapeHtml(post.title || "Published render") + '</h2><div class="library-post-meta"><span>' + escapeHtml(post.status || "ready") + '</span>' + (post.template_id ? '<span>Composition ' + escapeHtml(post.template_id) + '</span>' : "") + (post.tracer ? '<span>' + escapeHtml(post.tracer) + '</span>' : "") + '<span>' + escapeHtml(compactDate(post.created_at)) + '</span></div>' +
7139
7364
  '<details class="library-content-details"><summary>Content</summary><div class="library-content-box">Caption\\n' + escapeHtml(post.caption || "") + (post.pinned_comment ? '\\n\\nPinned comment\\n' + escapeHtml(post.pinned_comment) : "") + '</div></details></div>' +
7140
7365
  '<div class="library-post-actions"><button type="button" class="secondary library-row-toggle" data-library-row-toggle="' + escapeHtml(post.post_id) + '" aria-expanded="' + (isCollapsed ? "false" : "true") + '">' + (isCollapsed ? "Expand" : "Minimize") + '</button><a class="button secondary" href="' + escapeHtml(href) + '" target="_blank" rel="noreferrer">View Post</a><button type="button" data-schedule data-schedule-endpoint="' + escapeHtml(accountPath('/approved/posts/' + encodeURIComponent(post.post_id) + '/schedules')) + '">Schedule</button><button type="button" class="secondary library-menu-button" data-library-menu-button="' + escapeHtml(post.post_id) + '" aria-label="Post actions">...</button><div class="library-row-menu" data-library-row-menu="' + escapeHtml(post.post_id) + '" hidden><button type="button" data-library-copy-post-id="' + escapeHtml(post.post_id) + '">Copy post id</button><button type="button" data-library-copy-share-url="' + escapeHtml(href) + '">Copy share url</button><a href="' + escapeHtml(href) + '" target="_blank" rel="noreferrer">View post page</a><button type="button" data-danger="true" data-library-delete="' + escapeHtml(post.post_id) + '">Delete</button></div></div></li>';
7141
7366
  };
@@ -7282,6 +7507,54 @@ export function renderLibraryPage(input) {
7282
7507
  `
7283
7508
  });
7284
7509
  }
7510
+ export function renderChatPage(input) {
7511
+ return shell({
7512
+ title: resolveAccountDisplayName({ displayName: input.name, email: input.email }) || "Chat",
7513
+ shellClass: "chat-page-stack",
7514
+ mainClass: "is-chat-page",
7515
+ style: `
7516
+ ${TEMPLATE_EDITOR_SHELL_STYLES}
7517
+
7518
+ .app-shell.is-chat-page {
7519
+ width: 100vw;
7520
+ max-width: none;
7521
+ height: 100dvh;
7522
+ min-height: 100dvh;
7523
+ padding: 0;
7524
+ }
7525
+ .page-stack.chat-page-stack {
7526
+ height: 100%;
7527
+ min-height: 0;
7528
+ gap: 0;
7529
+ }
7530
+ .chat-page-root {
7531
+ min-height: 0;
7532
+ height: 100dvh;
7533
+ display: grid;
7534
+ min-width: 0;
7535
+ }
7536
+ .chat-page-root .editor-chat-panel {
7537
+ height: 100%;
7538
+ min-height: 0;
7539
+ min-width: 0;
7540
+ width: 100%;
7541
+ border-radius: 0;
7542
+ border-right: 0;
7543
+ }
7544
+ `,
7545
+ body: `
7546
+ <section class="chat-page-root">
7547
+ <div class="editor-chat-panel" data-template-editor-chat-root></div>
7548
+ </section>
7549
+ `,
7550
+ runtimeBoot: {
7551
+ pageKind: "legacy",
7552
+ title: "Chat",
7553
+ userId: input.userId,
7554
+ editorChat: input.editorChat ?? null
7555
+ }
7556
+ });
7557
+ }
7285
7558
  export function renderCalendarPage(input) {
7286
7559
  const calendarSeed = {
7287
7560
  channels: input.channels.map((channel) => ({
@@ -8104,7 +8377,7 @@ export function renderCalendarPage(input) {
8104
8377
  }
8105
8378
  }
8106
8379
 
8107
- @media (max-width: 767px) {
8380
+ @media (max-width: 1024px) {
8108
8381
  .calendar-rail {
8109
8382
  padding: 14px;
8110
8383
  border-radius: 24px;
@@ -9062,3 +9335,4 @@ export function renderCalendarPage(input) {
9062
9335
  `
9063
9336
  });
9064
9337
  }
9338
+ //# sourceMappingURL=account-pages-legacy.js.map