@adminide-stack/yantra-mobile 12.0.131 → 12.0.132-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (149) hide show
  1. package/lib/components/CustomDrawer.js +2 -2
  2. package/lib/components/CustomDrawer.js.map +1 -1
  3. package/lib/features/apps/AppsCatalog.js +7 -1
  4. package/lib/features/apps/AppsCatalog.js.map +1 -1
  5. package/lib/features/apps/nativeSurfaceTwin.js +35 -0
  6. package/lib/features/apps/nativeSurfaceTwin.js.map +1 -0
  7. package/lib/features/canvas/CanvasBoardsGrid.js +49 -49
  8. package/lib/features/canvas/CanvasBoardsGrid.js.map +1 -1
  9. package/lib/features/canvas/CanvasExperience.js +69 -28
  10. package/lib/features/canvas/CanvasExperience.js.map +1 -1
  11. package/lib/features/canvas/audioEditor/AudioWaveform.js +1 -1
  12. package/lib/features/canvas/designCatalog.js +63 -39
  13. package/lib/features/canvas/designCatalog.js.map +1 -1
  14. package/lib/features/canvas/designCategories.js +56 -0
  15. package/lib/features/canvas/designCategories.js.map +1 -1
  16. package/lib/features/canvas/generator/DirectorSheet.js +3 -2
  17. package/lib/features/canvas/generator/DirectorSheet.js.map +1 -1
  18. package/lib/features/canvas/home/HeroComposer.js +165 -76
  19. package/lib/features/canvas/home/HeroComposer.js.map +1 -1
  20. package/lib/features/canvas/home/HeroMonitor.js +75 -46
  21. package/lib/features/canvas/home/HeroMonitor.js.map +1 -1
  22. package/lib/features/canvas/home/TemplatesGallery.js +273 -138
  23. package/lib/features/canvas/home/TemplatesGallery.js.map +1 -1
  24. package/lib/features/canvas/home/templatesGallerySources.js +221 -0
  25. package/lib/features/canvas/home/templatesGallerySources.js.map +1 -0
  26. package/lib/features/canvas/homeWork.js +12 -11
  27. package/lib/features/canvas/homeWork.js.map +1 -1
  28. package/lib/features/canvas/lazyEditors.js +51 -0
  29. package/lib/features/canvas/lazyEditors.js.map +1 -0
  30. package/lib/features/canvas/rankedCatalogSearch.js +33 -23
  31. package/lib/features/canvas/rankedCatalogSearch.js.map +1 -1
  32. package/lib/features/canvas/set/SetViewportWebView.js +1 -1
  33. package/lib/features/canvas/set/agent/tools.js +1 -1
  34. package/lib/features/canvas/studioCatalog.js +2 -2
  35. package/lib/features/canvas/studioCatalog.js.map +1 -1
  36. package/lib/features/canvas/templateSearch.js +8 -1
  37. package/lib/features/canvas/templateSearch.js.map +1 -1
  38. package/lib/features/canvas/videoEditor/BlurBoxOverlay.js +1 -1
  39. package/lib/features/design/DesignEditorExperience.js +103 -29
  40. package/lib/features/design/DesignEditorExperience.js.map +1 -1
  41. package/lib/features/design/DesignExperience.js +4 -3
  42. package/lib/features/design/DesignExperience.js.map +1 -1
  43. package/lib/features/design/doc/aiDesign.js +1 -1
  44. package/lib/features/design/doc/aiDesign.js.map +1 -1
  45. package/lib/features/design/doc/artifacts.js +40 -13
  46. package/lib/features/design/doc/artifacts.js.map +1 -1
  47. package/lib/features/design/doc/chart.js +387 -13
  48. package/lib/features/design/doc/chart.js.map +1 -1
  49. package/lib/features/design/doc/copyStyle.js +47 -0
  50. package/lib/features/design/doc/copyStyle.js.map +1 -0
  51. package/lib/features/design/doc/docOps.js +22 -4
  52. package/lib/features/design/doc/docOps.js.map +1 -1
  53. package/lib/features/design/doc/geometry.js +32 -18
  54. package/lib/features/design/doc/geometry.js.map +1 -1
  55. package/lib/features/design/doc/newDoc.js +5 -1
  56. package/lib/features/design/doc/newDoc.js.map +1 -1
  57. package/lib/features/design/doc/resize.js +101 -0
  58. package/lib/features/design/doc/resize.js.map +1 -0
  59. package/lib/features/design/doc/sizes.js +15 -2
  60. package/lib/features/design/doc/sizes.js.map +1 -1
  61. package/lib/features/design/editor/ChartDataPanel.js +49 -9
  62. package/lib/features/design/editor/ChartDataPanel.js.map +1 -1
  63. package/lib/features/design/editor/CropOverlay.js +1 -1
  64. package/lib/features/design/editor/DesignCanvas.js +14 -13
  65. package/lib/features/design/editor/DesignCanvas.js.map +1 -1
  66. package/lib/features/design/editor/ExportSheet.js +8 -4
  67. package/lib/features/design/editor/ExportSheet.js.map +1 -1
  68. package/lib/features/design/editor/LayerTools.js +37 -5
  69. package/lib/features/design/editor/LayerTools.js.map +1 -1
  70. package/lib/features/design/editor/Slideshow.js +30 -5
  71. package/lib/features/design/editor/Slideshow.js.map +1 -1
  72. package/lib/features/design/editor/useDesignSave.js +21 -36
  73. package/lib/features/design/editor/useDesignSave.js.map +1 -1
  74. package/lib/features/design/export/exportDesign.js +3 -1
  75. package/lib/features/design/export/exportDesign.js.map +1 -1
  76. package/lib/features/design/home/DesignHome.js +7 -2
  77. package/lib/features/design/home/DesignHome.js.map +1 -1
  78. package/lib/features/design/render/DesignPageView.js +1 -1
  79. package/lib/features/design/render/DesignPageView.js.map +1 -1
  80. package/lib/features/design/render/PageCapture.js +11 -10
  81. package/lib/features/design/render/PageCapture.js.map +1 -1
  82. package/lib/features/design/render/fontRegistry.js +27 -3
  83. package/lib/features/design/render/fontRegistry.js.map +1 -1
  84. package/lib/features/design/render/svgParts.js +27 -74
  85. package/lib/features/design/render/svgParts.js.map +1 -1
  86. package/lib/features/devices/brands/qnScale.js +7 -2
  87. package/lib/features/devices/brands/qnScale.js.map +1 -1
  88. package/lib/features/devices/brands/uuids.js +2 -1
  89. package/lib/features/devices/brands/uuids.js.map +1 -1
  90. package/lib/features/devices/devices.js +2 -1
  91. package/lib/features/devices/devices.js.map +1 -1
  92. package/lib/features/docs/DocBlockRow.js +1 -1
  93. package/lib/features/docs/docArtifacts.js +2 -2
  94. package/lib/features/docs/docArtifacts.js.map +1 -1
  95. package/lib/features/health/healthSync.js +3 -1
  96. package/lib/features/health/healthSync.js.map +1 -1
  97. package/lib/features/health/nativeHealth.js +8 -0
  98. package/lib/features/health/nativeHealth.js.map +1 -0
  99. package/lib/features/links/openInApp.js +11 -1
  100. package/lib/features/links/openInApp.js.map +1 -1
  101. package/lib/features/webbuilder/WebBuilderExperience.js +135 -28
  102. package/lib/features/webbuilder/WebBuilderExperience.js.map +1 -1
  103. package/lib/features/webbuilder/buildFragments.js +14 -6
  104. package/lib/features/webbuilder/buildFragments.js.map +1 -1
  105. package/lib/features/webbuilder/components/PreviewFrame.js +28 -3
  106. package/lib/features/webbuilder/components/PreviewFrame.js.map +1 -1
  107. package/lib/features/webbuilder/components/PreviewScreen.js +1 -1
  108. package/lib/features/webbuilder/components/PreviewScreen.js.map +1 -1
  109. package/lib/features/webbuilder/components/ProjectChat.js +24 -5
  110. package/lib/features/webbuilder/components/ProjectChat.js.map +1 -1
  111. package/lib/features/webbuilder/components/ProjectsScreen.js +18 -2
  112. package/lib/features/webbuilder/components/ProjectsScreen.js.map +1 -1
  113. package/lib/features/webbuilder/components/ui.js +20 -6
  114. package/lib/features/webbuilder/components/ui.js.map +1 -1
  115. package/lib/features/webbuilder/exportCode.js +52 -0
  116. package/lib/features/webbuilder/exportCode.js.map +1 -0
  117. package/lib/features/webbuilder/siteAddress.js +71 -0
  118. package/lib/features/webbuilder/siteAddress.js.map +1 -0
  119. package/lib/features/webbuilder/widgetBlocks.js +30 -0
  120. package/lib/features/webbuilder/widgetBlocks.js.map +1 -0
  121. package/lib/screens/Chat/index.js +5 -6
  122. package/lib/screens/Chat/index.js.map +1 -1
  123. package/lib/screens/ChatHistory/index.js +3 -2
  124. package/lib/screens/ChatHistory/index.js.map +1 -1
  125. package/lib/screens/Home/HomeScreen.js +20 -4
  126. package/lib/screens/Home/HomeScreen.js.map +1 -1
  127. package/lib/screens/Home/components/WebBuilderCodePrewarm.js +18 -0
  128. package/lib/screens/Home/components/WebBuilderCodePrewarm.js.map +1 -0
  129. package/lib/screens/WebBuilderNative/index.js +1 -1
  130. package/lib/screens/WebBuilderNative/index.js.map +1 -1
  131. package/lib/screens/WebBuilderNative/keepAlive.js +112 -0
  132. package/lib/screens/WebBuilderNative/keepAlive.js.map +1 -0
  133. package/lib/screens/WebBuilderNative/runningBuilds.js +40 -0
  134. package/lib/screens/WebBuilderNative/runningBuilds.js.map +1 -0
  135. package/lib/screens/WebBuilderNative/useSandboxKeepAlive.js +57 -0
  136. package/lib/screens/WebBuilderNative/useSandboxKeepAlive.js.map +1 -0
  137. package/lib/screens/WebBuilderNative/useWebBuilderGallery.js +73 -14
  138. package/lib/screens/WebBuilderNative/useWebBuilderGallery.js.map +1 -1
  139. package/lib/screens/WebBuilderNative/useWebBuilderHost.js +143 -34
  140. package/lib/screens/WebBuilderNative/useWebBuilderHost.js.map +1 -1
  141. package/lib/screens/WebBuilderNative/useWebBuilderJobs.js +17 -5
  142. package/lib/screens/WebBuilderNative/useWebBuilderJobs.js.map +1 -1
  143. package/lib/screens/WebBuilderNative/versionUndo.js +144 -0
  144. package/lib/screens/WebBuilderNative/versionUndo.js.map +1 -0
  145. package/lib/screens/WebBuilderNative/webbuilderSnapshotKey.js +1 -0
  146. package/lib/screens/WebBuilderNative/webbuilderSnapshotKey.js.map +1 -0
  147. package/lib/screens/WebBuilderNative/webbuilderTransport.js +90 -6
  148. package/lib/screens/WebBuilderNative/webbuilderTransport.js.map +1 -1
  149. package/package.json +4 -4
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runningBuilds.js","sources":["../../../src/screens/WebBuilderNative/runningBuilds.ts"],"sourcesContent":["/**\n * The Web Builder turns running on this device, by project: what Projects shows as\n * \"Building…\" so a site started from the studio (or an edit left mid-turn) can be\n * found and opened again (web: the project's tile shows its live run).\n *\n * Module state, not React state: it outlives the Web Builder screen, so leaving and\n * coming back still shows the run. An entry ends when its job settles, when its\n * project is opened and has no work left (see the host), or after the build budget\n * plus grace - a turn the app stopped watching (another site was opened, the screen\n * closed) is the agent's, or the server deadline's, to end.\n */\nimport { useSyncExternalStore } from 'react';\nimport { WEBBUILDER_JOB_BUDGET_MS, WEBBUILDER_JOB_GRACE_MS } from '../../features/webbuilder/buildJob';\n\nexport interface RunningBuild {\n channelId: string;\n /** The workspace it runs in: Projects lists only its own org's runs. */\n org: string;\n /** The site's name, or one made from the ask. */\n title: string;\n /** What the person asked, as their chat bubble shows it - until its post comes back. */\n ask?: string;\n startedAt: number;\n}\n\nconst LIFETIME_MS = WEBBUILDER_JOB_BUDGET_MS + WEBBUILDER_JOB_GRACE_MS;\n\nlet running = new Map<string, RunningBuild>();\nlet snapshot: RunningBuild[] = [];\nconst listeners = new Set<() => void>();\n\nfunction publish(next: Map<string, RunningBuild>): void {\n running = next;\n snapshot = [...next.values()].sort((a, b) => b.startedAt - a.startedAt);\n listeners.forEach((listener) => listener());\n}\n\n/** Drop entries past their lifetime; `now` is injectable for tests. */\nfunction pruned(now = Date.now()): Map<string, RunningBuild> {\n const next = new Map([...running].filter(([, run]) => now - run.startedAt < LIFETIME_MS));\n return next.size === running.size ? running : next;\n}\n\nexport function markBuildRunning(run: RunningBuild): void {\n if (!run.channelId) return;\n const next = new Map(pruned());\n next.set(run.channelId, run);\n publish(next);\n}\n\nexport function markBuildDone(channelId: string | null | undefined, startedBefore = Infinity): void {\n const run = channelId ? running.get(channelId) : undefined;\n // A newer turn on the same site keeps running.\n if (!run || run.startedAt > startedBefore) return;\n const next = new Map(running);\n next.delete(run.channelId);\n publish(next);\n}\n\nexport function runningBuild(channelId: string | null | undefined): RunningBuild | null {\n return (channelId && running.get(channelId)) || null;\n}\n\nfunction subscribe(listener: () => void): () => void {\n listeners.add(listener);\n // Entries expire by time: re-check once a minute while someone is listening.\n const timer = setInterval(() => {\n const next = pruned();\n if (next !== running) publish(next);\n }, 60_000);\n return () => {\n listeners.delete(listener);\n clearInterval(timer);\n };\n}\n\n/** The running builds, newest first. */\nexport function useRunningBuilds(): RunningBuild[] {\n return useSyncExternalStore(\n subscribe,\n () => snapshot,\n () => snapshot,\n );\n}\n\n/** Tests only. */\nexport function resetRunningBuilds(): void {\n publish(new Map());\n}\n"],"names":[],"mappings":"8IAuBA,MAAM,cAAc,wBAA2B,GAAA,uBAAA;AAC/C,IAAI,OAAA,uBAAc,GAA0B,EAAA;AAC5C,IAAI,WAA2B,EAAC;AAChC,MAAM,SAAA,uBAAgB,GAAgB,EAAA;AACtC,SAAS,QAAQ,IAAuC,EAAA;AACtD,EAAU,OAAA,GAAA,IAAA;AACV,EAAA,QAAA,GAAW,CAAC,GAAG,IAAK,CAAA,MAAA,EAAQ,CAAA,CAAE,IAAK,CAAA,CAAC,CAAG,EAAA,CAAA,KAAM,CAAE,CAAA,SAAA,GAAY,EAAE,SAAS,CAAA;AACtE,EAAU,SAAA,CAAA,OAAA,CAAQ,CAAY,QAAA,KAAA,QAAA,EAAU,CAAA;AAC1C;AAGA,SAAS,MAAO,CAAA,GAAA,GAAM,IAAK,CAAA,GAAA,EAAkC,EAAA;AAC3D,EAAA,MAAM,OAAO,IAAI,GAAA,CAAI,CAAC,GAAG,OAAO,CAAE,CAAA,MAAA,CAAO,CAAC,GAAG,GAAG,CAAA,KAAM,MAAM,GAAI,CAAA,SAAA,GAAY,WAAW,CAAC,CAAA;AACxF,EAAA,OAAO,IAAK,CAAA,IAAA,KAAS,OAAQ,CAAA,IAAA,GAAO,OAAU,GAAA,IAAA;AAChD;AACO,SAAS,iBAAiB,GAAyB,EAAA;AACxD,EAAI,IAAA,CAAC,IAAI,SAAW,EAAA;AACpB,EAAA,MAAM,IAAO,GAAA,IAAI,GAAI,CAAA,MAAA,EAAQ,CAAA;AAC7B,EAAK,IAAA,CAAA,GAAA,CAAI,GAAI,CAAA,SAAA,EAAW,GAAG,CAAA;AAC3B,EAAA,OAAA,CAAQ,IAAI,CAAA;AACd;AACgB,SAAA,aAAA,CAAc,SAAsC,EAAA,aAAA,GAAgB,QAAgB,EAAA;AAClG,EAAA,MAAM,GAAM,GAAA,SAAA,GAAY,OAAQ,CAAA,GAAA,CAAI,SAAS,CAAI,GAAA,MAAA;AAEjD,EAAA,IAAI,CAAC,GAAA,IAAO,GAAI,CAAA,SAAA,GAAY,aAAe,EAAA;AAC3C,EAAM,MAAA,IAAA,GAAO,IAAI,GAAA,CAAI,OAAO,CAAA;AAC5B,EAAK,IAAA,CAAA,MAAA,CAAO,IAAI,SAAS,CAAA;AACzB,EAAA,OAAA,CAAQ,IAAI,CAAA;AACd;AAIA,SAAS,UAAU,QAAkC,EAAA;AACnD,EAAA,SAAA,CAAU,IAAI,QAAQ,CAAA;AAEtB,EAAM,MAAA,KAAA,GAAQ,YAAY,MAAM;AAC9B,IAAA,MAAM,OAAO,MAAO,EAAA;AACpB,IAAI,IAAA,IAAA,KAAS,OAAS,EAAA,OAAA,CAAQ,IAAI,CAAA;AAAA,KACjC,GAAM,CAAA;AACT,EAAA,OAAO,MAAM;AACX,IAAA,SAAA,CAAU,OAAO,QAAQ,CAAA;AACzB,IAAA,aAAA,CAAc,KAAK,CAAA;AAAA,GACrB;AACF;AAGO,SAAS,gBAAmC,GAAA;AACjD,EAAA,OAAO,oBAAqB,CAAA,SAAA,EAAW,MAAM,QAAA,EAAU,MAAM,QAAQ,CAAA;AACvE"}
@@ -0,0 +1,57 @@
1
+ import {useRef,useEffect,useCallback}from'react';import {AppState}from'react-native';import {KeepAliveScheduler,WEBBUILDER_EXTEND_ACTION,KEEPALIVE_EXTEND_MS,KEEPALIVE_TICK_MS}from'./keepAlive.js';function useSandboxKeepAlive(opts) {
2
+ const runRef = useRef(opts.run);
3
+ runRef.current = opts.run;
4
+ const schedulerRef = useRef(null);
5
+ if (!schedulerRef.current) {
6
+ schedulerRef.current = new KeepAliveScheduler({
7
+ now: () => Date.now(),
8
+ extend: async (sandboxId2) => {
9
+ const run = runRef.current;
10
+ if (!run) return {
11
+ successful: false,
12
+ data: {},
13
+ error: "no sandbox access"
14
+ };
15
+ return run(WEBBUILDER_EXTEND_ACTION, {
16
+ sandbox_id: sandboxId2,
17
+ timeoutMs: KEEPALIVE_EXTEND_MS
18
+ }, 9e4);
19
+ }
20
+ });
21
+ }
22
+ const sandboxId = String(opts.sandboxId || "").trim();
23
+ const enabled = Boolean(opts.run) && opts.live;
24
+ useEffect(() => {
25
+ const s = schedulerRef.current;
26
+ s.setTarget(sandboxId, enabled);
27
+ if (enabled) {
28
+ s.noteActivity();
29
+ s.tick().catch(() => void 0);
30
+ }
31
+ }, [sandboxId, enabled]);
32
+ useEffect(() => {
33
+ const s = schedulerRef.current;
34
+ s.setVisible(AppState.currentState === "active");
35
+ const sub = AppState.addEventListener("change", (state) => {
36
+ s.setVisible(state === "active");
37
+ if (state === "active") {
38
+ s.noteActivity();
39
+ s.tick().catch(() => void 0);
40
+ }
41
+ });
42
+ const timer = setInterval(() => {
43
+ s.tick().catch(() => void 0);
44
+ }, KEEPALIVE_TICK_MS);
45
+ return () => {
46
+ sub.remove();
47
+ clearInterval(timer);
48
+ };
49
+ }, []);
50
+ const noteActivity = useCallback(() => {
51
+ var _a;
52
+ return (_a = schedulerRef.current) == null ? void 0 : _a.noteActivity();
53
+ }, []);
54
+ return {
55
+ noteActivity
56
+ };
57
+ }export{useSandboxKeepAlive};//# sourceMappingURL=useSandboxKeepAlive.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useSandboxKeepAlive.js","sources":["../../../src/screens/WebBuilderNative/useSandboxKeepAlive.ts"],"sourcesContent":["/**\n * Feeds the keep-alive scheduler (keepAlive.ts) the app's foreground state and the\n * person's activity (web `sandbox/useSandboxKeepAlive.ts`): `WEBBUILDER_EXTEND`\n * every 10 minutes while a build is open, the app is in front and the person did\n * something in the last 15 minutes. Opening a project, sending a turn and\n * returning to the app count as activity (`noteActivity`).\n */\nimport { useCallback, useEffect, useRef } from 'react';\nimport { AppState } from 'react-native';\nimport {\n KEEPALIVE_EXTEND_MS,\n KEEPALIVE_TICK_MS,\n KeepAliveScheduler,\n WEBBUILDER_EXTEND_ACTION,\n type ExtendResult,\n} from './keepAlive';\n\nexport function useSandboxKeepAlive(opts: {\n run: ((action: string, params: Record<string, unknown>, timeoutMs: number) => Promise<ExtendResult>) | null;\n sandboxId?: string | null;\n /** A build is open (its preview may be on screen). */\n live: boolean;\n}): { noteActivity: () => void } {\n const runRef = useRef(opts.run);\n runRef.current = opts.run;\n const schedulerRef = useRef<KeepAliveScheduler | null>(null);\n if (!schedulerRef.current) {\n schedulerRef.current = new KeepAliveScheduler({\n now: () => Date.now(),\n extend: async (sandboxId) => {\n const run = runRef.current;\n if (!run) return { successful: false, data: {}, error: 'no sandbox access' };\n return run(WEBBUILDER_EXTEND_ACTION, { sandbox_id: sandboxId, timeoutMs: KEEPALIVE_EXTEND_MS }, 90_000);\n },\n });\n }\n\n const sandboxId = String(opts.sandboxId || '').trim();\n const enabled = Boolean(opts.run) && opts.live;\n\n useEffect(() => {\n const s = schedulerRef.current!;\n s.setTarget(sandboxId, enabled);\n if (enabled) {\n s.noteActivity();\n s.tick().catch(() => undefined);\n }\n }, [sandboxId, enabled]);\n\n useEffect(() => {\n const s = schedulerRef.current!;\n s.setVisible(AppState.currentState === 'active');\n const sub = AppState.addEventListener('change', (state) => {\n s.setVisible(state === 'active');\n if (state === 'active') {\n s.noteActivity();\n s.tick().catch(() => undefined);\n }\n });\n const timer = setInterval(() => {\n s.tick().catch(() => undefined);\n }, KEEPALIVE_TICK_MS);\n return () => {\n sub.remove();\n clearInterval(timer);\n };\n }, []);\n\n const noteActivity = useCallback(() => schedulerRef.current?.noteActivity(), []);\n return { noteActivity };\n}\n"],"names":["sandboxId"],"mappings":"oMAUO,SAAS,oBAAoB,IAOlC,EAAA;AACA,EAAM,MAAA,MAAA,GAAS,MAAO,CAAA,IAAA,CAAK,GAAG,CAAA;AAC9B,EAAA,MAAA,CAAO,UAAU,IAAK,CAAA,GAAA;AACtB,EAAM,MAAA,YAAA,GAAe,OAAkC,IAAI,CAAA;AAC3D,EAAI,IAAA,CAAC,aAAa,OAAS,EAAA;AACzB,IAAa,YAAA,CAAA,OAAA,GAAU,IAAI,kBAAmB,CAAA;AAAA,MAC5C,GAAA,EAAK,MAAM,IAAA,CAAK,GAAI,EAAA;AAAA,MACpB,MAAA,EAAQ,OAAMA,UAAa,KAAA;AACzB,QAAA,MAAM,MAAM,MAAO,CAAA,OAAA;AACnB,QAAI,IAAA,CAAC,KAAY,OAAA;AAAA,UACf,UAAY,EAAA,KAAA;AAAA,UACZ,MAAM,EAAC;AAAA,UACP,KAAO,EAAA;AAAA,SACT;AACA,QAAA,OAAO,IAAI,wBAA0B,EAAA;AAAA,UACnC,UAAYA,EAAAA,UAAAA;AAAA,UACZ,SAAW,EAAA;AAAA,WACV,GAAM,CAAA;AAAA;AACX,KACD,CAAA;AAAA;AAEH,EAAA,MAAM,YAAY,MAAO,CAAA,IAAA,CAAK,SAAa,IAAA,EAAE,EAAE,IAAK,EAAA;AACpD,EAAA,MAAM,OAAU,GAAA,OAAA,CAAQ,IAAK,CAAA,GAAG,KAAK,IAAK,CAAA,IAAA;AAC1C,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,MAAM,IAAI,YAAa,CAAA,OAAA;AACvB,IAAE,CAAA,CAAA,SAAA,CAAU,WAAW,OAAO,CAAA;AAC9B,IAAA,IAAI,OAAS,EAAA;AACX,MAAA,CAAA,CAAE,YAAa,EAAA;AACf,MAAA,CAAA,CAAE,IAAK,EAAA,CAAE,KAAM,CAAA,MAAM,MAAS,CAAA;AAAA;AAChC,GACC,EAAA,CAAC,SAAW,EAAA,OAAO,CAAC,CAAA;AACvB,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,MAAM,IAAI,YAAa,CAAA,OAAA;AACvB,IAAE,CAAA,CAAA,UAAA,CAAW,QAAS,CAAA,YAAA,KAAiB,QAAQ,CAAA;AAC/C,IAAA,MAAM,GAAM,GAAA,QAAA,CAAS,gBAAiB,CAAA,QAAA,EAAU,CAAS,KAAA,KAAA;AACvD,MAAE,CAAA,CAAA,UAAA,CAAW,UAAU,QAAQ,CAAA;AAC/B,MAAA,IAAI,UAAU,QAAU,EAAA;AACtB,QAAA,CAAA,CAAE,YAAa,EAAA;AACf,QAAA,CAAA,CAAE,IAAK,EAAA,CAAE,KAAM,CAAA,MAAM,MAAS,CAAA;AAAA;AAChC,KACD,CAAA;AACD,IAAM,MAAA,KAAA,GAAQ,YAAY,MAAM;AAC9B,MAAA,CAAA,CAAE,IAAK,EAAA,CAAE,KAAM,CAAA,MAAM,MAAS,CAAA;AAAA,OAC7B,iBAAiB,CAAA;AACpB,IAAA,OAAO,MAAM;AACX,MAAA,GAAA,CAAI,MAAO,EAAA;AACX,MAAA,aAAA,CAAc,KAAK,CAAA;AAAA,KACrB;AAAA,GACF,EAAG,EAAE,CAAA;AACL,EAAM,MAAA,YAAA,GAAe,YAAY,MAAG;AAlEtC,IAAA,IAAA,EAAA;AAkEyC,IAAA,OAAA,CAAA,EAAA,GAAA,YAAA,CAAa,YAAb,IAAsB,GAAA,MAAA,GAAA,EAAA,CAAA,YAAA,EAAA;AAAA,GAAA,EAAgB,EAAE,CAAA;AAC/E,EAAO,OAAA;AAAA,IACL;AAAA,GACF;AACF"}
@@ -1,8 +1,27 @@
1
- import {useState,useRef,useEffect,useCallback}from'react';import {useApolloClient}from'@apollo/client/index.js';import AsyncStorage from'@react-native-async-storage/async-storage';import {MessagesDocument,GetChannelsByUserWithLastMessageDocument}from'common/graphql';import {getThreadMessagesQueryVariables,getHistoryChannelsQueryVariables}from'../../hooks/useChatApi.js';import {lastMessageHasCover,projectFromPosts}from'../../features/webbuilder/buildFragments.js';const GALLERY_PAGE_SIZE = 10;
1
+ import {useState,useRef,useEffect,useCallback}from'react';import {useApolloClient}from'@apollo/client/index.js';import AsyncStorage from'@react-native-async-storage/async-storage';import {MessagesDocument,GetChannelsByUserWithLastMessageDocument}from'common/graphql';import {getThreadMessagesQueryVariables,getHistoryChannelsQueryVariables}from'../../hooks/useChatApi.js';import {lastMessageHasCover,projectFromPosts,isGenericTitle}from'../../features/webbuilder/buildFragments.js';import {WEBBUILDER_PROJECTS_SNAPSHOT_KEY}from'./webbuilderSnapshotKey.js';var __defProp = Object.defineProperty;
2
+ var __defProps = Object.defineProperties;
3
+ var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
4
+ var __getOwnPropSymbols = Object.getOwnPropertySymbols;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __propIsEnum = Object.prototype.propertyIsEnumerable;
7
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
8
+ var __spreadValues = (a, b) => {
9
+ for (var prop in b || (b = {}))
10
+ if (__hasOwnProp.call(b, prop))
11
+ __defNormalProp(a, prop, b[prop]);
12
+ if (__getOwnPropSymbols)
13
+ for (var prop of __getOwnPropSymbols(b)) {
14
+ if (__propIsEnum.call(b, prop))
15
+ __defNormalProp(a, prop, b[prop]);
16
+ }
17
+ return a;
18
+ };
19
+ var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
20
+ const GALLERY_PAGE_SIZE = 10;
2
21
  const MIN_FILL = 8;
3
22
  const MAX_PAGES_PER_LOAD = 4;
4
23
  const READ_CONCURRENCY = 4;
5
- const SNAPSHOT_KEY = "yantra.webbuilder.projects.v1";
24
+ const SNAPSHOT_KEY = WEBBUILDER_PROJECTS_SNAPSHOT_KEY;
6
25
  const SNAPSHOT_MAX = 40;
7
26
  const memory = /* @__PURE__ */ new Map();
8
27
  function sortProjects(list) {
@@ -13,16 +32,46 @@ function stampOf(channel) {
13
32
  return String((_b = (_a = channel.updatedAt) != null ? _a : channel.createdAt) != null ? _b : "");
14
33
  }
15
34
  async function readSnapshot(org) {
35
+ var _a;
16
36
  try {
17
37
  const raw = await AsyncStorage.getItem(`${SNAPSHOT_KEY}:${org}`);
18
- const list = raw ? JSON.parse(raw) : [];
19
- return Array.isArray(list) ? list.filter((p) => (p == null ? void 0 : p.channelId) && p.title) : [];
38
+ const parsed = raw ? JSON.parse(raw) : {};
39
+ const saved = Array.isArray(parsed) ? {
40
+ projects: parsed
41
+ } : parsed;
42
+ const projects = Array.isArray(saved.projects) ? saved.projects.filter((p) => (p == null ? void 0 : p.channelId) && p.title) : [];
43
+ const known = /* @__PURE__ */ new Map();
44
+ for (const row of Array.isArray(saved.known) ? saved.known : []) {
45
+ if (Array.isArray(row) && typeof row[0] === "string" && typeof row[1] === "string") {
46
+ known.set(row[0], {
47
+ stamp: row[1],
48
+ project: (_a = row[2]) != null ? _a : null
49
+ });
50
+ }
51
+ }
52
+ return {
53
+ projects,
54
+ known
55
+ };
20
56
  } catch (e) {
21
- return [];
57
+ return {
58
+ projects: [],
59
+ known: /* @__PURE__ */ new Map()
60
+ };
22
61
  }
23
62
  }
24
- function writeSnapshot(org, projects) {
25
- AsyncStorage.setItem(`${SNAPSHOT_KEY}:${org}`, JSON.stringify(projects.slice(0, SNAPSHOT_MAX))).catch(() => void 0);
63
+ function writeSnapshot(org, projects, known) {
64
+ const snapshot = {
65
+ projects: projects.slice(0, SNAPSHOT_MAX),
66
+ known: [...known].slice(-300).map(([id, k]) => [id, k.stamp, k.project])
67
+ };
68
+ AsyncStorage.setItem(`${SNAPSHOT_KEY}:${org}`, JSON.stringify(snapshot)).catch(() => void 0);
69
+ }
70
+ function keepKnownName(next, prior) {
71
+ if (!next || !prior || !isGenericTitle(next.title) || isGenericTitle(prior.title)) return next;
72
+ return __spreadProps(__spreadValues({}, next), {
73
+ title: prior.title
74
+ });
26
75
  }
27
76
  async function eachLimited(items, limit, work) {
28
77
  let next = 0;
@@ -76,14 +125,19 @@ function useWebBuilderGallery(orgName, cdnOrigin) {
76
125
  projects,
77
126
  known: knownRef.current
78
127
  });
79
- if (projects.length) writeSnapshot(orgName, projects);
128
+ if (projects.length) writeSnapshot(orgName, projects, knownRef.current);
80
129
  }, [orgName, projects]);
130
+ const hydratedRef = useRef(Promise.resolve());
81
131
  useEffect(() => {
82
132
  var _a2;
83
133
  if (!orgName || ((_a2 = memory.get(orgName)) == null ? void 0 : _a2.projects.length)) return void 0;
84
134
  let live = true;
85
- readSnapshot(orgName).then((saved) => {
86
- if (live && saved.length) setProjects((prev) => prev.length ? prev : saved);
135
+ hydratedRef.current = readSnapshot(orgName).then((saved) => {
136
+ if (!live) return;
137
+ saved.known.forEach((k, id) => {
138
+ if (!knownRef.current.has(id)) knownRef.current.set(id, k);
139
+ });
140
+ if (saved.projects.length) setProjects((prev) => prev.length ? prev : saved.projects);
87
141
  }).catch(() => void 0);
88
142
  return () => {
89
143
  live = false;
@@ -95,7 +149,7 @@ function useWebBuilderGallery(orgName, cdnOrigin) {
95
149
  if (!id) return null;
96
150
  if (lastMessageHasCover(channel)) {
97
151
  const fromLast = projectFromPosts(channel, [channel.lastMessage], cdnOrigin);
98
- if (fromLast) return fromLast;
152
+ if (fromLast && !isGenericTitle(fromLast.title)) return fromLast;
99
153
  }
100
154
  const result = await client.query({
101
155
  query: MessagesDocument,
@@ -114,6 +168,7 @@ function useWebBuilderGallery(orgName, cdnOrigin) {
114
168
  async (mode) => {
115
169
  if (!orgName || busyRef.current) return;
116
170
  busyRef.current = true;
171
+ await hydratedRef.current;
117
172
  const run = runRef.current + 1;
118
173
  runRef.current = run;
119
174
  const stale = () => runRef.current !== run;
@@ -163,19 +218,23 @@ function useWebBuilderGallery(orgName, cdnOrigin) {
163
218
  skipRef.current += channels.length;
164
219
  more = channels.length >= GALLERY_PAGE_SIZE;
165
220
  await eachLimited(channels, READ_CONCURRENCY, async (channel) => {
166
- var _a2;
221
+ var _a2, _b;
167
222
  const id = (_a2 = channel.id) != null ? _a2 : "";
168
223
  seen.add(id);
169
224
  const stamp = stampOf(channel);
170
225
  const prior = known.get(id);
171
- if (prior && prior.stamp === stamp) {
226
+ const unnamed = Boolean((prior == null ? void 0 : prior.project) && isGenericTitle(prior.project.title));
227
+ if (prior && prior.stamp === stamp && !unnamed) {
172
228
  if (prior.project) tally.found += 1;
173
229
  return;
174
230
  }
175
- const project = await projectFor(channel, "network-only").catch(() => {
231
+ const quick = channel.lastMessage ? keepKnownName(projectFromPosts(channel, [channel.lastMessage], cdnOrigin), prior == null ? void 0 : prior.project) : null;
232
+ if (quick) put(quick, id);
233
+ const read = await projectFor(channel, "network-only").catch(() => {
176
234
  var _a3;
177
235
  return (_a3 = prior == null ? void 0 : prior.project) != null ? _a3 : null;
178
236
  });
237
+ const project = keepKnownName(read, (_b = prior == null ? void 0 : prior.project) != null ? _b : quick);
179
238
  known.set(id, {
180
239
  stamp,
181
240
  project
@@ -1 +1 @@
1
- {"version":3,"file":"useWebBuilderGallery.js","sources":["../../../src/screens/WebBuilderNative/useWebBuilderGallery.ts"],"sourcesContent":["/**\n * Projects - the web surface's gallery hydrate, on the host, made fast.\n *\n * Same rule as the web (`realServices` gallery load): page the person's AI\n * assistant chats 10 at a time, newest first, and keep a chat only if its posts\n * hold a build. What makes it quick on a phone:\n *\n * - The last list paints at once: kept in memory across visits and on disk for a\n * cold start (per workspace).\n * - Reads go through the Apollo cache first. The app's type policies\n * (`graphql/typePolicies.ts`) keep `channelsByUser` as one merged list per\n * workspace and `messages` per channel, so a cached page is complete and\n * paints without the network.\n * - The refresh re-reads a chat's thread only when the chat changed\n * (`updatedAt`); an unchanged chat keeps the project it already gave. A typical\n * refresh is one list request, not one per chat.\n * - Thread reads run a few at a time and each project appears as soon as it is\n * known, newest first.\n */\nimport { useCallback, useEffect, useRef, useState } from 'react';\nimport { useApolloClient, type ApolloClient, type FetchPolicy } from '@apollo/client';\nimport AsyncStorage from '@react-native-async-storage/async-storage';\nimport { GetChannelsByUserWithLastMessageDocument, MessagesDocument } from 'common/graphql';\nimport { getHistoryChannelsQueryVariables, getThreadMessagesQueryVariables } from '../../hooks/useChatApi';\nimport { lastMessageHasCover, projectFromPosts, type ChannelLike } from '../../features/webbuilder/buildFragments';\nimport type { WebBuilderProject } from '../../features/webbuilder/types';\n\n/** The web gallery's page size (`GALLERY_PAGE_SIZE`). */\nexport const GALLERY_PAGE_SIZE = 10;\n/** Keep paging (up to this many pages per load) while pages yield too few projects to fill the screen. */\nconst MIN_FILL = 8;\nconst MAX_PAGES_PER_LOAD = 4;\n/** Thread reads in flight at once: enough to be quick, few enough to keep the app responsive. */\nconst READ_CONCURRENCY = 4;\nconst SNAPSHOT_KEY = 'yantra.webbuilder.projects.v1';\nconst SNAPSHOT_MAX = 40;\n\ntype Loose = Record<string, any>; // eslint-disable-line @typescript-eslint/no-explicit-any\n\n/** What a chat last gave: its project (or none) at the `updatedAt` it was read for. */\ninterface Known {\n stamp: string;\n project: WebBuilderProject | null;\n}\n\n/** Per-workspace memory: survives leaving and reopening Web Builder in one app run. */\nconst memory = new Map<string, { projects: WebBuilderProject[]; known: Map<string, Known> }>();\n\nexport interface WebBuilderGallery {\n projects: WebBuilderProject[];\n loading: boolean;\n loadingMore: boolean;\n hasMore: boolean;\n error: string | null;\n refresh: () => void;\n loadMore: () => void;\n /** Put (or update) one site - the open one, as its posts change. */\n upsert: (project: WebBuilderProject) => void;\n}\n\nfunction sortProjects(list: Iterable<WebBuilderProject>): WebBuilderProject[] {\n return [...list].sort((a, b) => b.updatedAt - a.updatedAt);\n}\n\nfunction stampOf(channel: ChannelLike): string {\n return String(channel.updatedAt ?? channel.createdAt ?? '');\n}\n\nasync function readSnapshot(org: string): Promise<WebBuilderProject[]> {\n try {\n const raw = await AsyncStorage.getItem(`${SNAPSHOT_KEY}:${org}`);\n const list = raw ? (JSON.parse(raw) as WebBuilderProject[]) : [];\n return Array.isArray(list) ? list.filter((p) => p?.channelId && p.title) : [];\n } catch {\n return [];\n }\n}\n\nfunction writeSnapshot(org: string, projects: WebBuilderProject[]): void {\n AsyncStorage.setItem(`${SNAPSHOT_KEY}:${org}`, JSON.stringify(projects.slice(0, SNAPSHOT_MAX))).catch(\n () => undefined,\n );\n}\n\n/** Runs `work` over `items`, at most `limit` at a time. */\nasync function eachLimited<T>(items: T[], limit: number, work: (item: T) => Promise<void>): Promise<void> {\n let next = 0;\n const lane = async (): Promise<void> => {\n while (next < items.length) {\n const item = items[next];\n next += 1;\n // eslint-disable-next-line no-await-in-loop -- one lane works through its share in order\n await work(item);\n }\n };\n await Promise.all(Array.from({ length: Math.min(limit, items.length) }, lane));\n}\n\nasync function readChannels(\n client: ApolloClient<object>,\n orgName: string,\n skip: number,\n fetchPolicy: FetchPolicy,\n): Promise<ChannelLike[]> {\n const result = await client.query({\n query: GetChannelsByUserWithLastMessageDocument,\n variables: getHistoryChannelsQueryVariables(orgName, { limit: GALLERY_PAGE_SIZE, skip }),\n fetchPolicy,\n errorPolicy: 'all',\n // Same slot as chat history: the type policy merges both into one list.\n context: { cacheKey: 'chat-history-channels-list' },\n });\n return (((result.data as Loose)?.channelsByUser ?? []) as ChannelLike[]).filter((c) => c?.id);\n}\n\nexport function useWebBuilderGallery(orgName: string, cdnOrigin: string): WebBuilderGallery {\n const client = useApolloClient();\n const remembered = orgName ? memory.get(orgName) : undefined;\n const [projects, setProjects] = useState<WebBuilderProject[]>(() => remembered?.projects ?? []);\n const [loading, setLoading] = useState(false);\n const [loadingMore, setLoadingMore] = useState(false);\n const [hasMore, setHasMore] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const skipRef = useRef(0);\n const busyRef = useRef(false);\n const runRef = useRef(0);\n const knownRef = useRef<Map<string, Known>>(remembered?.known ?? new Map());\n\n // Keep memory and the disk snapshot in step with what is shown.\n useEffect(() => {\n if (!orgName) return;\n memory.set(orgName, { projects, known: knownRef.current });\n if (projects.length) writeSnapshot(orgName, projects);\n }, [orgName, projects]);\n\n // A cold start paints the last list from disk while the cache and network catch up.\n useEffect(() => {\n if (!orgName || memory.get(orgName)?.projects.length) return undefined;\n let live = true;\n readSnapshot(orgName)\n .then((saved) => {\n if (live && saved.length) setProjects((prev) => (prev.length ? prev : saved));\n })\n .catch(() => undefined);\n return () => {\n live = false;\n };\n }, [orgName]);\n\n const projectFor = useCallback(\n async (channel: ChannelLike, fetchPolicy: FetchPolicy): Promise<WebBuilderProject | null> => {\n const id = channel.id ?? '';\n if (!id) return null;\n // The web skips the thread read when the last post already has the screenshot.\n if (lastMessageHasCover(channel)) {\n const fromLast = projectFromPosts(channel, [channel.lastMessage], cdnOrigin);\n if (fromLast) return fromLast;\n }\n const result = await client.query({\n query: MessagesDocument,\n variables: getThreadMessagesQueryVariables(id),\n fetchPolicy,\n errorPolicy: 'all',\n context: { cacheKey: `messages-list:${id}` },\n });\n const posts = ((result.data as Loose)?.messages?.data ?? []) as unknown[];\n const withLast = channel.lastMessage ? [...posts, channel.lastMessage] : posts;\n return projectFromPosts(channel, withLast, cdnOrigin);\n },\n [client, cdnOrigin],\n );\n\n const load = useCallback(\n async (mode: 'reset' | 'more') => {\n if (!orgName || busyRef.current) return;\n busyRef.current = true;\n const run = runRef.current + 1;\n runRef.current = run;\n const stale = () => runRef.current !== run;\n const known = knownRef.current;\n const seen = new Set<string>();\n const put = (project: WebBuilderProject | null, channelId: string) => {\n if (stale()) return;\n setProjects((prev) => {\n const byId = new Map(prev.map((p) => [p.channelId, p]));\n if (project) byId.set(channelId, project);\n else byId.delete(channelId);\n return sortProjects(byId.values());\n });\n };\n\n if (mode === 'reset') {\n skipRef.current = 0;\n setLoading(projects.length === 0);\n } else {\n setLoadingMore(true);\n }\n setError(null);\n\n try {\n // 1. From the cache: instant when the type policies already hold the list.\n if (mode === 'reset') {\n const cached = await readChannels(client, orgName, 0, 'cache-only').catch(() => []);\n await eachLimited(cached, READ_CONCURRENCY, async (channel) => {\n const id = channel.id ?? '';\n if (known.has(id)) return;\n const project = await projectFor(channel, 'cache-only').catch(() => null);\n if (project) {\n known.set(id, { stamp: stampOf(channel), project });\n put(project, id);\n }\n });\n if (!stale() && projects.length === 0 && known.size) setLoading(false);\n }\n\n // 2. From the network: the list, then only the threads that changed.\n let more = false;\n const tally = { found: 0 };\n for (let page = 0; page < MAX_PAGES_PER_LOAD; page += 1) {\n // eslint-disable-next-line no-await-in-loop -- pages are sequential by design\n const channels = await readChannels(client, orgName, skipRef.current, 'network-only');\n if (stale()) return;\n skipRef.current += channels.length;\n more = channels.length >= GALLERY_PAGE_SIZE;\n // eslint-disable-next-line no-await-in-loop\n await eachLimited(channels, READ_CONCURRENCY, async (channel) => {\n const id = channel.id ?? '';\n seen.add(id);\n const stamp = stampOf(channel);\n const prior = known.get(id);\n if (prior && prior.stamp === stamp) {\n if (prior.project) tally.found += 1;\n return;\n }\n const project = await projectFor(channel, 'network-only').catch(() => prior?.project ?? null);\n known.set(id, { stamp, project });\n if (project) tally.found += 1;\n put(project, id);\n });\n if (!more || tally.found >= MIN_FILL) break;\n }\n if (stale()) return;\n if (mode === 'reset') {\n // A chat no longer in the first pages (deleted, moved) drops off the list.\n setProjects((prev) =>\n prev.filter((p) => seen.has(p.channelId) || p.updatedAt > Date.now() - 60_000),\n );\n }\n setHasMore(more);\n } catch (err) {\n console.warn('[WebBuilderNative] could not load projects:', err);\n if (!stale()) setError('Check your connection and try again.');\n } finally {\n busyRef.current = false;\n if (!stale()) {\n setLoading(false);\n setLoadingMore(false);\n }\n }\n },\n // `projects.length` only steers the first spinner; reading it stale is harmless.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [client, orgName, projectFor],\n );\n\n useEffect(() => {\n load('reset').catch(() => undefined);\n }, [load]);\n\n const refresh = useCallback(() => {\n busyRef.current = false;\n load('reset').catch(() => undefined);\n }, [load]);\n const loadMore = useCallback(() => {\n if (hasMore) load('more').catch(() => undefined);\n }, [hasMore, load]);\n\n const upsert = useCallback((project: WebBuilderProject) => {\n setProjects((prev) => {\n const existing = prev.find((p) => p.channelId === project.channelId);\n if (\n existing &&\n existing.title === project.title &&\n existing.coverUrl === project.coverUrl &&\n existing.liveUrl === project.liveUrl &&\n existing.updatedAt === project.updatedAt\n ) {\n return prev;\n }\n return sortProjects([project, ...prev.filter((p) => p.channelId !== project.channelId)]);\n });\n }, []);\n\n return { projects, loading, loadingMore, hasMore, error, refresh, loadMore, upsert };\n}\n"],"names":["_a"],"mappings":"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"}
1
+ {"version":3,"file":"useWebBuilderGallery.js","sources":["../../../src/screens/WebBuilderNative/useWebBuilderGallery.ts"],"sourcesContent":["/**\n * Projects - the web surface's gallery hydrate, on the host, made fast.\n *\n * Same rule as the web (`realServices` gallery load): page the person's AI\n * assistant chats 10 at a time, newest first, and keep a chat only if its posts\n * hold a build. What makes it quick on a phone:\n *\n * - The last list paints at once: kept in memory across visits and on disk for a\n * cold start (per workspace).\n * - Reads go through the Apollo cache first. The app's type policies\n * (`graphql/typePolicies.ts`) keep `channelsByUser` as one merged list per\n * workspace and `messages` per channel, so a cached page is complete and\n * paints without the network.\n * - The refresh re-reads a chat's thread only when the chat changed\n * (`updatedAt`); an unchanged chat keeps the project it already gave. What each\n * chat gave (a project, or none for a plain chat) is saved with the list, so a\n * cold start re-reads only the chats that changed, not every chat again.\n * - A changed chat shows at once from its last post when that is a build; its\n * thread read then fills in the cover and the name. Thread reads run a few at a\n * time and each project appears as soon as it is known, newest first.\n * - A name, once known, is kept: a read that only yields the placeholder\n * (\"Generated Page\") never replaces it.\n */\nimport { useCallback, useEffect, useRef, useState } from 'react';\nimport { useApolloClient, type ApolloClient, type FetchPolicy } from '@apollo/client';\nimport AsyncStorage from '@react-native-async-storage/async-storage';\nimport { GetChannelsByUserWithLastMessageDocument, MessagesDocument } from 'common/graphql';\nimport { getHistoryChannelsQueryVariables, getThreadMessagesQueryVariables } from '../../hooks/useChatApi';\nimport {\n isGenericTitle,\n lastMessageHasCover,\n projectFromPosts,\n type ChannelLike,\n} from '../../features/webbuilder/buildFragments';\nimport type { WebBuilderProject } from '../../features/webbuilder/types';\nimport { WEBBUILDER_PROJECTS_SNAPSHOT_KEY } from './webbuilderSnapshotKey';\n\n/** The web gallery's page size (`GALLERY_PAGE_SIZE`). */\nexport const GALLERY_PAGE_SIZE = 10;\n/** Keep paging (up to this many pages per load) while pages yield too few projects to fill the screen. */\nconst MIN_FILL = 8;\nconst MAX_PAGES_PER_LOAD = 4;\n/** Thread reads in flight at once: enough to be quick, few enough to keep the app responsive. */\nconst READ_CONCURRENCY = 4;\nconst SNAPSHOT_KEY = WEBBUILDER_PROJECTS_SNAPSHOT_KEY;\nconst SNAPSHOT_MAX = 40;\n/** Chats remembered with their stamp (plain chats included: each would otherwise be re-read). */\nconst KNOWN_MAX = 300;\n\ntype Loose = Record<string, any>; // eslint-disable-line @typescript-eslint/no-explicit-any\n\n/** What a chat last gave: its project (or none) at the `updatedAt` it was read for. */\ninterface Known {\n stamp: string;\n project: WebBuilderProject | null;\n}\n\n/** Per-workspace memory: survives leaving and reopening Web Builder in one app run. */\nconst memory = new Map<string, { projects: WebBuilderProject[]; known: Map<string, Known> }>();\n\nexport interface WebBuilderGallery {\n projects: WebBuilderProject[];\n loading: boolean;\n loadingMore: boolean;\n hasMore: boolean;\n error: string | null;\n refresh: () => void;\n loadMore: () => void;\n /** Put (or update) one site - the open one, as its posts change. */\n upsert: (project: WebBuilderProject) => void;\n}\n\nfunction sortProjects(list: Iterable<WebBuilderProject>): WebBuilderProject[] {\n return [...list].sort((a, b) => b.updatedAt - a.updatedAt);\n}\n\nfunction stampOf(channel: ChannelLike): string {\n return String(channel.updatedAt ?? channel.createdAt ?? '');\n}\n\ninterface Snapshot {\n projects: WebBuilderProject[];\n /** `[channelId, stamp, project | null]`: what each chat gave at the `updatedAt` it was read for. */\n known: Array<[string, string, WebBuilderProject | null]>;\n}\n\nasync function readSnapshot(org: string): Promise<{ projects: WebBuilderProject[]; known: Map<string, Known> }> {\n try {\n const raw = await AsyncStorage.getItem(`${SNAPSHOT_KEY}:${org}`);\n const parsed = raw ? (JSON.parse(raw) as Partial<Snapshot> | WebBuilderProject[]) : {};\n // An older snapshot is the bare projects list.\n const saved: Partial<Snapshot> = Array.isArray(parsed) ? { projects: parsed } : parsed;\n const projects = Array.isArray(saved.projects) ? saved.projects.filter((p) => p?.channelId && p.title) : [];\n const known = new Map<string, Known>();\n for (const row of Array.isArray(saved.known) ? saved.known : []) {\n if (Array.isArray(row) && typeof row[0] === 'string' && typeof row[1] === 'string') {\n known.set(row[0], { stamp: row[1], project: row[2] ?? null });\n }\n }\n return { projects, known };\n } catch {\n return { projects: [], known: new Map() };\n }\n}\n\nfunction writeSnapshot(org: string, projects: WebBuilderProject[], known: Map<string, Known>): void {\n const snapshot: Snapshot = {\n projects: projects.slice(0, SNAPSHOT_MAX),\n known: [...known].slice(-KNOWN_MAX).map(([id, k]) => [id, k.stamp, k.project]),\n };\n AsyncStorage.setItem(`${SNAPSHOT_KEY}:${org}`, JSON.stringify(snapshot)).catch(() => undefined);\n}\n\n/** A read that only found the placeholder name keeps the name the site already had. */\nfunction keepKnownName(next: WebBuilderProject | null, prior: WebBuilderProject | null | undefined) {\n if (!next || !prior || !isGenericTitle(next.title) || isGenericTitle(prior.title)) return next;\n return { ...next, title: prior.title };\n}\n\n/** Runs `work` over `items`, at most `limit` at a time. */\nasync function eachLimited<T>(items: T[], limit: number, work: (item: T) => Promise<void>): Promise<void> {\n let next = 0;\n const lane = async (): Promise<void> => {\n while (next < items.length) {\n const item = items[next];\n next += 1;\n // eslint-disable-next-line no-await-in-loop -- one lane works through its share in order\n await work(item);\n }\n };\n await Promise.all(Array.from({ length: Math.min(limit, items.length) }, lane));\n}\n\nasync function readChannels(\n client: ApolloClient<object>,\n orgName: string,\n skip: number,\n fetchPolicy: FetchPolicy,\n): Promise<ChannelLike[]> {\n const result = await client.query({\n query: GetChannelsByUserWithLastMessageDocument,\n variables: getHistoryChannelsQueryVariables(orgName, { limit: GALLERY_PAGE_SIZE, skip }),\n fetchPolicy,\n errorPolicy: 'all',\n // Same slot as chat history: the type policy merges both into one list.\n context: { cacheKey: 'chat-history-channels-list' },\n });\n return (((result.data as Loose)?.channelsByUser ?? []) as ChannelLike[]).filter((c) => c?.id);\n}\n\nexport function useWebBuilderGallery(orgName: string, cdnOrigin: string): WebBuilderGallery {\n const client = useApolloClient();\n const remembered = orgName ? memory.get(orgName) : undefined;\n const [projects, setProjects] = useState<WebBuilderProject[]>(() => remembered?.projects ?? []);\n const [loading, setLoading] = useState(false);\n const [loadingMore, setLoadingMore] = useState(false);\n const [hasMore, setHasMore] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const skipRef = useRef(0);\n const busyRef = useRef(false);\n const runRef = useRef(0);\n const knownRef = useRef<Map<string, Known>>(remembered?.known ?? new Map());\n\n // Keep memory and the disk snapshot in step with what is shown.\n useEffect(() => {\n if (!orgName) return;\n memory.set(orgName, { projects, known: knownRef.current });\n if (projects.length) writeSnapshot(orgName, projects, knownRef.current);\n }, [orgName, projects]);\n\n // A cold start paints the last list from disk, and takes what each chat gave with it,\n // so the first load re-reads only the chats that changed since (it waits for this).\n const hydratedRef = useRef<Promise<void>>(Promise.resolve());\n useEffect(() => {\n if (!orgName || memory.get(orgName)?.projects.length) return undefined;\n let live = true;\n hydratedRef.current = readSnapshot(orgName)\n .then((saved) => {\n if (!live) return;\n saved.known.forEach((k, id) => {\n if (!knownRef.current.has(id)) knownRef.current.set(id, k);\n });\n if (saved.projects.length) setProjects((prev) => (prev.length ? prev : saved.projects));\n })\n .catch(() => undefined);\n return () => {\n live = false;\n };\n }, [orgName]);\n\n const projectFor = useCallback(\n async (channel: ChannelLike, fetchPolicy: FetchPolicy): Promise<WebBuilderProject | null> => {\n const id = channel.id ?? '';\n if (!id) return null;\n // The web skips the thread read when the last post already has the screenshot.\n // Only with a real name: the last post alone has no brief to name the site from.\n if (lastMessageHasCover(channel)) {\n const fromLast = projectFromPosts(channel, [channel.lastMessage], cdnOrigin);\n if (fromLast && !isGenericTitle(fromLast.title)) return fromLast;\n }\n const result = await client.query({\n query: MessagesDocument,\n variables: getThreadMessagesQueryVariables(id),\n fetchPolicy,\n errorPolicy: 'all',\n context: { cacheKey: `messages-list:${id}` },\n });\n const posts = ((result.data as Loose)?.messages?.data ?? []) as unknown[];\n const withLast = channel.lastMessage ? [...posts, channel.lastMessage] : posts;\n return projectFromPosts(channel, withLast, cdnOrigin);\n },\n [client, cdnOrigin],\n );\n\n const load = useCallback(\n async (mode: 'reset' | 'more') => {\n if (!orgName || busyRef.current) return;\n busyRef.current = true;\n await hydratedRef.current;\n const run = runRef.current + 1;\n runRef.current = run;\n const stale = () => runRef.current !== run;\n const known = knownRef.current;\n const seen = new Set<string>();\n const put = (project: WebBuilderProject | null, channelId: string) => {\n if (stale()) return;\n setProjects((prev) => {\n const byId = new Map(prev.map((p) => [p.channelId, p]));\n if (project) byId.set(channelId, project);\n else byId.delete(channelId);\n return sortProjects(byId.values());\n });\n };\n\n if (mode === 'reset') {\n skipRef.current = 0;\n setLoading(projects.length === 0);\n } else {\n setLoadingMore(true);\n }\n setError(null);\n\n try {\n // 1. From the cache: instant when the type policies already hold the list.\n if (mode === 'reset') {\n const cached = await readChannels(client, orgName, 0, 'cache-only').catch(() => []);\n await eachLimited(cached, READ_CONCURRENCY, async (channel) => {\n const id = channel.id ?? '';\n if (known.has(id)) return;\n const project = await projectFor(channel, 'cache-only').catch(() => null);\n if (project) {\n known.set(id, { stamp: stampOf(channel), project });\n put(project, id);\n }\n });\n if (!stale() && projects.length === 0 && known.size) setLoading(false);\n }\n\n // 2. From the network: the list, then only the threads that changed.\n let more = false;\n const tally = { found: 0 };\n for (let page = 0; page < MAX_PAGES_PER_LOAD; page += 1) {\n // eslint-disable-next-line no-await-in-loop -- pages are sequential by design\n const channels = await readChannels(client, orgName, skipRef.current, 'network-only');\n if (stale()) return;\n skipRef.current += channels.length;\n more = channels.length >= GALLERY_PAGE_SIZE;\n // eslint-disable-next-line no-await-in-loop\n await eachLimited(channels, READ_CONCURRENCY, async (channel) => {\n const id = channel.id ?? '';\n seen.add(id);\n const stamp = stampOf(channel);\n const prior = known.get(id);\n // Unchanged: keep what it gave - unless that is still the placeholder name.\n const unnamed = Boolean(prior?.project && isGenericTitle(prior.project.title));\n if (prior && prior.stamp === stamp && !unnamed) {\n if (prior.project) tally.found += 1;\n return;\n }\n // Show it now from its last post when that is a build; the read fills in the rest.\n const quick = channel.lastMessage\n ? keepKnownName(projectFromPosts(channel, [channel.lastMessage], cdnOrigin), prior?.project)\n : null;\n if (quick) put(quick, id);\n const read = await projectFor(channel, 'network-only').catch(() => prior?.project ?? null);\n const project = keepKnownName(read, prior?.project ?? quick);\n known.set(id, { stamp, project });\n if (project) tally.found += 1;\n put(project, id);\n });\n if (!more || tally.found >= MIN_FILL) break;\n }\n if (stale()) return;\n if (mode === 'reset') {\n // A chat no longer in the first pages (deleted, moved) drops off the list.\n setProjects((prev) =>\n prev.filter((p) => seen.has(p.channelId) || p.updatedAt > Date.now() - 60_000),\n );\n }\n setHasMore(more);\n } catch (err) {\n console.warn('[WebBuilderNative] could not load projects:', err);\n if (!stale()) setError('Check your connection and try again.');\n } finally {\n busyRef.current = false;\n if (!stale()) {\n setLoading(false);\n setLoadingMore(false);\n }\n }\n },\n // `projects.length` only steers the first spinner; reading it stale is harmless.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [client, orgName, projectFor],\n );\n\n useEffect(() => {\n load('reset').catch(() => undefined);\n }, [load]);\n\n const refresh = useCallback(() => {\n busyRef.current = false;\n load('reset').catch(() => undefined);\n }, [load]);\n const loadMore = useCallback(() => {\n if (hasMore) load('more').catch(() => undefined);\n }, [hasMore, load]);\n\n const upsert = useCallback((project: WebBuilderProject) => {\n setProjects((prev) => {\n const existing = prev.find((p) => p.channelId === project.channelId);\n if (\n existing &&\n existing.title === project.title &&\n existing.coverUrl === project.coverUrl &&\n existing.liveUrl === project.liveUrl &&\n existing.updatedAt === project.updatedAt\n ) {\n return prev;\n }\n return sortProjects([project, ...prev.filter((p) => p.channelId !== project.channelId)]);\n });\n }, []);\n\n return { projects, loading, loadingMore, hasMore, error, refresh, loadMore, upsert };\n}\n"],"names":["_a"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import {useRef,useState,useMemo,useEffect,useCallback}from'react';import {useAiCodeDeployToCloudflareMutation}from'common/graphql';import {v4}from'uuid';import {useCdecliConnection}from'../../contexts/CdecliConnectionContext.js';import {useChatStream}from'../../hooks/useChatStream.js';import {useHostChatChannel}from'../../hooks/useHostChatChannel.js';import {config}from'../../config/env-config.js';import {useChatMessages}from'../../hooks/useChatApi.js';import {usePrerequisiteIds}from'../../hooks/usePrerequisiteIds.js';import {useCanvasSession}from'../../features/canvas/useCanvasSession.js';import {tokenClaims}from'../../features/canvas/sessionIdentity.js';import {buildsFromPosts,hasWorkflowPosts,projectFromPosts,turnState,pendingWork,transcriptFromPosts,stripAgentNote,displayAgentText}from'../../features/webbuilder/buildFragments.js';import {WEBBUILDER_CHAT_SKILL,buildTriggerParams,templateRemixNote,newObjectId}from'../../features/webbuilder/workflow.js';import {useWebBuilderGallery}from'./useWebBuilderGallery.js';import {agentEditNote,agentPublishMessage}from'../../features/webbuilder/webbuilderPrompts.js';import {useCodeAssistantCatalog}from'./useCodeAssistantCatalog.js';import {uploadAttachment,triggerBuild,createSandboxRouter,directSandboxRun,restoreSandbox}from'./webbuilderTransport.js';import {useSandboxProxyApi}from'./sandboxProxyApi.js';import {useRestoredSandboxes,applyRestored}from'./useRestoredSandboxes.js';import {useWebBuilderJobs}from'./useWebBuilderJobs.js';var __defProp = Object.defineProperty;
1
+ import {useRef,useState,useMemo,useEffect,useCallback}from'react';import {useAiCodeDeployToCloudflareMutation}from'common/graphql';import {v4}from'uuid';import {useCdecliConnection}from'../../contexts/CdecliConnectionContext.js';import {useChatStream}from'../../hooks/useChatStream.js';import {useHostChatChannel}from'../../hooks/useHostChatChannel.js';import {config}from'../../config/env-config.js';import {useChatMessages}from'../../hooks/useChatApi.js';import {usePrerequisiteIds}from'../../hooks/usePrerequisiteIds.js';import {useCanvasSession}from'../../features/canvas/useCanvasSession.js';import {tokenClaims}from'../../features/canvas/sessionIdentity.js';import {buildsFromPosts,hasWorkflowPosts,projectFromPosts,turnState,pendingWork,transcriptFromPosts,stripAgentNote,displayAgentText}from'../../features/webbuilder/buildFragments.js';import {WEBBUILDER_CHAT_SKILL,buildTriggerParams,templateRemixNote,newObjectId}from'../../features/webbuilder/workflow.js';import {useWebBuilderGallery}from'./useWebBuilderGallery.js';import {agentEditNote,agentPublishMessage}from'../../features/webbuilder/webbuilderPrompts.js';import {useCodeAssistantCatalog}from'./useCodeAssistantCatalog.js';import {uploadAttachment,triggerBuild,createSandboxRouter,directSandboxRun,restoreSandbox}from'./webbuilderTransport.js';import {useSandboxProxyApi}from'./sandboxProxyApi.js';import {useSandboxKeepAlive}from'./useSandboxKeepAlive.js';import {pendingAskUser}from'../../features/chat/askUser.js';import {useRunningBuilds,markBuildDone}from'./runningBuilds.js';import {undoLastChange,redoChange}from'./versionUndo.js';import {WEBBUILDER_EXPORT_ZIP_ACTION,exportZipParams,exportZipUrl}from'../../features/webbuilder/exportCode.js';import {useRestoredSandboxes,applyRestored}from'./useRestoredSandboxes.js';import {useWebBuilderJobs}from'./useWebBuilderJobs.js';var __defProp = Object.defineProperty;
2
2
  var __defProps = Object.defineProperties;
3
3
  var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
4
4
  var __getOwnPropSymbols = Object.getOwnPropertySymbols;
@@ -29,7 +29,7 @@ function failureText(error) {
29
29
  return "The build didn't start. Try again.";
30
30
  }
31
31
  function useWebBuilderHost(routeOrg) {
32
- var _a, _b, _c, _d;
32
+ var _a, _b, _c, _d, _e, _f, _g;
33
33
  const {
34
34
  orgName,
35
35
  projectId: appProjectId
@@ -95,7 +95,7 @@ function useWebBuilderHost(routeOrg) {
95
95
  const chat = useChatStream(agentChannelId, chatRouting);
96
96
  const agentLane = activeChannelId !== null && (agentChannels.current.has(activeChannelId) || posts.length > 0 && !hasWorkflowPosts(posts));
97
97
  const agentBusy = agentLane && (chat.isLoading || chat.isStreaming);
98
- const jobs = useWebBuilderJobs();
98
+ const jobs = useWebBuilderJobs(effectiveOrg);
99
99
  const agentJob = useRef(null);
100
100
  const sendAgentTurn = useCallback((channelId, text, files, task) => {
101
101
  const ready = unsavedAgentChannels.current.has(channelId) ? ensureChannel() : Promise.resolve();
@@ -246,7 +246,7 @@ function useWebBuilderHost(routeOrg) {
246
246
  return () => clearInterval(timer);
247
247
  }, [turn, refetch, jobs]);
248
248
  const runTurn = useCallback(async (opts) => {
249
- var _a2, _b2, _c2, _d2, _e, _f;
249
+ var _a2, _b2, _c2, _d2, _e2, _f2;
250
250
  const {
251
251
  current
252
252
  } = tokenRef;
@@ -290,7 +290,7 @@ function useWebBuilderHost(routeOrg) {
290
290
  channelId: opts.channelId,
291
291
  projectId: opts.projectId,
292
292
  ask: opts.text,
293
- siteTitle: (_e = (_d2 = opts.edit) == null ? void 0 : _d2.title) != null ? _e : null,
293
+ siteTitle: (_e2 = (_d2 = opts.edit) == null ? void 0 : _d2.title) != null ? _e2 : null,
294
294
  mode,
295
295
  lane: "workflow",
296
296
  startedAt: startedAt2
@@ -304,7 +304,7 @@ function useWebBuilderHost(routeOrg) {
304
304
  });
305
305
  const outcome = await triggerBuild(current, indexUrl, built.params);
306
306
  if (!outcome.ok) {
307
- const reason = (_f = outcome.error) != null ? _f : "";
307
+ const reason = (_f2 = outcome.error) != null ? _f2 : "";
308
308
  console.warn("[WebBuilderNative] trigger failed:", reason);
309
309
  if (outcome.auth) session.retry();
310
310
  setTurn(null);
@@ -373,11 +373,50 @@ ${templateRemixNote(request.template)}` : request.text,
373
373
  const running = Boolean(turn && turn.channelId === activeChannelId);
374
374
  const startedAt = (_c = turn == null ? void 0 : turn.startedAt) != null ? _c : 0;
375
375
  const progress = (_d = state == null ? void 0 : state.progress) != null ? _d : null;
376
+ const runningBuilds = useRunningBuilds();
377
+ const openRun = (_e = runningBuilds.find((r) => r.channelId === activeChannelId)) != null ? _e : null;
378
+ const openRunState = useMemo(() => openRun && posts.length ? turnState(posts, openRun.startedAt) : null, [openRun, posts]);
379
+ const watchingLive = agentBusy || running;
380
+ useEffect(() => {
381
+ if (!openRun || !openRunState || watchingLive || openRunState.status === "running") return;
382
+ markBuildDone(openRun.channelId, openRun.startedAt);
383
+ }, [openRun, openRunState, watchingLive]);
384
+ const openRunPending = openRun !== null && !watchingLive && (!openRunState || openRunState.status === "running");
385
+ useEffect(() => {
386
+ if (!openRun || watchingLive) return void 0;
387
+ const timer = setInterval(() => refetch().catch(() => void 0), POLL_MS);
388
+ return () => clearInterval(timer);
389
+ }, [openRun, watchingLive, refetch]);
390
+ const shownProjects = useMemo(() => {
391
+ const runs = runningBuilds.filter((r) => !r.org || !effectiveOrg || r.org === effectiveOrg);
392
+ if (!runs.length) return projects;
393
+ const ids = new Set(runs.map((r) => r.channelId));
394
+ const rows = projects.map((p) => ids.has(p.channelId) ? __spreadProps(__spreadValues({}, p), {
395
+ building: true
396
+ }) : p);
397
+ for (const run of runs) {
398
+ if (!projects.some((p) => p.channelId === run.channelId)) {
399
+ rows.push({
400
+ channelId: run.channelId,
401
+ title: run.title,
402
+ updatedAt: run.startedAt,
403
+ building: true
404
+ });
405
+ }
406
+ }
407
+ return rows.sort((a, b) => b.updatedAt - a.updatedAt);
408
+ }, [projects, runningBuilds, effectiveOrg]);
376
409
  const building = useMemo(() => {
377
410
  if (agentBusy) return {
378
411
  statusText: "Building with the agent\u2026",
379
412
  startedAt: startedAt || Date.now()
380
413
  };
414
+ if (!running && work !== "creating" && openRun && openRunPending) {
415
+ return {
416
+ statusText: (openRunState == null ? void 0 : openRunState.progress) || "Building your site\u2026",
417
+ startedAt: openRun.startedAt
418
+ };
419
+ }
381
420
  if (!running && work !== "creating") return null;
382
421
  let fallback = "Starting your build\u2026";
383
422
  if ((turn == null ? void 0 : turn.mode) === "edit") fallback = "Updating your app\u2026";
@@ -386,13 +425,13 @@ ${templateRemixNote(request.template)}` : request.text,
386
425
  statusText: progress || fallback,
387
426
  startedAt: startedAt || Date.now()
388
427
  };
389
- }, [agentBusy, running, work, turn == null ? void 0 : turn.mode, progress, startedAt]);
428
+ }, [agentBusy, running, work, turn == null ? void 0 : turn.mode, progress, startedAt, openRun, openRunState, openRunPending]);
390
429
  const messages = useMemo(() => {
391
430
  const rows = transcriptFromPosts(posts).map((m) => ({
392
431
  id: m.id,
393
432
  role: m.role,
394
433
  content: m.role === "user" ? stripAgentNote(m.content) : displayAgentText(m.content)
395
- }));
434
+ })).filter((m) => m.role === "user" || m.content.trim());
396
435
  const pending = running && (turn == null ? void 0 : turn.text) ? turn.text.replace(/^build\s*me\s*:\s*/i, "") : "";
397
436
  if (pending && !rows.some((m) => m.role === "user" && pending.startsWith(m.content))) {
398
437
  rows.push({
@@ -449,42 +488,73 @@ ${agentEditNote(target)}` : "";
449
488
  // The template note is for the agent; the person sees what they typed.
450
489
  content: m.role === "user" ? stripAgentNote(m.content).replace(/^build\s*me\s*:\s*/i, "") : displayAgentText(m.content),
451
490
  attachments: m.attachments
452
- })), [chat.messages]);
491
+ })).filter((m) => m.role === "user" || m.content.trim()), [chat.messages]);
492
+ const withOpenRun = useCallback((rows) => {
493
+ if (!openRunPending || !(openRun == null ? void 0 : openRun.ask)) return rows;
494
+ const {
495
+ ask
496
+ } = openRun;
497
+ if (rows.some((m) => m.role === "user" && (m.content.startsWith(ask) || ask.startsWith(m.content)))) {
498
+ return rows;
499
+ }
500
+ return [...rows, {
501
+ id: `run-${openRun.startedAt}`,
502
+ role: "user",
503
+ content: ask
504
+ }];
505
+ }, [openRunPending, openRun]);
506
+ const openRunStreaming = openRunPending ? (openRunState == null ? void 0 : openRunState.progress) || "Building your site\u2026" : null;
507
+ const pendingAsk = useMemo(() => pendingAskUser(agentLane && chat.messages.length ? chat.messages : transcriptFromPosts(posts).map((m) => ({
508
+ role: m.role,
509
+ content: m.content
510
+ }))), [agentLane, chat.messages, posts]);
453
511
  const hostChat = useMemo(() => agentLane ? {
454
- messages: agentMessages,
455
- streaming: chat.response ? displayAgentText(chat.response) : null,
456
- busy: agentBusy,
512
+ messages: withOpenRun(agentMessages.length ? agentMessages : messages),
513
+ streaming: chat.response ? displayAgentText(chat.response) : openRunStreaming,
514
+ busy: agentBusy || openRunPending,
457
515
  error: chat.error,
458
516
  connected: cdecli.channelConnected,
517
+ ask: pendingAsk,
459
518
  send
460
519
  } : {
461
- messages,
462
- streaming: running ? progress || (building == null ? void 0 : building.statusText) || "Working on it\u2026" : null,
463
- busy: running,
520
+ messages: withOpenRun(messages),
521
+ streaming: running ? progress || (building == null ? void 0 : building.statusText) || "Working on it\u2026" : openRunStreaming,
522
+ busy: running || openRunPending,
464
523
  error: turnError || (session.ready ? session.error : null),
465
524
  connected: Boolean(token),
525
+ ask: pendingAsk,
466
526
  send
467
- }, [agentLane, agentMessages, chat.response, agentBusy, chat.error, cdecli.channelConnected, messages, running, progress, building == null ? void 0 : building.statusText, turnError, session.ready, session.error, token, send]);
527
+ }, [agentLane, agentMessages, pendingAsk, withOpenRun, openRunStreaming, openRunPending, chat.response, agentBusy, chat.error, cdecli.channelConnected, messages, running, progress, building == null ? void 0 : building.statusText, turnError, session.ready, session.error, token, send]);
468
528
  const sandboxApi = useSandboxProxyApi();
529
+ const sandboxCall = useMemo(() => createSandboxRouter({
530
+ channelId: activeChannelId,
531
+ proxy: sandboxApi.proxy,
532
+ direct: token && indexUrl ? directSandboxRun(token, indexUrl) : async () => ({
533
+ successful: false,
534
+ error: "Not signed in to the build service."
535
+ }),
536
+ proxyMissing: sandboxApi.proxyMissing
537
+ }), [activeChannelId, token, indexUrl, sandboxApi]);
538
+ const keepAlive = useSandboxKeepAlive({
539
+ run: token || activeChannelId ? sandboxCall : null,
540
+ sandboxId: (_f = builds[0]) == null ? void 0 : _f.sandboxId,
541
+ live: activeChannelId !== null && Boolean((_g = builds[0]) == null ? void 0 : _g.sandboxId)
542
+ });
543
+ const {
544
+ noteActivity
545
+ } = keepAlive;
546
+ useEffect(() => {
547
+ if (activeChannelId) noteActivity();
548
+ }, [activeChannelId, posts.length, chat.messages.length, noteActivity]);
469
549
  const resumePreview = useCallback(async (build) => {
470
- const {
471
- current
472
- } = tokenRef;
473
550
  const channelId = activeChannelId;
474
- if (!build.sandboxId || !current && !channelId) return "failed";
475
- const call = createSandboxRouter({
476
- channelId,
477
- proxy: sandboxApi.proxy,
478
- direct: current && indexUrl ? directSandboxRun(current, indexUrl) : async () => ({
479
- successful: false,
480
- error: "Not signed in to the build service."
481
- }),
482
- proxyMissing: sandboxApi.proxyMissing
483
- });
484
- const result = await restoreSandbox(call, {
551
+ if (!build.sandboxId || !tokenRef.current && !channelId) return "failed";
552
+ noteActivity();
553
+ const result = await restoreSandbox(sandboxCall, {
485
554
  sandboxId: build.sandboxId,
486
555
  snapshotId: build.snapshotId,
487
- templateId: build.templateId
556
+ templateId: build.templateId,
557
+ previewUrl: build.previewUrl
488
558
  });
489
559
  if (result.outcome === "ok" && result.sandboxId && result.previewUrl) {
490
560
  rememberRestored(build.key, result.sandboxId, result.previewUrl);
@@ -506,7 +576,43 @@ ${agentEditNote(target)}` : "";
506
576
  }
507
577
  }
508
578
  return result.outcome;
509
- }, [activeChannelId, indexUrl, sandboxApi, rememberRestored, activeProject, upsertProject]);
579
+ }, [activeChannelId, sandboxCall, noteActivity, sandboxApi, rememberRestored, activeProject, upsertProject]);
580
+ const redoFor = useRef(/* @__PURE__ */ new Map());
581
+ const undo = useCallback(async (build) => {
582
+ if (!build.sandboxId) return {
583
+ ok: false,
584
+ message: "There is nothing to undo yet."
585
+ };
586
+ noteActivity();
587
+ const outcome = await undoLastChange(sandboxCall, build.sandboxId);
588
+ if (outcome.redo) redoFor.current.set(build.key, outcome.redo);
589
+ else redoFor.current.delete(build.key);
590
+ return {
591
+ ok: outcome.ok,
592
+ message: outcome.message,
593
+ canRedo: Boolean(outcome.redo),
594
+ askAgent: Boolean(outcome.listFailed) && build.origin === "agent"
595
+ };
596
+ }, [sandboxCall, noteActivity]);
597
+ const redo = useCallback(async (build) => {
598
+ const version = redoFor.current.get(build.key);
599
+ if (!build.sandboxId || !version) return {
600
+ ok: false,
601
+ message: "There is nothing to redo."
602
+ };
603
+ const outcome = await redoChange(sandboxCall, build.sandboxId, version);
604
+ if (outcome.ok) redoFor.current.delete(build.key);
605
+ return {
606
+ ok: outcome.ok,
607
+ message: outcome.message
608
+ };
609
+ }, [sandboxCall]);
610
+ const downloadCode = useCallback(async (build) => {
611
+ if (!build.sandboxId) return null;
612
+ noteActivity();
613
+ const result = await sandboxCall(WEBBUILDER_EXPORT_ZIP_ACTION, exportZipParams(build.sandboxId, build.title), 10 * 60 * 1e3).catch(() => null);
614
+ return exportZipUrl(result);
615
+ }, [sandboxCall, noteActivity]);
510
616
  const [deployToCloudflare] = useAiCodeDeployToCloudflareMutation({});
511
617
  const publish = useCallback(async (build, projectName) => {
512
618
  var _a2;
@@ -552,7 +658,7 @@ ${agentEditNote(target)}` : "";
552
658
  startBlocked = session.ready && session.error ? session.error : "Connecting to Web Builder\u2026";
553
659
  }
554
660
  return {
555
- projects,
661
+ projects: shownProjects,
556
662
  projectsLoading: gallery.loading,
557
663
  projectsError: gallery.error,
558
664
  projectsHasMore: gallery.hasMore,
@@ -574,6 +680,9 @@ ${agentEditNote(target)}` : "";
574
680
  publishing: work === "publishing",
575
681
  hostChat,
576
682
  publish,
577
- resumePreview
683
+ resumePreview,
684
+ undo,
685
+ redo,
686
+ downloadCode
578
687
  };
579
688
  }export{useWebBuilderHost};//# sourceMappingURL=useWebBuilderHost.js.map