@agent-native/core 0.76.9 → 0.76.11

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 (137) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/core/CHANGELOG.md +39 -0
  3. package/corpus/core/docs/content/locales/ar-SA/actions.md +7 -7
  4. package/corpus/core/docs/content/locales/ar-SA/template-plan.md +6 -4
  5. package/corpus/core/docs/content/locales/de-DE/actions.md +9 -9
  6. package/corpus/core/docs/content/locales/de-DE/template-plan.md +6 -4
  7. package/corpus/core/docs/content/locales/es-ES/actions.md +9 -9
  8. package/corpus/core/docs/content/locales/es-ES/template-plan.md +6 -4
  9. package/corpus/core/docs/content/locales/fr-FR/actions.md +9 -9
  10. package/corpus/core/docs/content/locales/fr-FR/template-plan.md +6 -4
  11. package/corpus/core/docs/content/locales/hi-IN/actions.md +9 -9
  12. package/corpus/core/docs/content/locales/hi-IN/template-plan.md +4 -4
  13. package/corpus/core/docs/content/locales/ja-JP/actions.md +9 -9
  14. package/corpus/core/docs/content/locales/ja-JP/template-plan.md +4 -4
  15. package/corpus/core/docs/content/locales/ko-KR/actions.md +9 -9
  16. package/corpus/core/docs/content/locales/ko-KR/template-plan.md +4 -4
  17. package/corpus/core/docs/content/locales/pt-BR/actions.md +9 -9
  18. package/corpus/core/docs/content/locales/pt-BR/template-plan.md +6 -4
  19. package/corpus/core/docs/content/locales/zh-CN/actions.md +9 -9
  20. package/corpus/core/docs/content/locales/zh-CN/template-plan.md +4 -4
  21. package/corpus/core/package.json +1 -1
  22. package/corpus/core/scripts/generate-og-font-data.mjs +9 -5
  23. package/corpus/core/src/client/AgentPanel.tsx +70 -1
  24. package/corpus/core/src/client/AssistantChat.tsx +1 -3
  25. package/corpus/core/src/client/ErrorBoundary.tsx +151 -10
  26. package/corpus/core/src/client/MultiTabAssistantChat.tsx +0 -62
  27. package/corpus/core/src/client/composer/TiptapComposer.tsx +6 -13
  28. package/corpus/core/src/server/og-fonts-data.ts +7 -4
  29. package/corpus/core/src/server/og-fonts.ts +10 -3
  30. package/corpus/core/src/server/social-og-image.ts +45 -6
  31. package/corpus/templates/analytics/app/components/dashboard/SqlChart.tsx +199 -19
  32. package/corpus/templates/analytics/app/components/layout/CommandPalette.tsx +51 -31
  33. package/corpus/templates/analytics/app/components/layout/DashboardHeader.tsx +26 -16
  34. package/corpus/templates/analytics/app/components/layout/Header.tsx +1 -2
  35. package/corpus/templates/analytics/app/components/layout/HeaderActions.tsx +8 -1
  36. package/corpus/templates/analytics/app/components/layout/Layout.tsx +2 -5
  37. package/corpus/templates/analytics/app/components/layout/Sidebar.tsx +172 -107
  38. package/corpus/templates/analytics/app/i18n-data.ts +1076 -5
  39. package/corpus/templates/analytics/app/pages/Settings.tsx +2 -2
  40. package/corpus/templates/analytics/app/pages/adhoc/AdhocRouter.tsx +1 -21
  41. package/corpus/templates/analytics/app/pages/adhoc/DashboardSkeleton.tsx +35 -0
  42. package/corpus/templates/analytics/app/pages/adhoc/explorer-dashboard/index.tsx +3 -23
  43. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/index.tsx +3 -26
  44. package/corpus/templates/analytics/changelog/2026-06-24-analytics-navigation-command-palette-and-settings-now-honor-.md +6 -0
  45. package/corpus/templates/analytics/changelog/2026-06-24-daily-dashboard-charts-now-use-local-day-buckets-and-show-th.md +6 -0
  46. package/corpus/templates/analytics/changelog/2026-06-24-dashboard-loading-placeholders-now-span-the-available-page-w.md +6 -0
  47. package/corpus/templates/analytics/changelog/2026-06-24-first-party-dashboard-dates-now-align-to-pacific-time-for-da.md +6 -0
  48. package/corpus/templates/analytics/server/lib/first-party-metric-catalog.ts +19 -7
  49. package/corpus/templates/assets/app/components/layout/Header.tsx +0 -2
  50. package/corpus/templates/calendar/app/components/layout/AppLayout.tsx +0 -2
  51. package/corpus/templates/calendar/app/pages/CalendarView.tsx +0 -2
  52. package/corpus/templates/chat/app/components/layout/Header.tsx +1 -6
  53. package/corpus/templates/clips/app/components/layout/Header.tsx +1 -6
  54. package/corpus/templates/clips/app/hooks/use-library.ts +2 -0
  55. package/corpus/templates/clips/app/routes/_app.settings._index.tsx +3 -1
  56. package/corpus/templates/clips/changelog/2026-06-24-archive-and-spaces-navigation-is-more-resilient-when-sidebar.md +6 -0
  57. package/corpus/templates/content/app/components/layout/Header.tsx +0 -2
  58. package/corpus/templates/design/app/components/layout/Header.tsx +1 -6
  59. package/corpus/templates/forms/app/components/layout/Header.tsx +1 -6
  60. package/corpus/templates/macros/app/components/layout/Header.tsx +1 -6
  61. package/corpus/templates/mail/app/components/email/CodeBlockLangPicker.tsx +4 -2
  62. package/corpus/templates/mail/app/components/email/ComposeBubbleToolbar.tsx +18 -12
  63. package/corpus/templates/mail/app/components/email/ComposeEditor.tsx +9 -5
  64. package/corpus/templates/mail/app/components/email/ComposeModal.tsx +36 -19
  65. package/corpus/templates/mail/app/components/email/EmailList.tsx +59 -55
  66. package/corpus/templates/mail/app/components/email/EmailListItem.tsx +7 -3
  67. package/corpus/templates/mail/app/components/email/EmailThread.tsx +25 -16
  68. package/corpus/templates/mail/app/components/email/InlineReplyComposer.tsx +25 -19
  69. package/corpus/templates/mail/app/components/email/IntegrationsSidebar.tsx +32 -17
  70. package/corpus/templates/mail/app/components/email/RecipientInput.tsx +26 -10
  71. package/corpus/templates/mail/app/components/email/SendLaterButton.tsx +5 -3
  72. package/corpus/templates/mail/app/components/email/SnoozeModal.tsx +4 -2
  73. package/corpus/templates/mail/app/components/email/SnoozePopover.tsx +9 -5
  74. package/corpus/templates/mail/app/components/email/extensions/ComposeImageBlock.tsx +4 -2
  75. package/corpus/templates/mail/app/components/layout/AppLayout.tsx +168 -86
  76. package/corpus/templates/mail/app/components/layout/SearchBar.tsx +4 -2
  77. package/corpus/templates/mail/app/i18n/ar-SA.ts +212 -0
  78. package/corpus/templates/mail/app/i18n/de-DE.ts +212 -0
  79. package/corpus/templates/mail/app/i18n/en-US.ts +212 -0
  80. package/corpus/templates/mail/app/i18n/es-ES.ts +212 -0
  81. package/corpus/templates/mail/app/i18n/fr-FR.ts +212 -0
  82. package/corpus/templates/mail/app/i18n/hi-IN.ts +212 -0
  83. package/corpus/templates/mail/app/i18n/ja-JP.ts +212 -0
  84. package/corpus/templates/mail/app/i18n/ko-KR.ts +212 -0
  85. package/corpus/templates/mail/app/i18n/pt-BR.ts +212 -0
  86. package/corpus/templates/mail/app/i18n/zh-CN.ts +211 -0
  87. package/corpus/templates/mail/changelog/2026-06-24-interface-language-support-now-covers-more-mail-controls-and.md +6 -0
  88. package/corpus/templates/plan/app/components/layout/Header.tsx +1 -6
  89. package/corpus/templates/slides/app/components/layout/Header.tsx +1 -6
  90. package/corpus/templates/videos/app/components/layout/Header.tsx +1 -6
  91. package/dist/client/AgentPanel.d.ts +5 -1
  92. package/dist/client/AgentPanel.d.ts.map +1 -1
  93. package/dist/client/AgentPanel.js +23 -4
  94. package/dist/client/AgentPanel.js.map +1 -1
  95. package/dist/client/AssistantChat.d.ts +1 -3
  96. package/dist/client/AssistantChat.d.ts.map +1 -1
  97. package/dist/client/AssistantChat.js.map +1 -1
  98. package/dist/client/ErrorBoundary.d.ts.map +1 -1
  99. package/dist/client/ErrorBoundary.js +128 -7
  100. package/dist/client/ErrorBoundary.js.map +1 -1
  101. package/dist/client/MultiTabAssistantChat.d.ts.map +1 -1
  102. package/dist/client/MultiTabAssistantChat.js +1 -17
  103. package/dist/client/MultiTabAssistantChat.js.map +1 -1
  104. package/dist/client/composer/TiptapComposer.d.ts +4 -0
  105. package/dist/client/composer/TiptapComposer.d.ts.map +1 -1
  106. package/dist/client/composer/TiptapComposer.js +5 -9
  107. package/dist/client/composer/TiptapComposer.js.map +1 -1
  108. package/dist/server/og-fonts-data.d.ts +1 -0
  109. package/dist/server/og-fonts-data.d.ts.map +1 -1
  110. package/dist/server/og-fonts-data.js +5 -4
  111. package/dist/server/og-fonts-data.js.map +1 -1
  112. package/dist/server/og-fonts.d.ts +1 -0
  113. package/dist/server/og-fonts.d.ts.map +1 -1
  114. package/dist/server/og-fonts.js +10 -4
  115. package/dist/server/og-fonts.js.map +1 -1
  116. package/dist/server/social-og-image.d.ts.map +1 -1
  117. package/dist/server/social-og-image.js +33 -6
  118. package/dist/server/social-og-image.js.map +1 -1
  119. package/docs/content/locales/ar-SA/actions.md +7 -7
  120. package/docs/content/locales/ar-SA/template-plan.md +6 -4
  121. package/docs/content/locales/de-DE/actions.md +9 -9
  122. package/docs/content/locales/de-DE/template-plan.md +6 -4
  123. package/docs/content/locales/es-ES/actions.md +9 -9
  124. package/docs/content/locales/es-ES/template-plan.md +6 -4
  125. package/docs/content/locales/fr-FR/actions.md +9 -9
  126. package/docs/content/locales/fr-FR/template-plan.md +6 -4
  127. package/docs/content/locales/hi-IN/actions.md +9 -9
  128. package/docs/content/locales/hi-IN/template-plan.md +4 -4
  129. package/docs/content/locales/ja-JP/actions.md +9 -9
  130. package/docs/content/locales/ja-JP/template-plan.md +4 -4
  131. package/docs/content/locales/ko-KR/actions.md +9 -9
  132. package/docs/content/locales/ko-KR/template-plan.md +4 -4
  133. package/docs/content/locales/pt-BR/actions.md +9 -9
  134. package/docs/content/locales/pt-BR/template-plan.md +6 -4
  135. package/docs/content/locales/zh-CN/actions.md +9 -9
  136. package/docs/content/locales/zh-CN/template-plan.md +4 -4
  137. package/package.json +1 -1
@@ -1,4 +1,5 @@
1
1
  export declare const OG_FONT_FAMILY = "Liberation Sans";
2
+ export declare const OG_ARABIC_FONT_FAMILY = "Noto Naskh Arabic";
2
3
  /**
3
4
  * Materialize the embedded OG fonts to disk and return their paths for resvg's
4
5
  * `fontFiles` option. resvg 2.x only accepts file paths (no in-memory buffers),
@@ -1 +1 @@
1
- {"version":3,"file":"og-fonts.d.ts","sourceRoot":"","sources":["../../src/server/og-fonts.ts"],"names":[],"mappings":"AAwBA,eAAO,MAAM,cAAc,oBAAoB,CAAC;AAIhD;;;;;;GAMG;AACH,wBAAgB,kBAAkB,IAAI,MAAM,EAAE,GAAG,SAAS,CA+BzD"}
1
+ {"version":3,"file":"og-fonts.d.ts","sourceRoot":"","sources":["../../src/server/og-fonts.ts"],"names":[],"mappings":"AA8BA,eAAO,MAAM,cAAc,oBAAoB,CAAC;AAChD,eAAO,MAAM,qBAAqB,sBAAsB,CAAC;AAIzD;;;;;;GAMG;AACH,wBAAgB,kBAAkB,IAAI,MAAM,EAAE,GAAG,SAAS,CA+BzD"}
@@ -2,13 +2,14 @@ import { createHash } from "node:crypto";
2
2
  import { existsSync, mkdirSync, writeFileSync } from "node:fs";
3
3
  import { tmpdir } from "node:os";
4
4
  import path from "node:path";
5
- import { LIBERATION_SANS_BOLD_BASE64, LIBERATION_SANS_REGULAR_BASE64, } from "./og-fonts-data.js";
5
+ import { LIBERATION_SANS_BOLD_BASE64, LIBERATION_SANS_REGULAR_BASE64, NOTO_NASKH_ARABIC_BASE64, } from "./og-fonts-data.js";
6
6
  /**
7
7
  * Liberation Sans is the metric-compatible libre replacement for
8
- * Arial/Helvetica that the OG image SVG asks for. It ships embedded as base64
8
+ * Arial/Helvetica that the OG image SVG asks for. Noto Naskh Arabic fills the
9
+ * Arabic script gap for localized docs titles. They ship embedded as base64
9
10
  * (see {@link ./og-fonts-data.ts}) so the renderer never depends on the host's
10
- * system fonts — Linux serverless runtimes (Netlify/Lambda) have neither Arial
11
- * nor Inter, which previously left every `<text>` element rendering nothing.
11
+ * system fonts — Linux serverless runtimes (Netlify/Lambda) have neither Arial,
12
+ * Inter, nor Arabic fallback fonts.
12
13
  */
13
14
  const OG_FONT_FILES = [
14
15
  {
@@ -16,8 +17,13 @@ const OG_FONT_FILES = [
16
17
  base64: LIBERATION_SANS_REGULAR_BASE64,
17
18
  },
18
19
  { filename: "LiberationSans-Bold.ttf", base64: LIBERATION_SANS_BOLD_BASE64 },
20
+ {
21
+ filename: "NotoNaskhArabic-Variable.ttf",
22
+ base64: NOTO_NASKH_ARABIC_BASE64,
23
+ },
19
24
  ];
20
25
  export const OG_FONT_FAMILY = "Liberation Sans";
26
+ export const OG_ARABIC_FONT_FAMILY = "Noto Naskh Arabic";
21
27
  let cachedFontFiles;
22
28
  /**
23
29
  * Materialize the embedded OG fonts to disk and return their paths for resvg's
@@ -1 +1 @@
1
- {"version":3,"file":"og-fonts.js","sourceRoot":"","sources":["../../src/server/og-fonts.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAC/D,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EACL,2BAA2B,EAC3B,8BAA8B,GAC/B,MAAM,oBAAoB,CAAC;AAE5B;;;;;;GAMG;AACH,MAAM,aAAa,GAAG;IACpB;QACE,QAAQ,EAAE,4BAA4B;QACtC,MAAM,EAAE,8BAA8B;KACvC;IACD,EAAE,QAAQ,EAAE,yBAAyB,EAAE,MAAM,EAAE,2BAA2B,EAAE;CACpE,CAAC;AAEX,MAAM,CAAC,MAAM,cAAc,GAAG,iBAAiB,CAAC;AAEhD,IAAI,eAA4C,CAAC;AAEjD;;;;;;GAMG;AACH,MAAM,UAAU,kBAAkB;IAChC,IAAI,eAAe,KAAK,SAAS;QAAE,OAAO,eAAe,IAAI,SAAS,CAAC;IAEvE,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC;QAClC,MAAM,OAAO,GAAG,aAAa,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE;YACzC,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;YACjD,IAAI,CAAC,KAAK,CAAC,UAAU;gBAAE,MAAM,IAAI,KAAK,CAAC,eAAe,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC;YACvE,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YAC3B,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YACnB,OAAO,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,KAAK,EAAE,CAAC;QAC5C,CAAC,CAAC,CAAC;QAEH,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CACvB,MAAM,EAAE,EACR,yBAAyB,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,CAC3D,CAAC;QACF,SAAS,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAExC,MAAM,SAAS,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,EAAE,EAAE;YACpD,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;YAC5C,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC;gBAAE,aAAa,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;YACtD,OAAO,MAAM,CAAC;QAChB,CAAC,CAAC,CAAC;QAEH,eAAe,GAAG,SAAS,CAAC;QAC5B,OAAO,SAAS,CAAC;IACnB,CAAC;IAAC,MAAM,CAAC;QACP,eAAe,GAAG,IAAI,CAAC;QACvB,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC","sourcesContent":["import { createHash } from \"node:crypto\";\nimport { existsSync, mkdirSync, writeFileSync } from \"node:fs\";\nimport { tmpdir } from \"node:os\";\nimport path from \"node:path\";\nimport {\n LIBERATION_SANS_BOLD_BASE64,\n LIBERATION_SANS_REGULAR_BASE64,\n} from \"./og-fonts-data.js\";\n\n/**\n * Liberation Sans is the metric-compatible libre replacement for\n * Arial/Helvetica that the OG image SVG asks for. It ships embedded as base64\n * (see {@link ./og-fonts-data.ts}) so the renderer never depends on the host's\n * system fonts — Linux serverless runtimes (Netlify/Lambda) have neither Arial\n * nor Inter, which previously left every `<text>` element rendering nothing.\n */\nconst OG_FONT_FILES = [\n {\n filename: \"LiberationSans-Regular.ttf\",\n base64: LIBERATION_SANS_REGULAR_BASE64,\n },\n { filename: \"LiberationSans-Bold.ttf\", base64: LIBERATION_SANS_BOLD_BASE64 },\n] as const;\n\nexport const OG_FONT_FAMILY = \"Liberation Sans\";\n\nlet cachedFontFiles: string[] | null | undefined;\n\n/**\n * Materialize the embedded OG fonts to disk and return their paths for resvg's\n * `fontFiles` option. resvg 2.x only accepts file paths (no in-memory buffers),\n * so the bytes are written once to a content-hashed tmp directory and cached\n * for the lifetime of the process. Returns `undefined` if the fonts can't be\n * written, letting the caller fall back to system fonts.\n */\nexport function resolveOgFontFiles(): string[] | undefined {\n if (cachedFontFiles !== undefined) return cachedFontFiles ?? undefined;\n\n try {\n const hash = createHash(\"sha256\");\n const decoded = OG_FONT_FILES.map((font) => {\n const bytes = Buffer.from(font.base64, \"base64\");\n if (!bytes.byteLength) throw new Error(`empty font: ${font.filename}`);\n hash.update(font.filename);\n hash.update(bytes);\n return { filename: font.filename, bytes };\n });\n\n const fontDir = path.join(\n tmpdir(),\n `agent-native-og-fonts-${hash.digest(\"hex\").slice(0, 16)}`,\n );\n mkdirSync(fontDir, { recursive: true });\n\n const fontFiles = decoded.map(({ filename, bytes }) => {\n const target = path.join(fontDir, filename);\n if (!existsSync(target)) writeFileSync(target, bytes);\n return target;\n });\n\n cachedFontFiles = fontFiles;\n return fontFiles;\n } catch {\n cachedFontFiles = null;\n return undefined;\n }\n}\n"]}
1
+ {"version":3,"file":"og-fonts.js","sourceRoot":"","sources":["../../src/server/og-fonts.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAC/D,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EACL,2BAA2B,EAC3B,8BAA8B,EAC9B,wBAAwB,GACzB,MAAM,oBAAoB,CAAC;AAE5B;;;;;;;GAOG;AACH,MAAM,aAAa,GAAG;IACpB;QACE,QAAQ,EAAE,4BAA4B;QACtC,MAAM,EAAE,8BAA8B;KACvC;IACD,EAAE,QAAQ,EAAE,yBAAyB,EAAE,MAAM,EAAE,2BAA2B,EAAE;IAC5E;QACE,QAAQ,EAAE,8BAA8B;QACxC,MAAM,EAAE,wBAAwB;KACjC;CACO,CAAC;AAEX,MAAM,CAAC,MAAM,cAAc,GAAG,iBAAiB,CAAC;AAChD,MAAM,CAAC,MAAM,qBAAqB,GAAG,mBAAmB,CAAC;AAEzD,IAAI,eAA4C,CAAC;AAEjD;;;;;;GAMG;AACH,MAAM,UAAU,kBAAkB;IAChC,IAAI,eAAe,KAAK,SAAS;QAAE,OAAO,eAAe,IAAI,SAAS,CAAC;IAEvE,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC;QAClC,MAAM,OAAO,GAAG,aAAa,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE;YACzC,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;YACjD,IAAI,CAAC,KAAK,CAAC,UAAU;gBAAE,MAAM,IAAI,KAAK,CAAC,eAAe,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC;YACvE,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YAC3B,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YACnB,OAAO,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,KAAK,EAAE,CAAC;QAC5C,CAAC,CAAC,CAAC;QAEH,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CACvB,MAAM,EAAE,EACR,yBAAyB,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,CAC3D,CAAC;QACF,SAAS,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAExC,MAAM,SAAS,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,EAAE,EAAE;YACpD,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;YAC5C,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC;gBAAE,aAAa,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;YACtD,OAAO,MAAM,CAAC;QAChB,CAAC,CAAC,CAAC;QAEH,eAAe,GAAG,SAAS,CAAC;QAC5B,OAAO,SAAS,CAAC;IACnB,CAAC;IAAC,MAAM,CAAC;QACP,eAAe,GAAG,IAAI,CAAC;QACvB,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC","sourcesContent":["import { createHash } from \"node:crypto\";\nimport { existsSync, mkdirSync, writeFileSync } from \"node:fs\";\nimport { tmpdir } from \"node:os\";\nimport path from \"node:path\";\nimport {\n LIBERATION_SANS_BOLD_BASE64,\n LIBERATION_SANS_REGULAR_BASE64,\n NOTO_NASKH_ARABIC_BASE64,\n} from \"./og-fonts-data.js\";\n\n/**\n * Liberation Sans is the metric-compatible libre replacement for\n * Arial/Helvetica that the OG image SVG asks for. Noto Naskh Arabic fills the\n * Arabic script gap for localized docs titles. They ship embedded as base64\n * (see {@link ./og-fonts-data.ts}) so the renderer never depends on the host's\n * system fonts — Linux serverless runtimes (Netlify/Lambda) have neither Arial,\n * Inter, nor Arabic fallback fonts.\n */\nconst OG_FONT_FILES = [\n {\n filename: \"LiberationSans-Regular.ttf\",\n base64: LIBERATION_SANS_REGULAR_BASE64,\n },\n { filename: \"LiberationSans-Bold.ttf\", base64: LIBERATION_SANS_BOLD_BASE64 },\n {\n filename: \"NotoNaskhArabic-Variable.ttf\",\n base64: NOTO_NASKH_ARABIC_BASE64,\n },\n] as const;\n\nexport const OG_FONT_FAMILY = \"Liberation Sans\";\nexport const OG_ARABIC_FONT_FAMILY = \"Noto Naskh Arabic\";\n\nlet cachedFontFiles: string[] | null | undefined;\n\n/**\n * Materialize the embedded OG fonts to disk and return their paths for resvg's\n * `fontFiles` option. resvg 2.x only accepts file paths (no in-memory buffers),\n * so the bytes are written once to a content-hashed tmp directory and cached\n * for the lifetime of the process. Returns `undefined` if the fonts can't be\n * written, letting the caller fall back to system fonts.\n */\nexport function resolveOgFontFiles(): string[] | undefined {\n if (cachedFontFiles !== undefined) return cachedFontFiles ?? undefined;\n\n try {\n const hash = createHash(\"sha256\");\n const decoded = OG_FONT_FILES.map((font) => {\n const bytes = Buffer.from(font.base64, \"base64\");\n if (!bytes.byteLength) throw new Error(`empty font: ${font.filename}`);\n hash.update(font.filename);\n hash.update(bytes);\n return { filename: font.filename, bytes };\n });\n\n const fontDir = path.join(\n tmpdir(),\n `agent-native-og-fonts-${hash.digest(\"hex\").slice(0, 16)}`,\n );\n mkdirSync(fontDir, { recursive: true });\n\n const fontFiles = decoded.map(({ filename, bytes }) => {\n const target = path.join(fontDir, filename);\n if (!existsSync(target)) writeFileSync(target, bytes);\n return target;\n });\n\n cachedFontFiles = fontFiles;\n return fontFiles;\n } catch {\n cachedFontFiles = null;\n return undefined;\n }\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"social-og-image.d.ts","sourceRoot":"","sources":["../../src/server/social-og-image.ts"],"names":[],"mappings":"AAAA,OAAO,EAML,KAAK,OAAO,EACb,MAAM,IAAI,CAAC;AAQZ,MAAM,WAAW,uBAAuB;IACtC,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B;AAED,eAAO,MAAM,2BAA2B,OAAO,CAAC;AAChD,eAAO,MAAM,4BAA4B,MAAM,CAAC;AAChD,eAAO,MAAM,mCAAmC,2EAC0B,CAAC;AAC3E,eAAO,MAAM,2CAA2C,oFAC2B,CAAC;AAuMpF,wBAAgB,gCAAgC,CAAC,KAAK,CAAC,EAAE,OAAO,GAAG,MAAM,CAyBxE;AAqBD,wBAAgB,8BAA8B,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAUtE;AAED,wBAAgB,2BAA2B,CACzC,KAAK,GAAE,uBAA4B,GAClC,MAAM,CA0CR;AAED,wBAAsB,2BAA2B,CAC/C,KAAK,GAAE,uBAA4B,GAClC,OAAO,CAAC,UAAU,CAAC,CAuBrB;AAED,wBAAgB,iCAAiC,CAC/C,UAAU,CAAC,EAAE,MAAM,EACnB,WAAW,SAAc,GACxB,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAYxB;AAED,wBAAgB,+BAA+B,CAC7C,OAAO,GAAE,uBAA4B,2FAsCtC"}
1
+ {"version":3,"file":"social-og-image.d.ts","sourceRoot":"","sources":["../../src/server/social-og-image.ts"],"names":[],"mappings":"AAAA,OAAO,EAML,KAAK,OAAO,EACb,MAAM,IAAI,CAAC;AAYZ,MAAM,WAAW,uBAAuB;IACtC,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B;AAED,eAAO,MAAM,2BAA2B,OAAO,CAAC;AAChD,eAAO,MAAM,4BAA4B,MAAM,CAAC;AAChD,eAAO,MAAM,mCAAmC,2EAC0B,CAAC;AAC3E,eAAO,MAAM,2CAA2C,oFAC2B,CAAC;AAyNpF,wBAAgB,gCAAgC,CAAC,KAAK,CAAC,EAAE,OAAO,GAAG,MAAM,CAyBxE;AAqBD,wBAAgB,8BAA8B,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAUtE;AAED,wBAAgB,2BAA2B,CACzC,KAAK,GAAE,uBAA4B,GAClC,MAAM,CA2DR;AAED,wBAAsB,2BAA2B,CAC/C,KAAK,GAAE,uBAA4B,GAClC,OAAO,CAAC,UAAU,CAAC,CAuBrB;AAED,wBAAgB,iCAAiC,CAC/C,UAAU,CAAC,EAAE,MAAM,EACnB,WAAW,SAAc,GACxB,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAYxB;AAED,wBAAgB,+BAA+B,CAC7C,OAAO,GAAE,uBAA4B,2FAsCtC"}
@@ -1,7 +1,7 @@
1
1
  import { defineEventHandler, getHeader, getMethod, getQuery, getRequestURL, } from "h3";
2
2
  import { resolveBuiltInAuthMarketing, resolveBuiltInAuthMarketingByName, } from "./auth-marketing.js";
3
3
  import { getAppName } from "./app-name.js";
4
- import { OG_FONT_FAMILY, resolveOgFontFiles } from "./og-fonts.js";
4
+ import { OG_ARABIC_FONT_FAMILY, OG_FONT_FAMILY, resolveOgFontFiles, } from "./og-fonts.js";
5
5
  export const AGENT_NATIVE_OG_IMAGE_WIDTH = 1200;
6
6
  export const AGENT_NATIVE_OG_IMAGE_HEIGHT = 630;
7
7
  export const AGENT_NATIVE_OG_IMAGE_CACHE_CONTROL = "public, max-age=60, stale-while-revalidate=604800, stale-if-error=3600";
@@ -12,7 +12,8 @@ const BRAND_BLUE = "#00B5FF";
12
12
  const BRAND_MINT = "#48FFE4";
13
13
  const BG = "#000000";
14
14
  const FG = "#f5f5f5";
15
- const FONT_FAMILY = `${OG_FONT_FAMILY}, Arial, Helvetica, system-ui, sans-serif`;
15
+ const DEFAULT_FONT_FAMILY = `${OG_FONT_FAMILY}, Arial, Helvetica, system-ui, sans-serif`;
16
+ const ARABIC_FONT_FAMILY = `${OG_ARABIC_FONT_FAMILY}, ${OG_FONT_FAMILY}, Arial, Helvetica, system-ui, sans-serif`;
16
17
  const DEFAULT_ACCENT_TEXT = "100% free and open source";
17
18
  const LOGO_MARK = `
18
19
  <path d="M24.5537 65.7695H0L15.0859 39.4619L37.708 0L60.4912 39.4619H39.6396L24.5537 65.7695Z" fill="white"/>
@@ -68,6 +69,12 @@ function estimateTextWidth(value, fontSize) {
68
69
  }
69
70
  return units * fontSize;
70
71
  }
72
+ function containsArabicText(value) {
73
+ return /[\u0600-\u06ff\u0750-\u077f\u0870-\u089f\ufb50-\ufdff\ufe70-\ufeff]/u.test(value);
74
+ }
75
+ function fontFamilyForText(value) {
76
+ return containsArabicText(value) ? ARABIC_FONT_FAMILY : DEFAULT_FONT_FAMILY;
77
+ }
71
78
  function trimTextToWidth(value, fontSize, maxWidth) {
72
79
  const ellipsis = "...";
73
80
  let trimmed = value.trim();
@@ -142,8 +149,11 @@ function getTitleLayout(title) {
142
149
  lineHeight: 60,
143
150
  };
144
151
  }
145
- function textBlock({ lines, x, y, fontSize, lineHeight, weight, fill, anchor = "start", }) {
146
- return `<text x="${x}" y="${y}" text-anchor="${anchor}" font-family="${FONT_FAMILY}" font-size="${fontSize}" font-weight="${weight}" fill="${fill}">${lines
152
+ function textBlock({ lines, x, y, fontSize, lineHeight, weight, fill, anchor = "start", direction, fontFamily = DEFAULT_FONT_FAMILY, }) {
153
+ const directionAttrs = direction
154
+ ? ` direction="${direction}" unicode-bidi="plaintext"`
155
+ : "";
156
+ return `<text x="${x}" y="${y}" text-anchor="${anchor}"${directionAttrs} font-family="${fontFamily}" font-size="${fontSize}" font-weight="${weight}" fill="${fill}">${lines
147
157
  .map((line, index) => `<tspan x="${x}" dy="${index === 0 ? 0 : lineHeight}">${escapeSvg(line)}</tspan>`)
148
158
  .join("")}</text>`;
149
159
  }
@@ -195,6 +205,10 @@ export function renderAgentNativeOgImageSvg(input = {}) {
195
205
  const title = cleanText(input.title) || titleFromAppName(appName);
196
206
  const accentText = cleanText(input.accentText) || DEFAULT_ACCENT_TEXT;
197
207
  const titleLayout = getTitleLayout(title);
208
+ const titleIsRtl = containsArabicText(title);
209
+ const textX = titleIsRtl ? WIDTH - 80 : 80;
210
+ const accentX = titleIsRtl ? WIDTH - 84 : 84;
211
+ const textAnchor = titleIsRtl ? "end" : "start";
198
212
  const titleY = titleLayout.lines.length > 1 ? 288 : 330;
199
213
  const accentY = titleY + titleLayout.lineHeight * (titleLayout.lines.length - 1) + 70;
200
214
  return `<svg xmlns="http://www.w3.org/2000/svg" width="${WIDTH}" height="${HEIGHT}" viewBox="0 0 ${WIDTH} ${HEIGHT}">
@@ -216,7 +230,7 @@ export function renderAgentNativeOgImageSvg(input = {}) {
216
230
  <g>
217
231
  ${textBlock({
218
232
  lines: titleLayout.lines,
219
- x: 80,
233
+ x: textX,
220
234
  y: titleY,
221
235
  fontSize: titleLayout.fontSize,
222
236
  lineHeight: titleLayout.lineHeight,
@@ -225,8 +239,21 @@ export function renderAgentNativeOgImageSvg(input = {}) {
225
239
  // bundle, which is the intended look for the display title.
226
240
  weight: 800,
227
241
  fill: FG,
242
+ anchor: textAnchor,
243
+ direction: titleIsRtl ? "rtl" : undefined,
244
+ fontFamily: fontFamilyForText(title),
245
+ })}
246
+ ${textBlock({
247
+ lines: [accentText],
248
+ x: accentX,
249
+ y: accentY,
250
+ fontSize: 34,
251
+ lineHeight: 40,
252
+ weight: 800,
253
+ fill: BRAND_BLUE,
254
+ anchor: textAnchor,
255
+ fontFamily: fontFamilyForText(accentText),
228
256
  })}
229
- <text x="84" y="${accentY}" font-family="${FONT_FAMILY}" font-size="34" font-weight="800" fill="${BRAND_BLUE}">${escapeSvg(accentText)}</text>
230
257
  </g>
231
258
  </svg>`;
232
259
  }
@@ -1 +1 @@
1
- {"version":3,"file":"social-og-image.js","sourceRoot":"","sources":["../../src/server/social-og-image.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n defineEventHandler,\n getHeader,\n getMethod,\n getQuery,\n getRequestURL,\n type H3Event,\n} from \"h3\";\nimport {\n resolveBuiltInAuthMarketing,\n resolveBuiltInAuthMarketingByName,\n} from \"./auth-marketing.js\";\nimport { getAppName } from \"./app-name.js\";\nimport { OG_FONT_FAMILY, resolveOgFontFiles } from \"./og-fonts.js\";\n\nexport interface AgentNativeOgImageInput {\n appName?: string | null;\n title?: string | null;\n accentText?: string | null;\n}\n\nexport const AGENT_NATIVE_OG_IMAGE_WIDTH = 1200;\nexport const AGENT_NATIVE_OG_IMAGE_HEIGHT = 630;\nexport const AGENT_NATIVE_OG_IMAGE_CACHE_CONTROL =\n \"public, max-age=60, stale-while-revalidate=604800, stale-if-error=3600\";\nexport const AGENT_NATIVE_OG_IMAGE_NETLIFY_CACHE_CONTROL =\n \"public, durable, max-age=60, stale-while-revalidate=604800, stale-if-error=3600\";\n\nconst WIDTH = AGENT_NATIVE_OG_IMAGE_WIDTH;\nconst HEIGHT = AGENT_NATIVE_OG_IMAGE_HEIGHT;\nconst BRAND_BLUE = \"#00B5FF\";\nconst BRAND_MINT = \"#48FFE4\";\nconst BG = \"#000000\";\nconst FG = \"#f5f5f5\";\nconst FONT_FAMILY = `${OG_FONT_FAMILY}, Arial, Helvetica, system-ui, sans-serif`;\nconst DEFAULT_ACCENT_TEXT = \"100% free and open source\";\n\nconst LOGO_MARK = `\n <path d=\"M24.5537 65.7695H0L15.0859 39.4619L37.708 0L60.4912 39.4619H39.6396L24.5537 65.7695Z\" fill=\"white\"/>\n <path d=\"M89.446 0H114L76.2921 65.7704H51.7383L89.446 0Z\" fill=\"url(#brand)\"/>\n`;\n\nfunction escapeSvg(value: string): string {\n return value\n .replace(/&/g, \"&amp;\")\n .replace(/</g, \"&lt;\")\n .replace(/>/g, \"&gt;\")\n .replace(/\"/g, \"&quot;\");\n}\n\nfunction cleanText(value: string | null | undefined): string {\n return String(value ?? \"\")\n .replace(/\\s+/g, \" \")\n .trim();\n}\n\nfunction titleCase(value: string): string {\n return value\n .split(/[\\s._-]+/)\n .filter(Boolean)\n .map((part) => part.charAt(0).toUpperCase() + part.slice(1).toLowerCase())\n .join(\" \");\n}\n\nfunction titleFromAppName(appName: string): string {\n if (appName) return appName;\n const basePath =\n process.env.VITE_APP_BASE_PATH || process.env.APP_BASE_PATH || \"\";\n const slug = basePath.split(\"/\").filter(Boolean)[0] || \"\";\n return titleCase(slug) || \"Agent-Native\";\n}\n\ninterface WrappedText {\n lines: string[];\n truncated: boolean;\n}\n\ninterface TitleLayout {\n lines: string[];\n fontSize: number;\n lineHeight: number;\n}\n\nfunction estimateTextWidth(value: string, fontSize: number): number {\n let units = 0;\n for (const char of value) {\n if (char === \" \") {\n units += 0.28;\n } else if (/[MW@#%&]/.test(char)) {\n units += 0.86;\n } else if (/[A-Z]/.test(char)) {\n units += 0.64;\n } else if (/[ilI.,:;|!']/u.test(char)) {\n units += 0.26;\n } else if (/[0-9]/.test(char)) {\n units += 0.56;\n } else {\n units += 0.54;\n }\n }\n return units * fontSize;\n}\n\nfunction trimTextToWidth(\n value: string,\n fontSize: number,\n maxWidth: number,\n): string {\n const ellipsis = \"...\";\n let trimmed = value.trim();\n while (\n trimmed.length > 0 &&\n estimateTextWidth(`${trimmed}${ellipsis}`, fontSize) > maxWidth\n ) {\n trimmed = trimmed.slice(0, -1).trimEnd();\n }\n return trimmed ? `${trimmed}${ellipsis}` : ellipsis;\n}\n\nfunction wrapTextToWidth(\n value: string,\n fontSize: number,\n maxWidth: number,\n maxLines: number,\n): WrappedText {\n const words = value.split(/\\s+/).filter(Boolean);\n const lines: string[] = [];\n let current = \"\";\n let truncated = false;\n\n for (const word of words) {\n const next = current ? `${current} ${word}` : word;\n if (estimateTextWidth(next, fontSize) <= maxWidth) {\n current = next;\n continue;\n }\n if (!current) {\n lines.push(trimTextToWidth(word, fontSize, maxWidth));\n truncated = true;\n current = \"\";\n } else {\n lines.push(current);\n current = word;\n }\n if (lines.length === maxLines) {\n truncated = true;\n break;\n }\n }\n if (current && lines.length < maxLines) lines.push(current);\n\n const usedWordCount = lines.join(\" \").split(/\\s+/).filter(Boolean).length;\n if (usedWordCount < words.length && lines.length > 0) {\n lines[lines.length - 1] = trimTextToWidth(\n lines[lines.length - 1],\n fontSize,\n maxWidth,\n );\n truncated = true;\n }\n\n return {\n lines: lines.length ? lines : [trimTextToWidth(value, fontSize, maxWidth)],\n truncated,\n };\n}\n\nfunction getTitleLayout(title: string): TitleLayout {\n const maxTitleWidth = 900;\n if (estimateTextWidth(title, 88) <= maxTitleWidth) {\n return {\n lines: [title],\n fontSize: 88,\n lineHeight: 96,\n };\n }\n\n for (const fontSize of [76, 70, 64, 58, 52]) {\n const wrapped = wrapTextToWidth(title, fontSize, maxTitleWidth, 2);\n if (!wrapped.truncated) {\n const lineHeight = Math.round(fontSize * 1.1);\n return {\n lines: wrapped.lines,\n fontSize,\n lineHeight,\n };\n }\n }\n\n const fallbackFontSize = 52;\n const wrapped = wrapTextToWidth(title, fallbackFontSize, maxTitleWidth, 2);\n return {\n lines: wrapped.lines,\n fontSize: fallbackFontSize,\n lineHeight: 60,\n };\n}\n\nfunction textBlock({\n lines,\n x,\n y,\n fontSize,\n lineHeight,\n weight,\n fill,\n anchor = \"start\",\n}: {\n lines: string[];\n x: number;\n y: number;\n fontSize: number;\n lineHeight: number;\n weight: number;\n fill: string;\n anchor?: \"start\" | \"middle\";\n}): string {\n return `<text x=\"${x}\" y=\"${y}\" text-anchor=\"${anchor}\" font-family=\"${FONT_FAMILY}\" font-size=\"${fontSize}\" font-weight=\"${weight}\" fill=\"${fill}\">${lines\n .map(\n (line, index) =>\n `<tspan x=\"${x}\" dy=\"${index === 0 ? 0 : lineHeight}\">${escapeSvg(line)}</tspan>`,\n )\n .join(\"\")}</text>`;\n}\n\nexport function resolveAgentNativeOgImageAppName(event?: H3Event): string {\n const explicitAppName = cleanText(process.env.APP_NAME);\n if (explicitAppName) {\n return (\n resolveBuiltInAuthMarketingByName(explicitAppName)?.appName ??\n explicitAppName\n );\n }\n\n const requestHost = event\n ? (getHeader(event, \"x-forwarded-host\") ?? getHeader(event, \"host\"))\n : undefined;\n const requestPath = event ? getRequestURL(event).pathname : undefined;\n const builtInAppName = resolveBuiltInAuthMarketing({\n requestHost,\n requestPath,\n })?.appName;\n if (builtInAppName) return builtInAppName;\n\n const appName = getAppName();\n if (appName) {\n return resolveBuiltInAuthMarketingByName(appName)?.appName ?? appName;\n }\n\n return \"Agent-Native\";\n}\n\nfunction queryStringValue(\n value: unknown,\n maxLength: number,\n): string | undefined {\n if (typeof value !== \"string\") return undefined;\n const clean = cleanText(value).slice(0, maxLength);\n return clean || undefined;\n}\n\nfunction pngBody(bytes: Uint8Array): ArrayBuffer {\n const body = new ArrayBuffer(bytes.byteLength);\n new Uint8Array(body).set(bytes);\n return body;\n}\n\nfunction textByteLength(value: string): number {\n return new TextEncoder().encode(value).byteLength;\n}\n\nexport function isResvgRuntimeUnavailableError(error: unknown): boolean {\n const message = error instanceof Error ? error.message : String(error ?? \"\");\n return (\n /@resvg\\/resvg-js|resvgjs\\.[\\w-]+\\.node|native binding/i.test(message) &&\n // \"no such module\" is workerd's wording when the package is externalized\n // out of the Cloudflare worker bundle.\n /cannot find|no such module|err_module_not_found|dlopen|invalid elf|wrong architecture|not a valid win32|native binding/i.test(\n message,\n )\n );\n}\n\nexport function renderAgentNativeOgImageSvg(\n input: AgentNativeOgImageInput = {},\n): string {\n const appName =\n cleanText(input.appName) || resolveAgentNativeOgImageAppName();\n const title = cleanText(input.title) || titleFromAppName(appName);\n const accentText = cleanText(input.accentText) || DEFAULT_ACCENT_TEXT;\n const titleLayout = getTitleLayout(title);\n const titleY = titleLayout.lines.length > 1 ? 288 : 330;\n const accentY =\n titleY + titleLayout.lineHeight * (titleLayout.lines.length - 1) + 70;\n\n return `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"${WIDTH}\" height=\"${HEIGHT}\" viewBox=\"0 0 ${WIDTH} ${HEIGHT}\">\n <title>${escapeSvg(title)} - Agent-Native preview</title>\n <defs>\n <linearGradient id=\"brand\" x1=\"101.702\" y1=\"67.4791\" x2=\"113.672\" y2=\"-37.4275\" gradientUnits=\"userSpaceOnUse\">\n <stop stop-color=\"${BRAND_BLUE}\"/>\n <stop offset=\"1\" stop-color=\"${BRAND_MINT}\"/>\n </linearGradient>\n <pattern id=\"grid\" width=\"48\" height=\"48\" patternUnits=\"userSpaceOnUse\">\n <path d=\"M 48 0 L 0 0 0 48\" fill=\"none\" stroke=\"#ffffff\" stroke-opacity=\"0.07\" stroke-width=\"1\"/>\n </pattern>\n </defs>\n <rect width=\"${WIDTH}\" height=\"${HEIGHT}\" fill=\"${BG}\"/>\n <rect width=\"${WIDTH}\" height=\"${HEIGHT}\" fill=\"url(#grid)\"/>\n <g transform=\"translate(80 116) scale(0.94)\">\n ${LOGO_MARK}\n </g>\n <g>\n ${textBlock({\n lines: titleLayout.lines,\n x: 80,\n y: titleY,\n fontSize: titleLayout.fontSize,\n lineHeight: titleLayout.lineHeight,\n // resvg's fontdb maps font-weight 850 to the Regular face (only 400/700\n // exist for Liberation Sans); 800 resolves to Bold, the heaviest face we\n // bundle, which is the intended look for the display title.\n weight: 800,\n fill: FG,\n })}\n <text x=\"84\" y=\"${accentY}\" font-family=\"${FONT_FAMILY}\" font-size=\"34\" font-weight=\"800\" fill=\"${BRAND_BLUE}\">${escapeSvg(accentText)}</text>\n </g>\n</svg>`;\n}\n\nexport async function renderAgentNativeOgImagePng(\n input: AgentNativeOgImageInput = {},\n): Promise<Uint8Array> {\n const overridePackage =\n typeof process !== \"undefined\"\n ? process.env.AGENT_NATIVE_RESVG_PACKAGE\n : undefined;\n const resvgPackage = overridePackage || \"@resvg/resvg-js\";\n const { Resvg } = await import(/* @vite-ignore */ resvgPackage);\n // Feed resvg the embedded Liberation Sans font explicitly. System fonts can't\n // be relied on: Linux serverless runtimes (Netlify/Lambda) ship neither Arial\n // nor Inter, so without a bundled font every `<text>` rendered blank.\n const fontFiles = resolveOgFontFiles();\n const hasBundledFonts = Boolean(fontFiles?.length);\n const image = new Resvg(renderAgentNativeOgImageSvg(input), {\n fitTo: { mode: \"width\", value: WIDTH },\n font: {\n loadSystemFonts: !hasBundledFonts,\n ...(hasBundledFonts ? { fontFiles } : {}),\n defaultFontFamily: OG_FONT_FAMILY,\n serifFamily: OG_FONT_FAMILY,\n sansSerifFamily: OG_FONT_FAMILY,\n },\n }).render();\n return image.asPng();\n}\n\nexport function agentNativeOgImageResponseHeaders(\n byteLength?: number,\n contentType = \"image/png\",\n): Record<string, string> {\n const headers: Record<string, string> = {\n \"Content-Type\": contentType,\n \"Cache-Control\": AGENT_NATIVE_OG_IMAGE_CACHE_CONTROL,\n \"CDN-Cache-Control\": AGENT_NATIVE_OG_IMAGE_CACHE_CONTROL,\n \"Netlify-CDN-Cache-Control\": AGENT_NATIVE_OG_IMAGE_NETLIFY_CACHE_CONTROL,\n \"Cross-Origin-Resource-Policy\": \"cross-origin\",\n };\n if (typeof byteLength === \"number\") {\n headers[\"Content-Length\"] = String(byteLength);\n }\n return headers;\n}\n\nexport function createAgentNativeOgImageHandler(\n options: AgentNativeOgImageInput = {},\n) {\n return defineEventHandler(async (event) => {\n if (getMethod(event) === \"HEAD\") {\n return new Response(null, {\n headers: agentNativeOgImageResponseHeaders(),\n });\n }\n\n const query = getQuery(event);\n const appName =\n cleanText(options.appName) || resolveAgentNativeOgImageAppName(event);\n const input = {\n ...options,\n appName,\n title: cleanText(options.title) || queryStringValue(query.title, 140),\n accentText:\n cleanText(options.accentText) || queryStringValue(query.accentText, 80),\n };\n\n let png: Uint8Array;\n try {\n png = await renderAgentNativeOgImagePng(input);\n } catch (error) {\n if (!isResvgRuntimeUnavailableError(error)) throw error;\n const svg = renderAgentNativeOgImageSvg(input);\n return new Response(svg, {\n headers: agentNativeOgImageResponseHeaders(\n textByteLength(svg),\n \"image/svg+xml; charset=utf-8\",\n ),\n });\n }\n\n return new Response(pngBody(png), {\n headers: agentNativeOgImageResponseHeaders(png.byteLength),\n });\n });\n}\n"]}
1
+ {"version":3,"file":"social-og-image.js","sourceRoot":"","sources":["../../src/server/social-og-image.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,kBAAkB,EAClB,SAAS,EACT,SAAS,EACT,QAAQ,EACR,aAAa,GAEd,MAAM,IAAI,CAAC;AACZ,OAAO,EACL,2BAA2B,EAC3B,iCAAiC,GAClC,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAC3C,OAAO,EACL,qBAAqB,EACrB,cAAc,EACd,kBAAkB,GACnB,MAAM,eAAe,CAAC;AAQvB,MAAM,CAAC,MAAM,2BAA2B,GAAG,IAAI,CAAC;AAChD,MAAM,CAAC,MAAM,4BAA4B,GAAG,GAAG,CAAC;AAChD,MAAM,CAAC,MAAM,mCAAmC,GAC9C,wEAAwE,CAAC;AAC3E,MAAM,CAAC,MAAM,2CAA2C,GACtD,iFAAiF,CAAC;AAEpF,MAAM,KAAK,GAAG,2BAA2B,CAAC;AAC1C,MAAM,MAAM,GAAG,4BAA4B,CAAC;AAC5C,MAAM,UAAU,GAAG,SAAS,CAAC;AAC7B,MAAM,UAAU,GAAG,SAAS,CAAC;AAC7B,MAAM,EAAE,GAAG,SAAS,CAAC;AACrB,MAAM,EAAE,GAAG,SAAS,CAAC;AACrB,MAAM,mBAAmB,GAAG,GAAG,cAAc,2CAA2C,CAAC;AACzF,MAAM,kBAAkB,GAAG,GAAG,qBAAqB,KAAK,cAAc,2CAA2C,CAAC;AAClH,MAAM,mBAAmB,GAAG,2BAA2B,CAAC;AAExD,MAAM,SAAS,GAAG;;;CAGjB,CAAC;AAEF,SAAS,SAAS,CAAC,KAAa;IAC9B,OAAO,KAAK;SACT,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC;SACtB,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC;SACrB,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC;SACrB,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;AAC7B,CAAC;AAED,SAAS,SAAS,CAAC,KAAgC;IACjD,OAAO,MAAM,CAAC,KAAK,IAAI,EAAE,CAAC;SACvB,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC;SACpB,IAAI,EAAE,CAAC;AACZ,CAAC;AAED,SAAS,SAAS,CAAC,KAAa;IAC9B,OAAO,KAAK;SACT,KAAK,CAAC,UAAU,CAAC;SACjB,MAAM,CAAC,OAAO,CAAC;SACf,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;SACzE,IAAI,CAAC,GAAG,CAAC,CAAC;AACf,CAAC;AAED,SAAS,gBAAgB,CAAC,OAAe;IACvC,IAAI,OAAO;QAAE,OAAO,OAAO,CAAC;IAC5B,MAAM,QAAQ,GACZ,OAAO,CAAC,GAAG,CAAC,kBAAkB,IAAI,OAAO,CAAC,GAAG,CAAC,aAAa,IAAI,EAAE,CAAC;IACpE,MAAM,IAAI,GAAG,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IAC1D,OAAO,SAAS,CAAC,IAAI,CAAC,IAAI,cAAc,CAAC;AAC3C,CAAC;AAaD,SAAS,iBAAiB,CAAC,KAAa,EAAE,QAAgB;IACxD,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,IAAI,IAAI,KAAK,GAAG,EAAE,CAAC;YACjB,KAAK,IAAI,IAAI,CAAC;QAChB,CAAC;aAAM,IAAI,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YACjC,KAAK,IAAI,IAAI,CAAC;QAChB,CAAC;aAAM,IAAI,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YAC9B,KAAK,IAAI,IAAI,CAAC;QAChB,CAAC;aAAM,IAAI,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YACtC,KAAK,IAAI,IAAI,CAAC;QAChB,CAAC;aAAM,IAAI,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YAC9B,KAAK,IAAI,IAAI,CAAC;QAChB,CAAC;aAAM,CAAC;YACN,KAAK,IAAI,IAAI,CAAC;QAChB,CAAC;IACH,CAAC;IACD,OAAO,KAAK,GAAG,QAAQ,CAAC;AAC1B,CAAC;AAED,SAAS,kBAAkB,CAAC,KAAa;IACvC,OAAO,sEAAsE,CAAC,IAAI,CAChF,KAAK,CACN,CAAC;AACJ,CAAC;AAED,SAAS,iBAAiB,CAAC,KAAa;IACtC,OAAO,kBAAkB,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,kBAAkB,CAAC,CAAC,CAAC,mBAAmB,CAAC;AAC9E,CAAC;AAED,SAAS,eAAe,CACtB,KAAa,EACb,QAAgB,EAChB,QAAgB;IAEhB,MAAM,QAAQ,GAAG,KAAK,CAAC;IACvB,IAAI,OAAO,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;IAC3B,OACE,OAAO,CAAC,MAAM,GAAG,CAAC;QAClB,iBAAiB,CAAC,GAAG,OAAO,GAAG,QAAQ,EAAE,EAAE,QAAQ,CAAC,GAAG,QAAQ,EAC/D,CAAC;QACD,OAAO,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC;IAC3C,CAAC;IACD,OAAO,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,GAAG,QAAQ,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC;AACtD,CAAC;AAED,SAAS,eAAe,CACtB,KAAa,EACb,QAAgB,EAChB,QAAgB,EAChB,QAAgB;IAEhB,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IACjD,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,IAAI,OAAO,GAAG,EAAE,CAAC;IACjB,IAAI,SAAS,GAAG,KAAK,CAAC;IAEtB,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,IAAI,GAAG,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,IAAI,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;QACnD,IAAI,iBAAiB,CAAC,IAAI,EAAE,QAAQ,CAAC,IAAI,QAAQ,EAAE,CAAC;YAClD,OAAO,GAAG,IAAI,CAAC;YACf,SAAS;QACX,CAAC;QACD,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,KAAK,CAAC,IAAI,CAAC,eAAe,CAAC,IAAI,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC;YACtD,SAAS,GAAG,IAAI,CAAC;YACjB,OAAO,GAAG,EAAE,CAAC;QACf,CAAC;aAAM,CAAC;YACN,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YACpB,OAAO,GAAG,IAAI,CAAC;QACjB,CAAC;QACD,IAAI,KAAK,CAAC,MAAM,KAAK,QAAQ,EAAE,CAAC;YAC9B,SAAS,GAAG,IAAI,CAAC;YACjB,MAAM;QACR,CAAC;IACH,CAAC;IACD,IAAI,OAAO,IAAI,KAAK,CAAC,MAAM,GAAG,QAAQ;QAAE,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAE5D,MAAM,aAAa,GAAG,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC;IAC1E,IAAI,aAAa,GAAG,KAAK,CAAC,MAAM,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACrD,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,GAAG,eAAe,CACvC,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,EACvB,QAAQ,EACR,QAAQ,CACT,CAAC;QACF,SAAS,GAAG,IAAI,CAAC;IACnB,CAAC;IAED,OAAO;QACL,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,eAAe,CAAC,KAAK,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC;QAC1E,SAAS;KACV,CAAC;AACJ,CAAC;AAED,SAAS,cAAc,CAAC,KAAa;IACnC,MAAM,aAAa,GAAG,GAAG,CAAC;IAC1B,IAAI,iBAAiB,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,aAAa,EAAE,CAAC;QAClD,OAAO;YACL,KAAK,EAAE,CAAC,KAAK,CAAC;YACd,QAAQ,EAAE,EAAE;YACZ,UAAU,EAAE,EAAE;SACf,CAAC;IACJ,CAAC;IAED,KAAK,MAAM,QAAQ,IAAI,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC;QAC5C,MAAM,OAAO,GAAG,eAAe,CAAC,KAAK,EAAE,QAAQ,EAAE,aAAa,EAAE,CAAC,CAAC,CAAC;QACnE,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,CAAC;YACvB,MAAM,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,GAAG,GAAG,CAAC,CAAC;YAC9C,OAAO;gBACL,KAAK,EAAE,OAAO,CAAC,KAAK;gBACpB,QAAQ;gBACR,UAAU;aACX,CAAC;QACJ,CAAC;IACH,CAAC;IAED,MAAM,gBAAgB,GAAG,EAAE,CAAC;IAC5B,MAAM,OAAO,GAAG,eAAe,CAAC,KAAK,EAAE,gBAAgB,EAAE,aAAa,EAAE,CAAC,CAAC,CAAC;IAC3E,OAAO;QACL,KAAK,EAAE,OAAO,CAAC,KAAK;QACpB,QAAQ,EAAE,gBAAgB;QAC1B,UAAU,EAAE,EAAE;KACf,CAAC;AACJ,CAAC;AAED,SAAS,SAAS,CAAC,EACjB,KAAK,EACL,CAAC,EACD,CAAC,EACD,QAAQ,EACR,UAAU,EACV,MAAM,EACN,IAAI,EACJ,MAAM,GAAG,OAAO,EAChB,SAAS,EACT,UAAU,GAAG,mBAAmB,GAYjC;IACC,MAAM,cAAc,GAAG,SAAS;QAC9B,CAAC,CAAC,eAAe,SAAS,4BAA4B;QACtD,CAAC,CAAC,EAAE,CAAC;IACP,OAAO,YAAY,CAAC,QAAQ,CAAC,kBAAkB,MAAM,IAAI,cAAc,iBAAiB,UAAU,gBAAgB,QAAQ,kBAAkB,MAAM,WAAW,IAAI,KAAK,KAAK;SACxK,GAAG,CACF,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CACd,aAAa,CAAC,SAAS,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,UAAU,KAAK,SAAS,CAAC,IAAI,CAAC,UAAU,CACpF;SACA,IAAI,CAAC,EAAE,CAAC,SAAS,CAAC;AACvB,CAAC;AAED,MAAM,UAAU,gCAAgC,CAAC,KAAe;IAC9D,MAAM,eAAe,GAAG,SAAS,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IACxD,IAAI,eAAe,EAAE,CAAC;QACpB,OAAO,CACL,iCAAiC,CAAC,eAAe,CAAC,EAAE,OAAO;YAC3D,eAAe,CAChB,CAAC;IACJ,CAAC;IAED,MAAM,WAAW,GAAG,KAAK;QACvB,CAAC,CAAC,CAAC,SAAS,CAAC,KAAK,EAAE,kBAAkB,CAAC,IAAI,SAAS,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;QACpE,CAAC,CAAC,SAAS,CAAC;IACd,MAAM,WAAW,GAAG,KAAK,CAAC,CAAC,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC;IACtE,MAAM,cAAc,GAAG,2BAA2B,CAAC;QACjD,WAAW;QACX,WAAW;KACZ,CAAC,EAAE,OAAO,CAAC;IACZ,IAAI,cAAc;QAAE,OAAO,cAAc,CAAC;IAE1C,MAAM,OAAO,GAAG,UAAU,EAAE,CAAC;IAC7B,IAAI,OAAO,EAAE,CAAC;QACZ,OAAO,iCAAiC,CAAC,OAAO,CAAC,EAAE,OAAO,IAAI,OAAO,CAAC;IACxE,CAAC;IAED,OAAO,cAAc,CAAC;AACxB,CAAC;AAED,SAAS,gBAAgB,CACvB,KAAc,EACd,SAAiB;IAEjB,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,SAAS,CAAC;IAChD,MAAM,KAAK,GAAG,SAAS,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC;IACnD,OAAO,KAAK,IAAI,SAAS,CAAC;AAC5B,CAAC;AAED,SAAS,OAAO,CAAC,KAAiB;IAChC,MAAM,IAAI,GAAG,IAAI,WAAW,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;IAC/C,IAAI,UAAU,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IAChC,OAAO,IAAI,CAAC;AACd,CAAC;AAED,SAAS,cAAc,CAAC,KAAa;IACnC,OAAO,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,UAAU,CAAC;AACpD,CAAC;AAED,MAAM,UAAU,8BAA8B,CAAC,KAAc;IAC3D,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC;IAC7E,OAAO,CACL,wDAAwD,CAAC,IAAI,CAAC,OAAO,CAAC;QACtE,yEAAyE;QACzE,uCAAuC;QACvC,yHAAyH,CAAC,IAAI,CAC5H,OAAO,CACR,CACF,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,2BAA2B,CACzC,QAAiC,EAAE;IAEnC,MAAM,OAAO,GACX,SAAS,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,gCAAgC,EAAE,CAAC;IACjE,MAAM,KAAK,GAAG,SAAS,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,gBAAgB,CAAC,OAAO,CAAC,CAAC;IAClE,MAAM,UAAU,GAAG,SAAS,CAAC,KAAK,CAAC,UAAU,CAAC,IAAI,mBAAmB,CAAC;IACtE,MAAM,WAAW,GAAG,cAAc,CAAC,KAAK,CAAC,CAAC;IAC1C,MAAM,UAAU,GAAG,kBAAkB,CAAC,KAAK,CAAC,CAAC;IAC7C,MAAM,KAAK,GAAG,UAAU,CAAC,CAAC,CAAC,KAAK,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;IAC3C,MAAM,OAAO,GAAG,UAAU,CAAC,CAAC,CAAC,KAAK,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;IAC7C,MAAM,UAAU,GAAG,UAAU,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC;IAChD,MAAM,MAAM,GAAG,WAAW,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC;IACxD,MAAM,OAAO,GACX,MAAM,GAAG,WAAW,CAAC,UAAU,GAAG,CAAC,WAAW,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,GAAG,EAAE,CAAC;IAExE,OAAO,kDAAkD,KAAK,aAAa,MAAM,kBAAkB,KAAK,IAAI,MAAM;WACzG,SAAS,CAAC,KAAK,CAAC;;;0BAGD,UAAU;qCACC,UAAU;;;;;;iBAM9B,KAAK,aAAa,MAAM,WAAW,EAAE;iBACrC,KAAK,aAAa,MAAM;;MAEnC,SAAS;;;MAGT,SAAS,CAAC;QACV,KAAK,EAAE,WAAW,CAAC,KAAK;QACxB,CAAC,EAAE,KAAK;QACR,CAAC,EAAE,MAAM;QACT,QAAQ,EAAE,WAAW,CAAC,QAAQ;QAC9B,UAAU,EAAE,WAAW,CAAC,UAAU;QAClC,wEAAwE;QACxE,yEAAyE;QACzE,4DAA4D;QAC5D,MAAM,EAAE,GAAG;QACX,IAAI,EAAE,EAAE;QACR,MAAM,EAAE,UAAU;QAClB,SAAS,EAAE,UAAU,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS;QACzC,UAAU,EAAE,iBAAiB,CAAC,KAAK,CAAC;KACrC,CAAC;MACA,SAAS,CAAC;QACV,KAAK,EAAE,CAAC,UAAU,CAAC;QACnB,CAAC,EAAE,OAAO;QACV,CAAC,EAAE,OAAO;QACV,QAAQ,EAAE,EAAE;QACZ,UAAU,EAAE,EAAE;QACd,MAAM,EAAE,GAAG;QACX,IAAI,EAAE,UAAU;QAChB,MAAM,EAAE,UAAU;QAClB,UAAU,EAAE,iBAAiB,CAAC,UAAU,CAAC;KAC1C,CAAC;;OAEC,CAAC;AACR,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,2BAA2B,CAC/C,QAAiC,EAAE;IAEnC,MAAM,eAAe,GACnB,OAAO,OAAO,KAAK,WAAW;QAC5B,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,0BAA0B;QACxC,CAAC,CAAC,SAAS,CAAC;IAChB,MAAM,YAAY,GAAG,eAAe,IAAI,iBAAiB,CAAC;IAC1D,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,MAAM,CAAC,kBAAkB,CAAC,YAAY,CAAC,CAAC;IAChE,8EAA8E;IAC9E,8EAA8E;IAC9E,sEAAsE;IACtE,MAAM,SAAS,GAAG,kBAAkB,EAAE,CAAC;IACvC,MAAM,eAAe,GAAG,OAAO,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;IACnD,MAAM,KAAK,GAAG,IAAI,KAAK,CAAC,2BAA2B,CAAC,KAAK,CAAC,EAAE;QAC1D,KAAK,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE;QACtC,IAAI,EAAE;YACJ,eAAe,EAAE,CAAC,eAAe;YACjC,GAAG,CAAC,eAAe,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACzC,iBAAiB,EAAE,cAAc;YACjC,WAAW,EAAE,cAAc;YAC3B,eAAe,EAAE,cAAc;SAChC;KACF,CAAC,CAAC,MAAM,EAAE,CAAC;IACZ,OAAO,KAAK,CAAC,KAAK,EAAE,CAAC;AACvB,CAAC;AAED,MAAM,UAAU,iCAAiC,CAC/C,UAAmB,EACnB,WAAW,GAAG,WAAW;IAEzB,MAAM,OAAO,GAA2B;QACtC,cAAc,EAAE,WAAW;QAC3B,eAAe,EAAE,mCAAmC;QACpD,mBAAmB,EAAE,mCAAmC;QACxD,2BAA2B,EAAE,2CAA2C;QACxE,8BAA8B,EAAE,cAAc;KAC/C,CAAC;IACF,IAAI,OAAO,UAAU,KAAK,QAAQ,EAAE,CAAC;QACnC,OAAO,CAAC,gBAAgB,CAAC,GAAG,MAAM,CAAC,UAAU,CAAC,CAAC;IACjD,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,MAAM,UAAU,+BAA+B,CAC7C,UAAmC,EAAE;IAErC,OAAO,kBAAkB,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE;QACxC,IAAI,SAAS,CAAC,KAAK,CAAC,KAAK,MAAM,EAAE,CAAC;YAChC,OAAO,IAAI,QAAQ,CAAC,IAAI,EAAE;gBACxB,OAAO,EAAE,iCAAiC,EAAE;aAC7C,CAAC,CAAC;QACL,CAAC;QAED,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;QAC9B,MAAM,OAAO,GACX,SAAS,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,gCAAgC,CAAC,KAAK,CAAC,CAAC;QACxE,MAAM,KAAK,GAAG;YACZ,GAAG,OAAO;YACV,OAAO;YACP,KAAK,EAAE,SAAS,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,gBAAgB,CAAC,KAAK,CAAC,KAAK,EAAE,GAAG,CAAC;YACrE,UAAU,EACR,SAAS,CAAC,OAAO,CAAC,UAAU,CAAC,IAAI,gBAAgB,CAAC,KAAK,CAAC,UAAU,EAAE,EAAE,CAAC;SAC1E,CAAC;QAEF,IAAI,GAAe,CAAC;QACpB,IAAI,CAAC;YACH,GAAG,GAAG,MAAM,2BAA2B,CAAC,KAAK,CAAC,CAAC;QACjD,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,8BAA8B,CAAC,KAAK,CAAC;gBAAE,MAAM,KAAK,CAAC;YACxD,MAAM,GAAG,GAAG,2BAA2B,CAAC,KAAK,CAAC,CAAC;YAC/C,OAAO,IAAI,QAAQ,CAAC,GAAG,EAAE;gBACvB,OAAO,EAAE,iCAAiC,CACxC,cAAc,CAAC,GAAG,CAAC,EACnB,8BAA8B,CAC/B;aACF,CAAC,CAAC;QACL,CAAC;QAED,OAAO,IAAI,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE;YAChC,OAAO,EAAE,iCAAiC,CAAC,GAAG,CAAC,UAAU,CAAC;SAC3D,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC","sourcesContent":["import {\n defineEventHandler,\n getHeader,\n getMethod,\n getQuery,\n getRequestURL,\n type H3Event,\n} from \"h3\";\nimport {\n resolveBuiltInAuthMarketing,\n resolveBuiltInAuthMarketingByName,\n} from \"./auth-marketing.js\";\nimport { getAppName } from \"./app-name.js\";\nimport {\n OG_ARABIC_FONT_FAMILY,\n OG_FONT_FAMILY,\n resolveOgFontFiles,\n} from \"./og-fonts.js\";\n\nexport interface AgentNativeOgImageInput {\n appName?: string | null;\n title?: string | null;\n accentText?: string | null;\n}\n\nexport const AGENT_NATIVE_OG_IMAGE_WIDTH = 1200;\nexport const AGENT_NATIVE_OG_IMAGE_HEIGHT = 630;\nexport const AGENT_NATIVE_OG_IMAGE_CACHE_CONTROL =\n \"public, max-age=60, stale-while-revalidate=604800, stale-if-error=3600\";\nexport const AGENT_NATIVE_OG_IMAGE_NETLIFY_CACHE_CONTROL =\n \"public, durable, max-age=60, stale-while-revalidate=604800, stale-if-error=3600\";\n\nconst WIDTH = AGENT_NATIVE_OG_IMAGE_WIDTH;\nconst HEIGHT = AGENT_NATIVE_OG_IMAGE_HEIGHT;\nconst BRAND_BLUE = \"#00B5FF\";\nconst BRAND_MINT = \"#48FFE4\";\nconst BG = \"#000000\";\nconst FG = \"#f5f5f5\";\nconst DEFAULT_FONT_FAMILY = `${OG_FONT_FAMILY}, Arial, Helvetica, system-ui, sans-serif`;\nconst ARABIC_FONT_FAMILY = `${OG_ARABIC_FONT_FAMILY}, ${OG_FONT_FAMILY}, Arial, Helvetica, system-ui, sans-serif`;\nconst DEFAULT_ACCENT_TEXT = \"100% free and open source\";\n\nconst LOGO_MARK = `\n <path d=\"M24.5537 65.7695H0L15.0859 39.4619L37.708 0L60.4912 39.4619H39.6396L24.5537 65.7695Z\" fill=\"white\"/>\n <path d=\"M89.446 0H114L76.2921 65.7704H51.7383L89.446 0Z\" fill=\"url(#brand)\"/>\n`;\n\nfunction escapeSvg(value: string): string {\n return value\n .replace(/&/g, \"&amp;\")\n .replace(/</g, \"&lt;\")\n .replace(/>/g, \"&gt;\")\n .replace(/\"/g, \"&quot;\");\n}\n\nfunction cleanText(value: string | null | undefined): string {\n return String(value ?? \"\")\n .replace(/\\s+/g, \" \")\n .trim();\n}\n\nfunction titleCase(value: string): string {\n return value\n .split(/[\\s._-]+/)\n .filter(Boolean)\n .map((part) => part.charAt(0).toUpperCase() + part.slice(1).toLowerCase())\n .join(\" \");\n}\n\nfunction titleFromAppName(appName: string): string {\n if (appName) return appName;\n const basePath =\n process.env.VITE_APP_BASE_PATH || process.env.APP_BASE_PATH || \"\";\n const slug = basePath.split(\"/\").filter(Boolean)[0] || \"\";\n return titleCase(slug) || \"Agent-Native\";\n}\n\ninterface WrappedText {\n lines: string[];\n truncated: boolean;\n}\n\ninterface TitleLayout {\n lines: string[];\n fontSize: number;\n lineHeight: number;\n}\n\nfunction estimateTextWidth(value: string, fontSize: number): number {\n let units = 0;\n for (const char of value) {\n if (char === \" \") {\n units += 0.28;\n } else if (/[MW@#%&]/.test(char)) {\n units += 0.86;\n } else if (/[A-Z]/.test(char)) {\n units += 0.64;\n } else if (/[ilI.,:;|!']/u.test(char)) {\n units += 0.26;\n } else if (/[0-9]/.test(char)) {\n units += 0.56;\n } else {\n units += 0.54;\n }\n }\n return units * fontSize;\n}\n\nfunction containsArabicText(value: string): boolean {\n return /[\\u0600-\\u06ff\\u0750-\\u077f\\u0870-\\u089f\\ufb50-\\ufdff\\ufe70-\\ufeff]/u.test(\n value,\n );\n}\n\nfunction fontFamilyForText(value: string): string {\n return containsArabicText(value) ? ARABIC_FONT_FAMILY : DEFAULT_FONT_FAMILY;\n}\n\nfunction trimTextToWidth(\n value: string,\n fontSize: number,\n maxWidth: number,\n): string {\n const ellipsis = \"...\";\n let trimmed = value.trim();\n while (\n trimmed.length > 0 &&\n estimateTextWidth(`${trimmed}${ellipsis}`, fontSize) > maxWidth\n ) {\n trimmed = trimmed.slice(0, -1).trimEnd();\n }\n return trimmed ? `${trimmed}${ellipsis}` : ellipsis;\n}\n\nfunction wrapTextToWidth(\n value: string,\n fontSize: number,\n maxWidth: number,\n maxLines: number,\n): WrappedText {\n const words = value.split(/\\s+/).filter(Boolean);\n const lines: string[] = [];\n let current = \"\";\n let truncated = false;\n\n for (const word of words) {\n const next = current ? `${current} ${word}` : word;\n if (estimateTextWidth(next, fontSize) <= maxWidth) {\n current = next;\n continue;\n }\n if (!current) {\n lines.push(trimTextToWidth(word, fontSize, maxWidth));\n truncated = true;\n current = \"\";\n } else {\n lines.push(current);\n current = word;\n }\n if (lines.length === maxLines) {\n truncated = true;\n break;\n }\n }\n if (current && lines.length < maxLines) lines.push(current);\n\n const usedWordCount = lines.join(\" \").split(/\\s+/).filter(Boolean).length;\n if (usedWordCount < words.length && lines.length > 0) {\n lines[lines.length - 1] = trimTextToWidth(\n lines[lines.length - 1],\n fontSize,\n maxWidth,\n );\n truncated = true;\n }\n\n return {\n lines: lines.length ? lines : [trimTextToWidth(value, fontSize, maxWidth)],\n truncated,\n };\n}\n\nfunction getTitleLayout(title: string): TitleLayout {\n const maxTitleWidth = 900;\n if (estimateTextWidth(title, 88) <= maxTitleWidth) {\n return {\n lines: [title],\n fontSize: 88,\n lineHeight: 96,\n };\n }\n\n for (const fontSize of [76, 70, 64, 58, 52]) {\n const wrapped = wrapTextToWidth(title, fontSize, maxTitleWidth, 2);\n if (!wrapped.truncated) {\n const lineHeight = Math.round(fontSize * 1.1);\n return {\n lines: wrapped.lines,\n fontSize,\n lineHeight,\n };\n }\n }\n\n const fallbackFontSize = 52;\n const wrapped = wrapTextToWidth(title, fallbackFontSize, maxTitleWidth, 2);\n return {\n lines: wrapped.lines,\n fontSize: fallbackFontSize,\n lineHeight: 60,\n };\n}\n\nfunction textBlock({\n lines,\n x,\n y,\n fontSize,\n lineHeight,\n weight,\n fill,\n anchor = \"start\",\n direction,\n fontFamily = DEFAULT_FONT_FAMILY,\n}: {\n lines: string[];\n x: number;\n y: number;\n fontSize: number;\n lineHeight: number;\n weight: number;\n fill: string;\n anchor?: \"start\" | \"middle\" | \"end\";\n direction?: \"ltr\" | \"rtl\";\n fontFamily?: string;\n}): string {\n const directionAttrs = direction\n ? ` direction=\"${direction}\" unicode-bidi=\"plaintext\"`\n : \"\";\n return `<text x=\"${x}\" y=\"${y}\" text-anchor=\"${anchor}\"${directionAttrs} font-family=\"${fontFamily}\" font-size=\"${fontSize}\" font-weight=\"${weight}\" fill=\"${fill}\">${lines\n .map(\n (line, index) =>\n `<tspan x=\"${x}\" dy=\"${index === 0 ? 0 : lineHeight}\">${escapeSvg(line)}</tspan>`,\n )\n .join(\"\")}</text>`;\n}\n\nexport function resolveAgentNativeOgImageAppName(event?: H3Event): string {\n const explicitAppName = cleanText(process.env.APP_NAME);\n if (explicitAppName) {\n return (\n resolveBuiltInAuthMarketingByName(explicitAppName)?.appName ??\n explicitAppName\n );\n }\n\n const requestHost = event\n ? (getHeader(event, \"x-forwarded-host\") ?? getHeader(event, \"host\"))\n : undefined;\n const requestPath = event ? getRequestURL(event).pathname : undefined;\n const builtInAppName = resolveBuiltInAuthMarketing({\n requestHost,\n requestPath,\n })?.appName;\n if (builtInAppName) return builtInAppName;\n\n const appName = getAppName();\n if (appName) {\n return resolveBuiltInAuthMarketingByName(appName)?.appName ?? appName;\n }\n\n return \"Agent-Native\";\n}\n\nfunction queryStringValue(\n value: unknown,\n maxLength: number,\n): string | undefined {\n if (typeof value !== \"string\") return undefined;\n const clean = cleanText(value).slice(0, maxLength);\n return clean || undefined;\n}\n\nfunction pngBody(bytes: Uint8Array): ArrayBuffer {\n const body = new ArrayBuffer(bytes.byteLength);\n new Uint8Array(body).set(bytes);\n return body;\n}\n\nfunction textByteLength(value: string): number {\n return new TextEncoder().encode(value).byteLength;\n}\n\nexport function isResvgRuntimeUnavailableError(error: unknown): boolean {\n const message = error instanceof Error ? error.message : String(error ?? \"\");\n return (\n /@resvg\\/resvg-js|resvgjs\\.[\\w-]+\\.node|native binding/i.test(message) &&\n // \"no such module\" is workerd's wording when the package is externalized\n // out of the Cloudflare worker bundle.\n /cannot find|no such module|err_module_not_found|dlopen|invalid elf|wrong architecture|not a valid win32|native binding/i.test(\n message,\n )\n );\n}\n\nexport function renderAgentNativeOgImageSvg(\n input: AgentNativeOgImageInput = {},\n): string {\n const appName =\n cleanText(input.appName) || resolveAgentNativeOgImageAppName();\n const title = cleanText(input.title) || titleFromAppName(appName);\n const accentText = cleanText(input.accentText) || DEFAULT_ACCENT_TEXT;\n const titleLayout = getTitleLayout(title);\n const titleIsRtl = containsArabicText(title);\n const textX = titleIsRtl ? WIDTH - 80 : 80;\n const accentX = titleIsRtl ? WIDTH - 84 : 84;\n const textAnchor = titleIsRtl ? \"end\" : \"start\";\n const titleY = titleLayout.lines.length > 1 ? 288 : 330;\n const accentY =\n titleY + titleLayout.lineHeight * (titleLayout.lines.length - 1) + 70;\n\n return `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"${WIDTH}\" height=\"${HEIGHT}\" viewBox=\"0 0 ${WIDTH} ${HEIGHT}\">\n <title>${escapeSvg(title)} - Agent-Native preview</title>\n <defs>\n <linearGradient id=\"brand\" x1=\"101.702\" y1=\"67.4791\" x2=\"113.672\" y2=\"-37.4275\" gradientUnits=\"userSpaceOnUse\">\n <stop stop-color=\"${BRAND_BLUE}\"/>\n <stop offset=\"1\" stop-color=\"${BRAND_MINT}\"/>\n </linearGradient>\n <pattern id=\"grid\" width=\"48\" height=\"48\" patternUnits=\"userSpaceOnUse\">\n <path d=\"M 48 0 L 0 0 0 48\" fill=\"none\" stroke=\"#ffffff\" stroke-opacity=\"0.07\" stroke-width=\"1\"/>\n </pattern>\n </defs>\n <rect width=\"${WIDTH}\" height=\"${HEIGHT}\" fill=\"${BG}\"/>\n <rect width=\"${WIDTH}\" height=\"${HEIGHT}\" fill=\"url(#grid)\"/>\n <g transform=\"translate(80 116) scale(0.94)\">\n ${LOGO_MARK}\n </g>\n <g>\n ${textBlock({\n lines: titleLayout.lines,\n x: textX,\n y: titleY,\n fontSize: titleLayout.fontSize,\n lineHeight: titleLayout.lineHeight,\n // resvg's fontdb maps font-weight 850 to the Regular face (only 400/700\n // exist for Liberation Sans); 800 resolves to Bold, the heaviest face we\n // bundle, which is the intended look for the display title.\n weight: 800,\n fill: FG,\n anchor: textAnchor,\n direction: titleIsRtl ? \"rtl\" : undefined,\n fontFamily: fontFamilyForText(title),\n })}\n ${textBlock({\n lines: [accentText],\n x: accentX,\n y: accentY,\n fontSize: 34,\n lineHeight: 40,\n weight: 800,\n fill: BRAND_BLUE,\n anchor: textAnchor,\n fontFamily: fontFamilyForText(accentText),\n })}\n </g>\n</svg>`;\n}\n\nexport async function renderAgentNativeOgImagePng(\n input: AgentNativeOgImageInput = {},\n): Promise<Uint8Array> {\n const overridePackage =\n typeof process !== \"undefined\"\n ? process.env.AGENT_NATIVE_RESVG_PACKAGE\n : undefined;\n const resvgPackage = overridePackage || \"@resvg/resvg-js\";\n const { Resvg } = await import(/* @vite-ignore */ resvgPackage);\n // Feed resvg the embedded Liberation Sans font explicitly. System fonts can't\n // be relied on: Linux serverless runtimes (Netlify/Lambda) ship neither Arial\n // nor Inter, so without a bundled font every `<text>` rendered blank.\n const fontFiles = resolveOgFontFiles();\n const hasBundledFonts = Boolean(fontFiles?.length);\n const image = new Resvg(renderAgentNativeOgImageSvg(input), {\n fitTo: { mode: \"width\", value: WIDTH },\n font: {\n loadSystemFonts: !hasBundledFonts,\n ...(hasBundledFonts ? { fontFiles } : {}),\n defaultFontFamily: OG_FONT_FAMILY,\n serifFamily: OG_FONT_FAMILY,\n sansSerifFamily: OG_FONT_FAMILY,\n },\n }).render();\n return image.asPng();\n}\n\nexport function agentNativeOgImageResponseHeaders(\n byteLength?: number,\n contentType = \"image/png\",\n): Record<string, string> {\n const headers: Record<string, string> = {\n \"Content-Type\": contentType,\n \"Cache-Control\": AGENT_NATIVE_OG_IMAGE_CACHE_CONTROL,\n \"CDN-Cache-Control\": AGENT_NATIVE_OG_IMAGE_CACHE_CONTROL,\n \"Netlify-CDN-Cache-Control\": AGENT_NATIVE_OG_IMAGE_NETLIFY_CACHE_CONTROL,\n \"Cross-Origin-Resource-Policy\": \"cross-origin\",\n };\n if (typeof byteLength === \"number\") {\n headers[\"Content-Length\"] = String(byteLength);\n }\n return headers;\n}\n\nexport function createAgentNativeOgImageHandler(\n options: AgentNativeOgImageInput = {},\n) {\n return defineEventHandler(async (event) => {\n if (getMethod(event) === \"HEAD\") {\n return new Response(null, {\n headers: agentNativeOgImageResponseHeaders(),\n });\n }\n\n const query = getQuery(event);\n const appName =\n cleanText(options.appName) || resolveAgentNativeOgImageAppName(event);\n const input = {\n ...options,\n appName,\n title: cleanText(options.title) || queryStringValue(query.title, 140),\n accentText:\n cleanText(options.accentText) || queryStringValue(query.accentText, 80),\n };\n\n let png: Uint8Array;\n try {\n png = await renderAgentNativeOgImagePng(input);\n } catch (error) {\n if (!isResvgRuntimeUnavailableError(error)) throw error;\n const svg = renderAgentNativeOgImageSvg(input);\n return new Response(svg, {\n headers: agentNativeOgImageResponseHeaders(\n textByteLength(svg),\n \"image/svg+xml; charset=utf-8\",\n ),\n });\n }\n\n return new Response(pngBody(png), {\n headers: agentNativeOgImageResponseHeaders(png.byteLength),\n });\n });\n}\n"]}
@@ -162,19 +162,19 @@ export default defineAction({
162
162
 
163
163
  بالنسبة لإجراء `GET`، يتم تمرير `leadId` كمعلمة استعلام: `/_agent-native/actions/get-lead?leadId=abc`.
164
164
 
165
- ```an-api title="The auto-mounted action endpoint" method="GET" path="/_agent-native/actions/get-lead"
165
+ ```an-api title="نقطة نهاية action المثبتة تلقائيا" method="GET" path="/_agent-native/actions/get-lead"
166
166
  {
167
167
  "method": "GET",
168
168
  "path": "/_agent-native/actions/get-lead",
169
- "summary": "Every action is mounted here automatically the filename is the action name.",
170
- "description": "POST by default; `http: { method: \"GET\" }` makes it a GET. The React hooks and `callAction` always call this path by name, regardless of any `http.path` override.",
171
- "auth": "Session cookie; frontend calls carry `X-Agent-Native-Frontend: 1`",
169
+ "summary": "كل action يثبت هنا تلقائيا - اسم الملف هو اسم action.",
170
+ "description": "الافتراضي هو POST؛ `http: { method: \"GET\" }` يجعلها GET. تستدعي React hooks و `callAction` هذا المسار دائما بالاسم، بغض النظر عن أي تجاوز `http.path`.",
171
+ "auth": "ملف تعريف ارتباط للجلسة؛ تحمل استدعاءات الواجهة `X-Agent-Native-Frontend: 1`",
172
172
  "params": [
173
- { "name": "leadId", "in": "query", "type": "string", "required": true, "description": "GET args arrive as query params; POST args arrive in the JSON body." }
173
+ { "name": "leadId", "in": "query", "type": "string", "required": true, "description": "تصل وسائط GET كمعاملات query؛ وتصل وسائط POST في جسم JSON." }
174
174
  ],
175
175
  "responses": [
176
- { "status": "200", "description": "The action's return value as JSON." },
177
- { "status": "400", "description": "Input failed schema validation before run() fired." }
176
+ { "status": "200", "description": "قيمة إرجاع action بصيغة JSON." },
177
+ { "status": "400", "description": "فشل الإدخال في التحقق من schema قبل تشغيل run()." }
178
178
  ]
179
179
  }
180
180
  ```
@@ -25,7 +25,7 @@ Codex، أو رمز Claude، أو Markdown، أو خطة التنفيذ المل
25
25
 
26
26
  ```an-diagram title="أمرين، وسطح مراجعة واحد" summary="يتم نشر كلا الأمرين من خلال موصل Plan MCP المستضاف في نفس سطح التعليق التوضيحي والتعليق."
27
27
  {
28
- "html": "<div class=\"diagram-plan\"><div class=\"diagram-col\"><div class=\"diagram-node\"><span class=\"diagram-pill accent\">/visual-plan</span><small class=\"diagram-muted\">before code architecture, UI, refactor</small></div><div class=\"diagram-node\"><span class=\"diagram-pill\">/visual-recap</span><small class=\"diagram-muted\">after code PR, commit, branch, diff</small></div></div><div class=\"diagram-arrow diagram-muted\" aria-hidden=\"true\">&rarr;</div><div class=\"diagram-panel center\">Plan MCP connector<br><small class=\"diagram-muted\">plan.agent-native.com</small></div><div class=\"diagram-arrow diagram-muted\" aria-hidden=\"true\">&rarr;</div><div class=\"diagram-box\">Review surface<br><small class=\"diagram-muted\">diagrams · wireframes · annotated code · comments</small></div><div class=\"diagram-arrow diagram-muted\" aria-hidden=\"true\">&harr;</div><div class=\"diagram-node\">Coding agent<br><small class=\"diagram-muted\">feedback handed back</small></div></div>",
28
+ "html": "<div class=\"diagram-plan\"><div class=\"diagram-col\"><div class=\"diagram-node\"><span class=\"diagram-pill accent\">/visual-plan</span><small class=\"diagram-muted\">قبل الكود - البنية وواجهة UI وإعادة التنظيم</small></div><div class=\"diagram-node\"><span class=\"diagram-pill\">/visual-recap</span><small class=\"diagram-muted\">بعد الكود - PR و commit و branch و diff</small></div></div><div class=\"diagram-arrow diagram-muted\" aria-hidden=\"true\">&rarr;</div><div class=\"diagram-panel center\">موصل Plan MCP<br><small class=\"diagram-muted\">plan.agent-native.com</small></div><div class=\"diagram-arrow diagram-muted\" aria-hidden=\"true\">&rarr;</div><div class=\"diagram-box\">سطح المراجعة<br><small class=\"diagram-muted\">مخططات · إطارات سلكية · كود مشروح · تعليقات</small></div><div class=\"diagram-arrow diagram-muted\" aria-hidden=\"true\">&harr;</div><div class=\"diagram-node\">وكيل البرمجة<br><small class=\"diagram-muted\">تمت إعادة الملاحظات</small></div></div>",
29
29
  "css": ".diagram-plan{display:flex;align-items:center;gap:12px;flex-wrap:wrap}.diagram-plan .diagram-col{display:flex;flex-direction:column;gap:10px}.diagram-plan .diagram-arrow{font-size:22px;line-height:1}.diagram-plan .center{display:flex;flex-direction:column;align-items:center;gap:4px;text-align:center}"
30
30
  }
31
31
  ```
@@ -33,7 +33,7 @@ Codex، أو رمز Claude، أو Markdown، أو خطة التنفيذ المل
33
33
  ```an-wireframe
34
34
  {
35
35
  "surface": "desktop",
36
- "html": "<div style='display:grid;grid-template-columns:1fr 250px;gap:14px;padding:16px;min-height:520px;box-sizing:border-box'><main style='display:flex;flex-direction:column;gap:12px;min-width:0'><div style='display:flex;align-items:center;gap:10px'><h1 style='margin:0'>Checkout redesign plan</h1><div style='flex:1'></div><button>مشاركة</button><button class='primary'>Approve</button></div><div class='wf-card' style='display:grid;grid-template-columns:1fr 1fr;gap:10px;min-height:150px'><div class='wf-box'>Current wireframe</div><div class='wf-box'>Proposed wireframe</div></div><div class='wf-card' style='flex:1;display:flex;flex-direction:column;gap:10px'><strong>Implementation plan</strong><div class='wf-box'>Decision: keep existing checkout shell</div><div class='wf-box'>Annotated code walkthrough</div><div class='wf-box'>Open questions</div></div></main><aside class='wf-card' style='display:flex;flex-direction:column;gap:10px'><strong>Comments</strong><div class='wf-box'>Pin on primary CTA</div><div class='wf-box'>Question for agent</div><div class='wf-box'>Resolved copy note</div><button class='primary'>Hand back feedback</button></aside></div>"
36
+ "html": "<div style='display:grid;grid-template-columns:1fr 250px;gap:14px;padding:16px;min-height:520px;box-sizing:border-box'><main style='display:flex;flex-direction:column;gap:12px;min-width:0'><div style='display:flex;align-items:center;gap:10px'><h1 style='margin:0'>خطة إعادة تصميم الدفع</h1><div style='flex:1'></div><button>مشاركة</button><button class='primary'>موافقة</button></div><div class='wf-card' style='display:grid;grid-template-columns:1fr 1fr;gap:10px;min-height:150px'><div class='wf-box'>الإطار السلكي الحالي</div><div class='wf-box'>الإطار السلكي المقترح</div></div><div class='wf-card' style='flex:1;display:flex;flex-direction:column;gap:10px'><strong>خطة التنفيذ</strong><div class='wf-box'>قرار: الإبقاء على غلاف الدفع الحالي</div><div class='wf-box'>شرح الكود مع التعليقات</div><div class='wf-box'>أسئلة مفتوحة</div></div></main><aside class='wf-card' style='display:flex;flex-direction:column;gap:10px'><strong>التعليقات</strong><div class='wf-box'>تثبيت على CTA الأساسي</div><div class='wf-box'>سؤال للوكيل</div><div class='wf-box'>ملاحظة نصية محلولة</div><button class='primary'>إرجاع الملاحظات</button></aside></div>"
37
37
  }
38
38
  ```
39
39
 
@@ -570,7 +570,8 @@ const riskCardServerBlock = defineBlock<RiskCardData>({
570
570
  Read: ServerReadStub,
571
571
  placement: ["block"],
572
572
  label: "Risk card",
573
- description: "A markdown risk note with a low, medium, or high severity.",
573
+ description:
574
+ "ملاحظة مخاطر بصيغة markdown بدرجة خطورة منخفضة أو متوسطة أو عالية.",
574
575
  });
575
576
 
576
577
  export function registerPlanBlocks(registry: BlockRegistry): void {
@@ -622,7 +623,8 @@ const riskCardBlock = defineBlock<RiskCardData>({
622
623
  placement: ["block"],
623
624
  editSurface: "panel",
624
625
  label: "Risk card",
625
- description: "A markdown risk note with a low, medium, or high severity.",
626
+ description:
627
+ "ملاحظة مخاطر بصيغة markdown بدرجة خطورة منخفضة أو متوسطة أو عالية.",
626
628
  empty: () => ({ severity: "medium", body: "Describe the risk." }),
627
629
  });
628
630
 
@@ -85,9 +85,9 @@ um actions, keine erforderliche Voraussetzung für die Aktion selbst.
85
85
  "language": "ts",
86
86
  "code": "import { defineAction } from \"@agent-native/core/action\";\nimport { z } from \"zod\";\n\nexport default defineAction({\n description: \"Reply to an email thread in the user's voice.\",\n schema: z.object({\n emailId: z.string().describe(\"The id of the email to reply to.\"),\n body: z.string().describe(\"The reply body, in markdown.\"),\n }),\n run: async ({ emailId, body }) => {\n await db.insert(replies).values({ emailId, body });\n return { ok: true, emailId };\n },\n});",
87
87
  "annotations": [
88
- { "lines": "5", "label": "Tool surface", "note": "`description` is what the agent reads to decide when to call this. The per-field `.describe()` calls flow into the JSON Schema too." },
88
+ { "lines": "5", "label": "Tool-Oberflaeche", "note": "`description` liest der Agent, um zu entscheiden, wann er diese Action aufruft. Die `.describe()`-Aufrufe pro Feld fliessen ebenfalls ins JSON Schema." },
89
89
  { "lines": "6-9", "label": "Typisierter Vertrag", "note": "Ein schema validiert Eingaben von **jeder** Oberfläche und wird für das Modell in JSON Schema umgewandelt. Ungültige Eingaben erreichen `run` nie." },
90
- { "lines": "10-13", "label": "One implementation", "note": "The `run` body is the single source of truth — the UI button and the agent tool both execute exactly this." }
90
+ { "lines": "10-13", "label": "Eine Implementierung", "note": "Der `run`-Body ist die einzige Quelle der Wahrheit: UI-Button und Agent-Tool fuehren genau das aus." }
91
91
  ]
92
92
  }
93
93
  ```
@@ -150,19 +150,19 @@ export default defineAction({
150
150
 
151
151
  Für eine `GET`-Aktion wird `leadId` als Abfrageparameter übergeben: `/_agent-native/actions/get-lead?leadId=abc`.
152
152
 
153
- ```an-api title="The auto-mounted action endpoint" method="GET" path="/_agent-native/actions/get-lead"
153
+ ```an-api title="Automatisch gemounteter Action-Endpunkt" method="GET" path="/_agent-native/actions/get-lead"
154
154
  {
155
155
  "method": "GET",
156
156
  "path": "/_agent-native/actions/get-lead",
157
- "summary": "Every action is mounted here automatically the filename is the action name.",
158
- "description": "POST by default; `http: { method: \"GET\" }` makes it a GET. The React hooks and `callAction` always call this path by name, regardless of any `http.path` override.",
159
- "auth": "Session cookie; frontend calls carry `X-Agent-Native-Frontend: 1`",
157
+ "summary": "Jede Action wird hier automatisch gemountet - der Dateiname ist der Action-Name.",
158
+ "description": "Standard ist POST; `http: { method: \"GET\" }` macht daraus GET. React-Hooks und `callAction` rufen diesen Pfad immer per Namen auf, unabhaengig von jedem `http.path` Override.",
159
+ "auth": "Session-Cookie; Frontend-Aufrufe tragen `X-Agent-Native-Frontend: 1`",
160
160
  "params": [
161
- { "name": "leadId", "in": "query", "type": "string", "required": true, "description": "GET args arrive as query params; POST args arrive in the JSON body." }
161
+ { "name": "leadId", "in": "query", "type": "string", "required": true, "description": "GET-Argumente kommen als Query-Parameter an; POST-Argumente im JSON-Body." }
162
162
  ],
163
163
  "responses": [
164
- { "status": "200", "description": "The action's return value as JSON." },
165
- { "status": "400", "description": "Input failed schema validation before run() fired." }
164
+ { "status": "200", "description": "Der Rueckgabewert der Action als JSON." },
165
+ { "status": "400", "description": "Die Eingabe ist vor dem Start von run() an der Schema-Validierung gescheitert." }
166
166
  ]
167
167
  }
168
168
  ```
@@ -25,7 +25,7 @@ Agent auf die gleiche Weise.
25
25
 
26
26
  ```an-diagram title="Zwei Befehle, eine Überprüfungsoberfläche" summary="Beide Befehle veröffentlichen über den gehosteten Plan MCP-Connector in derselben Anmerkungs- und Kommentaroberfläche."
27
27
  {
28
- "html": "<div class=\"diagram-plan\"><div class=\"diagram-col\"><div class=\"diagram-node\"><span class=\"diagram-pill accent\">/visual-plan</span><small class=\"diagram-muted\">before code architecture, UI, refactor</small></div><div class=\"diagram-node\"><span class=\"diagram-pill\">/visual-recap</span><small class=\"diagram-muted\">after code PR, commit, branch, diff</small></div></div><div class=\"diagram-arrow diagram-muted\" aria-hidden=\"true\">&rarr;</div><div class=\"diagram-panel center\">Plan MCP connector<br><small class=\"diagram-muted\">plan.agent-native.com</small></div><div class=\"diagram-arrow diagram-muted\" aria-hidden=\"true\">&rarr;</div><div class=\"diagram-box\">Review surface<br><small class=\"diagram-muted\">diagrams · wireframes · annotated code · comments</small></div><div class=\"diagram-arrow diagram-muted\" aria-hidden=\"true\">&harr;</div><div class=\"diagram-node\">Coding agent<br><small class=\"diagram-muted\">feedback handed back</small></div></div>",
28
+ "html": "<div class=\"diagram-plan\"><div class=\"diagram-col\"><div class=\"diagram-node\"><span class=\"diagram-pill accent\">/visual-plan</span><small class=\"diagram-muted\">vor dem Code: Architektur, UI, Refactor</small></div><div class=\"diagram-node\"><span class=\"diagram-pill\">/visual-recap</span><small class=\"diagram-muted\">nach dem Code: PR, Commit, Branch, Diff</small></div></div><div class=\"diagram-arrow diagram-muted\" aria-hidden=\"true\">&rarr;</div><div class=\"diagram-panel center\">Plan MCP-Connector<br><small class=\"diagram-muted\">plan.agent-native.com</small></div><div class=\"diagram-arrow diagram-muted\" aria-hidden=\"true\">&rarr;</div><div class=\"diagram-box\">Review-Oberflaeche<br><small class=\"diagram-muted\">Diagramme · Wireframes · annotierter Code · Kommentare</small></div><div class=\"diagram-arrow diagram-muted\" aria-hidden=\"true\">&harr;</div><div class=\"diagram-node\">Coding Agent<br><small class=\"diagram-muted\">Feedback zurueckgegeben</small></div></div>",
29
29
  "css": ".diagram-plan{display:flex;align-items:center;gap:12px;flex-wrap:wrap}.diagram-plan .diagram-col{display:flex;flex-direction:column;gap:10px}.diagram-plan .diagram-arrow{font-size:22px;line-height:1}.diagram-plan .center{display:flex;flex-direction:column;align-items:center;gap:4px;text-align:center}"
30
30
  }
31
31
  ```
@@ -33,7 +33,7 @@ Agent auf die gleiche Weise.
33
33
  ```an-wireframe
34
34
  {
35
35
  "surface": "desktop",
36
- "html": "<div style='display:grid;grid-template-columns:1fr 250px;gap:14px;padding:16px;min-height:520px;box-sizing:border-box'><main style='display:flex;flex-direction:column;gap:12px;min-width:0'><div style='display:flex;align-items:center;gap:10px'><h1 style='margin:0'>Checkout redesign plan</h1><div style='flex:1'></div><button>Teilen</button><button class='primary'>Approve</button></div><div class='wf-card' style='display:grid;grid-template-columns:1fr 1fr;gap:10px;min-height:150px'><div class='wf-box'>Current wireframe</div><div class='wf-box'>Proposed wireframe</div></div><div class='wf-card' style='flex:1;display:flex;flex-direction:column;gap:10px'><strong>Implementation plan</strong><div class='wf-box'>Decision: keep existing checkout shell</div><div class='wf-box'>Annotated code walkthrough</div><div class='wf-box'>Open questions</div></div></main><aside class='wf-card' style='display:flex;flex-direction:column;gap:10px'><strong>Comments</strong><div class='wf-box'>Pin on primary CTA</div><div class='wf-box'>Question for agent</div><div class='wf-box'>Resolved copy note</div><button class='primary'>Hand back feedback</button></aside></div>"
36
+ "html": "<div style='display:grid;grid-template-columns:1fr 250px;gap:14px;padding:16px;min-height:520px;box-sizing:border-box'><main style='display:flex;flex-direction:column;gap:12px;min-width:0'><div style='display:flex;align-items:center;gap:10px'><h1 style='margin:0'>Plan fuer Checkout-Redesign</h1><div style='flex:1'></div><button>Teilen</button><button class='primary'>Genehmigen</button></div><div class='wf-card' style='display:grid;grid-template-columns:1fr 1fr;gap:10px;min-height:150px'><div class='wf-box'>Aktuelles Wireframe</div><div class='wf-box'>Vorgeschlagenes Wireframe</div></div><div class='wf-card' style='flex:1;display:flex;flex-direction:column;gap:10px'><strong>Implementierungsplan</strong><div class='wf-box'>Entscheidung: bestehende Checkout-Shell beibehalten</div><div class='wf-box'>Annotierte Code-Einfuehrung</div><div class='wf-box'>Offene Fragen</div></div></main><aside class='wf-card' style='display:flex;flex-direction:column;gap:10px'><strong>Kommentare</strong><div class='wf-box'>Pin am primaeren CTA</div><div class='wf-box'>Frage an den Agenten</div><div class='wf-box'>Geklaerte Copy-Notiz</div><button class='primary'>Feedback zurueckgeben</button></aside></div>"
37
37
  }
38
38
  ```
39
39
 
@@ -569,7 +569,8 @@ const riskCardServerBlock = defineBlock<RiskCardData>({
569
569
  Read: ServerReadStub,
570
570
  placement: ["block"],
571
571
  label: "Risk card",
572
- description: "A markdown risk note with a low, medium, or high severity.",
572
+ description:
573
+ "Eine Markdown-Risiknotiz mit niedriger, mittlerer oder hoher Schwere.",
573
574
  });
574
575
 
575
576
  export function registerPlanBlocks(registry: BlockRegistry): void {
@@ -621,7 +622,8 @@ const riskCardBlock = defineBlock<RiskCardData>({
621
622
  placement: ["block"],
622
623
  editSurface: "panel",
623
624
  label: "Risk card",
624
- description: "A markdown risk note with a low, medium, or high severity.",
625
+ description:
626
+ "Eine Markdown-Risiknotiz mit niedriger, mittlerer oder hoher Schwere.",
625
627
  empty: () => ({ severity: "medium", body: "Describe the risk." }),
626
628
  });
627
629
 
@@ -85,9 +85,9 @@ alrededor de actions, no es un requisito previo requerido para la acción en sí
85
85
  "language": "ts",
86
86
  "code": "import { defineAction } from \"@agent-native/core/action\";\nimport { z } from \"zod\";\n\nexport default defineAction({\n description: \"Reply to an email thread in the user's voice.\",\n schema: z.object({\n emailId: z.string().describe(\"The id of the email to reply to.\"),\n body: z.string().describe(\"The reply body, in markdown.\"),\n }),\n run: async ({ emailId, body }) => {\n await db.insert(replies).values({ emailId, body });\n return { ok: true, emailId };\n },\n});",
87
87
  "annotations": [
88
- { "lines": "5", "label": "Tool surface", "note": "`description` is what the agent reads to decide when to call this. The per-field `.describe()` calls flow into the JSON Schema too." },
88
+ { "lines": "5", "label": "Superficie de herramienta", "note": "`description` es lo que lee el agente para decidir cuándo llamar a esto. Las llamadas `.describe()` de cada campo también pasan al JSON Schema." },
89
89
  { "lines": "6-9", "label": "Contrato tipado", "note": "Un schema valida la entrada de **todas** las superficies y se convierte a JSON Schema para el modelo. Las entradas no válidas nunca llegan a `run`." },
90
- { "lines": "10-13", "label": "One implementation", "note": "The `run` body is the single source of truth the UI button and the agent tool both execute exactly this." }
90
+ { "lines": "10-13", "label": "Una implementación", "note": "El cuerpo de `run` es la única fuente de verdad: el botón de la UI y la herramienta del agente ejecutan exactamente esto." }
91
91
  ]
92
92
  }
93
93
  ```
@@ -150,19 +150,19 @@ export default defineAction({
150
150
 
151
151
  Para una acción `GET`, se pasa `leadId` como parámetro de consulta: `/_agent-native/actions/get-lead?leadId=abc`.
152
152
 
153
- ```an-api title="The auto-mounted action endpoint" method="GET" path="/_agent-native/actions/get-lead"
153
+ ```an-api title="Endpoint de accion montado automaticamente" method="GET" path="/_agent-native/actions/get-lead"
154
154
  {
155
155
  "method": "GET",
156
156
  "path": "/_agent-native/actions/get-lead",
157
- "summary": "Every action is mounted here automatically the filename is the action name.",
158
- "description": "POST by default; `http: { method: \"GET\" }` makes it a GET. The React hooks and `callAction` always call this path by name, regardless of any `http.path` override.",
159
- "auth": "Session cookie; frontend calls carry `X-Agent-Native-Frontend: 1`",
157
+ "summary": "Cada accion se monta aqui automaticamente: el nombre del archivo es el nombre de la accion.",
158
+ "description": "POST por defecto; `http: { method: \"GET\" }` lo convierte en GET. Los hooks de React y `callAction` siempre llaman a esta ruta por nombre, sin importar cualquier override de `http.path`.",
159
+ "auth": "Cookie de sesion; las llamadas del frontend llevan `X-Agent-Native-Frontend: 1`",
160
160
  "params": [
161
- { "name": "leadId", "in": "query", "type": "string", "required": true, "description": "GET args arrive as query params; POST args arrive in the JSON body." }
161
+ { "name": "leadId", "in": "query", "type": "string", "required": true, "description": "Los argumentos GET llegan como query params; los argumentos POST llegan en el cuerpo JSON." }
162
162
  ],
163
163
  "responses": [
164
- { "status": "200", "description": "The action's return value as JSON." },
165
- { "status": "400", "description": "Input failed schema validation before run() fired." }
164
+ { "status": "200", "description": "El valor de retorno de la accion como JSON." },
165
+ { "status": "400", "description": "La entrada fallo la validacion del schema antes de que run() se ejecutara." }
166
166
  ]
167
167
  }
168
168
  ```
@@ -25,7 +25,7 @@ agente de la misma manera.
25
25
 
26
26
  ```an-diagram title="Dos comandos, una superficie de revisión" summary="Ambos comandos publican a través del conector alojado Plan MCP en la misma superficie de anotaciones y comentarios."
27
27
  {
28
- "html": "<div class=\"diagram-plan\"><div class=\"diagram-col\"><div class=\"diagram-node\"><span class=\"diagram-pill accent\">/visual-plan</span><small class=\"diagram-muted\">before code architecture, UI, refactor</small></div><div class=\"diagram-node\"><span class=\"diagram-pill\">/visual-recap</span><small class=\"diagram-muted\">after code PR, commit, branch, diff</small></div></div><div class=\"diagram-arrow diagram-muted\" aria-hidden=\"true\">&rarr;</div><div class=\"diagram-panel center\">Plan MCP connector<br><small class=\"diagram-muted\">plan.agent-native.com</small></div><div class=\"diagram-arrow diagram-muted\" aria-hidden=\"true\">&rarr;</div><div class=\"diagram-box\">Review surface<br><small class=\"diagram-muted\">diagrams · wireframes · annotated code · comments</small></div><div class=\"diagram-arrow diagram-muted\" aria-hidden=\"true\">&harr;</div><div class=\"diagram-node\">Coding agent<br><small class=\"diagram-muted\">feedback handed back</small></div></div>",
28
+ "html": "<div class=\"diagram-plan\"><div class=\"diagram-col\"><div class=\"diagram-node\"><span class=\"diagram-pill accent\">/visual-plan</span><small class=\"diagram-muted\">antes del codigo: arquitectura, UI, refactor</small></div><div class=\"diagram-node\"><span class=\"diagram-pill\">/visual-recap</span><small class=\"diagram-muted\">despues del codigo: PR, commit, rama, diff</small></div></div><div class=\"diagram-arrow diagram-muted\" aria-hidden=\"true\">&rarr;</div><div class=\"diagram-panel center\">Conector MCP de Plan<br><small class=\"diagram-muted\">plan.agent-native.com</small></div><div class=\"diagram-arrow diagram-muted\" aria-hidden=\"true\">&rarr;</div><div class=\"diagram-box\">Superficie de revision<br><small class=\"diagram-muted\">diagramas · wireframes · codigo anotado · comentarios</small></div><div class=\"diagram-arrow diagram-muted\" aria-hidden=\"true\">&harr;</div><div class=\"diagram-node\">Agente de codigo<br><small class=\"diagram-muted\">feedback devuelto</small></div></div>",
29
29
  "css": ".diagram-plan{display:flex;align-items:center;gap:12px;flex-wrap:wrap}.diagram-plan .diagram-col{display:flex;flex-direction:column;gap:10px}.diagram-plan .diagram-arrow{font-size:22px;line-height:1}.diagram-plan .center{display:flex;flex-direction:column;align-items:center;gap:4px;text-align:center}"
30
30
  }
31
31
  ```
@@ -33,7 +33,7 @@ agente de la misma manera.
33
33
  ```an-wireframe
34
34
  {
35
35
  "surface": "desktop",
36
- "html": "<div style='display:grid;grid-template-columns:1fr 250px;gap:14px;padding:16px;min-height:520px;box-sizing:border-box'><main style='display:flex;flex-direction:column;gap:12px;min-width:0'><div style='display:flex;align-items:center;gap:10px'><h1 style='margin:0'>Checkout redesign plan</h1><div style='flex:1'></div><button>Compartir</button><button class='primary'>Approve</button></div><div class='wf-card' style='display:grid;grid-template-columns:1fr 1fr;gap:10px;min-height:150px'><div class='wf-box'>Current wireframe</div><div class='wf-box'>Proposed wireframe</div></div><div class='wf-card' style='flex:1;display:flex;flex-direction:column;gap:10px'><strong>Implementation plan</strong><div class='wf-box'>Decision: keep existing checkout shell</div><div class='wf-box'>Annotated code walkthrough</div><div class='wf-box'>Open questions</div></div></main><aside class='wf-card' style='display:flex;flex-direction:column;gap:10px'><strong>Comments</strong><div class='wf-box'>Pin on primary CTA</div><div class='wf-box'>Question for agent</div><div class='wf-box'>Resolved copy note</div><button class='primary'>Hand back feedback</button></aside></div>"
36
+ "html": "<div style='display:grid;grid-template-columns:1fr 250px;gap:14px;padding:16px;min-height:520px;box-sizing:border-box'><main style='display:flex;flex-direction:column;gap:12px;min-width:0'><div style='display:flex;align-items:center;gap:10px'><h1 style='margin:0'>Plan de rediseño del checkout</h1><div style='flex:1'></div><button>Compartir</button><button class='primary'>Aprobar</button></div><div class='wf-card' style='display:grid;grid-template-columns:1fr 1fr;gap:10px;min-height:150px'><div class='wf-box'>Wireframe actual</div><div class='wf-box'>Wireframe propuesto</div></div><div class='wf-card' style='flex:1;display:flex;flex-direction:column;gap:10px'><strong>Plan de implementación</strong><div class='wf-box'>Decisión: conservar el shell actual del checkout</div><div class='wf-box'>Recorrido de código anotado</div><div class='wf-box'>Preguntas abiertas</div></div></main><aside class='wf-card' style='display:flex;flex-direction:column;gap:10px'><strong>Comentarios</strong><div class='wf-box'>Pin en el CTA principal</div><div class='wf-box'>Pregunta para el agente</div><div class='wf-box'>Nota de copy resuelta</div><button class='primary'>Devolver feedback</button></aside></div>"
37
37
  }
38
38
  ```
39
39
 
@@ -570,7 +570,8 @@ const riskCardServerBlock = defineBlock<RiskCardData>({
570
570
  Read: ServerReadStub,
571
571
  placement: ["block"],
572
572
  label: "Risk card",
573
- description: "A markdown risk note with a low, medium, or high severity.",
573
+ description:
574
+ "Una nota de riesgo en markdown con severidad baja, media o alta.",
574
575
  });
575
576
 
576
577
  export function registerPlanBlocks(registry: BlockRegistry): void {
@@ -622,7 +623,8 @@ const riskCardBlock = defineBlock<RiskCardData>({
622
623
  placement: ["block"],
623
624
  editSurface: "panel",
624
625
  label: "Risk card",
625
- description: "A markdown risk note with a low, medium, or high severity.",
626
+ description:
627
+ "Una nota de riesgo en markdown con severidad baja, media o alta.",
626
628
  empty: () => ({ severity: "medium", body: "Describe the risk." }),
627
629
  });
628
630