@agent-native/core 0.101.14 → 0.102.1

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 (316) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/core/CHANGELOG.md +12 -0
  3. package/corpus/core/package.json +1 -1
  4. package/corpus/core/src/cli/templates-meta.ts +13 -0
  5. package/corpus/core/src/client/agent-page/AgentTabsPage.tsx +1 -1
  6. package/corpus/core/src/client/org/OrgSwitcher.tsx +2 -0
  7. package/corpus/templates/analytics/changelog/2026-07-15-fixed-the-agent-discarding-a-successful-dashboard-edit-and-r.md +6 -0
  8. package/corpus/templates/analytics/server/lib/real-data-actions.ts +30 -1
  9. package/corpus/templates/analytics/server/plugins/agent-chat.ts +15 -0
  10. package/corpus/templates/content/actions/_database-source-utils.ts +59 -4
  11. package/corpus/templates/content/actions/prepare-builder-source-review.ts +9 -0
  12. package/corpus/templates/content/actions/preview-builder-source-review.ts +6 -4
  13. package/corpus/templates/content/app/components/editor/DescriptionField.tsx +10 -2
  14. package/corpus/templates/content/app/components/editor/database/DatabaseView.tsx +4 -0
  15. package/corpus/templates/content/changelog/2026-07-15-page-and-database-descriptions-now-use-a-clean-single-line-f.md +6 -0
  16. package/corpus/templates/content/changelog/2026-07-15-selected-builder-reviews-now-load-only-the-chosen-rows-avoid.md +6 -0
  17. package/corpus/templates/content/shared/api.ts +1 -0
  18. package/corpus/templates/tasks/.agents/commands/visual-plan.md +9 -0
  19. package/corpus/templates/tasks/.agents/commands/visual-recap.md +8 -0
  20. package/corpus/templates/tasks/.agents/skills/actions/SKILL.md +492 -0
  21. package/corpus/templates/tasks/.agents/skills/adding-a-feature/SKILL.md +189 -0
  22. package/corpus/templates/tasks/.agents/skills/agent-native-docs/SKILL.md +76 -0
  23. package/corpus/templates/tasks/.agents/skills/agent-watchdog/README.md +43 -0
  24. package/corpus/templates/tasks/.agents/skills/agent-watchdog/SKILL.md +110 -0
  25. package/corpus/templates/tasks/.agents/skills/agent-watchdog/agents/openai.yaml +4 -0
  26. package/corpus/templates/tasks/.agents/skills/capture-learnings/SKILL.md +89 -0
  27. package/corpus/templates/tasks/.agents/skills/change-summary/SKILL.md +83 -0
  28. package/corpus/templates/tasks/.agents/skills/create-skill/SKILL.md +221 -0
  29. package/corpus/templates/tasks/.agents/skills/delegate-to-agent/SKILL.md +263 -0
  30. package/corpus/templates/tasks/.agents/skills/efficient-fable/README.md +75 -0
  31. package/corpus/templates/tasks/.agents/skills/efficient-fable/SKILL.md +95 -0
  32. package/corpus/templates/tasks/.agents/skills/efficient-frontier/README.md +53 -0
  33. package/corpus/templates/tasks/.agents/skills/efficient-frontier/SKILL.md +73 -0
  34. package/corpus/templates/tasks/.agents/skills/frontend-design/SKILL.md +112 -0
  35. package/corpus/templates/tasks/.agents/skills/plan-arbiter/README.md +41 -0
  36. package/corpus/templates/tasks/.agents/skills/plan-arbiter/SKILL.md +114 -0
  37. package/corpus/templates/tasks/.agents/skills/plan-arbiter/agents/openai.yaml +4 -0
  38. package/corpus/templates/tasks/.agents/skills/plow-ahead/README.md +42 -0
  39. package/corpus/templates/tasks/.agents/skills/plow-ahead/SKILL.md +104 -0
  40. package/corpus/templates/tasks/.agents/skills/plow-ahead/agents/openai.yaml +4 -0
  41. package/corpus/templates/tasks/.agents/skills/quick-recap/README.md +55 -0
  42. package/corpus/templates/tasks/.agents/skills/quick-recap/SKILL.md +54 -0
  43. package/corpus/templates/tasks/.agents/skills/read-the-damn-docs/README.md +54 -0
  44. package/corpus/templates/tasks/.agents/skills/read-the-damn-docs/SKILL.md +126 -0
  45. package/corpus/templates/tasks/.agents/skills/read-the-damn-docs/agents/openai.yaml +4 -0
  46. package/corpus/templates/tasks/.agents/skills/real-time-sync/SKILL.md +232 -0
  47. package/corpus/templates/tasks/.agents/skills/security/SKILL.md +280 -0
  48. package/corpus/templates/tasks/.agents/skills/self-modifying-code/SKILL.md +106 -0
  49. package/corpus/templates/tasks/.agents/skills/shadcn-ui/SKILL.md +95 -0
  50. package/corpus/templates/tasks/.agents/skills/stay-within-limits/README.md +61 -0
  51. package/corpus/templates/tasks/.agents/skills/stay-within-limits/SKILL.md +83 -0
  52. package/corpus/templates/tasks/.agents/skills/upgrade-agent-native/SKILL.md +87 -0
  53. package/corpus/templates/tasks/.agents/skills/visual-plan/SKILL.md +547 -0
  54. package/corpus/templates/tasks/.agents/skills/visual-plan/agent-native-skill.json +12 -0
  55. package/corpus/templates/tasks/.agents/skills/visual-plan/references/canvas.md +121 -0
  56. package/corpus/templates/tasks/.agents/skills/visual-plan/references/document-quality.md +178 -0
  57. package/corpus/templates/tasks/.agents/skills/visual-plan/references/exemplar.md +62 -0
  58. package/corpus/templates/tasks/.agents/skills/visual-plan/references/wireframe.md +264 -0
  59. package/corpus/templates/tasks/.agents/skills/visual-recap/SKILL.md +634 -0
  60. package/corpus/templates/tasks/.agents/skills/visual-recap/agent-native-skill.json +12 -0
  61. package/corpus/templates/tasks/.agents/skills/visual-recap/references/wireframe.md +264 -0
  62. package/corpus/templates/tasks/.claude/commands/visual-plan.md +9 -0
  63. package/corpus/templates/tasks/.claude/commands/visual-recap.md +8 -0
  64. package/corpus/templates/tasks/.env.example +5 -0
  65. package/corpus/templates/tasks/.ignore +0 -0
  66. package/corpus/templates/tasks/.prettierrc +5 -0
  67. package/corpus/templates/tasks/AGENTS.md +158 -0
  68. package/corpus/templates/tasks/DEVELOPING.md +185 -0
  69. package/corpus/templates/tasks/README.md +16 -0
  70. package/corpus/templates/tasks/actions/bulk-delete-inbox-items.ts +24 -0
  71. package/corpus/templates/tasks/actions/bulk-delete-tasks.ts +21 -0
  72. package/corpus/templates/tasks/actions/bulk-mark-inbox-items-ready.ts +27 -0
  73. package/corpus/templates/tasks/actions/bulk-update-tasks.ts +35 -0
  74. package/corpus/templates/tasks/actions/create-custom-field.ts +22 -0
  75. package/corpus/templates/tasks/actions/create-inbox-item.ts +30 -0
  76. package/corpus/templates/tasks/actions/create-task.ts +33 -0
  77. package/corpus/templates/tasks/actions/delete-custom-field.ts +19 -0
  78. package/corpus/templates/tasks/actions/delete-inbox-item.ts +17 -0
  79. package/corpus/templates/tasks/actions/delete-task.ts +17 -0
  80. package/corpus/templates/tasks/actions/lib/boolean-query-param.ts +30 -0
  81. package/corpus/templates/tasks/actions/list-custom-fields.ts +18 -0
  82. package/corpus/templates/tasks/actions/list-inbox-items.ts +16 -0
  83. package/corpus/templates/tasks/actions/list-tasks.ts +52 -0
  84. package/corpus/templates/tasks/actions/list-visible-task-fields.ts +18 -0
  85. package/corpus/templates/tasks/actions/mark-inbox-item-ready.ts +31 -0
  86. package/corpus/templates/tasks/actions/navigate.ts +51 -0
  87. package/corpus/templates/tasks/actions/reorder-custom-fields.ts +27 -0
  88. package/corpus/templates/tasks/actions/reorder-inbox-items.ts +24 -0
  89. package/corpus/templates/tasks/actions/reorder-tasks.ts +29 -0
  90. package/corpus/templates/tasks/actions/run.ts +2 -0
  91. package/corpus/templates/tasks/actions/update-custom-field.ts +33 -0
  92. package/corpus/templates/tasks/actions/update-inbox-item.ts +24 -0
  93. package/corpus/templates/tasks/actions/update-task.ts +68 -0
  94. package/corpus/templates/tasks/actions/update-visible-task-fields.ts +27 -0
  95. package/corpus/templates/tasks/actions/view-screen-helpers.ts +68 -0
  96. package/corpus/templates/tasks/actions/view-screen.ts +207 -0
  97. package/corpus/templates/tasks/app/components/custom-fields/FieldCreateBar.tsx +121 -0
  98. package/corpus/templates/tasks/app/components/custom-fields/FieldListRow.tsx +116 -0
  99. package/corpus/templates/tasks/app/components/custom-fields/FieldsList.tsx +225 -0
  100. package/corpus/templates/tasks/app/components/custom-fields/FieldsListSkeleton.tsx +18 -0
  101. package/corpus/templates/tasks/app/components/custom-fields/FieldsPage.tsx +42 -0
  102. package/corpus/templates/tasks/app/components/custom-fields/editor/FieldEditorSidebar.tsx +69 -0
  103. package/corpus/templates/tasks/app/components/custom-fields/editor/FieldTitleSection.tsx +32 -0
  104. package/corpus/templates/tasks/app/components/custom-fields/editor/config/CurrencyConfigControl.tsx +49 -0
  105. package/corpus/templates/tasks/app/components/custom-fields/editor/config/FieldConfigControl.tsx +56 -0
  106. package/corpus/templates/tasks/app/components/custom-fields/editor/config/NumberConfigControl.tsx +52 -0
  107. package/corpus/templates/tasks/app/components/custom-fields/editor/config/PercentConfigControl.tsx +30 -0
  108. package/corpus/templates/tasks/app/components/custom-fields/editor/config/SelectConfigControl.tsx +135 -0
  109. package/corpus/templates/tasks/app/components/custom-fields/editor/config/select-colors.ts +23 -0
  110. package/corpus/templates/tasks/app/components/custom-fields/editor/config/utils.ts +52 -0
  111. package/corpus/templates/tasks/app/components/custom-fields/editor/types.ts +7 -0
  112. package/corpus/templates/tasks/app/components/dnd/SortableItem.tsx +72 -0
  113. package/corpus/templates/tasks/app/components/dnd/SortableList.tsx +136 -0
  114. package/corpus/templates/tasks/app/components/dnd/reorder-moving-items.ts +160 -0
  115. package/corpus/templates/tasks/app/components/dnd/sortable-drag-props.ts +60 -0
  116. package/corpus/templates/tasks/app/components/inbox/InboxList.tsx +222 -0
  117. package/corpus/templates/tasks/app/components/inbox/InboxListPage.tsx +39 -0
  118. package/corpus/templates/tasks/app/components/inbox/InboxListRow.tsx +122 -0
  119. package/corpus/templates/tasks/app/components/layout/Header.tsx +55 -0
  120. package/corpus/templates/tasks/app/components/layout/HeaderActions.tsx +84 -0
  121. package/corpus/templates/tasks/app/components/layout/Layout.tsx +134 -0
  122. package/corpus/templates/tasks/app/components/layout/Sidebar.tsx +211 -0
  123. package/corpus/templates/tasks/app/components/shared/AddListItemInput.tsx +57 -0
  124. package/corpus/templates/tasks/app/components/shared/AlertDialog.tsx +53 -0
  125. package/corpus/templates/tasks/app/components/shared/BulkDeleteDialog.tsx +76 -0
  126. package/corpus/templates/tasks/app/components/shared/ChipSelect.tsx +240 -0
  127. package/corpus/templates/tasks/app/components/shared/DeleteItemDialog.tsx +40 -0
  128. package/corpus/templates/tasks/app/components/shared/InlineEditable.tsx +166 -0
  129. package/corpus/templates/tasks/app/components/shared/ListEmptyState.tsx +13 -0
  130. package/corpus/templates/tasks/app/components/shared/ListErrorMessage.tsx +15 -0
  131. package/corpus/templates/tasks/app/components/shared/ListViewHeader.tsx +75 -0
  132. package/corpus/templates/tasks/app/components/shared/PageHeader.tsx +25 -0
  133. package/corpus/templates/tasks/app/components/shared/RowActionsMenu.tsx +42 -0
  134. package/corpus/templates/tasks/app/components/shared/SidePanel.tsx +99 -0
  135. package/corpus/templates/tasks/app/components/shared/dnd/ListRowPreview.tsx +37 -0
  136. package/corpus/templates/tasks/app/components/shared/list/List.tsx +63 -0
  137. package/corpus/templates/tasks/app/components/shared/list/ListRow.tsx +159 -0
  138. package/corpus/templates/tasks/app/components/shared/list/ListRowDragHandle.tsx +52 -0
  139. package/corpus/templates/tasks/app/components/shared/list/ListSkeletonRows.tsx +11 -0
  140. package/corpus/templates/tasks/app/components/shared/list/SortableListItemShell.tsx +32 -0
  141. package/corpus/templates/tasks/app/components/shared/list/types.ts +1 -0
  142. package/corpus/templates/tasks/app/components/shared/selection/ListSelectionBar.tsx +158 -0
  143. package/corpus/templates/tasks/app/components/shared/selection/ListSelectionHeaderToggle.tsx +28 -0
  144. package/corpus/templates/tasks/app/components/shared/selection/ListSelectionToolbar.tsx +81 -0
  145. package/corpus/templates/tasks/app/components/shared/selection/get-list-row-selection-ui.ts +41 -0
  146. package/corpus/templates/tasks/app/components/shared/selection/use-list-selection.ts +196 -0
  147. package/corpus/templates/tasks/app/components/tasks/TaskList.tsx +333 -0
  148. package/corpus/templates/tasks/app/components/tasks/TaskListHeaderRow.tsx +46 -0
  149. package/corpus/templates/tasks/app/components/tasks/TaskListPage.tsx +115 -0
  150. package/corpus/templates/tasks/app/components/tasks/TaskListRow.css +49 -0
  151. package/corpus/templates/tasks/app/components/tasks/TaskListRow.tsx +307 -0
  152. package/corpus/templates/tasks/app/components/tasks/fields/TaskFieldsSidebar.tsx +102 -0
  153. package/corpus/templates/tasks/app/components/tasks/fields/TaskTitleSection.tsx +27 -0
  154. package/corpus/templates/tasks/app/components/tasks/fields/controls/DateValueControl.tsx +21 -0
  155. package/corpus/templates/tasks/app/components/tasks/fields/controls/FieldValueControl.tsx +76 -0
  156. package/corpus/templates/tasks/app/components/tasks/fields/controls/MultiSelectValueControl.tsx +52 -0
  157. package/corpus/templates/tasks/app/components/tasks/fields/controls/NumberValueControl.tsx +56 -0
  158. package/corpus/templates/tasks/app/components/tasks/fields/controls/RichTextValueControl.tsx +115 -0
  159. package/corpus/templates/tasks/app/components/tasks/fields/controls/SingleSelectValueControl.tsx +61 -0
  160. package/corpus/templates/tasks/app/components/tasks/fields/controls/TextValueControl.tsx +20 -0
  161. package/corpus/templates/tasks/app/components/tasks/use-task-row-completion-animation.ts +181 -0
  162. package/corpus/templates/tasks/app/components/ui/accordion.tsx +56 -0
  163. package/corpus/templates/tasks/app/components/ui/alert-dialog.tsx +1 -0
  164. package/corpus/templates/tasks/app/components/ui/alert.tsx +1 -0
  165. package/corpus/templates/tasks/app/components/ui/aspect-ratio.tsx +5 -0
  166. package/corpus/templates/tasks/app/components/ui/avatar.tsx +1 -0
  167. package/corpus/templates/tasks/app/components/ui/badge.tsx +1 -0
  168. package/corpus/templates/tasks/app/components/ui/breadcrumb.tsx +115 -0
  169. package/corpus/templates/tasks/app/components/ui/button.tsx +1 -0
  170. package/corpus/templates/tasks/app/components/ui/calendar.tsx +70 -0
  171. package/corpus/templates/tasks/app/components/ui/card.tsx +1 -0
  172. package/corpus/templates/tasks/app/components/ui/carousel.tsx +260 -0
  173. package/corpus/templates/tasks/app/components/ui/chart.tsx +379 -0
  174. package/corpus/templates/tasks/app/components/ui/checkbox.tsx +1 -0
  175. package/corpus/templates/tasks/app/components/ui/collapsible.tsx +1 -0
  176. package/corpus/templates/tasks/app/components/ui/command.tsx +1 -0
  177. package/corpus/templates/tasks/app/components/ui/context-menu.tsx +1 -0
  178. package/corpus/templates/tasks/app/components/ui/dialog.tsx +1 -0
  179. package/corpus/templates/tasks/app/components/ui/drawer.tsx +1 -0
  180. package/corpus/templates/tasks/app/components/ui/dropdown-menu.tsx +1 -0
  181. package/corpus/templates/tasks/app/components/ui/form.tsx +177 -0
  182. package/corpus/templates/tasks/app/components/ui/hover-card.tsx +27 -0
  183. package/corpus/templates/tasks/app/components/ui/input-otp.tsx +69 -0
  184. package/corpus/templates/tasks/app/components/ui/input.tsx +1 -0
  185. package/corpus/templates/tasks/app/components/ui/label.tsx +1 -0
  186. package/corpus/templates/tasks/app/components/ui/menubar.tsx +254 -0
  187. package/corpus/templates/tasks/app/components/ui/navigation-menu.tsx +128 -0
  188. package/corpus/templates/tasks/app/components/ui/pagination.tsx +121 -0
  189. package/corpus/templates/tasks/app/components/ui/popover.tsx +1 -0
  190. package/corpus/templates/tasks/app/components/ui/progress.tsx +1 -0
  191. package/corpus/templates/tasks/app/components/ui/radio-group.tsx +1 -0
  192. package/corpus/templates/tasks/app/components/ui/resizable.tsx +43 -0
  193. package/corpus/templates/tasks/app/components/ui/scroll-area.tsx +1 -0
  194. package/corpus/templates/tasks/app/components/ui/select.tsx +1 -0
  195. package/corpus/templates/tasks/app/components/ui/separator.tsx +1 -0
  196. package/corpus/templates/tasks/app/components/ui/sheet.tsx +1 -0
  197. package/corpus/templates/tasks/app/components/ui/sidebar.tsx +782 -0
  198. package/corpus/templates/tasks/app/components/ui/skeleton.tsx +1 -0
  199. package/corpus/templates/tasks/app/components/ui/slider.tsx +1 -0
  200. package/corpus/templates/tasks/app/components/ui/sonner.tsx +1 -0
  201. package/corpus/templates/tasks/app/components/ui/spinner.tsx +1 -0
  202. package/corpus/templates/tasks/app/components/ui/switch.tsx +1 -0
  203. package/corpus/templates/tasks/app/components/ui/table.tsx +1 -0
  204. package/corpus/templates/tasks/app/components/ui/tabs.tsx +1 -0
  205. package/corpus/templates/tasks/app/components/ui/textarea.tsx +1 -0
  206. package/corpus/templates/tasks/app/components/ui/toast.tsx +1 -0
  207. package/corpus/templates/tasks/app/components/ui/toaster.tsx +1 -0
  208. package/corpus/templates/tasks/app/components/ui/toggle-group.tsx +1 -0
  209. package/corpus/templates/tasks/app/components/ui/toggle.tsx +43 -0
  210. package/corpus/templates/tasks/app/components/ui/tooltip.tsx +1 -0
  211. package/corpus/templates/tasks/app/components/ui/use-toast.ts +3 -0
  212. package/corpus/templates/tasks/app/entry.client.tsx +19 -0
  213. package/corpus/templates/tasks/app/entry.server.tsx +10 -0
  214. package/corpus/templates/tasks/app/global.css +93 -0
  215. package/corpus/templates/tasks/app/hooks/cache.ts +31 -0
  216. package/corpus/templates/tasks/app/hooks/use-custom-fields.ts +93 -0
  217. package/corpus/templates/tasks/app/hooks/use-inbox-items.ts +196 -0
  218. package/corpus/templates/tasks/app/hooks/use-mobile.tsx +21 -0
  219. package/corpus/templates/tasks/app/hooks/use-navigation-state.ts +40 -0
  220. package/corpus/templates/tasks/app/hooks/use-tasks.ts +276 -0
  221. package/corpus/templates/tasks/app/hooks/use-toast.ts +188 -0
  222. package/corpus/templates/tasks/app/hooks/use-visible-task-fields.ts +140 -0
  223. package/corpus/templates/tasks/app/lib/app-config.ts +11 -0
  224. package/corpus/templates/tasks/app/lib/tab-id.ts +1 -0
  225. package/corpus/templates/tasks/app/lib/utils.ts +1 -0
  226. package/corpus/templates/tasks/app/root.tsx +130 -0
  227. package/corpus/templates/tasks/app/routes/_index.tsx +22 -0
  228. package/corpus/templates/tasks/app/routes/extensions.$id.$slug.tsx +2 -0
  229. package/corpus/templates/tasks/app/routes/extensions.$id.tsx +11 -0
  230. package/corpus/templates/tasks/app/routes/extensions._index.tsx +11 -0
  231. package/corpus/templates/tasks/app/routes/extensions.tsx +5 -0
  232. package/corpus/templates/tasks/app/routes/fields.tsx +16 -0
  233. package/corpus/templates/tasks/app/routes/inbox.tsx +16 -0
  234. package/corpus/templates/tasks/app/routes/tasks.tsx +17 -0
  235. package/corpus/templates/tasks/app/routes/team.tsx +17 -0
  236. package/corpus/templates/tasks/app/routes.ts +4 -0
  237. package/corpus/templates/tasks/app/vite-env.d.ts +6 -0
  238. package/corpus/templates/tasks/components.json +20 -0
  239. package/corpus/templates/tasks/data/sync-config.json +1 -0
  240. package/corpus/templates/tasks/docs/features/README.md +81 -0
  241. package/corpus/templates/tasks/docs/features/f1-tasks.mdx +67 -0
  242. package/corpus/templates/tasks/docs/features/f2-custom-fields.mdx +68 -0
  243. package/corpus/templates/tasks/docs/features/f3-projects.mdx +33 -0
  244. package/corpus/templates/tasks/docs/features/f4-inbox.mdx +55 -0
  245. package/corpus/templates/tasks/docs/features/f5-views.mdx +65 -0
  246. package/corpus/templates/tasks/docs/features/f6-search.mdx +43 -0
  247. package/corpus/templates/tasks/docs/features/f7-first-run.mdx +66 -0
  248. package/corpus/templates/tasks/docs/features/f8-chat.mdx +120 -0
  249. package/corpus/templates/tasks/e2e/global-setup.ts +42 -0
  250. package/corpus/templates/tasks/e2e/helpers/inbox.ts +71 -0
  251. package/corpus/templates/tasks/e2e/helpers/tasks.ts +99 -0
  252. package/corpus/templates/tasks/learnings.defaults.md +5 -0
  253. package/corpus/templates/tasks/netlify.toml +10 -0
  254. package/corpus/templates/tasks/package.json +104 -0
  255. package/corpus/templates/tasks/playwright.config.ts +34 -0
  256. package/corpus/templates/tasks/public/agent-native-icon-dark.svg +10 -0
  257. package/corpus/templates/tasks/public/agent-native-icon-light.svg +10 -0
  258. package/corpus/templates/tasks/public/agent-native-logo-dark.svg +21 -0
  259. package/corpus/templates/tasks/public/agent-native-logo-light.svg +21 -0
  260. package/corpus/templates/tasks/public/favicon.svg +1 -0
  261. package/corpus/templates/tasks/public/icon-180.svg +1 -0
  262. package/corpus/templates/tasks/public/icon-192.svg +1 -0
  263. package/corpus/templates/tasks/public/icon-512.svg +1 -0
  264. package/corpus/templates/tasks/public/manifest.json +21 -0
  265. package/corpus/templates/tasks/react-router.config.ts +7 -0
  266. package/corpus/templates/tasks/server/custom-fields/normalize.ts +163 -0
  267. package/corpus/templates/tasks/server/custom-fields/parse.ts +146 -0
  268. package/corpus/templates/tasks/server/custom-fields/schema.ts +147 -0
  269. package/corpus/templates/tasks/server/custom-fields/store.ts +425 -0
  270. package/corpus/templates/tasks/server/custom-fields/task-fields.ts +104 -0
  271. package/corpus/templates/tasks/server/custom-fields/types.ts +98 -0
  272. package/corpus/templates/tasks/server/custom-fields/validate.ts +210 -0
  273. package/corpus/templates/tasks/server/custom-fields/values/store.ts +386 -0
  274. package/corpus/templates/tasks/server/db/bulk-write.ts +30 -0
  275. package/corpus/templates/tasks/server/db/index.ts +5 -0
  276. package/corpus/templates/tasks/server/db/record-utils.ts +7 -0
  277. package/corpus/templates/tasks/server/db/schema.ts +67 -0
  278. package/corpus/templates/tasks/server/db/test-tasks-table.ts +76 -0
  279. package/corpus/templates/tasks/server/db/transaction.ts +6 -0
  280. package/corpus/templates/tasks/server/errors.ts +31 -0
  281. package/corpus/templates/tasks/server/inbox/store.ts +216 -0
  282. package/corpus/templates/tasks/server/middleware/auth.ts +15 -0
  283. package/corpus/templates/tasks/server/plugins/agent-chat.ts +46 -0
  284. package/corpus/templates/tasks/server/plugins/auth.ts +15 -0
  285. package/corpus/templates/tasks/server/plugins/db.ts +90 -0
  286. package/corpus/templates/tasks/server/routes/[...page].get.ts +5 -0
  287. package/corpus/templates/tasks/server/stored-items/store.ts +548 -0
  288. package/corpus/templates/tasks/server/tasks/store.ts +306 -0
  289. package/corpus/templates/tasks/server/user-config/store.ts +138 -0
  290. package/corpus/templates/tasks/shared/app-state-tabs.ts +15 -0
  291. package/corpus/templates/tasks/shared/boolean-param.ts +22 -0
  292. package/corpus/templates/tasks/shared/bulk-limits.ts +7 -0
  293. package/corpus/templates/tasks/shared/list-screen-snapshot.ts +90 -0
  294. package/corpus/templates/tasks/shared/navigation.ts +104 -0
  295. package/corpus/templates/tasks/shared/visible-task-fields.ts +2 -0
  296. package/corpus/templates/tasks/ssr-entry.ts +15 -0
  297. package/corpus/templates/tasks/tsconfig.json +24 -0
  298. package/corpus/templates/tasks/vite.config.ts +19 -0
  299. package/corpus/templates/tasks/vitest.config.ts +8 -0
  300. package/dist/cli/templates-meta.d.ts.map +1 -1
  301. package/dist/cli/templates-meta.js +13 -0
  302. package/dist/cli/templates-meta.js.map +1 -1
  303. package/dist/client/agent-page/AgentTabsPage.js +1 -1
  304. package/dist/client/agent-page/AgentTabsPage.js.map +1 -1
  305. package/dist/client/org/OrgSwitcher.d.ts.map +1 -1
  306. package/dist/client/org/OrgSwitcher.js +2 -1
  307. package/dist/client/org/OrgSwitcher.js.map +1 -1
  308. package/dist/collab/routes.d.ts +1 -1
  309. package/dist/collab/struct-routes.d.ts +1 -1
  310. package/dist/notifications/routes.d.ts +2 -2
  311. package/dist/observability/routes.d.ts +5 -5
  312. package/dist/resources/handlers.d.ts +3 -3
  313. package/dist/secrets/routes.d.ts +9 -9
  314. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  315. package/dist/server/transcribe-voice.d.ts +1 -1
  316. package/package.json +1 -1
@@ -0,0 +1,222 @@
1
+ import { useEffect, useState } from "react";
2
+ import { toast } from "sonner";
3
+
4
+ import { INERT_SORTABLE_PROPS } from "@/components/dnd/SortableItem";
5
+ import { InboxListRow } from "@/components/inbox/InboxListRow";
6
+ import { AddListItemInput } from "@/components/shared/AddListItemInput";
7
+ import { BulkDeleteDialog } from "@/components/shared/BulkDeleteDialog";
8
+ import { DeleteItemDialog } from "@/components/shared/DeleteItemDialog";
9
+ import { ListRowPreview } from "@/components/shared/dnd/ListRowPreview";
10
+ import { List } from "@/components/shared/list/List";
11
+ import { ListSkeletonRows } from "@/components/shared/list/ListSkeletonRows";
12
+ import { ListEmptyState } from "@/components/shared/ListEmptyState";
13
+ import { ListViewHeader } from "@/components/shared/ListViewHeader";
14
+ import { ListSelectionBar } from "@/components/shared/selection/ListSelectionBar";
15
+ import { useListSelection } from "@/components/shared/selection/use-list-selection";
16
+ import {
17
+ useBulkDeleteInboxItems,
18
+ useCreateInboxItem,
19
+ useDeleteInboxItem,
20
+ useMarkInboxItemReady,
21
+ useReorderInboxItems,
22
+ useUpdateInboxItem,
23
+ } from "@/hooks/use-inbox-items";
24
+ import type { InboxItem } from "@/hooks/use-inbox-items";
25
+
26
+ interface InboxListProps {
27
+ serverItems: InboxItem[];
28
+ isPending: boolean;
29
+ selectedInboxItemId: string | null;
30
+ }
31
+
32
+ export function InboxList({
33
+ serverItems,
34
+ isPending,
35
+ selectedInboxItemId,
36
+ }: InboxListProps) {
37
+ const createInboxItem = useCreateInboxItem();
38
+ const updateInboxItem = useUpdateInboxItem();
39
+ const deleteInboxItem = useDeleteInboxItem();
40
+ const markInboxItemReady = useMarkInboxItemReady();
41
+ const reorderInboxItems = useReorderInboxItems();
42
+ const bulkDeleteInboxItems = useBulkDeleteInboxItems();
43
+ const [items, setItems] = useState<InboxItem[]>(serverItems);
44
+ const [bulkDeleteOpen, setBulkDeleteOpen] = useState(false);
45
+ const [pendingDeleteId, setPendingDeleteId] = useState<string | null>(null);
46
+ const selection = useListSelection(items, "inboxSelection");
47
+ const selectionActive = selection.state.selectionMode;
48
+ const toolbarBusy =
49
+ createInboxItem.isPending ||
50
+ updateInboxItem.isPending ||
51
+ deleteInboxItem.isPending ||
52
+ markInboxItemReady.isPending;
53
+
54
+ useEffect(() => {
55
+ setItems(serverItems);
56
+ }, [serverItems]);
57
+
58
+ const pendingDeleteItem = pendingDeleteId
59
+ ? items.find((item) => item.id === pendingDeleteId)
60
+ : null;
61
+
62
+ function handleReorder(nextItems: InboxItem[]) {
63
+ setItems(nextItems);
64
+ reorderInboxItems.mutate(
65
+ { inboxItemIds: nextItems.map((item) => item.id) },
66
+ { onError: () => setItems(serverItems) },
67
+ );
68
+ }
69
+
70
+ async function handleBulkDelete(ids: string[]) {
71
+ await bulkDeleteInboxItems.mutateAsync({ inboxItemIds: ids });
72
+ }
73
+
74
+ async function confirmBulkDelete() {
75
+ const ids = selection.state.selectedItems.map((item) => item.id);
76
+ if (ids.length === 0) return;
77
+
78
+ try {
79
+ await handleBulkDelete(ids);
80
+ toast.success(
81
+ `Deleted ${ids.length} ${ids.length === 1 ? "inbox item" : "inbox items"}`,
82
+ );
83
+ selection.actions.clearSelection();
84
+ setBulkDeleteOpen(false);
85
+ } catch {
86
+ toast.error("Could not delete selected inbox items.");
87
+ }
88
+ }
89
+
90
+ return (
91
+ <>
92
+ <ListViewHeader
93
+ title="Inbox"
94
+ description="Capture rough ideas here, then mark ready when they become tasks."
95
+ isPending={isPending}
96
+ showSelectToggle={items.length > 0}
97
+ selection={selection}
98
+ toolbarBusy={toolbarBusy}
99
+ />
100
+
101
+ <div className="flex min-h-0 flex-1 flex-col overflow-hidden">
102
+ <div className="shrink-0">
103
+ {selectionActive ? (
104
+ <ListSelectionBar
105
+ promotedToTask={false}
106
+ items={items}
107
+ selection={selection}
108
+ toolbarBusy={toolbarBusy}
109
+ onOpenBulkDelete={() => setBulkDeleteOpen(true)}
110
+ />
111
+ ) : (
112
+ <AddListItemInput
113
+ disabled={createInboxItem.isPending}
114
+ onCreate={(title) => createInboxItem.mutateAsync({ title })}
115
+ placeholder="Add to inbox..."
116
+ buttonLabel="Add item"
117
+ inputAriaLabel="New inbox item title"
118
+ errorMessage="Failed to add inbox item. Please try again."
119
+ />
120
+ )}
121
+ </div>
122
+
123
+ {isPending ? (
124
+ <div
125
+ aria-label="Inbox list"
126
+ className="min-h-0 flex-1 overflow-y-auto overscroll-contain pr-1 outline-none"
127
+ >
128
+ <ListSkeletonRows />
129
+ </div>
130
+ ) : items.length === 0 ? (
131
+ <div
132
+ aria-label="Inbox list"
133
+ className="min-h-0 flex-1 overflow-y-auto overscroll-contain pr-1 outline-none"
134
+ >
135
+ <ListEmptyState
136
+ heading="Inbox is empty"
137
+ description="Add an item above or ask chat to capture something for triage."
138
+ />
139
+ </div>
140
+ ) : (
141
+ <div className="mt-2 min-h-0 flex-1 overflow-y-auto overscroll-contain pr-1 outline-none">
142
+ <List
143
+ items={items}
144
+ selection={selection}
145
+ ariaLabel="Inbox list"
146
+ listClassName="flex flex-col gap-3 pb-6"
147
+ onReorder={handleReorder}
148
+ renderItem={({ item, sortable }) => (
149
+ <InboxListRow
150
+ sortable={sortable}
151
+ selection={selection}
152
+ item={item}
153
+ highlighted={selectedInboxItemId === item.id}
154
+ onUpdateTitle={(title) =>
155
+ updateInboxItem.mutateAsync({ inboxItemId: item.id, title })
156
+ }
157
+ onMarkReady={() =>
158
+ markInboxItemReady.mutateAsync({ inboxItemId: item.id })
159
+ }
160
+ onRequestDelete={() => setPendingDeleteId(item.id)}
161
+ />
162
+ )}
163
+ renderOverlay={({ item, blockDragCount }) => (
164
+ <ListRowPreview
165
+ id={item.id}
166
+ overlayDataAttribute="data-dnd-overlay-inbox-item-id"
167
+ blockDragCount={blockDragCount}
168
+ >
169
+ <InboxListRow
170
+ sortable={INERT_SORTABLE_PROPS}
171
+ selection={selection}
172
+ item={item}
173
+ highlighted={selectedInboxItemId === item.id}
174
+ onUpdateTitle={(title) =>
175
+ updateInboxItem.mutateAsync({
176
+ inboxItemId: item.id,
177
+ title,
178
+ })
179
+ }
180
+ onMarkReady={() =>
181
+ markInboxItemReady.mutateAsync({ inboxItemId: item.id })
182
+ }
183
+ onRequestDelete={() => setPendingDeleteId(item.id)}
184
+ />
185
+ </ListRowPreview>
186
+ )}
187
+ />
188
+ </div>
189
+ )}
190
+
191
+ {!selectionActive ? (
192
+ <DeleteItemDialog
193
+ open={pendingDeleteItem !== null}
194
+ onOpenChange={(open) => {
195
+ if (!open) setPendingDeleteId(null);
196
+ }}
197
+ entityLabel="inbox item"
198
+ itemTitle={pendingDeleteItem?.title ?? null}
199
+ pending={deleteInboxItem.isPending}
200
+ onConfirm={async () => {
201
+ if (!pendingDeleteId) return;
202
+ await deleteInboxItem.mutateAsync({
203
+ inboxItemId: pendingDeleteId,
204
+ });
205
+ setPendingDeleteId(null);
206
+ }}
207
+ />
208
+ ) : null}
209
+
210
+ <BulkDeleteDialog
211
+ open={bulkDeleteOpen}
212
+ onOpenChange={setBulkDeleteOpen}
213
+ selectedItems={selection.state.selectedItems}
214
+ entitySingular="inbox item"
215
+ entityPlural="inbox items"
216
+ pending={bulkDeleteInboxItems.isPending}
217
+ onConfirm={() => void confirmBulkDelete()}
218
+ />
219
+ </div>
220
+ </>
221
+ );
222
+ }
@@ -0,0 +1,39 @@
1
+ import { useSearchParams } from "react-router";
2
+
3
+ import { InboxList } from "@/components/inbox/InboxList";
4
+ import { ListErrorMessage } from "@/components/shared/ListErrorMessage";
5
+ import { ListViewHeader } from "@/components/shared/ListViewHeader";
6
+ import { useInboxItems } from "@/hooks/use-inbox-items";
7
+
8
+ export function InboxListPage() {
9
+ const [searchParams] = useSearchParams();
10
+ const selectedInboxItemId = searchParams.get("inboxItem");
11
+ const { items: serverItems, isPending, isError, error } = useInboxItems();
12
+
13
+ return (
14
+ <div className="mx-auto flex h-full min-h-0 w-full max-w-3xl flex-col gap-6 overflow-hidden p-4 md:p-6">
15
+ {isError ? (
16
+ <>
17
+ <ListViewHeader
18
+ title="Inbox"
19
+ description="Capture rough ideas here, then mark ready when they become tasks."
20
+ isPending={false}
21
+ showSelectToggle={false}
22
+ selection={null}
23
+ toolbarBusy={false}
24
+ />
25
+ <ListErrorMessage
26
+ error={error}
27
+ fallbackMessage="Failed to load inbox items."
28
+ />
29
+ </>
30
+ ) : (
31
+ <InboxList
32
+ serverItems={serverItems}
33
+ isPending={isPending}
34
+ selectedInboxItemId={selectedInboxItemId}
35
+ />
36
+ )}
37
+ </div>
38
+ );
39
+ }
@@ -0,0 +1,122 @@
1
+ import { IconChecks, IconTrash } from "@tabler/icons-react";
2
+ import { useState } from "react";
3
+
4
+ import type { SortableItemRenderProps } from "@/components/dnd/SortableItem";
5
+ import {
6
+ InlineEditable,
7
+ LIST_ROW_TITLE_FIELD_CLASS,
8
+ } from "@/components/shared/InlineEditable";
9
+ import { ListRow } from "@/components/shared/list/ListRow";
10
+ import { ListRowDragHandle } from "@/components/shared/list/ListRowDragHandle";
11
+ import { RowActionsMenu } from "@/components/shared/RowActionsMenu";
12
+ import type { ListSelection } from "@/components/shared/selection/use-list-selection";
13
+ import { Button } from "@/components/ui/button";
14
+ import { DropdownMenuItem } from "@/components/ui/dropdown-menu";
15
+ import { cn } from "@/lib/utils";
16
+
17
+ export interface InboxListRowProps {
18
+ sortable: SortableItemRenderProps;
19
+ selection: ListSelection<{ id: string }>;
20
+ item: { id: string; title: string };
21
+ highlighted?: boolean;
22
+ onUpdateTitle: (title: string) => Promise<unknown>;
23
+ onRequestDelete: () => void;
24
+ onMarkReady: () => Promise<unknown>;
25
+ }
26
+
27
+ export function InboxListRow({
28
+ sortable,
29
+ selection,
30
+ item,
31
+ highlighted = false,
32
+ onUpdateTitle,
33
+ onRequestDelete,
34
+ onMarkReady,
35
+ }: InboxListRowProps) {
36
+ const [displayTitle, setDisplayTitle] = useState(item.title);
37
+ const [markReadyPending, setMarkReadyPending] = useState(false);
38
+ const busy = markReadyPending;
39
+
40
+ async function handleMarkReady() {
41
+ setMarkReadyPending(true);
42
+ try {
43
+ await onMarkReady();
44
+ } catch {
45
+ setMarkReadyPending(false);
46
+ }
47
+ }
48
+
49
+ return (
50
+ <ListRow
51
+ sortable={sortable}
52
+ item={item}
53
+ itemLabel={displayTitle}
54
+ selection={selection}
55
+ highlighted={highlighted}
56
+ dataAttributes={{ "data-inbox-item-id": item.id }}
57
+ >
58
+ {({ rowDrag, rowSelection }) => (
59
+ <>
60
+ <ListRowDragHandle
61
+ rowDrag={rowDrag}
62
+ rowSelection={rowSelection}
63
+ displayTitle={displayTitle}
64
+ disabled={busy}
65
+ />
66
+
67
+ <div className="min-w-0 flex-1">
68
+ {rowSelection.selectionMode ? (
69
+ <div
70
+ className={cn(
71
+ LIST_ROW_TITLE_FIELD_CLASS,
72
+ "flex items-center truncate border-transparent bg-transparent text-left",
73
+ )}
74
+ >
75
+ {displayTitle}
76
+ </div>
77
+ ) : (
78
+ <InlineEditable
79
+ value={item.title}
80
+ onSave={onUpdateTitle}
81
+ onDisplayTitleChange={setDisplayTitle}
82
+ ariaLabel="Edit title"
83
+ disabled={busy}
84
+ titleDragProps={rowDrag.titleDragProps}
85
+ />
86
+ )}
87
+ </div>
88
+
89
+ <Button
90
+ type="button"
91
+ variant="secondary"
92
+ size="sm"
93
+ disabled={busy || rowSelection.selectionMode}
94
+ onClick={() => void handleMarkReady()}
95
+ >
96
+ Mark ready
97
+ </Button>
98
+
99
+ <RowActionsMenu
100
+ ariaLabel={`Actions for ${displayTitle}`}
101
+ disabled={busy || rowSelection.selectionMode}
102
+ >
103
+ <DropdownMenuItem
104
+ className="gap-2"
105
+ onSelect={() => selection.actions.startSelection(item.id)}
106
+ >
107
+ <IconChecks className="size-4" />
108
+ Select
109
+ </DropdownMenuItem>
110
+ <DropdownMenuItem
111
+ className="gap-2 text-destructive focus:bg-destructive focus:text-destructive-foreground"
112
+ onSelect={onRequestDelete}
113
+ >
114
+ <IconTrash className="size-4" />
115
+ Delete
116
+ </DropdownMenuItem>
117
+ </RowActionsMenu>
118
+ </>
119
+ )}
120
+ </ListRow>
121
+ );
122
+ }
@@ -0,0 +1,55 @@
1
+ import { AgentToggleButton } from "@agent-native/core/client";
2
+ import { IconMenu2 } from "@tabler/icons-react";
3
+ import { useLocation } from "react-router";
4
+
5
+ import { APP_TITLE } from "@/lib/app-config";
6
+
7
+ import { useHeaderTitle, useHeaderActions } from "./HeaderActions";
8
+
9
+ const pageTitles: Record<string, string> = {
10
+ "/tasks": "Tasks",
11
+ "/settings": "Settings",
12
+ "/team": "Team",
13
+ };
14
+
15
+ function resolveTitle(pathname: string): string {
16
+ if (pageTitles[pathname]) return pageTitles[pathname];
17
+ if (pathname.startsWith("/extensions")) return "Extensions";
18
+ return APP_TITLE;
19
+ }
20
+
21
+ interface HeaderProps {
22
+ onOpenMobileSidebar?: () => void;
23
+ }
24
+
25
+ export function Header({ onOpenMobileSidebar }: HeaderProps) {
26
+ const location = useLocation();
27
+ const title = useHeaderTitle();
28
+ const actions = useHeaderActions();
29
+
30
+ return (
31
+ <header className="flex h-12 items-center gap-3 border-b border-border bg-background px-4 lg:px-6 shrink-0">
32
+ {onOpenMobileSidebar && (
33
+ <button
34
+ type="button"
35
+ onClick={onOpenMobileSidebar}
36
+ aria-label="Open navigation"
37
+ className="flex h-9 w-9 cursor-pointer items-center justify-center rounded-md text-muted-foreground hover:text-foreground hover:bg-accent md:hidden"
38
+ >
39
+ <IconMenu2 className="h-4 w-4" />
40
+ </button>
41
+ )}
42
+ <div className="flex items-center gap-3 flex-1 min-w-0">
43
+ {title ?? (
44
+ <h1 className="text-lg font-semibold tracking-tight truncate">
45
+ {resolveTitle(location.pathname)}
46
+ </h1>
47
+ )}
48
+ </div>
49
+ <div className="flex items-center gap-2 shrink-0">
50
+ {actions}
51
+ <AgentToggleButton />
52
+ </div>
53
+ </header>
54
+ );
55
+ }
@@ -0,0 +1,84 @@
1
+ import {
2
+ useEffect,
3
+ useSyncExternalStore,
4
+ type ReactNode,
5
+ type FC,
6
+ } from "react";
7
+
8
+ /**
9
+ * External store for the page's header title + actions. We use an external
10
+ * store (not React context) so that pages can inject ReactNode without
11
+ * subscribing to the header state themselves — subscribing would trigger a
12
+ * re-render on every update, which in turn creates new JSX, which updates
13
+ * the store again, which re-renders… an infinite loop.
14
+ *
15
+ * Only <Header /> reads the store via useSyncExternalStore; pages only write.
16
+ */
17
+
18
+ type Listener = () => void;
19
+
20
+ let currentTitle: ReactNode = null;
21
+ let currentActions: ReactNode = null;
22
+ const listeners = new Set<Listener>();
23
+
24
+ function notify() {
25
+ for (const l of listeners) l();
26
+ }
27
+
28
+ function subscribe(l: Listener): () => void {
29
+ listeners.add(l);
30
+ return () => {
31
+ listeners.delete(l);
32
+ };
33
+ }
34
+
35
+ /** Consumed only by <Header /> — returns the current title. */
36
+ export function useHeaderTitle(): ReactNode {
37
+ return useSyncExternalStore(
38
+ subscribe,
39
+ () => currentTitle,
40
+ () => currentTitle,
41
+ );
42
+ }
43
+
44
+ /** Consumed only by <Header /> — returns the current actions slot. */
45
+ export function useHeaderActions(): ReactNode {
46
+ return useSyncExternalStore(
47
+ subscribe,
48
+ () => currentActions,
49
+ () => currentActions,
50
+ );
51
+ }
52
+
53
+ /**
54
+ * Provider is now a no-op wrapper for backwards compatibility — the state
55
+ * lives in the module-level store above. Kept as a component so callers of
56
+ * <HeaderActionsProvider> don't need to change.
57
+ */
58
+ export const HeaderActionsProvider: FC<{ children: ReactNode }> = ({
59
+ children,
60
+ }) => <>{children}</>;
61
+
62
+ /** Mount a custom title into the app header. Cleans up on unmount. */
63
+ export function useSetPageTitle(node: ReactNode) {
64
+ useEffect(() => {
65
+ currentTitle = node;
66
+ notify();
67
+ return () => {
68
+ currentTitle = null;
69
+ notify();
70
+ };
71
+ });
72
+ }
73
+
74
+ /** Mount ReactNode into the header's actions slot. Cleans up on unmount. */
75
+ export function useSetHeaderActions(node: ReactNode) {
76
+ useEffect(() => {
77
+ currentActions = node;
78
+ notify();
79
+ return () => {
80
+ currentActions = null;
81
+ notify();
82
+ };
83
+ });
84
+ }
@@ -0,0 +1,134 @@
1
+ import {
2
+ AgentSidebar,
3
+ focusAgentChat,
4
+ useAgentChatHomeHandoff,
5
+ } from "@agent-native/core/client";
6
+ import { IconMenu2 } from "@tabler/icons-react";
7
+ import { useState, useEffect } from "react";
8
+ import { useLocation } from "react-router";
9
+
10
+ import { Button } from "@/components/ui/button";
11
+ import {
12
+ Sheet,
13
+ SheetContent,
14
+ SheetDescription,
15
+ SheetTitle,
16
+ } from "@/components/ui/sheet";
17
+ import { TAB_ID } from "@/lib/tab-id";
18
+
19
+ import { Header } from "./Header";
20
+ import { HeaderActionsProvider } from "./HeaderActions";
21
+ import { Sidebar } from "./Sidebar";
22
+
23
+ interface LayoutProps {
24
+ children: React.ReactNode;
25
+ }
26
+
27
+ const SIDEBAR_COLLAPSE_KEY = "tasks.sidebar.collapsed";
28
+
29
+ /**
30
+ * Routes whose page renders its own toolbar. Layout still wraps these with the
31
+ * left Sidebar and agent surfaces but skips the global Header so they don't
32
+ * double-stack chrome.
33
+ */
34
+ function routeOwnsToolbar(pathname: string): boolean {
35
+ return pathname === "/tasks" || pathname.startsWith("/extensions");
36
+ }
37
+
38
+ export function Layout({ children }: LayoutProps) {
39
+ const location = useLocation();
40
+ const [mobileSidebarOpen, setMobileSidebarOpen] = useState(false);
41
+ const [sidebarCollapsed, setSidebarCollapsed] = useState(true);
42
+ const chatHomeHandoffActive = useAgentChatHomeHandoff({
43
+ storageKey: "chat",
44
+ activePath: location.pathname,
45
+ enabled: true,
46
+ });
47
+
48
+ useEffect(() => {
49
+ setMobileSidebarOpen(false);
50
+ }, [location.pathname]);
51
+
52
+ useEffect(() => {
53
+ try {
54
+ const stored = window.localStorage.getItem(SIDEBAR_COLLAPSE_KEY);
55
+ if (stored !== null) setSidebarCollapsed(stored === "1");
56
+ } catch {
57
+ // Ignore storage access errors; the default collapsed state still works.
58
+ }
59
+ }, []);
60
+
61
+ useEffect(() => {
62
+ try {
63
+ window.localStorage.setItem(
64
+ SIDEBAR_COLLAPSE_KEY,
65
+ sidebarCollapsed ? "1" : "0",
66
+ );
67
+ } catch {
68
+ // Ignore storage access errors.
69
+ }
70
+ }, [sidebarCollapsed]);
71
+
72
+ const ownsToolbar = routeOwnsToolbar(location.pathname);
73
+
74
+ const contentFrame = (
75
+ <div className="flex h-full min-w-0 flex-1 flex-col overflow-hidden">
76
+ {ownsToolbar ? (
77
+ <div className="flex h-12 shrink-0 items-center border-b border-border px-4 md:hidden">
78
+ <button
79
+ type="button"
80
+ onClick={() => setMobileSidebarOpen(true)}
81
+ aria-label="Open navigation"
82
+ className="flex h-9 w-9 cursor-pointer items-center justify-center rounded-md text-muted-foreground hover:bg-accent hover:text-foreground"
83
+ >
84
+ <IconMenu2 className="h-4 w-4" />
85
+ </button>
86
+ </div>
87
+ ) : (
88
+ <Header onOpenMobileSidebar={() => setMobileSidebarOpen(true)} />
89
+ )}
90
+ <main className="min-w-0 flex-1 overflow-y-auto overscroll-contain">
91
+ {children}
92
+ </main>
93
+ </div>
94
+ );
95
+
96
+ return (
97
+ <HeaderActionsProvider>
98
+ <div className="flex h-screen w-full overflow-hidden bg-background text-foreground">
99
+ <div className="hidden md:block">
100
+ <Sidebar
101
+ collapsed={sidebarCollapsed}
102
+ onCollapsedChange={setSidebarCollapsed}
103
+ />
104
+ </div>
105
+ <Sheet open={mobileSidebarOpen} onOpenChange={setMobileSidebarOpen}>
106
+ <SheetContent side="left" className="p-0 w-[260px]">
107
+ <SheetTitle className="sr-only">Navigation</SheetTitle>
108
+ <SheetDescription className="sr-only">
109
+ App navigation links
110
+ </SheetDescription>
111
+ <Sidebar collapsed={false} collapsible={false} />
112
+ </SheetContent>
113
+ </Sheet>
114
+ <AgentSidebar
115
+ position="right"
116
+ chatViewTransition
117
+ storageKey="chat"
118
+ browserTabId={TAB_ID}
119
+ openOnChatRunning={chatHomeHandoffActive}
120
+ onFullscreenRequest={() => focusAgentChat()}
121
+ emptyStateText="Ask the agent to inspect or change this app."
122
+ dynamicSuggestions={false}
123
+ suggestions={[
124
+ "Check my calendar and mails - any tasks for today?",
125
+ "Prioritise my tasks",
126
+ "I need to clean my house, create a list of tasks for it",
127
+ ]}
128
+ >
129
+ {contentFrame}
130
+ </AgentSidebar>
131
+ </div>
132
+ </HeaderActionsProvider>
133
+ );
134
+ }