@adminide-stack/yantra-mobile 12.0.108-alpha.0 → 12.0.109-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 (83) hide show
  1. package/lib/compute.js +57 -1
  2. package/lib/compute.js.map +1 -1
  3. package/lib/config/env-config.js +8 -0
  4. package/lib/config/env-config.js.map +1 -1
  5. package/lib/features/apps/AppsCatalog.js +10 -0
  6. package/lib/features/apps/AppsCatalog.js.map +1 -1
  7. package/lib/features/apps/appArt.js +2 -1
  8. package/lib/features/apps/appArt.js.map +1 -1
  9. package/lib/features/apps/mobileAppCatalog.js +11 -0
  10. package/lib/features/apps/mobileAppCatalog.js.map +1 -1
  11. package/lib/features/apps/nativeAppHandoff.js +2 -1
  12. package/lib/features/apps/nativeAppHandoff.js.map +1 -1
  13. package/lib/features/health/healthSyncStore.js +7 -1
  14. package/lib/features/health/healthSyncStore.js.map +1 -1
  15. package/lib/features/messaging/NewGroupScreen.js.map +1 -1
  16. package/lib/features/messaging/useChatViewer.js +3 -2
  17. package/lib/features/messaging/useChatViewer.js.map +1 -1
  18. package/lib/features/webbuilder/WebBuilderExperience.js +474 -0
  19. package/lib/features/webbuilder/WebBuilderExperience.js.map +1 -0
  20. package/lib/features/webbuilder/brandKitStorage.js +69 -0
  21. package/lib/features/webbuilder/brandKitStorage.js.map +1 -0
  22. package/lib/features/webbuilder/buildFragments.js +360 -0
  23. package/lib/features/webbuilder/buildFragments.js.map +1 -0
  24. package/lib/features/webbuilder/components/BottomTabs.js +65 -0
  25. package/lib/features/webbuilder/components/BottomTabs.js.map +1 -0
  26. package/lib/features/webbuilder/components/BrandKitSheet.js +197 -0
  27. package/lib/features/webbuilder/components/BrandKitSheet.js.map +1 -0
  28. package/lib/features/webbuilder/components/CodeView.js +99 -0
  29. package/lib/features/webbuilder/components/CodeView.js.map +1 -0
  30. package/lib/features/webbuilder/components/Composer.js +217 -0
  31. package/lib/features/webbuilder/components/Composer.js.map +1 -0
  32. package/lib/features/webbuilder/components/PreviewFrame.js +72 -0
  33. package/lib/features/webbuilder/components/PreviewFrame.js.map +1 -0
  34. package/lib/features/webbuilder/components/PreviewPlaceholder.js +107 -0
  35. package/lib/features/webbuilder/components/PreviewPlaceholder.js.map +1 -0
  36. package/lib/features/webbuilder/components/PreviewScreen.js +200 -0
  37. package/lib/features/webbuilder/components/PreviewScreen.js.map +1 -0
  38. package/lib/features/webbuilder/components/ProjectChat.js +580 -0
  39. package/lib/features/webbuilder/components/ProjectChat.js.map +1 -0
  40. package/lib/features/webbuilder/components/ProjectsScreen.js +339 -0
  41. package/lib/features/webbuilder/components/ProjectsScreen.js.map +1 -0
  42. package/lib/features/webbuilder/components/PublishSheet.js +143 -0
  43. package/lib/features/webbuilder/components/PublishSheet.js.map +1 -0
  44. package/lib/features/webbuilder/components/SiteScoreSheet.js +73 -0
  45. package/lib/features/webbuilder/components/SiteScoreSheet.js.map +1 -0
  46. package/lib/features/webbuilder/components/StudioHome.js +367 -0
  47. package/lib/features/webbuilder/components/StudioHome.js.map +1 -0
  48. package/lib/features/webbuilder/components/TemplateCard.js +73 -0
  49. package/lib/features/webbuilder/components/TemplateCard.js.map +1 -0
  50. package/lib/features/webbuilder/components/TemplatesSheet.js +178 -0
  51. package/lib/features/webbuilder/components/TemplatesSheet.js.map +1 -0
  52. package/lib/features/webbuilder/components/VersionsSheet.js +57 -0
  53. package/lib/features/webbuilder/components/VersionsSheet.js.map +1 -0
  54. package/lib/features/webbuilder/components/ui.js +291 -0
  55. package/lib/features/webbuilder/components/ui.js.map +1 -0
  56. package/lib/features/webbuilder/nativeWebBuilderRegistry.js +15 -0
  57. package/lib/features/webbuilder/nativeWebBuilderRegistry.js.map +1 -0
  58. package/lib/features/webbuilder/studioCatalog.js +147 -0
  59. package/lib/features/webbuilder/studioCatalog.js.map +1 -0
  60. package/lib/features/webbuilder/webbuilderPrompts.js +42 -0
  61. package/lib/features/webbuilder/webbuilderPrompts.js.map +1 -0
  62. package/lib/features/webbuilder/webbuilderTheme.js +63 -0
  63. package/lib/features/webbuilder/webbuilderTheme.js.map +1 -0
  64. package/lib/features/webbuilder/workflow.js +540 -0
  65. package/lib/features/webbuilder/workflow.js.map +1 -0
  66. package/lib/hooks/useChatApi.js +7 -2
  67. package/lib/hooks/useChatApi.js.map +1 -1
  68. package/lib/hooks/useChatStream.js +1 -1
  69. package/lib/hooks/useChatStream.js.map +1 -1
  70. package/lib/index.js +1 -1
  71. package/lib/index.js.map +1 -1
  72. package/lib/routes.json +106 -0
  73. package/lib/screens/WebBuilderNative/index.js +71 -0
  74. package/lib/screens/WebBuilderNative/index.js.map +1 -0
  75. package/lib/screens/WebBuilderNative/useCodeAssistantCatalog.js +45 -0
  76. package/lib/screens/WebBuilderNative/useCodeAssistantCatalog.js.map +1 -0
  77. package/lib/screens/WebBuilderNative/useWebBuilderGallery.js +125 -0
  78. package/lib/screens/WebBuilderNative/useWebBuilderGallery.js.map +1 -0
  79. package/lib/screens/WebBuilderNative/useWebBuilderHost.js +408 -0
  80. package/lib/screens/WebBuilderNative/useWebBuilderHost.js.map +1 -0
  81. package/lib/screens/WebBuilderNative/webbuilderTransport.js +88 -0
  82. package/lib/screens/WebBuilderNative/webbuilderTransport.js.map +1 -0
  83. package/package.json +4 -4
@@ -0,0 +1 @@
1
+ {"version":3,"file":"buildFragments.js","sources":["../../../src/features/webbuilder/buildFragments.ts"],"sourcesContent":["/**\n * Reads builds out of a build chat's posts.\n *\n * The build workflow posts its result as a \"fragment\" on an assistant post:\n * sandbox id and preview URL, the source files, and after a publish the live\n * address. Native twin of the web host's `creationsFromMessages`\n * (browser `pages/webbuilder/remoteWebbuilder.ts`) and the surface's\n * `extractFragmentFromSource` lookup order: `propsConfiguration.contents`\n * first (where the real fragment lives; `props` is account-scoped), then the\n * post's own fields. Pure, so the host and jest can both use it.\n */\nimport type { WebBuilderBuild, WebBuilderProject } from './types';\n\ntype Loose = Record<string, any>; // eslint-disable-line @typescript-eslint/no-explicit-any\n\ninterface Fragment {\n sandboxUrl?: string;\n sandboxId?: string;\n liveUrl?: string;\n files?: Record<string, string> | null;\n title?: string;\n projectId?: string;\n /** Stack the build was made from (`vite-react`, `nextjs`), shown on the tile. */\n template?: string;\n /** Screenshot of the build: an object key or a full URL. */\n coverImageUrl?: string;\n /** CDN origin the cover key was stored under. */\n coverImagePath?: string;\n isCreatingSandbox: boolean;\n isDeploying: boolean;\n}\n\n/** `contents` arrives as an object or, from some resolvers, as JSON text. */\nfunction asObject(value: unknown): Loose | null {\n if (!value) return null;\n if (typeof value === 'object') return Array.isArray(value) ? null : (value as Loose);\n if (typeof value === 'string') {\n const trimmed = value.trim();\n if (!trimmed.startsWith('{')) return null;\n try {\n const parsed = JSON.parse(trimmed);\n return parsed && typeof parsed === 'object' ? (parsed as Loose) : null;\n } catch {\n return null;\n }\n }\n return null;\n}\n\nfunction str(value: unknown): string | undefined {\n return typeof value === 'string' && value.trim() ? value.trim() : undefined;\n}\n\n/** `props` is account-scoped (`props[\"<accountId>\"] = {…}`): the records one level down. */\nfunction accountRecords(record: Loose): Loose[] {\n return Object.values(record)\n .map((v) => asObject(v))\n .filter((v): v is Loose => v !== null);\n}\n\n/** First non-empty string among `key` on each record (and on its `fragment`). */\nfunction pick(records: Loose[], key: string): string | undefined {\n for (const record of records) {\n const value = str(record[key]) || str(asObject(record.fragment)?.[key]);\n if (value) return value;\n }\n return undefined;\n}\n\n/** A live sandbox URL (`https://3000-<id>.e2b.dev`) - never a cover. */\nconst LIVE_SANDBOX = /^https?:\\/\\/\\d+-[a-z0-9]+(\\.|\\/|$)/i;\n\n/**\n * The preview origin for a sandbox. A bare `https://3000-<id>` (no domain) is\n * the E2B default domain, as the surface's `liveSandboxIframeSrc` maps it.\n */\nexport function sandboxPreviewUrl(url: string | undefined, sandboxId: string | undefined): string | null {\n const given = str(url);\n if (given) {\n if (/^https?:\\/\\/\\d+-[a-z0-9]+\\/?$/i.test(given)) return `${given.replace(/\\/$/, '')}.e2b.dev`;\n return given;\n }\n const id = str(sandboxId);\n return id ? `https://3000-${id}.e2b.dev` : null;\n}\n\nfunction httpOrigin(value: string | undefined): string | null {\n const match = /^(https?:\\/\\/[^/?#]+)/i.exec((value ?? '').trim());\n return match && !LIVE_SANDBOX.test(match[1]) ? match[1] : null;\n}\n\n/**\n * The image to show for a cover - the web `resolveCoverDisplayUrl`: an object\n * key joined to the origin it was stored under, else to the app's CDN, else a\n * full https URL as given. A live sandbox URL is never a cover.\n */\nexport function coverDisplayUrl(cover: string | undefined, path?: string, cdnOrigin?: string): string | null {\n const raw = str(cover);\n if (!raw || LIVE_SANDBOX.test(raw)) return null;\n if (/^https?:\\/\\//i.test(raw)) return raw.split('?')[0];\n const key = raw.replace(/^\\/+/, '');\n if (!/\\/uploads\\//i.test(`/${key}`) && !/\\.(png|jpe?g|webp|gif|svg)$/i.test(key)) return null;\n const origin = httpOrigin(path) || httpOrigin(cdnOrigin);\n return origin ? `${origin}/${key}` : null;\n}\n\nexport function readFragment(post: unknown): Fragment | null {\n if (!post || typeof post !== 'object') return null;\n const p = post as Loose;\n const cfg: Loose = p.propsConfiguration || {};\n const contents: Loose = asObject(cfg.contents) || asObject(cfg.content) || {};\n const contentsProps: Loose = asObject(contents.props) || {};\n const props: Loose = asObject(p.props) || {};\n const fragment: Loose | null =\n asObject(contentsProps.fragment) ||\n asObject(contents.fragment) ||\n asObject(cfg.fragment) ||\n asObject(props.fragment) ||\n asObject(p.fragment) ||\n null;\n const f: Loose = fragment || {};\n const sandboxUrl = str(f.sandboxUrl) || str(f.url) || str(p.sandboxUrl) || str(contents.sandboxUrl);\n const sandboxId = str(f.sandboxId) || str(p.sandboxId) || str(contents.sandboxId) || str(cfg.sandboxId);\n const publish = asObject(f.publish);\n const liveUrl =\n str(f.customDomainUrl) || str(f.cloudflareUrl) || str(f.deployUrl) || str(publish?.url) || undefined;\n const attachment = asObject(props.attachment) || asObject(contents.attachment) || {};\n const isCreatingSandbox =\n f.isCreatingSandbox === true || contents.isCreatingSandbox === true || p.isCreatingSandbox === true;\n const isDeploying = attachment.isDeploying === true || f.isDeploying === true;\n if (!sandboxUrl && !sandboxId && !liveUrl && !fragment && !isCreatingSandbox && !isDeploying) return null;\n const files = f.files && typeof f.files === 'object' ? (f.files as Record<string, string>) : null;\n // The cover rides on the fragment, the post's props, or an account record of either.\n const coverSources = [f, props, contents, contentsProps, p, ...accountRecords(props), ...accountRecords(contents)];\n const coverImageUrl = [\n pick(coverSources, 'coverImageUrl'),\n pick(coverSources, 'previewImageUrl'),\n str(f.cover),\n ].find((c) => c && !LIVE_SANDBOX.test(c));\n return {\n sandboxUrl,\n sandboxId,\n liveUrl,\n files,\n title: str(f.title),\n projectId: str(f.projectId) || str(contentsProps.projectId),\n template: str(f.templateId) || str(f.template) || str(contentsProps.template) || str(contents.template),\n coverImageUrl,\n coverImagePath: pick(coverSources, 'coverImagePath'),\n isCreatingSandbox,\n isDeploying,\n };\n}\n\n/** A live sandbox preview the agent named in its reply (`https://3000-<id>.e2b.dev/…`). */\nconst PREVIEW_LINK = /https?:\\/\\/\\d+-([a-z0-9]+)\\.e2b\\.(?:dev|app)(?:\\/[^\\s)\\]\"'<>]*)?/i;\n\n/**\n * A build named only in the agent's words: the agent-chat lane (template remix,\n * questions answered by building) replies with the preview link and posts no\n * fragment, so the link is the build.\n */\nfunction linkFragment(post: unknown): Fragment | null {\n const p = (post ?? {}) as Loose;\n const text = typeof p.message === 'string' ? p.message : '';\n const match = PREVIEW_LINK.exec(text);\n if (!match) return null;\n return {\n sandboxUrl: match[0].replace(/[.,;:!?]+$/, ''),\n sandboxId: match[1],\n isCreatingSandbox: false,\n isDeploying: false,\n };\n}\n\nfunction postTime(post: unknown): number {\n const p = (post ?? {}) as Loose;\n const raw = p.updatedAt || p.createdAt;\n const ms = typeof raw === 'number' ? raw : Date.parse(String(raw ?? ''));\n return Number.isFinite(ms) ? ms : 0;\n}\n\n/** Posts in the order they happened (the query is not sorted). */\nfunction chronological(posts: readonly unknown[]): unknown[] {\n return [...posts].sort((a, b) => postTime(a) - postTime(b));\n}\n\nfunction isUserPost(post: unknown): boolean {\n const p = (post ?? {}) as Loose;\n const contents = asObject(p.propsConfiguration?.contents) || {};\n const props = asObject(p.props) || {};\n const role = String(props.role ?? contents.role ?? asObject(contents.props)?.role ?? '').toUpperCase();\n return role === 'USER';\n}\n\n/** The stack label the web tile shows when a build does not name one. */\nexport const DEFAULT_TEMPLATE = 'vite-react';\n\n/**\n * Newest-first builds of one site. A later post about the same sandbox (a\n * publish, a re-post, its screenshot) updates that build.\n */\nexport function buildsFromPosts(\n posts: readonly unknown[],\n fallbackTitle?: string,\n cdnOrigin?: string,\n): WebBuilderBuild[] {\n const fallback = fallbackTitle ?? 'Web app';\n const byKey = new Map<string, WebBuilderBuild>();\n let lastKey: string | null = null;\n // The web hydrate's rule: a build is an ASSISTANT post with something to show - a\n // preview URL, a live address or a screenshot. A user turn carries the sandbox it\n // edits (and a bare sandbox id is a turn in flight), neither is a build.\n const fragments = chronological(posts)\n .filter((post) => !isUserPost(post))\n .map((post) => ({ post, fragment: readFragment(post) ?? linkFragment(post) }))\n .filter(\n (row): row is { post: unknown; fragment: Fragment } =>\n row.fragment !== null &&\n Boolean(row.fragment.sandboxUrl || row.fragment.liveUrl || row.fragment.coverImageUrl),\n );\n for (const { post, fragment } of fragments) {\n const previewUrl = sandboxPreviewUrl(fragment.sandboxUrl, fragment.sandboxId);\n const key = fragment.sandboxId || previewUrl || '';\n // A publish or screenshot post may name no sandbox: it belongs to the latest build.\n const target: string | null = key || (fragment.liveUrl || fragment.coverImageUrl ? lastKey : null);\n if (target) {\n const existing = byKey.get(target);\n byKey.set(target, {\n key: target,\n title: fragment.title || existing?.title || fallback,\n previewUrl: previewUrl || existing?.previewUrl || null,\n sandboxId: fragment.sandboxId || existing?.sandboxId || null,\n projectId: fragment.projectId || existing?.projectId || null,\n liveUrl: fragment.liveUrl || existing?.liveUrl || null,\n files: fragment.files || existing?.files || null,\n templateId: fragment.template || existing?.templateId || DEFAULT_TEMPLATE,\n coverUrl:\n coverDisplayUrl(fragment.coverImageUrl, fragment.coverImagePath, cdnOrigin) ||\n existing?.coverUrl ||\n null,\n updatedAt: Math.max(postTime(post), existing?.updatedAt ?? 0),\n });\n lastKey = target;\n }\n }\n return [...byKey.values()].filter((b) => b.previewUrl || b.liveUrl).sort((a, b) => b.updatedAt - a.updatedAt);\n}\n\n/** The newest post flags a sandbox still being created or a publish still running. */\nexport function pendingWork(posts: readonly unknown[]): 'creating' | 'publishing' | null {\n const newest = chronological(posts)\n .map((post) => readFragment(post))\n .filter((f): f is Fragment => f !== null)\n .pop();\n if (!newest) return null;\n if (newest.isCreatingSandbox && !newest.sandboxUrl && !newest.sandboxId) return 'creating';\n if (newest.isDeploying && !newest.liveUrl) return 'publishing';\n return null;\n}\n\n// ── Titles: the web surface's `resolveArtifactTitle` rules ─────────────────\n\n/** Not a product name: what the workflow and the chat call a site before it has one. */\nexport function isGenericTitle(title?: string | null): boolean {\n const t = (title || '').trim().toLowerCase();\n return (\n !t ||\n t === 'generated page' ||\n t === 'generated project' ||\n t === 'generated with cdecli' ||\n t === 'ai assistant' ||\n t === 'web app' ||\n /^build \\d+$/.test(t) ||\n /^build\\s+me\\b/.test(t) ||\n /^remix\\s+the\\b/.test(t) ||\n /^(build|chat)[- ]channel\\b/.test(t) ||\n t.startsWith('selected skill') ||\n /^code[\\s_-]?assist/.test(t)\n );\n}\n\nconst TITLE_STOP_WORDS = new Set([\n 'a',\n 'an',\n 'the',\n 'create',\n 'build',\n 'make',\n 'please',\n 'me',\n 'my',\n 'for',\n 'with',\n 'simple',\n 'using',\n 'from',\n 'template',\n 'to',\n 'and',\n 'or',\n 'remix',\n 'yantra',\n]);\n\nfunction titleCase(words: string[]): string {\n return words.map((w) => w.charAt(0).toUpperCase() + w.slice(1).toLowerCase()).join(' ');\n}\n\n/** The person's words, without the internal lines a turn carries. */\nexport function cleanPrompt(text: string): string {\n return text\n .replace(/^\\[webbuilder context:[^\\]]*\\]\\s*/i, '')\n .replace(/^build\\s*me\\s*:\\s*/i, '')\n .replace(/\\s+/g, ' ')\n .trim();\n}\n\n/**\n * Product name from a brief: \"Build me: todo app\" -> \"Todo App\". An edit\n * (\"make the header blue\") is not a name.\n */\nexport function titleFromPrompt(message?: string | null): string | null {\n const raw = (message || '').replace(/^\\[webbuilder context:[^\\]]*\\]\\s*/i, '').trim();\n if (!raw) return null;\n const buildMe = raw.match(/^\\s*Build me:\\s*(.+)$/im)?.[1]?.trim();\n if (!buildMe && /^(change|update|rename|fix|edit|add|remove|delete|improve|make|refactor)\\b/i.test(raw)) {\n return null;\n }\n const source = (buildMe || raw.split(/\\n+/)[0] || '').replace(/^[\"']|[\"']$/g, '');\n const words = source\n .replace(/[^\\w\\s-]/g, ' ')\n .split(/\\s+/)\n .filter((w) => w.length > 1 && !TITLE_STOP_WORDS.has(w.toLowerCase()))\n .slice(0, 3);\n if (!words.length) return null;\n const title = titleCase(words);\n return isGenericTitle(title) ? null : title;\n}\n\n/** `package.json` name of the build (\"bakery-site\" -> \"Bakery Site\"). */\nfunction titleFromPackageJson(files?: Record<string, string> | null): string | null {\n if (!files) return null;\n const raw = Object.entries(files).find(([path]) => /(^|\\/)package\\.json$/.test(path))?.[1];\n if (!raw) return null;\n try {\n const name = String((JSON.parse(raw) as { name?: string }).name || '').trim();\n if (!name || name === 'vite-react' || name === 'project') return null;\n return name\n .replace(/^@[^/]+\\//, '')\n .replace(/[-_]+/g, ' ')\n .replace(/\\b\\w/g, (c) => c.toUpperCase());\n } catch {\n return null;\n }\n}\n\n/** The build's own title, then the brief's product name, the chat's name, the package name. */\nexport function resolveSiteTitle(opts: {\n fragmentTitle?: string | null;\n channelTitle?: string | null;\n userPrompt?: string | null;\n files?: Record<string, string> | null;\n}): string {\n const candidates = [\n opts.fragmentTitle,\n titleFromPrompt(opts.userPrompt),\n opts.channelTitle,\n titleFromPackageJson(opts.files),\n ];\n const found = candidates.find((c) => c && !isGenericTitle(c));\n return (found || 'Generated Page').trim();\n}\n\n// ── Sites ──────────────────────────────────────────────────────────────────\n\nexport interface ChannelLike {\n id?: string | null;\n title?: string | null;\n displayName?: string | null;\n updatedAt?: unknown;\n createdAt?: unknown;\n lastMessage?: unknown;\n}\n\n/** True when the newest build in `lastMessage` already carries a cover - the web skips the thread read then. */\nexport function lastMessageHasCover(channel: ChannelLike): boolean {\n return Boolean(readFragment(channel.lastMessage)?.coverImageUrl);\n}\n\n/**\n * A site from one chat's posts, the way the web gallery hydrates a tile: only\n * a chat with a build is a site; its name follows `resolveSiteTitle`.\n */\nexport function projectFromPosts(\n channel: ChannelLike,\n posts: readonly unknown[],\n cdnOrigin?: string,\n): WebBuilderProject | null {\n const channelId = str(channel.id);\n if (!channelId) return null;\n const builds = buildsFromPosts(posts, '', cdnOrigin);\n const latest = builds[0];\n if (!latest) return null;\n const userPrompts = chronological(posts)\n .filter((post) => isUserPost(post) && str((post as Loose).message))\n .map((post) => String((post as Loose).message));\n // The brief names the product (\"Build me: …\"); a later message (\"the live preview is\n // not loading\") never does, so only the brief - or failing that the first ask - counts.\n const brief = userPrompts.find((m) => /build\\s*me\\s*:/i.test(m)) ?? userPrompts[0];\n const channelTitle = [channel.displayName, channel.title].map((t) => str(t)).find((t) => t && !isGenericTitle(t));\n const title = resolveSiteTitle({\n fragmentTitle: latest.title,\n channelTitle,\n userPrompt: brief,\n files: latest.files,\n });\n const updated = Date.parse(String(channel.updatedAt ?? channel.createdAt ?? ''));\n return {\n channelId,\n title,\n updatedAt: Math.max(Number.isFinite(updated) ? updated : 0, latest.updatedAt),\n previewUrl: latest.previewUrl,\n liveUrl: builds.find((b) => b.liveUrl)?.liveUrl ?? null,\n coverUrl: builds.find((b) => b.coverUrl)?.coverUrl ?? null,\n templateId: latest.templateId,\n };\n}\n\n/** `?wb=<n>` on the preview so a rebuilt sandbox is fetched again, not served from cache. */\nexport function cacheBustedUrl(url: string, nonce: number | string): string {\n const [base, hash = ''] = url.split('#');\n const clean = base.replace(/([?&])wb=[^&]*(&|$)/, '$1').replace(/[?&]$/, '');\n const sep = clean.includes('?') ? '&' : '?';\n return `${clean}${sep}wb=${nonce}${hash ? `#${hash}` : ''}`;\n}\n\n/**\n * The publish name the web dialog suggests: the site's title as a slug,\n * `[a-z0-9-]`, at most 58 characters (`<name>.pages.dev`).\n */\nexport function publishSlug(title: string): string {\n const slug = title\n .toLowerCase()\n .normalize('NFKD')\n .replace(/[\\u0300-\\u036f]/g, '')\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '')\n .slice(0, 58)\n .replace(/-+$/, '');\n return slug || 'my-site';\n}\n\nexport function isValidPublishSlug(value: string): boolean {\n return /^[a-z0-9](?:[a-z0-9-]{0,56}[a-z0-9])?$/.test(value);\n}\n\n/** Display host of a URL: `https://bakery.pages.dev/` -> `bakery.pages.dev`. */\nexport function displayHost(url: string | null | undefined): string {\n const match = /^https?:\\/\\/([^/?#]+)/i.exec((url ?? '').trim());\n return match ? match[1] : (url ?? '').trim();\n}\n\n/** \"just now\", \"5 min ago\", \"3 h ago\", \"Yesterday\", \"12 Oct\". */\nexport function relativeTime(ms: number, now = Date.now()): string {\n if (!ms) return '';\n const diff = Math.max(0, now - ms);\n const min = Math.floor(diff / 60_000);\n if (min < 1) return 'just now';\n if (min < 60) return `${min} min ago`;\n const hours = Math.floor(min / 60);\n if (hours < 24) return `${hours} h ago`;\n if (hours < 48) return 'Yesterday';\n const d = new Date(ms);\n const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];\n return `${d.getDate()} ${months[d.getMonth()]}`;\n}\n\n// ── The agent transcript: the build chat's posts, as the web Agent pane shows them ──\n\n/** A live progress row (web `isThinkingPost`): shown as the agent's status, never kept. */\nexport function isThinkingPost(post: unknown): boolean {\n const p = (post ?? {}) as Loose;\n const props = asObject(p.props) || {};\n const attachment = asObject(props.attachment) || {};\n const msg = String(p.message ?? '')\n .trim()\n .toLowerCase();\n return (\n String(p.type ?? '').toUpperCase() === 'THINKING' ||\n Boolean(props.thinking ?? props.isThinking) ||\n props.kind === 'thinking' ||\n Boolean(attachment.isThinking) ||\n msg === 'thinking...' ||\n msg === 'initializing...' ||\n msg === 'initializing…'\n );\n}\n\nfunction readFragmentStatus(p: Loose): string | undefined {\n const contents = asObject(p.propsConfiguration?.contents) || {};\n return (\n str(asObject(asObject(contents.props)?.fragment)?.statusText) || str(asObject(contents.fragment)?.statusText)\n );\n}\n\n/** The workflow's progress line on a post: its thought, else the fragment's status. */\nexport function progressText(post: unknown): string | null {\n const p = (post ?? {}) as Loose;\n const props = asObject(p.props) || {};\n const thought = str(asObject(props.attachment)?.thought);\n if (thought) return thought;\n const status = str(asObject(props.fragment)?.statusText) || str(readFragmentStatus(p));\n if (status) return status;\n return isThinkingPost(post) ? (str(p.message) ?? null) : null;\n}\n\n/** A post the workflow wrote when the build failed (`fragment.isError` / `type: 'error'`). */\nexport function isErrorPost(post: unknown): boolean {\n const p = (post ?? {}) as Loose;\n const props = asObject(p.props) || {};\n const contents = asObject(p.propsConfiguration?.contents) || {};\n const f =\n asObject(props.fragment) || asObject(asObject(contents.props)?.fragment) || asObject(contents.fragment) || {};\n return f.isError === true || f.type === 'error';\n}\n\nexport interface TranscriptMessage {\n id: string;\n role: 'user' | 'assistant';\n content: string;\n createdAt: number;\n}\n\n/**\n * The conversation in a build chat: the person's turns and the agent's replies,\n * oldest first, without progress rows or empty build markers.\n */\nexport function transcriptFromPosts(posts: readonly unknown[]): TranscriptMessage[] {\n const out: TranscriptMessage[] = [];\n for (const post of chronological(posts)) {\n const p = post as Loose;\n const text = str(p.message);\n if (text && !isThinkingPost(post)) {\n const user = isUserPost(post);\n out.push({\n id: String(p.id ?? `${user ? 'u' : 'a'}-${out.length}`),\n role: user ? 'user' : 'assistant',\n content: user ? cleanPrompt(text) || text : text,\n createdAt: postTime(post),\n });\n }\n }\n return out;\n}\n\n/** How a turn started at `since` is going, read from the posts that arrived after it. */\nexport function turnState(\n posts: readonly unknown[],\n since: number,\n): { status: 'running' | 'done' | 'failed'; progress: string | null; error: string | null } {\n // A clock skew allowance, as the web ignores posts older than start - 2 s.\n const after = chronological(posts).filter((post) => postTime(post) >= since - 2_000);\n let progress: string | null = null;\n for (const post of after) {\n if (!isUserPost(post)) {\n if (isErrorPost(post)) {\n const p = post as Loose;\n return { status: 'failed', progress: null, error: str(p.message) ?? 'The build failed.' };\n }\n const fragment = readFragment(post);\n if (fragment && (fragment.sandboxUrl || fragment.liveUrl) && !isThinkingPost(post)) {\n return { status: 'done', progress: null, error: null };\n }\n progress = progressText(post) ?? progress;\n }\n }\n // A plain answer (the agent asked a question, or replied without building) ends\n // the turn too - unless progress rows keep arriving after it.\n const newest = after.filter((post) => !isUserPost(post)).pop() as Loose | undefined;\n if (newest && !isThinkingPost(newest) && !progressText(newest) && str(newest.message)) {\n return { status: 'done', progress: null, error: null };\n }\n return { status: 'running', progress, error: null };\n}\n\n/**\n * The chat was built through the build workflow (its posts carry fragments):\n * edits go back through the workflow. A chat without any is an agent-chat site\n * (a template remix), and its turns stay in the agent chat.\n */\nexport function hasWorkflowPosts(posts: readonly unknown[]): boolean {\n return posts.some((post) => readFragment(post) !== null);\n}\n\n/** The person's words, without the agent-only template note a remix turn carries. */\nexport function stripAgentNote(content: string): string {\n return content.replace(/\\n*\\[Template\\][\\s\\S]*$/, '').trim();\n}\n"],"names":["_a"],"mappings":"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"}
@@ -0,0 +1,65 @@
1
+ import {jsx}from'react/jsx-runtime';import {StyleSheet,View}from'react-native';import {MaterialCommunityIcons}from'@expo/vector-icons';import {Tap}from'../../inbox/components/Tap.js';const BOTTOM_TABS_SPACE = 92;
2
+ const TABS = [{
3
+ id: "create",
4
+ label: "Create",
5
+ icon: "home-outline",
6
+ iconActive: "home"
7
+ }, {
8
+ id: "projects",
9
+ label: "Projects",
10
+ icon: "view-grid-outline",
11
+ iconActive: "view-grid"
12
+ }];
13
+ function BottomTabs({
14
+ tab,
15
+ onTab,
16
+ theme,
17
+ bottomInset
18
+ }) {
19
+ return /* @__PURE__ */ jsx(View, { pointerEvents: "box-none", style: [styles.dock, {
20
+ bottom: Math.max(bottomInset, 12)
21
+ }], children: /* @__PURE__ */ jsx(View, { style: [styles.pill, {
22
+ backgroundColor: theme.card,
23
+ borderColor: theme.border,
24
+ shadowColor: theme.isDark ? "#000" : "#1c1917"
25
+ }], accessibilityRole: "tablist", children: TABS.map((t) => {
26
+ const active = t.id === tab;
27
+ return /* @__PURE__ */ jsx(Tap, { onPress: () => onTab(t.id), accessibilityRole: "tab", accessibilityState: {
28
+ selected: active
29
+ }, accessibilityLabel: t.label, style: [styles.tab, active && {
30
+ backgroundColor: theme.accent
31
+ }], pressedStyle: styles.pressed, testID: `webbuilder-tab-${t.id}`, children: /* @__PURE__ */ jsx(MaterialCommunityIcons, { name: active ? t.iconActive : t.icon, size: 25, color: active ? theme.accentInk : theme.muted }) }, t.id);
32
+ }) }) });
33
+ }
34
+ const styles = StyleSheet.create({
35
+ dock: {
36
+ position: "absolute",
37
+ left: 0,
38
+ right: 0,
39
+ alignItems: "center"
40
+ },
41
+ pill: {
42
+ flexDirection: "row",
43
+ gap: 6,
44
+ padding: 6,
45
+ borderRadius: 999,
46
+ borderWidth: StyleSheet.hairlineWidth,
47
+ shadowOpacity: 0.12,
48
+ shadowRadius: 20,
49
+ shadowOffset: {
50
+ width: 0,
51
+ height: 8
52
+ },
53
+ elevation: 8
54
+ },
55
+ tab: {
56
+ width: 88,
57
+ height: 52,
58
+ borderRadius: 999,
59
+ alignItems: "center",
60
+ justifyContent: "center"
61
+ },
62
+ pressed: {
63
+ opacity: 0.6
64
+ }
65
+ });export{BOTTOM_TABS_SPACE,BottomTabs};//# sourceMappingURL=BottomTabs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BottomTabs.js","sources":["../../../../src/features/webbuilder/components/BottomTabs.tsx"],"sourcesContent":["/**\n * Web Builder's two places - Create and Projects - as the Lovable app shows\n * them: an icon-only pill floating over the content, the current place on a\n * soft capsule. Labels stay for screen readers.\n */\nimport React from 'react';\nimport { StyleSheet, View } from 'react-native';\nimport { MaterialCommunityIcons } from '@expo/vector-icons';\nimport { Tap } from '../../inbox/components/Tap';\nimport type { WebBuilderTheme } from '../webbuilderTheme';\nimport type { IconName } from './ui';\n\nexport type HomeTab = 'create' | 'projects';\n\n/** Height the pill takes over the content, for the screens' bottom padding. */\nexport const BOTTOM_TABS_SPACE = 92;\n\nconst TABS: Array<{ id: HomeTab; label: string; icon: IconName; iconActive: IconName }> = [\n { id: 'create', label: 'Create', icon: 'home-outline', iconActive: 'home' },\n { id: 'projects', label: 'Projects', icon: 'view-grid-outline', iconActive: 'view-grid' },\n];\n\nexport function BottomTabs({\n tab,\n onTab,\n theme,\n bottomInset,\n}: {\n tab: HomeTab;\n onTab: (tab: HomeTab) => void;\n theme: WebBuilderTheme;\n bottomInset: number;\n}) {\n return (\n <View pointerEvents=\"box-none\" style={[styles.dock, { bottom: Math.max(bottomInset, 12) }]}>\n <View\n style={[\n styles.pill,\n {\n backgroundColor: theme.card,\n borderColor: theme.border,\n shadowColor: theme.isDark ? '#000' : '#1c1917',\n },\n ]}\n accessibilityRole=\"tablist\"\n >\n {TABS.map((t) => {\n const active = t.id === tab;\n return (\n <Tap\n key={t.id}\n onPress={() => onTab(t.id)}\n accessibilityRole=\"tab\"\n accessibilityState={{ selected: active }}\n accessibilityLabel={t.label}\n style={[styles.tab, active && { backgroundColor: theme.accent }]}\n pressedStyle={styles.pressed}\n testID={`webbuilder-tab-${t.id}`}\n >\n <MaterialCommunityIcons\n name={active ? t.iconActive : t.icon}\n size={25}\n color={active ? theme.accentInk : theme.muted}\n />\n </Tap>\n );\n })}\n </View>\n </View>\n );\n}\n\nconst styles = StyleSheet.create({\n dock: { position: 'absolute', left: 0, right: 0, alignItems: 'center' },\n pill: {\n flexDirection: 'row',\n gap: 6,\n padding: 6,\n borderRadius: 999,\n borderWidth: StyleSheet.hairlineWidth,\n shadowOpacity: 0.12,\n shadowRadius: 20,\n shadowOffset: { width: 0, height: 8 },\n elevation: 8,\n },\n tab: { width: 88, height: 52, borderRadius: 999, alignItems: 'center', justifyContent: 'center' },\n pressed: { opacity: 0.6 },\n});\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,197 @@
1
+ import {jsx,jsxs}from'react/jsx-runtime';import {useState,useEffect}from'react';import {StyleSheet,ScrollView,Text,View,TextInput}from'react-native';import {MaterialCommunityIcons}from'@expo/vector-icons';import {Tap,DIM}from'../../inbox/components/Tap.js';import {BRAND_MAX_COLORS,normalizeHex,sanitizeBrandKit,BRAND_MAX_FONTS}from'../studioCatalog.js';import {Sheet,PrimaryButton}from'./ui.js';function Field({
2
+ label,
3
+ value,
4
+ onChangeText,
5
+ placeholder,
6
+ theme,
7
+ testID,
8
+ autoCapitalize = "sentences"
9
+ }) {
10
+ return /* @__PURE__ */ jsxs(View, { style: styles.field, children: [
11
+ /* @__PURE__ */ jsx(Text, { style: [styles.label, {
12
+ color: theme.muted
13
+ }], children: label }),
14
+ /* @__PURE__ */ jsx(TextInput, { value, onChangeText, placeholder, placeholderTextColor: theme.faint, autoCapitalize, style: [styles.input, {
15
+ color: theme.ink,
16
+ backgroundColor: theme.chip,
17
+ borderColor: theme.border
18
+ }], accessibilityLabel: label, testID })
19
+ ] });
20
+ }
21
+ function BrandKitSheet({
22
+ visible,
23
+ brand,
24
+ onSave,
25
+ onClose,
26
+ theme
27
+ }) {
28
+ const [name, setName] = useState("");
29
+ const [description, setDescription] = useState("");
30
+ const [colors, setColors] = useState([]);
31
+ const [colorDraft, setColorDraft] = useState("");
32
+ const [fonts, setFonts] = useState("");
33
+ const [voice, setVoice] = useState("");
34
+ const [error, setError] = useState(null);
35
+ const [saving, setSaving] = useState(false);
36
+ useEffect(() => {
37
+ var _a, _b, _c, _d, _e;
38
+ if (!visible) return;
39
+ setName((_a = brand == null ? void 0 : brand.name) != null ? _a : "");
40
+ setDescription((_b = brand == null ? void 0 : brand.description) != null ? _b : "");
41
+ setColors((_c = brand == null ? void 0 : brand.colors) != null ? _c : []);
42
+ setColorDraft("");
43
+ setFonts(((_d = brand == null ? void 0 : brand.fonts) != null ? _d : []).join(", "));
44
+ setVoice((_e = brand == null ? void 0 : brand.voice) != null ? _e : "");
45
+ setError(null);
46
+ }, [visible, brand]);
47
+ const addColor = () => {
48
+ const hex = normalizeHex(colorDraft);
49
+ if (!hex) {
50
+ setError("Colours are hex codes, like #ea580c.");
51
+ return;
52
+ }
53
+ setError(null);
54
+ setColors((prev) => prev.includes(hex) ? prev : [...prev, hex].slice(0, BRAND_MAX_COLORS));
55
+ setColorDraft("");
56
+ };
57
+ const save = async () => {
58
+ const kit = sanitizeBrandKit({
59
+ name,
60
+ description,
61
+ colors,
62
+ fonts: fonts.split(",").map((f) => f.trim()).filter(Boolean).slice(0, BRAND_MAX_FONTS),
63
+ voice
64
+ });
65
+ if (!kit) {
66
+ setError("Give your brand a name or at least one colour.");
67
+ return;
68
+ }
69
+ setSaving(true);
70
+ const ok = await onSave(kit);
71
+ setSaving(false);
72
+ if (ok) onClose();
73
+ else setError("Couldn't save the brand kit on this device. Try again.");
74
+ };
75
+ return /* @__PURE__ */ jsx(Sheet, { visible, title: "Brand kit", onClose, theme, testID: "webbuilder-brand-sheet", children: /* @__PURE__ */ jsxs(ScrollView, { style: styles.scroll, contentContainerStyle: styles.body, keyboardShouldPersistTaps: "handled", children: [
76
+ /* @__PURE__ */ jsx(Text, { style: [styles.intro, {
77
+ color: theme.muted
78
+ }], children: "New builds follow your brand: its name, colours, fonts and voice." }),
79
+ /* @__PURE__ */ jsx(Field, { label: "Brand name", value: name, onChangeText: setName, placeholder: "Sweet Crumbs", theme, testID: "webbuilder-brand-name" }),
80
+ /* @__PURE__ */ jsx(Field, { label: "What it is", value: description, onChangeText: setDescription, placeholder: "A neighbourhood bakery in Austin", theme }),
81
+ /* @__PURE__ */ jsxs(View, { style: styles.field, children: [
82
+ /* @__PURE__ */ jsx(Text, { style: [styles.label, {
83
+ color: theme.muted
84
+ }], children: "Colours (primary first)" }),
85
+ /* @__PURE__ */ jsx(View, { style: styles.swatches, children: colors.map((color, index) => /* @__PURE__ */ jsxs(Tap, { onPress: () => setColors((prev) => prev.filter((c) => c !== color)), accessibilityLabel: `Remove ${color}`, style: [styles.swatchChip, {
86
+ borderColor: theme.border,
87
+ backgroundColor: theme.card
88
+ }], pressedStyle: DIM, children: [
89
+ /* @__PURE__ */ jsx(View, { style: [styles.swatch, {
90
+ backgroundColor: color
91
+ }] }),
92
+ /* @__PURE__ */ jsx(Text, { style: [styles.swatchLabel, {
93
+ color: theme.ink
94
+ }], children: index === 0 ? `${color} \xB7 primary` : color }),
95
+ /* @__PURE__ */ jsx(MaterialCommunityIcons, { name: "close", size: 14, color: theme.muted })
96
+ ] }, color)) }),
97
+ colors.length < BRAND_MAX_COLORS ? /* @__PURE__ */ jsxs(View, { style: styles.colorRow, children: [
98
+ /* @__PURE__ */ jsx(TextInput, { value: colorDraft, onChangeText: setColorDraft, onSubmitEditing: addColor, placeholder: "#ea580c", placeholderTextColor: theme.faint, autoCapitalize: "none", autoCorrect: false, style: [styles.input, styles.colorInput, {
99
+ color: theme.ink,
100
+ backgroundColor: theme.chip,
101
+ borderColor: theme.border
102
+ }], accessibilityLabel: "Add a colour", testID: "webbuilder-brand-color" }),
103
+ /* @__PURE__ */ jsx(Tap, { onPress: addColor, accessibilityRole: "button", accessibilityLabel: "Add colour", style: [styles.add, {
104
+ backgroundColor: theme.accentSoft
105
+ }], pressedStyle: DIM, children: /* @__PURE__ */ jsx(MaterialCommunityIcons, { name: "plus", size: 20, color: theme.accent }) })
106
+ ] }) : null
107
+ ] }),
108
+ /* @__PURE__ */ jsx(Field, { label: "Fonts", value: fonts, onChangeText: setFonts, placeholder: "Fraunces, Inter", theme, autoCapitalize: "words" }),
109
+ /* @__PURE__ */ jsx(Field, { label: "Voice", value: voice, onChangeText: setVoice, placeholder: "Warm, plain-spoken", theme }),
110
+ error ? /* @__PURE__ */ jsx(Text, { style: [styles.error, {
111
+ color: theme.crit
112
+ }], children: error }) : null,
113
+ /* @__PURE__ */ jsx(PrimaryButton, { label: "Save brand kit", onPress: () => {
114
+ save().catch(() => void 0);
115
+ }, theme, busy: saving, testID: "webbuilder-brand-save" }),
116
+ brand ? /* @__PURE__ */ jsx(Tap, { onPress: () => {
117
+ onSave(null).then((ok) => ok ? onClose() : void 0).catch(() => void 0);
118
+ }, accessibilityRole: "button", style: styles.clear, pressedStyle: DIM, children: /* @__PURE__ */ jsx(Text, { style: [styles.clearLabel, {
119
+ color: theme.crit
120
+ }], children: "Remove brand kit" }) }) : null
121
+ ] }) });
122
+ }
123
+ const styles = StyleSheet.create({
124
+ scroll: {
125
+ maxHeight: 560
126
+ },
127
+ body: {
128
+ gap: 12,
129
+ paddingBottom: 8
130
+ },
131
+ intro: {
132
+ fontSize: 14,
133
+ lineHeight: 20
134
+ },
135
+ field: {
136
+ gap: 6
137
+ },
138
+ label: {
139
+ fontSize: 13,
140
+ fontWeight: "600"
141
+ },
142
+ input: {
143
+ borderRadius: 12,
144
+ borderWidth: StyleSheet.hairlineWidth,
145
+ paddingHorizontal: 14,
146
+ paddingVertical: 11,
147
+ fontSize: 15
148
+ },
149
+ swatches: {
150
+ flexDirection: "row",
151
+ flexWrap: "wrap",
152
+ gap: 6
153
+ },
154
+ swatchChip: {
155
+ flexDirection: "row",
156
+ alignItems: "center",
157
+ gap: 6,
158
+ paddingHorizontal: 8,
159
+ paddingVertical: 6,
160
+ borderRadius: 999,
161
+ borderWidth: StyleSheet.hairlineWidth
162
+ },
163
+ swatch: {
164
+ width: 16,
165
+ height: 16,
166
+ borderRadius: 8
167
+ },
168
+ swatchLabel: {
169
+ fontSize: 13
170
+ },
171
+ colorRow: {
172
+ flexDirection: "row",
173
+ gap: 8,
174
+ alignItems: "center"
175
+ },
176
+ colorInput: {
177
+ flex: 1
178
+ },
179
+ add: {
180
+ width: 44,
181
+ height: 44,
182
+ borderRadius: 12,
183
+ alignItems: "center",
184
+ justifyContent: "center"
185
+ },
186
+ error: {
187
+ fontSize: 13
188
+ },
189
+ clear: {
190
+ alignSelf: "center",
191
+ padding: 8
192
+ },
193
+ clearLabel: {
194
+ fontSize: 14,
195
+ fontWeight: "600"
196
+ }
197
+ });export{BrandKitSheet};//# sourceMappingURL=BrandKitSheet.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BrandKitSheet.js","sources":["../../../../src/features/webbuilder/components/BrandKitSheet.tsx"],"sourcesContent":["/**\n * Brand kit editor - the web BrandKitDialog's manual entry: name, what it is,\n * colours (primary first), fonts and voice. Every new build's brief carries\n * it. Kept on this device, like the web keeps it per browser.\n */\nimport React, { useEffect, useState } from 'react';\nimport { ScrollView, StyleSheet, Text, TextInput, View } from 'react-native';\nimport { MaterialCommunityIcons } from '@expo/vector-icons';\nimport { DIM, Tap } from '../../inbox/components/Tap';\nimport { BRAND_MAX_COLORS, BRAND_MAX_FONTS, normalizeHex, sanitizeBrandKit, type BrandKit } from '../studioCatalog';\nimport type { WebBuilderTheme } from '../webbuilderTheme';\nimport { PrimaryButton, Sheet } from './ui';\n\nfunction Field({\n label,\n value,\n onChangeText,\n placeholder,\n theme,\n testID,\n autoCapitalize = 'sentences',\n}: {\n label: string;\n value: string;\n onChangeText: (text: string) => void;\n placeholder: string;\n theme: WebBuilderTheme;\n testID?: string;\n autoCapitalize?: 'none' | 'sentences' | 'words';\n}) {\n return (\n <View style={styles.field}>\n <Text style={[styles.label, { color: theme.muted }]}>{label}</Text>\n <TextInput\n value={value}\n onChangeText={onChangeText}\n placeholder={placeholder}\n placeholderTextColor={theme.faint}\n autoCapitalize={autoCapitalize}\n style={[styles.input, { color: theme.ink, backgroundColor: theme.chip, borderColor: theme.border }]}\n accessibilityLabel={label}\n testID={testID}\n />\n </View>\n );\n}\n\nexport function BrandKitSheet({\n visible,\n brand,\n onSave,\n onClose,\n theme,\n}: {\n visible: boolean;\n brand: BrandKit | null;\n onSave: (kit: BrandKit | null) => Promise<boolean>;\n onClose: () => void;\n theme: WebBuilderTheme;\n}) {\n const [name, setName] = useState('');\n const [description, setDescription] = useState('');\n const [colors, setColors] = useState<string[]>([]);\n const [colorDraft, setColorDraft] = useState('');\n const [fonts, setFonts] = useState('');\n const [voice, setVoice] = useState('');\n const [error, setError] = useState<string | null>(null);\n const [saving, setSaving] = useState(false);\n\n useEffect(() => {\n if (!visible) return;\n setName(brand?.name ?? '');\n setDescription(brand?.description ?? '');\n setColors(brand?.colors ?? []);\n setColorDraft('');\n setFonts((brand?.fonts ?? []).join(', '));\n setVoice(brand?.voice ?? '');\n setError(null);\n }, [visible, brand]);\n\n const addColor = () => {\n const hex = normalizeHex(colorDraft);\n if (!hex) {\n setError('Colours are hex codes, like #ea580c.');\n return;\n }\n setError(null);\n setColors((prev) => (prev.includes(hex) ? prev : [...prev, hex].slice(0, BRAND_MAX_COLORS)));\n setColorDraft('');\n };\n\n const save = async () => {\n const kit = sanitizeBrandKit({\n name,\n description,\n colors,\n fonts: fonts\n .split(',')\n .map((f) => f.trim())\n .filter(Boolean)\n .slice(0, BRAND_MAX_FONTS),\n voice,\n });\n if (!kit) {\n setError('Give your brand a name or at least one colour.');\n return;\n }\n setSaving(true);\n const ok = await onSave(kit);\n setSaving(false);\n if (ok) onClose();\n else setError(\"Couldn't save the brand kit on this device. Try again.\");\n };\n\n return (\n <Sheet visible={visible} title=\"Brand kit\" onClose={onClose} theme={theme} testID=\"webbuilder-brand-sheet\">\n <ScrollView style={styles.scroll} contentContainerStyle={styles.body} keyboardShouldPersistTaps=\"handled\">\n <Text style={[styles.intro, { color: theme.muted }]}>\n New builds follow your brand: its name, colours, fonts and voice.\n </Text>\n <Field\n label=\"Brand name\"\n value={name}\n onChangeText={setName}\n placeholder=\"Sweet Crumbs\"\n theme={theme}\n testID=\"webbuilder-brand-name\"\n />\n <Field\n label=\"What it is\"\n value={description}\n onChangeText={setDescription}\n placeholder=\"A neighbourhood bakery in Austin\"\n theme={theme}\n />\n <View style={styles.field}>\n <Text style={[styles.label, { color: theme.muted }]}>Colours (primary first)</Text>\n <View style={styles.swatches}>\n {colors.map((color, index) => (\n <Tap\n key={color}\n onPress={() => setColors((prev) => prev.filter((c) => c !== color))}\n accessibilityLabel={`Remove ${color}`}\n style={[styles.swatchChip, { borderColor: theme.border, backgroundColor: theme.card }]}\n pressedStyle={DIM}\n >\n <View style={[styles.swatch, { backgroundColor: color }]} />\n <Text style={[styles.swatchLabel, { color: theme.ink }]}>\n {index === 0 ? `${color} · primary` : color}\n </Text>\n <MaterialCommunityIcons name=\"close\" size={14} color={theme.muted} />\n </Tap>\n ))}\n </View>\n {colors.length < BRAND_MAX_COLORS ? (\n <View style={styles.colorRow}>\n <TextInput\n value={colorDraft}\n onChangeText={setColorDraft}\n onSubmitEditing={addColor}\n placeholder=\"#ea580c\"\n placeholderTextColor={theme.faint}\n autoCapitalize=\"none\"\n autoCorrect={false}\n style={[\n styles.input,\n styles.colorInput,\n { color: theme.ink, backgroundColor: theme.chip, borderColor: theme.border },\n ]}\n accessibilityLabel=\"Add a colour\"\n testID=\"webbuilder-brand-color\"\n />\n <Tap\n onPress={addColor}\n accessibilityRole=\"button\"\n accessibilityLabel=\"Add colour\"\n style={[styles.add, { backgroundColor: theme.accentSoft }]}\n pressedStyle={DIM}\n >\n <MaterialCommunityIcons name=\"plus\" size={20} color={theme.accent} />\n </Tap>\n </View>\n ) : null}\n </View>\n <Field\n label=\"Fonts\"\n value={fonts}\n onChangeText={setFonts}\n placeholder=\"Fraunces, Inter\"\n theme={theme}\n autoCapitalize=\"words\"\n />\n <Field\n label=\"Voice\"\n value={voice}\n onChangeText={setVoice}\n placeholder=\"Warm, plain-spoken\"\n theme={theme}\n />\n {error ? <Text style={[styles.error, { color: theme.crit }]}>{error}</Text> : null}\n <PrimaryButton\n label=\"Save brand kit\"\n onPress={() => {\n save().catch(() => undefined);\n }}\n theme={theme}\n busy={saving}\n testID=\"webbuilder-brand-save\"\n />\n {brand ? (\n <Tap\n onPress={() => {\n onSave(null)\n .then((ok) => (ok ? onClose() : undefined))\n .catch(() => undefined);\n }}\n accessibilityRole=\"button\"\n style={styles.clear}\n pressedStyle={DIM}\n >\n <Text style={[styles.clearLabel, { color: theme.crit }]}>Remove brand kit</Text>\n </Tap>\n ) : null}\n </ScrollView>\n </Sheet>\n );\n}\n\nconst styles = StyleSheet.create({\n scroll: { maxHeight: 560 },\n body: { gap: 12, paddingBottom: 8 },\n intro: { fontSize: 14, lineHeight: 20 },\n field: { gap: 6 },\n label: { fontSize: 13, fontWeight: '600' },\n input: {\n borderRadius: 12,\n borderWidth: StyleSheet.hairlineWidth,\n paddingHorizontal: 14,\n paddingVertical: 11,\n fontSize: 15,\n },\n swatches: { flexDirection: 'row', flexWrap: 'wrap', gap: 6 },\n swatchChip: {\n flexDirection: 'row',\n alignItems: 'center',\n gap: 6,\n paddingHorizontal: 8,\n paddingVertical: 6,\n borderRadius: 999,\n borderWidth: StyleSheet.hairlineWidth,\n },\n swatch: { width: 16, height: 16, borderRadius: 8 },\n swatchLabel: { fontSize: 13 },\n colorRow: { flexDirection: 'row', gap: 8, alignItems: 'center' },\n colorInput: { flex: 1 },\n add: { width: 44, height: 44, borderRadius: 12, alignItems: 'center', justifyContent: 'center' },\n error: { fontSize: 13 },\n clear: { alignSelf: 'center', padding: 8 },\n clearLabel: { fontSize: 14, fontWeight: '600' },\n});\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,99 @@
1
+ import {jsx,jsxs}from'react/jsx-runtime';import {useMemo,useState,useEffect}from'react';import {Platform,StyleSheet,View,ScrollView,Text}from'react-native';import {MaterialCommunityIcons}from'@expo/vector-icons';import {Tap,DIM}from'../../inbox/components/Tap.js';import {CenteredState}from'./ui.js';const MAX_CHARS = 6e4;
2
+ const MONO = Platform.select({
3
+ ios: "Menlo",
4
+ android: "monospace",
5
+ default: "monospace"
6
+ });
7
+ function orderedFiles(files) {
8
+ const rank = (path) => {
9
+ if (/(^|\/)src\//.test(path) || /(^|\/)(app|pages|components)\//.test(path)) return 0;
10
+ if (/\.(tsx?|jsx?|vue|svelte|css|html)$/.test(path)) return 1;
11
+ return 2;
12
+ };
13
+ return Object.keys(files).filter((path) => !/(^|\/)(package-lock\.json|yarn\.lock|pnpm-lock\.yaml)$/.test(path)).sort((a, b) => rank(a) - rank(b) || a.localeCompare(b));
14
+ }
15
+ function CodeView({
16
+ files,
17
+ theme,
18
+ onAsk
19
+ }) {
20
+ var _a;
21
+ const paths = useMemo(() => files ? orderedFiles(files) : [], [files]);
22
+ const [path, setPath] = useState(null);
23
+ useEffect(() => {
24
+ var _a2;
25
+ if (!path || !paths.includes(path)) setPath((_a2 = paths[0]) != null ? _a2 : null);
26
+ }, [paths, path]);
27
+ if (!files || paths.length === 0) {
28
+ return /* @__PURE__ */ jsx(CenteredState, { icon: "code-tags", title: "No code to show yet", body: "The source appears here once a build finishes.", theme, primary: {
29
+ label: "Ask the agent",
30
+ icon: "creation",
31
+ onPress: onAsk
32
+ }, testID: "webbuilder-code-empty" });
33
+ }
34
+ const source = path ? (_a = files[path]) != null ? _a : "" : "";
35
+ const shown = source.length > MAX_CHARS ? `${source.slice(0, MAX_CHARS)}
36
+
37
+ \u2026 (cut for the phone)` : source;
38
+ return /* @__PURE__ */ jsxs(View, { style: styles.fill, testID: "webbuilder-code", children: [
39
+ /* @__PURE__ */ jsx(ScrollView, { horizontal: true, showsHorizontalScrollIndicator: false, style: [styles.tabs, {
40
+ borderBottomColor: theme.border,
41
+ backgroundColor: theme.card
42
+ }], contentContainerStyle: styles.tabsContent, children: paths.map((p) => {
43
+ const active = p === path;
44
+ return /* @__PURE__ */ jsxs(Tap, { onPress: () => setPath(p), accessibilityRole: "tab", accessibilityState: {
45
+ selected: active
46
+ }, style: [styles.tab, {
47
+ borderColor: active ? theme.accent : theme.border
48
+ }, active && {
49
+ backgroundColor: theme.accentSoft
50
+ }], pressedStyle: DIM, children: [
51
+ /* @__PURE__ */ jsx(MaterialCommunityIcons, { name: "file-code-outline", size: 14, color: active ? theme.accent : theme.muted }),
52
+ /* @__PURE__ */ jsx(Text, { style: [styles.tabLabel, {
53
+ color: active ? theme.accent : theme.muted
54
+ }], numberOfLines: 1, children: p })
55
+ ] }, p);
56
+ }) }),
57
+ /* @__PURE__ */ jsx(ScrollView, { style: [styles.fill, {
58
+ backgroundColor: theme.code
59
+ }], children: /* @__PURE__ */ jsx(ScrollView, { horizontal: true, contentContainerStyle: styles.codePad, children: /* @__PURE__ */ jsx(Text, { selectable: true, style: [styles.code, {
60
+ color: theme.codeInk,
61
+ fontFamily: MONO
62
+ }], children: shown }) }) })
63
+ ] });
64
+ }
65
+ const styles = StyleSheet.create({
66
+ fill: {
67
+ flex: 1
68
+ },
69
+ tabs: {
70
+ flexGrow: 0,
71
+ borderBottomWidth: StyleSheet.hairlineWidth
72
+ },
73
+ tabsContent: {
74
+ gap: 6,
75
+ paddingHorizontal: 10,
76
+ paddingVertical: 8
77
+ },
78
+ tab: {
79
+ flexDirection: "row",
80
+ alignItems: "center",
81
+ gap: 5,
82
+ maxWidth: 220,
83
+ paddingHorizontal: 10,
84
+ paddingVertical: 6,
85
+ borderRadius: 8,
86
+ borderWidth: StyleSheet.hairlineWidth
87
+ },
88
+ tabLabel: {
89
+ fontSize: 12,
90
+ fontWeight: "600"
91
+ },
92
+ codePad: {
93
+ padding: 14
94
+ },
95
+ code: {
96
+ fontSize: 12,
97
+ lineHeight: 18
98
+ }
99
+ });export{CodeView,orderedFiles};//# sourceMappingURL=CodeView.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CodeView.js","sources":["../../../../src/features/webbuilder/components/CodeView.tsx"],"sourcesContent":["/**\n * The Code tab - the source the build posted (web CreationView `view='code'`),\n * read-only on a phone: the files as a list, one file at a time in a\n * monospace pane that scrolls both ways. Changes go through the agent.\n */\nimport React, { useEffect, useMemo, useState } from 'react';\nimport { Platform, ScrollView, StyleSheet, Text, View } from 'react-native';\nimport { MaterialCommunityIcons } from '@expo/vector-icons';\nimport { DIM, Tap } from '../../inbox/components/Tap';\nimport type { WebBuilderTheme } from '../webbuilderTheme';\nimport { CenteredState } from './ui';\n\n/** Very long files are cut so the pane stays responsive. */\nconst MAX_CHARS = 60_000;\nconst MONO = Platform.select({ ios: 'Menlo', android: 'monospace', default: 'monospace' });\n\n/** Source first (src/, app/, pages/), config after, lockfiles never. */\nexport function orderedFiles(files: Record<string, string>): string[] {\n const rank = (path: string) => {\n if (/(^|\\/)src\\//.test(path) || /(^|\\/)(app|pages|components)\\//.test(path)) return 0;\n if (/\\.(tsx?|jsx?|vue|svelte|css|html)$/.test(path)) return 1;\n return 2;\n };\n return Object.keys(files)\n .filter((path) => !/(^|\\/)(package-lock\\.json|yarn\\.lock|pnpm-lock\\.yaml)$/.test(path))\n .sort((a, b) => rank(a) - rank(b) || a.localeCompare(b));\n}\n\nexport function CodeView({\n files,\n theme,\n onAsk,\n}: {\n files: Record<string, string> | null;\n theme: WebBuilderTheme;\n onAsk: () => void;\n}) {\n const paths = useMemo(() => (files ? orderedFiles(files) : []), [files]);\n const [path, setPath] = useState<string | null>(null);\n useEffect(() => {\n if (!path || !paths.includes(path)) setPath(paths[0] ?? null);\n }, [paths, path]);\n\n if (!files || paths.length === 0) {\n return (\n <CenteredState\n icon=\"code-tags\"\n title=\"No code to show yet\"\n body=\"The source appears here once a build finishes.\"\n theme={theme}\n primary={{ label: 'Ask the agent', icon: 'creation', onPress: onAsk }}\n testID=\"webbuilder-code-empty\"\n />\n );\n }\n\n const source = path ? (files[path] ?? '') : '';\n const shown = source.length > MAX_CHARS ? `${source.slice(0, MAX_CHARS)}\\n\\n… (cut for the phone)` : source;\n\n return (\n <View style={styles.fill} testID=\"webbuilder-code\">\n <ScrollView\n horizontal\n showsHorizontalScrollIndicator={false}\n style={[styles.tabs, { borderBottomColor: theme.border, backgroundColor: theme.card }]}\n contentContainerStyle={styles.tabsContent}\n >\n {paths.map((p) => {\n const active = p === path;\n return (\n <Tap\n key={p}\n onPress={() => setPath(p)}\n accessibilityRole=\"tab\"\n accessibilityState={{ selected: active }}\n style={[\n styles.tab,\n { borderColor: active ? theme.accent : theme.border },\n active && { backgroundColor: theme.accentSoft },\n ]}\n pressedStyle={DIM}\n >\n <MaterialCommunityIcons\n name=\"file-code-outline\"\n size={14}\n color={active ? theme.accent : theme.muted}\n />\n <Text\n style={[styles.tabLabel, { color: active ? theme.accent : theme.muted }]}\n numberOfLines={1}\n >\n {p}\n </Text>\n </Tap>\n );\n })}\n </ScrollView>\n <ScrollView style={[styles.fill, { backgroundColor: theme.code }]}>\n <ScrollView horizontal contentContainerStyle={styles.codePad}>\n <Text selectable style={[styles.code, { color: theme.codeInk, fontFamily: MONO }]}>\n {shown}\n </Text>\n </ScrollView>\n </ScrollView>\n </View>\n );\n}\n\nconst styles = StyleSheet.create({\n fill: { flex: 1 },\n tabs: { flexGrow: 0, borderBottomWidth: StyleSheet.hairlineWidth },\n tabsContent: { gap: 6, paddingHorizontal: 10, paddingVertical: 8 },\n tab: {\n flexDirection: 'row',\n alignItems: 'center',\n gap: 5,\n maxWidth: 220,\n paddingHorizontal: 10,\n paddingVertical: 6,\n borderRadius: 8,\n borderWidth: StyleSheet.hairlineWidth,\n },\n tabLabel: { fontSize: 12, fontWeight: '600' },\n codePad: { padding: 14 },\n code: { fontSize: 12, lineHeight: 18 },\n});\n"],"names":["_a"],"mappings":"4SAaA,MAAM,SAAY,GAAA,GAAA;AAClB,MAAM,IAAA,GAAO,SAAS,MAAO,CAAA;AAAA,EAC3B,GAAK,EAAA,OAAA;AAAA,EACL,OAAS,EAAA,WAAA;AAAA,EACT,OAAS,EAAA;AACX,CAAC,CAAA;AAGM,SAAS,aAAa,KAAyC,EAAA;AACpE,EAAM,MAAA,IAAA,GAAO,CAAC,IAAiB,KAAA;AAC7B,IAAI,IAAA,aAAA,CAAc,KAAK,IAAI,CAAA,IAAK,iCAAiC,IAAK,CAAA,IAAI,GAAU,OAAA,CAAA;AACpF,IAAA,IAAI,oCAAqC,CAAA,IAAA,CAAK,IAAI,CAAA,EAAU,OAAA,CAAA;AAC5D,IAAO,OAAA,CAAA;AAAA,GACT;AACA,EAAO,OAAA,MAAA,CAAO,IAAK,CAAA,KAAK,CAAE,CAAA,MAAA,CAAO,UAAQ,CAAC,wDAAA,CAAyD,IAAK,CAAA,IAAI,CAAC,CAAA,CAAE,KAAK,CAAC,CAAA,EAAG,CAAM,KAAA,IAAA,CAAK,CAAC,CAAA,GAAI,IAAK,CAAA,CAAC,CAAK,IAAA,CAAA,CAAE,aAAc,CAAA,CAAC,CAAC,CAAA;AACvK;AACO,SAAS,QAAS,CAAA;AAAA,EACvB,KAAA;AAAA,EACA,KAAA;AAAA,EACA;AACF,CAIG,EAAA;AArCH,EAAA,IAAA,EAAA;AAsCE,EAAM,MAAA,KAAA,GAAQ,OAAQ,CAAA,MAAM,KAAQ,GAAA,YAAA,CAAa,KAAK,CAAA,GAAI,EAAC,EAAG,CAAC,KAAK,CAAC,CAAA;AACrE,EAAA,MAAM,CAAC,IAAA,EAAM,OAAO,CAAA,GAAI,SAAwB,IAAI,CAAA;AACpD,EAAA,SAAA,CAAU,MAAM;AAxClB,IAAAA,IAAAA,GAAAA;AAyCI,IAAA,IAAI,CAAC,IAAA,IAAQ,CAAC,KAAA,CAAM,SAAS,IAAI,CAAA,EAAWA,OAAAA,CAAAA,CAAAA,GAAAA,GAAA,KAAM,CAAA,CAAC,CAAP,KAAA,IAAA,GAAAA,MAAY,IAAI,CAAA;AAAA,GAC3D,EAAA,CAAC,KAAO,EAAA,IAAI,CAAC,CAAA;AAChB,EAAA,IAAI,CAAC,KAAA,IAAS,KAAM,CAAA,MAAA,KAAW,CAAG,EAAA;AAChC,IAAO,uBAAA,GAAA,CAAC,iBAAc,IAAK,EAAA,WAAA,EAAY,OAAM,qBAAsB,EAAA,IAAA,EAAK,gDAAiD,EAAA,KAAA,EAAc,OAAS,EAAA;AAAA,MAC9I,KAAO,EAAA,eAAA;AAAA,MACP,IAAM,EAAA,UAAA;AAAA,MACN,OAAS,EAAA;AAAA,KACX,EAAG,QAAO,uBAAwB,EAAA,CAAA;AAAA;AAEpC,EAAA,MAAM,SAAS,IAAO,GAAA,CAAA,EAAA,GAAA,KAAA,CAAM,IAAI,CAAA,KAAV,YAAe,EAAK,GAAA,EAAA;AAC1C,EAAM,MAAA,KAAA,GAAQ,OAAO,MAAS,GAAA,SAAA,GAAY,GAAG,MAAO,CAAA,KAAA,CAAM,CAAG,EAAA,SAAS,CAAC;;AAAA,0BAA8B,CAAA,GAAA,MAAA;AACrG,EAAA,4BAAQ,IAAK,EAAA,EAAA,KAAA,EAAO,MAAO,CAAA,IAAA,EAAM,QAAO,iBAC9B,EAAA,QAAA,EAAA;AAAA,oBAAC,GAAA,CAAA,UAAA,EAAA,EAAW,YAAU,IAAC,EAAA,8BAAA,EAAgC,OAAO,KAAO,EAAA,CAAC,OAAO,IAAM,EAAA;AAAA,MACzF,mBAAmB,KAAM,CAAA,MAAA;AAAA,MACzB,iBAAiB,KAAM,CAAA;AAAA,KACxB,CAAG,EAAA,qBAAA,EAAuB,OAAO,WACrB,EAAA,QAAA,EAAA,KAAA,CAAM,IAAI,CAAK,CAAA,KAAA;AACxB,MAAA,MAAM,SAAS,CAAM,KAAA,IAAA;AACrB,MAAO,uBAAA,IAAA,CAAC,OAAY,OAAS,EAAA,MAAM,QAAQ,CAAC,CAAA,EAAG,iBAAkB,EAAA,KAAA,EAAM,kBAAoB,EAAA;AAAA,QACzF,QAAU,EAAA;AAAA,OACT,EAAA,KAAA,EAAO,CAAC,MAAA,CAAO,GAAK,EAAA;AAAA,QACrB,WAAa,EAAA,MAAA,GAAS,KAAM,CAAA,MAAA,GAAS,KAAM,CAAA;AAAA,SAC1C,MAAU,IAAA;AAAA,QACX,iBAAiB,KAAM,CAAA;AAAA,OACxB,CAAG,EAAA,YAAA,EAAc,GACE,EAAA,QAAA,EAAA;AAAA,wBAAC,GAAA,CAAA,sBAAA,EAAA,EAAuB,IAAK,EAAA,mBAAA,EAAoB,IAAM,EAAA,EAAA,EAAI,OAAO,MAAS,GAAA,KAAA,CAAM,MAAS,GAAA,KAAA,CAAM,KAAO,EAAA,CAAA;AAAA,wBACtG,GAAA,CAAA,IAAA,EAAA,EAAK,KAAO,EAAA,CAAC,OAAO,QAAU,EAAA;AAAA,UAC/C,KAAO,EAAA,MAAA,GAAS,KAAM,CAAA,MAAA,GAAS,KAAM,CAAA;AAAA,SACtC,CAAA,EAAG,aAAe,EAAA,CAAA,EACI,QACL,EAAA,CAAA,EAAA;AAAA,OAAA,EAAA,EAZH,CAaD,CAAA;AAAA,KACjB,CACK,EAAA,CAAA;AAAA,oBACC,GAAA,CAAA,UAAA,EAAA,EAAW,KAAO,EAAA,CAAC,OAAO,IAAM,EAAA;AAAA,MACvC,iBAAiB,KAAM,CAAA;AAAA,KACxB,CACW,EAAA,QAAA,kBAAA,GAAA,CAAC,UAAW,EAAA,EAAA,UAAA,EAAU,MAAC,qBAAuB,EAAA,MAAA,CAAO,OACjD,EAAA,QAAA,kBAAA,GAAA,CAAC,QAAK,UAAU,EAAA,IAAA,EAAC,KAAO,EAAA,CAAC,OAAO,IAAM,EAAA;AAAA,MAChD,OAAO,KAAM,CAAA,OAAA;AAAA,MACb,UAAY,EAAA;AAAA,KACb,CAAA,EACgB,QACL,EAAA,KAAA,EAAA,CAAA,EACJ,CACJ,EAAA;AAAA,GACJ,EAAA,CAAA;AACR;AACA,MAAM,MAAA,GAAS,WAAW,MAAO,CAAA;AAAA,EAC/B,IAAM,EAAA;AAAA,IACJ,IAAM,EAAA;AAAA,GACR;AAAA,EACA,IAAM,EAAA;AAAA,IACJ,QAAU,EAAA,CAAA;AAAA,IACV,mBAAmB,UAAW,CAAA;AAAA,GAChC;AAAA,EACA,WAAa,EAAA;AAAA,IACX,GAAK,EAAA,CAAA;AAAA,IACL,iBAAmB,EAAA,EAAA;AAAA,IACnB,eAAiB,EAAA;AAAA,GACnB;AAAA,EACA,GAAK,EAAA;AAAA,IACH,aAAe,EAAA,KAAA;AAAA,IACf,UAAY,EAAA,QAAA;AAAA,IACZ,GAAK,EAAA,CAAA;AAAA,IACL,QAAU,EAAA,GAAA;AAAA,IACV,iBAAmB,EAAA,EAAA;AAAA,IACnB,eAAiB,EAAA,CAAA;AAAA,IACjB,YAAc,EAAA,CAAA;AAAA,IACd,aAAa,UAAW,CAAA;AAAA,GAC1B;AAAA,EACA,QAAU,EAAA;AAAA,IACR,QAAU,EAAA,EAAA;AAAA,IACV,UAAY,EAAA;AAAA,GACd;AAAA,EACA,OAAS,EAAA;AAAA,IACP,OAAS,EAAA;AAAA,GACX;AAAA,EACA,IAAM,EAAA;AAAA,IACJ,QAAU,EAAA,EAAA;AAAA,IACV,UAAY,EAAA;AAAA;AAEhB,CAAC,CAAA"}