dsh-edge 0.1.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 (159) hide show
  1. package/LICENSE +21 -0
  2. package/README.i18n.yaml +6 -0
  3. package/README.md +183 -0
  4. package/README.zh.md +183 -0
  5. package/THIRD_PARTY_NOTICES.md +199 -0
  6. package/dist/_headers +3 -0
  7. package/dist/assets/fonts/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
  8. package/dist/assets/fonts/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
  9. package/dist/assets/fonts/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
  10. package/dist/assets/fonts/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
  11. package/dist/assets/fonts/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
  12. package/dist/assets/fonts/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
  13. package/dist/assets/fonts/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
  14. package/dist/assets/fonts/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
  15. package/dist/assets/fonts/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
  16. package/dist/assets/fonts/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
  17. package/dist/assets/fonts/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
  18. package/dist/assets/fonts/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
  19. package/dist/assets/fonts/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
  20. package/dist/assets/fonts/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
  21. package/dist/assets/fonts/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
  22. package/dist/assets/fonts/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
  23. package/dist/assets/fonts/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
  24. package/dist/assets/fonts/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
  25. package/dist/assets/fonts/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
  26. package/dist/assets/fonts/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
  27. package/dist/assets/fonts/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
  28. package/dist/assets/fonts/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
  29. package/dist/assets/fonts/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
  30. package/dist/assets/fonts/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
  31. package/dist/assets/fonts/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
  32. package/dist/assets/fonts/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
  33. package/dist/assets/fonts/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
  34. package/dist/assets/fonts/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
  35. package/dist/assets/fonts/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
  36. package/dist/assets/fonts/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
  37. package/dist/assets/fonts/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
  38. package/dist/assets/fonts/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
  39. package/dist/assets/fonts/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
  40. package/dist/assets/fonts/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
  41. package/dist/assets/fonts/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
  42. package/dist/assets/fonts/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
  43. package/dist/assets/fonts/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
  44. package/dist/assets/fonts/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
  45. package/dist/assets/fonts/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
  46. package/dist/assets/fonts/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
  47. package/dist/assets/fonts/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
  48. package/dist/assets/fonts/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
  49. package/dist/assets/fonts/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
  50. package/dist/assets/fonts/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
  51. package/dist/assets/fonts/KaTeX_Script-Regular-D5yQViql.woff +0 -0
  52. package/dist/assets/fonts/KaTeX_Size1-Regular-C195tn64.woff +0 -0
  53. package/dist/assets/fonts/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
  54. package/dist/assets/fonts/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
  55. package/dist/assets/fonts/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
  56. package/dist/assets/fonts/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
  57. package/dist/assets/fonts/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
  58. package/dist/assets/fonts/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
  59. package/dist/assets/fonts/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
  60. package/dist/assets/fonts/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
  61. package/dist/assets/fonts/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
  62. package/dist/assets/fonts/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
  63. package/dist/assets/fonts/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
  64. package/dist/assets/fonts/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
  65. package/dist/assets/fonts/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
  66. package/dist/assets/index-C-1AiF3k.js +112 -0
  67. package/dist/assets/index-CSGf6Qzd.css +1 -0
  68. package/dist/assets/langs/c-BIGW1oBm.js +2 -0
  69. package/dist/assets/langs/cpp-DIPi6g--.js +2 -0
  70. package/dist/assets/langs/csharp-DSvCPggb.js +2 -0
  71. package/dist/assets/langs/css-CLj8gQPS.js +2 -0
  72. package/dist/assets/langs/go-C27-OAKa.js +2 -0
  73. package/dist/assets/langs/html-CfGypltT.js +2 -0
  74. package/dist/assets/langs/ini-BEwlwnbL.js +2 -0
  75. package/dist/assets/langs/java-CylS5w8V.js +2 -0
  76. package/dist/assets/langs/kotlin-BdnUsdx6.js +2 -0
  77. package/dist/assets/langs/less-B1dDrJ26.js +2 -0
  78. package/dist/assets/langs/lua-BaeVxFsk.js +2 -0
  79. package/dist/assets/langs/markdown-Cvjx9yec.js +2 -0
  80. package/dist/assets/langs/mdx-Cmh6b_Ma.js +2 -0
  81. package/dist/assets/langs/php-D-uCvoST.js +2 -0
  82. package/dist/assets/langs/python-B6aJPvgy.js +2 -0
  83. package/dist/assets/langs/ruby-DEItuUFs.js +2 -0
  84. package/dist/assets/langs/rust-B1yitclQ.js +2 -0
  85. package/dist/assets/langs/scss-D5BDwBP9.js +2 -0
  86. package/dist/assets/langs/sql-CRqJ_cUM.js +2 -0
  87. package/dist/assets/langs/swift-D82vCrfD.js +2 -0
  88. package/dist/assets/langs/toml-vGWfd6FD.js +2 -0
  89. package/dist/assets/langs/xml-sdJ4AIDG.js +2 -0
  90. package/dist/assets/langs/yaml-Buea-lGh.js +2 -0
  91. package/dist/assets/vendor-Cjbwl5VI.js +411 -0
  92. package/dist/assets/vendor-CjyC-hUb.css +1 -0
  93. package/dist/favicon.svg +8 -0
  94. package/dist/index.html +35 -0
  95. package/dist/manifest.webmanifest +16 -0
  96. package/dist/plugins/@deepseek-ai/dsh-api-gateway/client.js +420 -0
  97. package/dist/plugins/@deepseek-ai/dsh-api-remotes/client.js +5937 -0
  98. package/dist/plugins/@deepseek-ai/dsh-client-connection/client.js +10207 -0
  99. package/dist/plugins/@deepseek-ai/dsh-client-locale/client.js +1231 -0
  100. package/dist/plugins/@deepseek-ai/dsh-client-modules/client.js +259 -0
  101. package/dist/plugins/@deepseek-ai/dsh-client-runtime/client.js +10539 -0
  102. package/dist/plugins/@deepseek-ai/dsh-client-ui-agent-preset/client.js +1725 -0
  103. package/dist/plugins/@deepseek-ai/dsh-client-ui-commands/client.js +1116 -0
  104. package/dist/plugins/@deepseek-ai/dsh-client-ui-conversation/client.js +9832 -0
  105. package/dist/plugins/@deepseek-ai/dsh-client-ui-deliverables/client.js +373 -0
  106. package/dist/plugins/@deepseek-ai/dsh-client-ui-edge/client.js +549 -0
  107. package/dist/plugins/@deepseek-ai/dsh-client-ui-input-trigger/client.js +859 -0
  108. package/dist/plugins/@deepseek-ai/dsh-client-ui-jobs/client.js +280 -0
  109. package/dist/plugins/@deepseek-ai/dsh-client-ui-layout/client.js +456 -0
  110. package/dist/plugins/@deepseek-ai/dsh-client-ui-model-selection/client.js +804 -0
  111. package/dist/plugins/@deepseek-ai/dsh-client-ui-permission-presets/client.js +461 -0
  112. package/dist/plugins/@deepseek-ai/dsh-client-ui-settings/client.js +254 -0
  113. package/dist/plugins/@deepseek-ai/dsh-client-ui-settings-general/client.js +609 -0
  114. package/dist/plugins/@deepseek-ai/dsh-client-ui-sidebar/client.js +292 -0
  115. package/dist/plugins/@deepseek-ai/dsh-client-ui-skill/client.js +327 -0
  116. package/dist/plugins/@deepseek-ai/dsh-client-ui-subagent/client.js +710 -0
  117. package/dist/plugins/@deepseek-ai/dsh-client-ui-theme/client.js +1312 -0
  118. package/dist/plugins/@deepseek-ai/dsh-client-ui-tool/client.js +1630 -0
  119. package/dist/plugins/@deepseek-ai/dsh-client-ui-trajectory/client.js +7370 -0
  120. package/dist/plugins/@deepseek-ai/dsh-client-ui-user-questions/client.js +700 -0
  121. package/dist/plugins/@deepseek-ai/dsh-client-ui-workflow-run/client.js +474 -0
  122. package/dist/plugins/@deepseek-ai/dsh-client-ui-workspace/client.js +2426 -0
  123. package/dist/plugins/@deepseek-ai/dsh-typert-registry/client.js +1372 -0
  124. package/package.json +97 -0
  125. package/scripts/assemble-web.mjs +230 -0
  126. package/scripts/bundle-size.d.mts +2 -0
  127. package/scripts/bundle-size.mjs +26 -0
  128. package/scripts/bundle.mjs +53 -0
  129. package/scripts/cli.d.mts +39 -0
  130. package/scripts/cli.mjs +496 -0
  131. package/scripts/install.d.mts +149 -0
  132. package/scripts/install.mjs +989 -0
  133. package/scripts/legal-files.mjs +24 -0
  134. package/scripts/verify-packed.mjs +32 -0
  135. package/scripts/wrangler-config.d.mts +18 -0
  136. package/scripts/wrangler-config.mjs +84 -0
  137. package/src/agent.ts +119 -0
  138. package/src/auth.ts +352 -0
  139. package/src/deepseek.ts +231 -0
  140. package/src/deployment.ts +147 -0
  141. package/src/direct-shell-core-empty.ts +10 -0
  142. package/src/direct-shell-protocol.ts +5 -0
  143. package/src/direct-shell.ts +341 -0
  144. package/src/do-session-persistence.ts +1064 -0
  145. package/src/edge-api.ts +796 -0
  146. package/src/edge-credentials.ts +56 -0
  147. package/src/edge-remotes.ts +41 -0
  148. package/src/edge-workspace-store.ts +339 -0
  149. package/src/http.ts +212 -0
  150. package/src/index.ts +196 -0
  151. package/src/instance.ts +1007 -0
  152. package/src/isolated-direct-shell-unavailable.ts +11 -0
  153. package/src/protocol.ts +39 -0
  154. package/src/release.ts +9 -0
  155. package/src/session-store.ts +1110 -0
  156. package/src/sse.ts +86 -0
  157. package/src/web-search.ts +38 -0
  158. package/src/workspace.ts +270 -0
  159. package/wrangler.jsonc +56 -0
@@ -0,0 +1,373 @@
1
+ window.__ModuleLoader__.load({
2
+ id: "@deepseek-ai/dsh-client-ui-deliverables",
3
+ factory: (require) => {
4
+ var module = { exports: {} };
5
+ var exports = module.exports;
6
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
7
+ let react_jsx_runtime = require("react/jsx-runtime");
8
+ let react = require("react");
9
+ let _deepseek_ai_dsh_client_runtime_client = require("@deepseek-ai/dsh-client-runtime/client");
10
+ //#region lib/types/client/turn-deliverables.js
11
+ /**
12
+ * Paths a call view reports having created or changed, by render intent rather
13
+ * than tool name: a diff card, or a generic card whose kind is `edit` (the
14
+ * shape `str_replace_editor`'s insert presents). Every other card produces
15
+ * nothing to open — a read looked, a delete removed, a terminal ran. Only
16
+ * root call views enter this Turn accumulator; nested Code Mode dispatches
17
+ * preserve the pre-assembly behavior and do not contribute independently.
18
+ */
19
+ function producedPaths(view) {
20
+ if (view === null) return [];
21
+ if (view.card === "diff") return (view.locations ?? []).map((location) => location.path);
22
+ if (view.card === "generic" && view.kind === "edit") return (view.locations ?? []).map((location) => location.path);
23
+ return [];
24
+ }
25
+ /**
26
+ * Files produced by one Turn data value.
27
+ *
28
+ * The source is the mutation tools' own follow-along `locations`, not the
29
+ * closing prose: a produced file must be listed whether or not the model
30
+ * remembered to name it. A mutation is recognized by render intent, not by
31
+ * tool name — a diff card, or a generic card whose `kind` is `edit` (the shape
32
+ * `str_replace_editor`'s insert presents) — so a new mutation tool joins by
33
+ * declaring what it does. Reads contribute nothing (looking at a file does not
34
+ * produce it), and neither do deletes (there is nothing left to open) or
35
+ * failed calls. Paths keep first-seen order and appear once, so a file written
36
+ * and then edited in the same turn is one entry.
37
+ *
38
+ * The Conversation Location index owns turn membership before this function
39
+ * runs, so paths cannot spill across turns and this derivation does not infer
40
+ * boundaries from neighboring presentation Nodes.
41
+ * @param data - engine-published Deliverables data for one Turn.
42
+ * @param seq - closing Assistant seq; later Tool settlements are excluded.
43
+ * @returns Produced paths in first-seen order; empty when the turn wrote nothing.
44
+ */
45
+ function producedForClosing(data, seq = Number.POSITIVE_INFINITY) {
46
+ if (data === void 0) return [];
47
+ const paths = [];
48
+ const seen = /* @__PURE__ */ new Set();
49
+ for (const produced of data.produced) {
50
+ if (produced.seq > seq || seen.has(produced.path)) continue;
51
+ seen.add(produced.path);
52
+ paths.push(produced.path);
53
+ }
54
+ return paths;
55
+ }
56
+ /**
57
+ * Claim the turn-tail chain only when its closing turn produced files.
58
+ * @param owner - Turn-tail owner currency for the closing assistant.
59
+ * @returns Produced paths as the component's match, or null to decline before mount.
60
+ */
61
+ function selectProducedFiles(owner) {
62
+ const paths = producedForClosing(owner.turn.data.get("deliverables"), owner.seq);
63
+ return paths.length === 0 ? null : paths;
64
+ }
65
+ /** Turn-local successful mutation accumulator; it publishes no view Node. */
66
+ const deliverablesDefinition = {
67
+ kind: "deliverables",
68
+ match: (event) => {
69
+ if (event.type === "turn/start") return {
70
+ id: String(event.data.turn),
71
+ role: "start"
72
+ };
73
+ if (event.type === "tool/call") return {
74
+ id: String(event.data.turn),
75
+ role: "update"
76
+ };
77
+ if (event.type === "tool/result" && (0, _deepseek_ai_dsh_client_runtime_client.isAppendSurfaceEvent)(event)) return {
78
+ id: String(event.data.turn),
79
+ role: "update"
80
+ };
81
+ return null;
82
+ },
83
+ start: (_context, match) => {
84
+ if (match.event.type !== "turn/start") throw new Error("deliverables start requires turn/start");
85
+ return {
86
+ turn: match.event.data.turn,
87
+ calls: /* @__PURE__ */ new Map(),
88
+ produced: []
89
+ };
90
+ },
91
+ update: (context, match) => {
92
+ if (match.event.type === "tool/call") {
93
+ const calls = new Map(context.state.calls);
94
+ calls.set(String(match.event.data.callId), match.view?.for === "call" ? match.view.view : null);
95
+ return {
96
+ ...context.state,
97
+ calls
98
+ };
99
+ }
100
+ if (match.event.type !== "tool/result") return context.state;
101
+ if (match.event.data.message.content[0].isError === true) return context.state;
102
+ const callId = String(match.event.data.message.source.callId);
103
+ const additions = producedPaths(context.state.calls.get(callId) ?? null).map((path) => ({
104
+ seq: match.event.seq,
105
+ path
106
+ }));
107
+ return additions.length === 0 ? context.state : {
108
+ ...context.state,
109
+ produced: [...context.state.produced, ...additions]
110
+ };
111
+ },
112
+ buildLocationData: (context, scope) => scope !== "turn" || context.state === void 0 ? null : {
113
+ kind: "turn",
114
+ turn: context.state.turn,
115
+ key: "deliverables",
116
+ value: { produced: context.state.produced }
117
+ }
118
+ };
119
+ /**
120
+ * Trailing path segment, the part that identifies the file at a glance.
121
+ * @param path - Slash- or backslash-separated path.
122
+ * @returns The final segment, or the whole string when separator-free.
123
+ */
124
+ function basename(path) {
125
+ const at = Math.max(path.lastIndexOf("/"), path.lastIndexOf("\\"));
126
+ return at === -1 ? path : path.slice(at + 1);
127
+ }
128
+ /**
129
+ * File-mention vocabulary over one turn's produced paths, for the closing
130
+ * message's prose: an inline-code token opens the file it names. A token
131
+ * resolves by exact path, or by being exactly the basename of exactly one
132
+ * produced path — a basename two paths share stays inert rather than
133
+ * guessing, so a mention link can never open the wrong file or 404.
134
+ * @param paths - The turn's produced paths (tool order, already deduped).
135
+ * @param openFile - The chat view's file opener.
136
+ * @param label - Localizes the accessible open-label for a resolved path.
137
+ * @returns The resolver MarkdownText consumes; the full path rides `title`,
138
+ * the same disambiguator the row's chips carry.
139
+ */
140
+ function producedFileMentions(paths, openFile, label) {
141
+ return { resolve(value) {
142
+ const path = paths.includes(value) ? value : onlyPathWithBasename(paths, value);
143
+ if (path === void 0) return void 0;
144
+ return {
145
+ open: () => {
146
+ openFile(path);
147
+ },
148
+ label: label(path),
149
+ title: path
150
+ };
151
+ } };
152
+ }
153
+ /** The single produced path whose basename is exactly `value`, else undefined. */
154
+ function onlyPathWithBasename(paths, value) {
155
+ const matches = paths.filter((path) => basename(path) === value);
156
+ return matches.length === 1 ? matches[0] : void 0;
157
+ }
158
+ //#endregion
159
+ //#region \0dsh-css:/private/var/folders/nm/n5smty9962gf5rhjc5855kt80000gn/T/dsh-edge-release-worktree.XXXXXX.RcJjogaGt4/repo/packages/client/ui-deliverables/src/client/ProducedFiles.module.css.mjs
160
+ const css = ".-gLsiq_root{grid-template-columns:max-content minmax(0,1fr);align-items:center;gap:6px 8px;margin-top:16px;font-size:13px;line-height:22px;display:grid;position:relative}.-gLsiq_label{color:var(--dsw-alias-label-tertiary);grid-area:1/1}.-gLsiq_row{flex-wrap:nowrap;grid-area:1/2;align-items:center;gap:8px;min-width:0;display:flex;overflow:hidden}.-gLsiq_file{text-overflow:ellipsis;white-space:nowrap;background:var(--dsw-alias-interactive-bg-hover);max-width:320px;color:var(--dsw-alias-label-secondary);font:inherit;cursor:pointer;border:none;border-radius:6px;flex:none;margin:0;padding:0 8px;overflow:hidden}.-gLsiq_file:hover{color:var(--dsw-alias-label-primary);text-decoration:underline}.-gLsiq_file:focus-visible,.-gLsiq_showFolder:focus-visible{box-shadow:inset 0 0 0 2px var(--dsw-alias-border-l3);outline:none}.-gLsiq_more{white-space:nowrap;color:var(--dsw-alias-label-tertiary);flex:none}.-gLsiq_showFolder{color:var(--dsw-alias-label-tertiary);font:inherit;cursor:pointer;background:0 0;border:none;border-radius:4px;grid-area:2/2;justify-self:start;margin:0;padding:0 2px;line-height:20px}.-gLsiq_showFolder:hover{color:var(--dsw-alias-label-secondary);text-decoration:underline}.-gLsiq_measure{visibility:hidden;pointer-events:none;contain:strict;width:0;height:0;position:absolute;overflow:hidden}.-gLsiq_probe{width:max-content;position:absolute;inset:0 auto auto 0}";
161
+ const tagId = "@deepseek-ai/dsh-client-ui-deliverables/ProducedFiles.module.css";
162
+ if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
163
+ const tag = document.createElement("style");
164
+ tag.dataset.plugin = "@deepseek-ai/dsh-client-ui-deliverables";
165
+ tag.dataset.pluginCss = tagId;
166
+ tag.textContent = css;
167
+ document.head.appendChild(tag);
168
+ }
169
+ var ProducedFiles_module_css_default = {
170
+ "file": "-gLsiq_file",
171
+ "more": "-gLsiq_more",
172
+ "row": "-gLsiq_row",
173
+ "measure": "-gLsiq_measure",
174
+ "probe": "-gLsiq_probe",
175
+ "root": "-gLsiq_root",
176
+ "showFolder": "-gLsiq_showFolder",
177
+ "label": "-gLsiq_label"
178
+ };
179
+ //#endregion
180
+ //#region lib/types/client/ProducedFiles.js
181
+ /** At most six chips compete for the one-line summary; every other path stays counted. */
182
+ const SHOWN_LIMIT = 6;
183
+ /**
184
+ * Select the largest prefix whose measured chips and exact remainder fit.
185
+ * @param available - usable width of the one-line file lane.
186
+ * @param gap - computed flex gap between adjacent visible items.
187
+ * @param chipWidths - measured widths for the candidate file chips.
188
+ * @param moreWidthsByShown - exact localized remainder width for each shown count.
189
+ * @returns Number of leading chips to render.
190
+ */
191
+ function fitProducedFiles(available, gap, chipWidths, moreWidthsByShown) {
192
+ if (available <= 0) return chipWidths.length;
193
+ const prefix = [0];
194
+ let prefixWidth = 0;
195
+ for (const width of chipWidths) {
196
+ prefixWidth += width;
197
+ prefix.push(prefixWidth);
198
+ }
199
+ let largestFit = 0;
200
+ for (const [shown, width] of prefix.entries()) {
201
+ const more = moreWidthsByShown[shown];
202
+ const items = shown + (more === void 0 ? 0 : 1);
203
+ if (width + (more ?? 0) + Math.max(0, items - 1) * gap <= available) largestFit = shown;
204
+ }
205
+ return largestFit;
206
+ }
207
+ function moreLabel(t, count) {
208
+ return count === 1 ? t("produced.moreOne") : t("produced.more", { count: String(count) });
209
+ }
210
+ /**
211
+ * Render one turn's produced files as openable chips.
212
+ * @param props - selector-matched paths, the chat view's file opener, and the locale seat.
213
+ * @returns The produced-files row.
214
+ */
215
+ function ProducedFiles({ matched: paths, openFile, isLoopback, useHostDescription, t }) {
216
+ const hostCanOpenPath = useHostDescription((description) => description?.canOpenPath === true);
217
+ const canOpenPath = isLoopback && hostCanOpenPath;
218
+ const limit = Math.min(paths.length, SHOWN_LIMIT);
219
+ const [shownCount, setShownCount] = (0, react.useState)(limit);
220
+ const rowRef = (0, react.useRef)(null);
221
+ const chipProbes = (0, react.useRef)([]);
222
+ const moreProbe = (0, react.useRef)(null);
223
+ (0, react.useLayoutEffect)(() => {
224
+ const row = rowRef.current;
225
+ const remainderProbe = moreProbe.current;
226
+ /* v8 ignore next -- React attaches both refs before the layout effect runs. */
227
+ if (row === null || remainderProbe === null) return;
228
+ const measure = () => {
229
+ const styles = getComputedStyle(row);
230
+ const gap = Number.parseFloat(styles.columnGap || styles.gap) || 0;
231
+ const chips = chipProbes.current.slice(0, limit).map((probe) => probe.getBoundingClientRect().width);
232
+ const more = Array.from({ length: limit + 1 }, (_, candidate) => {
233
+ if (paths.length === candidate) return void 0;
234
+ remainderProbe.textContent = moreLabel(t, paths.length - candidate);
235
+ return remainderProbe.getBoundingClientRect().width;
236
+ });
237
+ setShownCount(fitProducedFiles(row.clientWidth, gap, chips, more));
238
+ };
239
+ measure();
240
+ if (typeof ResizeObserver === "undefined") return;
241
+ const observer = new ResizeObserver(measure);
242
+ observer.observe(row);
243
+ for (const probe of [...chipProbes.current, moreProbe.current]) if (probe !== null) observer.observe(probe);
244
+ return () => {
245
+ observer.disconnect();
246
+ };
247
+ }, [
248
+ limit,
249
+ paths,
250
+ t
251
+ ]);
252
+ const visibleCount = Math.min(shownCount, limit);
253
+ const shown = paths.slice(0, visibleCount);
254
+ const hidden = paths.length - shown.length;
255
+ return (0, react_jsx_runtime.jsxs)("div", {
256
+ className: ProducedFiles_module_css_default.root,
257
+ children: [
258
+ (0, react_jsx_runtime.jsx)("span", {
259
+ className: ProducedFiles_module_css_default.label,
260
+ children: t("produced.label")
261
+ }),
262
+ (0, react_jsx_runtime.jsxs)("div", {
263
+ ref: rowRef,
264
+ className: ProducedFiles_module_css_default.row,
265
+ "data-produced-files-row": true,
266
+ children: [shown.map((path) => (0, react_jsx_runtime.jsx)("button", {
267
+ type: "button",
268
+ className: ProducedFiles_module_css_default.file,
269
+ title: path,
270
+ "aria-label": t("produced.open", { name: path }),
271
+ onClick: () => {
272
+ openFile(path);
273
+ },
274
+ children: basename(path)
275
+ }, path)), hidden > 0 && (0, react_jsx_runtime.jsx)("span", {
276
+ className: ProducedFiles_module_css_default.more,
277
+ children: moreLabel(t, hidden)
278
+ })]
279
+ }),
280
+ hidden > 0 && canOpenPath && (0, react_jsx_runtime.jsx)("button", {
281
+ type: "button",
282
+ className: ProducedFiles_module_css_default.showFolder,
283
+ onClick: () => {
284
+ openFile(".");
285
+ },
286
+ children: t("produced.showInFolder")
287
+ }),
288
+ (0, react_jsx_runtime.jsxs)("div", {
289
+ className: ProducedFiles_module_css_default.measure,
290
+ "aria-hidden": "true",
291
+ children: [paths.slice(0, limit).map((path, index) => (0, react_jsx_runtime.jsx)("button", {
292
+ ref: (node) => {
293
+ chipProbes.current[index] = node;
294
+ },
295
+ type: "button",
296
+ tabIndex: -1,
297
+ className: `${ProducedFiles_module_css_default.file} ${ProducedFiles_module_css_default.probe}`,
298
+ children: basename(path)
299
+ }, path)), (0, react_jsx_runtime.jsx)("span", {
300
+ ref: moreProbe,
301
+ className: `${ProducedFiles_module_css_default.more} ${ProducedFiles_module_css_default.probe}`
302
+ })]
303
+ })
304
+ ]
305
+ });
306
+ }
307
+ //#endregion
308
+ //#region lib/types/client/locales.js
309
+ /** `deliverables` namespace dictionaries. */
310
+ /** Dictionary namespace owned by this plugin. */
311
+ const NS = "deliverables";
312
+ /** Simplified Chinese dictionary (the key-set source of truth). */
313
+ const zh = {
314
+ "produced.label": "产物",
315
+ "produced.moreOne": "+ 1 个文件",
316
+ "produced.more": "+ {count} 个文件",
317
+ "produced.open": "打开 {name}",
318
+ "produced.showInFolder": "在文件夹中显示"
319
+ };
320
+ /** English dictionary (same key set). */
321
+ const en = {
322
+ "produced.label": "Produced",
323
+ "produced.moreOne": "+ 1 file",
324
+ "produced.more": "+ {count} files",
325
+ "produced.open": "Open {name}",
326
+ "produced.showInFolder": "Show in folder"
327
+ };
328
+ //#endregion
329
+ //#region lib/types/client/index.js
330
+ /** Required services for the tail-slot registration and its dictionaries. */
331
+ const inject = [
332
+ "slots",
333
+ "locale",
334
+ "conversationEvents",
335
+ "connection"
336
+ ];
337
+ /**
338
+ * Client plugin body: register the dictionaries and the turn-tail entry.
339
+ * @param ctx - client root context.
340
+ */
341
+ function apply(ctx) {
342
+ const connection = ctx.get("connection");
343
+ ctx.conversationEvents.register(deliverablesDefinition);
344
+ ctx.effect(() => ctx.locale.register(NS, {
345
+ zh,
346
+ en
347
+ }), "ui-deliverables: dictionaries");
348
+ ctx.slots.inject("conversation.chat.turnTail", () => ctx.slots.register({
349
+ name: "conversation.chat.turnTail",
350
+ select: selectProducedFiles,
351
+ locale: NS,
352
+ inject: () => ({
353
+ isLoopback: connection.isLoopback,
354
+ hooks: { hostDescription: connection.hostDescription }
355
+ })
356
+ }, ProducedFiles));
357
+ const t = ctx.locale.bind(NS);
358
+ ctx.provide("chatFileMentions", { forClosing(owner) {
359
+ const paths = selectProducedFiles(owner);
360
+ if (paths === null) return void 0;
361
+ return producedFileMentions(paths, owner.openFile, (path) => t("produced.open", { name: path }));
362
+ } });
363
+ }
364
+ //#endregion
365
+ exports.ProducedFiles = ProducedFiles;
366
+ exports.apply = apply;
367
+ exports.inject = inject;
368
+ exports.producedForClosing = producedForClosing;
369
+ return module.exports;
370
+ }
371
+ });
372
+
373
+ //# sourceMappingURL=client.js.map