@agent-native/core 0.86.0 → 0.86.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 (183) hide show
  1. package/corpus/README.md +2 -2
  2. package/corpus/core/CHANGELOG.md +9 -0
  3. package/corpus/core/package.json +2 -1
  4. package/corpus/core/src/client/AgentPanel.tsx +10 -0
  5. package/corpus/core/src/client/AssistantChat.tsx +91 -27
  6. package/corpus/core/src/client/ErrorBoundary.tsx +67 -12
  7. package/corpus/core/src/client/ErrorReportActions.tsx +69 -0
  8. package/corpus/core/src/client/FeedbackButton.tsx +8 -4
  9. package/corpus/core/src/client/db-admin/DbAdminPage.tsx +41 -13
  10. package/corpus/core/src/client/db-admin/SqlEditor.tsx +10 -3
  11. package/corpus/core/src/client/db-admin/TableEditor.tsx +12 -5
  12. package/corpus/core/src/client/db-admin/useAgentSync.ts +10 -3
  13. package/corpus/core/src/client/db-admin/useDbAdmin.ts +65 -16
  14. package/corpus/core/src/client/error-reporting.ts +100 -0
  15. package/corpus/core/src/client/index.ts +11 -0
  16. package/corpus/core/src/client/org/RequireActiveOrg.tsx +12 -0
  17. package/corpus/core/src/client/visual-style-controls.tsx +615 -2
  18. package/corpus/core/src/db-admin/index.ts +26 -0
  19. package/corpus/core/src/db-admin/operations.ts +87 -49
  20. package/corpus/core/src/localization/default-messages.ts +4 -0
  21. package/corpus/core/src/server/builder-design-systems.ts +328 -9
  22. package/corpus/core/src/server/index.ts +15 -0
  23. package/corpus/core/src/templates/default/app/root.tsx +47 -0
  24. package/corpus/templates/analytics/AGENTS.md +6 -4
  25. package/corpus/templates/analytics/actions/delete-db-admin-connection.ts +19 -0
  26. package/corpus/templates/analytics/actions/list-db-admin-connections.ts +18 -0
  27. package/corpus/templates/analytics/actions/navigate.ts +17 -3
  28. package/corpus/templates/analytics/actions/save-db-admin-connection.ts +33 -0
  29. package/corpus/templates/analytics/actions/view-screen.ts +11 -3
  30. package/corpus/templates/analytics/app/hooks/use-navigation-state.ts +11 -2
  31. package/corpus/templates/analytics/app/i18n/zh-TW.ts +23 -2
  32. package/corpus/templates/analytics/app/i18n-data.ts +230 -20
  33. package/corpus/templates/analytics/app/pages/Agents.tsx +398 -4
  34. package/corpus/templates/analytics/changelog/2026-07-03-connect-other-app-databases-from-analytics.md +6 -0
  35. package/corpus/templates/analytics/server/db/schema.ts +27 -0
  36. package/corpus/templates/analytics/server/lib/db-admin-connections.ts +458 -0
  37. package/corpus/templates/analytics/server/plugins/db.ts +35 -0
  38. package/corpus/templates/analytics/server/routes/[base]/_agent-native/analytics-db-admin/[...path].ts +1 -0
  39. package/corpus/templates/analytics/server/routes/_agent-native/analytics-db-admin/[...path].ts +160 -0
  40. package/corpus/templates/content/app/root.tsx +9 -0
  41. package/corpus/templates/content/changelog/2026-07-03-error-screens-now-include-a-feedback-button-with-debug-info.md +6 -0
  42. package/corpus/templates/design/AGENTS.md +13 -7
  43. package/corpus/templates/design/actions/get-design-system.ts +21 -0
  44. package/corpus/templates/design/actions/index-design-system-with-builder.ts +14 -37
  45. package/corpus/templates/design/app/components/design/code-workbench/CodeWorkbench.tsx +3 -3
  46. package/corpus/templates/design/app/components/design/code-workbench/SideBar.tsx +70 -9
  47. package/corpus/templates/design/app/components/design/code-workbench/editor/EditorTabs.tsx +11 -2
  48. package/corpus/templates/design/app/components/design/code-workbench/editor/MonacoHost.tsx +1 -1
  49. package/corpus/templates/design/app/components/design/code-workbench/editor/StatusBar.tsx +1 -1
  50. package/corpus/templates/design/app/components/design/code-workbench/explorer/ExplorerView.tsx +8 -2
  51. package/corpus/templates/design/app/components/design/code-workbench/explorer/FileTree.tsx +7 -3
  52. package/corpus/templates/design/app/components/design/code-workbench/store.tsx +48 -0
  53. package/corpus/templates/design/app/hooks/useDesignHotkeys.ts +6 -0
  54. package/corpus/templates/design/app/i18n/zh-TW.ts +15 -14
  55. package/corpus/templates/design/app/i18n-data.ts +154 -142
  56. package/corpus/templates/design/app/pages/DesignSystemSetup.tsx +32 -14
  57. package/corpus/templates/design/changelog/2026-07-03-design-system-setup-now-indexes-figma-code-and-design-md-sou.md +6 -0
  58. package/corpus/templates/design/changelog/2026-07-03-the-code-editor-is-easier-to-use-with-top-sidebar-tabs-clear.md +6 -0
  59. package/corpus/templates/design/server/lib/builder-design-system-proxy.ts +16 -106
  60. package/corpus/templates/mail/app/root.tsx +60 -1
  61. package/corpus/templates/mail/changelog/2026-07-03-error-screens-now-include-a-feedback-button-with-debug-info.md +6 -0
  62. package/corpus/templates/plan/app/i18n/ar-SA.ts +3 -0
  63. package/corpus/templates/plan/app/i18n/de-DE.ts +4 -0
  64. package/corpus/templates/plan/app/i18n/en-US.ts +4 -0
  65. package/corpus/templates/plan/app/i18n/es-ES.ts +4 -0
  66. package/corpus/templates/plan/app/i18n/fr-FR.ts +4 -0
  67. package/corpus/templates/plan/app/i18n/hi-IN.ts +3 -0
  68. package/corpus/templates/plan/app/i18n/ja-JP.ts +4 -0
  69. package/corpus/templates/plan/app/i18n/ko-KR.ts +4 -0
  70. package/corpus/templates/plan/app/i18n/pt-BR.ts +4 -0
  71. package/corpus/templates/plan/app/i18n/zh-CN.ts +3 -0
  72. package/corpus/templates/plan/app/i18n/zh-TW.ts +3 -0
  73. package/corpus/templates/plan/app/pages/PlansPage.tsx +74 -3
  74. package/corpus/templates/plan/changelog/2026-07-03-error-screens-now-include-a-feedback-button-with-debug-info.md +6 -0
  75. package/corpus/templates/slides/AGENTS.md +7 -3
  76. package/corpus/templates/slides/actions/get-design-system.ts +21 -0
  77. package/corpus/templates/slides/actions/import-file.ts +15 -7
  78. package/corpus/templates/slides/actions/index-design-system-with-builder.ts +14 -37
  79. package/corpus/templates/slides/app/components/design-system/DesignSystemSetup.tsx +32 -8
  80. package/corpus/templates/slides/app/components/editor/EditorSidebar.tsx +2 -2
  81. package/corpus/templates/slides/app/components/editor/EditorToolbar.tsx +2 -4
  82. package/corpus/templates/slides/app/components/editor/SlideEditor.tsx +163 -154
  83. package/corpus/templates/slides/app/components/editor/SlideStyleInspector.tsx +19 -15
  84. package/corpus/templates/slides/app/components/editor/SpeakerNotesPanel.tsx +56 -6
  85. package/corpus/templates/slides/app/components/layout/Layout.tsx +6 -1
  86. package/corpus/templates/slides/app/i18n/ar-SA.ts +8 -7
  87. package/corpus/templates/slides/app/i18n/de-DE.ts +7 -8
  88. package/corpus/templates/slides/app/i18n/en-US.ts +8 -7
  89. package/corpus/templates/slides/app/i18n/es-ES.ts +7 -8
  90. package/corpus/templates/slides/app/i18n/fr-FR.ts +7 -8
  91. package/corpus/templates/slides/app/i18n/hi-IN.ts +8 -7
  92. package/corpus/templates/slides/app/i18n/ja-JP.ts +7 -8
  93. package/corpus/templates/slides/app/i18n/ko-KR.ts +7 -7
  94. package/corpus/templates/slides/app/i18n/pt-BR.ts +7 -8
  95. package/corpus/templates/slides/app/i18n/zh-CN.ts +8 -7
  96. package/corpus/templates/slides/app/i18n/zh-TW.ts +8 -7
  97. package/corpus/templates/slides/app/lib/canvas-zoom.ts +16 -1
  98. package/corpus/templates/slides/app/pages/DeckEditor.tsx +3 -3
  99. package/corpus/templates/slides/changelog/2026-07-03-design-system-setup-now-indexes-figma-code-and-design-md-sou.md +6 -0
  100. package/corpus/templates/slides/changelog/2026-07-03-slide-editing-now-keeps-thumbnails-speaker-notes-styling-and.md +6 -0
  101. package/corpus/templates/slides/server/lib/builder-design-system-proxy.ts +16 -104
  102. package/dist/client/AgentPanel.d.ts.map +1 -1
  103. package/dist/client/AgentPanel.js +2 -1
  104. package/dist/client/AgentPanel.js.map +1 -1
  105. package/dist/client/AssistantChat.d.ts.map +1 -1
  106. package/dist/client/AssistantChat.js +77 -25
  107. package/dist/client/AssistantChat.js.map +1 -1
  108. package/dist/client/ErrorBoundary.d.ts.map +1 -1
  109. package/dist/client/ErrorBoundary.js +37 -2
  110. package/dist/client/ErrorBoundary.js.map +1 -1
  111. package/dist/client/ErrorReportActions.d.ts +13 -0
  112. package/dist/client/ErrorReportActions.d.ts.map +1 -0
  113. package/dist/client/ErrorReportActions.js +14 -0
  114. package/dist/client/ErrorReportActions.js.map +1 -0
  115. package/dist/client/FeedbackButton.d.ts +3 -1
  116. package/dist/client/FeedbackButton.d.ts.map +1 -1
  117. package/dist/client/FeedbackButton.js +6 -5
  118. package/dist/client/FeedbackButton.js.map +1 -1
  119. package/dist/client/db-admin/DbAdminPage.d.ts +9 -1
  120. package/dist/client/db-admin/DbAdminPage.d.ts.map +1 -1
  121. package/dist/client/db-admin/DbAdminPage.js +17 -13
  122. package/dist/client/db-admin/DbAdminPage.js.map +1 -1
  123. package/dist/client/db-admin/SqlEditor.d.ts +3 -1
  124. package/dist/client/db-admin/SqlEditor.d.ts.map +1 -1
  125. package/dist/client/db-admin/SqlEditor.js +3 -3
  126. package/dist/client/db-admin/SqlEditor.js.map +1 -1
  127. package/dist/client/db-admin/TableEditor.d.ts +3 -1
  128. package/dist/client/db-admin/TableEditor.d.ts.map +1 -1
  129. package/dist/client/db-admin/TableEditor.js +6 -6
  130. package/dist/client/db-admin/TableEditor.js.map +1 -1
  131. package/dist/client/db-admin/useAgentSync.d.ts +3 -2
  132. package/dist/client/db-admin/useAgentSync.d.ts.map +1 -1
  133. package/dist/client/db-admin/useAgentSync.js +8 -4
  134. package/dist/client/db-admin/useAgentSync.js.map +1 -1
  135. package/dist/client/db-admin/useDbAdmin.d.ts +11 -7
  136. package/dist/client/db-admin/useDbAdmin.d.ts.map +1 -1
  137. package/dist/client/db-admin/useDbAdmin.js +45 -20
  138. package/dist/client/db-admin/useDbAdmin.js.map +1 -1
  139. package/dist/client/error-reporting.d.ts +19 -0
  140. package/dist/client/error-reporting.d.ts.map +1 -0
  141. package/dist/client/error-reporting.js +71 -0
  142. package/dist/client/error-reporting.js.map +1 -0
  143. package/dist/client/index.d.ts +3 -1
  144. package/dist/client/index.d.ts.map +1 -1
  145. package/dist/client/index.js +3 -1
  146. package/dist/client/index.js.map +1 -1
  147. package/dist/client/org/RequireActiveOrg.d.ts.map +1 -1
  148. package/dist/client/org/RequireActiveOrg.js +2 -1
  149. package/dist/client/org/RequireActiveOrg.js.map +1 -1
  150. package/dist/client/visual-style-controls.d.ts +11 -0
  151. package/dist/client/visual-style-controls.d.ts.map +1 -1
  152. package/dist/client/visual-style-controls.js +350 -1
  153. package/dist/client/visual-style-controls.js.map +1 -1
  154. package/dist/collab/routes.d.ts +1 -1
  155. package/dist/collab/struct-routes.d.ts +1 -1
  156. package/dist/db-admin/index.d.ts +3 -0
  157. package/dist/db-admin/index.d.ts.map +1 -0
  158. package/dist/db-admin/index.js +2 -0
  159. package/dist/db-admin/index.js.map +1 -0
  160. package/dist/db-admin/operations.d.ts +26 -5
  161. package/dist/db-admin/operations.d.ts.map +1 -1
  162. package/dist/db-admin/operations.js +66 -55
  163. package/dist/db-admin/operations.js.map +1 -1
  164. package/dist/localization/default-messages.d.ts +3 -0
  165. package/dist/localization/default-messages.d.ts.map +1 -1
  166. package/dist/localization/default-messages.js +3 -0
  167. package/dist/localization/default-messages.js.map +1 -1
  168. package/dist/notifications/routes.d.ts +1 -1
  169. package/dist/provider-api/corpus-jobs.d.ts +2 -2
  170. package/dist/resources/handlers.d.ts +1 -1
  171. package/dist/secrets/routes.d.ts +9 -9
  172. package/dist/server/builder-design-systems.d.ts +61 -0
  173. package/dist/server/builder-design-systems.d.ts.map +1 -1
  174. package/dist/server/builder-design-systems.js +219 -14
  175. package/dist/server/builder-design-systems.js.map +1 -1
  176. package/dist/server/index.d.ts +1 -1
  177. package/dist/server/index.d.ts.map +1 -1
  178. package/dist/server/index.js +1 -1
  179. package/dist/server/index.js.map +1 -1
  180. package/dist/server/transcribe-voice.d.ts +1 -1
  181. package/dist/templates/default/app/root.tsx +47 -0
  182. package/package.json +2 -1
  183. package/src/templates/default/app/root.tsx +47 -0
package/corpus/README.md CHANGED
@@ -27,5 +27,5 @@ rg -n "defineAction|useActionQuery" node_modules/@agent-native/core/corpus
27
27
 
28
28
  ## Generated Counts
29
29
 
30
- - core files: 2043
31
- - template files: 4825
30
+ - core files: 2046
31
+ - template files: 4839
@@ -1,5 +1,14 @@
1
1
  # @agent-native/core
2
2
 
3
+ ## 0.86.1
4
+
5
+ ### Patch Changes
6
+
7
+ - 097d59b: Add compact feedback and GitHub issue actions to shared error states, with prefilled debug context for reports.
8
+ - 097d59b: Prevent reconnect replay text from temporarily duplicating assistant sidebar responses that are already visible.
9
+ - 097d59b: Add shared Builder DSI helpers for template design-system indexing.
10
+ - 097d59b: Expose target-aware database admin helpers and let the shared DB admin page use admin-gated custom API paths.
11
+
3
12
  ## 0.86.0
4
13
 
5
14
  ### Minor Changes
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agent-native/core",
3
- "version": "0.86.0",
3
+ "version": "0.86.1",
4
4
  "description": "Framework for agent-native application development — where AI agents and UI share SQL state, actions, and context",
5
5
  "homepage": "https://github.com/BuilderIO/agent-native#readme",
6
6
  "bugs": {
@@ -63,6 +63,7 @@
63
63
  },
64
64
  "./agent-web": "./dist/agent-web/index.js",
65
65
  "./db": "./dist/db/index.js",
66
+ "./db-admin": "./dist/db-admin/index.js",
66
67
  "./db/schema": "./dist/db/schema.js",
67
68
  "./db/drizzle-config": "./dist/db/drizzle-config.js",
68
69
  "./client": "./dist/client/index.js",
@@ -65,6 +65,7 @@ import {
65
65
  TooltipTrigger,
66
66
  normalizeTooltipText,
67
67
  } from "./components/ui/tooltip.js";
68
+ import { ErrorReportActions } from "./ErrorReportActions.js";
68
69
  import { FeedbackButton } from "./FeedbackButton.js";
69
70
  import { RunsTrayMenuItem } from "./progress/RunsTray.js";
70
71
  // Lazy-load the full assistant-ui chat stack (tiptap composer + react-markdown +
@@ -2251,6 +2252,15 @@ class AgentPanelErrorBoundary extends React.Component<
2251
2252
  >
2252
2253
  Reset agent panel
2253
2254
  </button>
2255
+ <ErrorReportActions
2256
+ appName="Agent panel"
2257
+ title="Agent panel UI error"
2258
+ details={this.state.error.message}
2259
+ issueTitle="Agent panel UI error"
2260
+ className="max-w-[260px]"
2261
+ feedbackClassName="h-7"
2262
+ githubClassName="h-7"
2263
+ />
2254
2264
  </div>
2255
2265
  );
2256
2266
  }
@@ -688,6 +688,62 @@ function collectRenderedToolCallStates(messages: readonly unknown[]): {
688
688
  return { byId, completedFingerprints };
689
689
  }
690
690
 
691
+ function assistantTextFromContent(content: unknown): string {
692
+ if (typeof content === "string") return content;
693
+ if (!Array.isArray(content)) return "";
694
+ return content
695
+ .filter(
696
+ (part): part is { type: "text"; text: string } =>
697
+ part?.type === "text" && typeof part.text === "string",
698
+ )
699
+ .map((part) => part.text)
700
+ .join("");
701
+ }
702
+
703
+ function latestRenderedAssistantText(messages: readonly unknown[]): string {
704
+ const latestEntry = messages.at(-1);
705
+ const latestMessage = getRepoMessage(latestEntry as any);
706
+ if (latestMessage?.role !== "assistant") return "";
707
+ return assistantTextFromContent(latestMessage.content);
708
+ }
709
+
710
+ function trimReconnectTextAlreadyRendered(
711
+ content: ContentPart[],
712
+ renderedAssistantText: string,
713
+ ): ContentPart[] {
714
+ if (!renderedAssistantText) return content;
715
+
716
+ let remainingOverlap = renderedAssistantText;
717
+ let changed = false;
718
+ const next: ContentPart[] = [];
719
+
720
+ for (const part of content) {
721
+ if (part.type !== "text" || remainingOverlap.length === 0) {
722
+ next.push(part);
723
+ continue;
724
+ }
725
+
726
+ if (remainingOverlap.startsWith(part.text)) {
727
+ remainingOverlap = remainingOverlap.slice(part.text.length);
728
+ changed = true;
729
+ continue;
730
+ }
731
+
732
+ if (part.text.startsWith(remainingOverlap)) {
733
+ const text = part.text.slice(remainingOverlap.length);
734
+ remainingOverlap = "";
735
+ changed = true;
736
+ if (text) next.push({ ...part, text });
737
+ continue;
738
+ }
739
+
740
+ remainingOverlap = "";
741
+ next.push(part);
742
+ }
743
+
744
+ return changed ? next : content;
745
+ }
746
+
691
747
  export function dedupeReconnectContentAgainstMessages(
692
748
  content: ContentPart[],
693
749
  messages: readonly unknown[],
@@ -695,35 +751,43 @@ export function dedupeReconnectContentAgainstMessages(
695
751
  if (content.length === 0 || messages.length === 0) return content;
696
752
  const { byId, completedFingerprints } =
697
753
  collectRenderedToolCallStates(messages);
698
- if (byId.size === 0) return content;
754
+ const renderedAssistantText = latestRenderedAssistantText(messages);
755
+ if (byId.size === 0 && !renderedAssistantText) return content;
699
756
 
700
757
  let changed = false;
701
- const filtered = content.filter((part) => {
702
- const id = toolCallIdFromContentPart(part);
703
- if (!id) return true;
704
- const existing = byId.get(id);
705
- if (existing) {
706
- if (toolCallPartHasResult(part) && !existing.hasResult) return true;
707
- changed = true;
708
- return false;
709
- }
710
- // Fingerprint fallback for the id-convergence window: a reconnect part
711
- // that is still PENDING while the rendered messages already show the same
712
- // call (same tool + same serialized args) completed is a replay artifact —
713
- // dropping it removes the "one spinning, one done" duplicate pair. Only
714
- // pending parts are dropped by fingerprint: completed-vs-completed keeps
715
- // the strict id match so a legitimately repeated identical call is never
716
- // hidden.
717
- if (!toolCallPartHasResult(part)) {
718
- const fingerprint = toolCallFingerprintFromContentPart(part);
719
- if (fingerprint && completedFingerprints.has(fingerprint)) {
720
- changed = true;
721
- return false;
722
- }
723
- }
724
- return true;
725
- });
726
- return changed ? filtered : content;
758
+ const filtered = byId.size
759
+ ? content.filter((part) => {
760
+ const id = toolCallIdFromContentPart(part);
761
+ if (!id) return true;
762
+ const existing = byId.get(id);
763
+ if (existing) {
764
+ if (toolCallPartHasResult(part) && !existing.hasResult) return true;
765
+ changed = true;
766
+ return false;
767
+ }
768
+ // Fingerprint fallback for the id-convergence window: a reconnect part
769
+ // that is still PENDING while the rendered messages already show the same
770
+ // call (same tool + same serialized args) completed is a replay artifact —
771
+ // dropping it removes the "one spinning, one done" duplicate pair. Only
772
+ // pending parts are dropped by fingerprint: completed-vs-completed keeps
773
+ // the strict id match so a legitimately repeated identical call is never
774
+ // hidden.
775
+ if (!toolCallPartHasResult(part)) {
776
+ const fingerprint = toolCallFingerprintFromContentPart(part);
777
+ if (fingerprint && completedFingerprints.has(fingerprint)) {
778
+ changed = true;
779
+ return false;
780
+ }
781
+ }
782
+ return true;
783
+ })
784
+ : content;
785
+ const textDeduped = trimReconnectTextAlreadyRendered(
786
+ filtered,
787
+ renderedAssistantText,
788
+ );
789
+ if (textDeduped !== filtered) changed = true;
790
+ return changed ? textDeduped : content;
727
791
  }
728
792
 
729
793
  const RECOVERY_USER_MESSAGE_PREFIXES = [
@@ -13,13 +13,16 @@ import {
13
13
  type LocaleCode,
14
14
  } from "../localization/shared.js";
15
15
  import { appPath } from "./api-path.js";
16
+ import { ErrorReportActions } from "./ErrorReportActions.js";
16
17
  import {
17
18
  isDynamicImportFailureMessage,
18
19
  recoverFromStaleChunkError,
19
20
  } from "./route-chunk-recovery.js";
20
21
 
21
- const homeLinkClassName =
22
- "mt-6 inline-flex items-center gap-1.5 rounded-md bg-primary px-4 py-2 text-sm font-medium text-primary-foreground shadow-sm hover:bg-primary/90 cursor-pointer";
22
+ const primaryActionClassName =
23
+ "inline-flex h-9 items-center gap-1.5 rounded-md bg-primary px-4 text-sm font-medium text-primary-foreground shadow-sm hover:bg-primary/90 cursor-pointer";
24
+ const secondaryActionClassName =
25
+ "inline-flex h-9 cursor-pointer items-center gap-1.5 rounded-md border border-border bg-background px-4 text-sm font-medium text-foreground shadow-sm hover:bg-accent";
23
26
 
24
27
  const errorCopy: Record<
25
28
  LocaleCode,
@@ -32,6 +35,9 @@ const errorCopy: Record<
32
35
  statusTitle: (status: number) => string;
33
36
  goHome: string;
34
37
  reload: string;
38
+ sendFeedback: string;
39
+ feedbackPlaceholder: string;
40
+ openGitHubIssue: string;
35
41
  }
36
42
  > = {
37
43
  "en-US": {
@@ -43,6 +49,9 @@ const errorCopy: Record<
43
49
  statusTitle: (status) => `${status} Error`,
44
50
  goHome: "Go home",
45
51
  reload: "Reload",
52
+ sendFeedback: "Send feedback",
53
+ feedbackPlaceholder: "Describe what happened before this error appeared.",
54
+ openGitHubIssue: "Open GitHub issue",
46
55
  },
47
56
  "zh-CN": {
48
57
  loadingLatest: "正在加载最新版本...",
@@ -53,6 +62,9 @@ const errorCopy: Record<
53
62
  statusTitle: (status) => `${status} 错误`,
54
63
  goHome: "回到首页",
55
64
  reload: "重新加载",
65
+ sendFeedback: "发送反馈",
66
+ feedbackPlaceholder: "描述此错误出现前发生了什么。",
67
+ openGitHubIssue: "打开 GitHub issue",
56
68
  },
57
69
  "zh-TW": {
58
70
  loadingLatest: "正在載入最新版本...",
@@ -63,6 +75,9 @@ const errorCopy: Record<
63
75
  statusTitle: (status) => `${status} 錯誤`,
64
76
  goHome: "回首頁",
65
77
  reload: "重新載入",
78
+ sendFeedback: "傳送意見回饋",
79
+ feedbackPlaceholder: "描述此錯誤出現前發生了什麼。",
80
+ openGitHubIssue: "開啟 GitHub issue",
66
81
  },
67
82
  "es-ES": {
68
83
  loadingLatest: "Cargando la versión más reciente...",
@@ -73,6 +88,10 @@ const errorCopy: Record<
73
88
  statusTitle: (status) => `Error ${status}`,
74
89
  goHome: "Ir al inicio",
75
90
  reload: "Recargar",
91
+ sendFeedback: "Enviar comentarios",
92
+ feedbackPlaceholder:
93
+ "Describe qué pasó antes de que apareciera este error.",
94
+ openGitHubIssue: "Abrir issue en GitHub",
76
95
  },
77
96
  "fr-FR": {
78
97
  loadingLatest: "Chargement de la dernière version...",
@@ -83,6 +102,9 @@ const errorCopy: Record<
83
102
  statusTitle: (status) => `Erreur ${status}`,
84
103
  goHome: "Accueil",
85
104
  reload: "Recharger",
105
+ sendFeedback: "Envoyer un retour",
106
+ feedbackPlaceholder: "Décrivez ce qui s'est passé avant cette erreur.",
107
+ openGitHubIssue: "Ouvrir une issue GitHub",
86
108
  },
87
109
  "de-DE": {
88
110
  loadingLatest: "Neueste Version wird geladen...",
@@ -93,6 +115,9 @@ const errorCopy: Record<
93
115
  statusTitle: (status) => `Fehler ${status}`,
94
116
  goHome: "Zur Startseite",
95
117
  reload: "Neu laden",
118
+ sendFeedback: "Feedback senden",
119
+ feedbackPlaceholder: "Beschreiben Sie, was vor diesem Fehler passiert ist.",
120
+ openGitHubIssue: "GitHub-Issue öffnen",
96
121
  },
97
122
  "ja-JP": {
98
123
  loadingLatest: "最新バージョンを読み込み中...",
@@ -103,6 +128,9 @@ const errorCopy: Record<
103
128
  statusTitle: (status) => `${status} エラー`,
104
129
  goHome: "ホームへ",
105
130
  reload: "再読み込み",
131
+ sendFeedback: "フィードバックを送信",
132
+ feedbackPlaceholder: "このエラーの直前に起きたことを説明してください。",
133
+ openGitHubIssue: "GitHub issue を開く",
106
134
  },
107
135
  "ko-KR": {
108
136
  loadingLatest: "최신 버전을 불러오는 중...",
@@ -113,6 +141,10 @@ const errorCopy: Record<
113
141
  statusTitle: (status) => `${status} 오류`,
114
142
  goHome: "홈으로 이동",
115
143
  reload: "새로고침",
144
+ sendFeedback: "피드백 보내기",
145
+ feedbackPlaceholder:
146
+ "이 오류가 나타나기 전에 무슨 일이 있었는지 적어 주세요.",
147
+ openGitHubIssue: "GitHub issue 열기",
116
148
  },
117
149
  "pt-BR": {
118
150
  loadingLatest: "Carregando a versão mais recente...",
@@ -123,6 +155,9 @@ const errorCopy: Record<
123
155
  statusTitle: (status) => `Erro ${status}`,
124
156
  goHome: "Ir para início",
125
157
  reload: "Recarregar",
158
+ sendFeedback: "Enviar feedback",
159
+ feedbackPlaceholder: "Descreva o que aconteceu antes deste erro aparecer.",
160
+ openGitHubIssue: "Abrir issue no GitHub",
126
161
  },
127
162
  "hi-IN": {
128
163
  loadingLatest: "नवीनतम संस्करण लोड हो रहा है...",
@@ -133,6 +168,9 @@ const errorCopy: Record<
133
168
  statusTitle: (status) => `${status} त्रुटि`,
134
169
  goHome: "होम पर जाएं",
135
170
  reload: "रीलोड करें",
171
+ sendFeedback: "फ़ीडबैक भेजें",
172
+ feedbackPlaceholder: "इस त्रुटि से पहले क्या हुआ, उसका वर्णन करें।",
173
+ openGitHubIssue: "GitHub issue खोलें",
136
174
  },
137
175
  "ar-SA": {
138
176
  loadingLatest: "جار تحميل أحدث إصدار...",
@@ -143,6 +181,9 @@ const errorCopy: Record<
143
181
  statusTitle: (status) => `خطأ ${status}`,
144
182
  goHome: "العودة للرئيسية",
145
183
  reload: "إعادة التحميل",
184
+ sendFeedback: "إرسال الملاحظات",
185
+ feedbackPlaceholder: "صف ما حدث قبل ظهور هذا الخطأ.",
186
+ openGitHubIssue: "فتح مشكلة في GitHub",
146
187
  },
147
188
  };
148
189
 
@@ -273,16 +314,30 @@ function ErrorScreen({ error }: { error: unknown }) {
273
314
  )}
274
315
  <h1 className="mt-3 text-2xl font-semibold">{title}</h1>
275
316
  <p className="mt-2 text-muted-foreground text-sm">{details}</p>
276
- <a href={appPath("/")} className={homeLinkClassName}>
277
- {copy.goHome}
278
- </a>
279
- <button
280
- type="button"
281
- onClick={() => window.location.reload()}
282
- className="mt-3 inline-flex cursor-pointer items-center gap-1.5 rounded-md border border-border bg-background px-4 py-2 text-sm font-medium text-foreground shadow-sm hover:bg-accent"
283
- >
284
- {copy.reload}
285
- </button>
317
+ <div className="mt-6 flex flex-wrap justify-center gap-2">
318
+ <a href={appPath("/")} className={primaryActionClassName}>
319
+ {copy.goHome}
320
+ </a>
321
+ <button
322
+ type="button"
323
+ onClick={() => window.location.reload()}
324
+ className={secondaryActionClassName}
325
+ >
326
+ {copy.reload}
327
+ </button>
328
+ <ErrorReportActions
329
+ appName="Agent Native"
330
+ title={title}
331
+ details={details}
332
+ status={status}
333
+ issueTitle={`Error screen: ${title}`}
334
+ feedbackLabel={copy.sendFeedback}
335
+ feedbackPlaceholder={copy.feedbackPlaceholder}
336
+ githubLabel={copy.openGitHubIssue}
337
+ feedbackClassName="h-9"
338
+ githubClassName="h-9"
339
+ />
340
+ </div>
286
341
  {stack && (
287
342
  <pre className="mt-6 w-full text-start text-xs overflow-auto p-4 bg-muted rounded">
288
343
  <code>{stack}</code>
@@ -0,0 +1,69 @@
1
+ import { IconBrandGithub } from "@tabler/icons-react";
2
+
3
+ import {
4
+ buildErrorReportTemplate,
5
+ buildGitHubIssueUrl,
6
+ type ErrorReportTemplateOptions,
7
+ } from "./error-reporting.js";
8
+ import { FeedbackButton } from "./FeedbackButton.js";
9
+ import { cn } from "./utils.js";
10
+
11
+ export interface ErrorReportActionsProps extends ErrorReportTemplateOptions {
12
+ feedbackLabel?: string;
13
+ feedbackPlaceholder?: string;
14
+ githubLabel?: string;
15
+ className?: string;
16
+ feedbackClassName?: string;
17
+ githubClassName?: string;
18
+ side?: "top" | "bottom" | "left" | "right";
19
+ align?: "start" | "center" | "end";
20
+ }
21
+
22
+ export function ErrorReportActions({
23
+ feedbackLabel = "Send feedback",
24
+ feedbackPlaceholder = "Describe what happened before this error appeared.",
25
+ githubLabel = "Open GitHub issue",
26
+ className,
27
+ feedbackClassName,
28
+ githubClassName,
29
+ side = "top",
30
+ align = "center",
31
+ ...report
32
+ }: ErrorReportActionsProps) {
33
+ const template = buildErrorReportTemplate({
34
+ ...report,
35
+ prompt: report.prompt ?? feedbackPlaceholder,
36
+ });
37
+ const githubIssueUrl = buildGitHubIssueUrl(report);
38
+
39
+ return (
40
+ <div
41
+ className={cn(
42
+ "flex flex-wrap items-center justify-center gap-2",
43
+ className,
44
+ )}
45
+ >
46
+ <FeedbackButton
47
+ variant="outlined"
48
+ label={feedbackLabel}
49
+ placeholder={feedbackPlaceholder}
50
+ initialValue={template}
51
+ side={side}
52
+ align={align}
53
+ className={cn("h-8 text-xs", feedbackClassName)}
54
+ />
55
+ <a
56
+ href={githubIssueUrl}
57
+ target="_blank"
58
+ rel="noreferrer"
59
+ className={cn(
60
+ "inline-flex h-8 items-center gap-1.5 rounded-md px-2 text-xs text-muted-foreground underline-offset-4 hover:bg-muted/60 hover:text-foreground hover:underline",
61
+ githubClassName,
62
+ )}
63
+ >
64
+ <IconBrandGithub className="size-3.5" />
65
+ {githubLabel}
66
+ </a>
67
+ </div>
68
+ );
69
+ }
@@ -13,7 +13,7 @@ import {
13
13
 
14
14
  import { DEFAULT_LOCALE, type LocaleCode } from "../localization/shared.js";
15
15
  import { getFeedbackClientContext } from "./feedback-context.js";
16
- import { useLocale } from "./i18n.js";
16
+ import { useOptionalLocale } from "./i18n.js";
17
17
  import { useSession } from "./use-session.js";
18
18
  import { cn } from "./utils.js";
19
19
 
@@ -241,6 +241,8 @@ export interface FeedbackButtonProps {
241
241
  align?: "start" | "center" | "end";
242
242
  /** Placeholder text for the textarea. */
243
243
  placeholder?: string;
244
+ /** Optional text to prefill when the popover opens. */
245
+ initialValue?: string;
244
246
  /** Current chat session/thread id, when the host already knows it. */
245
247
  chatSessionId?: string | null;
246
248
  /** Chat localStorage namespace, when the host uses per-app chat storage. */
@@ -273,6 +275,7 @@ export function FeedbackButton({
273
275
  side,
274
276
  align = "end",
275
277
  placeholder,
278
+ initialValue,
276
279
  chatSessionId,
277
280
  chatStorageKey,
278
281
  open: controlledOpen,
@@ -281,7 +284,8 @@ export function FeedbackButton({
281
284
  }: FeedbackButtonProps) {
282
285
  const target = parseTarget(url);
283
286
  const { session } = useSession();
284
- const { locale } = useLocale();
287
+ const localeContext = useOptionalLocale();
288
+ const locale = localeContext?.locale ?? DEFAULT_LOCALE;
285
289
  const copy = FEEDBACK_COPY[locale] ?? FEEDBACK_COPY[DEFAULT_LOCALE];
286
290
  const resolvedLabel = label ?? copy.label;
287
291
 
@@ -308,7 +312,7 @@ export function FeedbackButton({
308
312
  useEffect(() => {
309
313
  if (!open) return;
310
314
  openedAtRef.current = Date.now();
311
- setValue("");
315
+ setValue(initialValue ?? "");
312
316
  setHoneypot("");
313
317
  setSubmitting(false);
314
318
  setSubmitted(false);
@@ -332,7 +336,7 @@ export function FeedbackButton({
332
336
  closeTimerRef.current = null;
333
337
  }
334
338
  };
335
- }, [open, url]);
339
+ }, [copy.invalidUrl, copy.loadError, initialValue, open, url]);
336
340
 
337
341
  const submit = useCallback(
338
342
  async (e?: FormEvent) => {
@@ -1,11 +1,10 @@
1
1
  import { IconDatabase, IconLoader2 } from "@tabler/icons-react";
2
2
  /**
3
- * Code-mode database admin page — the shell that hosts the table browser, the
4
- * table editor, and the SQL editor.
3
+ * Database admin page — the shell that hosts the table browser, the table
4
+ * editor, and the SQL editor.
5
5
  *
6
- * Gated to Code mode: when the app cannot toggle into Code mode
7
- * (`canToggle` is false) we render a clean notice instead of the tool. The
8
- * backend also enforces this with a 403, so this is purely a friendlier UX.
6
+ * By default this is gated to Code mode for the core dev route. Trusted hosts
7
+ * can opt out and point it at their own admin-gated API path.
9
8
  */
10
9
  import { useEffect, useMemo, useState } from "react";
11
10
 
@@ -16,7 +15,7 @@ import { SqlEditor } from "./SqlEditor.js";
16
15
  import { TableBrowser } from "./TableBrowser.js";
17
16
  import { TableEditor } from "./TableEditor.js";
18
17
  import { useDbAdminAgentSync, useNavigateConsumer } from "./useAgentSync.js";
19
- import { useOverview } from "./useDbAdmin.js";
18
+ import { useOverview, type DbAdminRequestConfig } from "./useDbAdmin.js";
20
19
 
21
20
  const DIALECT_LABEL: Record<string, string> = {
22
21
  postgres: "Postgres",
@@ -24,9 +23,30 @@ const DIALECT_LABEL: Record<string, string> = {
24
23
  d1: "Cloudflare D1",
25
24
  };
26
25
 
27
- export function DbAdminPage() {
26
+ export interface DbAdminPageProps {
27
+ apiBasePath?: string;
28
+ cacheScope?: string;
29
+ title?: string;
30
+ subtitle?: string;
31
+ codeModeGate?: boolean;
32
+ syncNavigation?: boolean;
33
+ }
34
+
35
+ export function DbAdminPage({
36
+ apiBasePath,
37
+ cacheScope,
38
+ title = "Database",
39
+ subtitle,
40
+ codeModeGate = true,
41
+ syncNavigation = true,
42
+ }: DbAdminPageProps = {}) {
28
43
  const { canToggle, isLoading: devLoading } = useCodeMode();
29
- const { data: overview, isLoading: overviewLoading } = useOverview();
44
+ const requestConfig = useMemo<DbAdminRequestConfig | undefined>(() => {
45
+ if (!apiBasePath && !cacheScope) return undefined;
46
+ return { basePath: apiBasePath, scopeKey: cacheScope };
47
+ }, [apiBasePath, cacheScope]);
48
+ const { data: overview, isLoading: overviewLoading } =
49
+ useOverview(requestConfig);
30
50
 
31
51
  const [selectedTable, setSelectedTable] = useState<string | null>(null);
32
52
  const [mode, setMode] = useState<"table" | "sql">("table");
@@ -45,12 +65,12 @@ export function DbAdminPage() {
45
65
  }, [selectedTable, tables]);
46
66
 
47
67
  // Keep the agent's <current-screen> in sync, and let it drive navigation.
48
- useDbAdminAgentSync({ table: selectedTable, mode });
68
+ useDbAdminAgentSync({ table: selectedTable, mode, enabled: syncNavigation });
49
69
  useNavigateConsumer((table) => {
50
70
  setSelectedTable(table);
51
71
  setMode("table");
52
72
  setFkFilters(undefined);
53
- });
73
+ }, syncNavigation);
54
74
 
55
75
  const tableNames = useMemo(() => tables.map((t) => t.name), [tables]);
56
76
  // SqlEditor degrades gracefully without per-table columns; pass an empty map.
@@ -58,7 +78,7 @@ export function DbAdminPage() {
58
78
  const columnsByTable = useMemo<Record<string, string[]>>(() => ({}), []);
59
79
 
60
80
  // ─── Code mode gate ──────────────────────────────────────────────────────
61
- if (!devLoading && !canToggle) {
81
+ if (codeModeGate && !devLoading && !canToggle) {
62
82
  return (
63
83
  <div className="flex h-full w-full items-center justify-center bg-background p-6">
64
84
  <div className="flex max-w-md flex-col items-center rounded-lg border bg-card p-8 text-center shadow-sm">
@@ -79,20 +99,26 @@ export function DbAdminPage() {
79
99
  );
80
100
  }
81
101
 
82
- const showInitialLoading = (devLoading || overviewLoading) && !overview;
102
+ const showInitialLoading =
103
+ ((codeModeGate && devLoading) || overviewLoading) && !overview;
83
104
 
84
105
  return (
85
106
  <div className="flex h-full w-full flex-col bg-background text-foreground">
86
107
  {/* Header */}
87
108
  <header className="flex h-12 shrink-0 items-center gap-3 border-b px-4">
88
109
  <IconDatabase className="h-5 w-5 text-muted-foreground" stroke={1.75} />
89
- <span className="text-sm font-semibold">Database</span>
110
+ <span className="text-sm font-semibold">{title}</span>
90
111
  <span className="inline-flex items-center rounded-full border bg-muted px-2 py-0.5 text-xs font-medium text-muted-foreground">
91
112
  {DIALECT_LABEL[dialect] ?? dialect}
92
113
  </span>
93
114
  <span className="text-xs text-muted-foreground">
94
115
  {tables.length} {tables.length === 1 ? "table" : "tables"}
95
116
  </span>
117
+ {subtitle ? (
118
+ <span className="min-w-0 truncate text-xs text-muted-foreground">
119
+ {subtitle}
120
+ </span>
121
+ ) : null}
96
122
  </header>
97
123
 
98
124
  {/* Body: fixed sidebar + flexible main */}
@@ -122,12 +148,14 @@ export function DbAdminPage() {
122
148
  dialect={dialect}
123
149
  tableNames={tableNames}
124
150
  columnsByTable={columnsByTable}
151
+ requestConfig={requestConfig}
125
152
  />
126
153
  ) : selectedTable ? (
127
154
  <TableEditor
128
155
  key={selectedTable}
129
156
  table={selectedTable}
130
157
  dialect={dialect}
158
+ requestConfig={requestConfig}
131
159
  initialFilters={fkFilters}
132
160
  onNavigateToRow={(t, filters) => {
133
161
  setSelectedTable(t);
@@ -63,12 +63,13 @@ import {
63
63
  deleteSnippet,
64
64
  type SqlSnippet,
65
65
  } from "./sql-storage.js";
66
- import { runQuery } from "./useDbAdmin.js";
66
+ import { runQuery, type DbAdminRequestConfig } from "./useDbAdmin.js";
67
67
 
68
68
  export interface SqlEditorProps {
69
69
  dialect: DbAdminDialect;
70
70
  tableNames: string[];
71
71
  columnsByTable: Record<string, string[]>;
72
+ requestConfig?: DbAdminRequestConfig;
72
73
  }
73
74
 
74
75
  interface QueryResult {
@@ -266,6 +267,7 @@ export function SqlEditor({
266
267
  dialect,
267
268
  tableNames,
268
269
  columnsByTable,
270
+ requestConfig,
269
271
  }: SqlEditorProps) {
270
272
  const isDark = useIsDark();
271
273
  const editorRef = useRef<ReactCodeMirrorRef>(null);
@@ -307,7 +309,12 @@ export function SqlEditor({
307
309
  setRunning(true);
308
310
  setError(null);
309
311
  try {
310
- const res = await runQuery(trimmed, undefined, confirmDestructive);
312
+ const res = await runQuery(
313
+ trimmed,
314
+ undefined,
315
+ confirmDestructive,
316
+ requestConfig,
317
+ );
311
318
  setResult({
312
319
  columns: res.columns,
313
320
  rows: res.rows,
@@ -334,7 +341,7 @@ export function SqlEditor({
334
341
  setRunning(false);
335
342
  }
336
343
  },
337
- [],
344
+ [requestConfig],
338
345
  );
339
346
 
340
347
  const runActiveStatement = useCallback(() => {