@agent-native/core 0.154.5 → 0.157.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (263) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/templates/analytics/.agents/skills/account-health/SKILL.md +76 -0
  3. package/corpus/templates/analytics/.agents/skills/bigquery/SKILL.md +3 -0
  4. package/corpus/templates/analytics/.agents/skills/cross-source-analysis/SKILL.md +2 -1
  5. package/corpus/templates/analytics/.agents/skills/data-querying/SKILL.md +6 -5
  6. package/corpus/templates/analytics/AGENTS.md +3 -0
  7. package/corpus/templates/analytics/actions/account-deep-dive.ts +3 -2
  8. package/corpus/templates/analytics/actions/bigquery.ts +1 -1
  9. package/corpus/templates/analytics/app/components/dashboard/SqlChart.tsx +4 -1
  10. package/corpus/templates/analytics/app/components/layout/NewDashboardDialog.tsx +1 -0
  11. package/corpus/templates/analytics/app/pages/adhoc/explorer/components/PropertyCombobox.tsx +12 -4
  12. package/corpus/templates/analytics/app/pages/adhoc/explorer/components/PropertyValueCombobox.tsx +5 -2
  13. package/corpus/templates/analytics/app/pages/adhoc/explorer/property-label.ts +32 -0
  14. package/corpus/templates/analytics/changelog/2026-08-13-account-health-readouts-verify-customer-scope-and-contract-metrics.md +7 -0
  15. package/corpus/templates/analytics/server/jobs/analytics-bigquery-backfill.ts +43 -17
  16. package/corpus/templates/analytics/server/lib/analytics-query-catalog.ts +17 -0
  17. package/corpus/templates/analytics/server/plugins/agent-chat.ts +4 -0
  18. package/corpus/templates/assets/app/components/asset/AssetPreviewDialog.tsx +3 -1
  19. package/corpus/templates/assets/app/i18n-data.ts +21 -1
  20. package/corpus/templates/assets/app/routes/brand-kits.$id.tsx +17 -13
  21. package/corpus/templates/calendar/AGENTS.md +2 -0
  22. package/corpus/templates/calendar/DEVELOPING.md +4 -4
  23. package/corpus/templates/calendar/actions/get-settings.ts +4 -11
  24. package/corpus/templates/calendar/actions/update-availability.ts +15 -4
  25. package/corpus/templates/calendar/actions/update-settings.ts +15 -4
  26. package/corpus/templates/calendar/app/components/booking/DatePicker.tsx +9 -3
  27. package/corpus/templates/calendar/app/components/calendar/CreateEventDialog.tsx +21 -19
  28. package/corpus/templates/calendar/app/components/calendar/EventDetailPopover.tsx +9 -6
  29. package/corpus/templates/calendar/app/components/calendar/FindTimePanel.tsx +25 -3
  30. package/corpus/templates/calendar/app/components/calendar/InlineEventPickers.tsx +10 -3
  31. package/corpus/templates/calendar/app/components/calendar/MonthView.tsx +9 -9
  32. package/corpus/templates/calendar/app/components/calendar/WeekView.tsx +11 -3
  33. package/corpus/templates/calendar/app/components/layout/Sidebar.tsx +10 -4
  34. package/corpus/templates/calendar/app/i18n-data.ts +30 -0
  35. package/corpus/templates/calendar/app/lib/availability-schedule.ts +53 -0
  36. package/corpus/templates/calendar/app/lib/event-form-utils.ts +5 -0
  37. package/corpus/templates/calendar/app/pages/AvailabilitySettings.tsx +90 -28
  38. package/corpus/templates/calendar/app/pages/BookingLinksPage.tsx +135 -61
  39. package/corpus/templates/calendar/app/pages/BookingPage.tsx +2 -0
  40. package/corpus/templates/calendar/app/pages/CalendarView.tsx +30 -15
  41. package/corpus/templates/calendar/app/pages/Settings.tsx +60 -1
  42. package/corpus/templates/calendar/changelog/2026-08-13-find-a-time-now-closes-from-a-clear-header-action.md +6 -0
  43. package/corpus/templates/calendar/server/handlers/bookings.ts +2 -1
  44. package/corpus/templates/calendar/server/handlers/settings.ts +12 -16
  45. package/corpus/templates/calendar/shared/api.ts +1 -0
  46. package/corpus/templates/calendar/shared/availability-schedule.ts +64 -0
  47. package/corpus/templates/calendar/shared/calendar-week.ts +21 -0
  48. package/corpus/templates/calendar/shared/settings.ts +44 -0
  49. package/corpus/templates/clips/AGENTS.md +2 -0
  50. package/corpus/templates/clips/actions/add-comment.ts +5 -2
  51. package/corpus/templates/clips/actions/finalize-recording.ts +31 -1
  52. package/corpus/templates/clips/actions/get-recording-player-data.ts +9 -0
  53. package/corpus/templates/clips/actions/lib/ensure-seekable-video.ts +21 -1
  54. package/corpus/templates/clips/actions/reply-to-comment.ts +5 -2
  55. package/corpus/templates/clips/actions/update-comment.ts +9 -2
  56. package/corpus/templates/clips/app/components/library/search-bar.tsx +26 -13
  57. package/corpus/templates/clips/app/components/meetings/share-meeting-dialog.tsx +135 -3
  58. package/corpus/templates/clips/app/components/player/comments-panel.tsx +5 -99
  59. package/corpus/templates/clips/app/components/player/playback-comment-overlay.tsx +7 -3
  60. package/corpus/templates/clips/app/components/player/share-dialog.tsx +102 -59
  61. package/corpus/templates/clips/app/components/player/video-player.tsx +84 -24
  62. package/corpus/templates/clips/app/components/workspace/notifications-list.tsx +11 -3
  63. package/corpus/templates/clips/app/i18n/en-US.ts +12 -0
  64. package/corpus/templates/clips/app/lib/public-meeting.ts +11 -3
  65. package/corpus/templates/clips/app/lib/search-focus.ts +15 -0
  66. package/corpus/templates/clips/app/root.tsx +2 -1
  67. package/corpus/templates/clips/app/routes/_app.meetings._index.tsx +94 -26
  68. package/corpus/templates/clips/app/routes/_app.settings._index.tsx +24 -7
  69. package/corpus/templates/clips/app/routes/embed.$shareId.tsx +8 -4
  70. package/corpus/templates/clips/app/routes/r.$recordingId.tsx +29 -8
  71. package/corpus/templates/clips/app/routes/share.$shareId.tsx +24 -6
  72. package/corpus/templates/clips/app/routes/share.meeting.$meetingId.tsx +72 -26
  73. package/corpus/templates/clips/changelog/2026-08-13-inline-markdown-comments.md +6 -0
  74. package/corpus/templates/clips/changelog/2026-08-13-private-meeting-agent-links.md +6 -0
  75. package/corpus/templates/clips/desktop/src/app.tsx +116 -19
  76. package/corpus/templates/clips/desktop/src/lib/recorder.ts +13 -29
  77. package/corpus/templates/clips/desktop/src/lib/recording-request.ts +39 -0
  78. package/corpus/templates/clips/desktop/src/lib/settings-navigation.ts +11 -0
  79. package/corpus/templates/clips/desktop/src/lib/whisper-model-picker.ts +7 -0
  80. package/corpus/templates/clips/desktop/src/styles.css +117 -0
  81. package/corpus/templates/clips/server/db/index.ts +10 -0
  82. package/corpus/templates/clips/server/db/schema.ts +1 -0
  83. package/corpus/templates/clips/server/lib/recordings.ts +3 -1
  84. package/corpus/templates/clips/server/lib/seekable-media-state.ts +90 -0
  85. package/corpus/templates/clips/server/plugins/db.ts +5 -0
  86. package/corpus/templates/clips/server/routes/api/public-meeting.get.ts +37 -4
  87. package/corpus/templates/clips/server/routes/api/public-recording.get.ts +16 -5
  88. package/corpus/templates/clips/shared/clips-ai-prefs.ts +3 -0
  89. package/corpus/templates/clips/shared/meeting-agent-access.ts +5 -0
  90. package/corpus/templates/content/actions/add-comment.ts +2 -1
  91. package/corpus/templates/content/actions/update-comment.ts +1 -1
  92. package/corpus/templates/content/app/components/editor/CommentsSidebar.tsx +24 -15
  93. package/corpus/templates/design/actions/generate-design.ts +10 -7
  94. package/corpus/templates/design/actions/present-design-variants.ts +35 -6
  95. package/corpus/templates/design/actions/run-design-audit.ts +33 -0
  96. package/corpus/templates/design/changelog/2026-08-13-the-design-audit-now-catches-a-screen-that-renders-behind-an.md +6 -0
  97. package/corpus/templates/design/shared/design-review.ts +1 -0
  98. package/corpus/templates/design/shared/html-integrity.ts +66 -0
  99. package/corpus/templates/dispatch/.env.example +5 -0
  100. package/corpus/templates/dispatch/AGENTS.md +24 -19
  101. package/corpus/templates/dispatch/app/i18n-data.ts +20 -0
  102. package/corpus/templates/dispatch/server/lib/identity-sso.ts +21 -90
  103. package/corpus/templates/dispatch/server/plugins/identity-sso.ts +3 -12
  104. package/corpus/templates/dispatch/shared/feature-flags.ts +4 -0
  105. package/corpus/templates/factory/AGENTS.md +5 -1
  106. package/corpus/templates/factory/README.md +23 -0
  107. package/corpus/templates/factory/actions/view-screen.ts +17 -0
  108. package/corpus/templates/factory/app/components/factory/FactoryAgentsView.tsx +239 -0
  109. package/corpus/templates/factory/app/components/factory/FactoryCanvas.tsx +2 -0
  110. package/corpus/templates/factory/app/components/factory/FactoryInspector.tsx +130 -7
  111. package/corpus/templates/factory/app/i18n/en-US.ts +20 -0
  112. package/corpus/templates/factory/app/routes/factory.tsx +41 -0
  113. package/corpus/templates/factory/package.json +1 -0
  114. package/corpus/templates/factory/server/factory-graph/contracts.ts +2 -0
  115. package/corpus/templates/factory/server/plugins/agent-chat.ts +9 -0
  116. package/corpus/templates/factory/server/plugins/dispatch-db.ts +1 -0
  117. package/corpus/templates/factory/server/plugins/setup-dispatch.ts +3 -0
  118. package/corpus/templates/mail/app/lib/native-mcp-exclusions.ts +3 -0
  119. package/corpus/templates/mail/vite.config.ts +7 -0
  120. package/corpus/templates/plan/actions/reply-to-plan-comment.ts +7 -2
  121. package/corpus/templates/plan/actions/resolve-plan-comment.ts +2 -2
  122. package/corpus/templates/plan/actions/update-visual-plan.ts +1 -1
  123. package/corpus/templates/plan/app/pages/PlansPage.tsx +29 -20
  124. package/corpus/templates/slides/actions/add-slide-comment.ts +1 -1
  125. package/corpus/templates/slides/actions/update-slide-comment.ts +1 -1
  126. package/corpus/templates/slides/app/components/comments/SlideCommentsPanel.tsx +9 -6
  127. package/corpus/templates/slides/app/components/editor/AddSlidePopover.tsx +98 -29
  128. package/corpus/templates/slides/app/components/editor/EditorActionCluster.tsx +45 -91
  129. package/corpus/templates/slides/app/components/editor/EditorSidebar.tsx +94 -2
  130. package/corpus/templates/slides/app/components/editor/EditorToolbar.tsx +17 -29
  131. package/corpus/templates/slides/app/components/editor/SlideContextToolbar.tsx +1 -1
  132. package/corpus/templates/slides/app/context/DeckContext.tsx +42 -2
  133. package/corpus/templates/slides/app/global.css +7 -40
  134. package/corpus/templates/slides/app/i18n/en-US.ts +4 -0
  135. package/corpus/templates/slides/app/pages/DeckEditor.tsx +130 -45
  136. package/dist/agent/index.d.ts +1 -1
  137. package/dist/agent/production-agent.d.ts +30 -0
  138. package/dist/agent/production-agent.js +105 -9
  139. package/dist/agent/thread-data-builder.js +17 -1
  140. package/dist/agent/types.d.ts +9 -0
  141. package/dist/cli/skills-content/help.js +6 -4
  142. package/dist/cli/skills.js +17 -2
  143. package/dist/cli/workspacify.d.ts +5 -0
  144. package/dist/cli/workspacify.js +65 -8
  145. package/dist/client/AgentPanel.d.ts +5 -0
  146. package/dist/client/AgentPanel.js +32 -13
  147. package/dist/client/AssistantChat.d.ts +36 -1
  148. package/dist/client/AssistantChat.js +175 -110
  149. package/dist/client/MultiTabAssistantChat.js +44 -27
  150. package/dist/client/active-run-state.d.ts +14 -1
  151. package/dist/client/active-run-state.js +0 -0
  152. package/dist/client/agent-chat-adapter.js +107 -5
  153. package/dist/client/analytics.js +9 -2
  154. package/dist/client/chat/markdown-renderer.js +4 -2
  155. package/dist/client/chat/message-components.d.ts +2 -2
  156. package/dist/client/chat/message-components.js +69 -35
  157. package/dist/client/chat/run-recovery.d.ts +4 -1
  158. package/dist/client/chat/run-recovery.js +62 -30
  159. package/dist/client/chat/tool-call-display.d.ts +12 -3
  160. package/dist/client/chat/tool-call-display.js +79 -40
  161. package/dist/client/chat/widgets/DataChartRenderer.js +3 -1
  162. package/dist/client/chat/widgets/DataChartWidget.js +8 -2
  163. package/dist/client/chat/widgets/DataInsightsWidget.js +3 -1
  164. package/dist/client/chat/widgets/DataTableWidget.js +23 -9
  165. package/dist/client/chat/widgets/builtin-tool-renderers.js +3 -1
  166. package/dist/client/chat-first/apps-rail.js +9 -4
  167. package/dist/client/chat-first/primary-nav.d.ts +2 -1
  168. package/dist/client/chat-first/primary-nav.js +3 -3
  169. package/dist/client/chat-first/types.d.ts +5 -1
  170. package/dist/client/components/ui/message-scroller.d.ts +1 -1
  171. package/dist/client/components/ui/message-scroller.js +7 -3
  172. package/dist/client/composer/runtime-adapters.js +3 -2
  173. package/dist/client/context-xray/ContextMeter.js +5 -1
  174. package/dist/client/context-xray/ContextXRayPanel.js +5 -5
  175. package/dist/client/error-format.d.ts +4 -0
  176. package/dist/client/error-format.js +93 -5
  177. package/dist/client/i18n.js +83 -17
  178. package/dist/client/markdown/InlineMarkdown.d.ts +23 -0
  179. package/dist/client/markdown/InlineMarkdown.js +65 -0
  180. package/dist/client/markdown/index.d.ts +1 -0
  181. package/dist/client/markdown/index.js +1 -0
  182. package/dist/client/observability/ThumbsFeedback.js +10 -8
  183. package/dist/client/review/ReviewThreadPanel.js +2 -1
  184. package/dist/client/run-stream-ownership.d.ts +39 -0
  185. package/dist/client/run-stream-ownership.js +65 -0
  186. package/dist/client/sharing/ShareButton.js +160 -54
  187. package/dist/coding-tools/run-code.js +2 -1
  188. package/dist/coding-tools/sandbox/background.d.ts +1 -0
  189. package/dist/coding-tools/sandbox/background.js +8 -1
  190. package/dist/coding-tools/sandbox/executions-store.d.ts +2 -0
  191. package/dist/coding-tools/sandbox/executions-store.js +41 -3
  192. package/dist/collab/awareness.d.ts +2 -2
  193. package/dist/collab/routes.d.ts +1 -1
  194. package/dist/collab/struct-routes.d.ts +1 -1
  195. package/dist/feature-flags/index.d.ts +1 -1
  196. package/dist/feature-flags/index.js +1 -1
  197. package/dist/feature-flags/store.d.ts +10 -0
  198. package/dist/feature-flags/store.js +112 -1
  199. package/dist/guards/no-env-credentials.js +4 -0
  200. package/dist/index.d.ts +2 -2
  201. package/dist/index.js +1 -1
  202. package/dist/integrations/adapters/slack.js +5 -1
  203. package/dist/localization/core-messages/ar-SA.d.ts +3 -0
  204. package/dist/localization/core-messages/ar-SA.js +541 -0
  205. package/dist/localization/core-messages/de-DE.d.ts +3 -0
  206. package/dist/localization/core-messages/de-DE.js +513 -0
  207. package/dist/localization/core-messages/en-US.d.ts +513 -0
  208. package/dist/localization/core-messages/en-US.js +513 -0
  209. package/dist/localization/core-messages/es-ES.d.ts +3 -0
  210. package/dist/localization/core-messages/es-ES.js +520 -0
  211. package/dist/localization/core-messages/fr-FR.d.ts +3 -0
  212. package/dist/localization/core-messages/fr-FR.js +520 -0
  213. package/dist/localization/core-messages/hi-IN.d.ts +3 -0
  214. package/dist/localization/core-messages/hi-IN.js +513 -0
  215. package/dist/localization/core-messages/ja-JP.d.ts +3 -0
  216. package/dist/localization/core-messages/ja-JP.js +506 -0
  217. package/dist/localization/core-messages/ko-KR.d.ts +3 -0
  218. package/dist/localization/core-messages/ko-KR.js +506 -0
  219. package/dist/localization/core-messages/pt-BR.d.ts +3 -0
  220. package/dist/localization/core-messages/pt-BR.js +520 -0
  221. package/dist/localization/core-messages/zh-CN.d.ts +3 -0
  222. package/dist/localization/core-messages/zh-CN.js +506 -0
  223. package/dist/localization/core-messages/zh-TW.d.ts +3 -0
  224. package/dist/localization/core-messages/zh-TW.js +506 -0
  225. package/dist/localization/core-messages.d.ts +524 -0
  226. package/dist/localization/core-messages.js +124 -0
  227. package/dist/localization/default-messages.d.ts +3 -0
  228. package/dist/localization/default-messages.js +3 -0
  229. package/dist/notifications/routes.d.ts +1 -1
  230. package/dist/observability/routes.d.ts +2 -2
  231. package/dist/progress/routes.d.ts +1 -1
  232. package/dist/provider-api/actions/custom-provider-registration.d.ts +11 -11
  233. package/dist/provider-api/actions/provider-api.d.ts +15 -15
  234. package/dist/resources/agents.js +40 -5
  235. package/dist/resources/metadata.d.ts +10 -0
  236. package/dist/resources/metadata.js +7 -3
  237. package/dist/review/actions/create-review-comment.js +1 -1
  238. package/dist/review/actions/reply-review-comment.js +1 -1
  239. package/dist/review/actions/resolve-review-thread.js +1 -1
  240. package/dist/secrets/routes.d.ts +6 -6
  241. package/dist/server/action-routes.js +32 -6
  242. package/dist/server/agent-chat/plugin-options.d.ts +7 -2
  243. package/dist/server/agent-chat-plugin.d.ts +10 -0
  244. package/dist/server/agent-chat-plugin.js +144 -41
  245. package/dist/server/agent-discovery.js +14 -3
  246. package/dist/server/agent-run-context.d.ts +7 -1
  247. package/dist/server/agent-run-context.js +9 -2
  248. package/dist/server/agent-teams-run-queue.d.ts +3 -0
  249. package/dist/server/agent-teams.js +38 -9
  250. package/dist/server/core-routes-plugin.js +1 -1
  251. package/dist/server/identity-sso-migrations.d.ts +7 -0
  252. package/dist/server/identity-sso-migrations.js +36 -0
  253. package/dist/server/identity-sso-store.d.ts +3 -2
  254. package/dist/server/identity-sso-store.js +8 -3
  255. package/dist/server/index.d.ts +1 -1
  256. package/dist/server/realtime-token.d.ts +1 -1
  257. package/dist/server/release-migrations.js +4 -0
  258. package/dist/server/request-context.d.ts +7 -0
  259. package/dist/server/request-context.js +15 -0
  260. package/dist/tracking/providers.js +13 -3
  261. package/dist/workspace-files/tool.js +1 -1
  262. package/docs/content/agent-surfaces.mdx +30 -0
  263. package/package.json +3 -2
@@ -844,10 +844,11 @@ const enUS = {
844
844
  checking: "Checking",
845
845
  checkingUpload: "Checking upload",
846
846
  clearAll: "Clear all",
847
+ clearAllWithCount: "Clear all candidates ({{count}})",
847
848
  clearCandidates: "Clear candidates",
848
849
  clearCandidatesTitle: "Clear {{count}} candidates?",
849
850
  clearCandidatesDescription:
850
- "Clears the live stage and deletes unsaved draft candidates.",
851
+ "Clears the live stage and deletes every unsaved draft candidate. Saved library assets stay untouched.",
851
852
  clearGeneratedCandidatesTitle: "Clear generated candidates?",
852
853
  clearGeneratedCandidatesDescription:
853
854
  "This removes every unsaved candidate from the thread and deletes the generated asset rows behind them. Saved library assets are not touched.",
@@ -986,6 +987,7 @@ const enUS = {
986
987
  open: "Open",
987
988
  openAssets: "Open Assets",
988
989
  openChat: "Open chat",
990
+ hideDetails: "Hide details",
989
991
  openDetails: "Open details",
990
992
  preparing: "Preparing...",
991
993
  previousImage: "Previous image",
@@ -3728,7 +3730,9 @@ export const messagesByLocale = {
3728
3730
  checkingUpload: "正在检查上传",
3729
3731
  clear: "清除",
3730
3732
  clearAll: "全部清除",
3733
+ clearAllWithCount: "清除所有候选项({{count}})",
3731
3734
  clearCandidates: "清除候选项",
3735
+ hideDetails: "隐藏详情",
3732
3736
  clearCandidatesDescription: "清除实时阶段并删除未保存的草稿候选项。",
3733
3737
  clearCandidatesTitle: "清除 {{count}} 个候选项?",
3734
3738
  clearGeneratedCandidatesDescription:
@@ -4552,7 +4556,9 @@ export const messagesByLocale = {
4552
4556
  checkingImageLibraries: "Comprobando bibliotecas de imágenes...",
4553
4557
  checkingUpload: "Comprobando carga",
4554
4558
  clearAll: "Borrar todo",
4559
+ clearAllWithCount: "Borrar todos los candidatos ({{count}})",
4555
4560
  clearCandidates: "Limpiar candidatos",
4561
+ hideDetails: "Ocultar detalles",
4556
4562
  clearCandidatesDescription:
4557
4563
  "Limpia la etapa en vivo y elimina los candidatos de borrador no guardados.",
4558
4564
  clearGeneratedCandidatesDescription:
@@ -5026,7 +5032,9 @@ export const messagesByLocale = {
5026
5032
  checkingImageLibraries: "Vérification des bibliothèques d'images...",
5027
5033
  checkingUpload: "Vérification du téléchargement",
5028
5034
  clearAll: "Tout effacer",
5035
+ clearAllWithCount: "Effacer tous les candidats ({{count}})",
5029
5036
  clearCandidates: "Effacer les candidats",
5037
+ hideDetails: "Masquer les détails",
5030
5038
  clearCandidatesDescription:
5031
5039
  "Efface l'étape en direct et supprime les candidats de brouillon non enregistrés.",
5032
5040
  clearGeneratedCandidatesDescription:
@@ -5479,7 +5487,9 @@ export const messagesByLocale = {
5479
5487
  checkingImageLibraries: "Bildbibliotheken werden geprüft...",
5480
5488
  checkingUpload: "Upload wird überprüft",
5481
5489
  clearAll: "Alles löschen",
5490
+ clearAllWithCount: "Alle Kandidaten löschen ({{count}})",
5482
5491
  clearCandidates: "Kandidaten löschen",
5492
+ hideDetails: "Details ausblenden",
5483
5493
  clearCandidatesDescription:
5484
5494
  "Löscht die Live-Phase und entfernt nicht gespeicherte Entwurfskandidaten.",
5485
5495
  clearGeneratedCandidatesDescription:
@@ -5837,7 +5847,9 @@ export const messagesByLocale = {
5837
5847
  checkingImageLibraries: "画像ライブラリを確認中...",
5838
5848
  checkingUpload: "アップロードを確認しています",
5839
5849
  clearAll: "すべてクリア",
5850
+ clearAllWithCount: "候補をすべてクリア({{count}})",
5840
5851
  clearCandidates: "候補をクリアする",
5852
+ hideDetails: "詳細を非表示",
5841
5853
  clearCandidatesDescription:
5842
5854
  "ライブ ステージをクリアし、保存されていないドラフト候補を削除します。",
5843
5855
  clearGeneratedCandidatesDescription:
@@ -6186,7 +6198,9 @@ export const messagesByLocale = {
6186
6198
  checkingImageLibraries: "이미지 라이브러리 확인 중...",
6187
6199
  checkingUpload: "업로드 확인 중",
6188
6200
  clearAll: "모두 지우기",
6201
+ clearAllWithCount: "후보 모두 지우기({{count}})",
6189
6202
  clearCandidates: "후보 제거",
6203
+ hideDetails: "세부정보 숨기기",
6190
6204
  clearCandidatesDescription:
6191
6205
  "라이브 단계를 지우고 저장되지 않은 초안 후보를 삭제합니다.",
6192
6206
  clearGeneratedCandidatesDescription:
@@ -6534,7 +6548,9 @@ export const messagesByLocale = {
6534
6548
  checkingImageLibraries: "Verificando bibliotecas de imagens...",
6535
6549
  checkingUpload: "Verificando upload",
6536
6550
  clearAll: "Limpar tudo",
6551
+ clearAllWithCount: "Limpar todos os candidatos ({{count}})",
6537
6552
  clearCandidates: "Limpar candidatos",
6553
+ hideDetails: "Ocultar detalhes",
6538
6554
  clearCandidatesDescription:
6539
6555
  "Limpa o estágio ao vivo e exclui candidatos de rascunho não salvos.",
6540
6556
  clearGeneratedCandidatesDescription:
@@ -6985,7 +7001,9 @@ export const messagesByLocale = {
6985
7001
  checkingImageLibraries: "Image libraries जांची जा रही हैं...",
6986
7002
  checkingUpload: "अपलोड की जाँच की जा रही है",
6987
7003
  clearAll: "सभी साफ करें",
7004
+ clearAllWithCount: "सभी उम्मीदवार साफ करें ({{count}})",
6988
7005
  clearCandidates: "उम्मीदवारों को साफ करें",
7006
+ hideDetails: "विवरण छिपाएं",
6989
7007
  clearCandidatesDescription:
6990
7008
  "लाइव चरण को साफ करता है और अ सहेजे गए ड्राफ्ट उम्मीदवारों को हटाता है।",
6991
7009
  clearGeneratedCandidatesDescription:
@@ -7331,7 +7349,9 @@ export const messagesByLocale = {
7331
7349
  checkingImageLibraries: "جارٍ فحص مكتبات الصور...",
7332
7350
  checkingUpload: "التحقق من التحميل",
7333
7351
  clearAll: "مسح الكل",
7352
+ clearAllWithCount: "مسح كل المرشحين ({{count}})",
7334
7353
  clearCandidates: "امسح المرشحين",
7354
+ hideDetails: "إخفاء التفاصيل",
7335
7355
  clearCandidatesDescription:
7336
7356
  "مسح مرحلة المرشح وحذف المرشحين الذين تم إنشاؤهم غير المحفوظة. أصول المكتبة المحفوظة تبقى دون تغيير.",
7337
7357
  clearGeneratedCandidatesDescription:
@@ -4533,7 +4533,9 @@ function LiveCandidatesActions({
4533
4533
  const hasFailed = failedCount > 0;
4534
4534
  const isClearing = dismissSlots.isPending || deleteAssets.isPending;
4535
4535
  const actionLabel =
4536
- pending === "failed" ? t("library.dismissFailed") : t("library.clearAll");
4536
+ pending === "failed"
4537
+ ? t("library.dismissFailed")
4538
+ : t("library.clearAllWithCount", { count: totalCount });
4537
4539
  const busyLabel =
4538
4540
  pending === "failed" ? t("library.dismissing") : t("library.clearing");
4539
4541
 
@@ -4645,26 +4647,28 @@ function LiveCandidatesActions({
4645
4647
  </Button>
4646
4648
  </DropdownMenuTrigger>
4647
4649
  <DropdownMenuContent align="end">
4648
- <DropdownMenuItem
4649
- disabled={!hasFailed || isClearing}
4650
- onSelect={(event) => {
4651
- event.preventDefault();
4652
- setPending("failed");
4653
- }}
4654
- >
4655
- <IconTrash className="mr-2 h-4 w-4 shrink-0" />
4656
- {t("library.dismissFailedWithCount", { count: failedCount })}
4657
- </DropdownMenuItem>
4650
+ {hasFailed ? (
4651
+ <DropdownMenuItem
4652
+ disabled={isClearing}
4653
+ onSelect={(event) => {
4654
+ event.preventDefault();
4655
+ setPending("failed");
4656
+ }}
4657
+ >
4658
+ <IconTrash className="mr-2 h-4 w-4 shrink-0" />
4659
+ {t("library.dismissFailedWithCount", { count: failedCount })}
4660
+ </DropdownMenuItem>
4661
+ ) : null}
4658
4662
  <DropdownMenuItem
4659
4663
  className="text-destructive focus:bg-destructive/10 focus:text-destructive"
4660
- disabled={isClearing}
4664
+ disabled={isClearing || totalCount === 0}
4661
4665
  onSelect={(event) => {
4662
4666
  event.preventDefault();
4663
4667
  setPending("all");
4664
4668
  }}
4665
4669
  >
4666
4670
  <IconTrash className="mr-2 h-4 w-4 shrink-0" />
4667
- {t("library.clearAll")}
4671
+ {t("library.clearAllWithCount", { count: totalCount })}
4668
4672
  </DropdownMenuItem>
4669
4673
  </DropdownMenuContent>
4670
4674
  </DropdownMenu>
@@ -24,6 +24,8 @@ Detailed event, availability, booking, storage, and UI rules live in
24
24
  - Never hardcode API keys, tokens, webhook URLs, signing secrets, private Builder/internal data, customer data, or credential-looking literals. Use secrets/OAuth/runtime configuration and obvious placeholders in examples.
25
25
  - Use actions for events, availability, booking links, settings, navigation,
26
26
  Google Calendar connection, and sharing. Do not bypass app access checks.
27
+ - The `get-settings` and `update-settings` actions expose the General week-start
28
+ setting as `weekStart`: `sunday` or `monday`.
27
29
  - Use `connect-google-calendar` when the user asks to connect or reconnect
28
30
  Google Calendar. Return its link to the user; do not `fetch`
29
31
  `/_agent-native/google/auth-url` from the agent backend because that route
@@ -99,10 +99,10 @@ Structured data lives in SQL. Use `@agent-native/core/db/schema` helpers for sch
99
99
 
100
100
  Configuration lives in the SQL `settings` table, accessed via the settings API:
101
101
 
102
- | Key | Contents |
103
- | ----------------------- | -------------------------------------------- |
104
- | `calendar-settings` | App settings (timezone, booking page config) |
105
- | `calendar-availability` | Availability schedule configuration |
102
+ | Key | Contents |
103
+ | ----------------------- | -------------------------------------------------------- |
104
+ | `calendar-settings` | App settings (timezone, week start, booking page config) |
105
+ | `calendar-availability` | Availability schedule configuration |
106
106
 
107
107
  ### OAuth Tokens (via `@agent-native/core/oauth-tokens`)
108
108
 
@@ -3,14 +3,7 @@ import { getRequestUserEmail } from "@agent-native/core/server";
3
3
  import { getUserSetting } from "@agent-native/core/settings";
4
4
  import { z } from "zod";
5
5
 
6
- import type { Settings } from "../shared/api.js";
7
-
8
- const DEFAULT_SETTINGS: Settings = {
9
- timezone: "America/New_York",
10
- bookingPageTitle: "Book a Meeting",
11
- bookingPageDescription: "Select a time that works for you.",
12
- defaultEventDuration: 30,
13
- };
6
+ import { normalizeCalendarSettings } from "../shared/settings.js";
14
7
 
15
8
  export default defineAction({
16
9
  description: "Get calendar settings",
@@ -19,8 +12,8 @@ export default defineAction({
19
12
  run: async () => {
20
13
  const email = getRequestUserEmail();
21
14
  if (!email) throw new Error("no authenticated user");
22
- const settings =
23
- (await getUserSetting(email, "calendar-settings")) || DEFAULT_SETTINGS;
24
- return settings;
15
+ return normalizeCalendarSettings(
16
+ await getUserSetting(email, "calendar-settings"),
17
+ );
25
18
  },
26
19
  });
@@ -8,16 +8,27 @@ import {
8
8
  updateBookingUsername,
9
9
  } from "../server/handlers/booking-usernames.js";
10
10
  import type { AvailabilityConfig } from "../shared/api.js";
11
+ import { availabilitySlotsOverlap } from "../shared/availability-schedule.js";
11
12
 
12
13
  const timeSlotSchema = z.object({
13
14
  start: z.string(),
14
15
  end: z.string(),
15
16
  });
16
17
 
17
- const dayScheduleSchema = z.object({
18
- enabled: z.boolean(),
19
- slots: z.array(timeSlotSchema),
20
- });
18
+ const dayScheduleSchema = z
19
+ .object({
20
+ enabled: z.boolean(),
21
+ slots: z.array(timeSlotSchema),
22
+ })
23
+ .superRefine((day, ctx) => {
24
+ if (availabilitySlotsOverlap(day.slots)) {
25
+ ctx.addIssue({
26
+ code: z.ZodIssueCode.custom,
27
+ path: ["slots"],
28
+ message: "Availability windows must not overlap",
29
+ });
30
+ }
31
+ });
21
32
 
22
33
  const availabilitySchema = z.object({
23
34
  timezone: z.string(),
@@ -1,9 +1,13 @@
1
1
  import { defineAction } from "@agent-native/core";
2
2
  import { getRequestUserEmail } from "@agent-native/core/server";
3
- import { putUserSetting, putSetting } from "@agent-native/core/settings";
3
+ import {
4
+ getUserSetting,
5
+ putUserSetting,
6
+ putSetting,
7
+ } from "@agent-native/core/settings";
4
8
  import { z } from "zod";
5
9
 
6
- import type { Settings } from "../shared/api.js";
10
+ import { normalizeCalendarSettings } from "../shared/settings.js";
7
11
 
8
12
  export default defineAction({
9
13
  description: "Update calendar settings",
@@ -20,12 +24,19 @@ export default defineAction({
20
24
  .positive()
21
25
  .optional()
22
26
  .describe("Default event duration in minutes"),
27
+ weekStart: z
28
+ .enum(["sunday", "monday"])
29
+ .optional()
30
+ .describe("First day shown in calendar weeks"),
23
31
  }),
24
32
  run: async (args) => {
25
33
  const email = getRequestUserEmail();
26
34
  if (!email) throw new Error("no authenticated user");
27
- // The frontend sends the full settings object as the body
28
- const settings = args as unknown as Settings;
35
+ const currentSettings = await getUserSetting(email, "calendar-settings");
36
+ const settings = normalizeCalendarSettings({
37
+ ...normalizeCalendarSettings(currentSettings),
38
+ ...args,
39
+ });
29
40
  const settingsRecord = settings as unknown as Record<string, unknown>;
30
41
  await putUserSetting(email, "calendar-settings", settingsRecord);
31
42
  await putSetting("calendar-settings", settingsRecord);
@@ -1,4 +1,5 @@
1
1
  import type { AvailabilityConfig } from "@shared/api";
2
+ import { getWeekdayOrder } from "@shared/calendar-week";
2
3
  import { IconChevronLeft, IconChevronRight } from "@tabler/icons-react";
3
4
  import {
4
5
  startOfMonth,
@@ -31,6 +32,7 @@ interface DatePickerProps {
31
32
  availabilityLoading?: boolean;
32
33
  viewMonth: Date;
33
34
  onViewMonthChange: (month: Date) => void;
35
+ weekStartsOn?: 0 | 1;
34
36
  }
35
37
 
36
38
  const WEEKDAY_HEADERS = ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"];
@@ -53,12 +55,16 @@ export function DatePicker({
53
55
  availabilityLoading = false,
54
56
  viewMonth,
55
57
  onViewMonthChange,
58
+ weekStartsOn = 0,
56
59
  }: DatePickerProps) {
57
60
  const monthStart = startOfMonth(viewMonth);
58
61
  const monthEnd = endOfMonth(viewMonth);
59
- const calendarStart = startOfWeek(monthStart);
60
- const calendarEnd = endOfWeek(monthEnd);
62
+ const calendarStart = startOfWeek(monthStart, { weekStartsOn });
63
+ const calendarEnd = endOfWeek(monthEnd, { weekStartsOn });
61
64
  const days = eachDayOfInterval({ start: calendarStart, end: calendarEnd });
65
+ const weekdayHeaders = getWeekdayOrder(weekStartsOn).map(
66
+ (day) => WEEKDAY_HEADERS[day],
67
+ );
62
68
  const availableDateSet = useMemo(
63
69
  () => new Set(availableDates),
64
70
  [availableDates],
@@ -103,7 +109,7 @@ export function DatePicker({
103
109
 
104
110
  {/* Weekday headers */}
105
111
  <div className="grid grid-cols-7 mb-1">
106
- {WEEKDAY_HEADERS.map((d) => (
112
+ {weekdayHeaders.map((d) => (
107
113
  <div
108
114
  key={d}
109
115
  className="py-1 text-center text-xs font-medium text-muted-foreground"
@@ -76,8 +76,8 @@ import {
76
76
  createReminderDraft,
77
77
  dateTimeInTimezoneToIso,
78
78
  getEventEndValidationMessage,
79
- getLocalTimezone,
80
79
  remindersToDraftState,
80
+ resolveEventTimezone,
81
81
  type AttachmentDraft,
82
82
  type ReminderDraft,
83
83
  type ReminderMode,
@@ -236,7 +236,7 @@ export function CreateEventPopover({
236
236
  const defaultDurationMinutes = Number.isFinite(rawDefaultDuration)
237
237
  ? Math.max(5, rawDefaultDuration)
238
238
  : 30;
239
- const defaultTimezone = settings?.timezone || getLocalTimezone();
239
+ const defaultTimezone = settings?.timezone || resolveEventTimezone();
240
240
  const fallbackStart = "09:00";
241
241
  const fallbackEnd = addMinutesToTimeString(
242
242
  fallbackStart,
@@ -279,6 +279,7 @@ export function CreateEventPopover({
279
279
  const [findTimeOpen, setFindTimeOpen] = useState(false);
280
280
  const isOutOfOffice = eventType === "outOfOffice";
281
281
  const timedOnlyStatus = eventType === "focusTime";
282
+ const eventTimezone = resolveEventTimezone(timezone);
282
283
 
283
284
  const createEvent = useCreateEvent();
284
285
  const delEvent = useDeleteEvent();
@@ -308,8 +309,9 @@ export function CreateEventPopover({
308
309
  }
309
310
 
310
311
  const nextDate = format(defaultDate || new Date(), "yyyy-MM-dd");
311
- const draftTimezone =
312
- draft?.startTimeZone || draft?.endTimeZone || defaultTimezone;
312
+ const draftTimezone = resolveEventTimezone(
313
+ draft?.startTimeZone || draft?.endTimeZone || defaultTimezone,
314
+ );
313
315
  const initKey = buildEventFormInitializationKey({
314
316
  draftId: draft?.id,
315
317
  date: nextDate,
@@ -455,12 +457,12 @@ export function CreateEventPopover({
455
457
  ? date
456
458
  : effectiveAllDay
457
459
  ? new Date(`${date}T00:00:00`).toISOString()
458
- : dateTimeInTimezoneToIso(date, startTime, timezone);
460
+ : dateTimeInTimezoneToIso(date, startTime, eventTimezone);
459
461
  const endValue = fullDayOutOfOffice
460
462
  ? endDate
461
463
  : effectiveAllDay
462
464
  ? allDayEnd.toISOString()
463
- : dateTimeInTimezoneToIso(endDate, endTime, timezone);
465
+ : dateTimeInTimezoneToIso(endDate, endTime, eventTimezone);
464
466
  const attachmentResult = validateAttachmentDrafts(attachments);
465
467
  const reminderPatch = buildReminderPayload(reminderMode, reminders);
466
468
  const nextDraft: CalendarEventDraft = {
@@ -470,8 +472,8 @@ export function CreateEventPopover({
470
472
  description: isOutOfOffice ? "" : description,
471
473
  start: startValue,
472
474
  end: endValue,
473
- startTimeZone: effectiveAllDay ? undefined : timezone,
474
- endTimeZone: effectiveAllDay ? undefined : timezone,
475
+ startTimeZone: effectiveAllDay ? undefined : eventTimezone,
476
+ endTimeZone: effectiveAllDay ? undefined : eventTimezone,
475
477
  location: isOutOfOffice ? "" : location,
476
478
  allDay: effectiveAllDay,
477
479
  fullDay: fullDayOutOfOffice,
@@ -547,7 +549,7 @@ export function CreateEventPopover({
547
549
  declineMessage,
548
550
  availability,
549
551
  visibility,
550
- timezone,
552
+ eventTimezone,
551
553
  colorId,
552
554
  reminderMode,
553
555
  reminders,
@@ -578,7 +580,7 @@ export function CreateEventPopover({
578
580
  time: allDay
579
581
  ? t("eventForm.allDay")
580
582
  : t("eventForm.ai.timeRange", { startTime, endTime }),
581
- timezone,
583
+ timezone: eventTimezone,
582
584
  location: location || t("eventForm.ai.none"),
583
585
  attendees:
584
586
  attendees.map((attendee) => attendee.email).join(", ") ||
@@ -636,11 +638,11 @@ export function CreateEventPopover({
636
638
  const effectiveAllDay = allDay && !isOutOfOffice && !timedOnlyStatus;
637
639
  const currentStartISO =
638
640
  !effectiveAllDay && date && startTime
639
- ? dateTimeInTimezoneToIso(date, startTime, timezone)
641
+ ? dateTimeInTimezoneToIso(date, startTime, eventTimezone)
640
642
  : undefined;
641
643
  const currentEndISO =
642
644
  !effectiveAllDay && endDate && endTime
643
- ? dateTimeInTimezoneToIso(endDate, endTime, timezone)
645
+ ? dateTimeInTimezoneToIso(endDate, endTime, eventTimezone)
644
646
  : undefined;
645
647
  const findTimeDurationMinutes =
646
648
  currentStartISO && currentEndISO
@@ -654,8 +656,8 @@ export function CreateEventPopover({
654
656
  : defaultDurationMinutes;
655
657
 
656
658
  function handleSelectFindTimeSlot(slot: { start: string; end: string }) {
657
- const startParts = dateTimePartsInTimezone(slot.start, timezone);
658
- const endParts = dateTimePartsInTimezone(slot.end, timezone);
659
+ const startParts = dateTimePartsInTimezone(slot.start, eventTimezone);
660
+ const endParts = dateTimePartsInTimezone(slot.end, eventTimezone);
659
661
  if (!startParts || !endParts) return;
660
662
  setAllDay(false);
661
663
  setDate(startParts.date);
@@ -708,12 +710,12 @@ export function CreateEventPopover({
708
710
  ? date
709
711
  : effectiveAllDay
710
712
  ? new Date(`${date}T00:00:00`).toISOString()
711
- : dateTimeInTimezoneToIso(date, startTime, timezone);
713
+ : dateTimeInTimezoneToIso(date, startTime, eventTimezone);
712
714
  const endValue = fullDayOutOfOffice
713
715
  ? endDate
714
716
  : effectiveAllDay
715
717
  ? allDayEnd.toISOString()
716
- : dateTimeInTimezoneToIso(endDate, endTime, timezone);
718
+ : dateTimeInTimezoneToIso(endDate, endTime, eventTimezone);
717
719
 
718
720
  if (
719
721
  fullDayOutOfOffice
@@ -761,8 +763,8 @@ export function CreateEventPopover({
761
763
  description: isOutOfOffice ? "" : description,
762
764
  start: startValue,
763
765
  end: endValue,
764
- startTimeZone: effectiveAllDay ? undefined : timezone,
765
- endTimeZone: effectiveAllDay ? undefined : timezone,
766
+ startTimeZone: effectiveAllDay ? undefined : eventTimezone,
767
+ endTimeZone: effectiveAllDay ? undefined : eventTimezone,
766
768
  location: isOutOfOffice ? "" : location,
767
769
  accountEmail,
768
770
  allDay: effectiveAllDay,
@@ -1459,7 +1461,7 @@ export function CreateEventPopover({
1459
1461
  (draft ? t("eventForm.invite") : t("eventForm.newEventLower"))
1460
1462
  }
1461
1463
  date={date}
1462
- timezone={timezone}
1464
+ timezone={eventTimezone}
1463
1465
  durationMinutes={findTimeDurationMinutes}
1464
1466
  attendees={attendees}
1465
1467
  accountEmail={accountEmail}
@@ -1441,7 +1441,7 @@ export function EventDetailPopover({
1441
1441
  side={isMobile ? "bottom" : "right"}
1442
1442
  sideOffset={isMobile ? 6 : 8}
1443
1443
  collisionPadding={12}
1444
- className="flex max-h-[90vh] w-[calc(100vw-2rem)] flex-col overflow-hidden p-0 sm:w-[420px]"
1444
+ className="flex max-h-[var(--radix-popover-content-available-height)] w-[min(420px,calc(100vw-2rem))] flex-col overflow-hidden p-0"
1445
1445
  onClick={(e) => e.stopPropagation()}
1446
1446
  onOpenAutoFocus={(e) => {
1447
1447
  e.preventDefault();
@@ -1671,12 +1671,15 @@ export function EventDetailPopover({
1671
1671
  />
1672
1672
  </>
1673
1673
  )}
1674
+ <span className="ml-auto">
1675
+ <TimezonePickerPopover
1676
+ compact
1677
+ value={editTimezone}
1678
+ label={t("eventForm.timezone")}
1679
+ onChange={handleInlineTimezoneChange}
1680
+ />
1681
+ </span>
1674
1682
  </div>
1675
- <TimezonePickerPopover
1676
- value={editTimezone}
1677
- label={t("eventForm.timezone")}
1678
- onChange={handleInlineTimezoneChange}
1679
- />
1680
1683
  </>
1681
1684
  )}
1682
1685
  </div>
@@ -5,12 +5,14 @@ import type {
5
5
  FindTimeResult,
6
6
  FindTimeSlot,
7
7
  } from "@shared/api";
8
+ import { getWeekStartsOn } from "@shared/calendar-week";
8
9
  import {
9
10
  IconAlertCircle,
10
11
  IconCalendarTime,
11
12
  IconChevronLeft,
12
13
  IconChevronRight,
13
14
  IconLoader2,
15
+ IconX,
14
16
  IconUsers,
15
17
  } from "@tabler/icons-react";
16
18
  import {
@@ -31,11 +33,13 @@ import { Badge } from "@/components/ui/badge";
31
33
  import { Button } from "@/components/ui/button";
32
34
  import {
33
35
  Dialog,
36
+ DialogClose,
34
37
  DialogContent,
35
38
  DialogDescription,
36
39
  DialogTitle,
37
40
  } from "@/components/ui/dialog";
38
41
  import { Skeleton } from "@/components/ui/skeleton";
42
+ import { useSettings } from "@/hooks/use-settings";
39
43
  import { dateTimeInTimezoneToIso } from "@/lib/event-form-utils";
40
44
  import { cn } from "@/lib/utils";
41
45
 
@@ -188,6 +192,8 @@ export function FindTimePanel({
188
192
  isTakeover = false,
189
193
  }: FindTimePanelProps) {
190
194
  const t = useT();
195
+ const { data: settings } = useSettings();
196
+ const weekStartsOn = getWeekStartsOn(settings?.weekStart);
191
197
  const [anchorDate, setAnchorDate] = useState(() => parseDateOnly(date));
192
198
 
193
199
  useEffect(() => {
@@ -220,7 +226,10 @@ export function FindTimePanel({
220
226
  return () => window.removeEventListener("keydown", handleKeyDown, true);
221
227
  }, [isTakeover]);
222
228
 
223
- const weekStart = useMemo(() => startOfWeek(anchorDate), [anchorDate]);
229
+ const weekStart = useMemo(
230
+ () => startOfWeek(anchorDate, { weekStartsOn }),
231
+ [anchorDate, weekStartsOn],
232
+ );
224
233
  const days = useMemo(
225
234
  () =>
226
235
  eachDayOfInterval({
@@ -663,9 +672,11 @@ export function FindTimeTakeover({
663
672
  <Dialog open={open} onOpenChange={onOpenChange}>
664
673
  <DialogContent
665
674
  data-find-time-takeover
666
- className="flex h-[100dvh] max-h-[100dvh] w-screen max-w-none translate-x-[-50%] translate-y-[-50%] flex-col gap-0 overflow-hidden border-0 p-0 shadow-xl sm:rounded-none"
675
+ hideClose
676
+ overlayClassName="z-[310]"
677
+ className="z-[320] flex h-[100dvh] max-h-[100dvh] w-screen max-w-none translate-x-[-50%] translate-y-[-50%] flex-col gap-0 overflow-hidden border-0 p-0 shadow-xl sm:rounded-none"
667
678
  >
668
- <header className="flex h-14 shrink-0 items-center border-b border-border px-4 pr-12 md:px-6">
679
+ <header className="flex h-14 shrink-0 items-center border-b border-border px-4 md:px-6">
669
680
  <div className="min-w-0">
670
681
  <DialogTitle className="truncate text-base">
671
682
  {dialogTitle}
@@ -679,6 +690,17 @@ export function FindTimeTakeover({
679
690
  {subtitle || t("findTime.takeoverDescription")}
680
691
  </DialogDescription>
681
692
  </div>
693
+ <DialogClose asChild>
694
+ <button
695
+ type="button"
696
+ aria-label={t("eventDialog.close")}
697
+ title={t("eventDialog.close")}
698
+ className="ml-auto flex h-10 shrink-0 items-center gap-2 rounded-md px-3 text-sm text-muted-foreground transition-colors hover:bg-accent hover:text-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring"
699
+ >
700
+ <IconX className="h-4 w-4" />
701
+ <span>{t("eventDialog.close")}</span>
702
+ </button>
703
+ </DialogClose>
682
704
  </header>
683
705
  <div className="min-h-0 flex-1 overflow-auto p-4 md:p-6">
684
706
  <FindTimePanel
@@ -193,10 +193,12 @@ export function TimezonePickerPopover({
193
193
  value,
194
194
  onChange,
195
195
  label,
196
+ compact = false,
196
197
  }: {
197
198
  value: string;
198
199
  onChange: (value: string) => void;
199
200
  label: string;
201
+ compact?: boolean;
200
202
  }) {
201
203
  const t = useT();
202
204
  const [open, setOpen] = useState(false);
@@ -208,11 +210,16 @@ export function TimezonePickerPopover({
208
210
  type="button"
209
211
  variant="ghost"
210
212
  size="sm"
211
- className="h-auto justify-start rounded px-1.5 py-0.5 text-sm font-normal text-muted-foreground hover:bg-muted hover:text-foreground"
213
+ className={cn(
214
+ compact
215
+ ? "size-7 rounded-full p-0 text-muted-foreground hover:bg-muted hover:text-foreground"
216
+ : "h-auto justify-start rounded px-1.5 py-0.5 text-sm font-normal text-muted-foreground hover:bg-muted hover:text-foreground",
217
+ )}
212
218
  aria-label={label}
219
+ title={compact ? label : undefined}
213
220
  >
214
- <IconWorld className="mr-2 size-4 shrink-0" />
215
- {formatTimezoneLabel(value)}
221
+ <IconWorld className={cn("size-4 shrink-0", !compact && "mr-2")} />
222
+ {!compact && formatTimezoneLabel(value)}
216
223
  </Button>
217
224
  </PopoverTrigger>
218
225
  <PopoverContent
@@ -1,4 +1,5 @@
1
1
  import type { CalendarEvent } from "@shared/api";
2
+ import { getWeekdayOrder } from "@shared/calendar-week";
2
3
  import {
3
4
  startOfMonth,
4
5
  endOfMonth,
@@ -49,6 +50,7 @@ interface MonthViewProps {
49
50
  ) => void;
50
51
  onDraftDiscard?: (eventId: string) => void;
51
52
  isLoading?: boolean;
53
+ weekStartsOn?: 0 | 1;
52
54
  }
53
55
 
54
56
  // Skeleton pill widths per day-of-week (Sun–Sat), empty = no skeletons
@@ -85,6 +87,7 @@ export const MonthView = memo(function MonthView({
85
87
  onDraftCreate,
86
88
  onDraftDiscard,
87
89
  isLoading = false,
90
+ weekStartsOn = 0,
88
91
  }: MonthViewProps) {
89
92
  const isMobile = useIsMobile();
90
93
  const { prefs } = useViewPreferences();
@@ -93,8 +96,8 @@ export const MonthView = memo(function MonthView({
93
96
 
94
97
  const monthStart = startOfMonth(selectedDate);
95
98
  const monthEnd = endOfMonth(selectedDate);
96
- const calendarStart = startOfWeek(monthStart);
97
- const calendarEnd = endOfWeek(monthEnd);
99
+ const calendarStart = startOfWeek(monthStart, { weekStartsOn });
100
+ const calendarEnd = endOfWeek(monthEnd, { weekStartsOn });
98
101
  const allDays = eachDayOfInterval({
99
102
  start: calendarStart,
100
103
  end: calendarEnd,
@@ -103,13 +106,10 @@ export const MonthView = memo(function MonthView({
103
106
  ? allDays.filter((d) => d.getDay() !== 0 && d.getDay() !== 6)
104
107
  : allDays;
105
108
  const colCount = prefs.hideWeekends ? 5 : 7;
106
- const headers = prefs.hideWeekends
107
- ? (isMobile ? WEEKDAY_HEADERS_SHORT : WEEKDAY_HEADERS).filter(
108
- (_, i) => i !== 0 && i !== 6,
109
- )
110
- : isMobile
111
- ? WEEKDAY_HEADERS_SHORT
112
- : WEEKDAY_HEADERS;
109
+ const headerLabels = isMobile ? WEEKDAY_HEADERS_SHORT : WEEKDAY_HEADERS;
110
+ const headers = getWeekdayOrder(weekStartsOn)
111
+ .filter((day) => !prefs.hideWeekends || (day !== 0 && day !== 6))
112
+ .map((day) => headerLabels[day]);
113
113
 
114
114
  // Pre-group events by every day they overlap (not just their start day) so
115
115
  // multi-day events keep appearing as the grid moves past their start date.