@axhub/acp 0.1.0 → 0.1.2-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (207) hide show
  1. package/.next/BUILD_ID +1 -1
  2. package/.next/app-path-routes-manifest.json +4 -0
  3. package/.next/build-manifest.json +3 -3
  4. package/.next/fallback-build-manifest.json +3 -3
  5. package/.next/next-minimal-server.js.nft.json +1 -1
  6. package/.next/next-server.js.nft.json +1 -1
  7. package/.next/routes-manifest.json +28 -0
  8. package/.next/server/app/_global-error/page.js.nft.json +1 -1
  9. package/.next/server/app/_global-error.html +1 -1
  10. package/.next/server/app/_global-error.rsc +1 -1
  11. package/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  12. package/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  13. package/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  14. package/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  15. package/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  16. package/.next/server/app/_not-found/page.js.nft.json +1 -1
  17. package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  18. package/.next/server/app/_not-found.html +1 -1
  19. package/.next/server/app/_not-found.rsc +2 -2
  20. package/.next/server/app/_not-found.segments/_full.segment.rsc +2 -2
  21. package/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  22. package/.next/server/app/_not-found.segments/_index.segment.rsc +2 -2
  23. package/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  24. package/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  25. package/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  26. package/.next/server/app/api/acp/capabilities/route.js +5 -3
  27. package/.next/server/app/api/acp/capabilities/route.js.nft.json +1 -1
  28. package/.next/server/app/api/acp/commands/route.js +1 -1
  29. package/.next/server/app/api/acp/commands/route.js.nft.json +1 -1
  30. package/.next/server/app/api/acp/runtime/route.js.nft.json +1 -1
  31. package/.next/server/app/api/chat/cancel/route/app-paths-manifest.json +3 -0
  32. package/.next/server/app/api/chat/cancel/route/build-manifest.json +9 -0
  33. package/.next/server/app/api/chat/cancel/route/server-reference-manifest.json +4 -0
  34. package/.next/server/app/api/chat/cancel/route.js +11 -0
  35. package/.next/server/app/api/chat/cancel/route.js.nft.json +1 -0
  36. package/.next/server/app/api/chat/cancel/route_client-reference-manifest.js +3 -0
  37. package/.next/server/app/api/chat/resume/[streamId]/route/app-paths-manifest.json +3 -0
  38. package/.next/server/app/api/chat/resume/[streamId]/route/build-manifest.json +9 -0
  39. package/.next/server/app/api/chat/resume/[streamId]/route/server-reference-manifest.json +4 -0
  40. package/.next/server/app/api/chat/resume/[streamId]/route.js +11 -0
  41. package/.next/server/app/api/chat/resume/[streamId]/route.js.nft.json +1 -0
  42. package/.next/server/app/api/chat/resume/[streamId]/route_client-reference-manifest.js +3 -0
  43. package/.next/server/app/api/chat/route.js +5 -3
  44. package/.next/server/app/api/chat/route.js.nft.json +1 -1
  45. package/.next/server/app/api/conversations/[threadId]/messages/route.js +4 -2
  46. package/.next/server/app/api/conversations/[threadId]/messages/route.js.nft.json +1 -1
  47. package/.next/server/app/api/conversations/[threadId]/route.js +2 -2
  48. package/.next/server/app/api/conversations/[threadId]/route.js.nft.json +1 -1
  49. package/.next/server/app/api/conversations/[threadId]/runtime/route/app-paths-manifest.json +3 -0
  50. package/.next/server/app/api/conversations/[threadId]/runtime/route/build-manifest.json +9 -0
  51. package/.next/server/app/api/conversations/[threadId]/runtime/route/server-reference-manifest.json +4 -0
  52. package/.next/server/app/api/conversations/[threadId]/runtime/route.js +11 -0
  53. package/.next/server/app/api/conversations/[threadId]/runtime/route.js.nft.json +1 -0
  54. package/.next/server/app/api/conversations/[threadId]/runtime/route_client-reference-manifest.js +3 -0
  55. package/.next/server/app/api/conversations/route.js +2 -2
  56. package/.next/server/app/api/conversations/route.js.nft.json +1 -1
  57. package/.next/server/app/api/local-files/image/route.js.nft.json +1 -1
  58. package/.next/server/app/api/local-files/open/route.js.nft.json +1 -1
  59. package/.next/server/app/api/output-artifacts/thread/route/app-paths-manifest.json +3 -0
  60. package/.next/server/app/api/output-artifacts/thread/route/build-manifest.json +9 -0
  61. package/.next/server/app/api/output-artifacts/thread/route/server-reference-manifest.json +4 -0
  62. package/.next/server/app/api/output-artifacts/thread/route.js +8 -0
  63. package/.next/server/app/api/output-artifacts/thread/route.js.nft.json +1 -0
  64. package/.next/server/app/api/output-artifacts/thread/route_client-reference-manifest.js +3 -0
  65. package/.next/server/app/api/output-artifacts/workspace/route.js +1 -1
  66. package/.next/server/app/api/output-artifacts/workspace/route.js.nft.json +1 -1
  67. package/.next/server/app/api/tools/image-generation/files/[id]/route.js.nft.json +1 -1
  68. package/.next/server/app/api/tools/image-generation/records/route.js.nft.json +1 -1
  69. package/.next/server/app/api/tools/user-choice/route.js.nft.json +1 -1
  70. package/.next/server/app/favicon.ico/route.js.nft.json +1 -1
  71. package/.next/server/app/page.js +2 -2
  72. package/.next/server/app/page.js.nft.json +1 -1
  73. package/.next/server/app/page_client-reference-manifest.js +1 -1
  74. package/.next/server/app/session/[provider]/[sessionId]/page.js +1 -1
  75. package/.next/server/app/session/[provider]/[sessionId]/page.js.nft.json +1 -1
  76. package/.next/server/app/session/[provider]/[sessionId]/page_client-reference-manifest.js +1 -1
  77. package/.next/server/app/thread/[threadId]/page.js +1 -1
  78. package/.next/server/app/thread/[threadId]/page.js.nft.json +1 -1
  79. package/.next/server/app/thread/[threadId]/page_client-reference-manifest.js +1 -1
  80. package/.next/server/app-paths-manifest.json +4 -0
  81. package/.next/server/chunks/0zjb_server_app_api_conversations_[threadId]_runtime_route_actions_08lhdqs.js +3 -0
  82. package/.next/server/chunks/[root-of-the-server]__04pn6ap._.js +3 -0
  83. package/.next/server/chunks/[root-of-the-server]__0aovkxs._.js +3 -0
  84. package/.next/server/chunks/[root-of-the-server]__0c.r6ru._.js +76 -0
  85. package/.next/server/chunks/[root-of-the-server]__0gmxr~m._.js +3 -0
  86. package/.next/server/chunks/[root-of-the-server]__0iokgmz._.js +1 -1
  87. package/.next/server/chunks/[root-of-the-server]__0j-lxr4._.js +1 -1
  88. package/.next/server/chunks/[root-of-the-server]__0lbwo2g._.js +3 -0
  89. package/.next/server/chunks/[root-of-the-server]__0ly6hop._.js +1 -1
  90. package/.next/server/chunks/[root-of-the-server]__0ml.1wa._.js +3 -0
  91. package/.next/server/chunks/[root-of-the-server]__0o2epta._.js +3 -0
  92. package/.next/server/chunks/[root-of-the-server]__0os92l7._.js +3 -0
  93. package/.next/server/chunks/[root-of-the-server]__0tmhg7j._.js +1 -1
  94. package/.next/server/chunks/[root-of-the-server]__0txmfnw._.js +2 -2
  95. package/.next/server/chunks/[root-of-the-server]__0wo0b8z._.js +1 -1
  96. package/.next/server/chunks/[root-of-the-server]__0xh8d4~._.js +3 -0
  97. package/.next/server/chunks/[root-of-the-server]__0zmyki-._.js +3 -0
  98. package/.next/server/chunks/[root-of-the-server]__0zn3~pq._.js +3 -0
  99. package/.next/server/chunks/[root-of-the-server]__13xepwb._.js +3 -0
  100. package/.next/server/chunks/_0_9_730._.js +107 -0
  101. package/.next/server/chunks/_0gx~6n6._.js +107 -0
  102. package/.next/server/chunks/_next-internal_server_app_api_chat_cancel_route_actions_0hdg4o_.js +3 -0
  103. package/.next/server/chunks/_next-internal_server_app_api_chat_resume_[streamId]_route_actions_12ynw6q.js +3 -0
  104. package/.next/server/chunks/_next-internal_server_app_api_output-artifacts_thread_route_actions_04~2mo7.js +3 -0
  105. package/.next/server/chunks/lib_conversations_store_ts_0gzcj38._.js +4 -4
  106. package/.next/server/chunks/node_modules_0nyqhq8._.js +3 -0
  107. package/.next/server/chunks/ssr/{[root-of-the-server]__0piffp7._.js → [root-of-the-server]__09wwymw._.js} +2 -2
  108. package/.next/server/chunks/ssr/{[root-of-the-server]__0488vn3._.js → [root-of-the-server]__0n6oe29._.js} +1 -1
  109. package/.next/server/chunks/ssr/{[root-of-the-server]__0icm-_h._.js → [root-of-the-server]__0niwg81._.js} +2 -2
  110. package/.next/server/chunks/ssr/_03.pm1z._.js +18 -16
  111. package/.next/server/chunks/ssr/_0txwi90._.js +1 -1
  112. package/.next/server/chunks/ssr/lib_conversations_store_ts_0-pd6d3._.js +2 -2
  113. package/.next/server/chunks/ssr/node_modules_next_dist_client_components_builtin_forbidden_0ghu-f7.js +1 -1
  114. package/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_02suzhc.js +2 -2
  115. package/.next/server/functions-config-manifest.json +3 -0
  116. package/.next/server/instrumentation.js.nft.json +1 -1
  117. package/.next/server/middleware-build-manifest.js +3 -3
  118. package/.next/server/pages/404.html +1 -1
  119. package/.next/server/pages/500.html +1 -1
  120. package/.next/static/chunks/0c~b2_-vk17t5.css +1 -0
  121. package/.next/static/chunks/0r125n-._y5ny.js +104 -0
  122. package/README.md +2 -2
  123. package/bin/acp.mjs +0 -48
  124. package/dist/components/assistant-ui/acp-command-menu.mjs +42 -9
  125. package/dist/components/assistant-ui/acp-elicitation-option-list.mjs +1 -1
  126. package/dist/components/assistant-ui/acp-selectors.mjs +1 -3
  127. package/dist/components/assistant-ui/file.mjs +2 -2
  128. package/dist/components/assistant-ui/image-generation-settings-dialog.mjs +25 -6
  129. package/dist/components/assistant-ui/image.mjs +2 -2
  130. package/dist/components/assistant-ui/markdown-text.mjs +3 -3
  131. package/dist/components/assistant-ui/thread/composer.d.ts +1 -1
  132. package/dist/components/assistant-ui/thread/composer.mjs +37 -3
  133. package/dist/components/assistant-ui/thread/context-chips.mjs +3 -2
  134. package/dist/components/assistant-ui/thread/index.mjs +1 -1
  135. package/dist/components/assistant-ui/thread/message-list.mjs +1 -1
  136. package/dist/components/assistant-ui/thread/messages.mjs +2 -2
  137. package/dist/components/assistant-ui/thread-list.d.ts +8 -2
  138. package/dist/components/assistant-ui/thread-list.mjs +80 -7
  139. package/dist/components/assistant-ui/threadlist-sidebar.d.ts +4 -1
  140. package/dist/components/assistant-ui/threadlist-sidebar.mjs +2 -2
  141. package/dist/components/assistant-ui/tool-fallback.d.ts +1 -1
  142. package/dist/components/assistant-ui/tool-fallback.mjs +48 -11
  143. package/dist/components/tool-ui/option-list.d.ts +2 -1
  144. package/dist/components/tool-ui/option-list.mjs +4 -4
  145. package/dist/lib/acp2aisdk/client-context.d.ts +6 -0
  146. package/dist/lib/acp2aisdk/client-context.mjs +43 -7
  147. package/dist/lib/acp2aisdk/commands.mjs +5 -20
  148. package/dist/lib/acp2aisdk/context.d.ts +4 -0
  149. package/dist/lib/acp2aisdk/errors.d.ts +2 -0
  150. package/dist/lib/acp2aisdk/errors.mjs +37 -0
  151. package/dist/lib/acp2aisdk/index.d.ts +7 -0
  152. package/dist/lib/acp2aisdk/index.mjs +110 -34
  153. package/dist/lib/acp2aisdk/provider-compat.mjs +4 -2
  154. package/dist/lib/acp2aisdk/provider-registry.mjs +2 -5
  155. package/dist/lib/acp2aisdk/resumable-ui-stream.d.ts +11 -0
  156. package/dist/lib/acp2aisdk/resumable-ui-stream.mjs +323 -0
  157. package/dist/lib/acp2aisdk/runtime-message-filter.d.ts +6 -0
  158. package/dist/lib/acp2aisdk/runtime-message-filter.mjs +22 -0
  159. package/dist/lib/acp2aisdk/runtime-persistence.d.ts +21 -0
  160. package/dist/lib/acp2aisdk/runtime-persistence.mjs +135 -0
  161. package/dist/lib/acp2aisdk/session-store.mjs +7 -1
  162. package/dist/lib/acp2aisdk/skill-command-cache.mjs +1 -1
  163. package/dist/lib/acp2aisdk/types.d.ts +0 -1
  164. package/dist/lib/api/client.d.ts +51 -4
  165. package/dist/lib/api/client.mjs +35 -4
  166. package/dist/lib/api/cors.mjs +7 -1
  167. package/dist/lib/api/http-response.d.ts +2 -0
  168. package/dist/lib/api/http-response.mjs +22 -0
  169. package/dist/lib/conversations/client-adapter.d.ts +1 -0
  170. package/dist/lib/conversations/client-adapter.mjs +158 -51
  171. package/dist/lib/conversations/provider-message-loader.d.ts +7 -0
  172. package/dist/lib/conversations/provider-message-loader.mjs +99 -0
  173. package/dist/lib/conversations/runtime-message-recovery.d.ts +9 -0
  174. package/dist/lib/conversations/runtime-message-recovery.mjs +95 -0
  175. package/dist/lib/conversations/store.d.ts +2 -2
  176. package/dist/lib/conversations/store.mjs +49 -149
  177. package/dist/lib/conversations/title-text.d.ts +3 -0
  178. package/dist/lib/conversations/title-text.mjs +81 -0
  179. package/dist/lib/conversations/types.d.ts +12 -1
  180. package/dist/lib/local-image-files.mjs +9 -1
  181. package/dist/lib/local-image-paths.mjs +31 -6
  182. package/dist/lib/output-artifacts/thread.d.ts +22 -0
  183. package/dist/lib/output-artifacts/thread.mjs +47 -0
  184. package/dist/lib/output-artifacts/workspace.mjs +6 -2
  185. package/dist/lib/provider-history/codex.mjs +5 -30
  186. package/dist/public-api/server.d.ts +1 -1
  187. package/dist/public-api/server.mjs +1 -1
  188. package/dist/tools/image-generation/client.mjs +6 -4
  189. package/dist/tools/image-generation/server.mjs +3 -1
  190. package/dist/tools/image-generation/shared.d.ts +1 -0
  191. package/dist/tools/image-generation/shared.mjs +4 -0
  192. package/dist/tools/image-generation/ui-detail.mjs +66 -2
  193. package/dist/tools/user-choice/ui.mjs +66 -30
  194. package/package.json +6 -6
  195. package/.next/server/chunks/[root-of-the-server]__04xq..~._.js +0 -3
  196. package/.next/server/chunks/[root-of-the-server]__07sxz4_._.js +0 -3
  197. package/.next/server/chunks/[root-of-the-server]__0dwg3fr._.js +0 -178
  198. package/.next/server/chunks/[root-of-the-server]__0eanzwb._.js +0 -3
  199. package/.next/server/chunks/[root-of-the-server]__0gqx~5k._.js +0 -3
  200. package/.next/server/chunks/[root-of-the-server]__0~mtsby._.js +0 -3
  201. package/.next/server/chunks/[root-of-the-server]__10-n4io._.js +0 -3
  202. package/.next/server/chunks/[root-of-the-server]__10g507v._.js +0 -3
  203. package/.next/static/chunks/0zftsky7gte_9.js +0 -102
  204. package/.next/static/chunks/1610ha42i.fl~.css +0 -1
  205. /package/.next/static/{mbk_N5Gs4ZJg3lciRL6ya → 0241KNhzDEg6VFWZ_e_qK}/_buildManifest.js +0 -0
  206. /package/.next/static/{mbk_N5Gs4ZJg3lciRL6ya → 0241KNhzDEg6VFWZ_e_qK}/_clientMiddlewareManifest.js +0 -0
  207. /package/.next/static/{mbk_N5Gs4ZJg3lciRL6ya → 0241KNhzDEg6VFWZ_e_qK}/_ssgManifest.js +0 -0
package/README.md CHANGED
@@ -24,8 +24,8 @@ npx -y @axhub/acp --cors-origin https://client.example.com
24
24
  ### External Integration
25
25
 
26
26
  - [HTTP Chat API](docs/http-chat-api.md) documents `POST /api/chat`, including request parameters, stream chunks, response headers, a one-shot command example, and a live message listener example.
27
- - [Post Message API](docs/post-message-api.md) documents the browser `postMessage` bridge for iframe/opened-window context injection.
28
- - [Host Integration](docs/host-integration.md) documents generic iframe/child-window embedding, runtime API base configuration, context injection, HTTP chat execution, image generation, and React component reuse.
27
+ - [Post Message API](docs/post-message-api.md) documents the browser `postMessage` bridge for iframe/opened-window context injection, host runtime configuration, UI-originated chat/attachments, thread snapshot queries, and lifecycle subscriptions.
28
+ - [Host Integration](docs/host-integration.md) documents generic iframe/child-window embedding, runtime API base configuration, context injection, runtime config injection, postMessage thread queries, HTTP chat execution, image generation, and React component reuse.
29
29
  - [Component And Runtime API](docs/component-runtime-api.md) documents package-level `@axhub/acp/ui`, `@axhub/acp/runtime`, `@axhub/acp/server`, `@axhub/acp/react`, and `@axhub/acp/react/styles.css` entries for component/runtime reuse, including embedded runtime API base configuration and Composer host extension props.
30
30
 
31
31
  ### 1. Configure Codex
package/bin/acp.mjs CHANGED
@@ -92,10 +92,6 @@ function parseArgs(args) {
92
92
  return { help: false, options };
93
93
  }
94
94
 
95
- function quoteCommandArg(value) {
96
- return /[\s"']/u.test(value) ? `"${value.replace(/"/gu, '\\"')}"` : value;
97
- }
98
-
99
95
  function runtimeHostForUrl(hostname) {
100
96
  const normalized = String(hostname || "")
101
97
  .trim()
@@ -124,47 +120,6 @@ function buildServerRuntimeEnv(options) {
124
120
  };
125
121
  }
126
122
 
127
- function codexAcpCommandLine() {
128
- const codexAcpBin = require.resolve(
129
- "@zed-industries/codex-acp/bin/codex-acp.js",
130
- );
131
- return [process.execPath, codexAcpBin].map(quoteCommandArg).join(" ");
132
- }
133
-
134
- function isCodexProviderName(value) {
135
- const normalized = String(value || "")
136
- .trim()
137
- .toLowerCase();
138
- return normalized === "codex" || normalized === "openai";
139
- }
140
-
141
- function hasCodexCommandOverride(env) {
142
- if (env.ACP_CODEX_COMMAND?.trim()) return true;
143
-
144
- const rawJson =
145
- env.ACP_PROVIDER_COMMAND_OVERRIDES || env.AXHUB_ACP_COMMAND_OVERRIDES;
146
- if (!rawJson) return false;
147
-
148
- try {
149
- const parsed = JSON.parse(rawJson);
150
- if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
151
- return false;
152
- }
153
- for (const [providerName, value] of Object.entries(parsed)) {
154
- if (!isCodexProviderName(providerName)) continue;
155
- if (typeof value === "string") return Boolean(value.trim());
156
- if (!value || typeof value !== "object" || Array.isArray(value)) {
157
- continue;
158
- }
159
- return typeof value.command === "string" && Boolean(value.command.trim());
160
- }
161
- } catch {
162
- return false;
163
- }
164
-
165
- return false;
166
- }
167
-
168
123
  let parsed;
169
124
  try {
170
125
  parsed = parseArgs(process.argv.slice(2));
@@ -208,9 +163,6 @@ const child = spawn(process.execPath, [nextBin, ...nextArgs], {
208
163
  ACP_UI_DEFAULT_WORKSPACE_PATH:
209
164
  process.env.ACP_UI_DEFAULT_WORKSPACE_PATH || launchCwd,
210
165
  ...buildServerRuntimeEnv(parsed.options),
211
- ...(hasCodexCommandOverride(process.env)
212
- ? {}
213
- : { ACP_CODEX_COMMAND: codexAcpCommandLine() }),
214
166
  },
215
167
  stdio: "inherit",
216
168
  });
@@ -1,8 +1,8 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
- import { ComposerPrimitive, useAui, useAuiState, } from "@assistant-ui/react";
3
+ import { ComposerPrimitive, useAui, useAuiState, unstable_useTriggerPopoverScopeContext as useTriggerPopoverScopeContext, } from "@assistant-ui/react";
4
4
  import { SlashIcon, SparklesIcon } from "lucide-react";
5
- import { useEffect, useMemo, useRef, useState } from "react";
5
+ import { useCallback, useEffect, useMemo, useRef, useState, } from "react";
6
6
  import { Button } from "../ui/button.mjs";
7
7
  import { useAcpUiRuntimeContext } from "../../lib/acp2aisdk/client-context.mjs";
8
8
  import { persistSkillCommandCache, readSkillCommandCache, shouldFetchSkillCommands, } from "../../lib/acp2aisdk/skill-command-cache.mjs";
@@ -107,7 +107,6 @@ export function AcpCommandTriggerButton() {
107
107
  }, children: _jsx(SlashIcon, { className: "size-4" }) }));
108
108
  }
109
109
  export function AcpCommandMenu() {
110
- const aui = useAui();
111
110
  const { provider, workspacePath } = useAcpUiRuntimeContext();
112
111
  const threadId = useAuiState((state) => state.threads.mainThreadId);
113
112
  const messages = useAuiState((state) => state.thread.messages);
@@ -164,12 +163,46 @@ export function AcpCommandMenu() {
164
163
  setCommands(nextCommands);
165
164
  }, [provider, runtimeCommands, workspacePath]);
166
165
  const adapter = useMemo(() => createCommandAdapter(commands), [commands]);
167
- return (_jsxs(ComposerPrimitive.Unstable_TriggerPopover, { char: "/", adapter: adapter, className: "absolute bottom-full left-0 z-50 mb-3 w-[min(26rem,calc(100vw-2rem))] overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md", children: [_jsx(ComposerPrimitive.Unstable_TriggerPopover.Action, { removeOnExecute: true, onExecute: (item) => {
168
- var _a;
169
- const command = commandsRef.current.find((candidate) => candidate.name === item.id);
170
- const nextText = (_a = command === null || command === void 0 ? void 0 : command.name) !== null && _a !== void 0 ? _a : item.label;
171
- aui.thread().composer().setText(`${nextText} `);
172
- } }), _jsx("div", { className: "max-h-80 overflow-y-auto p-1.5", children: _jsx(ComposerPrimitive.Unstable_TriggerPopoverItems, { children: (items) => items.length ? (items.map((item, index) => (_jsxs(ComposerPrimitive.Unstable_TriggerPopoverItem, { item: item, index: index, className: cn("flex min-h-10 w-full items-start gap-2 rounded-sm px-2 py-1.5 text-left text-sm hover:bg-accent hover:text-accent-foreground data-[highlighted=true]:bg-accent"), children: [_jsx(SparklesIcon, { className: "mt-0.5 size-4 shrink-0 text-muted-foreground" }), _jsxs("span", { className: "min-w-0 flex-1", children: [_jsx("span", { className: "block truncate font-mono", children: item.label }), item.description ? (_jsx("span", { className: "block truncate text-muted-foreground text-xs", children: item.description })) : null] })] }, `${item.type}-${item.id}`)))) : (_jsx(EmptyCommandState, {})) }) })] }));
166
+ const popoverRef = useRef(null);
167
+ return (_jsxs(ComposerPrimitive.Unstable_TriggerPopover, { ref: popoverRef, char: "/", adapter: adapter, className: "absolute bottom-full left-0 z-50 mb-3 w-[min(26rem,calc(100vw-2rem))] overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md", children: [_jsx(AcpCommandPopoverDismiss, { popoverRef: popoverRef }), _jsx(AcpCommandAction, { commandsRef: commandsRef }), _jsx("div", { className: "max-h-80 overflow-y-auto p-1.5", children: _jsx(ComposerPrimitive.Unstable_TriggerPopoverItems, { children: (items) => items.length ? (items.map((item, index) => (_jsxs(ComposerPrimitive.Unstable_TriggerPopoverItem, { item: item, index: index, className: cn("flex min-h-10 w-full items-start gap-2 rounded-sm px-2 py-1.5 text-left text-sm hover:bg-accent hover:text-accent-foreground data-[highlighted=true]:bg-accent"), children: [_jsx(SparklesIcon, { className: "mt-0.5 size-4 shrink-0 text-muted-foreground" }), _jsxs("span", { className: "min-w-0 flex-1", children: [_jsx("span", { className: "block truncate font-mono", children: item.label }), item.description ? (_jsx("span", { className: "block truncate text-muted-foreground text-xs", children: item.description })) : null] })] }, `${item.type}-${item.id}`)))) : (_jsx(EmptyCommandState, {})) }) })] }));
168
+ }
169
+ function AcpCommandPopoverDismiss({ popoverRef, }) {
170
+ const { close, open } = useTriggerPopoverScopeContext();
171
+ useEffect(() => {
172
+ if (!open)
173
+ return;
174
+ function isInside(target) {
175
+ var _a;
176
+ return target instanceof Node && ((_a = popoverRef.current) === null || _a === void 0 ? void 0 : _a.contains(target));
177
+ }
178
+ function handlePointerDown(event) {
179
+ if (!isInside(event.target))
180
+ close();
181
+ }
182
+ function handleFocusIn(event) {
183
+ if (!isInside(event.target))
184
+ close();
185
+ }
186
+ document.addEventListener("pointerdown", handlePointerDown);
187
+ document.addEventListener("focusin", handleFocusIn);
188
+ return () => {
189
+ document.removeEventListener("pointerdown", handlePointerDown);
190
+ document.removeEventListener("focusin", handleFocusIn);
191
+ };
192
+ }, [close, open, popoverRef]);
193
+ return null;
194
+ }
195
+ function AcpCommandAction({ commandsRef, }) {
196
+ const aui = useAui();
197
+ const { close } = useTriggerPopoverScopeContext();
198
+ const handleExecute = useCallback((item) => {
199
+ var _a;
200
+ const command = commandsRef.current.find((candidate) => candidate.name === item.id);
201
+ const nextText = (_a = command === null || command === void 0 ? void 0 : command.name) !== null && _a !== void 0 ? _a : item.label;
202
+ aui.thread().composer().setText(`${nextText} `);
203
+ close();
204
+ }, [aui, close, commandsRef]);
205
+ return (_jsx(ComposerPrimitive.Unstable_TriggerPopover.Action, { removeOnExecute: true, onExecute: handleExecute }));
173
206
  }
174
207
  function EmptyCommandState() {
175
208
  return (_jsx("div", { className: "px-3 py-4 text-center text-muted-foreground text-sm", children: "\u6682\u65E0\u53EF\u7528\u547D\u4EE4" }));
@@ -131,8 +131,8 @@ export function AcpElicitationOptionListCard(part) {
131
131
  : "text-primary") }), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsx("div", { className: "break-words font-medium text-base leading-snug", children: payload.message }), _jsxs("div", { className: "mt-1 flex flex-wrap items-center gap-x-2 gap-y-1 text-muted-foreground text-xs", children: [_jsx("span", { children: payload.fieldTitle }), _jsx("span", { "aria-hidden": true, children: "\u00B7" }), _jsx("span", { children: statusLabel })] }), payload.fieldDescription && (_jsx("div", { className: "mt-2 break-words text-muted-foreground text-sm leading-snug", children: payload.fieldDescription }))] })] }), isReceipt ? (_jsxs("div", { className: cn("flex items-center gap-2 rounded-md border px-3 py-2 text-sm", isAcceptedReceipt
132
132
  ? "border-emerald-200 bg-emerald-50 text-emerald-900 dark:border-emerald-900/50 dark:bg-emerald-950/30 dark:text-emerald-200"
133
133
  : "border-muted-foreground/20 bg-muted/40 text-muted-foreground"), children: [isAcceptedReceipt ? (_jsx(CheckIcon, { className: "size-4 shrink-0" })) : (_jsx(XCircleIcon, { className: "size-4 shrink-0" })), _jsx("span", { className: "min-w-0 break-words", children: getReceiptText(payload, receiptAction, displayedChoice) })] })) : (_jsx(OptionList, { id: payload.toolCallId || part.toolCallId, options: payload.options, selectionMode: payload.selectionMode, defaultValue: payload.defaultValue, minSelections: payload.minSelections, maxSelections: payload.maxSelections, className: "my-0 border-0 bg-transparent p-0 shadow-none", actions: [
134
- { id: "confirm", label: "确认" },
135
134
  { id: "cancel", label: "取消", variant: "outline" },
135
+ { id: "confirm", label: "确认" },
136
136
  ], onAction: (actionId, selectedValue) => {
137
137
  if (disabled)
138
138
  return;
@@ -32,9 +32,7 @@ function getOptionLabel(options, value) {
32
32
  const PROVIDER_OPTIONS = ACP_PROVIDER_KEYS.map((provider, index) => ({
33
33
  value: provider,
34
34
  label: ACP_PROVIDER_REGISTRY[provider].label,
35
- description: ACP_PROVIDER_REGISTRY[provider].enabledInP0
36
- ? "已验证"
37
- : "需本地 CLI/登录",
35
+ description: "需先安装并登录",
38
36
  order: index,
39
37
  }));
40
38
  function capabilityOptions(snapshot, key) {
@@ -15,7 +15,7 @@ import { cva } from "class-variance-authority";
15
15
  import { BracesIcon, DownloadIcon, FileIcon, FileTextIcon, ImageIcon, MusicIcon, VideoIcon, } from "lucide-react";
16
16
  import { memo } from "react";
17
17
  import { cn } from "../../lib/utils.mjs";
18
- const fileVariants = cva("aui-file-root inline-flex items-center gap-3 rounded-lg transition-colors", {
18
+ const fileVariants = cva("aui-file-root inline-flex max-w-full items-center gap-3 rounded-lg transition-colors", {
19
19
  variants: {
20
20
  variant: {
21
21
  outline: "border-border border hover:bg-muted/50",
@@ -95,7 +95,7 @@ function FileDownload(_a) {
95
95
  }
96
96
  const FileImpl = ({ filename, data, mimeType }) => {
97
97
  const bytes = getBase64Size(data);
98
- return (_jsxs(FileRoot, { children: [_jsx(FileIconDisplay, { mimeType: mimeType }), _jsxs("div", { className: "flex min-w-0 flex-1 flex-col gap-0.5", children: [_jsx(FileName, { children: filename }), _jsx(FileSize, { bytes: bytes, className: "text-xs" })] }), _jsx(FileDownload, Object.assign({ data: data, mimeType: mimeType }, (filename !== undefined && { filename })))] }));
98
+ return (_jsxs(FileRoot, { className: "flex w-fit", children: [_jsx(FileIconDisplay, { mimeType: mimeType }), _jsxs("div", { className: "flex min-w-0 flex-1 flex-col gap-0.5", children: [_jsx(FileName, { children: filename }), _jsx(FileSize, { bytes: bytes, className: "text-xs" })] }), _jsx(FileDownload, Object.assign({ data: data, mimeType: mimeType }, (filename !== undefined && { filename })))] }));
99
99
  };
100
100
  const File = memo(FileImpl);
101
101
  File.displayName = "File";
@@ -6,22 +6,41 @@ import { Label } from "../ui/label.mjs";
6
6
  import { Switch } from "../ui/switch.mjs";
7
7
  import { Tabs, TabsContent, TabsList, TabsTrigger } from "../ui/tabs.mjs";
8
8
  import { useAcpUiRuntimeContext } from "../../lib/acp2aisdk/client-context.mjs";
9
- import { normalizeImageGenerationToolSettings } from "../../tools/image-generation/client.mjs";
10
- import { IMAGE_GENERATION_TOOL_ID } from "../../tools/image-generation/shared.mjs";
9
+ import { normalizeImageGenerationToolSettings, } from "../../tools/image-generation/client.mjs";
10
+ import { hasCompleteImageGenerationRuntimeSettings, IMAGE_GENERATION_TOOL_ID, normalizeImageGenerationRuntimeSettings, } from "../../tools/image-generation/shared.mjs";
11
11
  import { normalizeUserChoiceToolSettings } from "../../tools/user-choice/client.mjs";
12
12
  import { USER_CHOICE_TOOL_ID } from "../../tools/user-choice/shared.mjs";
13
13
  export function ImageGenerationSettingsDialog({ trigger, }) {
14
14
  var _a, _b, _c;
15
- const { builtinToolSettings, setBuiltinToolSettings } = useAcpUiRuntimeContext();
16
- const imageGenerationToolSettings = normalizeImageGenerationToolSettings(builtinToolSettings[IMAGE_GENERATION_TOOL_ID]);
15
+ const { builtinToolSettings, hostImageGenerationSettings, runtimeBuiltinToolSettings, setBuiltinToolSettings, setHostImageGenerationSettings, } = useAcpUiRuntimeContext();
16
+ const hasHostImageGenerationSettings = Boolean(hostImageGenerationSettings);
17
+ const imageGenerationToolSettings = normalizeImageGenerationToolSettings(runtimeBuiltinToolSettings[IMAGE_GENERATION_TOOL_ID]);
18
+ const imageGenerationSettingsComplete = hasCompleteImageGenerationRuntimeSettings(imageGenerationToolSettings);
17
19
  const userChoiceToolSettings = normalizeUserChoiceToolSettings(builtinToolSettings[USER_CHOICE_TOOL_ID]);
20
+ const updateImageGenerationSettings = (updater) => {
21
+ if (hasHostImageGenerationSettings) {
22
+ setHostImageGenerationSettings((current) => {
23
+ var _a;
24
+ return updater(normalizeImageGenerationToolSettings(Object.assign(Object.assign({}, imageGenerationToolSettings), ((_a = normalizeImageGenerationRuntimeSettings(current)) !== null && _a !== void 0 ? _a : {}))));
25
+ });
26
+ return;
27
+ }
28
+ setBuiltinToolSettings(IMAGE_GENERATION_TOOL_ID, (current) => updater(normalizeImageGenerationToolSettings(current)));
29
+ };
18
30
  const updateSetting = (key, value) => {
19
- setBuiltinToolSettings(IMAGE_GENERATION_TOOL_ID, (current) => (Object.assign(Object.assign({}, normalizeImageGenerationToolSettings(current)), { [key]: value })));
31
+ updateImageGenerationSettings((current) => (Object.assign(Object.assign({}, current), { [key]: value })));
32
+ };
33
+ const updateConnectionSetting = (key, value) => {
34
+ updateImageGenerationSettings((current) => {
35
+ const next = Object.assign(Object.assign({}, current), { [key]: value });
36
+ return Object.assign(Object.assign({}, next), { enabled: next.enabled && hasCompleteImageGenerationRuntimeSettings(next) });
37
+ });
20
38
  };
21
39
  const updateUserChoiceSetting = (key, value) => {
22
40
  setBuiltinToolSettings(USER_CHOICE_TOOL_ID, (current) => (Object.assign(Object.assign({}, normalizeUserChoiceToolSettings(current)), { [key]: value })));
23
41
  };
24
- return (_jsxs(Dialog, { children: [_jsx(DialogTrigger, { asChild: true, children: trigger }), _jsxs(DialogContent, { className: "sm:max-w-xl", children: [_jsxs(DialogHeader, { children: [_jsx(DialogTitle, { children: "\u5DE5\u5177\u8BBE\u7F6E" }), _jsx(DialogDescription, { children: "\u914D\u7F6E\u5185\u7F6E\u5DE5\u5177\u7684\u8FDE\u63A5\u4FE1\u606F\u548C\u4EA4\u4E92\u884C\u4E3A\u3002" })] }), _jsxs(Tabs, { defaultValue: "image-generation", children: [_jsxs(TabsList, { className: "grid w-full grid-cols-2", children: [_jsx(TabsTrigger, { value: "image-generation", children: "\u56FE\u7247\u751F\u6210" }), _jsx(TabsTrigger, { value: "user-choice", children: "\u7528\u6237\u9009\u62E9" })] }), _jsxs(TabsContent, { value: "image-generation", className: "grid gap-4", children: [_jsxs("div", { className: "flex items-center justify-between gap-3 rounded-md border px-3 py-2", children: [_jsxs("div", { className: "min-w-0", children: [_jsx(Label, { htmlFor: "image-generation-enabled", children: "\u56FE\u7247\u751F\u6210\u5DE5\u5177" }), _jsx("p", { className: "mt-1 text-muted-foreground text-xs", children: "\u5141\u8BB8\u6A21\u578B\u901A\u8FC7 MCP \u8C03\u7528\u56FE\u7247\u751F\u6210\u3002" })] }), _jsx(Switch, { id: "image-generation-enabled", checked: imageGenerationToolSettings.enabled, onCheckedChange: (checked) => updateSetting("enabled", checked) })] }), _jsx(SettingsInput, { id: "image-generation-base-url", label: "API Base URL", placeholder: "https://api.openai.com/v1", value: (_a = imageGenerationToolSettings.baseUrl) !== null && _a !== void 0 ? _a : "", onValueChange: (value) => updateSetting("baseUrl", value) }), _jsx(SettingsInput, { id: "image-generation-api-key", label: "API Key", placeholder: "sk-...", type: "password", value: (_b = imageGenerationToolSettings.apiKey) !== null && _b !== void 0 ? _b : "", onValueChange: (value) => updateSetting("apiKey", value) }), _jsx(SettingsInput, { id: "image-generation-model", label: "\u6A21\u578B", placeholder: "gpt-image-1", value: (_c = imageGenerationToolSettings.model) !== null && _c !== void 0 ? _c : "", onValueChange: (value) => updateSetting("model", value) }), _jsxs("div", { className: "flex items-center justify-between gap-3 rounded-md border px-3 py-2", children: [_jsxs("div", { className: "min-w-0", children: [_jsx(Label, { htmlFor: "image-generation-preserve-prompt", children: "\u4FDD\u7559\u539F\u59CB\u63D0\u793A\u8BCD" }), _jsx("p", { className: "mt-1 text-muted-foreground text-xs", children: "\u8BF7\u6C42\u56FE\u7247 API \u65F6\u5728 prompt \u524D\u52A0\u5165\u4E0D\u8981\u6539\u5199\u63D0\u793A\uFF0C\u8BB0\u5F55\u4ECD\u663E\u793A\u539F\u59CB\u63D0\u793A\u8BCD\u3002" })] }), _jsx(Switch, { id: "image-generation-preserve-prompt", checked: imageGenerationToolSettings.preservePrompt, onCheckedChange: (checked) => updateSetting("preservePrompt", checked) })] })] }), _jsxs(TabsContent, { value: "user-choice", className: "grid gap-4", children: [_jsxs("div", { className: "flex items-center justify-between gap-3 rounded-md border px-3 py-2", children: [_jsxs("div", { className: "min-w-0", children: [_jsx(Label, { htmlFor: "user-choice-enabled", children: "\u7528\u6237\u9009\u62E9\u5DE5\u5177" }), _jsx("p", { className: "mt-1 text-muted-foreground text-xs", children: "\u5141\u8BB8\u6A21\u578B\u901A\u8FC7 MCP \u53D1\u8D77\u5355\u9009\u3001\u591A\u9009\u6216\u591A\u9898\u9009\u62E9\u3002" })] }), _jsx(Switch, { id: "user-choice-enabled", checked: userChoiceToolSettings.enabled, onCheckedChange: (checked) => updateUserChoiceSetting("enabled", checked) })] }), _jsx(SettingsInput, { id: "user-choice-timeout-ms", label: "\u7B49\u5F85\u8D85\u65F6\uFF08\u6BEB\u79D2\uFF09", type: "number", value: String(userChoiceToolSettings.waitTimeoutMs), onValueChange: (value) => updateUserChoiceSetting("waitTimeoutMs", Number.parseInt(value, 10)) }), _jsxs("div", { className: "flex items-center justify-between gap-3", children: [_jsx(Label, { htmlFor: "user-choice-custom-input", children: "\u5141\u8BB8\u81EA\u5B9A\u4E49\u8F93\u5165" }), _jsx(Switch, { id: "user-choice-custom-input", checked: userChoiceToolSettings.allowCustomInput, onCheckedChange: (checked) => updateUserChoiceSetting("allowCustomInput", checked) })] }), _jsx(SettingsInput, { id: "user-choice-custom-input-label", label: "\u81EA\u5B9A\u4E49\u9009\u9879\u6587\u6848", value: userChoiceToolSettings.customInputLabel, onValueChange: (value) => updateUserChoiceSetting("customInputLabel", value) })] })] }), _jsx(DialogFooter, { showCloseButton: true })] })] }));
42
+ return (_jsxs(Dialog, { children: [_jsx(DialogTrigger, { asChild: true, children: trigger }), _jsxs(DialogContent, { className: "sm:max-w-xl", children: [_jsxs(DialogHeader, { children: [_jsx(DialogTitle, { children: "\u5DE5\u5177\u8BBE\u7F6E" }), _jsx(DialogDescription, { children: "\u914D\u7F6E\u5185\u7F6E\u5DE5\u5177\u7684\u8FDE\u63A5\u4FE1\u606F\u548C\u4EA4\u4E92\u884C\u4E3A\u3002" })] }), _jsxs(Tabs, { defaultValue: "image-generation", children: [_jsxs(TabsList, { className: "grid w-full grid-cols-2", children: [_jsx(TabsTrigger, { value: "image-generation", children: "\u56FE\u7247\u751F\u6210" }), _jsx(TabsTrigger, { value: "user-choice", children: "\u7528\u6237\u9009\u62E9" })] }), _jsxs(TabsContent, { value: "image-generation", className: "grid gap-4", children: [_jsxs("div", { className: "flex items-center justify-between gap-3 rounded-md border px-3 py-2", children: [_jsxs("div", { className: "min-w-0", children: [_jsx(Label, { htmlFor: "image-generation-enabled", children: "\u56FE\u7247\u751F\u6210\u5DE5\u5177" }), _jsx("p", { className: "mt-1 text-muted-foreground text-xs", children: "\u586B\u5199 API Base URL\u3001API Key \u548C\u6A21\u578B\u540E\u53EF\u5F00\u542F\u3002" })] }), _jsx(Switch, { id: "image-generation-enabled", checked: imageGenerationToolSettings.enabled &&
43
+ imageGenerationSettingsComplete, disabled: !imageGenerationSettingsComplete, onCheckedChange: (checked) => updateSetting("enabled", checked) })] }), _jsx(SettingsInput, { id: "image-generation-base-url", label: "API Base URL", placeholder: "https://api.openai.com/v1", value: (_a = imageGenerationToolSettings.baseUrl) !== null && _a !== void 0 ? _a : "", onValueChange: (value) => updateConnectionSetting("baseUrl", value) }), _jsx(SettingsInput, { id: "image-generation-api-key", label: "API Key", placeholder: "sk-...", type: "password", value: (_b = imageGenerationToolSettings.apiKey) !== null && _b !== void 0 ? _b : "", onValueChange: (value) => updateConnectionSetting("apiKey", value) }), _jsx(SettingsInput, { id: "image-generation-model", label: "\u6A21\u578B", placeholder: "gpt-image-1", value: (_c = imageGenerationToolSettings.model) !== null && _c !== void 0 ? _c : "", onValueChange: (value) => updateConnectionSetting("model", value) }), _jsxs("div", { className: "flex items-center justify-between gap-3 rounded-md border px-3 py-2", children: [_jsxs("div", { className: "min-w-0", children: [_jsx(Label, { htmlFor: "image-generation-preserve-prompt", children: "\u4FDD\u7559\u539F\u59CB\u63D0\u793A\u8BCD" }), _jsx("p", { className: "mt-1 text-muted-foreground text-xs", children: "\u8BF7\u6C42\u56FE\u7247 API \u65F6\u5728 prompt \u524D\u52A0\u5165\u4E0D\u8981\u6539\u5199\u63D0\u793A\uFF0C\u8BB0\u5F55\u4ECD\u663E\u793A\u539F\u59CB\u63D0\u793A\u8BCD\u3002" })] }), _jsx(Switch, { id: "image-generation-preserve-prompt", checked: imageGenerationToolSettings.preservePrompt, onCheckedChange: (checked) => updateSetting("preservePrompt", checked) })] })] }), _jsxs(TabsContent, { value: "user-choice", className: "grid gap-4", children: [_jsxs("div", { className: "flex items-center justify-between gap-3 rounded-md border px-3 py-2", children: [_jsxs("div", { className: "min-w-0", children: [_jsx(Label, { htmlFor: "user-choice-enabled", children: "\u7528\u6237\u9009\u62E9\u5DE5\u5177" }), _jsx("p", { className: "mt-1 text-muted-foreground text-xs", children: "\u5141\u8BB8\u6A21\u578B\u901A\u8FC7 MCP \u53D1\u8D77\u5355\u9009\u3001\u591A\u9009\u6216\u591A\u9898\u9009\u62E9\u3002" })] }), _jsx(Switch, { id: "user-choice-enabled", checked: userChoiceToolSettings.enabled, onCheckedChange: (checked) => updateUserChoiceSetting("enabled", checked) })] }), _jsx(SettingsInput, { id: "user-choice-timeout-ms", label: "\u7B49\u5F85\u8D85\u65F6\uFF08\u6BEB\u79D2\uFF09", type: "number", value: String(userChoiceToolSettings.waitTimeoutMs), onValueChange: (value) => updateUserChoiceSetting("waitTimeoutMs", Number.parseInt(value, 10)) }), _jsxs("div", { className: "flex items-center justify-between gap-3", children: [_jsx(Label, { htmlFor: "user-choice-custom-input", children: "\u5141\u8BB8\u81EA\u5B9A\u4E49\u8F93\u5165" }), _jsx(Switch, { id: "user-choice-custom-input", checked: userChoiceToolSettings.allowCustomInput, onCheckedChange: (checked) => updateUserChoiceSetting("allowCustomInput", checked) })] }), _jsx(SettingsInput, { id: "user-choice-custom-input-label", label: "\u81EA\u5B9A\u4E49\u9009\u9879\u6587\u6848", value: userChoiceToolSettings.customInputLabel, onValueChange: (value) => updateUserChoiceSetting("customInputLabel", value) })] })] }), _jsx(DialogFooter, { showCloseButton: true })] })] }));
25
44
  }
26
45
  function SettingsInput({ id, label, value, onValueChange, placeholder, type = "text", }) {
27
46
  return (_jsxs("div", { className: "grid gap-2", children: [_jsx(Label, { htmlFor: id, children: label }), _jsx(Input, { id: id, type: type, value: value, placeholder: placeholder, onChange: (event) => onValueChange(event.currentTarget.value) })] }));
@@ -81,7 +81,7 @@ const copyImagePart = async (part) => {
81
81
  const mime = (_b = (_a = mimeFromImage(part.image)) !== null && _a !== void 0 ? _a : blob.type) !== null && _b !== void 0 ? _b : "image/png";
82
82
  await navigator.clipboard.write([new ClipboardItem({ [mime]: blob })]);
83
83
  };
84
- const imageVariants = cva("aui-image-root relative overflow-hidden rounded-lg", {
84
+ const imageVariants = cva("aui-image-root relative w-fit max-w-full overflow-hidden rounded-lg", {
85
85
  variants: {
86
86
  variant: {
87
87
  outline: "border-border border",
@@ -90,7 +90,7 @@ const imageVariants = cva("aui-image-root relative overflow-hidden rounded-lg",
90
90
  },
91
91
  size: {
92
92
  sm: "max-w-64",
93
- default: "inline-block max-w-[30vw] max-md:max-w-full",
93
+ default: "max-w-[30vw] max-md:max-w-full",
94
94
  lg: "max-w-[512px]",
95
95
  full: "w-full",
96
96
  },
@@ -97,7 +97,7 @@ const defaultComponents = memoizeMarkdownComponents({
97
97
  },
98
98
  p: (_a) => {
99
99
  var { className } = _a, props = __rest(_a, ["className"]);
100
- return (_jsx("div", Object.assign({ className: cn("aui-md-p my-2.5 leading-[1.7] first:mt-0 last:mb-0", className) }, props)));
100
+ return (_jsx("div", Object.assign({ className: cn("aui-md-p my-2 leading-[1.6] first:mt-0 last:mb-0 md:my-2.5 md:leading-[1.7]", className) }, props)));
101
101
  },
102
102
  a: (_a) => {
103
103
  var { className, href, children } = _a, props = __rest(_a, ["className", "href", "children"]);
@@ -159,7 +159,7 @@ const defaultComponents = memoizeMarkdownComponents({
159
159
  },
160
160
  li: (_a) => {
161
161
  var { className } = _a, props = __rest(_a, ["className"]);
162
- return (_jsx("li", Object.assign({ className: cn("aui-md-li leading-[1.7]", className) }, props)));
162
+ return (_jsx("li", Object.assign({ className: cn("aui-md-li leading-[1.6] md:leading-[1.7]", className) }, props)));
163
163
  },
164
164
  sup: (_a) => {
165
165
  var { className } = _a, props = __rest(_a, ["className"]);
@@ -199,7 +199,7 @@ function LocalFile({ path, className }) {
199
199
  body: JSON.stringify({ path }),
200
200
  }).catch(() => { });
201
201
  };
202
- return (_jsxs(File.Root, { role: "button", tabIndex: 0, className: cn("my-2 cursor-pointer", className), onClick: openFile, onKeyDown: (event) => {
202
+ return (_jsxs(File.Root, { role: "button", tabIndex: 0, className: cn("my-1.5 flex w-fit cursor-pointer", className), onClick: openFile, onKeyDown: (event) => {
203
203
  if (event.key === "Enter" || event.key === " ") {
204
204
  event.preventDefault();
205
205
  openFile();
@@ -1,5 +1,5 @@
1
1
  import { ComposerPrimitive } from "@assistant-ui/react";
2
- import type { ComponentPropsWithoutRef, FC, ReactNode } from "react";
2
+ import { type ComponentPropsWithoutRef, type FC, type ReactNode } from "react";
3
3
  type ComposerInputProps = ComponentPropsWithoutRef<typeof ComposerPrimitive.Input>;
4
4
  export type ComposerProps = {
5
5
  placeholder?: string;
@@ -1,15 +1,49 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
- import { AuiIf, ComposerPrimitive } from "@assistant-ui/react";
3
+ import { AuiIf, ComposerPrimitive, useAui, useAuiState, } from "@assistant-ui/react";
4
4
  import { ArrowUpIcon, SquareIcon } from "lucide-react";
5
+ import { useCallback, } from "react";
5
6
  import { AcpCommandMenu } from "../acp-command-menu.mjs";
6
7
  import { AcpComposerSelectors } from "../acp-selectors.mjs";
7
8
  import { ComposerAddAttachment, ComposerAttachments, } from "../attachment.mjs";
8
9
  import { TooltipIconButton } from "../tooltip-icon-button.mjs";
9
10
  import { Button } from "../../ui/button.mjs";
11
+ import { useAcpUiRuntimeContext } from "../../../lib/acp2aisdk/client-context.mjs";
12
+ import { acpApiClient } from "../../../lib/api/client.mjs";
10
13
  export const Composer = ({ addAttachmentLabel, ariaLabel = "消息输入框", leadingActions, onPaste, placeholder = "输入 / 使用技能,或直接发送消息...", sendLabel, showAttachments = true, showCommandMenu = true, showSelectors = true, trailingActions, triggerPopovers, }) => {
11
- return (_jsx(ComposerPrimitive.Root, { className: "aui-composer-root relative flex w-full flex-col", children: _jsx(ComposerPrimitive.AttachmentDropzone, { asChild: true, children: _jsxs("div", { "data-slot": "aui_composer-shell", className: "flex w-full flex-col gap-2 rounded-(--composer-radius) border bg-background p-(--composer-padding) transition-shadow focus-within:border-ring/75 focus-within:ring-2 focus-within:ring-ring/20 data-[dragging=true]:border-ring data-[dragging=true]:border-dashed data-[dragging=true]:bg-accent/50", children: [_jsxs(ComposerPrimitive.Unstable_TriggerPopoverRoot, { children: [showAttachments ? _jsx(ComposerAttachments, {}) : null, _jsx(ComposerPrimitive.Input, { placeholder: placeholder, className: "aui-composer-input max-h-32 min-h-10 w-full resize-none bg-transparent px-1.75 py-1 text-[13px] outline-none placeholder:text-muted-foreground/80 md:text-sm", rows: 1, autoFocus: true, "aria-label": ariaLabel, onPaste: onPaste }), showCommandMenu ? _jsx(AcpCommandMenu, {}) : null, triggerPopovers] }), _jsx(ComposerAction, { addAttachmentLabel: addAttachmentLabel, leadingActions: leadingActions, sendLabel: sendLabel, showAddAttachment: showAttachments, showSelectors: showSelectors, trailingActions: trailingActions })] }) }) }));
14
+ const aui = useAui();
15
+ const cancelActiveChatRun = useCancelActiveChatRun();
16
+ const handleInputKeyDown = useCallback((event) => {
17
+ if (event.key !== "Escape")
18
+ return;
19
+ const composer = aui.composer();
20
+ queueMicrotask(() => {
21
+ if (event.defaultPrevented)
22
+ return;
23
+ if (!composer.getState().canCancel)
24
+ return;
25
+ cancelActiveChatRun();
26
+ composer.cancel();
27
+ });
28
+ }, [aui, cancelActiveChatRun]);
29
+ return (_jsx(ComposerPrimitive.Root, { className: "aui-composer-root relative flex w-full flex-col", children: _jsx(ComposerPrimitive.AttachmentDropzone, { asChild: true, children: _jsxs("div", { "data-slot": "aui_composer-shell", className: "flex w-full flex-col gap-2 rounded-(--composer-radius) border bg-background p-(--composer-padding) transition-shadow focus-within:border-ring/75 focus-within:ring-2 focus-within:ring-ring/20 data-[dragging=true]:border-ring data-[dragging=true]:border-dashed data-[dragging=true]:bg-accent/50", children: [_jsxs(ComposerPrimitive.Unstable_TriggerPopoverRoot, { children: [showAttachments ? _jsx(ComposerAttachments, {}) : null, _jsx(ComposerPrimitive.Input, { placeholder: placeholder, className: "aui-composer-input max-h-32 min-h-10 w-full resize-none bg-transparent px-1.75 py-1 text-[13px] outline-none placeholder:text-muted-foreground/80 md:text-sm", rows: 1, autoFocus: true, "aria-label": ariaLabel, cancelOnEscape: false, onKeyDown: handleInputKeyDown, onPaste: onPaste }), showCommandMenu ? _jsx(AcpCommandMenu, {}) : null, triggerPopovers] }), _jsx(ComposerAction, { addAttachmentLabel: addAttachmentLabel, leadingActions: leadingActions, sendLabel: sendLabel, showAddAttachment: showAttachments, showSelectors: showSelectors, trailingActions: trailingActions })] }) }) }));
12
30
  };
13
31
  export const ComposerAction = ({ addAttachmentLabel, leadingActions, sendLabel = "发送消息", showAddAttachment = true, showSelectors = true, trailingActions, }) => {
14
- return (_jsxs("div", { className: "aui-composer-action-wrapper relative flex items-center justify-between text-[13px] md:text-sm", children: [_jsxs("div", { className: "flex min-w-0 items-center gap-1", children: [showAddAttachment ? (_jsx(ComposerAddAttachment, { label: addAttachmentLabel })) : null, showSelectors ? _jsx(AcpComposerSelectors, {}) : null, leadingActions] }), _jsxs("div", { className: "flex items-center gap-1", children: [trailingActions, _jsx(AuiIf, { condition: (s) => !s.thread.isRunning, children: _jsx(ComposerPrimitive.Send, { asChild: true, children: _jsx(TooltipIconButton, { tooltip: sendLabel, side: "bottom", type: "button", variant: "default", size: "icon", className: "aui-composer-send size-8 rounded-full", "aria-label": sendLabel, children: _jsx(ArrowUpIcon, { className: "aui-composer-send-icon size-4" }) }) }) }), _jsx(AuiIf, { condition: (s) => s.thread.isRunning, children: _jsx(ComposerPrimitive.Cancel, { asChild: true, children: _jsx(Button, { type: "button", variant: "default", size: "icon", className: "aui-composer-cancel size-8 rounded-full", "aria-label": "\u505C\u6B62\u751F\u6210", children: _jsx(SquareIcon, { className: "aui-composer-cancel-icon size-3 fill-current" }) }) }) })] })] }));
32
+ const cancelActiveChatRun = useCancelActiveChatRun();
33
+ return (_jsxs("div", { className: "aui-composer-action-wrapper relative flex items-center justify-between text-[13px] md:text-sm", children: [_jsxs("div", { className: "flex min-w-0 items-center gap-1", children: [showAddAttachment ? (_jsx(ComposerAddAttachment, { label: addAttachmentLabel })) : null, showSelectors ? _jsx(AcpComposerSelectors, {}) : null, leadingActions] }), _jsxs("div", { className: "flex items-center gap-1", children: [trailingActions, _jsx(AuiIf, { condition: (s) => !s.thread.isRunning, children: _jsx(ComposerPrimitive.Send, { asChild: true, children: _jsx(TooltipIconButton, { tooltip: sendLabel, side: "bottom", type: "button", variant: "default", size: "icon", className: "aui-composer-send size-8 rounded-full", "aria-label": sendLabel, children: _jsx(ArrowUpIcon, { className: "aui-composer-send-icon size-4" }) }) }) }), _jsx(AuiIf, { condition: (s) => s.thread.isRunning, children: _jsx(ComposerPrimitive.Cancel, { asChild: true, children: _jsx(Button, { type: "button", variant: "default", size: "icon", className: "aui-composer-cancel size-8 rounded-full", "aria-label": "\u505C\u6B62\u751F\u6210", onClick: () => {
34
+ cancelActiveChatRun();
35
+ }, children: _jsx(SquareIcon, { className: "aui-composer-cancel-icon size-3 fill-current" }) }) }) })] })] }));
15
36
  };
37
+ function useCancelActiveChatRun() {
38
+ const { provider, workspacePath } = useAcpUiRuntimeContext();
39
+ const remoteId = useAuiState((state) => state.threadListItem.remoteId);
40
+ const mainThreadId = useAuiState((state) => state.threads.mainThreadId);
41
+ const threadId = remoteId !== null && remoteId !== void 0 ? remoteId : mainThreadId;
42
+ return useCallback(() => {
43
+ void acpApiClient.cancelChat({
44
+ threadId,
45
+ provider,
46
+ workspacePath,
47
+ });
48
+ }, [provider, threadId, workspacePath]);
49
+ }
@@ -103,9 +103,10 @@ function getContextChipTooltip(item, workspacePath) {
103
103
  }
104
104
  export const ContextChips = () => {
105
105
  const { contextBundle, removeContextItems, workspacePath } = useAcpUiRuntimeContext();
106
- if (contextBundle.items.length === 0)
106
+ const visibleItems = contextBundle.items.filter((item) => item.hidden !== true);
107
+ if (visibleItems.length === 0)
107
108
  return null;
108
- return (_jsx("div", { className: "flex min-h-11 w-full items-center", children: _jsx("div", { className: "flex w-full flex-wrap items-center gap-1.5 px-1", children: contextBundle.items.map((item) => {
109
+ return (_jsx("div", { className: "flex min-h-11 w-full items-center", children: _jsx("div", { className: "flex w-full flex-wrap items-center gap-1.5 px-1", children: visibleItems.map((item) => {
109
110
  const id = item.id;
110
111
  if (!id)
111
112
  return null;
@@ -21,7 +21,7 @@ export const Thread = ({ isHistoryRoute = false }) => {
21
21
  ["--thread-max-width"]: "44rem",
22
22
  ["--composer-radius"]: "24px",
23
23
  ["--composer-padding"]: "10px",
24
- }, children: _jsx(ThreadPrimitive.Viewport, { turnAnchor: "top", "data-slot": "aui_thread-viewport", className: "relative flex flex-1 flex-col overflow-x-auto overflow-y-auto scroll-smooth", children: _jsxs("div", { className: "mx-auto flex w-full max-w-(--thread-max-width) flex-1 flex-col px-4 pt-4", children: [_jsx(AuiIf, { condition: () => showLoadingState, children: _jsx(ThreadHistoryLoading, {}) }), _jsx(AuiIf, { condition: () => messageCount === 0 && (!isHistoryRoute || showHistoryEmpty), children: _jsx(ThreadEmptyState, { isHistoryRoute: isHistoryRoute }) }), _jsx(MessageList, {}), _jsxs(ThreadPrimitive.ViewportFooter, { className: "aui-thread-viewport-footer sticky bottom-0 mt-auto flex flex-col gap-0 overflow-visible rounded-t-(--composer-radius) bg-background pb-4 md:pb-6", children: [_jsx(ThreadScrollToBottom, {}), _jsx(ContextChips, {}), _jsx(Composer, {})] })] }) }) }));
24
+ }, children: _jsx(ThreadPrimitive.Viewport, { turnAnchor: "top", "data-slot": "aui_thread-viewport", className: "relative flex flex-1 flex-col overflow-x-auto overflow-y-auto scroll-smooth", children: _jsxs("div", { className: "mx-auto flex w-full max-w-(--thread-max-width) flex-1 flex-col px-3 pt-14 sm:px-4 lg:pt-4", children: [_jsx(AuiIf, { condition: () => showLoadingState, children: _jsx(ThreadHistoryLoading, {}) }), _jsx(AuiIf, { condition: () => messageCount === 0 && (!isHistoryRoute || showHistoryEmpty), children: _jsx(ThreadEmptyState, { isHistoryRoute: isHistoryRoute }) }), _jsx(MessageList, {}), _jsxs(ThreadPrimitive.ViewportFooter, { className: "aui-thread-viewport-footer sticky bottom-0 mt-auto flex flex-col gap-0 overflow-visible rounded-t-(--composer-radius) bg-background pb-4 md:pb-6", children: [_jsx(ThreadScrollToBottom, {}), _jsx(ContextChips, {}), _jsx(Composer, {})] })] }) }) }));
25
25
  };
26
26
  function useDebouncedHistoryEmptyState({ isHistoryRoute, isLoading, messageCount, }) {
27
27
  const [showEmpty, setShowEmpty] = useState(false);
@@ -3,7 +3,7 @@ import { jsx as _jsx } from "react/jsx-runtime";
3
3
  import { ThreadPrimitive, useAuiState } from "@assistant-ui/react";
4
4
  import { AssistantMessage, EditComposer, UserMessage } from "./messages.mjs";
5
5
  export const MessageList = () => {
6
- return (_jsx("div", { "data-slot": "aui_message-group", className: "mb-10 flex flex-col gap-y-8 empty:hidden", children: _jsx(ThreadPrimitive.Messages, { children: () => _jsx(ThreadMessage, {}) }) }));
6
+ return (_jsx("div", { "data-slot": "aui_message-group", className: "mb-8 flex flex-col gap-y-6 empty:hidden md:mb-10 md:gap-y-8", children: _jsx(ThreadPrimitive.Messages, { children: () => _jsx(ThreadMessage, {}) }) }));
7
7
  };
8
8
  export const ThreadMessage = () => {
9
9
  const role = useAuiState((s) => s.message.role);
@@ -30,7 +30,7 @@ export const MessageError = () => {
30
30
  export const AssistantMessage = () => {
31
31
  const ACTION_BAR_PT = "pt-1.5";
32
32
  const ACTION_BAR_HEIGHT = `-mb-7.5 min-h-7.5 ${ACTION_BAR_PT}`;
33
- return (_jsxs(MessagePrimitive.Root, { "data-slot": "aui_assistant-message-root", "data-role": "assistant", className: "fade-in slide-in-from-bottom-1 relative animate-in duration-150 [contain-intrinsic-size:auto_300px] [content-visibility:auto]", children: [_jsxs("div", { "data-slot": "aui_assistant-message-content", className: "wrap-break-word px-2 text-[15px] text-foreground leading-relaxed md:text-base", children: [_jsx(MessagePrimitive.Parts, { children: ({ part }) => {
33
+ return (_jsxs(MessagePrimitive.Root, { "data-slot": "aui_assistant-message-root", "data-role": "assistant", className: "fade-in slide-in-from-bottom-1 relative animate-in duration-150 [contain-intrinsic-size:auto_300px] [content-visibility:auto]", children: [_jsxs("div", { "data-slot": "aui_assistant-message-content", className: "wrap-break-word px-1.5 text-sm text-foreground leading-[1.6] md:px-2 md:text-base md:leading-relaxed [&>[data-slot=image-root]]:my-2", children: [_jsx(MessagePrimitive.Parts, { children: ({ part }) => {
34
34
  var _a;
35
35
  if (part.type === "text")
36
36
  return _jsx(MarkdownText, {});
@@ -57,7 +57,7 @@ export const UserMessage = () => {
57
57
  const isInternalUserChoiceResult = useAuiState((state) => state.message.content.some((part) => part.type === "text" && isUserChoiceResultMessageText(part.text)));
58
58
  if (isInternalUserChoiceResult)
59
59
  return null;
60
- return (_jsxs(MessagePrimitive.Root, { "data-slot": "aui_user-message-root", className: "fade-in slide-in-from-bottom-1 grid animate-in auto-rows-auto grid-cols-[minmax(72px,1fr)_auto] content-start gap-y-2 px-2 duration-150 [contain-intrinsic-size:auto_60px] [content-visibility:auto] [&:where(>*)]:col-start-2", "data-role": "user", children: [_jsx(UserMessageAttachments, {}), _jsxs("div", { className: "aui-user-message-content-wrapper relative col-start-2 min-w-0", children: [_jsx("div", { className: "aui-user-message-content wrap-break-word peer rounded-2xl bg-muted px-4 py-2.5 text-[15px] text-foreground md:text-base empty:hidden", children: _jsx(MessagePrimitive.Parts, { components: { Image: ImageMessage, File } }) }), _jsx("div", { className: "aui-user-action-bar-wrapper absolute start-0 top-1/2 -translate-x-full -translate-y-1/2 pe-2 peer-empty:hidden rtl:translate-x-full", children: _jsx(UserActionBar, {}) })] }), _jsx(BranchPicker, { "data-slot": "aui_user-branch-picker", className: "col-span-full col-start-1 row-start-3 -me-1 justify-end" })] }));
60
+ return (_jsxs(MessagePrimitive.Root, { "data-slot": "aui_user-message-root", className: "fade-in slide-in-from-bottom-1 grid animate-in auto-rows-auto grid-cols-[minmax(72px,1fr)_auto] content-start gap-y-2 px-2 duration-150 [contain-intrinsic-size:auto_60px] [content-visibility:auto] [&:where(>*)]:col-start-2", "data-role": "user", children: [_jsx(UserMessageAttachments, {}), _jsxs("div", { className: "aui-user-message-content-wrapper relative col-start-2 min-w-0", children: [_jsx("div", { className: "aui-user-message-content wrap-break-word peer rounded-2xl bg-muted px-3.5 py-2 text-sm text-foreground leading-[1.55] md:px-4 md:py-2.5 md:text-base md:leading-normal empty:hidden [&>[data-slot=image-root]]:my-2", children: _jsx(MessagePrimitive.Parts, { components: { Image: ImageMessage, File } }) }), _jsx("div", { className: "aui-user-action-bar-wrapper absolute start-0 top-1/2 -translate-x-full -translate-y-1/2 pe-2 peer-empty:hidden rtl:translate-x-full", children: _jsx(UserActionBar, {}) })] }), _jsx(BranchPicker, { "data-slot": "aui_user-branch-picker", className: "col-span-full col-start-1 row-start-3 -me-1 justify-end" })] }));
61
61
  };
62
62
  export const UserActionBar = () => {
63
63
  return (_jsx(ActionBarPrimitive.Root, { hideWhenRunning: true, autohide: "not-last", className: "aui-user-action-bar-root flex flex-col items-end", children: _jsx(ActionBarPrimitive.Edit, { asChild: true, children: _jsx(TooltipIconButton, { tooltip: "\u7F16\u8F91", className: "aui-user-action-edit p-4", children: _jsx(PencilIcon, {}) }) }) }));
@@ -1,2 +1,8 @@
1
- import type { FC } from "react";
2
- export declare const ThreadList: FC;
1
+ import { type FC, type MouseEventHandler } from "react";
2
+ type ThreadListProps = {
3
+ openInNewWindowHref?: string | null;
4
+ localNewThreadHref?: string | null;
5
+ onNewThreadClick?: MouseEventHandler<HTMLButtonElement>;
6
+ };
7
+ export declare const ThreadList: FC<ThreadListProps>;
8
+ export {};
@@ -1,9 +1,12 @@
1
+ "use client";
1
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { AuiIf, ThreadListItemMorePrimitive, ThreadListItemPrimitive, ThreadListPrimitive, } from "@assistant-ui/react";
3
- import { ArchiveIcon, MoreHorizontalIcon, PlusIcon, SettingsIcon, TrashIcon, } from "lucide-react";
3
+ import { AuiIf, ThreadListItemMorePrimitive, ThreadListItemPrimitive, ThreadListPrimitive, useAui, useAuiState, } from "@assistant-ui/react";
4
+ import { ArchiveIcon, ExternalLinkIcon, MoreHorizontalIcon, PlusIcon, SettingsIcon, TrashIcon, } from "lucide-react";
5
+ import { useEffect, useMemo, useRef, useState, } from "react";
4
6
  import { ImageGenerationSettingsDialog } from "./image-generation-settings-dialog.mjs";
5
7
  import { Button } from "../ui/button.mjs";
6
8
  import { Skeleton } from "../ui/skeleton.mjs";
9
+ import { acpApiClient } from "../../lib/api/client.mjs";
7
10
  const THREAD_LIST_SKELETON_ITEMS = [
8
11
  "thread-list-skeleton-1",
9
12
  "thread-list-skeleton-2",
@@ -11,11 +14,12 @@ const THREAD_LIST_SKELETON_ITEMS = [
11
14
  "thread-list-skeleton-4",
12
15
  "thread-list-skeleton-5",
13
16
  ];
14
- export const ThreadList = () => {
15
- return (_jsxs(ThreadListPrimitive.Root, { className: "aui-root aui-thread-list-root flex w-full min-w-0 flex-col gap-1", children: [_jsx(ThreadListNew, {}), _jsx(AuiIf, { condition: (s) => s.threads.isLoading && s.threads.threadIds.length === 0, children: _jsx(ThreadListSkeleton, {}) }), _jsxs(AuiIf, { condition: (s) => !s.threads.isLoading || s.threads.threadIds.length > 0, children: [_jsx(ThreadListPrimitive.Items, { children: () => _jsx(ThreadListItem, {}) }), _jsx(AuiIf, { condition: (s) => s.threads.hasMore, children: _jsx(ThreadListLoadMore, {}) })] })] }));
17
+ export const ThreadList = ({ openInNewWindowHref, localNewThreadHref, onNewThreadClick, }) => {
18
+ return (_jsxs(ThreadListPrimitive.Root, { className: "aui-root aui-thread-list-root flex w-full min-w-0 flex-col gap-1", children: [_jsx(ThreadListRuntimeRefresh, {}), _jsx(ThreadListNew, { openInNewWindowHref: openInNewWindowHref, localNewThreadHref: localNewThreadHref, onNewThreadClick: onNewThreadClick }), _jsx(AuiIf, { condition: (s) => s.threads.isLoading && s.threads.threadIds.length === 0, children: _jsx(ThreadListSkeleton, {}) }), _jsxs(AuiIf, { condition: (s) => !s.threads.isLoading || s.threads.threadIds.length > 0, children: [_jsx(ThreadListPrimitive.Items, { children: () => _jsx(ThreadListItem, {}) }), _jsx(AuiIf, { condition: (s) => s.threads.hasMore, children: _jsx(ThreadListLoadMore, {}) })] })] }));
16
19
  };
17
- const ThreadListNew = () => {
18
- return (_jsxs("div", { className: "aui-thread-list-top-actions mb-2 flex flex-col gap-1", children: [_jsx(ImageGenerationSettingsDialog, { trigger: _jsxs(Button, { type: "button", variant: "ghost", className: "aui-thread-list-settings h-9 w-full min-w-0 justify-start gap-2 rounded-lg px-3 text-sm hover:bg-muted data-[state=open]:bg-muted", children: [_jsx(SettingsIcon, { className: "size-4" }), _jsx("span", { className: "truncate", children: "\u8BBE\u7F6E" })] }) }), _jsx(ThreadListPrimitive.New, { asChild: true, children: _jsxs(Button, { variant: "ghost", className: "aui-thread-list-new h-9 w-full min-w-0 justify-start gap-2 rounded-lg px-3 text-sm hover:bg-muted", children: [_jsx(PlusIcon, { className: "size-4" }), _jsx("span", { className: "truncate", children: "\u65B0\u5EFA\u5BF9\u8BDD" })] }) })] }));
20
+ const ThreadListNew = ({ openInNewWindowHref, localNewThreadHref, onNewThreadClick, }) => {
21
+ const newThreadButton = (_jsxs(Button, { variant: "ghost", className: "aui-thread-list-new h-9 w-full min-w-0 justify-start gap-2 rounded-lg px-3 text-sm hover:bg-muted", onClick: onNewThreadClick, children: [_jsx(PlusIcon, { className: "size-4" }), _jsx("span", { className: "truncate", children: "\u65B0\u5EFA\u5BF9\u8BDD" })] }));
22
+ return (_jsxs("div", { className: "aui-thread-list-top-actions mb-2 flex flex-col gap-1", children: [_jsx(ImageGenerationSettingsDialog, { trigger: _jsxs(Button, { type: "button", variant: "ghost", className: "aui-thread-list-settings h-9 w-full min-w-0 justify-start gap-2 rounded-lg px-3 text-sm hover:bg-muted data-[state=open]:bg-muted", children: [_jsx(SettingsIcon, { className: "size-4" }), _jsx("span", { className: "truncate", children: "\u8BBE\u7F6E" })] }) }), localNewThreadHref ? (newThreadButton) : (_jsx(ThreadListPrimitive.New, { asChild: true, children: newThreadButton })), openInNewWindowHref ? (_jsx(Button, { asChild: true, variant: "ghost", className: "aui-thread-list-open-window h-9 w-full min-w-0 justify-start gap-2 rounded-lg px-3 text-sm hover:bg-muted", children: _jsxs("a", { href: openInNewWindowHref, target: "_blank", rel: "noopener noreferrer", children: [_jsx(ExternalLinkIcon, { className: "size-4" }), _jsx("span", { className: "truncate", children: "\u65B0\u7A97\u53E3\u6253\u5F00" })] }) })) : null] }));
19
23
  };
20
24
  const ThreadListSkeleton = () => {
21
25
  return (_jsx("div", { className: "flex flex-col gap-1", children: THREAD_LIST_SKELETON_ITEMS.map((item) => (_jsx("div", { role: "status", "aria-label": "\u6B63\u5728\u52A0\u8F7D\u5BF9\u8BDD", className: "aui-thread-list-skeleton-wrapper flex h-9 items-center px-3", children: _jsx(Skeleton, { className: "aui-thread-list-skeleton h-4 w-full" }) }, item))) }));
@@ -24,7 +28,76 @@ const ThreadListLoadMore = () => {
24
28
  return (_jsx(ThreadListPrimitive.LoadMore, { asChild: true, children: _jsxs(Button, { variant: "ghost", className: "aui-thread-list-load-more h-9 justify-center rounded-lg px-3 text-muted-foreground text-sm hover:bg-muted hover:text-foreground", children: [_jsx(AuiIf, { condition: (s) => !s.threads.isLoadingMore, children: "\u52A0\u8F7D\u66F4\u591A" }), _jsx(AuiIf, { condition: (s) => s.threads.isLoadingMore, children: "\u6B63\u5728\u52A0\u8F7D..." })] }) }));
25
29
  };
26
30
  const ThreadListItem = () => {
27
- return (_jsxs(ThreadListItemPrimitive.Root, { className: "aui-thread-list-item group/thread-list-item flex h-9 w-full min-w-0 items-center rounded-lg transition-colors hover:bg-muted focus-visible:bg-muted focus-visible:outline-none data-active:bg-muted", children: [_jsx(ThreadListItemPrimitive.Trigger, { className: "aui-thread-list-item-trigger flex h-full min-w-0 flex-1 items-center px-3 text-start text-sm", children: _jsx("span", { className: "aui-thread-list-item-title min-w-0 flex-1 truncate", children: _jsx(ThreadListItemPrimitive.Title, { fallback: "\u65B0\u5BF9\u8BDD" }) }) }), _jsx(ThreadListItemMore, {})] }));
31
+ return (_jsxs(ThreadListItemPrimitive.Root, { className: "aui-thread-list-item group/thread-list-item flex h-9 w-full min-w-0 items-center rounded-lg transition-colors hover:bg-muted focus-visible:bg-muted focus-visible:outline-none data-active:bg-muted", children: [_jsxs(ThreadListItemPrimitive.Trigger, { className: "aui-thread-list-item-trigger flex h-full min-w-0 flex-1 items-center px-3 text-start text-sm", children: [_jsx("span", { className: "aui-thread-list-item-title min-w-0 flex-1 truncate", children: _jsx(ThreadListItemPrimitive.Title, { fallback: "\u65B0\u5BF9\u8BDD" }) }), _jsx(ThreadListItemRunStatus, {})] }), _jsx(ThreadListItemMore, {})] }));
32
+ };
33
+ function getCustomString(custom, key) {
34
+ const value = custom === null || custom === void 0 ? void 0 : custom[key];
35
+ return typeof value === "string" && value.trim() ? value.trim() : undefined;
36
+ }
37
+ function getRunDisplayState(runState) {
38
+ return runState === "queued" || runState === "running" ? "running" : null;
39
+ }
40
+ const ThreadListRuntimeRefresh = () => {
41
+ const aui = useAui();
42
+ const isRunning = useAuiState((state) => state.thread.isRunning);
43
+ const previousIsRunningRef = useRef(isRunning);
44
+ useEffect(() => {
45
+ const wasRunning = previousIsRunningRef.current;
46
+ previousIsRunningRef.current = isRunning;
47
+ if (!wasRunning || isRunning)
48
+ return;
49
+ void aui
50
+ .threads()
51
+ .reload()
52
+ .catch(() => { });
53
+ }, [aui, isRunning]);
54
+ return null;
55
+ };
56
+ const ThreadListItemRunStatus = () => {
57
+ const remoteId = useAuiState((state) => state.threadListItem.remoteId);
58
+ const custom = useAuiState((state) => state.threadListItem.custom);
59
+ const isCurrentThread = useAuiState((state) => state.threads.mainThreadId === state.threadListItem.id);
60
+ const currentThreadIsRunning = useAuiState((state) => state.thread.isRunning);
61
+ const workspacePath = getCustomString(custom, "workspacePath");
62
+ const currentRunSignal = isCurrentThread ? currentThreadIsRunning : null;
63
+ const [displayState, setDisplayState] = useState(null);
64
+ const [refreshToken, setRefreshToken] = useState(0);
65
+ const previousRuntimeRefreshRef = useRef(`${isCurrentThread}:${currentRunSignal}`);
66
+ const runtimeRequest = useMemo(() => ({ refreshToken, remoteId, workspacePath }), [refreshToken, remoteId, workspacePath]);
67
+ useEffect(() => {
68
+ const signature = `${isCurrentThread}:${currentRunSignal}`;
69
+ if (previousRuntimeRefreshRef.current === signature)
70
+ return;
71
+ previousRuntimeRefreshRef.current = signature;
72
+ if (isCurrentThread)
73
+ setRefreshToken((value) => value + 1);
74
+ }, [isCurrentThread, currentRunSignal]);
75
+ useEffect(() => {
76
+ let cancelled = false;
77
+ if (!runtimeRequest.remoteId) {
78
+ setDisplayState(null);
79
+ return;
80
+ }
81
+ void acpApiClient
82
+ .getConversationRuntime(runtimeRequest.remoteId, runtimeRequest.workspacePath)
83
+ .then((runtime) => {
84
+ var _a;
85
+ if (cancelled)
86
+ return;
87
+ setDisplayState(getRunDisplayState((_a = runtime.metadata) === null || _a === void 0 ? void 0 : _a.runState));
88
+ })
89
+ .catch(() => {
90
+ if (!cancelled)
91
+ setDisplayState(null);
92
+ });
93
+ return () => {
94
+ cancelled = true;
95
+ };
96
+ }, [runtimeRequest]);
97
+ const isRunning = displayState === "running";
98
+ if (!isRunning)
99
+ return null;
100
+ return (_jsx("span", { className: "aui-thread-list-item-run-status ms-2 inline-flex size-3 shrink-0 items-center justify-center", role: "img", "aria-label": "\u5BF9\u8BDD\u8FD0\u884C\u4E2D", title: "\u5BF9\u8BDD\u8FD0\u884C\u4E2D", children: _jsx("span", { className: "block size-3 animate-spin rounded-full border border-foreground/70 border-t-transparent motion-reduce:animate-none" }) }));
28
101
  };
29
102
  const ThreadListItemMore = () => {
30
103
  return (_jsxs(ThreadListItemMorePrimitive.Root, { children: [_jsx(ThreadListItemMorePrimitive.Trigger, { asChild: true, children: _jsxs(Button, { variant: "ghost", size: "icon", className: "aui-thread-list-item-more me-0 h-7 w-0 overflow-hidden p-0 opacity-0 transition-[width,margin,opacity,background-color,color] group-hover/thread-list-item:me-2 group-hover/thread-list-item:w-7 group-hover/thread-list-item:opacity-100 group-focus-within/thread-list-item:me-2 group-focus-within/thread-list-item:w-7 group-focus-within/thread-list-item:opacity-100 data-[state=open]:me-2 data-[state=open]:w-7 data-[state=open]:bg-accent data-[state=open]:opacity-100", children: [_jsx(MoreHorizontalIcon, { className: "size-4" }), _jsx("span", { className: "sr-only", children: "\u66F4\u591A\u9009\u9879" })] }) }), _jsxs(ThreadListItemMorePrimitive.Content, { side: "bottom", align: "start", className: "aui-thread-list-item-more-content z-50 min-w-32 overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md", children: [_jsx(ThreadListItemPrimitive.Archive, { asChild: true, children: _jsxs(ThreadListItemMorePrimitive.Item, { className: "aui-thread-list-item-more-item flex cursor-pointer select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground", children: [_jsx(ArchiveIcon, { className: "size-4" }), "\u5F52\u6863"] }) }), _jsx(ThreadListItemPrimitive.Delete, { asChild: true, children: _jsxs(ThreadListItemMorePrimitive.Item, { className: "aui-thread-list-item-more-item flex cursor-pointer select-none items-center gap-2 rounded-sm px-2 py-1.5 text-destructive text-sm outline-none hover:bg-destructive/10 hover:text-destructive focus:bg-destructive/10 focus:text-destructive", children: [_jsx(TrashIcon, { className: "size-4" }), "\u5220\u9664"] }) })] })] }));
@@ -1,6 +1,9 @@
1
1
  import type * as React from "react";
2
2
  import { Sidebar } from "../ui/sidebar";
3
- export declare function ThreadListSidebar({ workspacePath, workspaceTitle, ...props }: React.ComponentProps<typeof Sidebar> & {
3
+ export declare function ThreadListSidebar({ workspacePath, workspaceTitle, openInNewWindowHref, localNewThreadHref, onNewThreadClick, ...props }: React.ComponentProps<typeof Sidebar> & {
4
4
  workspacePath?: string | null;
5
5
  workspaceTitle?: string | null;
6
+ openInNewWindowHref?: string | null;
7
+ localNewThreadHref?: string | null;
8
+ onNewThreadClick?: React.MouseEventHandler<HTMLButtonElement>;
6
9
  }): import("react/jsx-runtime").JSX.Element;