@mevdragon/vidfarm-devcli 0.2.11 → 0.2.13

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