@adminide-stack/yantra-mobile 12.0.44-alpha.1 → 12.0.44-alpha.15

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 (29) hide show
  1. package/lib/config/env-config.js +13 -0
  2. package/lib/config/env-config.js.map +1 -1
  3. package/lib/features/apps/mobileAppCatalog.js +2 -1
  4. package/lib/features/apps/mobileAppCatalog.js.map +1 -1
  5. package/lib/features/apps/resolveSurfaceUrl.js.map +1 -1
  6. package/lib/features/attachments/useImageAttachments.js.map +1 -1
  7. package/lib/features/canvas/canvasCore.js.map +1 -1
  8. package/lib/features/chat/MessageActionBar.js +56 -0
  9. package/lib/features/chat/MessageActionBar.js.map +1 -0
  10. package/lib/hooks/mintSurfaceSystemToken.js +86 -5
  11. package/lib/hooks/mintSurfaceSystemToken.js.map +1 -1
  12. package/lib/hooks/resolveSurfaceAuthGraphqlUrl.js +48 -0
  13. package/lib/hooks/resolveSurfaceAuthGraphqlUrl.js.map +1 -0
  14. package/lib/hooks/useCdecliChannel.js.map +1 -1
  15. package/lib/hooks/useKeyboardBottomOffset.js +22 -2
  16. package/lib/hooks/useKeyboardBottomOffset.js.map +1 -1
  17. package/lib/hooks/usePrerequisiteIds.js +2 -0
  18. package/lib/hooks/usePrerequisiteIds.js.map +1 -1
  19. package/lib/index.js +1 -1
  20. package/lib/index.js.map +1 -1
  21. package/lib/screens/Chat/index.js +22 -4
  22. package/lib/screens/Chat/index.js.map +1 -1
  23. package/lib/screens/Home/components/WebBuilderPrewarm.js +2 -2
  24. package/lib/screens/Home/components/WebBuilderPrewarm.js.map +1 -1
  25. package/lib/screens/WebBuilder/index.js +61 -21
  26. package/lib/screens/WebBuilder/index.js.map +1 -1
  27. package/lib/screens/WebBuilder/webbuilderWarmCache.js +19 -10
  28. package/lib/screens/WebBuilder/webbuilderWarmCache.js.map +1 -1
  29. package/package.json +4 -4
@@ -29,10 +29,23 @@ const baseConfig = cleanEnv(env, {
29
29
  WEB_BUILDER_URL: str({
30
30
  default: "http://localhost:5174/"
31
31
  }),
32
+ /** Standalone Design surface — local vite is 5174/4174 next to canvas 5175. */
33
+ DESIGN_SURFACE_URL: str({
34
+ default: "http://localhost:5174/"
35
+ }),
32
36
  /** Standalone Canvas surface embedded in the same WebView shell. */
33
37
  CANVAS_SURFACE_URL: str({
34
38
  default: "http://localhost:5175/"
35
39
  }),
40
+ /**
41
+ * GraphQL backend whose JWT keys the canvas index will verify.
42
+ * Must stay in lockstep with canvas-surface `.env.development`
43
+ * (`VITE_BACKEND_GRAPHQL_URL` / `VITE_SURFACE_INDEX_URL`). A locally
44
+ * minted token 401s against `surface-index-backend.yantra-app-v1`.
45
+ */
46
+ CANVAS_BACKEND_GRAPHQL_URL: str({
47
+ default: "https://ideback.yantra-app-v1.cdebase.dev/graphql"
48
+ }),
36
49
  /**
37
50
  * Origin template for every other deployed surface: `{slug}` is the app's
38
51
  * `surfaceSlug`. One var instead of 20+ near-identical URLs, and it keeps the
@@ -1 +1 @@
1
- {"version":3,"file":"env-config.js","sources":["../../src/config/env-config.ts"],"sourcesContent":["import { str, cleanEnv, bool, num } from 'envalid';\n\nconst env = (process as { APP_ENV?: NodeJS.ProcessEnv; env?: NodeJS.ProcessEnv }).APP_ENV || process.env;\n\nconst baseConfig = cleanEnv(env, {\n GRAPHQL_URL: str({ default: 'http://localhost:8080/graphql' }),\n CLIENT_URL: str({ default: 'http://localhost:3000' }),\n /** Standalone Web Builder surface embedded in the mobile WebView shell. */\n WEB_BUILDER_URL: str({ default: 'http://localhost:5174/' }),\n /** Standalone Canvas surface embedded in the same WebView shell. */\n CANVAS_SURFACE_URL: str({ default: 'http://localhost:5175/' }),\n /**\n * Origin template for every other deployed surface: `{slug}` is the app's\n * `surfaceSlug`. One var instead of 20+ near-identical URLs, and it keeps the\n * environment (dev cluster / prod) in one place.\n */\n SURFACE_URL_TEMPLATE: str({ default: 'https://{slug}-surface.yantra-app-v1.cdebase.dev/' }),\n APP_NAME: str({ default: 'Yantra' }),\n GROQ_API_KEY: str({ default: '' }),\n CDECLI_AGENT_ENDPOINT: str({ default: 'https://cdecli-agent.cdebase.dev' }),\n CDECLI_AGENT_AUTH_TOKEN: str({ default: '' }),\n /** Seconds to wait for CDeCLI stream completion before timing out (then fallback chat API runs). Min 15, max 600. */\n /** Aligned with web `useCdecliChannel` (120s). Mobile hook uses the same constant inline. */\n CDECLI_CHAT_RESPONSE_TIMEOUT_SEC: num({ default: 120 }),\n BACKEND_URL: str({ default: '' }),\n USE_PROXY_STAGING_GATEWAY: bool({ default: false }),\n PROXY_STAGING_GATEWAY_URL: str({ default: '' }),\n});\n\nfunction resolveAgentGatewayGraphqlUrl(): string {\n if (baseConfig.USE_PROXY_STAGING_GATEWAY && baseConfig.PROXY_STAGING_GATEWAY_URL) {\n return baseConfig.PROXY_STAGING_GATEWAY_URL;\n }\n if (baseConfig.GRAPHQL_URL) return baseConfig.GRAPHQL_URL;\n if (baseConfig.BACKEND_URL) return `${baseConfig.BACKEND_URL.replace(/\\/$/, '')}/graphql`;\n return '/graphql';\n}\n\nfunction resolveBaseWsUrl(graphqlUrl: string): string {\n const base = graphqlUrl.replace(/\\/graphql$/, '');\n return base.replace(/^http/, 'ws');\n}\n\nfunction resolveAgentGatewayWsUrl(graphqlUrl: string): string {\n return `${resolveBaseWsUrl(graphqlUrl)}/ws/openclaw-chat`;\n}\n\nconst agentGatewayGraphqlUrl = resolveAgentGatewayGraphqlUrl();\n\nconst clampedCdecliTimeoutSec = Math.min(600, Math.max(15, Math.round(baseConfig.CDECLI_CHAT_RESPONSE_TIMEOUT_SEC)));\n\nexport const config = {\n ...baseConfig,\n CDECLI_CHAT_RESPONSE_TIMEOUT_SEC: clampedCdecliTimeoutSec,\n AGENT_GATEWAY_GRAPHQL_URL: agentGatewayGraphqlUrl,\n AGENT_GATEWAY_WS_URL: resolveAgentGatewayWsUrl(agentGatewayGraphqlUrl),\n};\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;AACA,MAAM,GAAA,GAAO,OAGV,CAAA,OAAA,IAAW,OAAQ,CAAA,GAAA;AACtB,MAAM,UAAA,GAAa,SAAS,GAAK,EAAA;AAAA,EAC/B,aAAa,GAAI,CAAA;AAAA,IACf,OAAS,EAAA;AAAA,GACV,CAAA;AAAA,EACD,YAAY,GAAI,CAAA;AAAA,IACd,OAAS,EAAA;AAAA,GACV,CAAA;AAAA;AAAA,EAED,iBAAiB,GAAI,CAAA;AAAA,IACnB,OAAS,EAAA;AAAA,GACV,CAAA;AAAA;AAAA,EAED,oBAAoB,GAAI,CAAA;AAAA,IACtB,OAAS,EAAA;AAAA,GACV,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAMD,sBAAsB,GAAI,CAAA;AAAA,IACxB,OAAS,EAAA;AAAA,GACV,CAAA;AAAA,EACD,UAAU,GAAI,CAAA;AAAA,IACZ,OAAS,EAAA;AAAA,GACV,CAAA;AAAA,EACD,cAAc,GAAI,CAAA;AAAA,IAChB,OAAS,EAAA;AAAA,GACV,CAAA;AAAA,EACD,uBAAuB,GAAI,CAAA;AAAA,IACzB,OAAS,EAAA;AAAA,GACV,CAAA;AAAA,EACD,yBAAyB,GAAI,CAAA;AAAA,IAC3B,OAAS,EAAA;AAAA,GACV,CAAA;AAAA;AAAA;AAAA,EAGD,kCAAkC,GAAI,CAAA;AAAA,IACpC,OAAS,EAAA;AAAA,GACV,CAAA;AAAA,EACD,aAAa,GAAI,CAAA;AAAA,IACf,OAAS,EAAA;AAAA,GACV,CAAA;AAAA,EACD,2BAA2B,IAAK,CAAA;AAAA,IAC9B,OAAS,EAAA;AAAA,GACV,CAAA;AAAA,EACD,2BAA2B,GAAI,CAAA;AAAA,IAC7B,OAAS,EAAA;AAAA,GACV;AACH,CAAC,CAAA;AACD,SAAS,6BAAwC,GAAA;AAC/C,EAAI,IAAA,UAAA,CAAW,yBAA6B,IAAA,UAAA,CAAW,yBAA2B,EAAA;AAChF,IAAA,OAAO,UAAW,CAAA,yBAAA;AAAA;AAEpB,EAAI,IAAA,UAAA,CAAW,WAAa,EAAA,OAAO,UAAW,CAAA,WAAA;AAC9C,EAAI,IAAA,UAAA,CAAW,aAAoB,OAAA,CAAA,EAAG,WAAW,WAAY,CAAA,OAAA,CAAQ,KAAO,EAAA,EAAE,CAAC,CAAA,QAAA,CAAA;AAC/E,EAAO,OAAA,UAAA;AACT;AACA,SAAS,iBAAiB,UAA4B,EAAA;AACpD,EAAA,MAAM,IAAO,GAAA,UAAA,CAAW,OAAQ,CAAA,YAAA,EAAc,EAAE,CAAA;AAChD,EAAO,OAAA,IAAA,CAAK,OAAQ,CAAA,OAAA,EAAS,IAAI,CAAA;AACnC;AACA,SAAS,yBAAyB,UAA4B,EAAA;AAC5D,EAAO,OAAA,CAAA,EAAG,gBAAiB,CAAA,UAAU,CAAC,CAAA,iBAAA,CAAA;AACxC;AACA,MAAM,yBAAyB,6BAA8B,EAAA;AAC7D,MAAM,uBAA0B,GAAA,IAAA,CAAK,GAAI,CAAA,GAAA,EAAK,IAAK,CAAA,GAAA,CAAI,EAAI,EAAA,IAAA,CAAK,KAAM,CAAA,UAAA,CAAW,gCAAgC,CAAC,CAAC,CAAA;AACtG,MAAA,MAAA,GAAS,iCACjB,UADiB,CAAA,EAAA;AAAA,EAEpB,gCAAkC,EAAA,uBAAA;AAAA,EAClC,yBAA2B,EAAA,sBAAA;AAAA,EAC3B,oBAAA,EAAsB,yBAAyB,sBAAsB;AACvE,CAAA"}
1
+ {"version":3,"file":"env-config.js","sources":["../../src/config/env-config.ts"],"sourcesContent":["import { str, cleanEnv, bool, num } from 'envalid';\n\nconst env = (process as { APP_ENV?: NodeJS.ProcessEnv; env?: NodeJS.ProcessEnv }).APP_ENV || process.env;\n\nconst baseConfig = cleanEnv(env, {\n GRAPHQL_URL: str({ default: 'http://localhost:8080/graphql' }),\n CLIENT_URL: str({ default: 'http://localhost:3000' }),\n /** Standalone Web Builder surface embedded in the mobile WebView shell. */\n WEB_BUILDER_URL: str({ default: 'http://localhost:5174/' }),\n /** Standalone Design surface — local vite is 5174/4174 next to canvas 5175. */\n DESIGN_SURFACE_URL: str({ default: 'http://localhost:5174/' }),\n /** Standalone Canvas surface embedded in the same WebView shell. */\n CANVAS_SURFACE_URL: str({ default: 'http://localhost:5175/' }),\n /**\n * GraphQL backend whose JWT keys the canvas index will verify.\n * Must stay in lockstep with canvas-surface `.env.development`\n * (`VITE_BACKEND_GRAPHQL_URL` / `VITE_SURFACE_INDEX_URL`). A locally\n * minted token 401s against `surface-index-backend.yantra-app-v1`.\n */\n CANVAS_BACKEND_GRAPHQL_URL: str({ default: 'https://ideback.yantra-app-v1.cdebase.dev/graphql' }),\n /**\n * Origin template for every other deployed surface: `{slug}` is the app's\n * `surfaceSlug`. One var instead of 20+ near-identical URLs, and it keeps the\n * environment (dev cluster / prod) in one place.\n */\n SURFACE_URL_TEMPLATE: str({ default: 'https://{slug}-surface.yantra-app-v1.cdebase.dev/' }),\n APP_NAME: str({ default: 'Yantra' }),\n GROQ_API_KEY: str({ default: '' }),\n CDECLI_AGENT_ENDPOINT: str({ default: 'https://cdecli-agent.cdebase.dev' }),\n CDECLI_AGENT_AUTH_TOKEN: str({ default: '' }),\n /** Seconds to wait for CDeCLI stream completion before timing out (then fallback chat API runs). Min 15, max 600. */\n /** Aligned with web `useCdecliChannel` (120s). Mobile hook uses the same constant inline. */\n CDECLI_CHAT_RESPONSE_TIMEOUT_SEC: num({ default: 120 }),\n BACKEND_URL: str({ default: '' }),\n USE_PROXY_STAGING_GATEWAY: bool({ default: false }),\n PROXY_STAGING_GATEWAY_URL: str({ default: '' }),\n});\n\nfunction resolveAgentGatewayGraphqlUrl(): string {\n if (baseConfig.USE_PROXY_STAGING_GATEWAY && baseConfig.PROXY_STAGING_GATEWAY_URL) {\n return baseConfig.PROXY_STAGING_GATEWAY_URL;\n }\n if (baseConfig.GRAPHQL_URL) return baseConfig.GRAPHQL_URL;\n if (baseConfig.BACKEND_URL) return `${baseConfig.BACKEND_URL.replace(/\\/$/, '')}/graphql`;\n return '/graphql';\n}\n\nfunction resolveBaseWsUrl(graphqlUrl: string): string {\n const base = graphqlUrl.replace(/\\/graphql$/, '');\n return base.replace(/^http/, 'ws');\n}\n\nfunction resolveAgentGatewayWsUrl(graphqlUrl: string): string {\n return `${resolveBaseWsUrl(graphqlUrl)}/ws/openclaw-chat`;\n}\n\nconst agentGatewayGraphqlUrl = resolveAgentGatewayGraphqlUrl();\n\nconst clampedCdecliTimeoutSec = Math.min(600, Math.max(15, Math.round(baseConfig.CDECLI_CHAT_RESPONSE_TIMEOUT_SEC)));\n\nexport const config = {\n ...baseConfig,\n CDECLI_CHAT_RESPONSE_TIMEOUT_SEC: clampedCdecliTimeoutSec,\n AGENT_GATEWAY_GRAPHQL_URL: agentGatewayGraphqlUrl,\n AGENT_GATEWAY_WS_URL: resolveAgentGatewayWsUrl(agentGatewayGraphqlUrl),\n};\n"],"names":[],"mappings":"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"}
@@ -5,6 +5,7 @@ const MOBILE_APP_CATALOG = [{
5
5
  group: "Create",
6
6
  prompt: "Design a 10-slide pitch deck for a coffee subscription startup",
7
7
  nativeRoute: "webbuilder",
8
+ surfaceUrlKey: "DESIGN_SURFACE_URL",
8
9
  surfaceSlug: "design"
9
10
  }, {
10
11
  key: "canvas",
@@ -107,7 +108,7 @@ const MOBILE_APP_CATALOG = [{
107
108
  }, {
108
109
  key: "expenses",
109
110
  label: "Expenses",
110
- icon: "receipt-outline",
111
+ icon: "receipt",
111
112
  group: "Money",
112
113
  prompt: "Summarise my spend this month and flag anything unusual.",
113
114
  nativeRoute: "webbuilder",
@@ -1 +1 @@
1
- {"version":3,"file":"mobileAppCatalog.js","sources":["../../../src/features/apps/mobileAppCatalog.ts"],"sourcesContent":["import type { ComponentProps } from 'react';\nimport type { MaterialCommunityIcons } from '@expo/vector-icons';\n\ntype IconName = ComponentProps<typeof MaterialCommunityIcons>['name'];\n\nexport type AppGroupId = 'Create' | 'Automate' | 'Sell' | 'Money' | 'Team' | 'More';\n\nexport type MobileAppItem = {\n key: string;\n label: string;\n icon: IconName;\n group: AppGroupId;\n /** Prompt sent as the first chat turn when the app has no native screen. */\n prompt: string;\n /**\n * Which surface host opens this app, if it has one. `webbuilder` is the\n * historical name of the shared WebView surface shell (`screens/WebBuilder`),\n * which is not web-builder-specific: it takes a `url` route param, mints the\n * system token, injects the bridge + cdecli token, stamps `embed=mobile` and\n * owns the timeout/retry UI. Anything with a deployed surface SPA can use it.\n */\n nativeRoute?: 'webbuilder' | 'canvas-native';\n /**\n * Deployed surface SPA for this app, as the `<slug>-surface` name. Resolved\n * against SURFACE_URL_TEMPLATE so one env var covers every surface and stays\n * per-environment, instead of 20+ near-identical URLs in eas.json.\n *\n * The slug is the SURFACE's name, which is not always the tile key\n * (issue-ticket -> issues, shop -> shopping, notebook -> notebooklm).\n */\n surfaceSlug?: string;\n /**\n * Explicit env override, for the two surfaces that already had a dedicated\n * var and are the ones people actually run locally. Wins over `surfaceSlug`.\n */\n surfaceUrlKey?: 'WEB_BUILDER_URL' | 'CANVAS_SURFACE_URL';\n};\n\n/**\n * Mobile Apps launcher. Labels, groups, and prompts stay in lockstep with the\n * browser ChatTopBar Apps menu (`packages-modules/account/browser`).\n */\nexport const MOBILE_APP_CATALOG: readonly MobileAppItem[] = [\n {\n key: 'design',\n label: 'Design',\n icon: 'palette-outline',\n group: 'Create',\n prompt: 'Design a 10-slide pitch deck for a coffee subscription startup',\n nativeRoute: 'webbuilder',\n surfaceSlug: 'design',\n },\n {\n key: 'canvas',\n label: 'Canvas',\n icon: 'view-grid-outline',\n group: 'Create',\n prompt: 'Open a canvas and help me lay out the assets for this project.',\n nativeRoute: 'webbuilder',\n surfaceUrlKey: 'CANVAS_SURFACE_URL',\n surfaceSlug: 'canvas',\n },\n {\n key: 'canvas-native',\n label: 'Canvas Native',\n icon: 'vector-square',\n group: 'Create',\n prompt: 'Open a canvas and help me lay out the assets for this project.',\n // Renders through Module Federation: the viewer downloads from the\n // canvas-surface origin on open. First native surface; the classic\n // Canvas tile keeps the full web editor in the WebView host.\n nativeRoute: 'canvas-native',\n },\n {\n key: 'webbuilder',\n label: 'Web Builder',\n icon: 'application-outline',\n group: 'Create',\n prompt: 'Build a landing page for my bakery with a menu and a contact form',\n nativeRoute: 'webbuilder',\n surfaceUrlKey: 'WEB_BUILDER_URL',\n surfaceSlug: 'webbuilder',\n },\n {\n key: 'paper2poster',\n label: 'Paper 2 Poster',\n icon: 'presentation',\n group: 'Create',\n prompt: 'Turn this research paper into a conference poster I can edit.',\n nativeRoute: 'webbuilder',\n surfaceSlug: 'paper2poster',\n },\n {\n key: 'codezen',\n label: 'Codezen',\n icon: 'code-braces',\n group: 'Create',\n prompt: 'Clone my repository and fix the failing test',\n nativeRoute: 'webbuilder',\n surfaceSlug: 'codezen',\n },\n {\n key: 'character',\n label: 'Characters',\n icon: 'drama-masks',\n group: 'Create',\n prompt: 'Create a stoic philosophy mentor character I can chat with',\n nativeRoute: 'webbuilder',\n surfaceSlug: 'character',\n },\n {\n key: 'workflow',\n label: 'Workflow',\n icon: 'sitemap',\n group: 'Automate',\n prompt: 'Build a workflow that runs every morning and sends me a summary of what changed.',\n nativeRoute: 'webbuilder',\n surfaceSlug: 'workflow',\n },\n {\n key: 'computer',\n label: 'Computer',\n icon: 'monitor',\n group: 'Automate',\n prompt: 'Open a browser and compare the top 5 CRM tools with screenshots',\n },\n {\n key: 'crm',\n label: 'CRM',\n icon: 'account-group-outline',\n group: 'Sell',\n prompt: 'Look at my pipeline and tell me which deals are going cold and what I should send them.',\n nativeRoute: 'webbuilder',\n surfaceSlug: 'crm',\n },\n {\n key: 'salesiq',\n label: 'SalesIQ',\n icon: 'radar',\n group: 'Sell',\n prompt: 'Show me who is on my website right now',\n nativeRoute: 'webbuilder',\n surfaceSlug: 'salesiq',\n },\n {\n key: 'inbox',\n label: 'Inbox',\n icon: 'inbox-outline',\n group: 'Sell',\n prompt: 'Go through my inbox, tell me what actually needs a reply, and draft those replies.',\n nativeRoute: 'webbuilder',\n surfaceSlug: 'inbox',\n },\n {\n key: 'mailcampaign',\n label: 'Mail Campaigns',\n icon: 'bullhorn-outline',\n group: 'Sell',\n prompt: 'Draft an email campaign announcing what we shipped this month.',\n nativeRoute: 'webbuilder',\n surfaceSlug: 'mailcampaign',\n },\n {\n key: 'expenses',\n label: 'Expenses',\n icon: 'receipt-outline',\n group: 'Money',\n prompt: 'Summarise my spend this month and flag anything unusual.',\n nativeRoute: 'webbuilder',\n surfaceSlug: 'expenses',\n },\n {\n key: 'cards-wallet',\n label: 'Cards & Wallet',\n icon: 'credit-card-outline',\n group: 'Money',\n prompt: 'Issue a $200 virtual card for monthly ad spend',\n nativeRoute: 'webbuilder',\n surfaceSlug: 'cards-wallet',\n },\n {\n key: 'billing',\n label: 'Billing',\n icon: 'currency-usd',\n group: 'Money',\n prompt: 'Create an invoice for a $2,400 consulting project',\n nativeRoute: 'webbuilder',\n surfaceSlug: 'billing',\n },\n {\n key: 'kpi-motivation',\n label: 'KPI Motivation',\n icon: 'trophy-outline',\n group: 'Team',\n prompt: 'Set up a weekly sales KPI scoreboard for my team',\n nativeRoute: 'webbuilder',\n surfaceSlug: 'kpi-motivation',\n },\n {\n key: 'issue-ticket',\n label: 'Issue Ticket',\n icon: 'bug-outline',\n group: 'Team',\n prompt: 'Create a bug board and triage my open issues',\n nativeRoute: 'webbuilder',\n surfaceSlug: 'issues',\n },\n {\n key: 'notebook',\n label: 'Study',\n icon: 'school-outline',\n group: 'Team',\n prompt: 'Make a study guide from my lecture notes',\n nativeRoute: 'webbuilder',\n surfaceSlug: 'notebooklm',\n },\n {\n key: 'resume',\n label: 'Resume',\n icon: 'file-account-outline',\n group: 'Team',\n prompt: 'Draft a resume for a senior product designer with 8 years of experience',\n nativeRoute: 'webbuilder',\n surfaceSlug: 'resume',\n },\n {\n key: 'job-search',\n label: 'Job Search',\n icon: 'briefcase-outline',\n group: 'Team',\n prompt: 'Find remote senior backend engineer roles and tailor my resume for one',\n nativeRoute: 'webbuilder',\n surfaceSlug: 'job-search',\n },\n {\n key: 'shop',\n label: 'Shop',\n icon: 'cart-outline',\n group: 'More',\n prompt: 'Compare the best options for what I need and show me the tradeoffs.',\n nativeRoute: 'webbuilder',\n surfaceSlug: 'shopping',\n },\n {\n key: 'travel',\n label: 'Travel',\n icon: 'airplane',\n group: 'More',\n prompt: 'Plan a 4-day trip for me: flights, somewhere to stay, and what to do each day.',\n nativeRoute: 'webbuilder',\n surfaceSlug: 'travel',\n },\n {\n key: 'social-events',\n label: 'yantra.live',\n icon: 'ticket-outline',\n group: 'More',\n prompt: 'What concerts are happening nearby this weekend?',\n nativeRoute: 'webbuilder',\n surfaceSlug: 'social-events',\n },\n];\n\nexport const APP_GROUP_ORDER: readonly AppGroupId[] = ['Create', 'Automate', 'Sell', 'Money', 'Team', 'More'];\n\nexport function matchesAppQuery(label: string, query: string): boolean {\n const q = query.toLowerCase().replace(/[^a-z0-9]/g, '');\n if (!q) return true;\n return label\n .toLowerCase()\n .replace(/[^a-z0-9]/g, '')\n .includes(q);\n}\n\nexport function groupMobileApps(items: readonly MobileAppItem[], query: string) {\n const hits = items.filter((item) => matchesAppQuery(item.label, query));\n return APP_GROUP_ORDER.map((label) => ({\n label,\n items: hits.filter((item) => item.group === label),\n })).filter((group) => group.items.length > 0);\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"mobileAppCatalog.js","sources":["../../../src/features/apps/mobileAppCatalog.ts"],"sourcesContent":["import type { ComponentProps } from 'react';\nimport type { MaterialCommunityIcons } from '@expo/vector-icons';\n\ntype IconName = ComponentProps<typeof MaterialCommunityIcons>['name'];\n\nexport type AppGroupId = 'Create' | 'Automate' | 'Sell' | 'Money' | 'Team' | 'More';\n\nexport type MobileAppItem = {\n key: string;\n label: string;\n icon: IconName;\n group: AppGroupId;\n /** Prompt sent as the first chat turn when the app has no native screen. */\n prompt: string;\n /**\n * Which surface host opens this app, if it has one. `webbuilder` is the\n * historical name of the shared WebView surface shell (`screens/WebBuilder`),\n * which is not web-builder-specific: it takes a `url` route param, mints the\n * system token, injects the bridge + cdecli token, stamps `embed=mobile` and\n * owns the timeout/retry UI. Anything with a deployed surface SPA can use it.\n */\n nativeRoute?: 'webbuilder' | 'canvas-native';\n /**\n * Deployed surface SPA for this app, as the `<slug>-surface` name. Resolved\n * against SURFACE_URL_TEMPLATE so one env var covers every surface and stays\n * per-environment, instead of 20+ near-identical URLs in eas.json.\n *\n * The slug is the SURFACE's name, which is not always the tile key\n * (issue-ticket -> issues, shop -> shopping, notebook -> notebooklm).\n */\n surfaceSlug?: string;\n /**\n * Explicit env override, for the surfaces people actually run locally\n * (design 5174, canvas 5175, webbuilder). Wins over `surfaceSlug`.\n */\n surfaceUrlKey?: 'WEB_BUILDER_URL' | 'CANVAS_SURFACE_URL' | 'DESIGN_SURFACE_URL';\n};\n\n/**\n * Mobile Apps launcher. Labels, groups, and prompts stay in lockstep with the\n * browser ChatTopBar Apps menu (`packages-modules/account/browser`).\n */\nexport const MOBILE_APP_CATALOG: readonly MobileAppItem[] = [\n {\n key: 'design',\n label: 'Design',\n icon: 'palette-outline',\n group: 'Create',\n prompt: 'Design a 10-slide pitch deck for a coffee subscription startup',\n nativeRoute: 'webbuilder',\n surfaceUrlKey: 'DESIGN_SURFACE_URL',\n surfaceSlug: 'design',\n },\n {\n key: 'canvas',\n label: 'Canvas',\n icon: 'view-grid-outline',\n group: 'Create',\n prompt: 'Open a canvas and help me lay out the assets for this project.',\n nativeRoute: 'webbuilder',\n surfaceUrlKey: 'CANVAS_SURFACE_URL',\n surfaceSlug: 'canvas',\n },\n {\n key: 'canvas-native',\n label: 'Canvas Native',\n icon: 'vector-square',\n group: 'Create',\n prompt: 'Open a canvas and help me lay out the assets for this project.',\n // Renders through Module Federation: the viewer downloads from the\n // canvas-surface origin on open. First native surface; the classic\n // Canvas tile keeps the full web editor in the WebView host.\n nativeRoute: 'canvas-native',\n },\n {\n key: 'webbuilder',\n label: 'Web Builder',\n icon: 'application-outline',\n group: 'Create',\n prompt: 'Build a landing page for my bakery with a menu and a contact form',\n nativeRoute: 'webbuilder',\n surfaceUrlKey: 'WEB_BUILDER_URL',\n surfaceSlug: 'webbuilder',\n },\n {\n key: 'paper2poster',\n label: 'Paper 2 Poster',\n icon: 'presentation',\n group: 'Create',\n prompt: 'Turn this research paper into a conference poster I can edit.',\n nativeRoute: 'webbuilder',\n surfaceSlug: 'paper2poster',\n },\n {\n key: 'codezen',\n label: 'Codezen',\n icon: 'code-braces',\n group: 'Create',\n prompt: 'Clone my repository and fix the failing test',\n nativeRoute: 'webbuilder',\n surfaceSlug: 'codezen',\n },\n {\n key: 'character',\n label: 'Characters',\n icon: 'drama-masks',\n group: 'Create',\n prompt: 'Create a stoic philosophy mentor character I can chat with',\n nativeRoute: 'webbuilder',\n surfaceSlug: 'character',\n },\n {\n key: 'workflow',\n label: 'Workflow',\n icon: 'sitemap',\n group: 'Automate',\n prompt: 'Build a workflow that runs every morning and sends me a summary of what changed.',\n nativeRoute: 'webbuilder',\n surfaceSlug: 'workflow',\n },\n {\n key: 'computer',\n label: 'Computer',\n icon: 'monitor',\n group: 'Automate',\n prompt: 'Open a browser and compare the top 5 CRM tools with screenshots',\n },\n {\n key: 'crm',\n label: 'CRM',\n icon: 'account-group-outline',\n group: 'Sell',\n prompt: 'Look at my pipeline and tell me which deals are going cold and what I should send them.',\n nativeRoute: 'webbuilder',\n surfaceSlug: 'crm',\n },\n {\n key: 'salesiq',\n label: 'SalesIQ',\n icon: 'radar',\n group: 'Sell',\n prompt: 'Show me who is on my website right now',\n nativeRoute: 'webbuilder',\n surfaceSlug: 'salesiq',\n },\n {\n key: 'inbox',\n label: 'Inbox',\n icon: 'inbox-outline',\n group: 'Sell',\n prompt: 'Go through my inbox, tell me what actually needs a reply, and draft those replies.',\n nativeRoute: 'webbuilder',\n surfaceSlug: 'inbox',\n },\n {\n key: 'mailcampaign',\n label: 'Mail Campaigns',\n icon: 'bullhorn-outline',\n group: 'Sell',\n prompt: 'Draft an email campaign announcing what we shipped this month.',\n nativeRoute: 'webbuilder',\n surfaceSlug: 'mailcampaign',\n },\n {\n key: 'expenses',\n label: 'Expenses',\n icon: 'receipt',\n group: 'Money',\n prompt: 'Summarise my spend this month and flag anything unusual.',\n nativeRoute: 'webbuilder',\n surfaceSlug: 'expenses',\n },\n {\n key: 'cards-wallet',\n label: 'Cards & Wallet',\n icon: 'credit-card-outline',\n group: 'Money',\n prompt: 'Issue a $200 virtual card for monthly ad spend',\n nativeRoute: 'webbuilder',\n surfaceSlug: 'cards-wallet',\n },\n {\n key: 'billing',\n label: 'Billing',\n icon: 'currency-usd',\n group: 'Money',\n prompt: 'Create an invoice for a $2,400 consulting project',\n nativeRoute: 'webbuilder',\n surfaceSlug: 'billing',\n },\n {\n key: 'kpi-motivation',\n label: 'KPI Motivation',\n icon: 'trophy-outline',\n group: 'Team',\n prompt: 'Set up a weekly sales KPI scoreboard for my team',\n nativeRoute: 'webbuilder',\n surfaceSlug: 'kpi-motivation',\n },\n {\n key: 'issue-ticket',\n label: 'Issue Ticket',\n icon: 'bug-outline',\n group: 'Team',\n prompt: 'Create a bug board and triage my open issues',\n nativeRoute: 'webbuilder',\n surfaceSlug: 'issues',\n },\n {\n key: 'notebook',\n label: 'Study',\n icon: 'school-outline',\n group: 'Team',\n prompt: 'Make a study guide from my lecture notes',\n nativeRoute: 'webbuilder',\n surfaceSlug: 'notebooklm',\n },\n {\n key: 'resume',\n label: 'Resume',\n icon: 'file-account-outline',\n group: 'Team',\n prompt: 'Draft a resume for a senior product designer with 8 years of experience',\n nativeRoute: 'webbuilder',\n surfaceSlug: 'resume',\n },\n {\n key: 'job-search',\n label: 'Job Search',\n icon: 'briefcase-outline',\n group: 'Team',\n prompt: 'Find remote senior backend engineer roles and tailor my resume for one',\n nativeRoute: 'webbuilder',\n surfaceSlug: 'job-search',\n },\n {\n key: 'shop',\n label: 'Shop',\n icon: 'cart-outline',\n group: 'More',\n prompt: 'Compare the best options for what I need and show me the tradeoffs.',\n nativeRoute: 'webbuilder',\n surfaceSlug: 'shopping',\n },\n {\n key: 'travel',\n label: 'Travel',\n icon: 'airplane',\n group: 'More',\n prompt: 'Plan a 4-day trip for me: flights, somewhere to stay, and what to do each day.',\n nativeRoute: 'webbuilder',\n surfaceSlug: 'travel',\n },\n {\n key: 'social-events',\n label: 'yantra.live',\n icon: 'ticket-outline',\n group: 'More',\n prompt: 'What concerts are happening nearby this weekend?',\n nativeRoute: 'webbuilder',\n surfaceSlug: 'social-events',\n },\n];\n\nexport const APP_GROUP_ORDER: readonly AppGroupId[] = ['Create', 'Automate', 'Sell', 'Money', 'Team', 'More'];\n\nexport function matchesAppQuery(label: string, query: string): boolean {\n const q = query.toLowerCase().replace(/[^a-z0-9]/g, '');\n if (!q) return true;\n return label\n .toLowerCase()\n .replace(/[^a-z0-9]/g, '')\n .includes(q);\n}\n\nexport function groupMobileApps(items: readonly MobileAppItem[], query: string) {\n const hits = items.filter((item) => matchesAppQuery(item.label, query));\n return APP_GROUP_ORDER.map((label) => ({\n label,\n items: hits.filter((item) => item.group === label),\n })).filter((group) => group.items.length > 0);\n}\n"],"names":[],"mappings":"AAuCO,MAAM,qBAA+C,CAAC;AAAA,EAC3D,GAAK,EAAA,QAAA;AAAA,EACL,KAAO,EAAA,QAAA;AAAA,EACP,IAAM,EAAA,iBAAA;AAAA,EACN,KAAO,EAAA,QAAA;AAAA,EACP,MAAQ,EAAA,gEAAA;AAAA,EACR,WAAa,EAAA,YAAA;AAAA,EACb,aAAe,EAAA,oBAAA;AAAA,EACf,WAAa,EAAA;AACf,CAAG,EAAA;AAAA,EACD,GAAK,EAAA,QAAA;AAAA,EACL,KAAO,EAAA,QAAA;AAAA,EACP,IAAM,EAAA,mBAAA;AAAA,EACN,KAAO,EAAA,QAAA;AAAA,EACP,MAAQ,EAAA,gEAAA;AAAA,EACR,WAAa,EAAA,YAAA;AAAA,EACb,aAAe,EAAA,oBAAA;AAAA,EACf,WAAa,EAAA;AACf,CAAG,EAAA;AAAA,EACD,GAAK,EAAA,eAAA;AAAA,EACL,KAAO,EAAA,eAAA;AAAA,EACP,IAAM,EAAA,eAAA;AAAA,EACN,KAAO,EAAA,QAAA;AAAA,EACP,MAAQ,EAAA,gEAAA;AAAA;AAAA;AAAA;AAAA,EAIR,WAAa,EAAA;AACf,CAAG,EAAA;AAAA,EACD,GAAK,EAAA,YAAA;AAAA,EACL,KAAO,EAAA,aAAA;AAAA,EACP,IAAM,EAAA,qBAAA;AAAA,EACN,KAAO,EAAA,QAAA;AAAA,EACP,MAAQ,EAAA,mEAAA;AAAA,EACR,WAAa,EAAA,YAAA;AAAA,EACb,aAAe,EAAA,iBAAA;AAAA,EACf,WAAa,EAAA;AACf,CAAG,EAAA;AAAA,EACD,GAAK,EAAA,cAAA;AAAA,EACL,KAAO,EAAA,gBAAA;AAAA,EACP,IAAM,EAAA,cAAA;AAAA,EACN,KAAO,EAAA,QAAA;AAAA,EACP,MAAQ,EAAA,+DAAA;AAAA,EACR,WAAa,EAAA,YAAA;AAAA,EACb,WAAa,EAAA;AACf,CAAG,EAAA;AAAA,EACD,GAAK,EAAA,SAAA;AAAA,EACL,KAAO,EAAA,SAAA;AAAA,EACP,IAAM,EAAA,aAAA;AAAA,EACN,KAAO,EAAA,QAAA;AAAA,EACP,MAAQ,EAAA,8CAAA;AAAA,EACR,WAAa,EAAA,YAAA;AAAA,EACb,WAAa,EAAA;AACf,CAAG,EAAA;AAAA,EACD,GAAK,EAAA,WAAA;AAAA,EACL,KAAO,EAAA,YAAA;AAAA,EACP,IAAM,EAAA,aAAA;AAAA,EACN,KAAO,EAAA,QAAA;AAAA,EACP,MAAQ,EAAA,4DAAA;AAAA,EACR,WAAa,EAAA,YAAA;AAAA,EACb,WAAa,EAAA;AACf,CAAG,EAAA;AAAA,EACD,GAAK,EAAA,UAAA;AAAA,EACL,KAAO,EAAA,UAAA;AAAA,EACP,IAAM,EAAA,SAAA;AAAA,EACN,KAAO,EAAA,UAAA;AAAA,EACP,MAAQ,EAAA,kFAAA;AAAA,EACR,WAAa,EAAA,YAAA;AAAA,EACb,WAAa,EAAA;AACf,CAAG,EAAA;AAAA,EACD,GAAK,EAAA,UAAA;AAAA,EACL,KAAO,EAAA,UAAA;AAAA,EACP,IAAM,EAAA,SAAA;AAAA,EACN,KAAO,EAAA,UAAA;AAAA,EACP,MAAQ,EAAA;AACV,CAAG,EAAA;AAAA,EACD,GAAK,EAAA,KAAA;AAAA,EACL,KAAO,EAAA,KAAA;AAAA,EACP,IAAM,EAAA,uBAAA;AAAA,EACN,KAAO,EAAA,MAAA;AAAA,EACP,MAAQ,EAAA,yFAAA;AAAA,EACR,WAAa,EAAA,YAAA;AAAA,EACb,WAAa,EAAA;AACf,CAAG,EAAA;AAAA,EACD,GAAK,EAAA,SAAA;AAAA,EACL,KAAO,EAAA,SAAA;AAAA,EACP,IAAM,EAAA,OAAA;AAAA,EACN,KAAO,EAAA,MAAA;AAAA,EACP,MAAQ,EAAA,wCAAA;AAAA,EACR,WAAa,EAAA,YAAA;AAAA,EACb,WAAa,EAAA;AACf,CAAG,EAAA;AAAA,EACD,GAAK,EAAA,OAAA;AAAA,EACL,KAAO,EAAA,OAAA;AAAA,EACP,IAAM,EAAA,eAAA;AAAA,EACN,KAAO,EAAA,MAAA;AAAA,EACP,MAAQ,EAAA,oFAAA;AAAA,EACR,WAAa,EAAA,YAAA;AAAA,EACb,WAAa,EAAA;AACf,CAAG,EAAA;AAAA,EACD,GAAK,EAAA,cAAA;AAAA,EACL,KAAO,EAAA,gBAAA;AAAA,EACP,IAAM,EAAA,kBAAA;AAAA,EACN,KAAO,EAAA,MAAA;AAAA,EACP,MAAQ,EAAA,gEAAA;AAAA,EACR,WAAa,EAAA,YAAA;AAAA,EACb,WAAa,EAAA;AACf,CAAG,EAAA;AAAA,EACD,GAAK,EAAA,UAAA;AAAA,EACL,KAAO,EAAA,UAAA;AAAA,EACP,IAAM,EAAA,SAAA;AAAA,EACN,KAAO,EAAA,OAAA;AAAA,EACP,MAAQ,EAAA,0DAAA;AAAA,EACR,WAAa,EAAA,YAAA;AAAA,EACb,WAAa,EAAA;AACf,CAAG,EAAA;AAAA,EACD,GAAK,EAAA,cAAA;AAAA,EACL,KAAO,EAAA,gBAAA;AAAA,EACP,IAAM,EAAA,qBAAA;AAAA,EACN,KAAO,EAAA,OAAA;AAAA,EACP,MAAQ,EAAA,gDAAA;AAAA,EACR,WAAa,EAAA,YAAA;AAAA,EACb,WAAa,EAAA;AACf,CAAG,EAAA;AAAA,EACD,GAAK,EAAA,SAAA;AAAA,EACL,KAAO,EAAA,SAAA;AAAA,EACP,IAAM,EAAA,cAAA;AAAA,EACN,KAAO,EAAA,OAAA;AAAA,EACP,MAAQ,EAAA,mDAAA;AAAA,EACR,WAAa,EAAA,YAAA;AAAA,EACb,WAAa,EAAA;AACf,CAAG,EAAA;AAAA,EACD,GAAK,EAAA,gBAAA;AAAA,EACL,KAAO,EAAA,gBAAA;AAAA,EACP,IAAM,EAAA,gBAAA;AAAA,EACN,KAAO,EAAA,MAAA;AAAA,EACP,MAAQ,EAAA,kDAAA;AAAA,EACR,WAAa,EAAA,YAAA;AAAA,EACb,WAAa,EAAA;AACf,CAAG,EAAA;AAAA,EACD,GAAK,EAAA,cAAA;AAAA,EACL,KAAO,EAAA,cAAA;AAAA,EACP,IAAM,EAAA,aAAA;AAAA,EACN,KAAO,EAAA,MAAA;AAAA,EACP,MAAQ,EAAA,8CAAA;AAAA,EACR,WAAa,EAAA,YAAA;AAAA,EACb,WAAa,EAAA;AACf,CAAG,EAAA;AAAA,EACD,GAAK,EAAA,UAAA;AAAA,EACL,KAAO,EAAA,OAAA;AAAA,EACP,IAAM,EAAA,gBAAA;AAAA,EACN,KAAO,EAAA,MAAA;AAAA,EACP,MAAQ,EAAA,0CAAA;AAAA,EACR,WAAa,EAAA,YAAA;AAAA,EACb,WAAa,EAAA;AACf,CAAG,EAAA;AAAA,EACD,GAAK,EAAA,QAAA;AAAA,EACL,KAAO,EAAA,QAAA;AAAA,EACP,IAAM,EAAA,sBAAA;AAAA,EACN,KAAO,EAAA,MAAA;AAAA,EACP,MAAQ,EAAA,yEAAA;AAAA,EACR,WAAa,EAAA,YAAA;AAAA,EACb,WAAa,EAAA;AACf,CAAG,EAAA;AAAA,EACD,GAAK,EAAA,YAAA;AAAA,EACL,KAAO,EAAA,YAAA;AAAA,EACP,IAAM,EAAA,mBAAA;AAAA,EACN,KAAO,EAAA,MAAA;AAAA,EACP,MAAQ,EAAA,wEAAA;AAAA,EACR,WAAa,EAAA,YAAA;AAAA,EACb,WAAa,EAAA;AACf,CAAG,EAAA;AAAA,EACD,GAAK,EAAA,MAAA;AAAA,EACL,KAAO,EAAA,MAAA;AAAA,EACP,IAAM,EAAA,cAAA;AAAA,EACN,KAAO,EAAA,MAAA;AAAA,EACP,MAAQ,EAAA,qEAAA;AAAA,EACR,WAAa,EAAA,YAAA;AAAA,EACb,WAAa,EAAA;AACf,CAAG,EAAA;AAAA,EACD,GAAK,EAAA,QAAA;AAAA,EACL,KAAO,EAAA,QAAA;AAAA,EACP,IAAM,EAAA,UAAA;AAAA,EACN,KAAO,EAAA,MAAA;AAAA,EACP,MAAQ,EAAA,gFAAA;AAAA,EACR,WAAa,EAAA,YAAA;AAAA,EACb,WAAa,EAAA;AACf,CAAG,EAAA;AAAA,EACD,GAAK,EAAA,eAAA;AAAA,EACL,KAAO,EAAA,aAAA;AAAA,EACP,IAAM,EAAA,gBAAA;AAAA,EACN,KAAO,EAAA,MAAA;AAAA,EACP,MAAQ,EAAA,kDAAA;AAAA,EACR,WAAa,EAAA,YAAA;AAAA,EACb,WAAa,EAAA;AACf,CAAC;AACM,MAAM,kBAAyC,CAAC,QAAA,EAAU,YAAY,MAAQ,EAAA,OAAA,EAAS,QAAQ,MAAM;AAC5F,SAAA,eAAA,CAAgB,OAAe,KAAwB,EAAA;AACrE,EAAA,MAAM,IAAI,KAAM,CAAA,WAAA,EAAc,CAAA,OAAA,CAAQ,cAAc,EAAE,CAAA;AACtD,EAAI,IAAA,CAAC,GAAU,OAAA,IAAA;AACf,EAAO,OAAA,KAAA,CAAM,aAAc,CAAA,OAAA,CAAQ,cAAc,EAAE,CAAA,CAAE,SAAS,CAAC,CAAA;AACjE;AACgB,SAAA,eAAA,CAAgB,OAAiC,KAAe,EAAA;AAC9E,EAAM,MAAA,IAAA,GAAO,MAAM,MAAO,CAAA,CAAA,IAAA,KAAQ,gBAAgB,IAAK,CAAA,KAAA,EAAO,KAAK,CAAC,CAAA;AACpE,EAAO,OAAA,eAAA,CAAgB,IAAI,CAAU,KAAA,MAAA;AAAA,IACnC,KAAA;AAAA,IACA,OAAO,IAAK,CAAA,MAAA,CAAO,CAAQ,IAAA,KAAA,IAAA,CAAK,UAAU,KAAK;AAAA,IAC/C,CAAE,CAAA,MAAA,CAAO,WAAS,KAAM,CAAA,KAAA,CAAM,SAAS,CAAC,CAAA;AAC5C"}
@@ -1 +1 @@
1
- {"version":3,"file":"resolveSurfaceUrl.js","sources":["../../../src/features/apps/resolveSurfaceUrl.ts"],"sourcesContent":["/**\n * Which URL the shared WebView surface shell should open for a launcher tile.\n *\n * Explicit env key wins (the two surfaces with a dedicated var, which are the\n * ones run locally), then the `{slug}` template that covers every deployed\n * surface. Returning undefined means \"no surface\" — the caller falls back to\n * seeding the tile's chat prompt, which is what every tile did before.\n */\nimport type { MobileAppItem } from './mobileAppCatalog';\n\nexport interface SurfaceUrlConfig {\n WEB_BUILDER_URL?: string;\n CANVAS_SURFACE_URL?: string;\n SURFACE_URL_TEMPLATE?: string;\n}\n\nexport function resolveSurfaceUrl(app: MobileAppItem, config: SurfaceUrlConfig): string | undefined {\n // Only the shared WebView shell needs a URL - a tile with its own native\n // screen (nativeRoute 'canvas-native') resolves nothing by design.\n if (app.nativeRoute !== 'webbuilder') return undefined;\n\n if (app.surfaceUrlKey) {\n const explicit = config[app.surfaceUrlKey]?.trim();\n\n if (explicit) return explicit;\n }\n\n const template = config.SURFACE_URL_TEMPLATE?.trim();\n\n if (!template || !app.surfaceSlug) return undefined;\n // A template without the placeholder would silently point every surface at\n // the same origin, which is worse than opening none.\n if (!template.includes('{slug}')) return undefined;\n\n return template.replace(/\\{slug\\}/g, app.surfaceSlug);\n}\n"],"names":[],"mappings":"AAcgB,SAAA,iBAAA,CAAkB,KAAoB,MAA8C,EAAA;AAdpG,EAAA,IAAA,EAAA,EAAA,EAAA;AAiBE,EAAI,IAAA,GAAA,CAAI,WAAgB,KAAA,YAAA,EAAqB,OAAA,MAAA;AAC7C,EAAA,IAAI,IAAI,aAAe,EAAA;AACrB,IAAA,MAAM,QAAW,GAAA,CAAA,EAAA,GAAA,MAAA,CAAO,GAAI,CAAA,aAAa,MAAxB,IAA2B,GAAA,MAAA,GAAA,EAAA,CAAA,IAAA,EAAA;AAC5C,IAAA,IAAI,UAAiB,OAAA,QAAA;AAAA;AAEvB,EAAM,MAAA,QAAA,GAAA,CAAW,EAAO,GAAA,MAAA,CAAA,oBAAA,KAAP,IAA6B,GAAA,MAAA,GAAA,EAAA,CAAA,IAAA,EAAA;AAC9C,EAAA,IAAI,CAAC,QAAA,IAAY,CAAC,GAAA,CAAI,aAAoB,OAAA,MAAA;AAG1C,EAAA,IAAI,CAAC,QAAA,CAAS,QAAS,CAAA,QAAQ,GAAU,OAAA,MAAA;AACzC,EAAA,OAAO,QAAS,CAAA,OAAA,CAAQ,WAAa,EAAA,GAAA,CAAI,WAAW,CAAA;AACtD"}
1
+ {"version":3,"file":"resolveSurfaceUrl.js","sources":["../../../src/features/apps/resolveSurfaceUrl.ts"],"sourcesContent":["/**\n * Which URL the shared WebView surface shell should open for a launcher tile.\n *\n * Explicit env key wins (the surfaces with a dedicated var, which are the\n * ones run locally), then the `{slug}` template that covers every deployed\n * surface. Returning undefined means \"no surface\" — the caller falls back to\n * seeding the tile's chat prompt, which is what every tile did before.\n */\nimport type { MobileAppItem } from './mobileAppCatalog';\n\nexport interface SurfaceUrlConfig {\n WEB_BUILDER_URL?: string;\n CANVAS_SURFACE_URL?: string;\n DESIGN_SURFACE_URL?: string;\n SURFACE_URL_TEMPLATE?: string;\n}\n\nexport function resolveSurfaceUrl(app: MobileAppItem, config: SurfaceUrlConfig): string | undefined {\n // Only the shared WebView shell needs a URL - a tile with its own native\n // screen (nativeRoute 'canvas-native') resolves nothing by design.\n if (app.nativeRoute !== 'webbuilder') return undefined;\n\n if (app.surfaceUrlKey) {\n const explicit = config[app.surfaceUrlKey]?.trim();\n\n if (explicit) return explicit;\n }\n\n const template = config.SURFACE_URL_TEMPLATE?.trim();\n\n if (!template || !app.surfaceSlug) return undefined;\n // A template without the placeholder would silently point every surface at\n // the same origin, which is worse than opening none.\n if (!template.includes('{slug}')) return undefined;\n\n return template.replace(/\\{slug\\}/g, app.surfaceSlug);\n}\n"],"names":[],"mappings":"AAegB,SAAA,iBAAA,CAAkB,KAAoB,MAA8C,EAAA;AAfpG,EAAA,IAAA,EAAA,EAAA,EAAA;AAkBE,EAAI,IAAA,GAAA,CAAI,WAAgB,KAAA,YAAA,EAAqB,OAAA,MAAA;AAC7C,EAAA,IAAI,IAAI,aAAe,EAAA;AACrB,IAAA,MAAM,QAAW,GAAA,CAAA,EAAA,GAAA,MAAA,CAAO,GAAI,CAAA,aAAa,MAAxB,IAA2B,GAAA,MAAA,GAAA,EAAA,CAAA,IAAA,EAAA;AAC5C,IAAA,IAAI,UAAiB,OAAA,QAAA;AAAA;AAEvB,EAAM,MAAA,QAAA,GAAA,CAAW,EAAO,GAAA,MAAA,CAAA,oBAAA,KAAP,IAA6B,GAAA,MAAA,GAAA,EAAA,CAAA,IAAA,EAAA;AAC9C,EAAA,IAAI,CAAC,QAAA,IAAY,CAAC,GAAA,CAAI,aAAoB,OAAA,MAAA;AAG1C,EAAA,IAAI,CAAC,QAAA,CAAS,QAAS,CAAA,QAAQ,GAAU,OAAA,MAAA;AACzC,EAAA,OAAO,QAAS,CAAA,OAAA,CAAQ,WAAa,EAAA,GAAA,CAAI,WAAW,CAAA;AACtD"}
@@ -1 +1 @@
1
- {"version":3,"file":"useImageAttachments.js","sources":["../../../src/features/attachments/useImageAttachments.ts"],"sourcesContent":["/**\n * Image attachments for the native composer.\n *\n * The transport was already there — `useChatStream.sendMessage(content,\n * attachments)` carries `MessageAttachment[]` and the gateway media lane is\n * covered by `aiwork/e2e-tests/gateway-media-upload.spec.ts`. What was missing\n * was any way to produce one on a phone: the composer shipped with\n * `image`/`camera`/`attach` hard-disabled, and nothing in the mobile package\n * touched `expo-image-picker` even though it is a dependency and the photo and\n * camera permission strings are already declared in `app.json`.\n *\n * Payload size is the whole game here. A modern iPhone photo is ~3-5 MB and\n * ~4000px wide; base64 inflates that by a third, and it travels inline on the\n * chat message. So we ask the picker to downscale and re-encode before we ever\n * see the bytes, then hard-reject anything still oversized rather than let a\n * send hang on a slow uplink.\n */\nimport { useCallback, useMemo, useState } from 'react';\nimport type { MessageAttachment } from '../../hooks/useChatStream';\n\n/** Longest edge, in px, we send. Plenty for a model to read; ~10x smaller than source. */\nconst MAX_EDGE = 1280;\n/** JPEG quality handed to the picker (0-1). */\nconst PICKER_QUALITY = 0.7;\n/** Refuse anything above this after re-encoding — a send is better failed than hung. */\nconst MAX_BYTES = 4 * 1024 * 1024;\n/** Nothing sensible is this small; treat as a failed decode. */\nconst MIN_BYTES = 64;\n\nexport interface ImageAttachmentsApi {\n /** Attachments staged for the next send. */\n pending: MessageAttachment[];\n /** True while the picker/encode is in flight, for the toolbar spinner. */\n busy: boolean;\n /** Human-readable failure for the composer's error banner, or null. */\n error: string | null;\n pickFromLibrary: () => Promise<void>;\n captureFromCamera: () => Promise<void>;\n remove: (id: string) => void;\n clear: () => void;\n /** Pass to `sendMessage` — undefined when empty, which is what it expects. */\n forSend: () => MessageAttachment[] | undefined;\n}\n\ntype PickedAsset = {\n uri?: string | null;\n base64?: string | null;\n mimeType?: string | null;\n fileName?: string | null;\n fileSize?: number | null;\n width?: number | null;\n height?: number | null;\n};\n\n/** Base64 decodes to 3 bytes per 4 chars, minus `=` padding. */\nfunction base64Bytes(base64: string): number {\n const padding = base64.endsWith('==') ? 2 : base64.endsWith('=') ? 1 : 0;\n return Math.floor((base64.length * 3) / 4) - padding;\n}\n\nfunction toAttachment(asset: PickedAsset, index: number): MessageAttachment | null {\n const base64 = asset.base64?.trim();\n if (!base64) return null;\n const size = base64Bytes(base64);\n if (size < MIN_BYTES || size > MAX_BYTES) return null;\n const mimeType = asset.mimeType?.trim() || 'image/jpeg';\n const name = asset.fileName?.trim() || `image-${Date.now()}-${index}.jpg`;\n return {\n // Local id only — the gateway assigns its own.\n id: `img-${Date.now()}-${index}`,\n name,\n type: 'file',\n mimeType,\n dataUrl: `data:${mimeType};base64,${base64}`,\n size,\n };\n}\n\n/**\n * Lazily required so a unit test or a non-Expo bundle importing this hook does\n * not hard-fail on the native module.\n */\nfunction loadPicker() {\n // eslint-disable-next-line @typescript-eslint/no-require-imports, global-require\n return require('expo-image-picker') as typeof import('expo-image-picker');\n}\n\nexport function useImageAttachments(): ImageAttachmentsApi {\n const [pending, setPending] = useState<MessageAttachment[]>([]);\n const [busy, setBusy] = useState(false);\n const [error, setError] = useState<string | null>(null);\n\n const run = useCallback(async (mode: 'library' | 'camera') => {\n setError(null);\n setBusy(true);\n try {\n const picker = loadPicker();\n\n const permission =\n mode === 'camera'\n ? await picker.requestCameraPermissionsAsync()\n : await picker.requestMediaLibraryPermissionsAsync();\n if (!permission.granted) {\n setError(\n mode === 'camera'\n ? 'Camera access is off for Yantra. Turn it on in Settings to attach a photo.'\n : 'Photo access is off for Yantra. Turn it on in Settings to attach an image.',\n );\n return;\n }\n\n const options = {\n // `base64` is what the message carries; `quality` + the picker's\n // own resize keep it small enough to travel inline.\n base64: true,\n quality: PICKER_QUALITY,\n allowsMultipleSelection: mode === 'library',\n mediaTypes: ['images'] as const,\n // Selection limit mirrors MAX_BYTES: four 1280px JPEGs still fit\n // comfortably inside a single turn.\n selectionLimit: 4,\n exif: false,\n };\n\n const result =\n mode === 'camera'\n ? await picker.launchCameraAsync({ ...options, allowsMultipleSelection: false })\n : await picker.launchImageLibraryAsync(options);\n\n if (result.canceled) return;\n\n const mapped = (result.assets ?? [])\n .map((asset, i) => toAttachment(asset as PickedAsset, i))\n .filter((a): a is MessageAttachment => a !== null);\n\n if (mapped.length === 0) {\n setError('That image was too large to attach. Try a smaller one.');\n return;\n }\n setPending((prev) => [...prev, ...mapped]);\n } catch (e) {\n setError(e instanceof Error ? e.message : 'Could not attach that image.');\n } finally {\n setBusy(false);\n }\n }, []);\n\n const pickFromLibrary = useCallback(() => run('library'), [run]);\n const captureFromCamera = useCallback(() => run('camera'), [run]);\n const remove = useCallback((id: string) => setPending((prev) => prev.filter((a) => a.id !== id)), []);\n const clear = useCallback(() => setPending([]), []);\n const forSend = useCallback(() => (pending.length > 0 ? pending : undefined), [pending]);\n\n return useMemo(\n () => ({ pending, busy, error, pickFromLibrary, captureFromCamera, remove, clear, forSend }),\n [pending, busy, error, pickFromLibrary, captureFromCamera, remove, clear, forSend],\n );\n}\n\nexport const IMAGE_ATTACHMENT_LIMITS = { MAX_EDGE, MAX_BYTES, PICKER_QUALITY } as const;\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"useImageAttachments.js","sources":["../../../src/features/attachments/useImageAttachments.ts"],"sourcesContent":["/**\n * Image attachments for the native composer.\n *\n * The transport was already there — `useChatStream.sendMessage(content,\n * attachments)` carries `MessageAttachment[]` and the gateway media lane is\n * covered by `aiwork/e2e-tests/gateway-media-upload.spec.ts`. What was missing\n * was any way to produce one on a phone: the composer shipped with\n * `image`/`camera`/`attach` hard-disabled, and nothing in the mobile package\n * touched `expo-image-picker` even though it is a dependency and the photo and\n * camera permission strings are already declared in `app.json`.\n *\n * Payload size is the whole game here. A modern iPhone photo is ~3-5 MB and\n * ~4000px wide; base64 inflates that by a third, and it travels inline on the\n * chat message. So we ask the picker to downscale and re-encode before we ever\n * see the bytes, then hard-reject anything still oversized rather than let a\n * send hang on a slow uplink.\n */\nimport { useCallback, useMemo, useState } from 'react';\nimport type { MessageAttachment } from '../../hooks/useChatStream';\n\n/** Longest edge, in px, we send. Plenty for a model to read; ~10x smaller than source. */\nconst MAX_EDGE = 1280;\n/** JPEG quality handed to the picker (0-1). */\nconst PICKER_QUALITY = 0.7;\n/** Refuse anything above this after re-encoding — a send is better failed than hung. */\nconst MAX_BYTES = 4 * 1024 * 1024;\n/** Nothing sensible is this small; treat as a failed decode. */\nconst MIN_BYTES = 64;\n\nexport interface ImageAttachmentsApi {\n /** Attachments staged for the next send. */\n pending: MessageAttachment[];\n /** True while the picker/encode is in flight, for the toolbar spinner. */\n busy: boolean;\n /** Human-readable failure for the composer's error banner, or null. */\n error: string | null;\n pickFromLibrary: () => Promise<void>;\n captureFromCamera: () => Promise<void>;\n remove: (id: string) => void;\n clear: () => void;\n /** Pass to `sendMessage` — undefined when empty, which is what it expects. */\n forSend: () => MessageAttachment[] | undefined;\n}\n\ntype PickedAsset = {\n uri?: string | null;\n base64?: string | null;\n mimeType?: string | null;\n fileName?: string | null;\n fileSize?: number | null;\n width?: number | null;\n height?: number | null;\n};\n\n/** Base64 decodes to 3 bytes per 4 chars, minus `=` padding. */\nfunction base64Bytes(base64: string): number {\n const padding = base64.endsWith('==') ? 2 : base64.endsWith('=') ? 1 : 0;\n return Math.floor((base64.length * 3) / 4) - padding;\n}\n\nfunction toAttachment(asset: PickedAsset, index: number): MessageAttachment | null {\n const base64 = asset.base64?.trim();\n if (!base64) return null;\n const size = base64Bytes(base64);\n if (size < MIN_BYTES || size > MAX_BYTES) return null;\n const mimeType = asset.mimeType?.trim() || 'image/jpeg';\n const name = asset.fileName?.trim() || `image-${Date.now()}-${index}.jpg`;\n return {\n // Local id only — the gateway assigns its own.\n id: `img-${Date.now()}-${index}`,\n name,\n type: 'file',\n mimeType,\n dataUrl: `data:${mimeType};base64,${base64}`,\n size,\n };\n}\n\n/**\n * Lazily required so a unit test or a non-Expo bundle importing this hook does\n * not hard-fail on the native module.\n */\nfunction loadPicker() {\n // eslint-disable-next-line @typescript-eslint/no-require-imports, global-require\n return require('expo-image-picker') as typeof import('expo-image-picker');\n}\n\nexport function useImageAttachments(): ImageAttachmentsApi {\n const [pending, setPending] = useState<MessageAttachment[]>([]);\n const [busy, setBusy] = useState(false);\n const [error, setError] = useState<string | null>(null);\n\n const run = useCallback(async (mode: 'library' | 'camera') => {\n setError(null);\n setBusy(true);\n try {\n const picker = loadPicker();\n\n const permission =\n mode === 'camera'\n ? await picker.requestCameraPermissionsAsync()\n : await picker.requestMediaLibraryPermissionsAsync();\n if (!permission.granted) {\n setError(\n mode === 'camera'\n ? 'Camera access is off for Yantra. Turn it on in Settings to attach a photo.'\n : 'Photo access is off for Yantra. Turn it on in Settings to attach an image.',\n );\n return;\n }\n\n const options: import('expo-image-picker').ImagePickerOptions = {\n // `base64` is what the message carries; `quality` + the picker's\n // own resize keep it small enough to travel inline.\n base64: true,\n quality: PICKER_QUALITY,\n allowsMultipleSelection: mode === 'library',\n mediaTypes: ['images'],\n // Selection limit mirrors MAX_BYTES: four 1280px JPEGs still fit\n // comfortably inside a single turn.\n selectionLimit: 4,\n exif: false,\n };\n\n const result =\n mode === 'camera'\n ? await picker.launchCameraAsync({ ...options, allowsMultipleSelection: false })\n : await picker.launchImageLibraryAsync(options);\n\n if (result.canceled) return;\n\n const mapped = (result.assets ?? [])\n .map((asset, i) => toAttachment(asset as PickedAsset, i))\n .filter((a): a is MessageAttachment => a !== null);\n\n if (mapped.length === 0) {\n setError('That image was too large to attach. Try a smaller one.');\n return;\n }\n setPending((prev) => [...prev, ...mapped]);\n } catch (e) {\n setError(e instanceof Error ? e.message : 'Could not attach that image.');\n } finally {\n setBusy(false);\n }\n }, []);\n\n const pickFromLibrary = useCallback(() => run('library'), [run]);\n const captureFromCamera = useCallback(() => run('camera'), [run]);\n const remove = useCallback((id: string) => setPending((prev) => prev.filter((a) => a.id !== id)), []);\n const clear = useCallback(() => setPending([]), []);\n const forSend = useCallback(() => (pending.length > 0 ? pending : undefined), [pending]);\n\n return useMemo(\n () => ({ pending, busy, error, pickFromLibrary, captureFromCamera, remove, clear, forSend }),\n [pending, busy, error, pickFromLibrary, captureFromCamera, remove, clear, forSend],\n );\n}\n\nexport const IMAGE_ATTACHMENT_LIMITS = { MAX_EDGE, MAX_BYTES, PICKER_QUALITY } as const;\n"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"canvasCore.js","sources":["../../../src/features/canvas/canvasCore.ts"],"sourcesContent":["/**\n * Platform-agnostic canvas document model — the native twin of the web\n * surface's `state/boards.ts` (`@yantra/canvas-surface`).\n *\n * The shapes are copied, not imported: the surface package builds for the DOM\n * (react-dom peer, vite, `crypto.randomUUID`), so importing it would drag the\n * web toolchain into Metro. The contract that actually matters is the persisted\n * JSON — `PersistedCanvasState` version 1 — and tests below pin this file to\n * fixtures of that JSON, so drift between the copies fails a test rather than\n * corrupting a board.\n *\n * Everything here is pure data + math so it runs identically under Metro,\n * vite, and jest: no RN imports, no DOM globals.\n */\n\nexport interface CanvasItem {\n id: string;\n type: 'image' | 'video' | 'audio';\n src: string;\n x: number;\n y: number;\n width: number;\n height: number;\n zIndex: number;\n generatedAssetName?: string;\n}\n\nexport interface PersistedCanvasState {\n version: 1;\n savedAt: number;\n items: CanvasItem[];\n zoom: number;\n title?: string;\n}\n\n/** Parse a persisted board, tolerating the unknown: bad rows drop, not throw. */\nexport function parseBoardState(raw: unknown): PersistedCanvasState | null {\n if (!raw || typeof raw !== 'object') return null;\n const candidate = raw as Partial<PersistedCanvasState>;\n if (candidate.version !== 1 || !Array.isArray(candidate.items)) return null;\n const items = candidate.items.filter(isRenderableItem);\n return {\n version: 1,\n savedAt: typeof candidate.savedAt === 'number' ? candidate.savedAt : 0,\n items,\n zoom: clampZoom(typeof candidate.zoom === 'number' ? candidate.zoom : 1),\n ...(typeof candidate.title === 'string' && candidate.title ? { title: candidate.title } : {}),\n };\n}\n\n/** An item the viewer can draw. `blob:` URLs died with the web tab that made them. */\nexport function isRenderableItem(item: unknown): item is CanvasItem {\n if (!item || typeof item !== 'object') return false;\n const c = item as Partial<CanvasItem>;\n return (\n typeof c.id === 'string' &&\n (c.type === 'image' || c.type === 'video' || c.type === 'audio') &&\n typeof c.src === 'string' &&\n c.src.length > 0 &&\n !c.src.startsWith('blob:') &&\n [c.x, c.y, c.width, c.height].every((n) => typeof n === 'number' && Number.isFinite(n)) &&\n (c.width as number) > 0 &&\n (c.height as number) > 0\n );\n}\n\nexport const MIN_ZOOM = 0.25;\nexport const MAX_ZOOM = 4;\n\nexport function clampZoom(zoom: number): number {\n if (!Number.isFinite(zoom)) return 1;\n return Math.min(MAX_ZOOM, Math.max(MIN_ZOOM, zoom));\n}\n\nexport interface BoardBounds {\n minX: number;\n minY: number;\n maxX: number;\n maxY: number;\n}\n\n/** Union of item rects; null for an empty board (an empty union has no bounds). */\nexport function boardBounds(items: readonly CanvasItem[]): BoardBounds | null {\n if (items.length === 0) return null;\n let minX = Infinity;\n let minY = Infinity;\n let maxX = -Infinity;\n let maxY = -Infinity;\n for (const it of items) {\n minX = Math.min(minX, it.x);\n minY = Math.min(minY, it.y);\n maxX = Math.max(maxX, it.x + it.width);\n maxY = Math.max(maxY, it.y + it.height);\n }\n return { minX, minY, maxX, maxY };\n}\n\nexport interface FitTransform {\n /** Uniform scale applied to board coordinates. */\n scale: number;\n /** Screen-space translation, applied after scaling. */\n translateX: number;\n translateY: number;\n}\n\n/**\n * Fit the whole board inside a viewport with a margin — the viewer's initial\n * camera. Pure math so pinch/pan gestures can start from a reproducible state.\n */\nexport function fitToViewport(\n bounds: BoardBounds,\n viewportWidth: number,\n viewportHeight: number,\n margin = 24,\n): FitTransform {\n const boardW = Math.max(1, bounds.maxX - bounds.minX);\n const boardH = Math.max(1, bounds.maxY - bounds.minY);\n const availW = Math.max(1, viewportWidth - margin * 2);\n const availH = Math.max(1, viewportHeight - margin * 2);\n const scale = clampZoom(Math.min(availW / boardW, availH / boardH));\n // Center the scaled board in the viewport.\n const translateX = (viewportWidth - boardW * scale) / 2 - bounds.minX * scale;\n const translateY = (viewportHeight - boardH * scale) / 2 - bounds.minY * scale;\n return { scale, translateX, translateY };\n}\n\n/** Painter's order: stable sort by zIndex so overlapping media stack as authored. */\nexport function paintOrder(items: readonly CanvasItem[]): CanvasItem[] {\n return [...items].sort((a, b) => a.zIndex - b.zIndex || a.id.localeCompare(b.id));\n}\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAmCO,SAAS,gBAAgB,GAA2C,EAAA;AACzE,EAAA,IAAI,CAAC,GAAA,IAAO,OAAO,GAAA,KAAQ,UAAiB,OAAA,IAAA;AAC5C,EAAA,MAAM,SAAY,GAAA,GAAA;AAClB,EAAI,IAAA,SAAA,CAAU,YAAY,CAAK,IAAA,CAAC,MAAM,OAAQ,CAAA,SAAA,CAAU,KAAK,CAAA,EAAU,OAAA,IAAA;AACvE,EAAA,MAAM,KAAQ,GAAA,SAAA,CAAU,KAAM,CAAA,MAAA,CAAO,gBAAgB,CAAA;AACrD,EAAO,OAAA,cAAA,CAAA;AAAA,IACL,OAAS,EAAA,CAAA;AAAA,IACT,SAAS,OAAO,SAAA,CAAU,OAAY,KAAA,QAAA,GAAW,UAAU,OAAU,GAAA,CAAA;AAAA,IACrE,KAAA;AAAA,IACA,IAAA,EAAM,UAAU,OAAO,SAAA,CAAU,SAAS,QAAW,GAAA,SAAA,CAAU,OAAO,CAAC;AAAA,GAAA,EACnE,OAAO,SAAA,CAAU,KAAU,KAAA,QAAA,IAAY,UAAU,KAAQ,GAAA;AAAA,IAC3D,OAAO,SAAU,CAAA;AAAA,MACf,EAAC,CAAA;AAET;AAGO,SAAS,iBAAiB,IAAmC,EAAA;AAClE,EAAA,IAAI,CAAC,IAAA,IAAQ,OAAO,IAAA,KAAS,UAAiB,OAAA,KAAA;AAC9C,EAAA,MAAM,CAAI,GAAA,IAAA;AACV,EAAO,OAAA,OAAO,EAAE,EAAO,KAAA,QAAA,KAAa,EAAE,IAAS,KAAA,OAAA,IAAW,CAAE,CAAA,IAAA,KAAS,OAAW,IAAA,CAAA,CAAE,SAAS,OAAY,CAAA,IAAA,OAAO,CAAE,CAAA,GAAA,KAAQ,QAAY,IAAA,CAAA,CAAE,IAAI,MAAS,GAAA,CAAA,IAAK,CAAC,CAAA,CAAE,GAAI,CAAA,UAAA,CAAW,OAAO,CAAK,IAAA,CAAC,CAAE,CAAA,CAAA,EAAG,CAAE,CAAA,CAAA,EAAG,EAAE,KAAO,EAAA,CAAA,CAAE,MAAM,CAAA,CAAE,KAAM,CAAA,CAAA,CAAA,KAAK,OAAO,CAAM,KAAA,QAAA,IAAY,MAAO,CAAA,QAAA,CAAS,CAAC,CAAC,KAAK,CAAE,CAAA,KAAA,GAAkB,CAAK,IAAA,CAAA,CAAE,MAAmB,GAAA,CAAA;AAC/T;AACO,MAAM,QAAW,GAAA;AACjB,MAAM,QAAW,GAAA;AACjB,SAAS,UAAU,IAAsB,EAAA;AAC9C,EAAA,IAAI,CAAC,MAAA,CAAO,QAAS,CAAA,IAAI,GAAU,OAAA,CAAA;AACnC,EAAA,OAAO,KAAK,GAAI,CAAA,QAAA,EAAU,KAAK,GAAI,CAAA,QAAA,EAAU,IAAI,CAAC,CAAA;AACpD"}
1
+ {"version":3,"file":"canvasCore.js","sources":["../../../src/features/canvas/canvasCore.ts"],"sourcesContent":["/**\n * Platform-agnostic canvas document model — the native twin of the web\n * surface's `state/boards.ts` (`@yantra/canvas-surface`).\n *\n * The shapes are copied, not imported: the surface package builds for the DOM\n * (react-dom peer, vite, `crypto.randomUUID`), so importing it would drag the\n * web toolchain into Metro. The contract that actually matters is the persisted\n * JSON — `PersistedCanvasState` version 1 — and tests below pin this file to\n * fixtures of that JSON, so drift between the copies fails a test rather than\n * corrupting a board.\n *\n * Everything here is pure data + math so it runs identically under Metro,\n * vite, and jest: no RN imports, no DOM globals.\n */\n\nexport interface CanvasItem {\n id: string;\n type: 'image' | 'video' | 'audio';\n src: string;\n x: number;\n y: number;\n width: number;\n height: number;\n zIndex: number;\n generatedAssetName?: string;\n}\n\nexport interface PersistedCanvasState {\n version: 1;\n savedAt: number;\n items: CanvasItem[];\n zoom: number;\n title?: string;\n}\n\n/** Parse a persisted board, tolerating the unknown: bad rows drop, not throw. */\nexport function parseBoardState(raw: unknown): PersistedCanvasState | null {\n if (!raw || typeof raw !== 'object') return null;\n const candidate = raw as Partial<PersistedCanvasState>;\n if (candidate.version !== 1 || !Array.isArray(candidate.items)) return null;\n const items = candidate.items.filter(isRenderableItem);\n return {\n version: 1,\n savedAt: typeof candidate.savedAt === 'number' ? candidate.savedAt : 0,\n items,\n zoom: clampZoom(typeof candidate.zoom === 'number' ? candidate.zoom : 1),\n ...(typeof candidate.title === 'string' && candidate.title ? { title: candidate.title } : {}),\n };\n}\n\n/** An item the viewer can draw. `blob:` URLs died with the web tab that made them. */\nexport function isRenderableItem(item: unknown): item is CanvasItem {\n if (!item || typeof item !== 'object') return false;\n const c = item as Partial<CanvasItem>;\n return (\n typeof c.id === 'string' &&\n (c.type === 'image' || c.type === 'video' || c.type === 'audio') &&\n typeof c.src === 'string' &&\n c.src.length > 0 &&\n !c.src.startsWith('blob:') &&\n [c.x, c.y, c.width, c.height].every((n) => typeof n === 'number' && Number.isFinite(n)) &&\n (c.width as number) > 0 &&\n (c.height as number) > 0\n );\n}\n\n/** Floor for item edits - a rect can never be resized away entirely. */\nexport const MIN_ITEM_SIZE = 24;\n\nexport interface ItemFrame {\n x: number;\n y: number;\n width: number;\n height: number;\n}\n\nexport type ResizeCorner = 'tl' | 'tr' | 'bl' | 'br';\n\n/**\n * Resize `frame` by dragging `corner` by (dx, dy) board units, keeping the\n * OPPOSITE corner anchored - the gesture the selection handles drive. Both\n * axes move independently (free-form resize); each side clamps at\n * MIN_ITEM_SIZE so a rect can shrink to a nub but never invert or vanish.\n *\n * Marked as a worklet: the resize handles call this per-frame on the UI\n * thread. The directive is inert everywhere else (plain string in node/jest).\n */\nexport function resizeFrame(frame: ItemFrame, corner: ResizeCorner, dx: number, dy: number): ItemFrame {\n 'worklet';\n\n const left = corner === 'tl' || corner === 'bl';\n const top = corner === 'tl' || corner === 'tr';\n\n const width = Math.max(MIN_ITEM_SIZE, left ? frame.width - dx : frame.width + dx);\n const height = Math.max(MIN_ITEM_SIZE, top ? frame.height - dy : frame.height + dy);\n\n return {\n // Anchor the opposite corner: a left/top edge moves by however much the\n // clamped size actually changed, not by the raw drag delta.\n x: left ? frame.x + (frame.width - width) : frame.x,\n y: top ? frame.y + (frame.height - height) : frame.y,\n width,\n height,\n };\n}\n\n/** New items array with `id`'s frame replaced - the commit step after a drag\n * or resize gesture ends. Unknown ids return the array unchanged. */\nexport function applyItemFrame(items: readonly CanvasItem[], id: string, frame: ItemFrame): CanvasItem[] {\n return items.map((item) => (item.id === id ? { ...item, ...frame } : item));\n}\n\nexport const MIN_ZOOM = 0.25;\nexport const MAX_ZOOM = 4;\n\nexport function clampZoom(zoom: number): number {\n if (!Number.isFinite(zoom)) return 1;\n return Math.min(MAX_ZOOM, Math.max(MIN_ZOOM, zoom));\n}\n\nexport interface BoardBounds {\n minX: number;\n minY: number;\n maxX: number;\n maxY: number;\n}\n\n/** Union of item rects; null for an empty board (an empty union has no bounds). */\nexport function boardBounds(items: readonly CanvasItem[]): BoardBounds | null {\n if (items.length === 0) return null;\n let minX = Infinity;\n let minY = Infinity;\n let maxX = -Infinity;\n let maxY = -Infinity;\n for (const it of items) {\n minX = Math.min(minX, it.x);\n minY = Math.min(minY, it.y);\n maxX = Math.max(maxX, it.x + it.width);\n maxY = Math.max(maxY, it.y + it.height);\n }\n return { minX, minY, maxX, maxY };\n}\n\nexport interface FitTransform {\n /** Uniform scale applied to board coordinates. */\n scale: number;\n /** Screen-space translation, applied after scaling. */\n translateX: number;\n translateY: number;\n}\n\n/**\n * Fit the whole board inside a viewport with a margin — the viewer's initial\n * camera. Pure math so pinch/pan gestures can start from a reproducible state.\n */\nexport function fitToViewport(\n bounds: BoardBounds,\n viewportWidth: number,\n viewportHeight: number,\n margin = 24,\n): FitTransform {\n const boardW = Math.max(1, bounds.maxX - bounds.minX);\n const boardH = Math.max(1, bounds.maxY - bounds.minY);\n const availW = Math.max(1, viewportWidth - margin * 2);\n const availH = Math.max(1, viewportHeight - margin * 2);\n const scale = clampZoom(Math.min(availW / boardW, availH / boardH));\n // Center the scaled board in the viewport.\n const translateX = (viewportWidth - boardW * scale) / 2 - bounds.minX * scale;\n const translateY = (viewportHeight - boardH * scale) / 2 - bounds.minY * scale;\n return { scale, translateX, translateY };\n}\n\n/** Painter's order: stable sort by zIndex so overlapping media stack as authored. */\nexport function paintOrder(items: readonly CanvasItem[]): CanvasItem[] {\n return [...items].sort((a, b) => a.zIndex - b.zIndex || a.id.localeCompare(b.id));\n}\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAmCO,SAAS,gBAAgB,GAA2C,EAAA;AACzE,EAAA,IAAI,CAAC,GAAA,IAAO,OAAO,GAAA,KAAQ,UAAiB,OAAA,IAAA;AAC5C,EAAA,MAAM,SAAY,GAAA,GAAA;AAClB,EAAI,IAAA,SAAA,CAAU,YAAY,CAAK,IAAA,CAAC,MAAM,OAAQ,CAAA,SAAA,CAAU,KAAK,CAAA,EAAU,OAAA,IAAA;AACvE,EAAA,MAAM,KAAQ,GAAA,SAAA,CAAU,KAAM,CAAA,MAAA,CAAO,gBAAgB,CAAA;AACrD,EAAO,OAAA,cAAA,CAAA;AAAA,IACL,OAAS,EAAA,CAAA;AAAA,IACT,SAAS,OAAO,SAAA,CAAU,OAAY,KAAA,QAAA,GAAW,UAAU,OAAU,GAAA,CAAA;AAAA,IACrE,KAAA;AAAA,IACA,IAAA,EAAM,UAAU,OAAO,SAAA,CAAU,SAAS,QAAW,GAAA,SAAA,CAAU,OAAO,CAAC;AAAA,GAAA,EACnE,OAAO,SAAA,CAAU,KAAU,KAAA,QAAA,IAAY,UAAU,KAAQ,GAAA;AAAA,IAC3D,OAAO,SAAU,CAAA;AAAA,MACf,EAAC,CAAA;AAET;AAGO,SAAS,iBAAiB,IAAmC,EAAA;AAClE,EAAA,IAAI,CAAC,IAAA,IAAQ,OAAO,IAAA,KAAS,UAAiB,OAAA,KAAA;AAC9C,EAAA,MAAM,CAAI,GAAA,IAAA;AACV,EAAO,OAAA,OAAO,EAAE,EAAO,KAAA,QAAA,KAAa,EAAE,IAAS,KAAA,OAAA,IAAW,CAAE,CAAA,IAAA,KAAS,OAAW,IAAA,CAAA,CAAE,SAAS,OAAY,CAAA,IAAA,OAAO,CAAE,CAAA,GAAA,KAAQ,QAAY,IAAA,CAAA,CAAE,IAAI,MAAS,GAAA,CAAA,IAAK,CAAC,CAAA,CAAE,GAAI,CAAA,UAAA,CAAW,OAAO,CAAK,IAAA,CAAC,CAAE,CAAA,CAAA,EAAG,CAAE,CAAA,CAAA,EAAG,EAAE,KAAO,EAAA,CAAA,CAAE,MAAM,CAAA,CAAE,KAAM,CAAA,CAAA,CAAA,KAAK,OAAO,CAAM,KAAA,QAAA,IAAY,MAAO,CAAA,QAAA,CAAS,CAAC,CAAC,KAAK,CAAE,CAAA,KAAA,GAAkB,CAAK,IAAA,CAAA,CAAE,MAAmB,GAAA,CAAA;AAC/T;AA8CO,MAAM,QAAW,GAAA;AACjB,MAAM,QAAW,GAAA;AACjB,SAAS,UAAU,IAAsB,EAAA;AAC9C,EAAA,IAAI,CAAC,MAAA,CAAO,QAAS,CAAA,IAAI,GAAU,OAAA,CAAA;AACnC,EAAA,OAAO,KAAK,GAAI,CAAA,QAAA,EAAU,KAAK,GAAI,CAAA,QAAA,EAAU,IAAI,CAAC,CAAA;AACpD"}
@@ -0,0 +1,56 @@
1
+ import {jsxs,jsx}from'react/jsx-runtime';import {useState,useRef,useEffect,useCallback}from'react';import {Clipboard,Share,View,StyleSheet,TouchableOpacity}from'react-native';import {MaterialCommunityIcons}from'@expo/vector-icons';const ICON_SIZE = 16;
2
+ const ICON_COLOR = "#64748b";
3
+ const COPIED_COLOR = "#10b981";
4
+ function ActionButton({
5
+ icon,
6
+ label,
7
+ onPress,
8
+ color = ICON_COLOR
9
+ }) {
10
+ return /* @__PURE__ */ jsx(TouchableOpacity, { onPress, style: styles.button, hitSlop: {
11
+ top: 8,
12
+ bottom: 8,
13
+ left: 6,
14
+ right: 6
15
+ }, accessibilityRole: "button", accessibilityLabel: label, children: /* @__PURE__ */ jsx(MaterialCommunityIcons, { name: icon, size: ICON_SIZE, color }) });
16
+ }
17
+ function MessageActionBar({
18
+ text,
19
+ onRegenerate
20
+ }) {
21
+ const [copied, setCopied] = useState(false);
22
+ const copiedTimer = useRef(null);
23
+ useEffect(() => () => {
24
+ if (copiedTimer.current) clearTimeout(copiedTimer.current);
25
+ }, []);
26
+ const handleCopy = useCallback(() => {
27
+ Clipboard.setString(text);
28
+ setCopied(true);
29
+ if (copiedTimer.current) clearTimeout(copiedTimer.current);
30
+ copiedTimer.current = setTimeout(() => setCopied(false), 2e3);
31
+ }, [text]);
32
+ const handleShare = useCallback(() => {
33
+ void Share.share({
34
+ message: text
35
+ });
36
+ }, [text]);
37
+ return /* @__PURE__ */ jsxs(View, { style: styles.row, accessibilityRole: "toolbar", accessibilityLabel: "Message actions", children: [
38
+ /* @__PURE__ */ jsx(ActionButton, { icon: copied ? "check" : "content-copy", label: copied ? "Copied" : "Copy", onPress: handleCopy, color: copied ? COPIED_COLOR : ICON_COLOR }),
39
+ onRegenerate ? /* @__PURE__ */ jsx(ActionButton, { icon: "refresh", label: "Try again", onPress: onRegenerate }) : null,
40
+ /* @__PURE__ */ jsx(ActionButton, { icon: "share-variant-outline", label: "Share", onPress: handleShare })
41
+ ] });
42
+ }
43
+ const styles = StyleSheet.create({
44
+ row: {
45
+ flexDirection: "row",
46
+ alignItems: "center",
47
+ gap: 4
48
+ },
49
+ button: {
50
+ height: 28,
51
+ width: 28,
52
+ borderRadius: 8,
53
+ alignItems: "center",
54
+ justifyContent: "center"
55
+ }
56
+ });export{MessageActionBar,MessageActionBar as default};//# sourceMappingURL=MessageActionBar.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MessageActionBar.js","sources":["../../../src/features/chat/MessageActionBar.tsx"],"sourcesContent":["/**\n * Action row under a completed assistant message — the mobile counterpart of\n * the web chat's MessageActionBar (copy / try again / share). Rendered through\n * MessagesContainerUI's `renderMessageActions` slot.\n *\n * Read-aloud is deliberately absent for now: it needs `expo-speech`, a native\n * module the installed binaries don't carry yet. It joins the bar in the build\n * that adds the dependency; everything here works with the current binary.\n */\nimport React, { useCallback, useEffect, useRef, useState } from 'react';\n// Clipboard is deprecated in RN core but still functional on 0.79, and the\n// community package is not in the dependency set — using core avoids putting a\n// new native module between the user and a working Copy button.\n// eslint-disable-next-line @typescript-eslint/no-deprecated\nimport { Clipboard, Share, StyleSheet, TouchableOpacity, View } from 'react-native';\nimport { MaterialCommunityIcons } from '@expo/vector-icons';\n\nexport interface MessageActionBarProps {\n /** The assistant message's text — copied and shared verbatim. */\n text: string;\n /** Re-run the turn that produced this message. Omit to hide the button\n * (e.g. while a turn is streaming, to avoid double-sends). */\n onRegenerate?: () => void;\n}\n\nconst ICON_SIZE = 16;\nconst ICON_COLOR = '#64748b';\nconst COPIED_COLOR = '#10b981';\n\ninterface ActionButtonProps {\n icon: React.ComponentProps<typeof MaterialCommunityIcons>['name'];\n label: string;\n onPress: () => void;\n color?: string;\n}\n\nfunction ActionButton({ icon, label, onPress, color = ICON_COLOR }: ActionButtonProps) {\n return (\n <TouchableOpacity\n onPress={onPress}\n style={styles.button}\n hitSlop={{ top: 8, bottom: 8, left: 6, right: 6 }}\n accessibilityRole=\"button\"\n accessibilityLabel={label}\n >\n <MaterialCommunityIcons name={icon} size={ICON_SIZE} color={color} />\n </TouchableOpacity>\n );\n}\n\nexport function MessageActionBar({ text, onRegenerate }: MessageActionBarProps) {\n const [copied, setCopied] = useState(false);\n const copiedTimer = useRef<ReturnType<typeof setTimeout> | null>(null);\n useEffect(\n () => () => {\n if (copiedTimer.current) clearTimeout(copiedTimer.current);\n },\n [],\n );\n\n const handleCopy = useCallback(() => {\n // eslint-disable-next-line @typescript-eslint/no-deprecated\n Clipboard.setString(text);\n setCopied(true);\n if (copiedTimer.current) clearTimeout(copiedTimer.current);\n copiedTimer.current = setTimeout(() => setCopied(false), 2000);\n }, [text]);\n\n const handleShare = useCallback(() => {\n void Share.share({ message: text });\n }, [text]);\n\n return (\n <View style={styles.row} accessibilityRole=\"toolbar\" accessibilityLabel=\"Message actions\">\n <ActionButton\n icon={copied ? 'check' : 'content-copy'}\n label={copied ? 'Copied' : 'Copy'}\n onPress={handleCopy}\n color={copied ? COPIED_COLOR : ICON_COLOR}\n />\n {onRegenerate ? <ActionButton icon=\"refresh\" label=\"Try again\" onPress={onRegenerate} /> : null}\n <ActionButton icon=\"share-variant-outline\" label=\"Share\" onPress={handleShare} />\n </View>\n );\n}\n\nconst styles = StyleSheet.create({\n row: {\n flexDirection: 'row',\n alignItems: 'center',\n gap: 4,\n },\n button: {\n height: 28,\n width: 28,\n borderRadius: 8,\n alignItems: 'center',\n justifyContent: 'center',\n },\n});\n\nexport default MessageActionBar;\n"],"names":[],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import {AuthStrategy,HttpHeaders}from'common';import {readAuth0IdToken}from'../bridge/readAuth0Session.js';import {config}from'../config/env-config.js';const OBTAIN_USER_SYSTEM_TOKEN = `
1
+ import {AuthStrategy,HttpHeaders}from'common';import {readAuth0IdToken}from'../bridge/readAuth0Session.js';import {config}from'../config/env-config.js';export{isCanvasSurfaceUrl,resolveSurfaceAuthGraphqlUrl}from'./resolveSurfaceAuthGraphqlUrl.js';const OBTAIN_USER_SYSTEM_TOKEN = `
2
2
  mutation ObtainUserSystemToken($input: ObtainUserSystemTokenInput!) {
3
3
  obtainUserSystemToken(input: $input) {
4
4
  token
@@ -14,6 +14,29 @@ const GET_USER_SYSTEM_TOKEN = `
14
14
  }
15
15
  }
16
16
  `;
17
+ const GET_USER_ORGANIZATIONS = `
18
+ query GetUserOrganizations {
19
+ getUserOrganizations {
20
+ name
21
+ }
22
+ }
23
+ `;
24
+ const GET_PROJECTS = `
25
+ query GetProjects($orgName: String!) {
26
+ getProjects(orgName: $orgName) {
27
+ data {
28
+ id
29
+ }
30
+ }
31
+ }
32
+ `;
33
+ const GET_ENVIRONMENT_TAGS = `
34
+ query GetEnvironmentTags($projectId: ID) {
35
+ getEnvironmentTags(projectId: $projectId) {
36
+ id
37
+ }
38
+ }
39
+ `;
17
40
  const CREATE_SECRET_API_TOKEN = `
18
41
  mutation CreateSecretApiToken($input: CreateSecretApiTokenInput!) {
19
42
  createSecretApiToken(input: $input) {
@@ -60,6 +83,51 @@ async function gqlFetch(graphqlUrl, idToken, query, variables) {
60
83
  const DEV_TESTING_PROJECT_UUID = "9a8ac704-1040-4836-9b72-48b771ca91b1";
61
84
  const OBJECT_ID_HEX = /^[a-fA-F0-9]{24}$/;
62
85
  const PROJECT_UUID = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/;
86
+ async function discoverMintScope(graphqlUrl, idToken) {
87
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s;
88
+ const orgs = await gqlFetch(graphqlUrl, idToken, GET_USER_ORGANIZATIONS);
89
+ if ((_a = orgs.errors) == null ? void 0 : _a.length) {
90
+ throw new Error(((_b = orgs.errors[0]) == null ? void 0 : _b.message) || "getUserOrganizations failed");
91
+ }
92
+ const orgName = ((_f = (_e = (_d = (_c = orgs.data) == null ? void 0 : _c.getUserOrganizations) == null ? void 0 : _d.find((o) => {
93
+ var _a2;
94
+ return (_a2 = o == null ? void 0 : o.name) == null ? void 0 : _a2.trim();
95
+ })) == null ? void 0 : _e.name) == null ? void 0 : _f.trim()) || "";
96
+ if (!orgName) {
97
+ throw new Error("No organization on the canvas backend. Sign in once in the canvas harness (Chrome on localhost:5175), then retry from the app.");
98
+ }
99
+ const projects = await gqlFetch(graphqlUrl, idToken, GET_PROJECTS, {
100
+ orgName
101
+ });
102
+ if ((_g = projects.errors) == null ? void 0 : _g.length) {
103
+ throw new Error(((_h = projects.errors[0]) == null ? void 0 : _h.message) || "getProjects failed");
104
+ }
105
+ const projectId = ((_m = (_l = (_k = (_j = (_i = projects.data) == null ? void 0 : _i.getProjects) == null ? void 0 : _j.data) == null ? void 0 : _k.find((p) => {
106
+ var _a2;
107
+ return (_a2 = p == null ? void 0 : p.id) == null ? void 0 : _a2.trim();
108
+ })) == null ? void 0 : _l.id) == null ? void 0 : _m.trim()) || "";
109
+ if (!projectId) {
110
+ throw new Error(`No project on the canvas backend for org "${orgName}".`);
111
+ }
112
+ const tags = await gqlFetch(graphqlUrl, idToken, GET_ENVIRONMENT_TAGS, {
113
+ projectId
114
+ });
115
+ if ((_n = tags.errors) == null ? void 0 : _n.length) {
116
+ throw new Error(((_o = tags.errors[0]) == null ? void 0 : _o.message) || "getEnvironmentTags failed");
117
+ }
118
+ const environmentTagId = ((_s = (_r = (_q = (_p = tags.data) == null ? void 0 : _p.getEnvironmentTags) == null ? void 0 : _q.find((t) => {
119
+ var _a2;
120
+ return (_a2 = t == null ? void 0 : t.id) == null ? void 0 : _a2.trim();
121
+ })) == null ? void 0 : _r.id) == null ? void 0 : _s.trim()) || "";
122
+ if (!environmentTagId) {
123
+ throw new Error(`No environment tag on the canvas backend for project "${projectId}".`);
124
+ }
125
+ return {
126
+ orgName,
127
+ projectId,
128
+ environmentTagId
129
+ };
130
+ }
63
131
  function assertMintableProjectId(projectId) {
64
132
  if (projectId === "default" || !projectId.trim()) {
65
133
  throw new Error("Missing projectId \u2014 wait for org/project bootstrap, then retry.");
@@ -79,11 +147,24 @@ async function mintSurfaceSystemToken(input) {
79
147
  }
80
148
  const graphqlUrl = resolveSurfaceCatalogBackend(input.graphqlUrl);
81
149
  if (__DEV__) {
82
- console.log("[WebBuilder] minting system token via", graphqlUrl);
150
+ console.log("[WebBuilder] minting system token via", graphqlUrl, input.discoverScope ? "(discover scope)" : "");
151
+ }
152
+ let orgName = ((_a = input.orgName) == null ? void 0 : _a.trim()) || "";
153
+ let projectId = ((_b = input.projectId) == null ? void 0 : _b.trim()) || "";
154
+ let environmentTagId = ((_c = input.tagId) == null ? void 0 : _c.trim()) || "";
155
+ if (input.discoverScope) {
156
+ const scope = await discoverMintScope(graphqlUrl, idToken);
157
+ orgName = scope.orgName;
158
+ projectId = scope.projectId;
159
+ environmentTagId = scope.environmentTagId;
160
+ if (__DEV__) {
161
+ console.log("[WebBuilder] discovered canvas scope", {
162
+ orgName,
163
+ projectId,
164
+ environmentTagId
165
+ });
166
+ }
83
167
  }
84
- const orgName = (_a = input.orgName) == null ? void 0 : _a.trim();
85
- const projectId = (_b = input.projectId) == null ? void 0 : _b.trim();
86
- const environmentTagId = (_c = input.tagId) == null ? void 0 : _c.trim();
87
168
  if (!orgName) {
88
169
  throw new Error("Missing orgName \u2014 wait for org bootstrap, then retry.");
89
170
  }
@@ -1 +1 @@
1
- {"version":3,"file":"mintSurfaceSystemToken.js","sources":["../../src/hooks/mintSurfaceSystemToken.ts"],"sourcesContent":["import { AuthStrategy, HttpHeaders } from 'common';\nimport { readAuth0IdToken } from '../bridge/readAuth0Session';\nimport { config } from '../config/env-config';\n\nconst OBTAIN_USER_SYSTEM_TOKEN = `\n mutation ObtainUserSystemToken($input: ObtainUserSystemTokenInput!) {\n obtainUserSystemToken(input: $input) {\n token\n }\n }\n`;\n\nconst GET_USER_SYSTEM_TOKEN = `\n query GetUserSystemToken {\n getUserSystemToken {\n id\n expiresAt\n isSystemToken\n }\n }\n`;\n\nconst CREATE_SECRET_API_TOKEN = `\n mutation CreateSecretApiToken($input: CreateSecretApiTokenInput!) {\n createSecretApiToken(input: $input) {\n id\n expiresAt\n }\n }\n`;\n\nconst REVEAL_SECRET_API_TOKEN = `\n mutation RevealSecretApiToken($id: ID!) {\n revealSecretApiToken(id: $id)\n }\n`;\n\ntype GqlResult<T> = {\n data?: T;\n errors?: Array<{ message?: string }>;\n};\n\nfunction normalizeGraphqlUrl(url: string): string {\n return url.trim().replace(/\\/$/, '');\n}\n\n/** App GRAPHQL_URL from env (optional override for device LAN rewrite). */\nexport function resolveSurfaceCatalogBackend(graphqlUrl?: string): string {\n const raw = graphqlUrl?.trim() || config.GRAPHQL_URL;\n if (!raw?.trim()) {\n throw new Error('GRAPHQL_URL is not configured');\n }\n return normalizeGraphqlUrl(raw);\n}\n\nasync function gqlFetch<T>(\n graphqlUrl: string,\n idToken: string,\n query: string,\n variables?: Record<string, unknown>,\n): Promise<GqlResult<T>> {\n // Staging JWT middleware only treats Bearer as mobile/SPA when\n // `ismobile` (or `isspa`) is set — without it we get HTTP 401 Unauthorized.\n // Match Apollo mobile authHeadersLink + Auth0Client.commonFetchRequestHeaders.\n const res = await fetch(graphqlUrl, {\n method: 'POST',\n headers: {\n [HttpHeaders.CONTENT_TYPE]: 'application/json',\n [HttpHeaders.AUTHORIZATION]: `Bearer ${idToken}`,\n [HttpHeaders.IS_MOBILE]: 'true',\n [HttpHeaders.AUTH_STRATEGY]: AuthStrategy.Auth0,\n },\n body: JSON.stringify({ query, variables }),\n });\n if (!res.ok) {\n const text = await res.text().catch(() => '');\n throw new Error(`GraphQL HTTP ${res.status}: ${text.slice(0, 200)}`);\n }\n return (await res.json()) as GqlResult<T>;\n}\n\nexport type MintSurfaceTokenInput = {\n /** App GRAPHQL_URL (e.g. device-rewritten). Defaults to env GRAPHQL_URL. */\n graphqlUrl?: string;\n orgName?: string | null;\n projectId?: string | null;\n tagId?: string | null;\n};\n\nexport type MintSurfaceTokenResult = {\n token: string;\n /** Exact backend URL string to stamp into cdecli_token_meta. */\n backend: string;\n};\n\n/**\n * Reveal (or obtain) a system token on GRAPHQL_URL.\n */\n/** Local-dev testing project — must never mint against this on staging/prod. */\nconst DEV_TESTING_PROJECT_UUID = '9a8ac704-1040-4836-9b72-48b771ca91b1';\n\nconst OBJECT_ID_HEX = /^[a-fA-F0-9]{24}$/;\nconst PROJECT_UUID = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/;\n\nfunction assertMintableProjectId(projectId: string): void {\n if (projectId === 'default' || !projectId.trim()) {\n throw new Error('Missing projectId — wait for org/project bootstrap, then retry.');\n }\n if (!__DEV__ && projectId === DEV_TESTING_PROJECT_UUID) {\n throw new Error(\n 'Invalid projectId (dev testing UUID). Open Home so a real project can be created, then retry.',\n );\n }\n if (!OBJECT_ID_HEX.test(projectId) && !PROJECT_UUID.test(projectId)) {\n throw new Error(`Invalid projectId format for system token mint: ${projectId.slice(0, 48)}`);\n }\n}\n\nexport async function mintSurfaceSystemToken(input: MintSurfaceTokenInput): Promise<MintSurfaceTokenResult> {\n const idToken = await readAuth0IdToken();\n if (!idToken) {\n throw new Error('No Auth0 id_token in storage — sign in to the mobile app first.');\n }\n\n const graphqlUrl = resolveSurfaceCatalogBackend(input.graphqlUrl);\n if (__DEV__) {\n // eslint-disable-next-line no-console\n console.log('[WebBuilder] minting system token via', graphqlUrl);\n }\n\n const orgName = input.orgName?.trim();\n const projectId = input.projectId?.trim();\n const environmentTagId = input.tagId?.trim();\n\n if (!orgName) {\n throw new Error('Missing orgName — wait for org bootstrap, then retry.');\n }\n if (!projectId) {\n throw new Error('Missing projectId — wait for org/project bootstrap, then retry.');\n }\n if (!environmentTagId) {\n throw new Error('Missing environmentTagId — cannot mint system token.');\n }\n assertMintableProjectId(projectId);\n\n // Canonical get-or-create (resolves project UUID → Mongo id server-side).\n try {\n const obtained = await gqlFetch<{ obtainUserSystemToken?: { token?: string } | null }>(\n graphqlUrl,\n idToken,\n OBTAIN_USER_SYSTEM_TOKEN,\n {\n input: {\n projectId,\n environmentTagId,\n orgName,\n renewBufferSeconds: 7 * 24 * 60 * 60,\n algorithm: 'HS256',\n },\n },\n );\n const token = obtained.data?.obtainUserSystemToken?.token?.trim();\n if (token && !obtained.errors?.length) {\n if (__DEV__) {\n // eslint-disable-next-line no-console\n console.log('[WebBuilder] obtained system token length=', token.length);\n }\n return { token, backend: graphqlUrl };\n }\n if (obtained.errors?.length) {\n throw new Error(obtained.errors[0]?.message || 'obtainUserSystemToken failed');\n }\n } catch (err) {\n if (__DEV__) {\n // eslint-disable-next-line no-console\n console.warn('[WebBuilder] obtainUserSystemToken failed, falling back to reveal:', err);\n }\n // Fall through to get/create/reveal only when obtain is unavailable.\n const msg = err instanceof Error ? err.message : String(err);\n if (/12 bytes|24 hex|Project not found/i.test(msg)) {\n throw new Error(\n `Project \"${projectId}\" is not valid on this backend. Pick/create a real project in the app, then retry.`,\n );\n }\n // Continue to legacy path for older backends without obtainUserSystemToken.\n if (!/Unknown field|Cannot query field|obtainUserSystemToken/i.test(msg)) {\n // Non-schema errors from obtain should surface (auth, project, etc.).\n if (/Unauthorized|401|Project not found|Invalid project/i.test(msg)) {\n throw err instanceof Error ? err : new Error(msg);\n }\n }\n }\n\n const getResult = await gqlFetch<{ getUserSystemToken?: { id?: string } | null }>(\n graphqlUrl,\n idToken,\n GET_USER_SYSTEM_TOKEN,\n );\n if (getResult.errors?.length) {\n throw new Error(getResult.errors[0]?.message || 'GetUserSystemToken failed');\n }\n\n let tokenId = getResult.data?.getUserSystemToken?.id ?? null;\n\n if (!tokenId) {\n const createResult = await gqlFetch<{ createSecretApiToken?: { id?: string } | null }>(\n graphqlUrl,\n idToken,\n CREATE_SECRET_API_TOKEN,\n {\n input: {\n name: 'System Token',\n expiryInSeconds: 31536000,\n isSystemToken: true,\n environmentTagId,\n orgName,\n projectId,\n algorithm: 'HS256',\n },\n },\n );\n\n const createErr = createResult.errors?.[0]?.message || '';\n if (createErr && !/already have a system token/i.test(createErr)) {\n if (/12 bytes|24 hex/i.test(createErr)) {\n throw new Error(\n `Project \"${projectId}\" is not valid on this backend. Pick/create a real project in the app, then retry.`,\n );\n }\n throw new Error(createErr);\n }\n\n if (createResult.data?.createSecretApiToken?.id) {\n tokenId = createResult.data.createSecretApiToken.id;\n } else {\n const refetch = await gqlFetch<{ getUserSystemToken?: { id?: string } | null }>(\n graphqlUrl,\n idToken,\n GET_USER_SYSTEM_TOKEN,\n );\n tokenId = refetch.data?.getUserSystemToken?.id ?? null;\n }\n }\n\n if (!tokenId) {\n throw new Error('No system token id on GraphQL backend — cannot reveal.');\n }\n\n const reveal = await gqlFetch<{ revealSecretApiToken?: string | null }>(\n graphqlUrl,\n idToken,\n REVEAL_SECRET_API_TOKEN,\n { id: tokenId },\n );\n if (reveal.errors?.length) {\n throw new Error(reveal.errors[0]?.message || 'RevealSecretApiToken failed');\n }\n\n const token = reveal.data?.revealSecretApiToken?.trim();\n if (!token) {\n throw new Error('RevealSecretApiToken returned empty token');\n }\n\n if (__DEV__) {\n // eslint-disable-next-line no-console\n console.log('[WebBuilder] minted system token length=', token.length);\n }\n return { token, backend: graphqlUrl };\n}\n\nexport function appendSurfaceAuthParams(baseUrl: string, token: string, state: string): string {\n try {\n const url = new URL(baseUrl);\n url.searchParams.set('token', token);\n url.searchParams.set('code', token);\n url.searchParams.set('state', state);\n return url.toString();\n } catch {\n const sep = baseUrl.includes('?') ? '&' : '?';\n return `${baseUrl}${sep}token=${encodeURIComponent(token)}&code=${encodeURIComponent(token)}&state=${encodeURIComponent(state)}`;\n }\n}\n\nfunction tokenFingerprint(token: string): string {\n const sig = token.split('.')[2] ?? token;\n return sig.slice(-24);\n}\n\n/** Shared WebView boot script: token + meta + fetch shims for surface-kit auth. */\nfunction buildSurfaceHandoffScript(token: string, backend: string, state: string, withGateRecovery: boolean): string {\n const safeToken = JSON.stringify(token);\n const safeBackend = JSON.stringify(backend);\n const safeState = JSON.stringify(state);\n const fp = JSON.stringify(tokenFingerprint(token));\n const recovery = withGateRecovery\n ? `\n function tryRecover() {\n stamp();\n var gate = document.querySelector('[data-surface-login]');\n if (!gate) return;\n if (sessionStorage.getItem('__yantra_wb_token_reload')) return;\n sessionStorage.setItem('__yantra_wb_token_reload', '1');\n var u = new URL(window.location.href);\n u.searchParams.set('token', t);\n u.searchParams.set('code', t);\n u.searchParams.set('state', state);\n window.location.replace(u.toString());\n }\n tryRecover();\n var i = 0;\n var timer = setInterval(function () {\n i += 1;\n tryRecover();\n if (i >= 40 || !document.querySelector('[data-surface-login]')) clearInterval(timer);\n }, 250);\n`\n : `\n var n = 0;\n var id = setInterval(function () {\n stamp();\n n += 1;\n if (n >= 80) clearInterval(id);\n }, 50);\n`;\n\n return `\n(function () {\n try {\n var t = ${safeToken};\n var backend = ${safeBackend};\n var state = ${safeState};\n var fp = ${fp};\n var PROBE_Q = 'query { getUserAccount { id } }';\n\n if (!window.__yantraWbFetchPatched) {\n window.__yantraWbFetchPatched = true;\n var origFetch = window.fetch.bind(window);\n window.fetch = function (input, init) {\n try {\n var url = typeof input === 'string' ? input : (input && input.url) ? String(input.url) : '';\n // Force surface-kit resolveCatalogBackend() to our GRAPHQL_URL (not remote agent → ideback).\n if (url.indexOf('/v1/auth/context') !== -1) {\n return Promise.resolve(new Response(\n JSON.stringify({ backendGraphqlUrl: backend }),\n { status: 200, headers: { 'Content-Type': 'application/json' } }\n ));\n }\n var body = init && typeof init.body === 'string' ? init.body : '';\n if (body && body.indexOf('getUserAccount') !== -1) {\n var parsed = JSON.parse(body);\n var q = parsed && typeof parsed.query === 'string' ? parsed.query.replace(/\\\\s+/g, ' ').trim() : '';\n if (q === PROBE_Q) {\n return Promise.resolve(new Response(\n JSON.stringify({ data: { getUserAccount: { id: 'mobile-webview-handoff' } } }),\n { status: 200, headers: { 'Content-Type': 'application/json' } }\n ));\n }\n }\n } catch (e0) {}\n return origFetch(input, init);\n };\n }\n\n function stamp() {\n try {\n sessionStorage.setItem('cdecli_oauth_state', state);\n localStorage.setItem('cdecli_token', t);\n sessionStorage.setItem('cdecli_token', t);\n localStorage.setItem('cdecli_system_token', t);\n localStorage.setItem('cdecli_token_meta', JSON.stringify({\n backend: backend,\n verifiedAt: Date.now(),\n fp: fp\n }));\n } catch (e1) {}\n }\n stamp();\n ${recovery}\n } catch (e) {}\n true;\n})();\ntrue;\n`;\n}\n\nexport function buildCdecliTokenInjection(token: string, backend: string, state: string): string {\n return buildSurfaceHandoffScript(token, backend, state, false);\n}\n\nexport function buildSurfaceGateRecoveryScript(token: string, backend: string, state: string): string {\n return buildSurfaceHandoffScript(token, backend, state, true);\n}\n"],"names":["token"],"mappings":"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"}
1
+ {"version":3,"file":"mintSurfaceSystemToken.js","sources":["../../src/hooks/mintSurfaceSystemToken.ts"],"sourcesContent":["import { AuthStrategy, HttpHeaders } from 'common';\nimport { readAuth0IdToken } from '../bridge/readAuth0Session';\nimport { config } from '../config/env-config';\n\nconst OBTAIN_USER_SYSTEM_TOKEN = `\n mutation ObtainUserSystemToken($input: ObtainUserSystemTokenInput!) {\n obtainUserSystemToken(input: $input) {\n token\n }\n }\n`;\n\nconst GET_USER_SYSTEM_TOKEN = `\n query GetUserSystemToken {\n getUserSystemToken {\n id\n expiresAt\n isSystemToken\n }\n }\n`;\n\nconst GET_USER_ORGANIZATIONS = `\n query GetUserOrganizations {\n getUserOrganizations {\n name\n }\n }\n`;\n\nconst GET_PROJECTS = `\n query GetProjects($orgName: String!) {\n getProjects(orgName: $orgName) {\n data {\n id\n }\n }\n }\n`;\n\nconst GET_ENVIRONMENT_TAGS = `\n query GetEnvironmentTags($projectId: ID) {\n getEnvironmentTags(projectId: $projectId) {\n id\n }\n }\n`;\n\nconst CREATE_SECRET_API_TOKEN = `\n mutation CreateSecretApiToken($input: CreateSecretApiTokenInput!) {\n createSecretApiToken(input: $input) {\n id\n expiresAt\n }\n }\n`;\n\nconst REVEAL_SECRET_API_TOKEN = `\n mutation RevealSecretApiToken($id: ID!) {\n revealSecretApiToken(id: $id)\n }\n`;\n\ntype GqlResult<T> = {\n data?: T;\n errors?: Array<{ message?: string }>;\n};\n\nfunction normalizeGraphqlUrl(url: string): string {\n return url.trim().replace(/\\/$/, '');\n}\n\n/** App GRAPHQL_URL from env (optional override for device LAN rewrite). */\nexport function resolveSurfaceCatalogBackend(graphqlUrl?: string): string {\n const raw = graphqlUrl?.trim() || config.GRAPHQL_URL;\n if (!raw?.trim()) {\n throw new Error('GRAPHQL_URL is not configured');\n }\n return normalizeGraphqlUrl(raw);\n}\n\nexport { isCanvasSurfaceUrl, resolveSurfaceAuthGraphqlUrl } from './resolveSurfaceAuthGraphqlUrl';\nexport type { SurfaceAuthUrlConfig } from './resolveSurfaceAuthGraphqlUrl';\n\nasync function gqlFetch<T>(\n graphqlUrl: string,\n idToken: string,\n query: string,\n variables?: Record<string, unknown>,\n): Promise<GqlResult<T>> {\n // Staging JWT middleware only treats Bearer as mobile/SPA when\n // `ismobile` (or `isspa`) is set — without it we get HTTP 401 Unauthorized.\n // Match Apollo mobile authHeadersLink + Auth0Client.commonFetchRequestHeaders.\n const res = await fetch(graphqlUrl, {\n method: 'POST',\n headers: {\n [HttpHeaders.CONTENT_TYPE]: 'application/json',\n [HttpHeaders.AUTHORIZATION]: `Bearer ${idToken}`,\n [HttpHeaders.IS_MOBILE]: 'true',\n [HttpHeaders.AUTH_STRATEGY]: AuthStrategy.Auth0,\n },\n body: JSON.stringify({ query, variables }),\n });\n if (!res.ok) {\n const text = await res.text().catch(() => '');\n throw new Error(`GraphQL HTTP ${res.status}: ${text.slice(0, 200)}`);\n }\n return (await res.json()) as GqlResult<T>;\n}\n\nexport type MintSurfaceTokenInput = {\n /** App GRAPHQL_URL (e.g. device-rewritten). Defaults to env GRAPHQL_URL. */\n graphqlUrl?: string;\n orgName?: string | null;\n projectId?: string | null;\n tagId?: string | null;\n /**\n * Resolve org/project/env on `graphqlUrl` instead of using the mobile\n * app's local ids. Required when minting against a different cluster\n * than GRAPHQL_URL (canvas index lives on yantra-app-v1).\n */\n discoverScope?: boolean;\n};\n\nexport type MintSurfaceTokenResult = {\n token: string;\n /** Exact backend URL string to stamp into cdecli_token_meta. */\n backend: string;\n};\n\n/**\n * Reveal (or obtain) a system token on GRAPHQL_URL.\n */\n/** Local-dev testing project — must never mint against this on staging/prod. */\nconst DEV_TESTING_PROJECT_UUID = '9a8ac704-1040-4836-9b72-48b771ca91b1';\n\nconst OBJECT_ID_HEX = /^[a-fA-F0-9]{24}$/;\nconst PROJECT_UUID = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/;\n\nasync function discoverMintScope(\n graphqlUrl: string,\n idToken: string,\n): Promise<{ orgName: string; projectId: string; environmentTagId: string }> {\n const orgs = await gqlFetch<{ getUserOrganizations?: Array<{ name?: string | null } | null> | null }>(\n graphqlUrl,\n idToken,\n GET_USER_ORGANIZATIONS,\n );\n if (orgs.errors?.length) {\n throw new Error(orgs.errors[0]?.message || 'getUserOrganizations failed');\n }\n const orgName = orgs.data?.getUserOrganizations?.find((o) => o?.name?.trim())?.name?.trim() || '';\n if (!orgName) {\n throw new Error(\n 'No organization on the canvas backend. Sign in once in the canvas harness (Chrome on localhost:5175), then retry from the app.',\n );\n }\n\n const projects = await gqlFetch<{ getProjects?: { data?: Array<{ id?: string | null } | null> | null } | null }>(\n graphqlUrl,\n idToken,\n GET_PROJECTS,\n { orgName },\n );\n if (projects.errors?.length) {\n throw new Error(projects.errors[0]?.message || 'getProjects failed');\n }\n const projectId = projects.data?.getProjects?.data?.find((p) => p?.id?.trim())?.id?.trim() || '';\n if (!projectId) {\n throw new Error(`No project on the canvas backend for org \"${orgName}\".`);\n }\n\n const tags = await gqlFetch<{ getEnvironmentTags?: Array<{ id?: string | null } | null> | null }>(\n graphqlUrl,\n idToken,\n GET_ENVIRONMENT_TAGS,\n { projectId },\n );\n if (tags.errors?.length) {\n throw new Error(tags.errors[0]?.message || 'getEnvironmentTags failed');\n }\n const environmentTagId = tags.data?.getEnvironmentTags?.find((t) => t?.id?.trim())?.id?.trim() || '';\n if (!environmentTagId) {\n throw new Error(`No environment tag on the canvas backend for project \"${projectId}\".`);\n }\n\n return { orgName, projectId, environmentTagId };\n}\n\nfunction assertMintableProjectId(projectId: string): void {\n if (projectId === 'default' || !projectId.trim()) {\n throw new Error('Missing projectId — wait for org/project bootstrap, then retry.');\n }\n if (!__DEV__ && projectId === DEV_TESTING_PROJECT_UUID) {\n throw new Error(\n 'Invalid projectId (dev testing UUID). Open Home so a real project can be created, then retry.',\n );\n }\n if (!OBJECT_ID_HEX.test(projectId) && !PROJECT_UUID.test(projectId)) {\n throw new Error(`Invalid projectId format for system token mint: ${projectId.slice(0, 48)}`);\n }\n}\n\nexport async function mintSurfaceSystemToken(input: MintSurfaceTokenInput): Promise<MintSurfaceTokenResult> {\n const idToken = await readAuth0IdToken();\n if (!idToken) {\n throw new Error('No Auth0 id_token in storage — sign in to the mobile app first.');\n }\n\n const graphqlUrl = resolveSurfaceCatalogBackend(input.graphqlUrl);\n if (__DEV__) {\n // eslint-disable-next-line no-console\n console.log('[WebBuilder] minting system token via', graphqlUrl, input.discoverScope ? '(discover scope)' : '');\n }\n\n let orgName = input.orgName?.trim() || '';\n let projectId = input.projectId?.trim() || '';\n let environmentTagId = input.tagId?.trim() || '';\n\n if (input.discoverScope) {\n const scope = await discoverMintScope(graphqlUrl, idToken);\n orgName = scope.orgName;\n projectId = scope.projectId;\n environmentTagId = scope.environmentTagId;\n if (__DEV__) {\n // eslint-disable-next-line no-console\n console.log('[WebBuilder] discovered canvas scope', { orgName, projectId, environmentTagId });\n }\n }\n\n if (!orgName) {\n throw new Error('Missing orgName — wait for org bootstrap, then retry.');\n }\n if (!projectId) {\n throw new Error('Missing projectId — wait for org/project bootstrap, then retry.');\n }\n if (!environmentTagId) {\n throw new Error('Missing environmentTagId — cannot mint system token.');\n }\n assertMintableProjectId(projectId);\n\n // Canonical get-or-create (resolves project UUID → Mongo id server-side).\n try {\n const obtained = await gqlFetch<{ obtainUserSystemToken?: { token?: string } | null }>(\n graphqlUrl,\n idToken,\n OBTAIN_USER_SYSTEM_TOKEN,\n {\n input: {\n projectId,\n environmentTagId,\n orgName,\n renewBufferSeconds: 7 * 24 * 60 * 60,\n algorithm: 'HS256',\n },\n },\n );\n const token = obtained.data?.obtainUserSystemToken?.token?.trim();\n if (token && !obtained.errors?.length) {\n if (__DEV__) {\n // eslint-disable-next-line no-console\n console.log('[WebBuilder] obtained system token length=', token.length);\n }\n return { token, backend: graphqlUrl };\n }\n if (obtained.errors?.length) {\n throw new Error(obtained.errors[0]?.message || 'obtainUserSystemToken failed');\n }\n } catch (err) {\n if (__DEV__) {\n // eslint-disable-next-line no-console\n console.warn('[WebBuilder] obtainUserSystemToken failed, falling back to reveal:', err);\n }\n // Fall through to get/create/reveal only when obtain is unavailable.\n const msg = err instanceof Error ? err.message : String(err);\n if (/12 bytes|24 hex|Project not found/i.test(msg)) {\n throw new Error(\n `Project \"${projectId}\" is not valid on this backend. Pick/create a real project in the app, then retry.`,\n );\n }\n // Continue to legacy path for older backends without obtainUserSystemToken.\n if (!/Unknown field|Cannot query field|obtainUserSystemToken/i.test(msg)) {\n // Non-schema errors from obtain should surface (auth, project, etc.).\n if (/Unauthorized|401|Project not found|Invalid project/i.test(msg)) {\n throw err instanceof Error ? err : new Error(msg);\n }\n }\n }\n\n const getResult = await gqlFetch<{ getUserSystemToken?: { id?: string } | null }>(\n graphqlUrl,\n idToken,\n GET_USER_SYSTEM_TOKEN,\n );\n if (getResult.errors?.length) {\n throw new Error(getResult.errors[0]?.message || 'GetUserSystemToken failed');\n }\n\n let tokenId = getResult.data?.getUserSystemToken?.id ?? null;\n\n if (!tokenId) {\n const createResult = await gqlFetch<{ createSecretApiToken?: { id?: string } | null }>(\n graphqlUrl,\n idToken,\n CREATE_SECRET_API_TOKEN,\n {\n input: {\n name: 'System Token',\n expiryInSeconds: 31536000,\n isSystemToken: true,\n environmentTagId,\n orgName,\n projectId,\n algorithm: 'HS256',\n },\n },\n );\n\n const createErr = createResult.errors?.[0]?.message || '';\n if (createErr && !/already have a system token/i.test(createErr)) {\n if (/12 bytes|24 hex/i.test(createErr)) {\n throw new Error(\n `Project \"${projectId}\" is not valid on this backend. Pick/create a real project in the app, then retry.`,\n );\n }\n throw new Error(createErr);\n }\n\n if (createResult.data?.createSecretApiToken?.id) {\n tokenId = createResult.data.createSecretApiToken.id;\n } else {\n const refetch = await gqlFetch<{ getUserSystemToken?: { id?: string } | null }>(\n graphqlUrl,\n idToken,\n GET_USER_SYSTEM_TOKEN,\n );\n tokenId = refetch.data?.getUserSystemToken?.id ?? null;\n }\n }\n\n if (!tokenId) {\n throw new Error('No system token id on GraphQL backend — cannot reveal.');\n }\n\n const reveal = await gqlFetch<{ revealSecretApiToken?: string | null }>(\n graphqlUrl,\n idToken,\n REVEAL_SECRET_API_TOKEN,\n { id: tokenId },\n );\n if (reveal.errors?.length) {\n throw new Error(reveal.errors[0]?.message || 'RevealSecretApiToken failed');\n }\n\n const token = reveal.data?.revealSecretApiToken?.trim();\n if (!token) {\n throw new Error('RevealSecretApiToken returned empty token');\n }\n\n if (__DEV__) {\n // eslint-disable-next-line no-console\n console.log('[WebBuilder] minted system token length=', token.length);\n }\n return { token, backend: graphqlUrl };\n}\n\nexport function appendSurfaceAuthParams(baseUrl: string, token: string, state: string): string {\n try {\n const url = new URL(baseUrl);\n url.searchParams.set('token', token);\n url.searchParams.set('code', token);\n url.searchParams.set('state', state);\n return url.toString();\n } catch {\n const sep = baseUrl.includes('?') ? '&' : '?';\n return `${baseUrl}${sep}token=${encodeURIComponent(token)}&code=${encodeURIComponent(token)}&state=${encodeURIComponent(state)}`;\n }\n}\n\nfunction tokenFingerprint(token: string): string {\n const sig = token.split('.')[2] ?? token;\n return sig.slice(-24);\n}\n\n/** Shared WebView boot script: token + meta + fetch shims for surface-kit auth. */\nfunction buildSurfaceHandoffScript(token: string, backend: string, state: string, withGateRecovery: boolean): string {\n const safeToken = JSON.stringify(token);\n const safeBackend = JSON.stringify(backend);\n const safeState = JSON.stringify(state);\n const fp = JSON.stringify(tokenFingerprint(token));\n const recovery = withGateRecovery\n ? `\n function tryRecover() {\n stamp();\n var gate = document.querySelector('[data-surface-login]');\n if (!gate) return;\n if (sessionStorage.getItem('__yantra_wb_token_reload')) return;\n sessionStorage.setItem('__yantra_wb_token_reload', '1');\n var u = new URL(window.location.href);\n u.searchParams.set('token', t);\n u.searchParams.set('code', t);\n u.searchParams.set('state', state);\n window.location.replace(u.toString());\n }\n tryRecover();\n var i = 0;\n var timer = setInterval(function () {\n i += 1;\n tryRecover();\n if (i >= 40 || !document.querySelector('[data-surface-login]')) clearInterval(timer);\n }, 250);\n`\n : `\n var n = 0;\n var id = setInterval(function () {\n stamp();\n n += 1;\n if (n >= 80) clearInterval(id);\n }, 50);\n`;\n\n return `\n(function () {\n try {\n var t = ${safeToken};\n var backend = ${safeBackend};\n var state = ${safeState};\n var fp = ${fp};\n var PROBE_Q = 'query { getUserAccount { id } }';\n\n if (!window.__yantraWbFetchPatched) {\n window.__yantraWbFetchPatched = true;\n var origFetch = window.fetch.bind(window);\n window.fetch = function (input, init) {\n try {\n var url = typeof input === 'string' ? input : (input && input.url) ? String(input.url) : '';\n // Force surface-kit resolveCatalogBackend() to our GRAPHQL_URL (not remote agent → ideback).\n if (url.indexOf('/v1/auth/context') !== -1) {\n return Promise.resolve(new Response(\n JSON.stringify({ backendGraphqlUrl: backend }),\n { status: 200, headers: { 'Content-Type': 'application/json' } }\n ));\n }\n var body = init && typeof init.body === 'string' ? init.body : '';\n if (body && body.indexOf('getUserAccount') !== -1) {\n var parsed = JSON.parse(body);\n var q = parsed && typeof parsed.query === 'string' ? parsed.query.replace(/\\\\s+/g, ' ').trim() : '';\n if (q === PROBE_Q) {\n return Promise.resolve(new Response(\n JSON.stringify({ data: { getUserAccount: { id: 'mobile-webview-handoff' } } }),\n { status: 200, headers: { 'Content-Type': 'application/json' } }\n ));\n }\n }\n } catch (e0) {}\n return origFetch(input, init);\n };\n }\n\n function stamp() {\n try {\n sessionStorage.setItem('cdecli_oauth_state', state);\n localStorage.setItem('cdecli_token', t);\n sessionStorage.setItem('cdecli_token', t);\n localStorage.setItem('cdecli_system_token', t);\n localStorage.setItem('cdecli_token_meta', JSON.stringify({\n backend: backend,\n verifiedAt: Date.now(),\n fp: fp\n }));\n } catch (e1) {}\n }\n stamp();\n ${recovery}\n } catch (e) {}\n true;\n})();\ntrue;\n`;\n}\n\nexport function buildCdecliTokenInjection(token: string, backend: string, state: string): string {\n return buildSurfaceHandoffScript(token, backend, state, false);\n}\n\nexport function buildSurfaceGateRecoveryScript(token: string, backend: string, state: string): string {\n return buildSurfaceHandoffScript(token, backend, state, true);\n}\n"],"names":["_a","token"],"mappings":"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"}