@agent-native/core 0.101.13 → 0.101.14

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 (266) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/core/CHANGELOG.md +7 -0
  3. package/corpus/core/package.json +4 -2
  4. package/corpus/core/src/cli/create.ts +32 -10
  5. package/corpus/core/src/client/AgentPanel.tsx +61 -48
  6. package/corpus/core/src/client/chat/message-components.tsx +1 -0
  7. package/corpus/core/src/client/chat/tool-call-display.tsx +12 -2
  8. package/corpus/templates/clips/app/components/player/share-dialog.tsx +1 -1
  9. package/corpus/templates/clips/app/i18n/ar-SA.ts +1 -0
  10. package/corpus/templates/clips/app/i18n/de-DE.ts +1 -0
  11. package/corpus/templates/clips/app/i18n/en-US.ts +1 -0
  12. package/corpus/templates/clips/app/i18n/es-ES.ts +1 -0
  13. package/corpus/templates/clips/app/i18n/fr-FR.ts +1 -0
  14. package/corpus/templates/clips/app/i18n/hi-IN.ts +1 -0
  15. package/corpus/templates/clips/app/i18n/ja-JP.ts +1 -0
  16. package/corpus/templates/clips/app/i18n/ko-KR.ts +1 -0
  17. package/corpus/templates/clips/app/i18n/pt-BR.ts +1 -0
  18. package/corpus/templates/clips/app/i18n/zh-CN.ts +1 -0
  19. package/corpus/templates/clips/app/i18n/zh-TW.ts +1 -0
  20. package/corpus/templates/clips/changelog/2026-07-15-public-clip-links-are-labeled-share-with-humans-for-clarity.md +6 -0
  21. package/corpus/templates/forms/actions/delete-form.ts +4 -0
  22. package/corpus/templates/forms/actions/patch-form-fields.ts +3 -0
  23. package/corpus/templates/forms/actions/restore-form.ts +3 -0
  24. package/corpus/templates/forms/actions/update-form.ts +3 -0
  25. package/corpus/templates/forms/app/pages/FormBuilderPage.tsx +2 -2
  26. package/corpus/templates/forms/app/pages/ResponsesPage.tsx +2 -2
  27. package/corpus/templates/forms/changelog/2026-07-15-published-forms-now-reflect-field-updates-immediately-at-the.md +6 -0
  28. package/corpus/templates/forms/changelog/2026-07-15-response-rows-now-keep-every-column-aligned-to-the-top-for-c.md +6 -0
  29. package/corpus/templates/forms/server/handlers/forms.ts +1 -0
  30. package/corpus/templates/forms/server/lib/public-form-ssr.ts +47 -4
  31. package/corpus/templates/slides/app/components/editor/SlideEditor.tsx +189 -82
  32. package/corpus/templates/slides/app/pages/DeckEditor.tsx +1 -1
  33. package/dist/cli/create.d.ts +5 -2
  34. package/dist/cli/create.d.ts.map +1 -1
  35. package/dist/cli/create.js +22 -11
  36. package/dist/cli/create.js.map +1 -1
  37. package/dist/client/AgentPanel.d.ts +2 -0
  38. package/dist/client/AgentPanel.d.ts.map +1 -1
  39. package/dist/client/AgentPanel.js +24 -13
  40. package/dist/client/AgentPanel.js.map +1 -1
  41. package/dist/client/chat/message-components.d.ts.map +1 -1
  42. package/dist/client/chat/message-components.js +1 -1
  43. package/dist/client/chat/message-components.js.map +1 -1
  44. package/dist/client/chat/tool-call-display.d.ts +3 -1
  45. package/dist/client/chat/tool-call-display.d.ts.map +1 -1
  46. package/dist/client/chat/tool-call-display.js +6 -5
  47. package/dist/client/chat/tool-call-display.js.map +1 -1
  48. package/dist/notifications/routes.d.ts +2 -2
  49. package/dist/resources/handlers.d.ts +1 -1
  50. package/dist/templates/chat/.agents/skills/actions/SKILL.md +492 -0
  51. package/dist/templates/chat/.agents/skills/adding-a-feature/SKILL.md +189 -0
  52. package/dist/templates/chat/.agents/skills/agent-native-docs/SKILL.md +76 -0
  53. package/dist/templates/chat/.agents/skills/capture-learnings/SKILL.md +89 -0
  54. package/dist/templates/chat/.agents/skills/create-skill/SKILL.md +221 -0
  55. package/dist/templates/chat/.agents/skills/delegate-to-agent/SKILL.md +263 -0
  56. package/dist/templates/chat/.agents/skills/frontend-design/SKILL.md +112 -0
  57. package/dist/templates/chat/.agents/skills/real-time-sync/SKILL.md +232 -0
  58. package/dist/templates/chat/.agents/skills/security/SKILL.md +280 -0
  59. package/dist/templates/chat/.agents/skills/self-modifying-code/SKILL.md +106 -0
  60. package/dist/templates/chat/.agents/skills/shadcn-ui/SKILL.md +95 -0
  61. package/dist/templates/chat/.agents/skills/upgrade-agent-native/SKILL.md +87 -0
  62. package/dist/templates/chat/.env.example +5 -0
  63. package/dist/templates/chat/.ignore +0 -0
  64. package/dist/templates/chat/.oxfmtrc.json +8 -0
  65. package/dist/templates/chat/.react-router/types/+server-build.d.ts +18 -0
  66. package/dist/templates/chat/.react-router/types/app/+types/root.ts +68 -0
  67. package/dist/templates/chat/AGENTS.md +62 -0
  68. package/dist/templates/chat/CHANGELOG.md +10 -0
  69. package/dist/templates/chat/DEVELOPING.md +185 -0
  70. package/dist/templates/chat/README.md +32 -0
  71. package/dist/templates/chat/_gitignore +43 -0
  72. package/dist/templates/chat/actions/hello.ts +13 -0
  73. package/dist/templates/chat/actions/navigate.ts +41 -0
  74. package/dist/templates/chat/actions/run.ts +2 -0
  75. package/dist/templates/chat/actions/view-screen.ts +31 -0
  76. package/dist/templates/chat/app/components/layout/Header.tsx +59 -0
  77. package/dist/templates/chat/app/components/layout/Layout.tsx +184 -0
  78. package/dist/templates/chat/app/components/layout/Sidebar.tsx +580 -0
  79. package/dist/templates/chat/app/components/ui/button.tsx +1 -0
  80. package/dist/templates/chat/app/components/ui/card.tsx +1 -0
  81. package/dist/templates/chat/app/components/ui/dropdown-menu.tsx +1 -0
  82. package/dist/templates/chat/app/components/ui/input.tsx +1 -0
  83. package/dist/templates/chat/app/components/ui/label.tsx +1 -0
  84. package/dist/templates/chat/app/components/ui/sheet.tsx +1 -0
  85. package/dist/templates/chat/app/components/ui/toolkit-provider.tsx +12 -0
  86. package/dist/templates/chat/app/components/ui/tooltip.tsx +1 -0
  87. package/dist/templates/chat/app/entry.client.tsx +19 -0
  88. package/dist/templates/chat/app/entry.server.tsx +10 -0
  89. package/dist/templates/chat/app/global.css +93 -0
  90. package/dist/templates/chat/app/hooks/use-navigation-state.ts +111 -0
  91. package/dist/templates/chat/app/i18n/ar-SA.ts +68 -0
  92. package/dist/templates/chat/app/i18n/de-DE.ts +71 -0
  93. package/dist/templates/chat/app/i18n/en-US.ts +70 -0
  94. package/dist/templates/chat/app/i18n/es-ES.ts +71 -0
  95. package/dist/templates/chat/app/i18n/fr-FR.ts +71 -0
  96. package/dist/templates/chat/app/i18n/hi-IN.ts +69 -0
  97. package/dist/templates/chat/app/i18n/index.ts +34 -0
  98. package/dist/templates/chat/app/i18n/ja-JP.ts +70 -0
  99. package/dist/templates/chat/app/i18n/ko-KR.ts +70 -0
  100. package/dist/templates/chat/app/i18n/pt-BR.ts +71 -0
  101. package/dist/templates/chat/app/i18n/zh-CN.ts +67 -0
  102. package/dist/templates/chat/app/i18n/zh-TW.ts +67 -0
  103. package/dist/templates/chat/app/i18n-data.ts +556 -0
  104. package/dist/templates/chat/app/lib/agent-page.tsx +40 -0
  105. package/dist/templates/chat/app/lib/app-config.ts +11 -0
  106. package/dist/templates/chat/app/lib/tab-id.ts +1 -0
  107. package/dist/templates/chat/app/lib/utils.ts +1 -0
  108. package/dist/templates/chat/app/root.tsx +172 -0
  109. package/dist/templates/chat/app/routes/_index.tsx +91 -0
  110. package/dist/templates/chat/app/routes/agent.tsx +17 -0
  111. package/dist/templates/chat/app/routes/chat.$threadId.tsx +1 -0
  112. package/dist/templates/chat/app/routes/database.tsx +17 -0
  113. package/dist/templates/chat/app/routes/extensions.$id.$slug.tsx +2 -0
  114. package/dist/templates/chat/app/routes/extensions.$id.tsx +11 -0
  115. package/dist/templates/chat/app/routes/extensions._index.tsx +11 -0
  116. package/dist/templates/chat/app/routes/extensions.tsx +5 -0
  117. package/dist/templates/chat/app/routes/observability.tsx +18 -0
  118. package/dist/templates/chat/app/routes/settings.tsx +88 -0
  119. package/dist/templates/chat/app/routes/team.tsx +11 -0
  120. package/dist/templates/chat/app/routes.ts +4 -0
  121. package/dist/templates/chat/app/vite-env.d.ts +6 -0
  122. package/dist/templates/chat/changelog/2026-06-24-a-new-settings-page-gives-quick-access-to-language-workspace.md +6 -0
  123. package/dist/templates/chat/changelog/2026-06-24-added-a-language-picker-and-localized-app-chrome-for-support.md +6 -0
  124. package/dist/templates/chat/changelog/2026-06-27-traditional-chinese-copy-uses-taiwan-terminology.md +6 -0
  125. package/dist/templates/chat/changelog/2026-06-28-left-sidebar-collapse-motion-and-footer-chrome-are-quieter.md +6 -0
  126. package/dist/templates/chat/changelog/2026-06-29-chat-layouts-adapt-when-the-agent-sidebar-is-open.md +6 -0
  127. package/dist/templates/chat/changelog/2026-07-08-settings-are-cleaner-and-searchable.md +5 -0
  128. package/dist/templates/chat/changelog/2026-07-10-chat-now-makes-ai-connection-setup-clear-without-shifting-th.md +6 -0
  129. package/dist/templates/chat/changelog/2026-07-13-a-full-agent-page-now-brings-context-files-connections-jobs-.md +6 -0
  130. package/dist/templates/chat/changelog/2026-07-14-chat-opens-reliably-on-hosted-deployments-instead-of-failing.md +6 -0
  131. package/dist/templates/chat/changelog/2026-07-14-fixed-chat-template-startup-with-older-core-versions.md +6 -0
  132. package/dist/templates/chat/changelog/2026-07-15-chat-navigation-focuses-on-chat-and-agent.md +6 -0
  133. package/dist/templates/chat/components.json +20 -0
  134. package/dist/templates/chat/data/.gitkeep +0 -0
  135. package/dist/templates/chat/data/sync-config.json +1 -0
  136. package/dist/templates/chat/learnings.defaults.md +5 -0
  137. package/dist/templates/chat/netlify.toml +10 -0
  138. package/dist/templates/chat/package.json +92 -0
  139. package/dist/templates/chat/public/agent-native-icon-dark.svg +10 -0
  140. package/dist/templates/chat/public/agent-native-icon-light.svg +10 -0
  141. package/dist/templates/chat/public/agent-native-logo-dark.svg +21 -0
  142. package/dist/templates/chat/public/agent-native-logo-light.svg +21 -0
  143. package/dist/templates/chat/public/favicon.svg +1 -0
  144. package/dist/templates/chat/public/icon-180.svg +1 -0
  145. package/dist/templates/chat/public/icon-192.svg +1 -0
  146. package/dist/templates/chat/public/icon-512.svg +1 -0
  147. package/dist/templates/chat/public/manifest.json +21 -0
  148. package/dist/templates/chat/react-router.config.ts +7 -0
  149. package/dist/templates/chat/server/middleware/auth.ts +15 -0
  150. package/dist/templates/chat/server/plugins/agent-chat.ts +21 -0
  151. package/dist/templates/chat/server/plugins/auth.ts +17 -0
  152. package/dist/templates/chat/server/routes/[...page].get.ts +5 -0
  153. package/dist/templates/chat/ssr-entry.ts +15 -0
  154. package/dist/templates/chat/tsconfig.json +21 -0
  155. package/dist/templates/chat/vite.config.ts +19 -0
  156. package/package.json +2 -1
  157. package/src/templates/chat/.agents/skills/actions/SKILL.md +492 -0
  158. package/src/templates/chat/.agents/skills/adding-a-feature/SKILL.md +189 -0
  159. package/src/templates/chat/.agents/skills/agent-native-docs/SKILL.md +76 -0
  160. package/src/templates/chat/.agents/skills/capture-learnings/SKILL.md +89 -0
  161. package/src/templates/chat/.agents/skills/create-skill/SKILL.md +221 -0
  162. package/src/templates/chat/.agents/skills/delegate-to-agent/SKILL.md +263 -0
  163. package/src/templates/chat/.agents/skills/frontend-design/SKILL.md +112 -0
  164. package/src/templates/chat/.agents/skills/real-time-sync/SKILL.md +232 -0
  165. package/src/templates/chat/.agents/skills/security/SKILL.md +280 -0
  166. package/src/templates/chat/.agents/skills/self-modifying-code/SKILL.md +106 -0
  167. package/src/templates/chat/.agents/skills/shadcn-ui/SKILL.md +95 -0
  168. package/src/templates/chat/.agents/skills/upgrade-agent-native/SKILL.md +87 -0
  169. package/src/templates/chat/.env.example +5 -0
  170. package/src/templates/chat/.ignore +0 -0
  171. package/src/templates/chat/.oxfmtrc.json +8 -0
  172. package/src/templates/chat/.react-router/types/+server-build.d.ts +18 -0
  173. package/src/templates/chat/.react-router/types/app/+types/root.ts +68 -0
  174. package/src/templates/chat/AGENTS.md +62 -0
  175. package/src/templates/chat/CHANGELOG.md +10 -0
  176. package/src/templates/chat/DEVELOPING.md +185 -0
  177. package/src/templates/chat/README.md +32 -0
  178. package/src/templates/chat/_gitignore +43 -0
  179. package/src/templates/chat/actions/hello.spec.ts +23 -0
  180. package/src/templates/chat/actions/hello.ts +13 -0
  181. package/src/templates/chat/actions/navigate.spec.ts +68 -0
  182. package/src/templates/chat/actions/navigate.ts +41 -0
  183. package/src/templates/chat/actions/run.ts +2 -0
  184. package/src/templates/chat/actions/view-screen.spec.ts +40 -0
  185. package/src/templates/chat/actions/view-screen.ts +31 -0
  186. package/src/templates/chat/app/components/layout/Header.tsx +59 -0
  187. package/src/templates/chat/app/components/layout/Layout.tsx +184 -0
  188. package/src/templates/chat/app/components/layout/Sidebar.tsx +580 -0
  189. package/src/templates/chat/app/components/ui/button.tsx +1 -0
  190. package/src/templates/chat/app/components/ui/card.tsx +1 -0
  191. package/src/templates/chat/app/components/ui/dropdown-menu.tsx +1 -0
  192. package/src/templates/chat/app/components/ui/input.tsx +1 -0
  193. package/src/templates/chat/app/components/ui/label.tsx +1 -0
  194. package/src/templates/chat/app/components/ui/sheet.tsx +1 -0
  195. package/src/templates/chat/app/components/ui/toolkit-provider.tsx +12 -0
  196. package/src/templates/chat/app/components/ui/tooltip.tsx +1 -0
  197. package/src/templates/chat/app/entry.client.tsx +19 -0
  198. package/src/templates/chat/app/entry.server.tsx +10 -0
  199. package/src/templates/chat/app/global.css +93 -0
  200. package/src/templates/chat/app/hooks/use-navigation-state.ts +111 -0
  201. package/src/templates/chat/app/i18n/ar-SA.ts +68 -0
  202. package/src/templates/chat/app/i18n/de-DE.ts +71 -0
  203. package/src/templates/chat/app/i18n/en-US.ts +70 -0
  204. package/src/templates/chat/app/i18n/es-ES.ts +71 -0
  205. package/src/templates/chat/app/i18n/fr-FR.ts +71 -0
  206. package/src/templates/chat/app/i18n/hi-IN.ts +69 -0
  207. package/src/templates/chat/app/i18n/index.ts +34 -0
  208. package/src/templates/chat/app/i18n/ja-JP.ts +70 -0
  209. package/src/templates/chat/app/i18n/ko-KR.ts +70 -0
  210. package/src/templates/chat/app/i18n/pt-BR.ts +71 -0
  211. package/src/templates/chat/app/i18n/zh-CN.ts +67 -0
  212. package/src/templates/chat/app/i18n/zh-TW.ts +67 -0
  213. package/src/templates/chat/app/i18n-data.ts +556 -0
  214. package/src/templates/chat/app/lib/agent-page.spec.tsx +52 -0
  215. package/src/templates/chat/app/lib/agent-page.tsx +40 -0
  216. package/src/templates/chat/app/lib/app-config.ts +11 -0
  217. package/src/templates/chat/app/lib/tab-id.ts +1 -0
  218. package/src/templates/chat/app/lib/utils.ts +1 -0
  219. package/src/templates/chat/app/root.tsx +172 -0
  220. package/src/templates/chat/app/routes/_index.tsx +91 -0
  221. package/src/templates/chat/app/routes/agent.tsx +17 -0
  222. package/src/templates/chat/app/routes/chat.$threadId.tsx +1 -0
  223. package/src/templates/chat/app/routes/database.tsx +17 -0
  224. package/src/templates/chat/app/routes/extensions.$id.$slug.tsx +2 -0
  225. package/src/templates/chat/app/routes/extensions.$id.tsx +11 -0
  226. package/src/templates/chat/app/routes/extensions._index.tsx +11 -0
  227. package/src/templates/chat/app/routes/extensions.tsx +5 -0
  228. package/src/templates/chat/app/routes/observability.tsx +18 -0
  229. package/src/templates/chat/app/routes/settings.tsx +88 -0
  230. package/src/templates/chat/app/routes/team.tsx +11 -0
  231. package/src/templates/chat/app/routes.ts +4 -0
  232. package/src/templates/chat/app/vite-env.d.ts +6 -0
  233. package/src/templates/chat/changelog/2026-06-24-a-new-settings-page-gives-quick-access-to-language-workspace.md +6 -0
  234. package/src/templates/chat/changelog/2026-06-24-added-a-language-picker-and-localized-app-chrome-for-support.md +6 -0
  235. package/src/templates/chat/changelog/2026-06-27-traditional-chinese-copy-uses-taiwan-terminology.md +6 -0
  236. package/src/templates/chat/changelog/2026-06-28-left-sidebar-collapse-motion-and-footer-chrome-are-quieter.md +6 -0
  237. package/src/templates/chat/changelog/2026-06-29-chat-layouts-adapt-when-the-agent-sidebar-is-open.md +6 -0
  238. package/src/templates/chat/changelog/2026-07-08-settings-are-cleaner-and-searchable.md +5 -0
  239. package/src/templates/chat/changelog/2026-07-10-chat-now-makes-ai-connection-setup-clear-without-shifting-th.md +6 -0
  240. package/src/templates/chat/changelog/2026-07-13-a-full-agent-page-now-brings-context-files-connections-jobs-.md +6 -0
  241. package/src/templates/chat/changelog/2026-07-14-chat-opens-reliably-on-hosted-deployments-instead-of-failing.md +6 -0
  242. package/src/templates/chat/changelog/2026-07-14-fixed-chat-template-startup-with-older-core-versions.md +6 -0
  243. package/src/templates/chat/changelog/2026-07-15-chat-navigation-focuses-on-chat-and-agent.md +6 -0
  244. package/src/templates/chat/components.json +20 -0
  245. package/src/templates/chat/data/.gitkeep +0 -0
  246. package/src/templates/chat/data/sync-config.json +1 -0
  247. package/src/templates/chat/learnings.defaults.md +5 -0
  248. package/src/templates/chat/netlify.toml +10 -0
  249. package/src/templates/chat/package.json +92 -0
  250. package/src/templates/chat/public/agent-native-icon-dark.svg +10 -0
  251. package/src/templates/chat/public/agent-native-icon-light.svg +10 -0
  252. package/src/templates/chat/public/agent-native-logo-dark.svg +21 -0
  253. package/src/templates/chat/public/agent-native-logo-light.svg +21 -0
  254. package/src/templates/chat/public/favicon.svg +1 -0
  255. package/src/templates/chat/public/icon-180.svg +1 -0
  256. package/src/templates/chat/public/icon-192.svg +1 -0
  257. package/src/templates/chat/public/icon-512.svg +1 -0
  258. package/src/templates/chat/public/manifest.json +21 -0
  259. package/src/templates/chat/react-router.config.ts +7 -0
  260. package/src/templates/chat/server/middleware/auth.ts +15 -0
  261. package/src/templates/chat/server/plugins/agent-chat.ts +21 -0
  262. package/src/templates/chat/server/plugins/auth.ts +17 -0
  263. package/src/templates/chat/server/routes/[...page].get.ts +5 -0
  264. package/src/templates/chat/ssr-entry.ts +15 -0
  265. package/src/templates/chat/tsconfig.json +21 -0
  266. package/src/templates/chat/vite.config.ts +19 -0
@@ -0,0 +1,580 @@
1
+ import {
2
+ appPath,
3
+ FeedbackButton,
4
+ navigateWithAgentChatViewTransition,
5
+ useChatThreads,
6
+ useT,
7
+ type ChatThreadSummary,
8
+ } from "@agent-native/core/client";
9
+ import { ExtensionsSidebarSection } from "@agent-native/core/client/extensions";
10
+ import { OrgSwitcher } from "@agent-native/core/client/org";
11
+ import {
12
+ IconArchive,
13
+ IconBrain,
14
+ IconDots,
15
+ IconEdit,
16
+ IconLayoutSidebarLeftCollapse,
17
+ IconLayoutSidebarLeftExpand,
18
+ IconMessageCircle,
19
+ IconPin,
20
+ IconPlus,
21
+ IconSettings,
22
+ } from "@tabler/icons-react";
23
+ import { useEffect, useMemo, useRef, useState, type FormEvent } from "react";
24
+ import { Link, useLocation, useNavigate } from "react-router";
25
+ import { toast } from "sonner";
26
+
27
+ import {
28
+ DropdownMenu,
29
+ DropdownMenuContent,
30
+ DropdownMenuItem,
31
+ DropdownMenuTrigger,
32
+ } from "@/components/ui/dropdown-menu";
33
+ import { Input } from "@/components/ui/input";
34
+ import {
35
+ Tooltip,
36
+ TooltipContent,
37
+ TooltipTrigger,
38
+ } from "@/components/ui/tooltip";
39
+ import { APP_TITLE } from "@/lib/app-config";
40
+ import { cn } from "@/lib/utils";
41
+
42
+ const navItems = [
43
+ {
44
+ icon: IconMessageCircle,
45
+ labelKey: "navigation.chat",
46
+ href: "/",
47
+ view: "chat",
48
+ },
49
+ {
50
+ icon: IconBrain,
51
+ labelKey: "settings.agentTitle",
52
+ href: "/agent",
53
+ view: "agent",
54
+ },
55
+ {
56
+ icon: IconSettings,
57
+ labelKey: "navigation.settings",
58
+ href: "/settings",
59
+ view: "settings",
60
+ },
61
+ ];
62
+
63
+ const CHAT_STORAGE_KEY = "chat";
64
+ const CHAT_ACTIVE_THREAD_KEY = `agent-chat-active-thread:${CHAT_STORAGE_KEY}`;
65
+
66
+ interface SidebarProps {
67
+ collapsed?: boolean;
68
+ collapsible?: boolean;
69
+ onCollapsedChange?: (collapsed: boolean) => void;
70
+ }
71
+
72
+ function formatThreadAge(updatedAt: number) {
73
+ const diffMs = Math.max(0, Date.now() - updatedAt);
74
+ const minutes = Math.floor(diffMs / 60_000);
75
+ if (minutes < 1) return "now";
76
+ if (minutes < 60) return `${minutes}m`;
77
+ const hours = Math.floor(minutes / 60);
78
+ if (hours < 24) return `${hours}h`;
79
+ const days = Math.floor(hours / 24);
80
+ if (days < 7) return `${days}d`;
81
+ return new Date(updatedAt).toLocaleDateString([], {
82
+ month: "short",
83
+ day: "numeric",
84
+ });
85
+ }
86
+
87
+ function threadTitle(thread: ChatThreadSummary) {
88
+ return thread.title || thread.preview || "Untitled chat";
89
+ }
90
+
91
+ function threadUpdatedAt(thread: ChatThreadSummary) {
92
+ return Number.isFinite(thread.updatedAt)
93
+ ? thread.updatedAt
94
+ : Number.isFinite(thread.createdAt)
95
+ ? thread.createdAt
96
+ : 0;
97
+ }
98
+
99
+ function compareThreads(a: ChatThreadSummary, b: ChatThreadSummary) {
100
+ const aPinned = a.pinnedAt ?? 0;
101
+ const bPinned = b.pinnedAt ?? 0;
102
+ if (aPinned || bPinned) return bPinned - aPinned;
103
+ return threadUpdatedAt(b) - threadUpdatedAt(a);
104
+ }
105
+
106
+ function persistedActiveThreadId() {
107
+ try {
108
+ return localStorage.getItem(CHAT_ACTIVE_THREAD_KEY);
109
+ } catch {
110
+ return null;
111
+ }
112
+ }
113
+
114
+ function threadIdFromPath(pathname: string) {
115
+ const match = pathname.match(/^\/chat\/([^/]+)/);
116
+ if (!match) return null;
117
+ try {
118
+ const value = decodeURIComponent(match[1]).trim();
119
+ return value || null;
120
+ } catch {
121
+ return null;
122
+ }
123
+ }
124
+
125
+ function chatThreadPath(threadId: string) {
126
+ return `/chat/${encodeURIComponent(threadId)}`;
127
+ }
128
+
129
+ function ChatThreadsSection() {
130
+ const navigate = useNavigate();
131
+ const location = useLocation();
132
+ const t = useT();
133
+ const {
134
+ threads,
135
+ activeThreadId,
136
+ createThread,
137
+ switchThread,
138
+ pinThread,
139
+ archiveThread,
140
+ renameThread,
141
+ refreshThreads,
142
+ } = useChatThreads(undefined, CHAT_STORAGE_KEY, undefined, {
143
+ autoCreate: false,
144
+ restoreActiveThread: false,
145
+ });
146
+ const [renamingThreadId, setRenamingThreadId] = useState<string | null>(null);
147
+ const [renameDraft, setRenameDraft] = useState("");
148
+ const renameInputRef = useRef<HTMLInputElement | null>(null);
149
+ const committingRenameRef = useRef(false);
150
+
151
+ const visibleThreads = useMemo(
152
+ () =>
153
+ threads
154
+ .filter((thread) => thread.messageCount > 0 && !thread.archivedAt)
155
+ .sort(compareThreads)
156
+ .slice(0, 12),
157
+ [threads],
158
+ );
159
+
160
+ useEffect(() => {
161
+ const refresh = () => refreshThreads();
162
+ const handleRunning = (event: Event) => {
163
+ const detail = (event as CustomEvent).detail as
164
+ | { isRunning?: unknown }
165
+ | undefined;
166
+ if (typeof detail?.isRunning === "boolean") refreshThreads();
167
+ };
168
+
169
+ window.addEventListener("agent-chat:threads-updated", refresh);
170
+ window.addEventListener("agentNative.chatRunning", handleRunning);
171
+ window.addEventListener("focus", refresh);
172
+ return () => {
173
+ window.removeEventListener("agent-chat:threads-updated", refresh);
174
+ window.removeEventListener("agentNative.chatRunning", handleRunning);
175
+ window.removeEventListener("focus", refresh);
176
+ };
177
+ }, [refreshThreads]);
178
+
179
+ useEffect(() => {
180
+ if (!renamingThreadId) return;
181
+ requestAnimationFrame(() => {
182
+ renameInputRef.current?.focus();
183
+ renameInputRef.current?.select();
184
+ });
185
+ }, [renamingThreadId]);
186
+
187
+ function openThread(threadId: string, options?: { isNew?: boolean }) {
188
+ switchThread(threadId);
189
+ navigateWithAgentChatViewTransition(
190
+ navigate,
191
+ options?.isNew ? "/" : chatThreadPath(threadId),
192
+ );
193
+ window.requestAnimationFrame(() => {
194
+ window.dispatchEvent(
195
+ new CustomEvent("agent-chat:open-thread", {
196
+ detail: { threadId, newThread: options?.isNew === true },
197
+ }),
198
+ );
199
+ });
200
+ }
201
+
202
+ async function handleNewChat() {
203
+ const threadId = await createThread();
204
+ if (threadId) openThread(threadId, { isNew: true });
205
+ }
206
+
207
+ async function handleArchiveThread(threadId: string) {
208
+ const wasActive =
209
+ threadId === activeThreadId || threadId === persistedActiveThreadId();
210
+ const archived = await archiveThread(threadId);
211
+ if (!archived) {
212
+ toast.error(t("chat.archiveFailed"));
213
+ return;
214
+ }
215
+ if (wasActive) {
216
+ await handleNewChat();
217
+ }
218
+ }
219
+
220
+ function startRenameThread(thread: ChatThreadSummary) {
221
+ committingRenameRef.current = false;
222
+ setRenameDraft(threadTitle(thread));
223
+ setRenamingThreadId(thread.id);
224
+ }
225
+
226
+ function cancelRenameThread() {
227
+ committingRenameRef.current = true;
228
+ setRenamingThreadId(null);
229
+ setRenameDraft("");
230
+ }
231
+
232
+ async function commitRenameThread() {
233
+ if (committingRenameRef.current) return;
234
+ const threadId = renamingThreadId;
235
+ const title = renameDraft.trim();
236
+ if (!threadId) return;
237
+ committingRenameRef.current = true;
238
+ setRenamingThreadId(null);
239
+ setRenameDraft("");
240
+ if (title) {
241
+ const renamed = await renameThread(threadId, title);
242
+ if (!renamed) toast.error(t("chat.renameFailed"));
243
+ }
244
+ committingRenameRef.current = false;
245
+ }
246
+
247
+ function handleRenameSubmit(event: FormEvent<HTMLFormElement>) {
248
+ event.preventDefault();
249
+ void commitRenameThread();
250
+ }
251
+
252
+ return (
253
+ <div className="mt-2 border-s border-sidebar-border/70 ps-3">
254
+ <div className="mb-1 flex h-7 items-center gap-2 pe-1">
255
+ <div className="min-w-0 flex-1 text-xs font-medium text-sidebar-foreground/70">
256
+ {t("chat.chats")}
257
+ </div>
258
+ <Tooltip>
259
+ <TooltipTrigger asChild>
260
+ <button
261
+ type="button"
262
+ onClick={handleNewChat}
263
+ className="flex size-6 shrink-0 items-center justify-center rounded-md text-sidebar-foreground/65 transition-colors hover:bg-sidebar-accent hover:text-sidebar-accent-foreground"
264
+ aria-label={t("chat.newChat")}
265
+ >
266
+ <IconPlus className="size-3.5" />
267
+ </button>
268
+ </TooltipTrigger>
269
+ <TooltipContent>{t("chat.newChat")}</TooltipContent>
270
+ </Tooltip>
271
+ </div>
272
+ <div className="grid gap-0.5">
273
+ {visibleThreads.map((thread) => {
274
+ const isActive =
275
+ thread.id ===
276
+ (threadIdFromPath(location.pathname) ??
277
+ (location.pathname === "/" ? null : activeThreadId));
278
+ const isRenaming = thread.id === renamingThreadId;
279
+ return (
280
+ <div
281
+ key={thread.id}
282
+ className={cn(
283
+ "group flex h-8 min-w-0 items-center rounded-md text-sm transition-colors",
284
+ isActive
285
+ ? "bg-sidebar-accent text-sidebar-accent-foreground"
286
+ : "text-sidebar-foreground/80 hover:bg-sidebar-accent/65 hover:text-sidebar-accent-foreground",
287
+ )}
288
+ >
289
+ {isRenaming ? (
290
+ <form
291
+ onSubmit={handleRenameSubmit}
292
+ className="flex h-full min-w-0 flex-1 items-center px-1.5"
293
+ >
294
+ <Input
295
+ ref={renameInputRef}
296
+ value={renameDraft}
297
+ onChange={(event) => setRenameDraft(event.target.value)}
298
+ onBlur={() => void commitRenameThread()}
299
+ onKeyDown={(event) => {
300
+ if (event.key === "Escape") {
301
+ event.preventDefault();
302
+ cancelRenameThread();
303
+ }
304
+ }}
305
+ maxLength={160}
306
+ aria-label={t("chat.renameThread", {
307
+ title: threadTitle(thread),
308
+ })}
309
+ className="h-6 min-w-0 rounded-sm border-sidebar-border bg-background px-1.5 text-xs"
310
+ />
311
+ </form>
312
+ ) : (
313
+ <>
314
+ <button
315
+ type="button"
316
+ onClick={() => openThread(thread.id)}
317
+ className="flex h-full min-w-0 flex-1 items-center px-2 text-start outline-none focus-visible:ring-2 focus-visible:ring-ring"
318
+ >
319
+ <span className="min-w-0 flex-1 truncate">
320
+ {threadTitle(thread)}
321
+ </span>
322
+ </button>
323
+ <div className="relative flex size-7 shrink-0 items-center justify-end pe-1">
324
+ <span className="text-[11px] text-sidebar-foreground/50 transition-opacity group-hover:opacity-0 group-focus-within:opacity-0">
325
+ {isActive ? "" : formatThreadAge(threadUpdatedAt(thread))}
326
+ </span>
327
+ <DropdownMenu>
328
+ <DropdownMenuTrigger asChild>
329
+ <button
330
+ type="button"
331
+ aria-label={t("chat.optionsFor", {
332
+ title: threadTitle(thread),
333
+ })}
334
+ className="absolute end-1 flex size-6 items-center justify-center rounded-md text-sidebar-foreground/65 opacity-0 transition-opacity hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:opacity-100 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring group-hover:opacity-100 group-focus-within:opacity-100 data-[state=open]:opacity-100"
335
+ >
336
+ <IconDots className="size-4" />
337
+ </button>
338
+ </DropdownMenuTrigger>
339
+ <DropdownMenuContent
340
+ align="end"
341
+ side="right"
342
+ sideOffset={6}
343
+ >
344
+ <DropdownMenuItem
345
+ onSelect={() => startRenameThread(thread)}
346
+ >
347
+ <IconEdit className="size-4" />
348
+ {t("chat.renameChat")}
349
+ </DropdownMenuItem>
350
+ <DropdownMenuItem
351
+ onSelect={() =>
352
+ void pinThread(thread.id, !thread.pinnedAt)
353
+ }
354
+ >
355
+ <IconPin className="size-4" />
356
+ {thread.pinnedAt
357
+ ? t("chat.unpinChat")
358
+ : t("chat.pinChat")}
359
+ </DropdownMenuItem>
360
+ <DropdownMenuItem
361
+ className="text-destructive focus:bg-destructive focus:text-destructive-foreground"
362
+ onSelect={() => void handleArchiveThread(thread.id)}
363
+ >
364
+ <IconArchive className="size-4" />
365
+ {t("chat.archiveChat")}
366
+ </DropdownMenuItem>
367
+ </DropdownMenuContent>
368
+ </DropdownMenu>
369
+ </div>
370
+ </>
371
+ )}
372
+ </div>
373
+ );
374
+ })}
375
+ </div>
376
+ </div>
377
+ );
378
+ }
379
+
380
+ export function Sidebar({
381
+ collapsed = false,
382
+ collapsible = true,
383
+ onCollapsedChange,
384
+ }: SidebarProps) {
385
+ const location = useLocation();
386
+ const navigate = useNavigate();
387
+ const t = useT();
388
+ const isChatRoute =
389
+ location.pathname === "/" || location.pathname.startsWith("/chat/");
390
+ const ToggleIcon = collapsed
391
+ ? IconLayoutSidebarLeftExpand
392
+ : IconLayoutSidebarLeftCollapse;
393
+ const navClass = ({ isActive }: { isActive: boolean }) =>
394
+ cn(
395
+ "flex items-center text-sm transition-colors",
396
+ collapsed
397
+ ? "relative h-10 w-full justify-center rounded-none border-s-2 px-0"
398
+ : "h-9 rounded-md gap-3 px-3",
399
+ isActive
400
+ ? collapsed
401
+ ? "border-s-sidebar-accent-foreground/80 bg-sidebar-accent text-sidebar-accent-foreground"
402
+ : "bg-sidebar-accent text-sidebar-accent-foreground"
403
+ : collapsed
404
+ ? "border-s-transparent text-sidebar-foreground/70 hover:bg-sidebar-accent/55 hover:text-sidebar-accent-foreground"
405
+ : "text-sidebar-foreground hover:bg-sidebar-accent/65 hover:text-sidebar-accent-foreground",
406
+ );
407
+ const collapseButton = collapsible ? (
408
+ <Tooltip>
409
+ <TooltipTrigger asChild>
410
+ <button
411
+ type="button"
412
+ onClick={() => onCollapsedChange?.(!collapsed)}
413
+ className={cn(
414
+ "flex shrink-0 items-center justify-center rounded-md text-sidebar-foreground/65 transition-colors hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
415
+ collapsed ? "size-8" : "size-7",
416
+ )}
417
+ aria-label={
418
+ collapsed
419
+ ? t("navigation.expandSidebar")
420
+ : t("navigation.collapseSidebar")
421
+ }
422
+ >
423
+ <ToggleIcon className="size-4" />
424
+ </button>
425
+ </TooltipTrigger>
426
+ <TooltipContent side="right">
427
+ {collapsed
428
+ ? t("navigation.expandSidebar")
429
+ : t("navigation.collapseSidebar")}
430
+ </TooltipContent>
431
+ </Tooltip>
432
+ ) : null;
433
+
434
+ return (
435
+ <aside
436
+ data-collapsed={collapsed ? "true" : "false"}
437
+ className={cn(
438
+ "flex h-full min-w-0 shrink-0 flex-col overflow-hidden border-e border-sidebar-border bg-sidebar text-sidebar-foreground transition-[width] duration-200 ease-out",
439
+ collapsed ? "w-12" : "w-60",
440
+ )}
441
+ >
442
+ <div
443
+ className={cn(
444
+ "flex shrink-0 items-center border-b border-sidebar-border",
445
+ collapsed ? "h-12 justify-center px-0" : "h-14 px-3",
446
+ )}
447
+ >
448
+ <Link
449
+ to="/"
450
+ className={cn(
451
+ "flex min-w-0 items-center rounded outline-none focus-visible:ring-2 focus-visible:ring-ring",
452
+ collapsed ? "size-7 justify-center" : "flex-1 gap-3",
453
+ )}
454
+ aria-label={collapsed ? APP_TITLE : undefined}
455
+ >
456
+ <img
457
+ src={appPath("/agent-native-icon-light.svg")}
458
+ alt=""
459
+ aria-hidden="true"
460
+ className="block h-4 w-auto shrink-0 dark:hidden"
461
+ />
462
+ <img
463
+ src={appPath("/agent-native-icon-dark.svg")}
464
+ alt=""
465
+ aria-hidden="true"
466
+ className="hidden h-4 w-auto shrink-0 dark:block"
467
+ />
468
+ <div className={cn("min-w-0", collapsed && "sr-only")}>
469
+ <p className="truncate text-sm font-semibold text-sidebar-accent-foreground">
470
+ {APP_TITLE}
471
+ </p>
472
+ </div>
473
+ </Link>
474
+ </div>
475
+
476
+ <nav
477
+ className={cn(
478
+ "flex-1 overflow-y-auto",
479
+ collapsed ? "px-0 py-2" : "px-2 py-3",
480
+ )}
481
+ >
482
+ <div className={cn("grid", collapsed ? "gap-0" : "gap-1")}>
483
+ {navItems.map((item) => {
484
+ const Icon = item.icon;
485
+ const isActive =
486
+ item.href === "/"
487
+ ? isChatRoute
488
+ : location.pathname.startsWith(item.href);
489
+ const link = (
490
+ <Link
491
+ to={item.href}
492
+ onClick={(event) => {
493
+ if (
494
+ item.href === "/" &&
495
+ !isChatRoute &&
496
+ !event.metaKey &&
497
+ !event.ctrlKey &&
498
+ !event.shiftKey &&
499
+ !event.altKey
500
+ ) {
501
+ event.preventDefault();
502
+ navigateWithAgentChatViewTransition(navigate, "/");
503
+ }
504
+ }}
505
+ className={navClass({ isActive })}
506
+ aria-current={isActive ? "page" : undefined}
507
+ aria-label={collapsed ? t(item.labelKey) : undefined}
508
+ >
509
+ <Icon className="size-4 shrink-0" />
510
+ <span className={collapsed ? "sr-only" : "truncate"}>
511
+ {t(item.labelKey)}
512
+ </span>
513
+ </Link>
514
+ );
515
+ return (
516
+ <div key={item.href}>
517
+ {collapsed ? (
518
+ <Tooltip>
519
+ <TooltipTrigger asChild>{link}</TooltipTrigger>
520
+ <TooltipContent side="right">
521
+ {t(item.labelKey)}
522
+ </TooltipContent>
523
+ </Tooltip>
524
+ ) : (
525
+ link
526
+ )}
527
+ {!collapsed && item.view === "chat" && isChatRoute ? (
528
+ <ChatThreadsSection />
529
+ ) : null}
530
+ </div>
531
+ );
532
+ })}
533
+ </div>
534
+ </nav>
535
+
536
+ <div className={cn("mt-auto shrink-0", collapsed && "py-2")}>
537
+ {!collapsed ? (
538
+ <div className="px-2 py-1">
539
+ <ExtensionsSidebarSection />
540
+ </div>
541
+ ) : null}
542
+
543
+ <div className={cn(collapsed ? "px-1 py-1" : "px-3 py-2")}>
544
+ <OrgSwitcher
545
+ reserveSpace
546
+ className={
547
+ collapsed
548
+ ? "h-8 justify-center px-0 [&>span]:sr-only [&>svg:last-child]:hidden"
549
+ : undefined
550
+ }
551
+ />
552
+ </div>
553
+
554
+ <div
555
+ className={cn(
556
+ collapsed ? "flex justify-center px-1 py-1" : "px-3 py-2",
557
+ )}
558
+ >
559
+ <FeedbackButton
560
+ variant={collapsed ? "icon" : "sidebar"}
561
+ side="right"
562
+ align={collapsed ? "center" : "end"}
563
+ />
564
+ </div>
565
+
566
+ {collapseButton ? (
567
+ <div
568
+ className={cn(
569
+ collapsed
570
+ ? "flex justify-center px-1 py-1"
571
+ : "flex justify-end px-3 py-2",
572
+ )}
573
+ >
574
+ {collapseButton}
575
+ </div>
576
+ ) : null}
577
+ </div>
578
+ </aside>
579
+ );
580
+ }
@@ -0,0 +1 @@
1
+ export * from "@agent-native/toolkit/ui/button";
@@ -0,0 +1 @@
1
+ export * from "@agent-native/toolkit/ui/card";
@@ -0,0 +1 @@
1
+ export * from "@agent-native/toolkit/ui/dropdown-menu";
@@ -0,0 +1 @@
1
+ export * from "@agent-native/toolkit/ui/input";
@@ -0,0 +1 @@
1
+ export * from "@agent-native/toolkit/ui/label";
@@ -0,0 +1 @@
1
+ export * from "@agent-native/toolkit/ui/sheet";
@@ -0,0 +1,12 @@
1
+ import { ToolkitProvider, type ToolkitComponents } from "@agent-native/toolkit";
2
+ import type { ReactNode } from "react";
3
+
4
+ import { Button } from "@/components/ui/button";
5
+
6
+ const components: ToolkitComponents = {
7
+ Button: Button as ToolkitComponents["Button"],
8
+ };
9
+
10
+ export function AppToolkitProvider({ children }: { children: ReactNode }) {
11
+ return <ToolkitProvider components={components}>{children}</ToolkitProvider>;
12
+ }
@@ -0,0 +1 @@
1
+ export * from "@agent-native/toolkit/ui/tooltip";
@@ -0,0 +1,19 @@
1
+ // Import only the tiny URL-helper module, not the full client barrel.
2
+ // This keeps the ~650-700 KB gzip chat stack off the static import closure
3
+ // of the client entry point so it can't block the first page parse.
4
+ import { appBasePath } from "@agent-native/core/client/api-path";
5
+ import { hydrateRoot } from "react-dom/client";
6
+ import { HydratedRouter } from "react-router/dom";
7
+
8
+ const basePath = appBasePath();
9
+ const pathname = window.location.pathname;
10
+ const routerBasePath =
11
+ basePath && (pathname === basePath || pathname.startsWith(`${basePath}/`))
12
+ ? basePath
13
+ : "";
14
+ const context = (
15
+ window as Window & { __reactRouterContext?: { basename?: string } }
16
+ ).__reactRouterContext;
17
+ if (context) context.basename = routerBasePath;
18
+
19
+ hydrateRoot(document, <HydratedRouter />);
@@ -0,0 +1,10 @@
1
+ import {
2
+ createDocumentRequestHandler,
3
+ streamTimeout,
4
+ } from "@agent-native/core/server/entry-server";
5
+ import { ServerRouter } from "react-router";
6
+
7
+ const handleDocumentRequest = createDocumentRequestHandler(ServerRouter);
8
+
9
+ export { streamTimeout };
10
+ export default handleDocumentRequest;