dsh-edge 0.2.0 → 0.3.0-alpha.2

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.
@@ -0,0 +1,782 @@
1
+ window.__ModuleLoader__.load({
2
+ id: "@deepseek-ai/dsh-client-ui-attachment",
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_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
10
+ let react_dom = require("react-dom");
11
+ //#region ../../../node_modules/.pnpm/clsx@2.1.1/node_modules/clsx/dist/clsx.mjs
12
+ function r(e) {
13
+ var t, f, n = "";
14
+ if ("string" == typeof e || "number" == typeof e) n += e;
15
+ else if ("object" == typeof e) if (Array.isArray(e)) {
16
+ var o = e.length;
17
+ for (t = 0; t < o; t++) e[t] && (f = r(e[t])) && (n && (n += " "), n += f);
18
+ } else for (f in e) e[f] && (n && (n += " "), n += f);
19
+ return n;
20
+ }
21
+ function clsx() {
22
+ for (var e, t, f = 0, n = "", o = arguments.length; f < o; f++) (e = arguments[f]) && (t = r(e)) && (n && (n += " "), n += t);
23
+ return n;
24
+ }
25
+ //#endregion
26
+ //#region \0dsh-css:/home/runner/work/deepseek-harness/deepseek-harness/packages/client/ui-attachment/src/AttachmentRail.module.css.mjs
27
+ const css$4 = ".JVDQca_root{min-width:0;position:relative}.JVDQca_rail{scrollbar-width:none;--dsh-scrollbar-thumb:var(--dsw-alias-scrollbar-bg-l2);--dsh-scrollbar-thumb-hover:var(--dsw-alias-scrollbar-hover-l2);gap:10px;display:flex;overflow:auto hidden}.JVDQca_rail::-webkit-scrollbar{display:none}.JVDQca_item{flex:0 0 64px;width:64px;height:64px;position:relative}.JVDQca_thumbnail{border:1px solid var(--dsw-alias-border-l2-darkmode-thin);background:var(--dsw-alias-interactive-bg-hover);cursor:zoom-in;border-radius:16px;width:64px;height:64px;padding:0;overflow:hidden}.JVDQca_thumbnail img{object-fit:cover;width:100%;height:100%;display:block}.JVDQca_remove{z-index:1;background:var(--dsw-alias-button-contrast-fill);width:18px;height:18px;color:var(--dsw-alias-label-primary-inverted);cursor:pointer;opacity:0;border:none;border-radius:50%;place-items:center;padding:0;transition:opacity .2s ease-in-out;display:grid;position:absolute;top:4px;right:4px}.JVDQca_item:hover .JVDQca_remove,.JVDQca_remove:focus-visible{opacity:1}@media (pointer:coarse){.JVDQca_remove{opacity:1}}@media (prefers-reduced-motion:reduce){.JVDQca_remove{transition:none}}.JVDQca_arrow{z-index:2;border:1px solid var(--dsw-alias-border-l2-darkmode-thin);background:var(--dsw-specific-input-major);width:24px;height:24px;color:var(--dsw-alias-label-secondary);box-shadow:var(--dsw-shadow-lv2);cursor:pointer;border-radius:999px;place-items:center;padding:0;display:grid;position:absolute;top:50%;transform:translateY(-50%)}.JVDQca_arrow:hover{background:var(--dsw-alias-interactive-bg-hover-solid)}.JVDQca_arrowLeft{left:4px}.JVDQca_arrowRight{right:4px}";
28
+ const tagId$4 = "@deepseek-ai/dsh-client-ui-attachment/AttachmentRail.module.css";
29
+ if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$4) + "]") === null) {
30
+ const tag = document.createElement("style");
31
+ tag.dataset.plugin = "@deepseek-ai/dsh-client-ui-attachment";
32
+ tag.dataset.pluginCss = tagId$4;
33
+ tag.textContent = css$4;
34
+ document.head.appendChild(tag);
35
+ }
36
+ var AttachmentRail_module_css_default = {
37
+ "arrow": "JVDQca_arrow",
38
+ "arrowLeft": "JVDQca_arrowLeft",
39
+ "arrowRight": "JVDQca_arrowRight",
40
+ "item": "JVDQca_item",
41
+ "rail": "JVDQca_rail",
42
+ "remove": "JVDQca_remove",
43
+ "root": "JVDQca_root",
44
+ "thumbnail": "JVDQca_thumbnail"
45
+ };
46
+ //#endregion
47
+ //#region lib/types/AttachmentRail.js
48
+ /** Draft-attachment thumbnail rail: scrollbar-less horizontal overflow paged
49
+ * by edge arrows, hover-revealed per-item remove, single-click open. */
50
+ /** Approximate pixels per wheel step for `deltaMode` LINE deltas (Firefox
51
+ * notch wheels report lines, not pixels). */
52
+ const WHEEL_LINE_PX = 16;
53
+ /** Smooth paging unless the user asked for reduced motion. */
54
+ function pageBehavior() {
55
+ return window.matchMedia?.("(prefers-reduced-motion: reduce)").matches ? "auto" : "smooth";
56
+ }
57
+ /**
58
+ * Horizontal thumbnail rail over the caller's draft attachments.
59
+ *
60
+ * The rail scrolls with its scrollbar hidden; overflow is announced by edge
61
+ * arrows recomputed from scroll geometry on scroll, item-count changes, and
62
+ * rail size changes (a ResizeObserver on the rail element, so sidebar or
63
+ * panel resizes count, not only window resizes). A vertical wheel pans the
64
+ * rail horizontally and is consumed exclusively (non-passive listener), a
65
+ * newly added item is revealed at the rail's end while a rail that mounts
66
+ * over an existing draft keeps its start position, and each thumbnail opens
67
+ * on a single click while its remove control sits inside the card and
68
+ * reveals on hover or focus. The owner decides mounting; it renders the rail
69
+ * only while items exist.
70
+ *
71
+ * @param props.items - resolved thumbnails in draft order.
72
+ * @param props.labels - rail-level strings (group name, open tooltip, arrows).
73
+ * @param props.onOpen - single-click open of one item's original image.
74
+ * @param props.onRemove - remove one item from the draft.
75
+ * @returns the rail group with its paging arrows.
76
+ */
77
+ function AttachmentRail({ items, labels, onOpen, onRemove }) {
78
+ const railRef = (0, react.useRef)(null);
79
+ const countRef = (0, react.useRef)(null);
80
+ const [edges, setEdges] = (0, react.useState)({
81
+ left: false,
82
+ right: false
83
+ });
84
+ const updateEdges = (0, react.useCallback)(() => {
85
+ const el = railRef.current;
86
+ /* v8 ignore next -- defensive: every caller runs while the rail element is mounted. */
87
+ if (el === null) return;
88
+ const left = el.scrollLeft > 1;
89
+ const right = el.scrollLeft < el.scrollWidth - el.clientWidth - 1;
90
+ setEdges((prev) => prev.left === left && prev.right === right ? prev : {
91
+ left,
92
+ right
93
+ });
94
+ }, []);
95
+ (0, react.useLayoutEffect)(() => {
96
+ const grew = countRef.current !== null && items.length > countRef.current;
97
+ countRef.current = items.length;
98
+ const el = railRef.current;
99
+ /* v8 ignore next -- defensive: the rail div renders unconditionally, so the layout effect always finds it. */
100
+ if (el === null) return;
101
+ if (grew) el.scrollLeft = el.scrollWidth - el.clientWidth;
102
+ updateEdges();
103
+ }, [items.length, updateEdges]);
104
+ (0, react.useEffect)(() => {
105
+ const el = railRef.current;
106
+ /* v8 ignore next -- defensive: the rail div renders unconditionally, so the mount effect always finds it. */
107
+ if (el === null) return;
108
+ let disconnect = () => {};
109
+ if (typeof ResizeObserver !== "undefined") {
110
+ const observer = new ResizeObserver(updateEdges);
111
+ observer.observe(el);
112
+ disconnect = () => {
113
+ observer.disconnect();
114
+ };
115
+ }
116
+ const onWheel = (event) => {
117
+ if (event.deltaY === 0) return;
118
+ const scale = event.deltaMode === WheelEvent.DOM_DELTA_LINE ? WHEEL_LINE_PX : event.deltaMode === WheelEvent.DOM_DELTA_PAGE ? el.clientWidth : 1;
119
+ event.preventDefault();
120
+ el.scrollBy({
121
+ left: event.deltaX !== 0 ? event.deltaX * scale : Math.sign(event.deltaY) * Math.min(Math.abs(event.deltaY) * scale, 60),
122
+ behavior: "auto"
123
+ });
124
+ };
125
+ el.addEventListener("wheel", onWheel, { passive: false });
126
+ return () => {
127
+ disconnect();
128
+ el.removeEventListener("wheel", onWheel);
129
+ };
130
+ }, [updateEdges]);
131
+ const page = (direction) => {
132
+ const el = railRef.current;
133
+ /* v8 ignore next -- defensive: the arrows render only while the rail is mounted, so a click cannot find a null ref. */
134
+ if (el === null) return;
135
+ el.scrollBy({
136
+ left: direction * Math.max(el.clientWidth - 64, 200),
137
+ behavior: pageBehavior()
138
+ });
139
+ };
140
+ return (0, react_jsx_runtime.jsxs)("div", {
141
+ className: AttachmentRail_module_css_default.root,
142
+ children: [
143
+ edges.left && (0, react_jsx_runtime.jsx)("button", {
144
+ type: "button",
145
+ className: clsx(AttachmentRail_module_css_default.arrow, AttachmentRail_module_css_default.arrowLeft),
146
+ "aria-label": labels.scrollLeft,
147
+ onClick: () => {
148
+ page(-1);
149
+ },
150
+ children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronLeftOutline14, {})
151
+ }),
152
+ (0, react_jsx_runtime.jsx)("div", {
153
+ ref: railRef,
154
+ className: AttachmentRail_module_css_default.rail,
155
+ role: "group",
156
+ "aria-label": labels.group,
157
+ onScroll: updateEdges,
158
+ children: items.map((item) => (0, react_jsx_runtime.jsxs)("div", {
159
+ className: AttachmentRail_module_css_default.item,
160
+ children: [(0, react_jsx_runtime.jsx)("button", {
161
+ type: "button",
162
+ className: AttachmentRail_module_css_default.thumbnail,
163
+ title: labels.open,
164
+ onClick: () => {
165
+ onOpen(item);
166
+ },
167
+ children: (0, react_jsx_runtime.jsx)("img", {
168
+ src: item.previewUrl,
169
+ alt: item.alt
170
+ })
171
+ }), (0, react_jsx_runtime.jsx)("button", {
172
+ type: "button",
173
+ className: AttachmentRail_module_css_default.remove,
174
+ "aria-label": item.removeLabel,
175
+ onClick: () => {
176
+ onRemove(item);
177
+ },
178
+ children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconCloseFill14, { size: 12 })
179
+ })]
180
+ }, item.id))
181
+ }),
182
+ edges.right && (0, react_jsx_runtime.jsx)("button", {
183
+ type: "button",
184
+ className: clsx(AttachmentRail_module_css_default.arrow, AttachmentRail_module_css_default.arrowRight),
185
+ "aria-label": labels.scrollRight,
186
+ onClick: () => {
187
+ page(1);
188
+ },
189
+ children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronRightOutline14, {})
190
+ })
191
+ ]
192
+ });
193
+ }
194
+ //#endregion
195
+ //#region \0dsh-css:/home/runner/work/deepseek-harness/deepseek-harness/packages/client/ui-attachment/src/DropOverlay.module.css.mjs
196
+ const css$3 = ".BInVoG_mask{z-index:1000;pointer-events:none;background-color:var(--dsw-alias-bg-mask-drop);backdrop-filter:blur(10px);justify-content:center;align-items:center;animation:.16s ease-out BInVoG_fade-in;display:flex;position:fixed;inset:0}@keyframes BInVoG_fade-in{0%{opacity:0}to{opacity:1}}@media (prefers-reduced-motion:reduce){.BInVoG_mask{animation:none}}.BInVoG_wrap{color:var(--dsw-alias-label-primary);text-align:center;flex-direction:column;align-items:center;margin-top:-3%;padding:0 40px;display:flex}.BInVoG_illustration{width:115px;height:84px}.BInVoG_title{font:var(--dsw-font-l-20);margin-top:16px}.BInVoG_desc{font:var(--dsw-font-s-14);color:var(--dsw-alias-label-tertiary);white-space:pre-wrap;margin-top:16px}";
197
+ const tagId$3 = "@deepseek-ai/dsh-client-ui-attachment/DropOverlay.module.css";
198
+ if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$3) + "]") === null) {
199
+ const tag = document.createElement("style");
200
+ tag.dataset.plugin = "@deepseek-ai/dsh-client-ui-attachment";
201
+ tag.dataset.pluginCss = tagId$3;
202
+ tag.textContent = css$3;
203
+ document.head.appendChild(tag);
204
+ }
205
+ var DropOverlay_module_css_default = {
206
+ "desc": "BInVoG_desc",
207
+ "fade-in": "BInVoG_fade-in",
208
+ "illustration": "BInVoG_illustration",
209
+ "mask": "BInVoG_mask",
210
+ "title": "BInVoG_title",
211
+ "wrap": "BInVoG_wrap"
212
+ };
213
+ //#endregion
214
+ //#region lib/types/DropOverlay.js
215
+ /**
216
+ * Full-viewport invitation shown while a file drag is over the page
217
+ * (DeepSeek Chat's DragMask). Decoration only: `pointer-events: none` keeps
218
+ * drag targeting on the page below, so the owner's document-level listeners
219
+ * keep an accurate enter/leave count and own accept/reject. Rendered through
220
+ * a body portal for the same transformed-ancestor reason as the lightbox.
221
+ *
222
+ * @param props.disabled - drops are currently refused; renders the blocked
223
+ * illustration and drops the desc line.
224
+ * @param props.labels - resolved title and limits strings.
225
+ * @returns the overlay layer.
226
+ */
227
+ function DropOverlay({ disabled, labels }) {
228
+ return (0, react_dom.createPortal)((0, react_jsx_runtime.jsx)("div", {
229
+ className: DropOverlay_module_css_default.mask,
230
+ role: "status",
231
+ children: (0, react_jsx_runtime.jsxs)("div", {
232
+ className: DropOverlay_module_css_default.wrap,
233
+ children: [
234
+ (0, react_jsx_runtime.jsx)("div", {
235
+ className: DropOverlay_module_css_default.illustration,
236
+ "aria-hidden": "true",
237
+ children: disabled ? (0, react_jsx_runtime.jsx)(UploadDisabledIllustration, {}) : (0, react_jsx_runtime.jsx)(UploadIllustration, {})
238
+ }),
239
+ (0, react_jsx_runtime.jsx)("div", {
240
+ className: DropOverlay_module_css_default.title,
241
+ children: labels.title
242
+ }),
243
+ !disabled && labels.desc !== void 0 && (0, react_jsx_runtime.jsx)("div", {
244
+ className: DropOverlay_module_css_default.desc,
245
+ children: labels.desc
246
+ })
247
+ ]
248
+ })
249
+ }), document.body);
250
+ }
251
+ /** Tilted photo-and-note cards (DeepSeek Chat upload illustration). */
252
+ const UploadIllustration = () => (0, react_jsx_runtime.jsxs)("svg", {
253
+ width: "115",
254
+ height: "84",
255
+ viewBox: "0 0 115 84",
256
+ fill: "none",
257
+ xmlns: "http://www.w3.org/2000/svg",
258
+ children: [(0, react_jsx_runtime.jsxs)("g", {
259
+ clipPath: "url(#dshDropOverlayClip)",
260
+ children: [
261
+ (0, react_jsx_runtime.jsx)("rect", {
262
+ y: "17.0742",
263
+ width: "44.1832",
264
+ height: "43.6431",
265
+ rx: "12",
266
+ transform: "rotate(-22.7338 0 17.0742)",
267
+ fill: "#9CE5ED"
268
+ }),
269
+ (0, react_jsx_runtime.jsx)("rect", {
270
+ x: "73.4043",
271
+ y: "8.54297",
272
+ width: "43.7267",
273
+ height: "50.5284",
274
+ rx: "8",
275
+ transform: "rotate(17.403 73.4043 8.54297)",
276
+ fill: "#679EFE"
277
+ }),
278
+ (0, react_jsx_runtime.jsx)("path", {
279
+ d: "M30.4917 28.1369L40.8865 33.4564L37.2232 34.9524L29.5302 31.0159L26.7919 39.2122L23.1285 40.7082L26.8287 29.6338L16.8967 24.5516L20.5601 23.0556L27.7902 26.7549L30.3639 19.052L34.0273 17.556L30.4917 28.1369Z",
280
+ fill: "white"
281
+ }),
282
+ (0, react_jsx_runtime.jsx)("path", {
283
+ d: "M77.5088 26.3047L101.057 33.7966",
284
+ stroke: "white",
285
+ strokeWidth: "3"
286
+ }),
287
+ (0, react_jsx_runtime.jsx)("path", {
288
+ d: "M72.2646 42.7871L86.3938 47.2823",
289
+ stroke: "white",
290
+ strokeWidth: "3"
291
+ }),
292
+ (0, react_jsx_runtime.jsx)("path", {
293
+ d: "M74.8867 34.5469L98.4353 42.0388",
294
+ stroke: "white",
295
+ strokeWidth: "3"
296
+ }),
297
+ (0, react_jsx_runtime.jsx)("rect", {
298
+ x: "31.583",
299
+ y: "38.6641",
300
+ width: "44.9157",
301
+ height: "44.3666",
302
+ rx: "12",
303
+ transform: "rotate(-0.134233 31.583 38.6641)",
304
+ fill: "#3964FE"
305
+ }),
306
+ (0, react_jsx_runtime.jsx)("path", {
307
+ d: "M38.9521 73.0337C39.6129 71.7086 41.7113 66.0937 43.5113 61.1663C44.1607 59.3885 46.7484 59.3923 47.4591 61.1465C48.9728 64.8828 50.7969 68.6922 51.9988 69.1925C54.2946 70.1482 57.9854 59.3573 68.0064 70.1801",
308
+ stroke: "white",
309
+ strokeWidth: "3"
310
+ }),
311
+ (0, react_jsx_runtime.jsx)("circle", {
312
+ cx: "60.6157",
313
+ cy: "52.247",
314
+ r: "4.38794",
315
+ transform: "rotate(22.5996 60.6157 52.247)",
316
+ fill: "white"
317
+ })
318
+ ]
319
+ }), (0, react_jsx_runtime.jsx)("defs", { children: (0, react_jsx_runtime.jsx)("clipPath", {
320
+ id: "dshDropOverlayClip",
321
+ children: (0, react_jsx_runtime.jsx)("rect", {
322
+ width: "115",
323
+ height: "84",
324
+ fill: "white"
325
+ })
326
+ }) })]
327
+ });
328
+ /** Greyed cards with a blocked badge (DeepSeek Chat disabled illustration). */
329
+ const UploadDisabledIllustration = () => (0, react_jsx_runtime.jsxs)("svg", {
330
+ width: "115",
331
+ height: "84",
332
+ viewBox: "0 0 115 84",
333
+ fill: "none",
334
+ xmlns: "http://www.w3.org/2000/svg",
335
+ children: [
336
+ (0, react_jsx_runtime.jsx)("path", {
337
+ d: "M29.6829 4.63701L11.0677 12.4368C4.95519 14.998 2.07624 22.0294 4.6374 28.1419L12.2285 46.259C14.7896 52.3715 21.8211 55.2505 27.9336 52.6893L46.5488 44.8895C52.6613 42.3283 55.5403 35.2969 52.9791 29.1844L45.388 11.0673C42.8269 4.9548 35.7954 2.07585 29.6829 4.63701Z",
338
+ fill: "#979DA6"
339
+ }),
340
+ (0, react_jsx_runtime.jsx)("path", {
341
+ d: "M30.4915 28.1375L40.8863 33.4569L37.223 34.9529L29.53 31.0165L26.7917 39.2128L23.1283 40.7088L26.8285 29.6344L16.8965 24.5522L20.5599 23.0562L27.79 26.7555L30.3637 19.0526L34.0271 17.5566L30.4915 28.1375Z",
342
+ fill: "white"
343
+ }),
344
+ (0, react_jsx_runtime.jsx)("path", {
345
+ d: "M107.496 19.2285L81.0381 10.9357C76.8221 9.61423 72.333 11.9607 71.0116 16.1768L60.6844 49.1246C59.363 53.3406 61.7095 57.8297 65.9255 59.1511L92.383 67.4439C96.599 68.7654 101.088 66.4189 102.41 62.2029L112.737 29.255C114.058 25.039 111.712 20.55 107.496 19.2285Z",
346
+ fill: "#979DA6"
347
+ }),
348
+ (0, react_jsx_runtime.jsx)("path", {
349
+ d: "M77.5088 26.3047L101.057 33.7967",
350
+ stroke: "white",
351
+ strokeWidth: "3"
352
+ }),
353
+ (0, react_jsx_runtime.jsx)("path", {
354
+ d: "M72.2646 42.7871L86.3938 47.2823",
355
+ stroke: "white",
356
+ strokeWidth: "3"
357
+ }),
358
+ (0, react_jsx_runtime.jsx)("path", {
359
+ d: "M74.8867 34.5469L98.4353 42.0388",
360
+ stroke: "white",
361
+ strokeWidth: "3"
362
+ }),
363
+ (0, react_jsx_runtime.jsx)("path", {
364
+ d: "M66.5798 30.1418L41.481 30.2006C33.5281 30.2193 27.0962 36.6815 27.1148 44.6343L27.172 69.0742C27.1907 77.0271 33.6529 83.459 41.6057 83.4404L66.7045 83.3816C74.6574 83.363 81.0894 76.9008 81.0707 68.9479L81.0135 44.5081C80.9949 36.5552 74.5327 30.1232 66.5798 30.1418Z",
365
+ fill: "#F59E0B"
366
+ }),
367
+ (0, react_jsx_runtime.jsx)("path", {
368
+ d: "M54 70.7969C61.732 70.7969 68 64.5289 68 56.7969C68 49.0649 61.732 42.7969 54 42.7969C46.268 42.7969 40 49.0649 40 56.7969C40 64.5289 46.268 70.7969 54 70.7969Z",
369
+ stroke: "white",
370
+ strokeWidth: "3.5"
371
+ }),
372
+ (0, react_jsx_runtime.jsx)("path", {
373
+ d: "M44 46.7969L64 66.7969",
374
+ stroke: "white",
375
+ strokeWidth: "3.5",
376
+ strokeLinecap: "round"
377
+ })
378
+ ]
379
+ });
380
+ //#endregion
381
+ //#region \0dsh-css:/home/runner/work/deepseek-harness/deepseek-harness/packages/client/ui-attachment/src/ImageLightbox.module.css.mjs
382
+ const css$2 = ".fNh4Da_backdrop{z-index:1000;place-items:center;padding:40px;display:grid;position:fixed;inset:0}.fNh4Da_mask{background:var(--dsw-alias-bg-mask-1);backdrop-filter:var(--dsw-mask-blur);position:absolute;inset:0}.fNh4Da_image{object-fit:contain;background:var(--dsw-specific-input-major);max-width:min(100%,1600px);max-height:calc(100vh - 80px);box-shadow:var(--dsw-shadow-lv3);border-radius:12px;position:relative}.fNh4Da_close{z-index:1;border:1px solid var(--dsw-alias-border-l2-darkmode-thin);background:var(--dsw-specific-input-major);width:36px;height:36px;color:var(--dsw-alias-label-primary);cursor:pointer;border-radius:999px;place-items:center;display:grid;position:fixed;top:20px;right:20px}";
383
+ const tagId$2 = "@deepseek-ai/dsh-client-ui-attachment/ImageLightbox.module.css";
384
+ if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$2) + "]") === null) {
385
+ const tag = document.createElement("style");
386
+ tag.dataset.plugin = "@deepseek-ai/dsh-client-ui-attachment";
387
+ tag.dataset.pluginCss = tagId$2;
388
+ tag.textContent = css$2;
389
+ document.head.appendChild(tag);
390
+ }
391
+ var ImageLightbox_module_css_default = {
392
+ "backdrop": "fNh4Da_backdrop",
393
+ "close": "fNh4Da_close",
394
+ "image": "fNh4Da_image",
395
+ "mask": "fNh4Da_mask"
396
+ };
397
+ //#endregion
398
+ //#region lib/types/ImageLightbox.js
399
+ /**
400
+ * Document-level original-image preview opened by clicking a thumbnail.
401
+ * Closes on Escape, backdrop press, or the close control, and restores focus
402
+ * to the opener on unmount. Rendered through a body portal: an opener inside
403
+ * a transformed or filtered ancestor would otherwise trap the fixed backdrop
404
+ * in that ancestor's box instead of covering the viewport.
405
+ *
406
+ * @param props.src - the original image URL.
407
+ * @param props.alt - the image's alt text.
408
+ * @param props.labels - dialog and close-control strings.
409
+ * @param props.onClose - dismiss callback owned by the opener.
410
+ * @returns the modal preview dialog.
411
+ */
412
+ function ImageLightbox({ src, alt, labels, onClose }) {
413
+ const closeRef = (0, react.useRef)(null);
414
+ const restoreRef = (0, react.useRef)(null);
415
+ (0, react.useEffect)(() => {
416
+ restoreRef.current = document.activeElement instanceof HTMLElement ? document.activeElement : null;
417
+ closeRef.current?.focus();
418
+ const onKeyDown = (event) => {
419
+ if (event.key === "Escape") onClose();
420
+ };
421
+ window.addEventListener("keydown", onKeyDown);
422
+ return () => {
423
+ window.removeEventListener("keydown", onKeyDown);
424
+ restoreRef.current?.focus();
425
+ };
426
+ }, [onClose]);
427
+ return (0, react_dom.createPortal)((0, react_jsx_runtime.jsxs)("div", {
428
+ className: ImageLightbox_module_css_default.backdrop,
429
+ role: "dialog",
430
+ "aria-modal": "true",
431
+ "aria-label": labels.dialog,
432
+ children: [
433
+ (0, react_jsx_runtime.jsx)("div", {
434
+ className: ImageLightbox_module_css_default.mask,
435
+ "aria-hidden": "true",
436
+ onMouseDown: onClose
437
+ }),
438
+ (0, react_jsx_runtime.jsx)("img", {
439
+ className: ImageLightbox_module_css_default.image,
440
+ src,
441
+ alt
442
+ }),
443
+ (0, react_jsx_runtime.jsx)("button", {
444
+ ref: closeRef,
445
+ type: "button",
446
+ className: ImageLightbox_module_css_default.close,
447
+ "aria-label": labels.close,
448
+ onClick: onClose,
449
+ children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconCloseOutline16, { size: 16 })
450
+ })
451
+ ]
452
+ }), document.body);
453
+ }
454
+ //#endregion
455
+ //#region lib/types/client/labels.js
456
+ /**
457
+ * Resolve original-image lightbox strings from the conversation namespace.
458
+ * @param t - conversation namespace translator.
459
+ * @returns translated lightbox labels.
460
+ */
461
+ function lightboxLabels(t) {
462
+ return {
463
+ dialog: t("image.preview"),
464
+ close: t("image.closePreview")
465
+ };
466
+ }
467
+ /**
468
+ * Resolve historical message-image strings from the conversation namespace.
469
+ * @param t - conversation namespace translator.
470
+ * @returns translated message-image labels.
471
+ */
472
+ function messageImageLabels(t) {
473
+ return {
474
+ image: t("image.label"),
475
+ open: t("image.openOriginal"),
476
+ openNamed: (label) => t("image.openOriginalLabel", { label }),
477
+ loading: t("image.loading"),
478
+ loadFailed: t("image.loadFailed"),
479
+ lightbox: lightboxLabels(t)
480
+ };
481
+ }
482
+ /**
483
+ * Resolve the document-level drop invitation and its optional limits line.
484
+ * @param t - conversation namespace translator.
485
+ * @param accepting - whether the composer can accept dropped files.
486
+ * @param limits - optional translated count and size values.
487
+ * @returns translated drop-overlay labels.
488
+ */
489
+ function dropOverlayLabels(t, accepting, limits) {
490
+ if (!accepting) return { title: t("image.dropBlocked") };
491
+ return {
492
+ title: t("image.dropTitle"),
493
+ desc: limits === void 0 ? void 0 : t("image.dropDesc", limits)
494
+ };
495
+ }
496
+ /**
497
+ * Resolve draft-image rail strings from the conversation namespace.
498
+ * @param t - conversation namespace translator.
499
+ * @returns translated attachment-rail labels.
500
+ */
501
+ function attachmentRailLabels(t) {
502
+ return {
503
+ group: t("image.pending"),
504
+ open: t("image.openOriginal"),
505
+ scrollLeft: t("image.scrollLeft"),
506
+ scrollRight: t("image.scrollRight")
507
+ };
508
+ }
509
+ //#endregion
510
+ //#region \0dsh-css:/home/runner/work/deepseek-harness/deepseek-harness/packages/client/ui-attachment/src/client/ComposerAttachments.module.css.mjs
511
+ const css$1 = "._54WpYG_rail{min-width:0;padding:4px 12px 0}";
512
+ const tagId$1 = "@deepseek-ai/dsh-client-ui-attachment/ComposerAttachments.module.css";
513
+ if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$1) + "]") === null) {
514
+ const tag = document.createElement("style");
515
+ tag.dataset.plugin = "@deepseek-ai/dsh-client-ui-attachment";
516
+ tag.dataset.pluginCss = tagId$1;
517
+ tag.textContent = css$1;
518
+ document.head.appendChild(tag);
519
+ }
520
+ var ComposerAttachments_module_css_default = { "rail": "_54WpYG_rail" };
521
+ //#endregion
522
+ //#region lib/types/client/ComposerAttachments.js
523
+ /** Draft-image rail, document drop target, and original-image preview slot entry. */
524
+ function ComposerAttachments({ attachments, canAcceptDrop, onAddImages, onRemoveImage, dropLimits, t }) {
525
+ const [preview, setPreview] = (0, react.useState)(null);
526
+ const [dragActive, setDragActive] = (0, react.useState)(false);
527
+ const dragDepth = (0, react.useRef)(0);
528
+ const closePreview = (0, react.useCallback)(() => {
529
+ setPreview(null);
530
+ }, []);
531
+ (0, react.useEffect)(() => {
532
+ if (preview !== null && !attachments.some((attachment) => attachment.id === preview.id)) setPreview(null);
533
+ }, [attachments, preview]);
534
+ (0, react.useEffect)(() => {
535
+ const fileTransfer = (event) => {
536
+ const dataTransfer = event.dataTransfer;
537
+ if (dataTransfer === null || !dataTransfer.types.includes("Files")) return null;
538
+ return dataTransfer;
539
+ };
540
+ const reset = () => {
541
+ dragDepth.current = 0;
542
+ setDragActive(false);
543
+ };
544
+ const onDragEnter = (event) => {
545
+ if (fileTransfer(event) === null) return;
546
+ event.preventDefault();
547
+ dragDepth.current += 1;
548
+ setDragActive(true);
549
+ };
550
+ const onDragOver = (event) => {
551
+ const dataTransfer = fileTransfer(event);
552
+ if (dataTransfer === null) return;
553
+ event.preventDefault();
554
+ dataTransfer.dropEffect = canAcceptDrop ? "copy" : "none";
555
+ };
556
+ const onDragLeave = (event) => {
557
+ if (fileTransfer(event) === null) return;
558
+ dragDepth.current = Math.max(0, dragDepth.current - 1);
559
+ if (dragDepth.current === 0) setDragActive(false);
560
+ const leftViewport = event.clientX <= 0 || event.clientY <= 0 || event.clientX >= window.innerWidth || event.clientY >= window.innerHeight;
561
+ if ((event.target === document.documentElement || event.target === document.body) && leftViewport) reset();
562
+ };
563
+ const onDrop = (event) => {
564
+ const dataTransfer = fileTransfer(event);
565
+ if (dataTransfer === null) return;
566
+ event.preventDefault();
567
+ reset();
568
+ if (canAcceptDrop) onAddImages([...dataTransfer.files]);
569
+ };
570
+ document.addEventListener("dragenter", onDragEnter);
571
+ document.addEventListener("dragover", onDragOver);
572
+ document.addEventListener("dragleave", onDragLeave);
573
+ document.addEventListener("drop", onDrop);
574
+ window.addEventListener("dragend", reset);
575
+ return () => {
576
+ document.removeEventListener("dragenter", onDragEnter);
577
+ document.removeEventListener("dragover", onDragOver);
578
+ document.removeEventListener("dragleave", onDragLeave);
579
+ document.removeEventListener("drop", onDrop);
580
+ window.removeEventListener("dragend", reset);
581
+ };
582
+ }, [canAcceptDrop, onAddImages]);
583
+ const railItems = (0, react.useMemo)(() => attachments.map((attachment) => ({
584
+ id: attachment.id,
585
+ previewUrl: attachment.previewUrl,
586
+ alt: attachment.file.name || t("image.pending"),
587
+ removeLabel: t("image.remove", { name: attachment.file.name }),
588
+ attachment
589
+ })), [attachments, t]);
590
+ return (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
591
+ dragActive && (0, react_jsx_runtime.jsx)(DropOverlay, {
592
+ disabled: !canAcceptDrop,
593
+ labels: dropOverlayLabels(t, canAcceptDrop, dropLimits)
594
+ }),
595
+ railItems.length > 0 && (0, react_jsx_runtime.jsx)("div", {
596
+ className: ComposerAttachments_module_css_default.rail,
597
+ children: (0, react_jsx_runtime.jsx)(AttachmentRail, {
598
+ items: railItems,
599
+ labels: attachmentRailLabels(t),
600
+ onOpen: (item) => {
601
+ setPreview(item.attachment);
602
+ },
603
+ onRemove: (item) => {
604
+ onRemoveImage(item.attachment.id);
605
+ }
606
+ })
607
+ }),
608
+ preview !== null && (0, react_jsx_runtime.jsx)(ImageLightbox, {
609
+ src: preview.previewUrl,
610
+ alt: preview.file.name || t("image.original"),
611
+ labels: lightboxLabels(t),
612
+ onClose: closePreview
613
+ })
614
+ ] });
615
+ }
616
+ //#endregion
617
+ //#region \0dsh-css:/home/runner/work/deepseek-harness/deepseek-harness/packages/client/ui-attachment/src/MessageImage.module.css.mjs
618
+ const css = ".R_Yw7q_gallery{flex-wrap:wrap;gap:10px;max-width:100%;display:flex}.R_Yw7q_gallery[data-align=end]{justify-content:flex-end;align-self:flex-end}.R_Yw7q_gallery[data-align=start]{justify-content:flex-start;align-self:flex-start}.R_Yw7q_frame{border:1px solid var(--dsw-alias-border-l2-darkmode-thin);background:var(--dsw-alias-interactive-bg-hover);cursor:zoom-in;border-radius:16px;flex:none;place-items:center;min-width:44px;min-height:44px;padding:0;display:grid;overflow:hidden}.R_Yw7q_frame[data-variant=tile]{width:64px;min-width:64px;height:64px;min-height:64px}.R_Yw7q_frame img{object-fit:cover;width:100%;height:100%;display:block}.R_Yw7q_loading,.R_Yw7q_error{color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:18px}.R_Yw7q_error{border:1px solid var(--dsw-alias-border-l2-darkmode-thin);background:var(--dsw-alias-interactive-bg-hover-danger);cursor:pointer;border-radius:10px;max-width:240px;padding:10px 12px}.R_Yw7q_error[data-variant=tile]{border-radius:16px;width:64px;height:64px;padding:4px;overflow:hidden}";
619
+ const tagId = "@deepseek-ai/dsh-client-ui-attachment/MessageImage.module.css";
620
+ if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
621
+ const tag = document.createElement("style");
622
+ tag.dataset.plugin = "@deepseek-ai/dsh-client-ui-attachment";
623
+ tag.dataset.pluginCss = tagId;
624
+ tag.textContent = css;
625
+ document.head.appendChild(tag);
626
+ }
627
+ var MessageImage_module_css_default = {
628
+ "error": "R_Yw7q_error",
629
+ "frame": "R_Yw7q_frame",
630
+ "gallery": "R_Yw7q_gallery",
631
+ "loading": "R_Yw7q_loading"
632
+ };
633
+ //#endregion
634
+ //#region lib/types/MessageImage.js
635
+ /** Display box for a lone image (DeepSeek Chat rule): long edge 240px with
636
+ * the rendered aspect ratio clamped to [0.25, 4] — the overflow is cropped by
637
+ * `object-fit: cover` — and never upscaled past the image's natural size. The
638
+ * crop anchor keeps the top of very tall images and the left of very wide
639
+ * ones, where the informative content usually starts. */
640
+ function singleFit(attachment) {
641
+ const natural = attachment.width / attachment.height;
642
+ const ratio = Math.min(4, Math.max(.25, natural));
643
+ const box = ratio >= 1 ? {
644
+ width: 240,
645
+ height: 240 / ratio
646
+ } : {
647
+ width: 240 * ratio,
648
+ height: 240
649
+ };
650
+ const scale = Math.min(1, attachment.width / box.width, attachment.height / box.height);
651
+ return {
652
+ width: Math.max(1, Math.round(box.width * scale)),
653
+ height: Math.max(1, Math.round(box.height * scale)),
654
+ objectPosition: natural < .25 ? "center top" : natural > 4 ? "left center" : "center"
655
+ };
656
+ }
657
+ /**
658
+ * Compact history renderer with retryable loading and click-to-open original
659
+ * preview. A lone image renders at its `singleFit` size; an image among
660
+ * several renders as a fixed 64px square tile.
661
+ *
662
+ * @param props.attachment - the durable image reference to load and bound.
663
+ * @param props.load - session-authorized URL loader.
664
+ * @param props.variant - `single` for a message's lone image, `tile` otherwise.
665
+ * @param props.labels - resolved strings (tooltip, loading, retry, lightbox).
666
+ * @returns the bounded thumbnail button, or the retry control on failure.
667
+ */
668
+ function MessageImage({ attachment, load, variant, labels }) {
669
+ const [src, setSrc] = (0, react.useState)(null);
670
+ const [error, setError] = (0, react.useState)(false);
671
+ const [open, setOpen] = (0, react.useState)(false);
672
+ const [attempt, setAttempt] = (0, react.useState)(0);
673
+ const request = (0, react.useCallback)(() => {
674
+ setAttempt((a) => a + 1);
675
+ }, []);
676
+ const close = (0, react.useCallback)(() => {
677
+ setOpen(false);
678
+ }, []);
679
+ const fit = (0, react.useMemo)(() => variant === "single" ? singleFit(attachment) : void 0, [attachment, variant]);
680
+ (0, react.useEffect)(() => {
681
+ let live = true;
682
+ setError(false);
683
+ setSrc(null);
684
+ load(attachment).then((url) => {
685
+ if (live) setSrc(url);
686
+ }).catch(() => {
687
+ if (live) setError(true);
688
+ });
689
+ return () => {
690
+ live = false;
691
+ };
692
+ }, [
693
+ attachment,
694
+ load,
695
+ attempt
696
+ ]);
697
+ const label = attachment.name ?? labels.image;
698
+ if (error) return (0, react_jsx_runtime.jsx)("button", {
699
+ type: "button",
700
+ className: MessageImage_module_css_default.error,
701
+ "data-variant": variant,
702
+ onClick: request,
703
+ children: labels.loadFailed
704
+ });
705
+ return (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [(0, react_jsx_runtime.jsx)("button", {
706
+ type: "button",
707
+ className: MessageImage_module_css_default.frame,
708
+ "data-variant": variant,
709
+ style: fit === void 0 ? void 0 : {
710
+ width: fit.width,
711
+ height: fit.height
712
+ },
713
+ title: labels.open,
714
+ "aria-label": labels.openNamed(label),
715
+ onClick: () => {
716
+ if (src !== null) setOpen(true);
717
+ },
718
+ children: src === null ? (0, react_jsx_runtime.jsx)("span", {
719
+ className: MessageImage_module_css_default.loading,
720
+ children: labels.loading
721
+ }) : (0, react_jsx_runtime.jsx)("img", {
722
+ src,
723
+ alt: label,
724
+ style: fit === void 0 ? void 0 : { objectPosition: fit.objectPosition }
725
+ })
726
+ }), open && src !== null && (0, react_jsx_runtime.jsx)(ImageLightbox, {
727
+ src,
728
+ alt: label,
729
+ labels: labels.lightbox,
730
+ onClose: close
731
+ })] });
732
+ }
733
+ /** Wrapping image group shared by user and assistant history: a lone image
734
+ * renders large, several render as 64px square tiles (DeepSeek Chat rule). */
735
+ function ImageGallery({ images, load, align, labels }) {
736
+ if (images.length === 0) return null;
737
+ const variant = images.length === 1 ? "single" : "tile";
738
+ return (0, react_jsx_runtime.jsx)("div", {
739
+ className: MessageImage_module_css_default.gallery,
740
+ "data-align": align,
741
+ children: images.map((image, index) => (0, react_jsx_runtime.jsx)(MessageImage, {
742
+ ...image,
743
+ load,
744
+ variant,
745
+ labels
746
+ }, `${image.attachment.attachmentId}:${index}`))
747
+ });
748
+ }
749
+ //#endregion
750
+ //#region lib/types/client/MessageImages.js
751
+ /** Historical message-image slot entry. */
752
+ function MessageImages({ images, loadImage, align, t }) {
753
+ return (0, react_jsx_runtime.jsx)(ImageGallery, {
754
+ images,
755
+ load: loadImage,
756
+ align,
757
+ labels: messageImageLabels(t)
758
+ });
759
+ }
760
+ //#endregion
761
+ //#region lib/types/client/index.js
762
+ /** Slot registry required by this presentation plugin. */
763
+ const inject = ["slots"];
764
+ /** Register attachment presentation without exporting React components as package values. */
765
+ function apply(ctx) {
766
+ ctx.slots.inject("conversation.input.attachments", () => ctx.slots.register({
767
+ name: "conversation.input.attachments",
768
+ locale: "conversation"
769
+ }, ComposerAttachments));
770
+ ctx.slots.inject("conversation.message.images", () => ctx.slots.register({
771
+ name: "conversation.message.images",
772
+ locale: "conversation"
773
+ }, MessageImages));
774
+ }
775
+ //#endregion
776
+ exports.apply = apply;
777
+ exports.inject = inject;
778
+ return module.exports;
779
+ }
780
+ });
781
+
782
+ //# sourceMappingURL=client.js.map