@adminide-stack/yantra-mobile 12.0.142-alpha.0 → 12.0.144

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 (118) hide show
  1. package/lib/features/attachments/displayUserMessageText.js +2 -2
  2. package/lib/features/attachments/displayUserMessageText.js.map +1 -1
  3. package/lib/features/devices/bodyComposition.js +116 -0
  4. package/lib/features/devices/bodyComposition.js.map +1 -0
  5. package/lib/features/devices/readings.js.map +1 -1
  6. package/lib/features/devices/session.js +22 -2
  7. package/lib/features/devices/session.js.map +1 -1
  8. package/lib/features/notion/NotionAgentPanel.js +987 -0
  9. package/lib/features/notion/NotionAgentPanel.js.map +1 -0
  10. package/lib/features/notion/NotionAskAiSheet.js +362 -0
  11. package/lib/features/notion/NotionAskAiSheet.js.map +1 -0
  12. package/lib/features/notion/NotionBlockView.js +4 -3
  13. package/lib/features/notion/NotionBlockView.js.map +1 -1
  14. package/lib/features/notion/NotionConnectionsSheet.js +47 -13
  15. package/lib/features/notion/NotionConnectionsSheet.js.map +1 -1
  16. package/lib/features/notion/NotionPageView.js +68 -8
  17. package/lib/features/notion/NotionPageView.js.map +1 -1
  18. package/lib/features/notion/NotionSidebarSections.js +1 -1
  19. package/lib/features/notion/NotionSidebarSections.js.map +1 -1
  20. package/lib/features/notion/NotionWorkspaceView.js +14 -9
  21. package/lib/features/notion/NotionWorkspaceView.js.map +1 -1
  22. package/lib/features/notion/database/DatabaseCells.js +3 -1
  23. package/lib/features/notion/database/DatabaseCells.js.map +1 -1
  24. package/lib/features/notion/database/DatabaseDateLayouts.js +1 -1
  25. package/lib/features/notion/database/DatabaseDateLayouts.js.map +1 -1
  26. package/lib/features/notion/database/NotionDatabaseView.js +1 -1
  27. package/lib/features/notion/database/NotionDatabaseView.js.map +1 -1
  28. package/lib/features/notion/notionAgentRunStatus.js +2 -2
  29. package/lib/features/notion/notionAgentRunStatus.js.map +1 -1
  30. package/lib/features/notion/notionAgentRuns.js +57 -0
  31. package/lib/features/notion/notionAgentRuns.js.map +1 -0
  32. package/lib/features/notion/notionAiContext.js +91 -0
  33. package/lib/features/notion/notionAiContext.js.map +1 -0
  34. package/lib/features/notion/notionConnections.js +42 -4
  35. package/lib/features/notion/notionConnections.js.map +1 -1
  36. package/lib/features/notion/notionDocuments.js +61 -1
  37. package/lib/features/notion/notionDocuments.js.map +1 -1
  38. package/lib/features/notion/notionNotesAgent.js +173 -3
  39. package/lib/features/notion/notionNotesAgent.js.map +1 -1
  40. package/lib/features/notion/notionPageKinds.js +1 -1
  41. package/lib/features/webbuilder/WebBuilderExperience.js +229 -97
  42. package/lib/features/webbuilder/WebBuilderExperience.js.map +1 -1
  43. package/lib/features/webbuilder/buildFragments.js +33 -7
  44. package/lib/features/webbuilder/buildFragments.js.map +1 -1
  45. package/lib/features/webbuilder/buildJob.js +2 -2
  46. package/lib/features/webbuilder/buildJob.js.map +1 -1
  47. package/lib/features/webbuilder/codeFiles.js +18 -1
  48. package/lib/features/webbuilder/codeFiles.js.map +1 -1
  49. package/lib/features/webbuilder/components/BottomTabs.js.map +1 -1
  50. package/lib/features/webbuilder/components/CodeView.js +134 -56
  51. package/lib/features/webbuilder/components/CodeView.js.map +1 -1
  52. package/lib/features/webbuilder/components/Composer.js +2 -2
  53. package/lib/features/webbuilder/components/Composer.js.map +1 -1
  54. package/lib/features/webbuilder/components/PreviewFrame.js +19 -3
  55. package/lib/features/webbuilder/components/PreviewFrame.js.map +1 -1
  56. package/lib/features/webbuilder/components/PreviewScreen.js +310 -46
  57. package/lib/features/webbuilder/components/PreviewScreen.js.map +1 -1
  58. package/lib/features/webbuilder/components/ProjectChat.js +141 -12
  59. package/lib/features/webbuilder/components/ProjectChat.js.map +1 -1
  60. package/lib/features/webbuilder/components/ProjectSheets.js +792 -0
  61. package/lib/features/webbuilder/components/ProjectSheets.js.map +1 -0
  62. package/lib/features/webbuilder/components/ProjectsScreen.js +14 -2
  63. package/lib/features/webbuilder/components/ProjectsScreen.js.map +1 -1
  64. package/lib/features/webbuilder/components/PublishSheet.js +914 -58
  65. package/lib/features/webbuilder/components/PublishSheet.js.map +1 -1
  66. package/lib/features/webbuilder/components/TemplateCard.js +9 -6
  67. package/lib/features/webbuilder/components/TemplateCard.js.map +1 -1
  68. package/lib/features/webbuilder/components/TemplatesSheet.js +5 -17
  69. package/lib/features/webbuilder/components/TemplatesSheet.js.map +1 -1
  70. package/lib/features/webbuilder/connectorTemplates.js +171 -0
  71. package/lib/features/webbuilder/connectorTemplates.js.map +1 -0
  72. package/lib/features/webbuilder/fileTree.js +163 -0
  73. package/lib/features/webbuilder/fileTree.js.map +1 -0
  74. package/lib/features/webbuilder/followUps.js +67 -0
  75. package/lib/features/webbuilder/followUps.js.map +1 -0
  76. package/lib/features/webbuilder/previewProbe.js +49 -0
  77. package/lib/features/webbuilder/previewProbe.js.map +1 -0
  78. package/lib/features/webbuilder/previewRoutes.js +156 -0
  79. package/lib/features/webbuilder/previewRoutes.js.map +1 -0
  80. package/lib/features/webbuilder/projectTools.js +341 -0
  81. package/lib/features/webbuilder/projectTools.js.map +1 -0
  82. package/lib/features/webbuilder/publishTools.js +475 -0
  83. package/lib/features/webbuilder/publishTools.js.map +1 -0
  84. package/lib/features/webbuilder/sleepNotice.js +13 -0
  85. package/lib/features/webbuilder/sleepNotice.js.map +1 -0
  86. package/lib/features/webbuilder/stepLabels.js +37 -7
  87. package/lib/features/webbuilder/stepLabels.js.map +1 -1
  88. package/lib/features/webbuilder/studioCatalog.js +5 -1
  89. package/lib/features/webbuilder/studioCatalog.js.map +1 -1
  90. package/lib/features/webbuilder/templateCatalog.js +140 -0
  91. package/lib/features/webbuilder/templateCatalog.js.map +1 -0
  92. package/lib/features/webbuilder/webbuilderPrompts.js +31 -5
  93. package/lib/features/webbuilder/webbuilderPrompts.js.map +1 -1
  94. package/lib/features/webbuilder/workflow.js +8 -4
  95. package/lib/features/webbuilder/workflow.js.map +1 -1
  96. package/lib/screens/Devices/index.js +205 -3
  97. package/lib/screens/Devices/index.js.map +1 -1
  98. package/lib/screens/WebBuilderNative/codeWorkspaceApi.js +9 -6
  99. package/lib/screens/WebBuilderNative/codeWorkspaceApi.js.map +1 -1
  100. package/lib/screens/WebBuilderNative/index.js +1 -1
  101. package/lib/screens/WebBuilderNative/index.js.map +1 -1
  102. package/lib/screens/WebBuilderNative/keepAlive.js +53 -3
  103. package/lib/screens/WebBuilderNative/keepAlive.js.map +1 -1
  104. package/lib/screens/WebBuilderNative/unreachableRetry.js +23 -0
  105. package/lib/screens/WebBuilderNative/unreachableRetry.js.map +1 -0
  106. package/lib/screens/WebBuilderNative/useConnectorTemplates.js +37 -0
  107. package/lib/screens/WebBuilderNative/useConnectorTemplates.js.map +1 -0
  108. package/lib/screens/WebBuilderNative/useSandboxKeepAlive.js +6 -0
  109. package/lib/screens/WebBuilderNative/useSandboxKeepAlive.js.map +1 -1
  110. package/lib/screens/WebBuilderNative/useWebBuilderHost.js +302 -40
  111. package/lib/screens/WebBuilderNative/useWebBuilderHost.js.map +1 -1
  112. package/lib/screens/WebBuilderNative/useWebBuilderJobs.js +2 -2
  113. package/lib/screens/WebBuilderNative/useWebBuilderJobs.js.map +1 -1
  114. package/lib/screens/WebBuilderNative/versionUndo.js +1 -1
  115. package/lib/screens/WebBuilderNative/versionUndo.js.map +1 -1
  116. package/lib/screens/WebBuilderNative/webbuilderTransport.js +147 -77
  117. package/lib/screens/WebBuilderNative/webbuilderTransport.js.map +1 -1
  118. package/package.json +4 -4
@@ -25,27 +25,57 @@ function commandLabel(command) {
25
25
  if (/\bcurl\b|\bwget\b/i.test(command)) return "Checking the preview";
26
26
  return "Running a check";
27
27
  }
28
+ const TOOL_LABELS = [[/^(read|glob|grep|ls|notebook_?read)$/i, "Reading your site"], [/^(edit|multi_?edit|write|str_replace_editor|notebook_?edit)$/i, "Building your site"], [/^todo_?write$/i, "Planning the work"], [/^(web_?fetch|web_?search|web_?scrape|tool_?search)$/i, "Looking things up"]];
29
+ const GENERIC_STEP_LABEL = "Working on your site";
30
+ function isToolId(label) {
31
+ const t = label.replace(/\s*[—–-]\s*$/, "").trim();
32
+ return /__|_/.test(t) || /^mcp\b/i.test(t) || /^[A-Za-z][A-Za-z0-9]*$/.test(t) && /[a-z][A-Z]/.test(t);
33
+ }
34
+ function stepAction(step) {
35
+ var _a, _b, _c;
36
+ const executing = /Executing\s+([A-Z0-9_]{3,})/.exec(step.label);
37
+ if (executing) return executing[1];
38
+ const detail = ((_a = step.detail) != null ? _a : "").trim().replace(/(?:…|\.\.\.)$/, "");
39
+ return (_c = (_b = /\/([A-Z][A-Z0-9_]{2,})\s*$/.exec(detail)) == null ? void 0 : _b[1]) != null ? _c : null;
40
+ }
28
41
  function friendlyStepLabel(step) {
29
- var _a, _b;
42
+ var _a;
30
43
  const label = step.label.trim();
31
44
  let detail = ((_a = step.detail) != null ? _a : "").trim();
32
45
  const executing = /Executing\s+([A-Z0-9_]+)\s+on\s+([\w-]+)/.exec(label);
33
46
  if (executing) detail = `${executing[2]}/${executing[1]}`;
34
47
  if (/updating task plan/i.test(label)) return "Planning the work";
35
48
  if (/^\{.*\}$/.test(label)) return "Looking things up";
36
- const rawAction = (_b = /\/([A-Z0-9_]+)\s*$/.exec(detail)) == null ? void 0 : _b[1];
49
+ const rawAction = stepAction({
50
+ label,
51
+ detail
52
+ });
37
53
  if (rawAction) {
38
54
  const prefix = detail.split("/")[0].replace(/^yantra-/i, "").replace(/[-_]/g, "").toUpperCase();
39
55
  const action = prefix && rawAction.startsWith(`${prefix}_`) ? rawAction.slice(prefix.length + 1) : rawAction;
40
- const hit = ACTION_LABELS.find(([re]) => re.test(action));
41
- return hit ? hit[1] : "Working on your site";
56
+ const hit2 = ACTION_LABELS.find(([re]) => re.test(action));
57
+ return hit2 ? hit2[1] : GENERIC_STEP_LABEL;
42
58
  }
43
59
  if (/^Editing files$/i.test(label)) return "Building your site";
44
60
  if (/^Reading files$/i.test(label)) return "Reading your site";
45
61
  if (/^Running a command$/i.test(label)) return commandLabel(detail);
46
62
  if (/^Searching tools$/i.test(label)) return "Getting ready";
47
- if (/^Running\s/i.test(label)) return "Working on your site";
48
- return label || "Working on your site";
63
+ if (/^Running\s/i.test(label)) return GENERIC_STEP_LABEL;
64
+ const tool = label.replace(/^mcp__[^_]+__/i, "").replace(/\s*[—–-]\s*$/, "");
65
+ if (/^bash$/i.test(tool)) return commandLabel(detail);
66
+ const hit = TOOL_LABELS.find(([re]) => re.test(tool));
67
+ if (hit) return hit[1];
68
+ if (!label || isToolId(label)) return GENERIC_STEP_LABEL;
69
+ return label;
70
+ }
71
+ function firstContentLanded(steps) {
72
+ return steps.some((s) => {
73
+ var _a;
74
+ const action = (_a = stepAction(s)) != null ? _a : "";
75
+ if (/(?:^|_)FINISH$/.test(action)) return true;
76
+ if (s.status !== "done") return false;
77
+ return /(?:WRITE_FILES|EDIT_FILES|SET_CONTENT|APPLY_CONTENT)$/.test(action) || /^Editing files$/i.test(s.label.trim());
78
+ });
49
79
  }
50
80
  function friendlySteps(steps) {
51
81
  var _a, _b;
@@ -65,4 +95,4 @@ function friendlySteps(steps) {
65
95
  }
66
96
  }
67
97
  return out;
68
- }export{friendlyStepLabel,friendlySteps};//# sourceMappingURL=stepLabels.js.map
98
+ }export{GENERIC_STEP_LABEL,firstContentLanded,friendlyStepLabel,friendlySteps,stepAction};//# sourceMappingURL=stepLabels.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"stepLabels.js","sources":["../../../src/features/webbuilder/stepLabels.ts"],"sourcesContent":["/**\n * Plain-language names for the agent's tool steps in Web Builder (\"Building your site\",\n * \"Testing your site in a browser\") in place of connector slugs - web `chat/stepLabels.ts`\n * (#479, #496). Consecutive steps with the same name merge into one row. Pure.\n */\nimport type { ToolStep } from '../chat/toolActivity';\n\nconst ACTION_LABELS: Array<[RegExp, string]> = [\n [/^(BROWSER_CHECK|FINISH)$/i, 'Testing your site in a browser'],\n [/UNPUBLISH/i, 'Taking the site offline'],\n [/TERMINATE|STOP_SANDBOX|KILL_SANDBOX/i, 'Shutting down the preview'],\n [/EXPORT|ZIP/i, 'Packaging your code'],\n [/DEPLOY|PUBLISH/i, 'Publishing'],\n [/GITHUB|_PUSH\\b|COMMIT/i, 'Saving to GitHub'],\n [/SNAPSHOT|RESTORE_POINT|SAVE_VERSION/i, 'Saving a restore point'],\n [/RESTORE|ROLLBACK/i, 'Restoring a version'],\n [/CREATE_SANDBOX|RESUME|CONNECT_SANDBOX|CREATE_PROJECT/i, 'Setting up your project'],\n [/SCREENSHOT|PROBE|HEALTH|CHECK_PREVIEW|QA\\b/i, 'Checking the preview'],\n [/START|DEV_SERVER|PREVIEW/i, 'Starting the preview'],\n [/DATABASE|SUPABASE|SQL|MIGRAT/i, 'Setting up the database'],\n [/IMAGE|LOGO|PHOTO/i, 'Creating images'],\n [/EDIT_FILES|WRITE_FILES|SET_CONTENT|EDIT|WRITE/i, 'Building your site'],\n [/READ_FILES|LIST_FILES|READ|LIST/i, 'Reading your site'],\n [/INSTALL|PACKAGES?/i, 'Installing packages'],\n [/BUILD|TYPECHECK|LINT/i, 'Checking the build'],\n];\n\nfunction commandLabel(command: string): string {\n if (/\\b(npm|yarn|pnpm|bun)\\s+(i|install|add)\\b/i.test(command)) return 'Installing packages';\n if (/\\b(build|tsc|lint|test|vitest|jest|typecheck)\\b/i.test(command)) return 'Checking the build';\n if (/\\b(dev|preview|serve|start)\\b/i.test(command)) return 'Starting the preview';\n if (/\\bcurl\\b|\\bwget\\b/i.test(command)) return 'Checking the preview';\n return 'Running a check';\n}\n\n/** One raw step as a plain-language label. */\nexport function friendlyStepLabel(step: Pick<ToolStep, 'label' | 'detail'>): string {\n const label = step.label.trim();\n let detail = (step.detail ?? '').trim();\n // cdecli's own row: \"🚀 Executing WEBBUILDER_WRITE_FILES on yantra-webbuilder\".\n const executing = /Executing\\s+([A-Z0-9_]+)\\s+on\\s+([\\w-]+)/.exec(label);\n if (executing) detail = `${executing[2]}/${executing[1]}`;\n if (/updating task plan/i.test(label)) return 'Planning the work';\n if (/^\\{.*\\}$/.test(label)) return 'Looking things up';\n // `execute_action — yantra-<connector>/<ACTION>`: the action says what is happening.\n const rawAction = /\\/([A-Z0-9_]+)\\s*$/.exec(detail)?.[1];\n if (rawAction) {\n // WEBBUILDER_BUILD_X: the connector's own prefix must not match \"BUILD\".\n const prefix = detail\n .split('/')[0]\n .replace(/^yantra-/i, '')\n .replace(/[-_]/g, '')\n .toUpperCase();\n const action = prefix && rawAction.startsWith(`${prefix}_`) ? rawAction.slice(prefix.length + 1) : rawAction;\n const hit = ACTION_LABELS.find(([re]) => re.test(action));\n return hit ? hit[1] : 'Working on your site';\n }\n if (/^Editing files$/i.test(label)) return 'Building your site';\n if (/^Reading files$/i.test(label)) return 'Reading your site';\n if (/^Running a command$/i.test(label)) return commandLabel(detail);\n if (/^Searching tools$/i.test(label)) return 'Getting ready';\n if (/^Running\\s/i.test(label)) return 'Working on your site';\n return label || 'Working on your site';\n}\n\n/** The steps with friendly names, a run of the same name merged into one row. */\nexport function friendlySteps(steps: readonly ToolStep[]): ToolStep[] {\n const out: ToolStep[] = [];\n for (const step of steps) {\n const label = friendlyStepLabel(step);\n const last = out[out.length - 1];\n if (last && last.label === label) {\n last.count = (last.count ?? 1) + (step.count ?? 1);\n last.status = step.status;\n } else {\n out.push({ ...step, label, detail: undefined, count: step.count });\n }\n }\n return out;\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"stepLabels.js","sources":["../../../src/features/webbuilder/stepLabels.ts"],"sourcesContent":["/**\n * Plain-language names for the agent's tool steps in Web Builder (\"Building your site\",\n * \"Testing your site in a browser\") in place of connector slugs - web `chat/stepLabels.ts`\n * (#479, #496). Consecutive steps with the same name merge into one row. Pure.\n */\nimport type { ToolStep } from '../chat/toolActivity';\n\nconst ACTION_LABELS: Array<[RegExp, string]> = [\n [/^(BROWSER_CHECK|FINISH)$/i, 'Testing your site in a browser'],\n [/UNPUBLISH/i, 'Taking the site offline'],\n [/TERMINATE|STOP_SANDBOX|KILL_SANDBOX/i, 'Shutting down the preview'],\n [/EXPORT|ZIP/i, 'Packaging your code'],\n [/DEPLOY|PUBLISH/i, 'Publishing'],\n [/GITHUB|_PUSH\\b|COMMIT/i, 'Saving to GitHub'],\n [/SNAPSHOT|RESTORE_POINT|SAVE_VERSION/i, 'Saving a restore point'],\n [/RESTORE|ROLLBACK/i, 'Restoring a version'],\n [/CREATE_SANDBOX|RESUME|CONNECT_SANDBOX|CREATE_PROJECT/i, 'Setting up your project'],\n [/SCREENSHOT|PROBE|HEALTH|CHECK_PREVIEW|QA\\b/i, 'Checking the preview'],\n [/START|DEV_SERVER|PREVIEW/i, 'Starting the preview'],\n [/DATABASE|SUPABASE|SQL|MIGRAT/i, 'Setting up the database'],\n [/IMAGE|LOGO|PHOTO/i, 'Creating images'],\n [/EDIT_FILES|WRITE_FILES|SET_CONTENT|EDIT|WRITE/i, 'Building your site'],\n [/READ_FILES|LIST_FILES|READ|LIST/i, 'Reading your site'],\n [/INSTALL|PACKAGES?/i, 'Installing packages'],\n [/BUILD|TYPECHECK|LINT/i, 'Checking the build'],\n];\n\nfunction commandLabel(command: string): string {\n if (/\\b(npm|yarn|pnpm|bun)\\s+(i|install|add)\\b/i.test(command)) return 'Installing packages';\n if (/\\b(build|tsc|lint|test|vitest|jest|typecheck)\\b/i.test(command)) return 'Checking the build';\n if (/\\b(dev|preview|serve|start)\\b/i.test(command)) return 'Starting the preview';\n if (/\\bcurl\\b|\\bwget\\b/i.test(command)) return 'Checking the preview';\n return 'Running a check';\n}\n\n// Bare tool names the agent runs, as the person's words; anything else shaped like a tool id is generic.\nconst TOOL_LABELS: Array<[RegExp, string]> = [\n [/^(read|glob|grep|ls|notebook_?read)$/i, 'Reading your site'],\n [/^(edit|multi_?edit|write|str_replace_editor|notebook_?edit)$/i, 'Building your site'],\n [/^todo_?write$/i, 'Planning the work'],\n [/^(web_?fetch|web_?search|web_?scrape|tool_?search)$/i, 'Looking things up'],\n];\n\nexport const GENERIC_STEP_LABEL = 'Working on your site';\n\n/** A tool id rather than words: `execute_action`, `mcp__cdecli__…`, `WebFetch`, or one cut short (\"execute_action —\"). */\nfunction isToolId(label: string): boolean {\n const t = label.replace(/\\s*[—–-]\\s*$/, '').trim();\n return /__|_/.test(t) || /^mcp\\b/i.test(t) || (/^[A-Za-z][A-Za-z0-9]*$/.test(t) && /[a-z][A-Z]/.test(t));\n}\n\n/** A step title for people: `fallback` when the label is missing or is a tool id. */\nexport function readableStepLabel(label: string | null | undefined, fallback: string): string {\n const t = (label ?? '').trim();\n return t && !isToolId(t) ? t : fallback;\n}\n\n/** The connector action a raw step ran (`WEBBUILDER_WRITE_FILES`), even when its line was cut short; null if none. */\nexport function stepAction(step: Pick<ToolStep, 'label' | 'detail'>): string | null {\n const executing = /Executing\\s+([A-Z0-9_]{3,})/.exec(step.label);\n if (executing) return executing[1];\n const detail = (step.detail ?? '').trim().replace(/(?:…|\\.\\.\\.)$/, '');\n return /\\/([A-Z][A-Z0-9_]{2,})\\s*$/.exec(detail)?.[1] ?? null;\n}\n\n/** One raw step as a plain-language label. */\nexport function friendlyStepLabel(step: Pick<ToolStep, 'label' | 'detail'>): string {\n const label = step.label.trim();\n let detail = (step.detail ?? '').trim();\n // cdecli's own row: \"🚀 Executing WEBBUILDER_WRITE_FILES on yantra-webbuilder\".\n const executing = /Executing\\s+([A-Z0-9_]+)\\s+on\\s+([\\w-]+)/.exec(label);\n if (executing) detail = `${executing[2]}/${executing[1]}`;\n if (/updating task plan/i.test(label)) return 'Planning the work';\n if (/^\\{.*\\}$/.test(label)) return 'Looking things up';\n // `execute_action — yantra-<connector>/<ACTION>`: the action says what is happening.\n const rawAction = stepAction({ label, detail });\n if (rawAction) {\n // WEBBUILDER_BUILD_X: the connector's own prefix must not match \"BUILD\".\n const prefix = detail\n .split('/')[0]\n .replace(/^yantra-/i, '')\n .replace(/[-_]/g, '')\n .toUpperCase();\n const action = prefix && rawAction.startsWith(`${prefix}_`) ? rawAction.slice(prefix.length + 1) : rawAction;\n const hit = ACTION_LABELS.find(([re]) => re.test(action));\n return hit ? hit[1] : GENERIC_STEP_LABEL;\n }\n if (/^Editing files$/i.test(label)) return 'Building your site';\n if (/^Reading files$/i.test(label)) return 'Reading your site';\n if (/^Running a command$/i.test(label)) return commandLabel(detail);\n if (/^Searching tools$/i.test(label)) return 'Getting ready';\n if (/^Running\\s/i.test(label)) return GENERIC_STEP_LABEL;\n const tool = label.replace(/^mcp__[^_]+__/i, '').replace(/\\s*[—–-]\\s*$/, '');\n if (/^bash$/i.test(tool)) return commandLabel(detail);\n const hit = TOOL_LABELS.find(([re]) => re.test(tool));\n if (hit) return hit[1];\n if (!label || isToolId(label)) return GENERIC_STEP_LABEL;\n return label;\n}\n\n/** The agent wrote the site's content: a write / edit / content action finished, or the final check ran. */\nexport function firstContentLanded(steps: readonly ToolStep[]): boolean {\n return steps.some((s) => {\n const action = stepAction(s) ?? '';\n if (/(?:^|_)FINISH$/.test(action)) return true;\n if (s.status !== 'done') return false;\n return (\n /(?:WRITE_FILES|EDIT_FILES|SET_CONTENT|APPLY_CONTENT)$/.test(action) ||\n /^Editing files$/i.test(s.label.trim())\n );\n });\n}\n\n/** The steps with friendly names, a run of the same name merged into one row. */\nexport function friendlySteps(steps: readonly ToolStep[]): ToolStep[] {\n const out: ToolStep[] = [];\n for (const step of steps) {\n const label = friendlyStepLabel(step);\n const last = out[out.length - 1];\n if (last && last.label === label) {\n last.count = (last.count ?? 1) + (step.count ?? 1);\n last.status = step.status;\n } else {\n out.push({ ...step, label, detail: undefined, count: step.count });\n }\n }\n return out;\n}\n"],"names":["hit"],"mappings":";;;;;;;;;;;;;;;;;;;AAMA,MAAM,aAAyC,GAAA,CAAC,CAAC,2BAAA,EAA6B,gCAAgC,CAAG,EAAA,CAAC,YAAc,EAAA,yBAAyB,CAAG,EAAA,CAAC,sCAAwC,EAAA,2BAA2B,GAAG,CAAC,aAAA,EAAe,qBAAqB,CAAA,EAAG,CAAC,iBAAA,EAAmB,YAAY,CAAA,EAAG,CAAC,wBAA0B,EAAA,kBAAkB,CAAG,EAAA,CAAC,sCAAwC,EAAA,wBAAwB,CAAG,EAAA,CAAC,qBAAqB,qBAAqB,CAAA,EAAG,CAAC,uDAAA,EAAyD,yBAAyB,CAAA,EAAG,CAAC,6CAAA,EAA+C,sBAAsB,CAAG,EAAA,CAAC,2BAA6B,EAAA,sBAAsB,CAAG,EAAA,CAAC,+BAAiC,EAAA,yBAAyB,GAAG,CAAC,mBAAA,EAAqB,iBAAiB,CAAA,EAAG,CAAC,gDAAA,EAAkD,oBAAoB,CAAA,EAAG,CAAC,kCAAoC,EAAA,mBAAmB,CAAG,EAAA,CAAC,sBAAsB,qBAAqB,CAAA,EAAG,CAAC,uBAAA,EAAyB,oBAAoB,CAAC,CAAA;AACj/B,SAAS,aAAa,OAAyB,EAAA;AAC7C,EAAA,IAAI,4CAA6C,CAAA,IAAA,CAAK,OAAO,CAAA,EAAU,OAAA,qBAAA;AACvE,EAAA,IAAI,kDAAmD,CAAA,IAAA,CAAK,OAAO,CAAA,EAAU,OAAA,oBAAA;AAC7E,EAAA,IAAI,gCAAiC,CAAA,IAAA,CAAK,OAAO,CAAA,EAAU,OAAA,sBAAA;AAC3D,EAAA,IAAI,oBAAqB,CAAA,IAAA,CAAK,OAAO,CAAA,EAAU,OAAA,sBAAA;AAC/C,EAAO,OAAA,iBAAA;AACT;AAGA,MAAM,cAAuC,CAAC,CAAC,uCAAyC,EAAA,mBAAmB,GAAG,CAAC,+DAAA,EAAiE,oBAAoB,CAAA,EAAG,CAAC,gBAAkB,EAAA,mBAAmB,GAAG,CAAC,sDAAA,EAAwD,mBAAmB,CAAC,CAAA;AACtT,MAAM,kBAAqB,GAAA;AAGlC,SAAS,SAAS,KAAwB,EAAA;AACxC,EAAA,MAAM,IAAI,KAAM,CAAA,OAAA,CAAQ,cAAgB,EAAA,EAAE,EAAE,IAAK,EAAA;AACjD,EAAA,OAAO,MAAO,CAAA,IAAA,CAAK,CAAC,CAAA,IAAK,UAAU,IAAK,CAAA,CAAC,CAAK,IAAA,wBAAA,CAAyB,IAAK,CAAA,CAAC,CAAK,IAAA,YAAA,CAAa,KAAK,CAAC,CAAA;AACvG;AASO,SAAS,WAAW,IAAyD,EAAA;AAhCpF,EAAA,IAAA,EAAA,EAAA,EAAA,EAAA,EAAA;AAiCE,EAAA,MAAM,SAAY,GAAA,6BAAA,CAA8B,IAAK,CAAA,IAAA,CAAK,KAAK,CAAA;AAC/D,EAAI,IAAA,SAAA,EAAkB,OAAA,SAAA,CAAU,CAAC,CAAA;AACjC,EAAM,MAAA,MAAA,GAAA,CAAA,CAAU,UAAK,MAAL,KAAA,IAAA,GAAA,EAAA,GAAe,IAAI,IAAK,EAAA,CAAE,OAAQ,CAAA,eAAA,EAAiB,EAAE,CAAA;AACrE,EAAA,OAAA,CAAO,wCAA6B,IAAK,CAAA,MAAM,CAAxC,KAAA,IAAA,GAAA,MAAA,GAAA,EAAA,CAA4C,OAA5C,IAAkD,GAAA,EAAA,GAAA,IAAA;AAC3D;AAGO,SAAS,kBAAkB,IAAkD,EAAA;AAxCpF,EAAA,IAAA,EAAA;AAyCE,EAAM,MAAA,KAAA,GAAQ,IAAK,CAAA,KAAA,CAAM,IAAK,EAAA;AAC9B,EAAA,IAAI,MAAU,GAAA,CAAA,CAAA,EAAA,GAAA,IAAA,CAAK,MAAL,KAAA,IAAA,GAAA,EAAA,GAAe,IAAI,IAAK,EAAA;AAEtC,EAAM,MAAA,SAAA,GAAY,0CAA2C,CAAA,IAAA,CAAK,KAAK,CAAA;AACvE,EAAI,IAAA,SAAA,WAAoB,CAAG,EAAA,SAAA,CAAU,CAAC,CAAC,CAAA,CAAA,EAAI,SAAU,CAAA,CAAC,CAAC,CAAA,CAAA;AACvD,EAAA,IAAI,qBAAsB,CAAA,IAAA,CAAK,KAAK,CAAA,EAAU,OAAA,mBAAA;AAC9C,EAAA,IAAI,UAAW,CAAA,IAAA,CAAK,KAAK,CAAA,EAAU,OAAA,mBAAA;AAEnC,EAAA,MAAM,YAAY,UAAW,CAAA;AAAA,IAC3B,KAAA;AAAA,IACA;AAAA,GACD,CAAA;AACD,EAAA,IAAI,SAAW,EAAA;AAEb,IAAA,MAAM,MAAS,GAAA,MAAA,CAAO,KAAM,CAAA,GAAG,EAAE,CAAC,CAAA,CAAE,OAAQ,CAAA,WAAA,EAAa,EAAE,CAAE,CAAA,OAAA,CAAQ,OAAS,EAAA,EAAE,EAAE,WAAY,EAAA;AAC9F,IAAA,MAAM,MAAS,GAAA,MAAA,IAAU,SAAU,CAAA,UAAA,CAAW,CAAG,EAAA,MAAM,CAAG,CAAA,CAAA,CAAA,GAAI,SAAU,CAAA,KAAA,CAAM,MAAO,CAAA,MAAA,GAAS,CAAC,CAAI,GAAA,SAAA;AACnG,IAAMA,MAAAA,IAAAA,GAAM,aAAc,CAAA,IAAA,CAAK,CAAC,CAAC,EAAE,CAAM,KAAA,EAAA,CAAG,IAAK,CAAA,MAAM,CAAC,CAAA;AACxD,IAAOA,OAAAA,IAAAA,GAAMA,IAAI,CAAA,CAAC,CAAI,GAAA,kBAAA;AAAA;AAExB,EAAA,IAAI,kBAAmB,CAAA,IAAA,CAAK,KAAK,CAAA,EAAU,OAAA,oBAAA;AAC3C,EAAA,IAAI,kBAAmB,CAAA,IAAA,CAAK,KAAK,CAAA,EAAU,OAAA,mBAAA;AAC3C,EAAA,IAAI,uBAAuB,IAAK,CAAA,KAAK,CAAG,EAAA,OAAO,aAAa,MAAM,CAAA;AAClE,EAAA,IAAI,oBAAqB,CAAA,IAAA,CAAK,KAAK,CAAA,EAAU,OAAA,eAAA;AAC7C,EAAA,IAAI,aAAc,CAAA,IAAA,CAAK,KAAK,CAAA,EAAU,OAAA,kBAAA;AACtC,EAAM,MAAA,IAAA,GAAO,MAAM,OAAQ,CAAA,gBAAA,EAAkB,EAAE,CAAE,CAAA,OAAA,CAAQ,gBAAgB,EAAE,CAAA;AAC3E,EAAA,IAAI,UAAU,IAAK,CAAA,IAAI,CAAG,EAAA,OAAO,aAAa,MAAM,CAAA;AACpD,EAAM,MAAA,GAAA,GAAM,WAAY,CAAA,IAAA,CAAK,CAAC,CAAC,EAAE,CAAM,KAAA,EAAA,CAAG,IAAK,CAAA,IAAI,CAAC,CAAA;AACpD,EAAI,IAAA,GAAA,EAAY,OAAA,GAAA,CAAI,CAAC,CAAA;AACrB,EAAA,IAAI,CAAC,KAAA,IAAS,QAAS,CAAA,KAAK,GAAU,OAAA,kBAAA;AACtC,EAAO,OAAA,KAAA;AACT;AAGO,SAAS,mBAAmB,KAAqC,EAAA;AACtE,EAAO,OAAA,KAAA,CAAM,KAAK,CAAK,CAAA,KAAA;AA3EzB,IAAA,IAAA,EAAA;AA4EI,IAAA,MAAM,MAAS,GAAA,CAAA,EAAA,GAAA,UAAA,CAAW,CAAC,CAAA,KAAZ,IAAiB,GAAA,EAAA,GAAA,EAAA;AAChC,IAAA,IAAI,gBAAiB,CAAA,IAAA,CAAK,MAAM,CAAA,EAAU,OAAA,IAAA;AAC1C,IAAI,IAAA,CAAA,CAAE,MAAW,KAAA,MAAA,EAAe,OAAA,KAAA;AAChC,IAAO,OAAA,uDAAA,CAAwD,KAAK,MAAM,CAAA,IAAK,mBAAmB,IAAK,CAAA,CAAA,CAAE,KAAM,CAAA,IAAA,EAAM,CAAA;AAAA,GACtH,CAAA;AACH;AAGO,SAAS,cAAc,KAAwC,EAAA;AApFtE,EAAA,IAAA,EAAA,EAAA,EAAA;AAqFE,EAAA,MAAM,MAAkB,EAAC;AACzB,EAAA,KAAA,MAAW,QAAQ,KAAO,EAAA;AACxB,IAAM,MAAA,KAAA,GAAQ,kBAAkB,IAAI,CAAA;AACpC,IAAA,MAAM,IAAO,GAAA,GAAA,CAAI,GAAI,CAAA,MAAA,GAAS,CAAC,CAAA;AAC/B,IAAI,IAAA,IAAA,IAAQ,IAAK,CAAA,KAAA,KAAU,KAAO,EAAA;AAChC,MAAA,IAAA,CAAK,UAAS,EAAK,GAAA,IAAA,CAAA,KAAA,KAAL,YAAc,CAAM,KAAA,CAAA,EAAA,GAAA,IAAA,CAAK,UAAL,IAAc,GAAA,EAAA,GAAA,CAAA,CAAA;AAChD,MAAA,IAAA,CAAK,SAAS,IAAK,CAAA,MAAA;AAAA,KACd,MAAA;AACL,MAAI,GAAA,CAAA,IAAA,CAAK,iCACJ,IADI,CAAA,EAAA;AAAA,QAEP,KAAA;AAAA,QACA,MAAQ,EAAA,MAAA;AAAA,QACR,OAAO,IAAK,CAAA;AAAA,OACb,CAAA,CAAA;AAAA;AACH;AAEF,EAAO,OAAA,GAAA;AACT"}
@@ -133,6 +133,10 @@ function brandBriefBlock(kit) {
133
133
  if (kit.voice) lines.push(`- Voice: ${kit.voice}`);
134
134
  return lines.length > 1 ? lines.join("\n") : null;
135
135
  }
136
+ function brandKitNote(kit) {
137
+ const block = brandBriefBlock(kit);
138
+ return block ? block.replace(/^Brand kit \(follow it\):/, "[Brand kit] Follow it:") : null;
139
+ }
136
140
  function studioTurnText(opts) {
137
141
  const brief = opts.brief.trim();
138
142
  const parts = [/^build\b/i.test(brief) ? brief : `Build me: ${brief}`];
@@ -144,4 +148,4 @@ function studioTurnText(opts) {
144
148
  }
145
149
  function siteScoreTurn(url) {
146
150
  return `Check ${url.trim()} before I share it: score its speed, accessibility and SEO as one number out of 100, then list the fixes that would raise it most.`;
147
- }export{BRAND_MAX_COLORS,BRAND_MAX_FONTS,STUDIO_BRIEF_MAX,STUDIO_CHIPS,STUDIO_CHIP_GLYPH,STUDIO_STEPS,brandBriefBlock,brandSummary,hostLabel,isBareSeed,isValidLookUrl,normalizeHex,sanitizeBrandKit,siteScoreTurn,studioTurnText};//# sourceMappingURL=studioCatalog.js.map
151
+ }export{BRAND_MAX_COLORS,BRAND_MAX_FONTS,STUDIO_BRIEF_MAX,STUDIO_CHIPS,STUDIO_CHIP_GLYPH,STUDIO_STEPS,brandBriefBlock,brandKitNote,brandSummary,hostLabel,isBareSeed,isValidLookUrl,normalizeHex,sanitizeBrandKit,siteScoreTurn,studioTurnText};//# sourceMappingURL=studioCatalog.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"studioCatalog.js","sources":["../../../src/features/webbuilder/studioCatalog.ts"],"sourcesContent":["/**\n * The studio home's content and the text a studio Start sends - the native\n * twin of the web surface's `studio/studioCatalog.ts` and `studio/brandKit.ts`\n * (same chips and seeds, same steps, same `Build me:` turn and brand block),\n * so a site started on the phone is briefed exactly like one started on the\n * web. Pure: the screens and jest both use it.\n */\n\nexport type StudioChipIcon = 'landing' | 'store' | 'portfolio' | 'booking' | 'dashboard' | 'blog';\n\nexport interface StudioChip {\n id: string;\n label: string;\n icon: StudioChipIcon;\n /** Brief scaffold put in the hero input; the person finishes the sentence. */\n seed: string;\n}\n\nexport const STUDIO_CHIPS: readonly StudioChip[] = [\n { id: 'landing', label: 'Landing page', icon: 'landing', seed: 'A landing page for ' },\n { id: 'store', label: 'Online store', icon: 'store', seed: 'An online store that sells ' },\n { id: 'portfolio', label: 'Portfolio', icon: 'portfolio', seed: 'A portfolio site for ' },\n { id: 'booking', label: 'Booking app', icon: 'booking', seed: 'A booking app where customers can book ' },\n { id: 'dashboard', label: 'Dashboard', icon: 'dashboard', seed: 'A dashboard that tracks ' },\n { id: 'blog', label: 'Blog', icon: 'blog', seed: 'A blog about ' },\n];\n\n/** The chip glyphs (MaterialCommunityIcons) closest to the web's lucide icons. */\nexport const STUDIO_CHIP_GLYPH: Record<StudioChipIcon, string> = {\n landing: 'page-layout-header',\n store: 'shopping-outline',\n portfolio: 'briefcase-outline',\n booking: 'calendar-check-outline',\n dashboard: 'chart-bar',\n blog: 'newspaper-variant-outline',\n};\n\nexport interface StudioStep {\n n: number;\n title: string;\n detail: string;\n}\n\nexport const STUDIO_STEPS: readonly StudioStep[] = [\n { n: 1, title: 'Describe', detail: 'What it is and who it is for' },\n { n: 2, title: 'Pick a look', detail: 'Your brand kit or a site you like' },\n { n: 3, title: 'Build', detail: 'Ask the agent for any change' },\n { n: 4, title: 'Check & publish', detail: 'Site score, then live in one tap' },\n];\n\nexport const STUDIO_BRIEF_MAX = 4000;\n\n/** True when the input is still only a chip seed (nothing the person typed). */\nexport function isBareSeed(text: string): boolean {\n const t = text.trim();\n if (!t) return true;\n return STUDIO_CHIPS.some((c) => c.seed.trim() === t);\n}\n\n/** A look-alike site must be a full https address. */\nexport function isValidLookUrl(value: string): boolean {\n return /^https:\\/\\/[^\\s/$.?#][^\\s]*\\.[^\\s]{2,}$/i.test(value.trim());\n}\n\n/** \"stripe.com\" from a URL, for chips and card subtitles. */\nexport function hostLabel(url: string | null | undefined): string {\n const match = /^https?:\\/\\/([^/?#]+)/i.exec((url ?? '').trim());\n return match ? match[1].replace(/^www\\./, '') : (url ?? '').trim();\n}\n\n// ── Brand kit ──────────────────────────────────────────────────────────────\n\nexport interface BrandKit {\n name: string;\n /** #rrggbb, primary first. */\n colors: string[];\n fonts: string[];\n /** Tone of voice, e.g. \"friendly, plain-spoken\". */\n voice?: string;\n description?: string;\n updatedAt?: string;\n}\n\nexport const BRAND_MAX_COLORS = 6;\nexport const BRAND_MAX_FONTS = 3;\n\nconst clip = (s: string, n: number) => (s.length > n ? s.slice(0, n) : s);\nconst oneLine = (value: unknown, max: number) =>\n clip(\n String(value ?? '')\n .replace(/\\s+/g, ' ')\n .trim(),\n max,\n );\n\n/** `#abc` / `abc` / `#AABBCC` -> `#aabbcc`; anything else -> null. */\nexport function normalizeHex(raw: unknown): string | null {\n const value = String(raw ?? '')\n .trim()\n .replace(/^#/, '')\n .toLowerCase();\n if (/^[0-9a-f]{6}$/.test(value)) return `#${value}`;\n if (/^[0-9a-f]{3}$/.test(value)) return `#${value.replace(/(.)/g, '$1$1')}`;\n return null;\n}\n\n/** A kit worth keeping (a name or a colour), with every field cleaned - or null. */\nexport function sanitizeBrandKit(raw: unknown): BrandKit | null {\n if (!raw || typeof raw !== 'object') return null;\n const r = raw as Record<string, unknown>;\n const name = oneLine(r.name, 80);\n const colors = Array.from(\n new Set((Array.isArray(r.colors) ? r.colors : []).map(normalizeHex).filter((c): c is string => Boolean(c))),\n ).slice(0, BRAND_MAX_COLORS);\n const fonts = Array.from(\n new Set(\n (Array.isArray(r.fonts) ? r.fonts : [])\n .map((f) =>\n clip(\n String(f ?? '')\n .replace(/[\"'<>;{}]/g, '')\n .trim(),\n 60,\n ),\n )\n .filter(Boolean),\n ),\n ).slice(0, BRAND_MAX_FONTS);\n if (!name && !colors.length) return null;\n const voice = oneLine(r.voice, 120);\n const description = oneLine(r.description, 240);\n return {\n name,\n colors,\n fonts,\n ...(voice ? { voice } : {}),\n ...(description ? { description } : {}),\n ...(typeof r.updatedAt === 'string' ? { updatedAt: r.updatedAt } : {}),\n };\n}\n\n/** \"3 colours · Inter · friendly\" for the home card. */\nexport function brandSummary(kit: BrandKit): string {\n const parts = [`${kit.colors.length} colour${kit.colors.length === 1 ? '' : 's'}`];\n if (kit.fonts.length) parts.push(kit.fonts[0]);\n if (kit.voice) parts.push(kit.voice);\n return parts.join(' · ');\n}\n\n/** The block added to a new build's brief - the web `brandBriefBlock`. */\nexport function brandBriefBlock(kit: BrandKit | null | undefined): string | null {\n if (!kit) return null;\n const lines = ['Brand kit (follow it):'];\n if (kit.name) lines.push(`- Brand name: ${kit.name}`);\n if (kit.description) lines.push(`- About: ${kit.description}`);\n if (kit.colors.length) {\n const [primary, ...rest] = kit.colors;\n lines.push(`- Colours: primary ${primary}${rest.length ? `; also ${rest.join(', ')}` : ''}`);\n }\n if (kit.fonts.length) lines.push(`- Fonts: ${kit.fonts.join(', ')}`);\n if (kit.voice) lines.push(`- Voice: ${kit.voice}`);\n return lines.length > 1 ? lines.join('\\n') : null;\n}\n\n/**\n * The turn text for a studio Start: the brief, then the brand block, then the\n * look-alike site - the web `studioTurnText`.\n */\nexport function studioTurnText(opts: {\n brief: string;\n brandBlock?: string | null;\n referenceUrl?: string | null;\n}): string {\n const brief = opts.brief.trim();\n const parts = [/^build\\b/i.test(brief) ? brief : `Build me: ${brief}`];\n if (opts.brandBlock) parts.push(opts.brandBlock);\n if (opts.referenceUrl) {\n parts.push(`Make it look and feel like ${opts.referenceUrl} (layout, colours, type - not its content).`);\n }\n return parts.join('\\n\\n').slice(0, STUDIO_BRIEF_MAX);\n}\n\n/** The ask a Site score sends the agent: the web card's promise, one score for speed, accessibility and SEO. */\nexport function siteScoreTurn(url: string): string {\n return `Check ${url.trim()} before I share it: score its speed, accessibility and SEO as one number out of 100, then list the fixes that would raise it most.`;\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"studioCatalog.js","sources":["../../../src/features/webbuilder/studioCatalog.ts"],"sourcesContent":["/**\n * The studio home's content and the text a studio Start sends - the native\n * twin of the web surface's `studio/studioCatalog.ts` and `studio/brandKit.ts`\n * (same chips and seeds, same steps, same `Build me:` turn and brand block),\n * so a site started on the phone is briefed exactly like one started on the\n * web. Pure: the screens and jest both use it.\n */\n\nexport type StudioChipIcon = 'landing' | 'store' | 'portfolio' | 'booking' | 'dashboard' | 'blog';\n\nexport interface StudioChip {\n id: string;\n label: string;\n icon: StudioChipIcon;\n /** Brief scaffold put in the hero input; the person finishes the sentence. */\n seed: string;\n}\n\nexport const STUDIO_CHIPS: readonly StudioChip[] = [\n { id: 'landing', label: 'Landing page', icon: 'landing', seed: 'A landing page for ' },\n { id: 'store', label: 'Online store', icon: 'store', seed: 'An online store that sells ' },\n { id: 'portfolio', label: 'Portfolio', icon: 'portfolio', seed: 'A portfolio site for ' },\n { id: 'booking', label: 'Booking app', icon: 'booking', seed: 'A booking app where customers can book ' },\n { id: 'dashboard', label: 'Dashboard', icon: 'dashboard', seed: 'A dashboard that tracks ' },\n { id: 'blog', label: 'Blog', icon: 'blog', seed: 'A blog about ' },\n];\n\n/** The chip glyphs (MaterialCommunityIcons) closest to the web's lucide icons. */\nexport const STUDIO_CHIP_GLYPH: Record<StudioChipIcon, string> = {\n landing: 'page-layout-header',\n store: 'shopping-outline',\n portfolio: 'briefcase-outline',\n booking: 'calendar-check-outline',\n dashboard: 'chart-bar',\n blog: 'newspaper-variant-outline',\n};\n\nexport interface StudioStep {\n n: number;\n title: string;\n detail: string;\n}\n\nexport const STUDIO_STEPS: readonly StudioStep[] = [\n { n: 1, title: 'Describe', detail: 'What it is and who it is for' },\n { n: 2, title: 'Pick a look', detail: 'Your brand kit or a site you like' },\n { n: 3, title: 'Build', detail: 'Ask the agent for any change' },\n { n: 4, title: 'Check & publish', detail: 'Site score, then live in one tap' },\n];\n\nexport const STUDIO_BRIEF_MAX = 4000;\n\n/** True when the input is still only a chip seed (nothing the person typed). */\nexport function isBareSeed(text: string): boolean {\n const t = text.trim();\n if (!t) return true;\n return STUDIO_CHIPS.some((c) => c.seed.trim() === t);\n}\n\n/** A look-alike site must be a full https address. */\nexport function isValidLookUrl(value: string): boolean {\n return /^https:\\/\\/[^\\s/$.?#][^\\s]*\\.[^\\s]{2,}$/i.test(value.trim());\n}\n\n/** \"stripe.com\" from a URL, for chips and card subtitles. */\nexport function hostLabel(url: string | null | undefined): string {\n const match = /^https?:\\/\\/([^/?#]+)/i.exec((url ?? '').trim());\n return match ? match[1].replace(/^www\\./, '') : (url ?? '').trim();\n}\n\n// ── Brand kit ──────────────────────────────────────────────────────────────\n\nexport interface BrandKit {\n name: string;\n /** #rrggbb, primary first. */\n colors: string[];\n fonts: string[];\n /** Tone of voice, e.g. \"friendly, plain-spoken\". */\n voice?: string;\n description?: string;\n updatedAt?: string;\n}\n\nexport const BRAND_MAX_COLORS = 6;\nexport const BRAND_MAX_FONTS = 3;\n\nconst clip = (s: string, n: number) => (s.length > n ? s.slice(0, n) : s);\nconst oneLine = (value: unknown, max: number) =>\n clip(\n String(value ?? '')\n .replace(/\\s+/g, ' ')\n .trim(),\n max,\n );\n\n/** `#abc` / `abc` / `#AABBCC` -> `#aabbcc`; anything else -> null. */\nexport function normalizeHex(raw: unknown): string | null {\n const value = String(raw ?? '')\n .trim()\n .replace(/^#/, '')\n .toLowerCase();\n if (/^[0-9a-f]{6}$/.test(value)) return `#${value}`;\n if (/^[0-9a-f]{3}$/.test(value)) return `#${value.replace(/(.)/g, '$1$1')}`;\n return null;\n}\n\n/** A kit worth keeping (a name or a colour), with every field cleaned - or null. */\nexport function sanitizeBrandKit(raw: unknown): BrandKit | null {\n if (!raw || typeof raw !== 'object') return null;\n const r = raw as Record<string, unknown>;\n const name = oneLine(r.name, 80);\n const colors = Array.from(\n new Set((Array.isArray(r.colors) ? r.colors : []).map(normalizeHex).filter((c): c is string => Boolean(c))),\n ).slice(0, BRAND_MAX_COLORS);\n const fonts = Array.from(\n new Set(\n (Array.isArray(r.fonts) ? r.fonts : [])\n .map((f) =>\n clip(\n String(f ?? '')\n .replace(/[\"'<>;{}]/g, '')\n .trim(),\n 60,\n ),\n )\n .filter(Boolean),\n ),\n ).slice(0, BRAND_MAX_FONTS);\n if (!name && !colors.length) return null;\n const voice = oneLine(r.voice, 120);\n const description = oneLine(r.description, 240);\n return {\n name,\n colors,\n fonts,\n ...(voice ? { voice } : {}),\n ...(description ? { description } : {}),\n ...(typeof r.updatedAt === 'string' ? { updatedAt: r.updatedAt } : {}),\n };\n}\n\n/** \"3 colours · Inter · friendly\" for the home card. */\nexport function brandSummary(kit: BrandKit): string {\n const parts = [`${kit.colors.length} colour${kit.colors.length === 1 ? '' : 's'}`];\n if (kit.fonts.length) parts.push(kit.fonts[0]);\n if (kit.voice) parts.push(kit.voice);\n return parts.join(' · ');\n}\n\n/** The block added to a new build's brief - the web `brandBriefBlock`. */\nexport function brandBriefBlock(kit: BrandKit | null | undefined): string | null {\n if (!kit) return null;\n const lines = ['Brand kit (follow it):'];\n if (kit.name) lines.push(`- Brand name: ${kit.name}`);\n if (kit.description) lines.push(`- About: ${kit.description}`);\n if (kit.colors.length) {\n const [primary, ...rest] = kit.colors;\n lines.push(`- Colours: primary ${primary}${rest.length ? `; also ${rest.join(', ')}` : ''}`);\n }\n if (kit.fonts.length) lines.push(`- Fonts: ${kit.fonts.join(', ')}`);\n if (kit.voice) lines.push(`- Voice: ${kit.voice}`);\n return lines.length > 1 ? lines.join('\\n') : null;\n}\n\n/** The brand kit as a note for the agent (web `brandKitNote`): it rides with the turn, never in the person's bubble. */\nexport function brandKitNote(kit: BrandKit | null | undefined): string | null {\n const block = brandBriefBlock(kit);\n return block ? block.replace(/^Brand kit \\(follow it\\):/, '[Brand kit] Follow it:') : null;\n}\n\n/**\n * The turn text for a studio Start: the brief, then the brand block, then the\n * look-alike site - the web `studioTurnText`.\n */\nexport function studioTurnText(opts: {\n brief: string;\n brandBlock?: string | null;\n referenceUrl?: string | null;\n}): string {\n const brief = opts.brief.trim();\n const parts = [/^build\\b/i.test(brief) ? brief : `Build me: ${brief}`];\n if (opts.brandBlock) parts.push(opts.brandBlock);\n if (opts.referenceUrl) {\n parts.push(`Make it look and feel like ${opts.referenceUrl} (layout, colours, type - not its content).`);\n }\n return parts.join('\\n\\n').slice(0, STUDIO_BRIEF_MAX);\n}\n\n/** The ask a Site score sends the agent: the web card's promise, one score for speed, accessibility and SEO. */\nexport function siteScoreTurn(url: string): string {\n return `Check ${url.trim()} before I share it: score its speed, accessibility and SEO as one number out of 100, then list the fixes that would raise it most.`;\n}\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,140 @@
1
+ const CATEGORY_ALIASES = {
2
+ cafe: "restaurant",
3
+ caf\u00E9: "restaurant",
4
+ restaurants: "restaurant"
5
+ };
6
+ const CATEGORY_SPELLINGS = {
7
+ services: "Services",
8
+ portfolio: "Portfolio",
9
+ "landing page": "Landing Page",
10
+ ecommerce: "Ecommerce",
11
+ blog: "Blog",
12
+ events: "Events",
13
+ music: "Music",
14
+ editorial: "Editorial",
15
+ resume: "Resume",
16
+ "internal tools": "Internal Tools",
17
+ saas: "SaaS",
18
+ dashboards: "Dashboards",
19
+ "business tools": "Business Tools",
20
+ productivity: "Productivity",
21
+ education: "Education",
22
+ finance: "Finance",
23
+ "project management": "Project Management",
24
+ "developer tools": "Developer Tools",
25
+ lifestyle: "Lifestyle",
26
+ "product management": "Product Management",
27
+ presentations: "Presentations",
28
+ "online store": "Ecommerce",
29
+ ai: "AI",
30
+ crm: "CRM"
31
+ };
32
+ const NOT_CATEGORIES = /* @__PURE__ */ new Set(["aiassistant", "ai assistant", "codeassistant", "code assistant"]);
33
+ function categoryKey(raw) {
34
+ var _a;
35
+ const key = (raw != null ? raw : "").trim().toLowerCase().replace(/[-_\s]+/g, " ");
36
+ return (_a = CATEGORY_ALIASES[key]) != null ? _a : key;
37
+ }
38
+ function templateCategoryLabel(raw) {
39
+ var _a;
40
+ const k = categoryKey(raw);
41
+ if (!k || NOT_CATEGORIES.has(k)) return "";
42
+ return (_a = CATEGORY_SPELLINGS[k]) != null ? _a : k[0].toUpperCase() + k.slice(1);
43
+ }
44
+ function knownTemplateCategory(raw) {
45
+ var _a;
46
+ return (_a = CATEGORY_SPELLINGS[categoryKey(raw)]) != null ? _a : "";
47
+ }
48
+ function templateCategories(list) {
49
+ var _a;
50
+ const counts = /* @__PURE__ */ new Map();
51
+ for (const t of list) {
52
+ const c = templateCategoryLabel(t.category);
53
+ if (c) counts.set(c, ((_a = counts.get(c)) != null ? _a : 0) + 1);
54
+ }
55
+ return Array.from(counts.entries()).sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0])).map(([c]) => c);
56
+ }
57
+ function usefulTemplateDescription(t) {
58
+ var _a;
59
+ const d = ((_a = t.description) != null ? _a : "").trim();
60
+ if (!d) return "";
61
+ const norm = (s) => s.toLowerCase().replace(/[^a-z0-9]+/g, " ").trim();
62
+ const nd = norm(d);
63
+ if (/^(yantra )?(starter )?template( by yantra)?$/.test(nd)) return "";
64
+ if (/\be2b preview\b.*\bvia yantra webbuilder$/.test(nd)) return "";
65
+ if (nd === `${norm(t.title)} template by yantra` || nd === `${norm(t.title)} template`) return "";
66
+ return d;
67
+ }
68
+ function templateDisplayName(title) {
69
+ const raw = title.trim();
70
+ let t = /^[a-z0-9]+(?:[-_][a-z0-9]+)+$/i.test(raw) ? raw.split(/[-_]+/).map((w) => w ? w[0].toUpperCase() + w.slice(1) : w).join(" ") : raw;
71
+ t = t.replace(/\s*[-–—|·:]?\s*by\s+yantra$/i, "").replace(/^yantra\s+/i, "").replace(/\s+(template|starter)$/i, "").replace(/\s+v\d+(?:\.\d+)*$/i, "").trim();
72
+ return t || raw;
73
+ }
74
+ function templateName(t) {
75
+ var _a;
76
+ return templateDisplayName(((_a = t.displayName) == null ? void 0 : _a.trim()) || t.title);
77
+ }
78
+ const SEARCH_SYNONYMS = [["store", "shop", "ecommerce", "e-commerce", "retail", "boutique", "products", "sell", "jewelry", "jewellery", "fashion"], ["restaurant", "cafe", "caf\xE9", "food", "menu", "dining", "bakery", "coffee", "bistro", "bar"], ["booking", "book", "appointment", "reservation", "schedule", "salon", "spa", "clinic", "services"], ["portfolio", "agency", "designer", "photographer"], ["blog", "magazine", "news", "editorial", "stories", "journal"], ["saas", "startup", "software", "product", "app"], ["landing", "landing page", "launch", "waitlist"], ["event", "events", "conference", "wedding", "meetup", "tickets"], ["resume", "cv", "personal"], ["dashboard", "dashboards", "admin", "analytics", "internal tools"]];
79
+ function searchWords(word) {
80
+ const w = word.toLowerCase();
81
+ const group = SEARCH_SYNONYMS.find((g) => g.some((s) => s === w || w.length >= 4 && s.startsWith(w)));
82
+ return group ? [w, ...group] : [w];
83
+ }
84
+ const fold = (s) => s.toLowerCase().normalize("NFD").replace(/[̀-ͯ]/g, "").replace(/[-_/]+/g, " ");
85
+ function searchTemplates(list, query) {
86
+ const words = fold(query).split(/\s+/).filter(Boolean);
87
+ if (!words.length) return [...list];
88
+ const scored = [];
89
+ list.forEach((t, i) => {
90
+ var _a;
91
+ const name = fold(`${templateName(t)} ${t.title}`);
92
+ const rest = fold([templateCategoryLabel(t.category), usefulTemplateDescription(t), ...(_a = t.tags) != null ? _a : [], t.id].join(" "));
93
+ const tokens = name.split(" ");
94
+ let score = 0;
95
+ for (const word of words) {
96
+ const alts = searchWords(word).map(fold);
97
+ if (tokens.some((n) => n.startsWith(word))) score += 4;
98
+ else if (name.includes(word)) score += 3;
99
+ else if (alts.some((a) => name.includes(a))) score += 2;
100
+ else if (alts.some((a) => rest.includes(a))) score += 1;
101
+ else return;
102
+ }
103
+ scored.push({
104
+ t,
105
+ score,
106
+ i
107
+ });
108
+ });
109
+ return scored.sort((a, b) => b.score - a.score || a.i - b.i).map((s) => s.t);
110
+ }
111
+ function versionOf(t) {
112
+ const slug = fold(t.id || t.title).replace(/\s+by\s+yantra$/, "").trim();
113
+ const m = /^(.*?)\s+v(\d+)$/.exec(slug);
114
+ return m ? {
115
+ base: m[1].trim(),
116
+ version: Number(m[2])
117
+ } : {
118
+ base: slug,
119
+ version: 1
120
+ };
121
+ }
122
+ function newestTemplateVersions(list) {
123
+ var _a;
124
+ const groups = /* @__PURE__ */ new Map();
125
+ for (const t of list) {
126
+ const {
127
+ base
128
+ } = versionOf(t);
129
+ groups.set(base, [...(_a = groups.get(base)) != null ? _a : [], t]);
130
+ }
131
+ const hidden = /* @__PURE__ */ new Set();
132
+ for (const group of groups.values()) {
133
+ const newest = group.reduce((a, b) => versionOf(b).version > versionOf(a).version ? b : a);
134
+ for (const t of group.length < 2 ? [] : group.filter((x) => x !== newest)) {
135
+ const sameLook = t.cover && t.cover === newest.cover || templateCategoryLabel(t.category) && templateCategoryLabel(t.category) === templateCategoryLabel(newest.category);
136
+ if (sameLook) hidden.add(t);
137
+ }
138
+ }
139
+ return list.filter((t) => !hidden.has(t));
140
+ }export{knownTemplateCategory,newestTemplateVersions,searchTemplates,templateCategories,templateCategoryLabel,templateDisplayName,templateName,usefulTemplateDescription};//# sourceMappingURL=templateCatalog.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"templateCatalog.js","sources":["../../../src/features/webbuilder/templateCatalog.ts"],"sourcesContent":["/**\n * Template catalog helpers for the Templates sheet - the web `studio/templateCatalog.ts`:\n * one chip per category however repos spell it (the familiar shelf names), names and\n * descriptions worth showing, a search over names, categories, descriptions and tags\n * where a word finds its kind of site, and one card per look-alike version. Pure.\n */\nimport type { WebBuilderTemplate } from './workflow';\n\ntype WebBuilderTemplateOption = WebBuilderTemplate;\n\n// Same-meaning labels from template repos collapse into one chip.\nconst CATEGORY_ALIASES: Record<string, string> = { cafe: 'restaurant', café: 'restaurant', restaurants: 'restaurant' };\n// The familiar category names, so people find the shelves they expect.\nconst CATEGORY_SPELLINGS: Record<string, string> = {\n services: 'Services',\n portfolio: 'Portfolio',\n 'landing page': 'Landing Page',\n ecommerce: 'Ecommerce',\n blog: 'Blog',\n events: 'Events',\n music: 'Music',\n editorial: 'Editorial',\n resume: 'Resume',\n 'internal tools': 'Internal Tools',\n saas: 'SaaS',\n dashboards: 'Dashboards',\n 'business tools': 'Business Tools',\n productivity: 'Productivity',\n education: 'Education',\n finance: 'Finance',\n 'project management': 'Project Management',\n 'developer tools': 'Developer Tools',\n lifestyle: 'Lifestyle',\n 'product management': 'Product Management',\n presentations: 'Presentations',\n 'online store': 'Ecommerce',\n ai: 'AI',\n crm: 'CRM',\n};\n\n// Pipeline markers some catalogs put in `category`; they name no kind of site.\nconst NOT_CATEGORIES = new Set(['aiassistant', 'ai assistant', 'codeassistant', 'code assistant']);\n\nfunction categoryKey(raw: string | null | undefined): string {\n const key = (raw ?? '')\n .trim()\n .toLowerCase()\n .replace(/[-_\\s]+/g, ' ');\n return CATEGORY_ALIASES[key] ?? key;\n}\n\n/** One label per category however a repo spelled it: \"restaurant\", \"Restaurant\", \"cafe\" -> \"Restaurant\". */\nexport function templateCategoryLabel(raw: string | null | undefined): string {\n const k = categoryKey(raw);\n if (!k || NOT_CATEGORIES.has(k)) return '';\n return CATEGORY_SPELLINGS[k] ?? k[0].toUpperCase() + k.slice(1);\n}\n\n/** The shelf label for a category, or '' when it is not one of theirs. */\nexport function knownTemplateCategory(raw: string | null | undefined): string {\n return CATEGORY_SPELLINGS[categoryKey(raw)] ?? '';\n}\n\n/** Category chips for a result list: \"All\" first, then the categories in use, by how many use them. */\nexport function templateCategories(list: ReadonlyArray<WebBuilderTemplateOption>): string[] {\n const counts = new Map<string, number>();\n for (const t of list) {\n const c = templateCategoryLabel(t.category);\n if (c) counts.set(c, (counts.get(c) ?? 0) + 1);\n }\n return Array.from(counts.entries())\n .sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0]))\n .map(([c]) => c);\n}\n\n/**\n * A description worth showing on a card: boilerplate like \"Yantra starter template\" or\n * \"<Title> template by Yantra\" says nothing the title does not.\n */\nexport function usefulTemplateDescription(t: Pick<WebBuilderTemplateOption, 'title' | 'description'>): string {\n const d = (t.description ?? '').trim();\n if (!d) return '';\n const norm = (s: string) =>\n s\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, ' ')\n .trim();\n const nd = norm(d);\n if (/^(yantra )?(starter )?template( by yantra)?$/.test(nd)) return '';\n if (/\\be2b preview\\b.*\\bvia yantra webbuilder$/.test(nd)) return '';\n if (nd === `${norm(t.title)} template by yantra` || nd === `${norm(t.title)} template`) return '';\n return d;\n}\n\n/** A name for people: \"Arch Studio V2\" -> \"Arch Studio\", \"Triage by Yantra\" -> \"Triage\", \"pin-post\" -> \"Pin Post\". */\nexport function templateDisplayName(title: string): string {\n const raw = title.trim();\n let t = /^[a-z0-9]+(?:[-_][a-z0-9]+)+$/i.test(raw)\n ? raw\n .split(/[-_]+/)\n .map((w) => (w ? w[0].toUpperCase() + w.slice(1) : w))\n .join(' ')\n : raw;\n t = t\n .replace(/\\s*[-–—|·:]?\\s*by\\s+yantra$/i, '')\n .replace(/^yantra\\s+/i, '')\n .replace(/\\s+(template|starter)$/i, '')\n .replace(/\\s+v\\d+(?:\\.\\d+)*$/i, '')\n .trim();\n return t || raw;\n}\n\n/** The card's name for people: the catalog's display name, else its title (a slug title-cased). */\nexport function templateName(t: Pick<WebBuilderTemplateOption, 'title' | 'displayName'>): string {\n return templateDisplayName(t.displayName?.trim() || t.title);\n}\n\n// What people type for a kind of site, and the words its templates use (names, categories, tags).\nconst SEARCH_SYNONYMS: ReadonlyArray<ReadonlyArray<string>> = [\n [\n 'store',\n 'shop',\n 'ecommerce',\n 'e-commerce',\n 'retail',\n 'boutique',\n 'products',\n 'sell',\n 'jewelry',\n 'jewellery',\n 'fashion',\n ],\n ['restaurant', 'cafe', 'café', 'food', 'menu', 'dining', 'bakery', 'coffee', 'bistro', 'bar'],\n ['booking', 'book', 'appointment', 'reservation', 'schedule', 'salon', 'spa', 'clinic', 'services'],\n ['portfolio', 'agency', 'designer', 'photographer'],\n ['blog', 'magazine', 'news', 'editorial', 'stories', 'journal'],\n ['saas', 'startup', 'software', 'product', 'app'],\n ['landing', 'landing page', 'launch', 'waitlist'],\n ['event', 'events', 'conference', 'wedding', 'meetup', 'tickets'],\n ['resume', 'cv', 'personal'],\n ['dashboard', 'dashboards', 'admin', 'analytics', 'internal tools'],\n];\n\nfunction searchWords(word: string): string[] {\n const w = word.toLowerCase();\n const group = SEARCH_SYNONYMS.find((g) => g.some((s) => s === w || (w.length >= 4 && s.startsWith(w))));\n return group ? [w, ...group] : [w];\n}\n\nconst fold = (s: string) =>\n s\n .toLowerCase()\n .normalize('NFD')\n .replace(/[̀-ͯ]/g, '')\n .replace(/[-_/]+/g, ' ');\n\n/**\n * Templates matching a search, best first: every word must match the name, category, description, tags or id\n * (a word also matches its kind: \"store\" finds a jewellery shop). Empty query: the list as it is.\n */\nexport function searchTemplates<T extends WebBuilderTemplateOption>(list: ReadonlyArray<T>, query: string): T[] {\n const words = fold(query).split(/\\s+/).filter(Boolean);\n if (!words.length) return [...list];\n const scored: Array<{ t: T; score: number; i: number }> = [];\n list.forEach((t, i) => {\n const name = fold(`${templateName(t)} ${t.title}`);\n const rest = fold(\n [templateCategoryLabel(t.category), usefulTemplateDescription(t), ...(t.tags ?? []), t.id].join(' '),\n );\n const tokens = name.split(' ');\n let score = 0;\n for (const word of words) {\n const alts = searchWords(word).map(fold);\n if (tokens.some((n) => n.startsWith(word))) score += 4;\n else if (name.includes(word)) score += 3;\n else if (alts.some((a) => name.includes(a))) score += 2;\n else if (alts.some((a) => rest.includes(a))) score += 1;\n else return;\n }\n scored.push({ t, score, i });\n });\n return scored.sort((a, b) => b.score - a.score || a.i - b.i).map((s) => s.t);\n}\n\n/** \"arch-studio-v2\" -> { base: \"arch studio\", version: 2 }; no suffix is version 1. */\nfunction versionOf(t: Pick<WebBuilderTemplateOption, 'id' | 'title'>): { base: string; version: number } {\n const slug = fold(t.id || t.title)\n .replace(/\\s+by\\s+yantra$/, '')\n .trim();\n const m = /^(.*?)\\s+v(\\d+)$/.exec(slug);\n return m ? { base: m[1].trim(), version: Number(m[2]) } : { base: slug, version: 1 };\n}\n\n/**\n * One card per template: when \"x\" and \"x-v2\" both exist and look the same (same cover or category), only the\n * newest version is listed. Versions that look different stay.\n */\nexport function newestTemplateVersions<T extends WebBuilderTemplateOption>(list: ReadonlyArray<T>): T[] {\n const groups = new Map<string, T[]>();\n for (const t of list) {\n const { base } = versionOf(t);\n groups.set(base, [...(groups.get(base) ?? []), t]);\n }\n const hidden = new Set<T>();\n for (const group of groups.values()) {\n const newest = group.reduce((a, b) => (versionOf(b).version > versionOf(a).version ? b : a));\n for (const t of group.length < 2 ? [] : group.filter((x) => x !== newest)) {\n const sameLook =\n (t.cover && t.cover === newest.cover) ||\n (templateCategoryLabel(t.category) &&\n templateCategoryLabel(t.category) === templateCategoryLabel(newest.category));\n if (sameLook) hidden.add(t);\n }\n }\n return list.filter((t) => !hidden.has(t));\n}\n"],"names":[],"mappings":"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"}
@@ -36,13 +36,39 @@ function webbuilderAgentSuggestions(hasBuild, siteTitle) {
36
36
  prompt: "Check the site for errors and fix anything broken."
37
37
  }];
38
38
  }
39
- function wakePreviewPrompt() {
40
- return "Open a live preview of my latest build.";
41
- }
42
39
  function agentEditNote(build) {
43
40
  const preview = build.previewUrl ? withPublicSandboxHosts(build.previewUrl, publicSandboxDomain()) : "";
44
41
  return `[Open build] The user is looking at the live preview of "${build.title || "this site"}" that you built${build.sandboxId ? ` (sandbox ${build.sandboxId}${preview ? `, ${preview}` : ""})` : ""}. Apply their request to that site in that sandbox - do not start a new one - and reply with the preview link.${build.snapshotId ? ` If that sandbox is gone, boot from its latest restore point (snapshotId ${build.snapshotId}).` : ""}`;
45
42
  }
43
+ const PUBLISH_MESSAGE_RE = /^Publish "[^\n]+" as ([a-z0-9][a-z0-9-]*)(?: on ([^\s]+?))?\.(?: I reviewed the critical problems[^\n]*)?$/;
44
+ function publishEventText(message) {
45
+ const m = String(message != null ? message : "").trim().match(PUBLISH_MESSAGE_RE);
46
+ if (!m) return null;
47
+ return m[2] ? `Publishing ${m[1]} on ${m[2]}\u2026` : `Publishing ${m[1]}\u2026`;
48
+ }
49
+ const RESTORE_POINT_LINE_RE = /^[ \t>*_-]*saved a restore point\b[^\n]*(?:\n|$)/gim;
50
+ function withoutRestorePointLine(text) {
51
+ const raw = String(text != null ? text : "");
52
+ const stripped = raw.replace(RESTORE_POINT_LINE_RE, "");
53
+ if (stripped === raw) return {
54
+ text: raw,
55
+ saved: false
56
+ };
57
+ return {
58
+ text: stripped.replace(/\n{3,}/g, "\n\n").trim(),
59
+ saved: true
60
+ };
61
+ }
46
62
  function agentPublishMessage(build, projectName, customDomain) {
47
- return `Publish "${build.title || "this site"}" as ${projectName}${""}.`;
48
- }export{CHAT_STARTERS,agentEditNote,agentPublishMessage,wakePreviewPrompt,webbuilderAgentSuggestions};//# sourceMappingURL=webbuilderPrompts.js.map
63
+ return `Publish "${build.title || "this site"}" as ${projectName}${customDomain ? ` on ${customDomain}` : ""}.`;
64
+ }
65
+ function restoreTurnNote(b) {
66
+ var _a, _b, _c;
67
+ const clip = (t, n) => t.length > n ? `${t.slice(0, n - 1)}\u2026` : t;
68
+ const snapshot = String((_a = b.snapshotId) != null ? _a : "").trim();
69
+ const reason = clip(String((_b = b.restoreError) != null ? _b : "").replace(/\s+/g, " ").trim(), 200);
70
+ const why = `[Restore] This site's sandbox was cleaned up${reason ? ` and the app could not restore it (${reason})` : ""}.`;
71
+ const builder = /next/i.test(String((_c = b.templateId) != null ? _c : "")) ? "next" : "vite";
72
+ const how = snapshot ? ` Boot its restore point with WEBBUILDER_CREATE_SANDBOX {"snapshotId":"${snapshot}","builder":"${builder}","private":true}, start its dev server and send the preview link.` : " Restore it from its saved code (GitHub backup) in a new sandbox, start its dev server and send the preview link.";
73
+ return why + how;
74
+ }export{CHAT_STARTERS,agentEditNote,agentPublishMessage,publishEventText,restoreTurnNote,webbuilderAgentSuggestions,withoutRestorePointLine};//# sourceMappingURL=webbuilderPrompts.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"webbuilderPrompts.js","sources":["../../../src/features/webbuilder/webbuilderPrompts.ts"],"sourcesContent":["/**\n * Follow-ups for the Web Builder agent: the first asks of a site with nothing\n * built yet, and the edits people make most after a first build.\n */\nimport type { AgentChatSuggestion } from '../agent-chat';\nimport { publicSandboxDomain } from './publicSandboxDomain';\nimport { withPublicSandboxHosts } from './sandboxPublicHost';\n\n/**\n * Starter prompts in an empty chat (web `CHAT_STARTERS`, Lovable): complete briefs\n * that build on one tap - specific enough that the agent asks nothing.\n */\nexport const CHAT_STARTERS: readonly AgentChatSuggestion[] = [\n {\n id: 'cafe',\n label: 'Café website with menu',\n prompt: 'Build a website for a neighbourhood specialty coffee shop: hero, menu with prices, opening hours, location map and an Instagram-style gallery. Warm, modern look.',\n },\n {\n id: 'saas',\n label: 'SaaS landing page',\n prompt: 'Build a landing page for a project-management SaaS: hero with product screenshot, features, pricing (3 tiers), testimonials, FAQ and a sign-up call to action.',\n },\n {\n id: 'portfolio',\n label: 'Photographer portfolio',\n prompt: 'Build a portfolio for a wedding photographer: full-bleed gallery, about, packages with prices, and a contact form for booking enquiries. Elegant, minimal.',\n },\n {\n id: 'booking',\n label: 'Booking app with database',\n prompt: 'Build a booking app for a yoga studio: class schedule, book a spot (saved in a database), and an admin page listing bookings.',\n },\n];\n\nexport function webbuilderAgentSuggestions(hasBuild: boolean, siteTitle: string | null): AgentChatSuggestion[] {\n if (!hasBuild) return [...CHAT_STARTERS];\n const name = siteTitle ? ` on ${siteTitle}` : '';\n return [\n { id: 'mobile', label: 'Make it great on phones', prompt: `Make every page${name} look great on phones.` },\n { id: 'colors', label: 'New color scheme', prompt: 'Give the site a fresh, modern color scheme.' },\n { id: 'section', label: 'Add a section', prompt: 'Add a testimonials section above the footer.' },\n { id: 'fix', label: 'Fix what is broken', prompt: 'Check the site for errors and fix anything broken.' },\n ];\n}\n\ntype BuildRef = {\n title?: string;\n previewUrl: string | null;\n sandboxId: string | null;\n snapshotId?: string | null;\n};\n\n/**\n * Sent when the preview does not answer and the app could not bring it back\n * itself: the sandbox slept or was reclaimed (E2B sandboxes live about an hour\n * after the last change). The words are the web's (#428); the host's\n * `[Open build]` note names this build's sandbox and restore point, so the agent\n * reopens THIS site, booting the snapshot when the sandbox is gone.\n */\nexport function wakePreviewPrompt(): string {\n return 'Open a live preview of my latest build.';\n}\n\n/**\n * The note a change to an agent-chat build carries (web `agentEditForChat`): the\n * site lives in the agent's sandbox, so the change goes to that sandbox - never a\n * new one - with the restore point for when it expired. The note is kept in the\n * chat, so its preview link is on the platform's sandbox domain when one is set.\n */\nexport function agentEditNote(build: BuildRef): string {\n const preview = build.previewUrl ? withPublicSandboxHosts(build.previewUrl, publicSandboxDomain()) : '';\n return `[Open build] The user is looking at the live preview of \"${build.title || 'this site'}\" that you built${\n build.sandboxId ? ` (sandbox ${build.sandboxId}${preview ? `, ${preview}` : ''})` : ''\n }. Apply their request to that site in that sandbox - do not start a new one - and reply with the preview link.${\n build.snapshotId\n ? ` If that sandbox is gone, boot from its latest restore point (snapshotId ${build.snapshotId}).`\n : ''\n }`;\n}\n\n/** What Publish asks the agent for an agent-chat build (web `agentPublishMessage`). */\nexport function agentPublishMessage(build: BuildRef, projectName: string, customDomain?: string): string {\n return `Publish \"${build.title || 'this site'}\" as ${projectName}${customDomain ? ` on ${customDomain}` : ''}.`;\n}\n"],"names":[],"mappings":"uHAYO,MAAM,gBAAgD,CAAC;AAAA,EAC5D,EAAI,EAAA,MAAA;AAAA,EACJ,KAAO,EAAA,2BAAA;AAAA,EACP,MAAQ,EAAA;AACV,CAAG,EAAA;AAAA,EACD,EAAI,EAAA,MAAA;AAAA,EACJ,KAAO,EAAA,mBAAA;AAAA,EACP,MAAQ,EAAA;AACV,CAAG,EAAA;AAAA,EACD,EAAI,EAAA,WAAA;AAAA,EACJ,KAAO,EAAA,wBAAA;AAAA,EACP,MAAQ,EAAA;AACV,CAAG,EAAA;AAAA,EACD,EAAI,EAAA,SAAA;AAAA,EACJ,KAAO,EAAA,2BAAA;AAAA,EACP,MAAQ,EAAA;AACV,CAAC;AACe,SAAA,0BAAA,CAA2B,UAAmB,SAAiD,EAAA;AAC7G,EAAA,IAAI,CAAC,QAAA,EAAiB,OAAA,CAAC,GAAG,aAAa,CAAA;AACvC,EAAA,MAAM,IAAO,GAAA,SAAA,GAAY,CAAO,IAAA,EAAA,SAAS,CAAK,CAAA,GAAA,EAAA;AAC9C,EAAA,OAAO,CAAC;AAAA,IACN,EAAI,EAAA,QAAA;AAAA,IACJ,KAAO,EAAA,yBAAA;AAAA,IACP,MAAA,EAAQ,kBAAkB,IAAI,CAAA,sBAAA;AAAA,GAC7B,EAAA;AAAA,IACD,EAAI,EAAA,QAAA;AAAA,IACJ,KAAO,EAAA,kBAAA;AAAA,IACP,MAAQ,EAAA;AAAA,GACP,EAAA;AAAA,IACD,EAAI,EAAA,SAAA;AAAA,IACJ,KAAO,EAAA,eAAA;AAAA,IACP,MAAQ,EAAA;AAAA,GACP,EAAA;AAAA,IACD,EAAI,EAAA,KAAA;AAAA,IACJ,KAAO,EAAA,oBAAA;AAAA,IACP,MAAQ,EAAA;AAAA,GACT,CAAA;AACH;AAeO,SAAS,iBAA4B,GAAA;AAC1C,EAAO,OAAA,yCAAA;AACT;AAQO,SAAS,cAAc,KAAyB,EAAA;AACrD,EAAM,MAAA,OAAA,GAAU,MAAM,UAAa,GAAA,sBAAA,CAAuB,MAAM,UAAY,EAAA,mBAAA,EAAqB,CAAI,GAAA,EAAA;AACrG,EAAO,OAAA,CAAA,yDAAA,EAA4D,KAAM,CAAA,KAAA,IAAS,WAAW,CAAA,gBAAA,EAAmB,MAAM,SAAY,GAAA,CAAA,UAAA,EAAa,KAAM,CAAA,SAAS,CAAG,EAAA,OAAA,GAAU,KAAK,OAAO,CAAA,CAAA,GAAK,EAAE,CAAA,CAAA,CAAA,GAAM,EAAE,CAAA,8GAAA,EAAiH,KAAM,CAAA,UAAA,GAAa,CAA4E,yEAAA,EAAA,KAAA,CAAM,UAAU,CAAA,EAAA,CAAA,GAAO,EAAE,CAAA,CAAA;AACjb;AAGgB,SAAA,mBAAA,CAAoB,KAAiB,EAAA,WAAA,EAAqB,YAA+B,EAAA;AACvG,EAAO,OAAA,CAAA,SAAA,EAAY,KAAM,CAAA,KAAA,IAAS,WAAW,CAAA,KAAA,EAAQ,WAAW,CAAA,EAA0C,EAAE,CAAA,CAAA,CAAA;AAC9G"}
1
+ {"version":3,"file":"webbuilderPrompts.js","sources":["../../../src/features/webbuilder/webbuilderPrompts.ts"],"sourcesContent":["/**\n * Follow-ups for the Web Builder agent: the first asks of a site with nothing\n * built yet, and the edits people make most after a first build.\n */\nimport type { AgentChatSuggestion } from '../agent-chat';\nimport { publicSandboxDomain } from './publicSandboxDomain';\nimport { withPublicSandboxHosts } from './sandboxPublicHost';\n\n/**\n * Starter prompts in an empty chat (web `CHAT_STARTERS`): complete briefs\n * that build on one tap - specific enough that the agent asks nothing.\n */\nexport const CHAT_STARTERS: readonly AgentChatSuggestion[] = [\n {\n id: 'cafe',\n label: 'Café website with menu',\n prompt: 'Build a website for a neighbourhood specialty coffee shop: hero, menu with prices, opening hours, location map and an Instagram-style gallery. Warm, modern look.',\n },\n {\n id: 'saas',\n label: 'SaaS landing page',\n prompt: 'Build a landing page for a project-management SaaS: hero with product screenshot, features, pricing (3 tiers), testimonials, FAQ and a sign-up call to action.',\n },\n {\n id: 'portfolio',\n label: 'Photographer portfolio',\n prompt: 'Build a portfolio for a wedding photographer: full-bleed gallery, about, packages with prices, and a contact form for booking enquiries. Elegant, minimal.',\n },\n {\n id: 'booking',\n label: 'Booking app with database',\n prompt: 'Build a booking app for a yoga studio: class schedule, book a spot (saved in a database), and an admin page listing bookings.',\n },\n];\n\nexport function webbuilderAgentSuggestions(hasBuild: boolean, siteTitle: string | null): AgentChatSuggestion[] {\n if (!hasBuild) return [...CHAT_STARTERS];\n const name = siteTitle ? ` on ${siteTitle}` : '';\n return [\n { id: 'mobile', label: 'Make it great on phones', prompt: `Make every page${name} look great on phones.` },\n { id: 'colors', label: 'New color scheme', prompt: 'Give the site a fresh, modern color scheme.' },\n { id: 'section', label: 'Add a section', prompt: 'Add a testimonials section above the footer.' },\n { id: 'fix', label: 'Fix what is broken', prompt: 'Check the site for errors and fix anything broken.' },\n ];\n}\n\ntype BuildRef = {\n title?: string;\n previewUrl: string | null;\n sandboxId: string | null;\n snapshotId?: string | null;\n};\n\n/**\n * Sent when the preview does not answer and the app could not bring it back\n * itself: the sandbox slept or was reclaimed (E2B sandboxes live about an hour\n * after the last change). The words are the web's (#428); the host's\n * `[Open build]` note names this build's sandbox and restore point, so the agent\n * reopens THIS site, booting the snapshot when the sandbox is gone.\n */\nexport function wakePreviewPrompt(): string {\n return 'Open a live preview of my latest build.';\n}\n\n/**\n * The note a change to an agent-chat build carries (web `agentEditForChat`): the\n * site lives in the agent's sandbox, so the change goes to that sandbox - never a\n * new one - with the restore point for when it expired. The note is kept in the\n * chat, so its preview link is on the platform's sandbox domain when one is set.\n */\nexport function agentEditNote(build: BuildRef): string {\n const preview = build.previewUrl ? withPublicSandboxHosts(build.previewUrl, publicSandboxDomain()) : '';\n return `[Open build] The user is looking at the live preview of \"${build.title || 'this site'}\" that you built${\n build.sandboxId ? ` (sandbox ${build.sandboxId}${preview ? `, ${preview}` : ''})` : ''\n }. Apply their request to that site in that sandbox - do not start a new one - and reply with the preview link.${\n build.snapshotId\n ? ` If that sandbox is gone, boot from its latest restore point (snapshotId ${build.snapshotId}).`\n : ''\n }`;\n}\n\nconst PUBLISH_MESSAGE_RE =\n /^Publish \"[^\\n]+\" as ([a-z0-9][a-z0-9-]*)(?: on ([^\\s]+?))?\\.(?: I reviewed the critical problems[^\\n]*)?$/;\n\n/** A Publish turn shown as an event (\"Publishing lotus-flow…\"), not as something the person typed (web `publishEventText`). */\nexport function publishEventText(message: string): string | null {\n const m = String(message ?? '')\n .trim()\n .match(PUBLISH_MESSAGE_RE);\n if (!m) return null;\n return m[2] ? `Publishing ${m[1]} on ${m[2]}…` : `Publishing ${m[1]}…`;\n}\n\nconst RESTORE_POINT_LINE_RE = /^[ \\t>*_-]*saved a restore point\\b[^\\n]*(?:\\n|$)/gim;\n\n/** The reply without its \"Saved a restore point.\" line (Versions lists them); `saved` says it had one (web). */\nexport function withoutRestorePointLine(text: string): { text: string; saved: boolean } {\n const raw = String(text ?? '');\n const stripped = raw.replace(RESTORE_POINT_LINE_RE, '');\n if (stripped === raw) return { text: raw, saved: false };\n return { text: stripped.replace(/\\n{3,}/g, '\\n\\n').trim(), saved: true };\n}\n\n/** What Publish asks the agent for an agent-chat build (web `agentPublishMessage`). */\nexport function agentPublishMessage(build: BuildRef, projectName: string, customDomain?: string): string {\n return `Publish \"${build.title || 'this site'}\" as ${projectName}${customDomain ? ` on ${customDomain}` : ''}.`;\n}\n\n/**\n * The hidden note for \"Ask the AI to restore it\" (web `restoreTurnNote`): the saved copy to boot\n * and why the app could not. It rides with the turn; the bubble shows only the ask.\n */\nexport function restoreTurnNote(b: {\n snapshotId?: string | null;\n templateId?: string | null;\n restoreError?: string | null;\n}): string {\n const clip = (t: string, n: number) => (t.length > n ? `${t.slice(0, n - 1)}…` : t);\n const snapshot = String(b.snapshotId ?? '').trim();\n const reason = clip(\n String(b.restoreError ?? '')\n .replace(/\\s+/g, ' ')\n .trim(),\n 200,\n );\n const why = `[Restore] This site's sandbox was cleaned up${reason ? ` and the app could not restore it (${reason})` : ''}.`;\n const builder = /next/i.test(String(b.templateId ?? '')) ? 'next' : 'vite';\n const how = snapshot\n ? ` Boot its restore point with WEBBUILDER_CREATE_SANDBOX {\"snapshotId\":\"${snapshot}\",\"builder\":\"${builder}\",\"private\":true}, start its dev server and send the preview link.`\n : ' Restore it from its saved code (GitHub backup) in a new sandbox, start its dev server and send the preview link.';\n return why + how;\n}\n"],"names":[],"mappings":"uHAYO,MAAM,gBAAgD,CAAC;AAAA,EAC5D,EAAI,EAAA,MAAA;AAAA,EACJ,KAAO,EAAA,2BAAA;AAAA,EACP,MAAQ,EAAA;AACV,CAAG,EAAA;AAAA,EACD,EAAI,EAAA,MAAA;AAAA,EACJ,KAAO,EAAA,mBAAA;AAAA,EACP,MAAQ,EAAA;AACV,CAAG,EAAA;AAAA,EACD,EAAI,EAAA,WAAA;AAAA,EACJ,KAAO,EAAA,wBAAA;AAAA,EACP,MAAQ,EAAA;AACV,CAAG,EAAA;AAAA,EACD,EAAI,EAAA,SAAA;AAAA,EACJ,KAAO,EAAA,2BAAA;AAAA,EACP,MAAQ,EAAA;AACV,CAAC;AACe,SAAA,0BAAA,CAA2B,UAAmB,SAAiD,EAAA;AAC7G,EAAA,IAAI,CAAC,QAAA,EAAiB,OAAA,CAAC,GAAG,aAAa,CAAA;AACvC,EAAA,MAAM,IAAO,GAAA,SAAA,GAAY,CAAO,IAAA,EAAA,SAAS,CAAK,CAAA,GAAA,EAAA;AAC9C,EAAA,OAAO,CAAC;AAAA,IACN,EAAI,EAAA,QAAA;AAAA,IACJ,KAAO,EAAA,yBAAA;AAAA,IACP,MAAA,EAAQ,kBAAkB,IAAI,CAAA,sBAAA;AAAA,GAC7B,EAAA;AAAA,IACD,EAAI,EAAA,QAAA;AAAA,IACJ,KAAO,EAAA,kBAAA;AAAA,IACP,MAAQ,EAAA;AAAA,GACP,EAAA;AAAA,IACD,EAAI,EAAA,SAAA;AAAA,IACJ,KAAO,EAAA,eAAA;AAAA,IACP,MAAQ,EAAA;AAAA,GACP,EAAA;AAAA,IACD,EAAI,EAAA,KAAA;AAAA,IACJ,KAAO,EAAA,oBAAA;AAAA,IACP,MAAQ,EAAA;AAAA,GACT,CAAA;AACH;AAyBO,SAAS,cAAc,KAAyB,EAAA;AACrD,EAAM,MAAA,OAAA,GAAU,MAAM,UAAa,GAAA,sBAAA,CAAuB,MAAM,UAAY,EAAA,mBAAA,EAAqB,CAAI,GAAA,EAAA;AACrG,EAAO,OAAA,CAAA,yDAAA,EAA4D,KAAM,CAAA,KAAA,IAAS,WAAW,CAAA,gBAAA,EAAmB,MAAM,SAAY,GAAA,CAAA,UAAA,EAAa,KAAM,CAAA,SAAS,CAAG,EAAA,OAAA,GAAU,KAAK,OAAO,CAAA,CAAA,GAAK,EAAE,CAAA,CAAA,CAAA,GAAM,EAAE,CAAA,8GAAA,EAAiH,KAAM,CAAA,UAAA,GAAa,CAA4E,yEAAA,EAAA,KAAA,CAAM,UAAU,CAAA,EAAA,CAAA,GAAO,EAAE,CAAA,CAAA;AACjb;AACA,MAAM,kBAAqB,GAAA,4GAAA;AAGpB,SAAS,iBAAiB,OAAgC,EAAA;AAC/D,EAAM,MAAA,CAAA,GAAI,OAAO,OAAW,IAAA,IAAA,GAAA,OAAA,GAAA,EAAE,EAAE,IAAK,EAAA,CAAE,MAAM,kBAAkB,CAAA;AAC/D,EAAI,IAAA,CAAC,GAAU,OAAA,IAAA;AACf,EAAA,OAAO,CAAE,CAAA,CAAC,CAAI,GAAA,CAAA,WAAA,EAAc,EAAE,CAAC,CAAC,CAAO,IAAA,EAAA,CAAA,CAAE,CAAC,CAAC,CAAA,MAAA,CAAA,GAAM,CAAc,WAAA,EAAA,CAAA,CAAE,CAAC,CAAC,CAAA,MAAA,CAAA;AACrE;AACA,MAAM,qBAAwB,GAAA,qDAAA;AAGvB,SAAS,wBAAwB,IAGtC,EAAA;AACA,EAAM,MAAA,GAAA,GAAM,MAAO,CAAA,IAAA,IAAA,IAAA,GAAA,IAAA,GAAQ,EAAE,CAAA;AAC7B,EAAA,MAAM,QAAW,GAAA,GAAA,CAAI,OAAQ,CAAA,qBAAA,EAAuB,EAAE,CAAA;AACtD,EAAI,IAAA,QAAA,KAAa,KAAY,OAAA;AAAA,IAC3B,IAAM,EAAA,GAAA;AAAA,IACN,KAAO,EAAA;AAAA,GACT;AACA,EAAO,OAAA;AAAA,IACL,MAAM,QAAS,CAAA,OAAA,CAAQ,SAAW,EAAA,MAAM,EAAE,IAAK,EAAA;AAAA,IAC/C,KAAO,EAAA;AAAA,GACT;AACF;AAGgB,SAAA,mBAAA,CAAoB,KAAiB,EAAA,WAAA,EAAqB,YAA+B,EAAA;AACvG,EAAO,OAAA,CAAA,SAAA,EAAY,KAAM,CAAA,KAAA,IAAS,WAAW,CAAA,KAAA,EAAQ,WAAW,CAAA,EAAG,YAAe,GAAA,CAAA,IAAA,EAAO,YAAY,CAAA,CAAA,GAAK,EAAE,CAAA,CAAA,CAAA;AAC9G;AAMO,SAAS,gBAAgB,CAIrB,EAAA;AAtHX,EAAA,IAAA,EAAA,EAAA,EAAA,EAAA,EAAA;AAuHE,EAAA,MAAM,IAAO,GAAA,CAAC,CAAW,EAAA,CAAA,KAAc,EAAE,MAAS,GAAA,CAAA,GAAI,CAAG,EAAA,CAAA,CAAE,KAAM,CAAA,CAAA,EAAG,CAAI,GAAA,CAAC,CAAC,CAAM,MAAA,CAAA,GAAA,CAAA;AAChF,EAAA,MAAM,WAAW,MAAO,CAAA,CAAA,EAAA,GAAA,CAAA,CAAE,eAAF,IAAgB,GAAA,EAAA,GAAA,EAAE,EAAE,IAAK,EAAA;AACjD,EAAA,MAAM,MAAS,GAAA,IAAA,CAAK,MAAO,CAAA,CAAA,EAAA,GAAA,CAAA,CAAE,iBAAF,IAAkB,GAAA,EAAA,GAAA,EAAE,CAAE,CAAA,OAAA,CAAQ,MAAQ,EAAA,GAAG,CAAE,CAAA,IAAA,IAAQ,GAAG,CAAA;AACjF,EAAA,MAAM,MAAM,CAA+C,4CAAA,EAAA,MAAA,GAAS,CAAsC,mCAAA,EAAA,MAAM,MAAM,EAAE,CAAA,CAAA,CAAA;AACxH,EAAM,MAAA,OAAA,GAAU,OAAQ,CAAA,IAAA,CAAK,MAAO,CAAA,CAAA,EAAA,GAAA,CAAA,CAAE,eAAF,IAAgB,GAAA,EAAA,GAAA,EAAE,CAAC,CAAA,GAAI,MAAS,GAAA,MAAA;AACpE,EAAA,MAAM,MAAM,QAAW,GAAA,CAAA,sEAAA,EAAyE,QAAQ,CAAA,aAAA,EAAgB,OAAO,CAAuE,kEAAA,CAAA,GAAA,mHAAA;AACtM,EAAA,OAAO,GAAM,GAAA,GAAA;AACf"}
@@ -60,25 +60,29 @@ function isCardPack(info) {
60
60
  const FALLBACK_TEMPLATES = [{
61
61
  id: "assetwise",
62
62
  title: "Assetwise",
63
- description: "Yantra starter template",
63
+ description: "Track company equipment: who has it, where it is and when it is due back",
64
+ category: "Internal Tools",
64
65
  templateName: "vite-react",
65
66
  formTemplateId: "assetwise"
66
67
  }, {
67
68
  id: "arch-studio-v2-by-yantra",
68
69
  title: "Arch Studio V2",
69
- description: "Arch Studio v2 template by Yantra",
70
+ description: "A calm, minimal site for an architecture and design studio",
71
+ category: "Portfolio",
70
72
  templateName: "vite-react",
71
73
  formTemplateId: "arch-studio-v2-by-yantra"
72
74
  }, {
73
75
  id: "pinpost",
74
76
  title: "Pinpost",
75
- description: "Yantra starter template",
77
+ description: "A shared pin board for teams, with comments and reactions",
78
+ category: "SaaS",
76
79
  templateName: "vite-react",
77
80
  formTemplateId: "pinpost"
78
81
  }, {
79
82
  id: "triage-by-yantra",
80
83
  title: "Triage by Yantra",
81
- description: "Yantra starter template",
84
+ description: "Report, sort and fix bugs with your team",
85
+ category: "Developer Tools",
82
86
  templateName: "vite-react",
83
87
  formTemplateId: "triage-by-yantra"
84
88
  }];