dsh-image-viewer 0.1.0-beta.10 → 0.1.0-beta.11

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.
package/AGENTS.md CHANGED
@@ -13,7 +13,7 @@ Use this guide only when the user asks to install, update, verify, or remove `ds
13
13
  ## Install or update
14
14
 
15
15
  ```sh
16
- dsh plugin --profile web add dsh-image-viewer@0.1.0-beta.10
16
+ dsh plugin --profile web add dsh-image-viewer@0.1.0-beta.11
17
17
  ```
18
18
 
19
19
  DSH-Portable exposes the same standard `dsh plugin` command. Do not use a private executable path in public instructions.
package/README.en.md CHANGED
@@ -30,6 +30,8 @@ Release checks exercise real attachments on stable and current Alpha DSH: galler
30
30
 
31
31
  - Wheel zoom centered on the pointer, drag-to-pan, touch pinch, and double-click 100% view.
32
32
  - Fit and original-size controls, original-file download, keyboard navigation, and multi-image galleries.
33
+ - Visible filename, dimensions and pixel-scale percentage; very wide images can reach 100%, and window resizing keeps the image within reach.
34
+ - Loading and retryable error states, gesture reset on image changes, and independent notes for unnamed images.
33
35
  - One-shot region marking with each note edited beside its numbered image marker. Enter saves and collapses the note;
34
36
  Shift+Enter adds a new line.
35
37
  - Light and dark themes through DSH design tokens, responsive layout, focus containment, and reduced-motion support.
@@ -42,7 +44,7 @@ DSH keeps working when the plugin is absent or cannot recognize a newer image su
42
44
  Install the Beta:
43
45
 
44
46
  ```sh
45
- dsh plugin --profile web add dsh-image-viewer@0.1.0-beta.10
47
+ dsh plugin --profile web add dsh-image-viewer@0.1.0-beta.11
46
48
  ```
47
49
 
48
50
  Restart DSH manually after saving active work. The plugin never needs access to provider credentials or image-generation accounts.
package/README.md CHANGED
@@ -30,6 +30,8 @@
30
30
 
31
31
  - 以鼠标位置为中心的滚轮缩放、拖动查看、触控双指缩放和双击原始大小。
32
32
  - 适应窗口、原始大小、原图下载、键盘切换和多图浏览。
33
+ - 显示图片名称、尺寸与真实像素缩放比例;超宽大图也能切换到 100%,调整窗口后保持图片可见。
34
+ - 图片加载中和加载失败都有明确提示,失败可重试;切换图片会重置拖拽状态,区域备注不会串到另一张未命名图片。
33
35
  - 一次点击标记一个区域,备注直接显示在图片编号附近并自动获得输入焦点;Enter 保存并收起,Shift+Enter 换行。
34
36
  - 使用 DSH 设计令牌适配亮色、暗色、响应式布局、焦点约束和减少动态效果设置。
35
37
  - 提供可选的 `nativeImageViewer` 客户端服务,图片生成插件可以按需增加自己的后续操作。
@@ -41,7 +43,7 @@
41
43
  安装 Beta:
42
44
 
43
45
  ```sh
44
- dsh plugin --profile web add dsh-image-viewer@0.1.0-beta.10
46
+ dsh plugin --profile web add dsh-image-viewer@0.1.0-beta.11
45
47
  ```
46
48
 
47
49
  保存正在进行的工作后手动重启 DSH。本插件不需要供应商凭据或图片生成账户权限。
@@ -7,6 +7,7 @@
7
7
  "previews": [
8
8
  "0.1.2-alpha.2",
9
9
  "0.1.2-alpha.3",
10
- "0.1.3-alpha.2"
10
+ "0.1.3-alpha.2",
11
+ "0.1.5-alpha.1"
11
12
  ]
12
13
  }
package/lib/client.js CHANGED
@@ -29,7 +29,7 @@ window.__ModuleLoader__.load({
29
29
  const items = (Array.isArray(request?.items) ? request.items : []).flatMap((item, position) => {
30
30
  if (typeof item?.src !== "string" || item.src === "") return [];
31
31
  return [{
32
- id: typeof item.id === "string" && item.id !== "" ? item.id : `image-${position + 1}`,
32
+ id: typeof item.id === "string" && item.id !== "" ? item.id : item.src,
33
33
  src: item.src,
34
34
  name: typeof item.name === "string" && item.name !== "" ? item.name : `Image ${position + 1}`,
35
35
  width: boundedNumber(item.width, void 0),
@@ -144,6 +144,7 @@ window.__ModuleLoader__.load({
144
144
  const CSS$1 = String.raw`
145
145
  .niv-root{position:fixed;inset:0;z-index:1000;pointer-events:auto;overflow:hidden;background:rgba(7,8,10,.68);color:var(--dsw-alias-label-primary-inverted,#fff);outline:0;backdrop-filter:blur(13px) saturate(.72);-webkit-backdrop-filter:blur(13px) saturate(.72)}
146
146
  .niv-sr-only{position:absolute!important;width:1px!important;height:1px!important;padding:0!important;margin:-1px!important;overflow:hidden!important;clip:rect(0,0,0,0)!important;white-space:nowrap!important;border:0!important}
147
+ .niv-title{position:absolute;top:20px;left:24px;right:84px;display:flex;align-items:baseline;gap:12px;color:#fff;font-size:13px}.niv-title strong{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.niv-title small{flex:none;opacity:.7}.niv-load-status{position:absolute;z-index:7;display:flex;align-items:center;gap:12px;color:#fff;font-size:14px}
147
148
  .niv-topbar{position:absolute;right:50%;bottom:22px;z-index:6;max-width:calc(100vw - 36px);padding:5px;border:1px solid rgba(255,255,255,.12);border-radius:999px;background:rgba(38,39,43,.86);box-shadow:0 10px 34px rgba(0,0,0,.3);transform:translateX(50%);backdrop-filter:blur(18px);-webkit-backdrop-filter:blur(18px)}
148
149
  .niv-actions{display:flex;align-items:center;gap:2px;overflow-x:auto;scrollbar-width:none}.niv-actions::-webkit-scrollbar{display:none}.niv-button,.niv-download{box-sizing:border-box;display:inline-flex;align-items:center;justify-content:center;gap:6px;height:32px;padding:0 10px;border:0;border-radius:999px;background:transparent;color:rgba(255,255,255,.9);font:inherit;font-size:12px;text-decoration:none;white-space:nowrap;cursor:pointer}.niv-button:hover,.niv-download:hover,.niv-button[data-active=true]{background:rgba(255,255,255,.12)}.niv-button:focus-visible,.niv-download:focus-visible,.niv-close-floating:focus-visible{outline:2px solid rgba(255,255,255,.9);outline-offset:2px}.niv-button:disabled,.niv-download:disabled{opacity:.38;cursor:default}.niv-icon-only{width:32px;padding:0}.niv-zoom{min-width:44px;color:rgba(255,255,255,.68);font-size:12px;font-variant-numeric:tabular-nums;text-align:center}
149
150
  .niv-close-floating{position:absolute;top:20px;right:20px;z-index:8;display:grid;place-items:center;width:42px;height:42px;padding:0;border:1px solid rgba(255,255,255,.1);border-radius:50%;background:rgba(48,49,53,.82);box-shadow:0 8px 24px rgba(0,0,0,.28);color:#fff;cursor:pointer;backdrop-filter:blur(16px);-webkit-backdrop-filter:blur(16px)}.niv-close-floating:hover{background:rgba(66,67,72,.92)}
@@ -158,6 +159,168 @@ window.__ModuleLoader__.load({
158
159
  @media(max-width:760px){.niv-close-floating{top:12px;right:12px;width:40px;height:40px}.niv-workspace{padding:60px 12px 68px}.niv-image{max-width:calc(100vw - 24px);max-height:calc(100vh - 136px)}.niv-topbar{bottom:12px;max-width:calc(100vw - 24px)}.niv-button{padding:0 8px}.niv-button span.niv-label{display:none}.niv-inline-note{width:min(260px,calc(100vw - 40px))}.niv-copy-notes{display:none}}
159
160
  @media(prefers-reduced-motion:reduce){.niv-surface{transition:none}}
160
161
  `;
162
+ //#endregion
163
+ //#region src/image-transform.js
164
+ const clamp$1 = (value, min, max) => Math.min(max, Math.max(min, value));
165
+ const fitted = () => ({
166
+ zoom: 1,
167
+ x: 0,
168
+ y: 0
169
+ });
170
+ function useImageTransform(identity) {
171
+ const [transform, setTransform] = (0, react.useState)(fitted);
172
+ const [dragging, setDragging] = (0, react.useState)(false);
173
+ const [pixelScale, setPixelScale] = (0, react.useState)(1);
174
+ const stageRef = (0, react.useRef)(null), surfaceRef = (0, react.useRef)(null), imageRef = (0, react.useRef)(null);
175
+ const transformRef = (0, react.useRef)(transform);
176
+ const pointers = (0, react.useRef)(/* @__PURE__ */ new Map()), gesture = (0, react.useRef)();
177
+ transformRef.current = transform;
178
+ const resetGesture = (0, react.useCallback)(() => {
179
+ pointers.current.clear();
180
+ gesture.current = void 0;
181
+ setDragging(false);
182
+ }, []);
183
+ const fit = (0, react.useCallback)(() => {
184
+ resetGesture();
185
+ setTransform(fitted());
186
+ }, [resetGesture]);
187
+ (0, react.useEffect)(() => {
188
+ fit();
189
+ }, [identity, fit]);
190
+ const actualScale = () => imageRef.current?.naturalWidth / Math.max(1, surfaceRef.current?.offsetWidth || 1) || 1;
191
+ const boundedPan = (0, react.useCallback)((zoom, x, y) => {
192
+ const stage = stageRef.current, surface = surfaceRef.current;
193
+ if (!stage || !surface || zoom <= 1) return {
194
+ x: 0,
195
+ y: 0
196
+ };
197
+ const limitX = Math.max(0, (surface.offsetWidth * zoom - stage.clientWidth) / 2);
198
+ const limitY = Math.max(0, (surface.offsetHeight * zoom - stage.clientHeight) / 2);
199
+ return {
200
+ x: clamp$1(x, -limitX, limitX),
201
+ y: clamp$1(y, -limitY, limitY)
202
+ };
203
+ }, []);
204
+ const setZoomAt = (0, react.useCallback)((zoom, clientX, clientY) => {
205
+ const stage = stageRef.current;
206
+ if (!stage) return;
207
+ setTransform((current) => {
208
+ const next = clamp$1(zoom, .5, Math.max(8, actualScale()));
209
+ const box = stage.getBoundingClientRect();
210
+ const px = clientX - box.left - box.width / 2, py = clientY - box.top - box.height / 2;
211
+ const ratio = next / current.zoom;
212
+ return {
213
+ zoom: next,
214
+ ...boundedPan(next, px - (px - current.x) * ratio, py - (py - current.y) * ratio)
215
+ };
216
+ });
217
+ }, [boundedPan]);
218
+ const actual = (0, react.useCallback)(() => {
219
+ if (!imageRef.current?.naturalWidth) return;
220
+ resetGesture();
221
+ setTransform({
222
+ zoom: actualScale(),
223
+ x: 0,
224
+ y: 0
225
+ });
226
+ }, [resetGesture]);
227
+ const measure = (0, react.useCallback)(() => {
228
+ setPixelScale(1 / actualScale());
229
+ setTransform((current) => ({
230
+ ...current,
231
+ ...boundedPan(current.zoom, current.x, current.y)
232
+ }));
233
+ }, [boundedPan]);
234
+ (0, react.useEffect)(() => {
235
+ if (!stageRef.current || !surfaceRef.current) return;
236
+ const observer = new ResizeObserver(measure);
237
+ observer.observe(stageRef.current);
238
+ observer.observe(surfaceRef.current);
239
+ measure();
240
+ return () => observer.disconnect();
241
+ }, [identity, measure]);
242
+ (0, react.useEffect)(() => {
243
+ const stage = stageRef.current;
244
+ if (!stage) return;
245
+ const wheel = (event) => {
246
+ event.preventDefault();
247
+ const delta = event.deltaY * (event.deltaMode === 1 ? 16 : event.deltaMode === 2 ? stage.clientHeight : 1);
248
+ setZoomAt(transformRef.current.zoom * Math.exp(-delta * .0015), event.clientX, event.clientY);
249
+ };
250
+ stage.addEventListener("wheel", wheel, { passive: false });
251
+ return () => stage.removeEventListener("wheel", wheel);
252
+ }, [identity, setZoomAt]);
253
+ const onPointerDown = (event) => {
254
+ if (event.button !== 0 || event.target.closest("button,a,input,textarea,select")) return;
255
+ const current = transformRef.current;
256
+ if (event.pointerType === "mouse" && current.zoom <= 1) return;
257
+ event.currentTarget.setPointerCapture(event.pointerId);
258
+ pointers.current.set(event.pointerId, {
259
+ x: event.clientX,
260
+ y: event.clientY
261
+ });
262
+ if (pointers.current.size === 2) {
263
+ const [a, b] = [...pointers.current.values()];
264
+ gesture.current = {
265
+ kind: "pinch",
266
+ distance: Math.hypot(a.x - b.x, a.y - b.y),
267
+ transform: current
268
+ };
269
+ } else gesture.current = {
270
+ kind: "pan",
271
+ x: event.clientX,
272
+ y: event.clientY,
273
+ transform: current
274
+ };
275
+ setDragging(true);
276
+ };
277
+ const onPointerMove = (event) => {
278
+ if (!pointers.current.has(event.pointerId)) return;
279
+ pointers.current.set(event.pointerId, {
280
+ x: event.clientX,
281
+ y: event.clientY
282
+ });
283
+ const start = gesture.current;
284
+ if (start?.kind === "pinch" && pointers.current.size >= 2) {
285
+ const [a, b] = [...pointers.current.values()];
286
+ setZoomAt(start.transform.zoom * Math.hypot(a.x - b.x, a.y - b.y) / Math.max(1, start.distance), (a.x + b.x) / 2, (a.y + b.y) / 2);
287
+ } else if (start?.kind === "pan") setTransform({
288
+ zoom: start.transform.zoom,
289
+ ...boundedPan(start.transform.zoom, start.transform.x + event.clientX - start.x, start.transform.y + event.clientY - start.y)
290
+ });
291
+ };
292
+ const endPointer = (event) => {
293
+ pointers.current.delete(event.pointerId);
294
+ const remaining = [...pointers.current.values()][0];
295
+ if (remaining) gesture.current = {
296
+ kind: "pan",
297
+ ...remaining,
298
+ transform: transformRef.current
299
+ };
300
+ else resetGesture();
301
+ };
302
+ return {
303
+ transform,
304
+ transformRef,
305
+ dragging,
306
+ pixelScale,
307
+ stageRef,
308
+ surfaceRef,
309
+ imageRef,
310
+ fit,
311
+ actual,
312
+ measure,
313
+ setZoomAt,
314
+ resetGesture,
315
+ pointerHandlers: {
316
+ onPointerDown,
317
+ onPointerMove,
318
+ onPointerUp: endPointer,
319
+ onPointerCancel: endPointer,
320
+ onLostPointerCapture: endPointer
321
+ }
322
+ };
323
+ }
161
324
  //#endregion
162
325
  //#region src/client.jsx
163
326
  const name = "dsh-image-viewer";
@@ -183,7 +346,11 @@ window.__ModuleLoader__.load({
183
346
  next: "Next image",
184
347
  zoomHint: "Wheel to zoom · drag to pan · double-click for 100%",
185
348
  preparing: "Preparing…",
186
- failed: "Could not prepare this image."
349
+ failed: "Could not prepare this image.",
350
+ loading: "Loading image…",
351
+ loadFailed: "Image could not be loaded.",
352
+ retry: "Retry",
353
+ copyFailed: "Copy failed. Try again."
187
354
  },
188
355
  zh: {
189
356
  dialog: "图片查看器",
@@ -205,7 +372,11 @@ window.__ModuleLoader__.load({
205
372
  next: "下一张图片",
206
373
  zoomHint: "滚轮缩放 · 拖动查看 · 双击切换原始大小",
207
374
  preparing: "正在准备…",
208
- failed: "暂时无法准备这张图片。"
375
+ failed: "暂时无法准备这张图片。",
376
+ loading: "正在加载图片…",
377
+ loadFailed: "图片加载失败。",
378
+ retry: "重试",
379
+ copyFailed: "复制失败,请重试。"
209
380
  }
210
381
  };
211
382
  const fill = (value, variables) => Object.entries(variables).reduce((text, [key, replacement]) => text.replaceAll(`{${key}}`, String(replacement)), value);
@@ -281,12 +452,9 @@ window.__ModuleLoader__.load({
281
452
  function ViewerOverlay({ service, t }) {
282
453
  const request = (0, react.useSyncExternalStore)(service.subscribe, service.getSnapshot);
283
454
  const [index, setIndex] = (0, react.useState)(0);
284
- const [transform, setTransform] = (0, react.useState)({
285
- zoom: 1,
286
- x: 0,
287
- y: 0
288
- });
289
- const [dragging, setDragging] = (0, react.useState)(false);
455
+ const [imageState, setImageState] = (0, react.useState)("loading");
456
+ const [attempt, setAttempt] = (0, react.useState)(0);
457
+ const [copyFailed, setCopyFailed] = (0, react.useState)(false);
290
458
  const [annotating, setAnnotating] = (0, react.useState)(false);
291
459
  const [annotationsByImage, setAnnotationsByImage] = (0, react.useState)(service.getAnnotationsSnapshot);
292
460
  const annotationsByImageRef = (0, react.useRef)(annotationsByImage);
@@ -294,28 +462,16 @@ window.__ModuleLoader__.load({
294
462
  const [focusNote, setFocusNote] = (0, react.useState)();
295
463
  const [copied, setCopied] = (0, react.useState)(false);
296
464
  const rootRef = (0, react.useRef)(null);
297
- const stageRef = (0, react.useRef)(null);
298
- const surfaceRef = (0, react.useRef)(null);
299
- const imageRef = (0, react.useRef)(null);
300
- const pointersRef = (0, react.useRef)(/* @__PURE__ */ new Map());
301
- const gestureRef = (0, react.useRef)();
302
- const transformRef = (0, react.useRef)(transform);
303
- transformRef.current = transform;
304
465
  annotationsByImageRef.current = annotationsByImage;
305
466
  (0, react.useEffect)(() => {
306
467
  if (request === void 0) return;
307
468
  setIndex(request.index);
308
- setTransform({
309
- zoom: 1,
310
- x: 0,
311
- y: 0
312
- });
313
- setDragging(false);
314
469
  setAnnotating(false);
315
470
  setSelected(void 0);
316
471
  setCopied(false);
317
472
  }, [request?.revision]);
318
473
  const item = request?.items[index];
474
+ const { transform, transformRef, dragging, pixelScale, stageRef, surfaceRef, imageRef, fit, actual, measure, setZoomAt, resetGesture, pointerHandlers } = useImageTransform(`${request?.revision}:${item?.id}:${item?.src}`);
319
475
  const annotations = item === void 0 ? [] : annotationsByImage[item.id] ?? [];
320
476
  const setAnnotations = (0, react.useCallback)((update) => {
321
477
  if (item === void 0) return;
@@ -329,61 +485,16 @@ window.__ModuleLoader__.load({
329
485
  service.setAnnotations(item.id, next);
330
486
  setAnnotationsByImage(snapshot);
331
487
  }, [item?.id, service]);
332
- const boundedPan = (0, react.useCallback)((zoom, x, y) => {
333
- const stage = stageRef.current;
334
- const surface = surfaceRef.current;
335
- if (stage === null || surface === null || zoom <= 1) return {
336
- x: 0,
337
- y: 0
338
- };
339
- const limitX = Math.max(0, (surface.offsetWidth * zoom - stage.clientWidth) / 2) + 28;
340
- const limitY = Math.max(0, (surface.offsetHeight * zoom - stage.clientHeight) / 2) + 28;
341
- return {
342
- x: clamp(x, -limitX, limitX),
343
- y: clamp(y, -limitY, limitY)
344
- };
345
- }, []);
346
- const setZoomAt = (0, react.useCallback)((nextZoom, clientX, clientY) => {
347
- const stage = stageRef.current;
348
- if (stage === null) return;
349
- setTransform((current) => {
350
- const next = clamp(nextZoom, .5, 8);
351
- const box = stage.getBoundingClientRect();
352
- const px = clientX - box.left - box.width / 2;
353
- const py = clientY - box.top - box.height / 2;
354
- const ratio = next / current.zoom;
355
- return {
356
- zoom: next,
357
- ...boundedPan(next, px - (px - current.x) * ratio, py - (py - current.y) * ratio)
358
- };
359
- });
360
- }, [boundedPan]);
361
- const fit = (0, react.useCallback)(() => {
362
- setTransform({
363
- zoom: 1,
364
- x: 0,
365
- y: 0
366
- });
367
- }, []);
368
- const actual = (0, react.useCallback)(() => {
369
- const image = imageRef.current;
370
- const surface = surfaceRef.current;
371
- if (image === null || surface === null || image.naturalWidth === 0) return;
372
- const zoom = clamp(image.naturalWidth / Math.max(1, surface.offsetWidth), 1, 8);
373
- setTransform({
374
- zoom,
375
- x: 0,
376
- y: 0
377
- });
378
- }, []);
379
488
  (0, react.useEffect)(() => {
380
489
  if (request === void 0) return void 0;
381
490
  const previousOverflow = document.body.style.overflow;
382
491
  document.body.style.overflow = "hidden";
383
492
  rootRef.current?.focus();
384
493
  const onKeyDown = (event) => {
494
+ if (event.defaultPrevented || event.isComposing) return;
385
495
  if (event.key === "Escape") {
386
496
  event.preventDefault();
497
+ event.stopPropagation();
387
498
  if (event.target instanceof Element && event.target.closest(".niv-inline-note") !== null) {
388
499
  setSelected(void 0);
389
500
  return;
@@ -391,7 +502,17 @@ window.__ModuleLoader__.load({
391
502
  service.close();
392
503
  return;
393
504
  }
394
- const editing = event.target instanceof HTMLInputElement || event.target instanceof HTMLTextAreaElement;
505
+ if (event.ctrlKey || event.metaKey || event.altKey) return;
506
+ const editing = event.target instanceof Element && event.target.closest("input,textarea,select,[contenteditable=\"true\"]") !== null;
507
+ if (!editing && [
508
+ "ArrowLeft",
509
+ "ArrowRight",
510
+ "+",
511
+ "=",
512
+ "-",
513
+ "f",
514
+ "F"
515
+ ].includes(event.key)) event.stopPropagation();
395
516
  if (!editing && event.key === "ArrowLeft" && request.items.length > 1) {
396
517
  event.preventDefault();
397
518
  setIndex((value) => (value - 1 + request.items.length) % request.items.length);
@@ -413,19 +534,20 @@ window.__ModuleLoader__.load({
413
534
  const controls = [...rootRef.current.querySelectorAll("button:not(:disabled),a[href],input:not(:disabled),textarea:not(:disabled),[tabindex]:not([tabindex=\"-1\"])")];
414
535
  const first = controls[0];
415
536
  const last = controls.at(-1);
416
- if (event.shiftKey && document.activeElement === first) {
537
+ if (event.shiftKey && (document.activeElement === first || document.activeElement === rootRef.current)) {
417
538
  event.preventDefault();
418
539
  last?.focus();
419
- } else if (!event.shiftKey && document.activeElement === last) {
540
+ } else if (!event.shiftKey && (document.activeElement === last || document.activeElement === rootRef.current)) {
420
541
  event.preventDefault();
421
542
  first?.focus();
422
543
  }
423
544
  }
424
545
  };
425
- document.addEventListener("keydown", onKeyDown);
546
+ const root = rootRef.current;
547
+ root.addEventListener("keydown", onKeyDown);
426
548
  return () => {
427
549
  document.body.style.overflow = previousOverflow;
428
- document.removeEventListener("keydown", onKeyDown);
550
+ root.removeEventListener("keydown", onKeyDown);
429
551
  };
430
552
  }, [
431
553
  request,
@@ -443,77 +565,16 @@ window.__ModuleLoader__.load({
443
565
  annotations.length
444
566
  ]);
445
567
  (0, react.useEffect)(() => {
446
- setTransform({
447
- zoom: 1,
448
- x: 0,
449
- y: 0
450
- });
451
- setDragging(false);
452
568
  setAnnotating(false);
453
569
  setSelected(void 0);
454
- }, [item?.id]);
455
- const onWheel = (0, react.useCallback)((event) => {
456
- event.preventDefault();
457
- setZoomAt(transformRef.current.zoom * Math.exp(-event.deltaY * .0015), event.clientX, event.clientY);
458
- }, [setZoomAt]);
459
- (0, react.useEffect)(() => {
460
- const stage = stageRef.current;
461
- if (stage === null || request === void 0) return void 0;
462
- stage.addEventListener("wheel", onWheel, { passive: false });
463
- return () => stage.removeEventListener("wheel", onWheel);
464
- }, [onWheel, request]);
465
- const onPointerDown = (event) => {
466
- if (event.button !== 0 || annotating) return;
467
- if (event.target instanceof Element && event.target.closest("button,a,input,textarea,select")) return;
468
- if (event.pointerType === "mouse" && transform.zoom <= 1) return;
469
- event.currentTarget.setPointerCapture(event.pointerId);
470
- pointersRef.current.set(event.pointerId, {
471
- x: event.clientX,
472
- y: event.clientY
473
- });
474
- if (pointersRef.current.size === 2) {
475
- const [a, b] = [...pointersRef.current.values()];
476
- gestureRef.current = {
477
- kind: "pinch",
478
- distance: Math.hypot(a.x - b.x, a.y - b.y),
479
- transform
480
- };
481
- } else if (!annotating && transform.zoom > 1) {
482
- gestureRef.current = {
483
- kind: "pan",
484
- x: event.clientX,
485
- y: event.clientY,
486
- transform
487
- };
488
- setDragging(true);
489
- }
490
- };
491
- const onPointerMove = (event) => {
492
- if (!pointersRef.current.has(event.pointerId)) return;
493
- pointersRef.current.set(event.pointerId, {
494
- x: event.clientX,
495
- y: event.clientY
496
- });
497
- const gesture = gestureRef.current;
498
- if (gesture?.kind === "pinch" && pointersRef.current.size >= 2) {
499
- const [a, b] = [...pointersRef.current.values()];
500
- const distance = Math.max(1, Math.hypot(a.x - b.x, a.y - b.y));
501
- setZoomAt(gesture.transform.zoom * distance / Math.max(1, gesture.distance), (a.x + b.x) / 2, (a.y + b.y) / 2);
502
- } else if (gesture?.kind === "pan") {
503
- const pan = boundedPan(gesture.transform.zoom, gesture.transform.x + event.clientX - gesture.x, gesture.transform.y + event.clientY - gesture.y);
504
- setTransform({
505
- zoom: gesture.transform.zoom,
506
- ...pan
507
- });
508
- }
509
- };
510
- const endPointer = (event) => {
511
- pointersRef.current.delete(event.pointerId);
512
- if (pointersRef.current.size === 0) {
513
- gestureRef.current = void 0;
514
- setDragging(false);
515
- }
516
- };
570
+ setImageState("loading");
571
+ setCopyFailed(false);
572
+ setCopied(false);
573
+ }, [
574
+ item?.id,
575
+ item?.src,
576
+ request?.revision
577
+ ]);
517
578
  const addAnnotation = (event) => {
518
579
  if (!annotating || event.target.closest(".niv-annotation")) return;
519
580
  const bounds = surfaceRef.current?.getBoundingClientRect();
@@ -532,11 +593,13 @@ window.__ModuleLoader__.load({
532
593
  const copyNotes = async () => {
533
594
  const text = noteText(annotations, t);
534
595
  if (text === "") return;
535
- await navigator.clipboard.writeText(text);
536
- setCopied(true);
537
- window.setTimeout(() => {
538
- setCopied(false);
539
- }, 1200);
596
+ try {
597
+ await navigator.clipboard.writeText(text);
598
+ setCopied(true);
599
+ setCopyFailed(false);
600
+ } catch {
601
+ setCopyFailed(true);
602
+ }
540
603
  };
541
604
  if (request === void 0 || item === void 0) return null;
542
605
  const meta = [item.width && item.height ? `${item.width} × ${item.height}` : void 0, bytesLabel(item.bytes)].filter(Boolean).join(" · ");
@@ -550,7 +613,7 @@ window.__ModuleLoader__.load({
550
613
  tabIndex: -1,
551
614
  children: [
552
615
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
553
- className: "niv-title niv-sr-only",
616
+ className: "niv-title",
554
617
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: item.name }), meta !== "" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("small", { children: meta }) : null]
555
618
  }),
556
619
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("header", {
@@ -566,7 +629,11 @@ window.__ModuleLoader__.load({
566
629
  "data-active": annotating,
567
630
  "aria-label": annotating ? t("cancelAnnotate") : t("annotate"),
568
631
  "aria-pressed": annotating,
569
- onClick: () => setAnnotating((value) => !value),
632
+ disabled: imageState !== "ready",
633
+ onClick: () => {
634
+ resetGesture();
635
+ setAnnotating((value) => !value);
636
+ },
570
637
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconEditOutline16, {}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
571
638
  className: "niv-label",
572
639
  children: annotating ? t("cancelAnnotate") : t("annotate")
@@ -603,12 +670,13 @@ window.__ModuleLoader__.load({
603
670
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
604
671
  type: "button",
605
672
  className: "niv-button",
673
+ disabled: imageState !== "ready",
606
674
  onClick: actual,
607
675
  children: t("actual")
608
676
  }),
609
677
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
610
678
  className: "niv-zoom",
611
- children: [Math.round(transform.zoom * 100), "%"]
679
+ children: [Math.round(transform.zoom * pixelScale * 100), "%"]
612
680
  }),
613
681
  item.download === void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("a", {
614
682
  className: "niv-download",
@@ -622,13 +690,13 @@ window.__ModuleLoader__.load({
622
690
  download: item.download,
623
691
  item,
624
692
  t
625
- }),
693
+ }, item.src),
626
694
  item.actions.map((action) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ViewerAction, {
627
695
  action,
628
696
  annotations,
629
697
  item,
630
698
  service
631
- }, action.id))
699
+ }, `${item.src}:${action.id}`))
632
700
  ]
633
701
  })
634
702
  }),
@@ -649,133 +717,155 @@ window.__ModuleLoader__.load({
649
717
  onClick: (event) => {
650
718
  if (event.target === event.currentTarget && !annotating && transform.zoom === 1) service.close();
651
719
  },
652
- onPointerDown,
653
- onPointerMove,
654
- onPointerUp: endPointer,
655
- onPointerCancel: endPointer,
656
- onDoubleClick: () => {
720
+ ...!annotating && imageState === "ready" ? pointerHandlers : {},
721
+ onDoubleClick: (event) => {
722
+ if (event.target.closest("button,a,input,textarea") || imageState !== "ready") return;
657
723
  if (transform.zoom === 1) actual();
658
724
  else fit();
659
725
  },
660
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
661
- ref: surfaceRef,
662
- className: "niv-surface",
663
- onClick: addAnnotation,
664
- style: { transform: `translate3d(${transform.x}px,${transform.y}px,0) scale(${transform.zoom})` },
665
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("img", {
666
- ref: imageRef,
667
- className: "niv-image",
668
- src: item.src,
669
- alt: item.name,
670
- draggable: "false"
671
- }), annotations.map((annotation, position) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
672
- className: "niv-annotation",
673
- "data-x": annotation.x < .38 ? "right" : annotation.x > .62 ? "left" : "center",
674
- "data-y": annotation.y < .28 ? "down" : "up",
726
+ children: [
727
+ imageState !== "ready" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
728
+ className: "niv-load-status",
729
+ role: imageState === "error" ? "alert" : "status",
730
+ children: [t(imageState === "error" ? "loadFailed" : "loading"), imageState === "error" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
731
+ type: "button",
732
+ className: "niv-button",
733
+ onClick: () => {
734
+ setImageState("loading");
735
+ setAttempt((value) => value + 1);
736
+ },
737
+ children: t("retry")
738
+ }) : null]
739
+ }) : null,
740
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
741
+ ref: surfaceRef,
742
+ className: "niv-surface",
743
+ onClick: addAnnotation,
675
744
  style: {
676
- left: `${annotation.x * 100}%`,
677
- top: `${annotation.y * 100}%`,
678
- transform: `translate(-50%,-50%) scale(${1 / transform.zoom})`
745
+ visibility: imageState === "ready" ? "visible" : "hidden",
746
+ transform: `translate3d(${transform.x}px,${transform.y}px,0) scale(${transform.zoom})`
679
747
  },
680
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
748
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("img", {
749
+ ref: imageRef,
750
+ className: "niv-image",
751
+ src: item.src,
752
+ alt: item.name,
753
+ draggable: "false",
754
+ onLoad: () => {
755
+ setImageState("ready");
756
+ measure();
757
+ },
758
+ onError: () => setImageState("error")
759
+ }, `${request.revision}:${item.id}:${attempt}`), annotations.map((annotation, position) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
760
+ className: "niv-annotation",
761
+ "data-x": annotation.x < .38 ? "right" : annotation.x > .62 ? "left" : "center",
762
+ "data-y": annotation.y < .28 ? "down" : "up",
763
+ style: {
764
+ left: `${annotation.x * 100}%`,
765
+ top: `${annotation.y * 100}%`,
766
+ transform: `translate(-50%,-50%) scale(${1 / transform.zoom})`
767
+ },
768
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
769
+ type: "button",
770
+ className: "niv-pin",
771
+ "data-active": selected === annotation.id,
772
+ "aria-label": fill(t("note"), { value: position + 1 }),
773
+ onClick: (event) => {
774
+ event.stopPropagation();
775
+ const opening = selected !== annotation.id;
776
+ setSelected(opening ? annotation.id : void 0);
777
+ if (opening) setFocusNote(annotation.id);
778
+ },
779
+ children: position + 1
780
+ }), selected === annotation.id ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
781
+ className: "niv-inline-note",
782
+ "data-note-id": annotation.id,
783
+ onClick: (event) => event.stopPropagation(),
784
+ children: [
785
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
786
+ className: "niv-inline-index",
787
+ children: position + 1
788
+ }),
789
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("textarea", {
790
+ value: annotation.note,
791
+ rows: 1,
792
+ "aria-label": fill(t("note"), { value: position + 1 }),
793
+ placeholder: t("notePlaceholder"),
794
+ onChange: (event) => {
795
+ const note = event.target.value;
796
+ setAnnotations((current) => current.map((entry) => entry.id === annotation.id ? {
797
+ ...entry,
798
+ note
799
+ } : entry));
800
+ },
801
+ onKeyDown: (event) => {
802
+ if (event.key === "Enter" && !event.shiftKey || event.key === "Escape") {
803
+ event.preventDefault();
804
+ event.stopPropagation();
805
+ event.nativeEvent?.stopImmediatePropagation?.();
806
+ setSelected(void 0);
807
+ }
808
+ }
809
+ }),
810
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
811
+ type: "button",
812
+ className: "niv-note-remove",
813
+ "aria-label": t("removeNote"),
814
+ onClick: (event) => {
815
+ event.stopPropagation();
816
+ setAnnotations((current) => current.filter((entry) => entry.id !== annotation.id));
817
+ setSelected(void 0);
818
+ },
819
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconCloseOutline16, {})
820
+ })
821
+ ]
822
+ }) : null]
823
+ }, annotation.id))]
824
+ }),
825
+ showCounter ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
826
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
827
+ type: "button",
828
+ className: "niv-button niv-icon-only niv-nav niv-prev",
829
+ "aria-label": t("previous"),
830
+ onClick: (event) => {
831
+ event.stopPropagation();
832
+ setIndex((value) => (value - 1 + request.items.length) % request.items.length);
833
+ },
834
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronLeftOutline14, {})
835
+ }),
836
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
681
837
  type: "button",
682
- className: "niv-pin",
683
- "data-active": selected === annotation.id,
684
- "aria-label": fill(t("note"), { value: position + 1 }),
838
+ className: "niv-button niv-icon-only niv-nav niv-next",
839
+ "aria-label": t("next"),
685
840
  onClick: (event) => {
686
841
  event.stopPropagation();
687
- const opening = selected !== annotation.id;
688
- setSelected(opening ? annotation.id : void 0);
689
- if (opening) setFocusNote(annotation.id);
842
+ setIndex((value) => (value + 1) % request.items.length);
690
843
  },
691
- children: position + 1
692
- }), selected === annotation.id ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
693
- className: "niv-inline-note",
694
- "data-note-id": annotation.id,
695
- onClick: (event) => event.stopPropagation(),
844
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronRightOutline14, {})
845
+ }),
846
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
847
+ className: "niv-counter",
696
848
  children: [
697
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
698
- className: "niv-inline-index",
699
- children: position + 1
700
- }),
701
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("textarea", {
702
- value: annotation.note,
703
- rows: 1,
704
- "aria-label": fill(t("note"), { value: position + 1 }),
705
- placeholder: t("notePlaceholder"),
706
- onChange: (event) => {
707
- const note = event.target.value;
708
- setAnnotations((current) => current.map((entry) => entry.id === annotation.id ? {
709
- ...entry,
710
- note
711
- } : entry));
712
- },
713
- onKeyDown: (event) => {
714
- if (event.key === "Enter" && !event.shiftKey || event.key === "Escape") {
715
- event.preventDefault();
716
- event.stopPropagation();
717
- event.nativeEvent?.stopImmediatePropagation?.();
718
- setSelected(void 0);
719
- }
720
- }
721
- }),
722
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
723
- type: "button",
724
- className: "niv-note-remove",
725
- "aria-label": t("removeNote"),
726
- onClick: (event) => {
727
- event.stopPropagation();
728
- setAnnotations((current) => current.filter((entry) => entry.id !== annotation.id));
729
- setSelected(void 0);
730
- },
731
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconCloseOutline16, {})
732
- })
849
+ index + 1,
850
+ " / ",
851
+ request.items.length
733
852
  ]
734
- }) : null]
735
- }, annotation.id))]
736
- }), showCounter ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
737
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
738
- type: "button",
739
- className: "niv-button niv-icon-only niv-nav niv-prev",
740
- "aria-label": t("previous"),
741
- onClick: (event) => {
742
- event.stopPropagation();
743
- setIndex((value) => (value - 1 + request.items.length) % request.items.length);
744
- },
745
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronLeftOutline14, {})
746
- }),
747
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
748
- type: "button",
749
- className: "niv-button niv-icon-only niv-nav niv-next",
750
- "aria-label": t("next"),
751
- onClick: (event) => {
752
- event.stopPropagation();
753
- setIndex((value) => (value + 1) % request.items.length);
754
- },
755
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronRightOutline14, {})
756
- }),
757
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
758
- className: "niv-counter",
759
- children: [
760
- index + 1,
761
- " / ",
762
- request.items.length
763
- ]
764
- })
765
- ] }) : annotating ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
766
- className: "niv-hint",
767
- children: t("regionHint")
768
- }) : transform.zoom === 1 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
769
- className: "niv-hint",
770
- children: t("zoomHint")
771
- }) : null]
853
+ })
854
+ ] }) : annotating ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
855
+ className: "niv-hint",
856
+ children: t("regionHint")
857
+ }) : transform.zoom === 1 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
858
+ className: "niv-hint",
859
+ children: t("zoomHint")
860
+ }) : null
861
+ ]
772
862
  }), annotations.some((annotation) => annotation.note.trim() !== "") ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
773
863
  type: "button",
774
864
  className: "niv-copy-notes",
775
865
  onClick: () => {
776
866
  copyNotes();
777
867
  },
778
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconCopyOutline16, {}), copied ? t("copied") : t("copyNotes")]
868
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconCopyOutline16, {}), copyFailed ? t("copyFailed") : copied ? t("copied") : t("copyNotes")]
779
869
  }) : null]
780
870
  })
781
871
  ]
package/lib/client.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"client.js","names":["#listeners","#snapshot","#annotationsByImage","#revision","#emit","CSS","IconDownloadOutline16","IconEditOutline16","IconFullscreenOutline16","IconCloseOutline16","IconChevronLeftOutline14","IconChevronRightOutline14","IconCopyOutline16","VIEWER_CSS"],"sources":["../src/viewer.js","../src/styles.js","../src/client.jsx"],"sourcesContent":["const boundedNumber = (value, fallback) => Number.isFinite(value) && value > 0 ? value : fallback\nconst downloadOf = value => typeof value?.onInvoke === 'function' ? {\n pendingLabel: typeof value.pendingLabel === 'string' && value.pendingLabel !== '' ? value.pendingLabel : undefined,\n errorLabel: typeof value.errorLabel === 'string' && value.errorLabel !== '' ? value.errorLabel : undefined,\n onInvoke: value.onInvoke,\n} : undefined\nconst actionsOf = value => Array.isArray(value) ? value.flatMap((action, position) => {\n if (typeof action?.onInvoke !== 'function' || typeof action?.label !== 'string' || action.label.trim() === '') return []\n return [{\n id: typeof action.id === 'string' && action.id !== '' ? action.id : `action-${position + 1}`,\n label: action.label,\n pendingLabel: typeof action.pendingLabel === 'string' && action.pendingLabel !== '' ? action.pendingLabel : action.label,\n errorLabel: typeof action.errorLabel === 'string' && action.errorLabel !== '' ? action.errorLabel : action.label,\n closeOnSuccess: action.closeOnSuccess === true,\n onInvoke: action.onInvoke,\n }]\n}) : []\n\nexport function normalizeViewerRequest(request) {\n const rawItems = Array.isArray(request?.items) ? request.items : []\n const items = rawItems.flatMap((item, position) => {\n if (typeof item?.src !== 'string' || item.src === '') return []\n return [{\n id: typeof item.id === 'string' && item.id !== '' ? item.id : `image-${position + 1}`,\n src: item.src,\n name: typeof item.name === 'string' && item.name !== '' ? item.name : `Image ${position + 1}`,\n width: boundedNumber(item.width, undefined),\n height: boundedNumber(item.height, undefined),\n bytes: boundedNumber(item.bytes, undefined),\n download: downloadOf(item.download),\n actions: actionsOf(item.actions),\n }]\n })\n if (items.length === 0) return undefined\n const requestedIndex = Number.isInteger(request?.index) ? request.index : 0\n return {\n items,\n index: Math.max(0, Math.min(items.length - 1, requestedIndex)),\n opener: typeof HTMLElement !== 'undefined' && request?.opener instanceof HTMLElement ? request.opener : undefined,\n source: typeof request?.source === 'string' ? request.source : 'dsh',\n annotations: request?.annotations !== false,\n }\n}\n\nexport class NativeImageViewerService {\n #listeners = new Set()\n #revision = 0\n #snapshot\n #annotationsByImage = new Map()\n\n constructor() {\n this.subscribe = listener => {\n this.#listeners.add(listener)\n return () => { this.#listeners.delete(listener) }\n }\n this.getSnapshot = () => this.#snapshot\n this.getAnnotationsSnapshot = () => Object.fromEntries(\n [...this.#annotationsByImage].map(([id, annotations]) => [id, annotations.map(annotation => ({ ...annotation }))]),\n )\n }\n\n setAnnotations(imageId, annotations) {\n if (typeof imageId !== 'string' || imageId === '' || !Array.isArray(annotations)) return\n if (annotations.length === 0) this.#annotationsByImage.delete(imageId)\n else this.#annotationsByImage.set(imageId, annotations.map(annotation => ({ ...annotation })))\n }\n\n open(request) {\n const normalized = normalizeViewerRequest(request)\n if (normalized === undefined) return false\n this.#revision += 1\n this.#snapshot = { ...normalized, revision: this.#revision }\n this.#emit()\n return true\n }\n\n close() {\n if (this.#snapshot === undefined) return\n const opener = this.#snapshot.opener\n this.#snapshot = undefined\n this.#emit()\n if (typeof window === 'undefined') opener?.focus()\n else window.requestAnimationFrame(() => { opener?.focus() })\n }\n\n #emit() {\n for (const listener of this.#listeners) listener()\n }\n}\n\nexport function nativeImageButton(target) {\n if (!(target instanceof Element)) return undefined\n const button = target.closest('button')\n if (!(button instanceof HTMLButtonElement)) return undefined\n const image = button.querySelector(':scope > img')\n if (!(image instanceof HTMLImageElement) || image.currentSrc === '') return undefined\n if (button.matches('[data-variant=\"single\"],[data-variant=\"tile\"]')) return { button, image, group: button.parentElement }\n const rail = button.closest('[role=\"group\"]')\n if (rail !== null && button.hasAttribute('title')) return { button, image, group: rail }\n return undefined\n}\n\nexport function imageItemsForButton(match) {\n const buttons = match.group?.querySelectorAll('button[data-variant=\"single\"],button[data-variant=\"tile\"],button[title]')\n ?? []\n const candidates = [...buttons].flatMap((button, index) => {\n const image = button.querySelector(':scope > img')\n if (!(image instanceof HTMLImageElement) || image.currentSrc === '') return []\n return [{\n id: image.currentSrc,\n src: image.currentSrc,\n name: image.alt || `Image ${index + 1}`,\n width: image.naturalWidth || undefined,\n height: image.naturalHeight || undefined,\n button,\n }]\n })\n const items = candidates.length > 0 ? candidates : [{\n id: match.image.currentSrc,\n src: match.image.currentSrc,\n name: match.image.alt || 'Image',\n width: match.image.naturalWidth || undefined,\n height: match.image.naturalHeight || undefined,\n button: match.button,\n }]\n const index = Math.max(0, items.findIndex(item => item.button === match.button))\n return { items: items.map(({ button: _button, ...item }) => item), index }\n}\n","export const CSS = String.raw`\n.niv-root{position:fixed;inset:0;z-index:1000;pointer-events:auto;overflow:hidden;background:rgba(7,8,10,.68);color:var(--dsw-alias-label-primary-inverted,#fff);outline:0;backdrop-filter:blur(13px) saturate(.72);-webkit-backdrop-filter:blur(13px) saturate(.72)}\n.niv-sr-only{position:absolute!important;width:1px!important;height:1px!important;padding:0!important;margin:-1px!important;overflow:hidden!important;clip:rect(0,0,0,0)!important;white-space:nowrap!important;border:0!important}\n.niv-topbar{position:absolute;right:50%;bottom:22px;z-index:6;max-width:calc(100vw - 36px);padding:5px;border:1px solid rgba(255,255,255,.12);border-radius:999px;background:rgba(38,39,43,.86);box-shadow:0 10px 34px rgba(0,0,0,.3);transform:translateX(50%);backdrop-filter:blur(18px);-webkit-backdrop-filter:blur(18px)}\n.niv-actions{display:flex;align-items:center;gap:2px;overflow-x:auto;scrollbar-width:none}.niv-actions::-webkit-scrollbar{display:none}.niv-button,.niv-download{box-sizing:border-box;display:inline-flex;align-items:center;justify-content:center;gap:6px;height:32px;padding:0 10px;border:0;border-radius:999px;background:transparent;color:rgba(255,255,255,.9);font:inherit;font-size:12px;text-decoration:none;white-space:nowrap;cursor:pointer}.niv-button:hover,.niv-download:hover,.niv-button[data-active=true]{background:rgba(255,255,255,.12)}.niv-button:focus-visible,.niv-download:focus-visible,.niv-close-floating:focus-visible{outline:2px solid rgba(255,255,255,.9);outline-offset:2px}.niv-button:disabled,.niv-download:disabled{opacity:.38;cursor:default}.niv-icon-only{width:32px;padding:0}.niv-zoom{min-width:44px;color:rgba(255,255,255,.68);font-size:12px;font-variant-numeric:tabular-nums;text-align:center}\n.niv-close-floating{position:absolute;top:20px;right:20px;z-index:8;display:grid;place-items:center;width:42px;height:42px;padding:0;border:1px solid rgba(255,255,255,.1);border-radius:50%;background:rgba(48,49,53,.82);box-shadow:0 8px 24px rgba(0,0,0,.28);color:#fff;cursor:pointer;backdrop-filter:blur(16px);-webkit-backdrop-filter:blur(16px)}.niv-close-floating:hover{background:rgba(66,67,72,.92)}\n.niv-workspace{position:absolute;inset:0;display:grid;min-width:0;min-height:0;padding:68px 24px 72px;box-sizing:border-box}\n.niv-stage{position:relative;display:grid;place-items:center;min-width:0;min-height:0;overflow:hidden;padding:0;touch-action:none;user-select:none}.niv-stage[data-dragging=true]{cursor:grabbing}.niv-stage[data-annotating=true]{cursor:crosshair}\n.niv-surface{position:relative;display:inline-flex;max-width:100%;max-height:100%;transform-origin:center;will-change:transform}.niv-image{display:block;max-width:calc(100vw - 72px);max-height:calc(100vh - 154px);border-radius:12px;object-fit:contain;box-shadow:0 22px 60px rgba(0,0,0,.46);user-select:none;-webkit-user-drag:none}\n.niv-annotation{position:absolute;z-index:5;width:24px;height:24px;transform-origin:center;pointer-events:none}.niv-pin{position:absolute;inset:0;display:grid;place-items:center;width:24px;height:24px;padding:0;border:2px solid #fff;border-radius:50%;background:rgba(23,24,27,.94);box-shadow:0 4px 16px rgba(0,0,0,.35);color:#fff;font:inherit;font-size:11px;font-weight:700;cursor:pointer;pointer-events:auto}.niv-pin[data-active=true]{background:var(--dsw-alias-state-business-primary,#3964fe)}\n.niv-inline-note{position:absolute;bottom:34px;box-sizing:border-box;display:grid;width:min(300px,calc(100vw - 40px));grid-template-columns:24px minmax(0,1fr) 24px;align-items:center;gap:7px;padding:7px 8px;border:1px solid rgba(255,255,255,.12);border-radius:18px;background:rgba(32,33,37,.94);box-shadow:0 14px 38px rgba(0,0,0,.38);color:#fff;pointer-events:auto;backdrop-filter:blur(20px);-webkit-backdrop-filter:blur(20px)}.niv-annotation[data-x=right] .niv-inline-note{left:-8px}.niv-annotation[data-x=left] .niv-inline-note{right:-8px}.niv-annotation[data-x=center] .niv-inline-note{left:50%;transform:translateX(-50%)}.niv-annotation[data-y=down] .niv-inline-note{top:34px;bottom:auto}.niv-inline-note::after{position:absolute;width:9px;height:9px;background:rgba(32,33,37,.94);content:'';transform:rotate(45deg)}.niv-annotation[data-y=up] .niv-inline-note::after{bottom:-5px}.niv-annotation[data-y=down] .niv-inline-note::after{top:-5px}.niv-annotation[data-x=right] .niv-inline-note::after{left:13px}.niv-annotation[data-x=left] .niv-inline-note::after{right:13px}.niv-annotation[data-x=center] .niv-inline-note::after{left:calc(50% - 4px)}.niv-inline-index{display:grid;place-items:center;width:22px;height:22px;border-radius:50%;background:var(--dsw-alias-state-business-primary,#3964fe);color:#fff;font-size:10px;font-weight:700}.niv-inline-note textarea{box-sizing:border-box;width:100%;min-height:24px;max-height:92px;resize:none;overflow:auto;border:0;outline:0;background:transparent;color:#fff;font:inherit;font-size:12px;line-height:18px}.niv-inline-note textarea::placeholder{color:rgba(255,255,255,.44)}.niv-note-remove{display:grid;place-items:center;width:24px;height:24px;padding:0;border:0;border-radius:50%;background:transparent;color:rgba(255,255,255,.68);cursor:pointer}.niv-note-remove:hover{background:rgba(255,255,255,.1);color:#fff}\n.niv-nav{position:absolute;top:50%;z-index:4;width:42px;height:42px;padding:0;transform:translateY(-50%);background:rgba(48,49,53,.82);box-shadow:0 8px 24px rgba(0,0,0,.28);backdrop-filter:blur(16px)}.niv-prev{left:6px}.niv-next{right:6px}.niv-counter{position:absolute;bottom:8px;left:50%;padding:5px 10px;border-radius:999px;background:rgba(38,39,43,.86);color:rgba(255,255,255,.72);font-size:11px;transform:translateX(-50%);backdrop-filter:blur(16px)}\n.niv-hint{display:none}\n.niv-copy-notes{position:absolute;right:20px;bottom:22px;z-index:6;display:inline-flex;align-items:center;gap:6px;height:32px;padding:0 11px;border:1px solid rgba(255,255,255,.1);border-radius:999px;background:rgba(38,39,43,.86);color:rgba(255,255,255,.82);font:inherit;font-size:12px;cursor:pointer;backdrop-filter:blur(16px)}\n@media(max-width:760px){.niv-close-floating{top:12px;right:12px;width:40px;height:40px}.niv-workspace{padding:60px 12px 68px}.niv-image{max-width:calc(100vw - 24px);max-height:calc(100vh - 136px)}.niv-topbar{bottom:12px;max-width:calc(100vw - 24px)}.niv-button{padding:0 8px}.niv-button span.niv-label{display:none}.niv-inline-note{width:min(260px,calc(100vw - 40px))}.niv-copy-notes{display:none}}\n@media(prefers-reduced-motion:reduce){.niv-surface{transition:none}}\n`\n","import {\n useCallback, useEffect, useMemo, useRef, useState, useSyncExternalStore,\n} from 'react'\nimport {\n IconChevronLeftOutline14, IconChevronRightOutline14, IconCloseOutline16,\n IconCopyOutline16, IconDownloadOutline16, IconEditOutline16, IconFullscreenOutline16,\n} from '@deepseek-ai/dsh-client-ui-primitives'\nimport {\n imageItemsForButton, NativeImageViewerService, nativeImageButton,\n} from './viewer.js'\nimport { CSS as VIEWER_CSS } from './styles.js'\n\nexport const name = 'dsh-image-viewer'\nexport const inject = ['locale', 'slots']\n\nconst LOCALES = {\n en: {\n dialog: 'Image viewer', close: 'Close', fit: 'Fit', actual: '100%', download: 'Download',\n annotate: 'Mark region', cancelAnnotate: 'Cancel marking', regions: 'Region notes', regionHint: 'Click a point on the image, then add a note.',\n note: 'Region {value}', notePlaceholder: 'Describe what should change here…', removeNote: 'Remove region note',\n hideNotes: 'Hide notes', copyNotes: 'Copy notes', copied: 'Copied', previous: 'Previous image', next: 'Next image',\n zoomHint: 'Wheel to zoom · drag to pan · double-click for 100%', preparing: 'Preparing…', failed: 'Could not prepare this image.',\n },\n zh: {\n dialog: '图片查看器', close: '关闭', fit: '适应窗口', actual: '原始大小', download: '下载',\n annotate: '标记区域', cancelAnnotate: '取消标记', regions: '区域备注', regionHint: '点击图片中的位置,然后填写备注。',\n note: '区域 {value}', notePlaceholder: '描述这里需要怎样调整…', removeNote: '删除区域备注',\n hideNotes: '收起备注', copyNotes: '复制备注', copied: '已复制', previous: '上一张图片', next: '下一张图片',\n zoomHint: '滚轮缩放 · 拖动查看 · 双击切换原始大小', preparing: '正在准备…', failed: '暂时无法准备这张图片。',\n },\n}\n\nconst fill = (value, variables) => Object.entries(variables).reduce(\n (text, [key, replacement]) => text.replaceAll(`{${key}}`, String(replacement)),\n value,\n)\nconst clamp = (value, min, max) => Math.min(max, Math.max(min, value))\nconst bytesLabel = bytes => bytes === undefined ? undefined : bytes < 1024 * 1024\n ? `${Math.max(0.1, bytes / 1024).toLocaleString(undefined, { maximumFractionDigits: 1 })} KB`\n : `${(bytes / 1024 / 1024).toLocaleString(undefined, { maximumFractionDigits: 1 })} MB`\nconst downloadName = name => {\n const cleaned = String(name || 'image.png').replace(/[<>:\"/\\\\|?*\\u0000-\\u001f]/gu, '-').replace(/[. ]+$/u, '').trim()\n return cleaned === '' ? 'image.png' : cleaned\n}\nconst noteText = (annotations, t) => annotations.map((annotation, index) => {\n const label = fill(t('note'), { value: index + 1 })\n return `${label} (${Math.round(annotation.x * 100)}%, ${Math.round(annotation.y * 100)}%): ${annotation.note.trim()}`\n}).filter(line => !line.endsWith(': ')).join('\\n')\n\nfunction ViewerAction({ action, annotations, item, service }) {\n const [state, setState] = useState('idle')\n const invoke = async () => {\n if (state === 'pending') return\n setState('pending')\n try {\n await action.onInvoke({ annotations, item, src: item.src })\n setState('idle')\n if (action.closeOnSuccess) service.close()\n } catch { setState('failed') }\n }\n const label = state === 'pending' ? action.pendingLabel : state === 'failed' ? action.errorLabel : action.label\n return <button type=\"button\" className=\"niv-button\" disabled={state === 'pending'} onClick={() => { void invoke() }}><span className=\"niv-label\">{label}</span></button>\n}\n\nfunction ViewerDownload({ download, item, t }) {\n const [state, setState] = useState('idle')\n const invoke = async () => {\n if (state === 'pending') return\n setState('pending')\n try { await download.onInvoke({ item, src: item.src }); setState('idle') } catch { setState('failed') }\n }\n const label = state === 'pending' ? download.pendingLabel ?? t('preparing') : state === 'failed' ? download.errorLabel ?? t('failed') : t('download')\n return <button type=\"button\" className=\"niv-download\" disabled={state === 'pending'} onClick={() => { void invoke() }}><IconDownloadOutline16 /><span className=\"niv-label\">{label}</span></button>\n}\n\nfunction ViewerOverlay({ service, t }) {\n const request = useSyncExternalStore(service.subscribe, service.getSnapshot)\n const [index, setIndex] = useState(0)\n const [transform, setTransform] = useState({ zoom: 1, x: 0, y: 0 })\n const [dragging, setDragging] = useState(false)\n const [annotating, setAnnotating] = useState(false)\n const [annotationsByImage, setAnnotationsByImage] = useState(service.getAnnotationsSnapshot)\n const annotationsByImageRef = useRef(annotationsByImage)\n const [selected, setSelected] = useState()\n const [focusNote, setFocusNote] = useState()\n const [copied, setCopied] = useState(false)\n const rootRef = useRef(null)\n const stageRef = useRef(null)\n const surfaceRef = useRef(null)\n const imageRef = useRef(null)\n const pointersRef = useRef(new Map())\n const gestureRef = useRef()\n const transformRef = useRef(transform)\n transformRef.current = transform\n annotationsByImageRef.current = annotationsByImage\n\n useEffect(() => {\n if (request === undefined) return\n setIndex(request.index)\n setTransform({ zoom: 1, x: 0, y: 0 })\n setDragging(false)\n setAnnotating(false)\n setSelected(undefined)\n setCopied(false)\n }, [request?.revision])\n\n const item = request?.items[index]\n const annotations = item === undefined ? [] : annotationsByImage[item.id] ?? []\n const setAnnotations = useCallback((update) => {\n if (item === undefined) return\n const previous = annotationsByImageRef.current[item.id] ?? []\n const next = typeof update === 'function' ? update(previous) : update\n const snapshot = { ...annotationsByImageRef.current, [item.id]: next }\n annotationsByImageRef.current = snapshot\n service.setAnnotations(item.id, next)\n setAnnotationsByImage(snapshot)\n }, [item?.id, service])\n\n const boundedPan = useCallback((zoom, x, y) => {\n const stage = stageRef.current\n const surface = surfaceRef.current\n if (stage === null || surface === null || zoom <= 1) return { x: 0, y: 0 }\n const limitX = Math.max(0, (surface.offsetWidth * zoom - stage.clientWidth) / 2) + 28\n const limitY = Math.max(0, (surface.offsetHeight * zoom - stage.clientHeight) / 2) + 28\n return { x: clamp(x, -limitX, limitX), y: clamp(y, -limitY, limitY) }\n }, [])\n\n const setZoomAt = useCallback((nextZoom, clientX, clientY) => {\n const stage = stageRef.current\n if (stage === null) return\n setTransform(current => {\n const next = clamp(nextZoom, 0.5, 8)\n const box = stage.getBoundingClientRect()\n const px = clientX - box.left - box.width / 2\n const py = clientY - box.top - box.height / 2\n const ratio = next / current.zoom\n const pan = boundedPan(next, px - (px - current.x) * ratio, py - (py - current.y) * ratio)\n return { zoom: next, ...pan }\n })\n }, [boundedPan])\n\n const fit = useCallback(() => { setTransform({ zoom: 1, x: 0, y: 0 }) }, [])\n const actual = useCallback(() => {\n const image = imageRef.current\n const surface = surfaceRef.current\n if (image === null || surface === null || image.naturalWidth === 0) return\n const zoom = clamp(image.naturalWidth / Math.max(1, surface.offsetWidth), 1, 8)\n setTransform({ zoom, x: 0, y: 0 })\n }, [])\n\n useEffect(() => {\n if (request === undefined) return undefined\n const previousOverflow = document.body.style.overflow\n document.body.style.overflow = 'hidden'\n rootRef.current?.focus()\n const onKeyDown = event => {\n if (event.key === 'Escape') {\n event.preventDefault()\n if (event.target instanceof Element && event.target.closest('.niv-inline-note') !== null) {\n setSelected(undefined)\n return\n }\n service.close()\n return\n }\n const editing = event.target instanceof HTMLInputElement || event.target instanceof HTMLTextAreaElement\n if (!editing && event.key === 'ArrowLeft' && request.items.length > 1) {\n event.preventDefault(); setIndex(value => (value - 1 + request.items.length) % request.items.length)\n } else if (!editing && event.key === 'ArrowRight' && request.items.length > 1) {\n event.preventDefault(); setIndex(value => (value + 1) % request.items.length)\n } else if (!editing && (event.key === '+' || event.key === '=')) {\n event.preventDefault(); const box = stageRef.current?.getBoundingClientRect(); if (box) setZoomAt(transformRef.current.zoom * 1.2, box.left + box.width / 2, box.top + box.height / 2)\n } else if (!editing && event.key === '-') {\n event.preventDefault(); const box = stageRef.current?.getBoundingClientRect(); if (box) setZoomAt(transformRef.current.zoom / 1.2, box.left + box.width / 2, box.top + box.height / 2)\n } else if (!editing && event.key.toLowerCase() === 'f') {\n event.preventDefault(); fit()\n } else if (event.key === 'Tab') {\n const controls = [...rootRef.current.querySelectorAll('button:not(:disabled),a[href],input:not(:disabled),textarea:not(:disabled),[tabindex]:not([tabindex=\"-1\"])')]\n const first = controls[0]\n const last = controls.at(-1)\n if (event.shiftKey && document.activeElement === first) { event.preventDefault(); last?.focus() }\n else if (!event.shiftKey && document.activeElement === last) { event.preventDefault(); first?.focus() }\n }\n }\n document.addEventListener('keydown', onKeyDown)\n return () => {\n document.body.style.overflow = previousOverflow\n document.removeEventListener('keydown', onKeyDown)\n }\n }, [request, service, fit, setZoomAt])\n\n useEffect(() => {\n if (focusNote === undefined) return\n const field = rootRef.current?.querySelector(`[data-note-id=\"${CSS.escape(focusNote)}\"] textarea`)\n field?.focus()\n setFocusNote(undefined)\n }, [focusNote, selected, annotations.length])\n\n useEffect(() => {\n setTransform({ zoom: 1, x: 0, y: 0 })\n setDragging(false)\n setAnnotating(false)\n setSelected(undefined)\n }, [item?.id])\n\n const onWheel = useCallback(event => {\n event.preventDefault()\n setZoomAt(transformRef.current.zoom * Math.exp(-event.deltaY * 0.0015), event.clientX, event.clientY)\n }, [setZoomAt])\n useEffect(() => {\n const stage = stageRef.current\n if (stage === null || request === undefined) return undefined\n stage.addEventListener('wheel', onWheel, { passive: false })\n return () => stage.removeEventListener('wheel', onWheel)\n }, [onWheel, request])\n const onPointerDown = event => {\n if (event.button !== 0 || annotating) return\n if (event.target instanceof Element && event.target.closest('button,a,input,textarea,select')) return\n if (event.pointerType === 'mouse' && transform.zoom <= 1) return\n event.currentTarget.setPointerCapture(event.pointerId)\n pointersRef.current.set(event.pointerId, { x: event.clientX, y: event.clientY })\n if (pointersRef.current.size === 2) {\n const [a, b] = [...pointersRef.current.values()]\n gestureRef.current = { kind: 'pinch', distance: Math.hypot(a.x - b.x, a.y - b.y), transform }\n } else if (!annotating && transform.zoom > 1) {\n gestureRef.current = { kind: 'pan', x: event.clientX, y: event.clientY, transform }\n setDragging(true)\n }\n }\n const onPointerMove = event => {\n if (!pointersRef.current.has(event.pointerId)) return\n pointersRef.current.set(event.pointerId, { x: event.clientX, y: event.clientY })\n const gesture = gestureRef.current\n if (gesture?.kind === 'pinch' && pointersRef.current.size >= 2) {\n const [a, b] = [...pointersRef.current.values()]\n const distance = Math.max(1, Math.hypot(a.x - b.x, a.y - b.y))\n setZoomAt(gesture.transform.zoom * distance / Math.max(1, gesture.distance), (a.x + b.x) / 2, (a.y + b.y) / 2)\n } else if (gesture?.kind === 'pan') {\n const pan = boundedPan(gesture.transform.zoom, gesture.transform.x + event.clientX - gesture.x, gesture.transform.y + event.clientY - gesture.y)\n setTransform({ zoom: gesture.transform.zoom, ...pan })\n }\n }\n const endPointer = event => {\n pointersRef.current.delete(event.pointerId)\n if (pointersRef.current.size === 0) { gestureRef.current = undefined; setDragging(false) }\n }\n const addAnnotation = event => {\n if (!annotating || event.target.closest('.niv-annotation')) return\n const bounds = surfaceRef.current?.getBoundingClientRect()\n if (bounds === undefined) return\n const annotation = {\n id: crypto.randomUUID(),\n x: clamp((event.clientX - bounds.left) / bounds.width, 0, 1),\n y: clamp((event.clientY - bounds.top) / bounds.height, 0, 1),\n note: '',\n }\n setAnnotations(current => [...current, annotation])\n setAnnotating(false)\n setSelected(annotation.id)\n setFocusNote(annotation.id)\n }\n const copyNotes = async () => {\n const text = noteText(annotations, t)\n if (text === '') return\n await navigator.clipboard.writeText(text)\n setCopied(true)\n window.setTimeout(() => { setCopied(false) }, 1200)\n }\n\n if (request === undefined || item === undefined) return null\n const meta = [item.width && item.height ? `${item.width} × ${item.height}` : undefined, bytesLabel(item.bytes)].filter(Boolean).join(' · ')\n const showCounter = request.items.length > 1\n return <div ref={rootRef} className=\"niv-root\" role=\"dialog\" aria-modal=\"true\" aria-label={t('dialog')} tabIndex={-1}>\n <div className=\"niv-title niv-sr-only\"><strong>{item.name}</strong>{meta !== '' ? <small>{meta}</small> : null}</div>\n <header className=\"niv-topbar\" role=\"toolbar\" aria-label={t('dialog')}>\n <div className=\"niv-actions\">\n {request.annotations ? <button type=\"button\" className=\"niv-button\" data-active={annotating} aria-label={annotating ? t('cancelAnnotate') : t('annotate')} aria-pressed={annotating} onClick={() => setAnnotating(value => !value)}><IconEditOutline16 /><span className=\"niv-label\">{annotating ? t('cancelAnnotate') : t('annotate')}</span></button> : null}\n {annotations.length > 0 ? <button type=\"button\" className=\"niv-button\" data-active={selected !== undefined} onClick={() => { const first = annotations[0]; setSelected(current => current === undefined ? first.id : undefined); if (selected === undefined) setFocusNote(first.id) }}>{annotations.length} <span className=\"niv-label\">{t('regions')}</span></button> : null}\n <button type=\"button\" className=\"niv-button\" aria-label={t('fit')} onClick={fit}><IconFullscreenOutline16 /><span className=\"niv-label\">{t('fit')}</span></button>\n <button type=\"button\" className=\"niv-button\" onClick={actual}>{t('actual')}</button>\n <span className=\"niv-zoom\">{Math.round(transform.zoom * 100)}%</span>\n {item.download === undefined\n ? <a className=\"niv-download\" href={item.src} download={downloadName(item.name)}><IconDownloadOutline16 /><span className=\"niv-label\">{t('download')}</span></a>\n : <ViewerDownload download={item.download} item={item} t={t} />}\n {item.actions.map(action => <ViewerAction action={action} annotations={annotations} item={item} service={service} key={action.id} />)}\n </div>\n </header>\n <button type=\"button\" className=\"niv-close-floating\" aria-label={t('close')} onClick={() => service.close()}><IconCloseOutline16 /></button>\n <div className=\"niv-workspace\">\n <main ref={stageRef} className=\"niv-stage\" data-dragging={dragging} data-annotating={annotating} onClick={event => { if (event.target === event.currentTarget && !annotating && transform.zoom === 1) service.close() }} onPointerDown={onPointerDown} onPointerMove={onPointerMove} onPointerUp={endPointer} onPointerCancel={endPointer} onDoubleClick={() => { if (transform.zoom === 1) actual(); else fit() }}>\n <div ref={surfaceRef} className=\"niv-surface\" onClick={addAnnotation} style={{ transform: `translate3d(${transform.x}px,${transform.y}px,0) scale(${transform.zoom})` }}>\n <img ref={imageRef} className=\"niv-image\" src={item.src} alt={item.name} draggable=\"false\" />\n {annotations.map((annotation, position) => <div className=\"niv-annotation\" data-x={annotation.x < 0.38 ? 'right' : annotation.x > 0.62 ? 'left' : 'center'} data-y={annotation.y < 0.28 ? 'down' : 'up'} style={{ left: `${annotation.x * 100}%`, top: `${annotation.y * 100}%`, transform: `translate(-50%,-50%) scale(${1 / transform.zoom})` }} key={annotation.id}>\n <button type=\"button\" className=\"niv-pin\" data-active={selected === annotation.id} aria-label={fill(t('note'), { value: position + 1 })} onClick={event => { event.stopPropagation(); const opening = selected !== annotation.id; setSelected(opening ? annotation.id : undefined); if (opening) setFocusNote(annotation.id) }}>{position + 1}</button>\n {selected === annotation.id ? <div className=\"niv-inline-note\" data-note-id={annotation.id} onClick={event => event.stopPropagation()}>\n <span className=\"niv-inline-index\">{position + 1}</span>\n <textarea value={annotation.note} rows={1} aria-label={fill(t('note'), { value: position + 1 })} placeholder={t('notePlaceholder')} onChange={event => { const note = event.target.value; setAnnotations(current => current.map(entry => entry.id === annotation.id ? { ...entry, note } : entry)) }} onKeyDown={event => {\n if ((event.key === 'Enter' && !event.shiftKey) || event.key === 'Escape') {\n event.preventDefault()\n event.stopPropagation()\n event.nativeEvent?.stopImmediatePropagation?.()\n setSelected(undefined)\n }\n }} />\n <button type=\"button\" className=\"niv-note-remove\" aria-label={t('removeNote')} onClick={event => { event.stopPropagation(); setAnnotations(current => current.filter(entry => entry.id !== annotation.id)); setSelected(undefined) }}><IconCloseOutline16 /></button>\n </div> : null}\n </div>)}\n </div>\n {showCounter ? <>\n <button type=\"button\" className=\"niv-button niv-icon-only niv-nav niv-prev\" aria-label={t('previous')} onClick={event => { event.stopPropagation(); setIndex(value => (value - 1 + request.items.length) % request.items.length) }}><IconChevronLeftOutline14 /></button>\n <button type=\"button\" className=\"niv-button niv-icon-only niv-nav niv-next\" aria-label={t('next')} onClick={event => { event.stopPropagation(); setIndex(value => (value + 1) % request.items.length) }}><IconChevronRightOutline14 /></button>\n <span className=\"niv-counter\">{index + 1} / {request.items.length}</span>\n </> : annotating ? <span className=\"niv-hint\">{t('regionHint')}</span> : transform.zoom === 1 ? <span className=\"niv-hint\">{t('zoomHint')}</span> : null}\n </main>\n {annotations.some(annotation => annotation.note.trim() !== '') ? <button type=\"button\" className=\"niv-copy-notes\" onClick={() => { void copyNotes() }}><IconCopyOutline16 />{copied ? t('copied') : t('copyNotes')}</button> : null}\n </div>\n </div>\n}\n\nfunction installOfficialImageBridge(service) {\n const onClick = event => {\n const match = nativeImageButton(event.target)\n if (match === undefined) return\n const { items, index } = imageItemsForButton(match)\n event.preventDefault()\n event.stopPropagation()\n event.stopImmediatePropagation()\n service.open({ items, index, opener: match.button, source: 'dsh-native', annotations: true })\n }\n document.addEventListener('click', onClick, true)\n return () => { document.removeEventListener('click', onClick, true) }\n}\n\nexport function apply(ctx) {\n const service = new NativeImageViewerService()\n ctx.effect(() => ctx.locale.register(name, LOCALES), `${name}: dictionaries`)\n ctx.effect(() => {\n const style = document.createElement('style')\n style.dataset.plugin = name\n style.textContent = VIEWER_CSS\n document.head.appendChild(style)\n return () => { style.remove() }\n }, `${name}: styles`)\n ctx.effect(() => {\n const disposeService = ctx.reflect.provide('nativeImageViewer', service)\n const disposeBridge = installOfficialImageBridge(service)\n return () => { disposeBridge(); void disposeService() }\n }, `${name}: optional viewer service and native image bridge`)\n const t = ctx.locale.bind(name)\n ctx.slots.inject('shell.overlay', () => ctx.slots.register({\n name: 'shell.overlay', id: name, order: 20, inject: () => ({ service, t }),\n }, ViewerOverlay))\n}\n\nexport { NativeImageViewerService, normalizeViewerRequest } from './viewer.js'\n"],"mappings":"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"}
1
+ {"version":3,"file":"client.js","names":["#listeners","#snapshot","#annotationsByImage","#revision","#emit","CSS","clamp","IconDownloadOutline16","IconEditOutline16","IconFullscreenOutline16","IconCloseOutline16","IconChevronLeftOutline14","IconChevronRightOutline14","IconCopyOutline16","VIEWER_CSS"],"sources":["../src/viewer.js","../src/styles.js","../src/image-transform.js","../src/client.jsx"],"sourcesContent":["const boundedNumber = (value, fallback) => Number.isFinite(value) && value > 0 ? value : fallback\nconst downloadOf = value => typeof value?.onInvoke === 'function' ? {\n pendingLabel: typeof value.pendingLabel === 'string' && value.pendingLabel !== '' ? value.pendingLabel : undefined,\n errorLabel: typeof value.errorLabel === 'string' && value.errorLabel !== '' ? value.errorLabel : undefined,\n onInvoke: value.onInvoke,\n} : undefined\nconst actionsOf = value => Array.isArray(value) ? value.flatMap((action, position) => {\n if (typeof action?.onInvoke !== 'function' || typeof action?.label !== 'string' || action.label.trim() === '') return []\n return [{\n id: typeof action.id === 'string' && action.id !== '' ? action.id : `action-${position + 1}`,\n label: action.label,\n pendingLabel: typeof action.pendingLabel === 'string' && action.pendingLabel !== '' ? action.pendingLabel : action.label,\n errorLabel: typeof action.errorLabel === 'string' && action.errorLabel !== '' ? action.errorLabel : action.label,\n closeOnSuccess: action.closeOnSuccess === true,\n onInvoke: action.onInvoke,\n }]\n}) : []\n\nexport function normalizeViewerRequest(request) {\n const rawItems = Array.isArray(request?.items) ? request.items : []\n const items = rawItems.flatMap((item, position) => {\n if (typeof item?.src !== 'string' || item.src === '') return []\n return [{\n id: typeof item.id === 'string' && item.id !== '' ? item.id : item.src,\n src: item.src,\n name: typeof item.name === 'string' && item.name !== '' ? item.name : `Image ${position + 1}`,\n width: boundedNumber(item.width, undefined),\n height: boundedNumber(item.height, undefined),\n bytes: boundedNumber(item.bytes, undefined),\n download: downloadOf(item.download),\n actions: actionsOf(item.actions),\n }]\n })\n if (items.length === 0) return undefined\n const requestedIndex = Number.isInteger(request?.index) ? request.index : 0\n return {\n items,\n index: Math.max(0, Math.min(items.length - 1, requestedIndex)),\n opener: typeof HTMLElement !== 'undefined' && request?.opener instanceof HTMLElement ? request.opener : undefined,\n source: typeof request?.source === 'string' ? request.source : 'dsh',\n annotations: request?.annotations !== false,\n }\n}\n\nexport class NativeImageViewerService {\n #listeners = new Set()\n #revision = 0\n #snapshot\n #annotationsByImage = new Map()\n\n constructor() {\n this.subscribe = listener => {\n this.#listeners.add(listener)\n return () => { this.#listeners.delete(listener) }\n }\n this.getSnapshot = () => this.#snapshot\n this.getAnnotationsSnapshot = () => Object.fromEntries(\n [...this.#annotationsByImage].map(([id, annotations]) => [id, annotations.map(annotation => ({ ...annotation }))]),\n )\n }\n\n setAnnotations(imageId, annotations) {\n if (typeof imageId !== 'string' || imageId === '' || !Array.isArray(annotations)) return\n if (annotations.length === 0) this.#annotationsByImage.delete(imageId)\n else this.#annotationsByImage.set(imageId, annotations.map(annotation => ({ ...annotation })))\n }\n\n open(request) {\n const normalized = normalizeViewerRequest(request)\n if (normalized === undefined) return false\n this.#revision += 1\n this.#snapshot = { ...normalized, revision: this.#revision }\n this.#emit()\n return true\n }\n\n close() {\n if (this.#snapshot === undefined) return\n const opener = this.#snapshot.opener\n this.#snapshot = undefined\n this.#emit()\n if (typeof window === 'undefined') opener?.focus()\n else window.requestAnimationFrame(() => { opener?.focus() })\n }\n\n #emit() {\n for (const listener of this.#listeners) listener()\n }\n}\n\nexport function nativeImageButton(target) {\n if (!(target instanceof Element)) return undefined\n const button = target.closest('button')\n if (!(button instanceof HTMLButtonElement)) return undefined\n const image = button.querySelector(':scope > img')\n if (!(image instanceof HTMLImageElement) || image.currentSrc === '') return undefined\n if (button.matches('[data-variant=\"single\"],[data-variant=\"tile\"]')) return { button, image, group: button.parentElement }\n const rail = button.closest('[role=\"group\"]')\n if (rail !== null && button.hasAttribute('title')) return { button, image, group: rail }\n return undefined\n}\n\nexport function imageItemsForButton(match) {\n const buttons = match.group?.querySelectorAll('button[data-variant=\"single\"],button[data-variant=\"tile\"],button[title]')\n ?? []\n const candidates = [...buttons].flatMap((button, index) => {\n const image = button.querySelector(':scope > img')\n if (!(image instanceof HTMLImageElement) || image.currentSrc === '') return []\n return [{\n id: image.currentSrc,\n src: image.currentSrc,\n name: image.alt || `Image ${index + 1}`,\n width: image.naturalWidth || undefined,\n height: image.naturalHeight || undefined,\n button,\n }]\n })\n const items = candidates.length > 0 ? candidates : [{\n id: match.image.currentSrc,\n src: match.image.currentSrc,\n name: match.image.alt || 'Image',\n width: match.image.naturalWidth || undefined,\n height: match.image.naturalHeight || undefined,\n button: match.button,\n }]\n const index = Math.max(0, items.findIndex(item => item.button === match.button))\n return { items: items.map(({ button: _button, ...item }) => item), index }\n}\n","export const CSS = String.raw`\n.niv-root{position:fixed;inset:0;z-index:1000;pointer-events:auto;overflow:hidden;background:rgba(7,8,10,.68);color:var(--dsw-alias-label-primary-inverted,#fff);outline:0;backdrop-filter:blur(13px) saturate(.72);-webkit-backdrop-filter:blur(13px) saturate(.72)}\n.niv-sr-only{position:absolute!important;width:1px!important;height:1px!important;padding:0!important;margin:-1px!important;overflow:hidden!important;clip:rect(0,0,0,0)!important;white-space:nowrap!important;border:0!important}\n.niv-title{position:absolute;top:20px;left:24px;right:84px;display:flex;align-items:baseline;gap:12px;color:#fff;font-size:13px}.niv-title strong{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.niv-title small{flex:none;opacity:.7}.niv-load-status{position:absolute;z-index:7;display:flex;align-items:center;gap:12px;color:#fff;font-size:14px}\n.niv-topbar{position:absolute;right:50%;bottom:22px;z-index:6;max-width:calc(100vw - 36px);padding:5px;border:1px solid rgba(255,255,255,.12);border-radius:999px;background:rgba(38,39,43,.86);box-shadow:0 10px 34px rgba(0,0,0,.3);transform:translateX(50%);backdrop-filter:blur(18px);-webkit-backdrop-filter:blur(18px)}\n.niv-actions{display:flex;align-items:center;gap:2px;overflow-x:auto;scrollbar-width:none}.niv-actions::-webkit-scrollbar{display:none}.niv-button,.niv-download{box-sizing:border-box;display:inline-flex;align-items:center;justify-content:center;gap:6px;height:32px;padding:0 10px;border:0;border-radius:999px;background:transparent;color:rgba(255,255,255,.9);font:inherit;font-size:12px;text-decoration:none;white-space:nowrap;cursor:pointer}.niv-button:hover,.niv-download:hover,.niv-button[data-active=true]{background:rgba(255,255,255,.12)}.niv-button:focus-visible,.niv-download:focus-visible,.niv-close-floating:focus-visible{outline:2px solid rgba(255,255,255,.9);outline-offset:2px}.niv-button:disabled,.niv-download:disabled{opacity:.38;cursor:default}.niv-icon-only{width:32px;padding:0}.niv-zoom{min-width:44px;color:rgba(255,255,255,.68);font-size:12px;font-variant-numeric:tabular-nums;text-align:center}\n.niv-close-floating{position:absolute;top:20px;right:20px;z-index:8;display:grid;place-items:center;width:42px;height:42px;padding:0;border:1px solid rgba(255,255,255,.1);border-radius:50%;background:rgba(48,49,53,.82);box-shadow:0 8px 24px rgba(0,0,0,.28);color:#fff;cursor:pointer;backdrop-filter:blur(16px);-webkit-backdrop-filter:blur(16px)}.niv-close-floating:hover{background:rgba(66,67,72,.92)}\n.niv-workspace{position:absolute;inset:0;display:grid;min-width:0;min-height:0;padding:68px 24px 72px;box-sizing:border-box}\n.niv-stage{position:relative;display:grid;place-items:center;min-width:0;min-height:0;overflow:hidden;padding:0;touch-action:none;user-select:none}.niv-stage[data-dragging=true]{cursor:grabbing}.niv-stage[data-annotating=true]{cursor:crosshair}\n.niv-surface{position:relative;display:inline-flex;max-width:100%;max-height:100%;transform-origin:center;will-change:transform}.niv-image{display:block;max-width:calc(100vw - 72px);max-height:calc(100vh - 154px);border-radius:12px;object-fit:contain;box-shadow:0 22px 60px rgba(0,0,0,.46);user-select:none;-webkit-user-drag:none}\n.niv-annotation{position:absolute;z-index:5;width:24px;height:24px;transform-origin:center;pointer-events:none}.niv-pin{position:absolute;inset:0;display:grid;place-items:center;width:24px;height:24px;padding:0;border:2px solid #fff;border-radius:50%;background:rgba(23,24,27,.94);box-shadow:0 4px 16px rgba(0,0,0,.35);color:#fff;font:inherit;font-size:11px;font-weight:700;cursor:pointer;pointer-events:auto}.niv-pin[data-active=true]{background:var(--dsw-alias-state-business-primary,#3964fe)}\n.niv-inline-note{position:absolute;bottom:34px;box-sizing:border-box;display:grid;width:min(300px,calc(100vw - 40px));grid-template-columns:24px minmax(0,1fr) 24px;align-items:center;gap:7px;padding:7px 8px;border:1px solid rgba(255,255,255,.12);border-radius:18px;background:rgba(32,33,37,.94);box-shadow:0 14px 38px rgba(0,0,0,.38);color:#fff;pointer-events:auto;backdrop-filter:blur(20px);-webkit-backdrop-filter:blur(20px)}.niv-annotation[data-x=right] .niv-inline-note{left:-8px}.niv-annotation[data-x=left] .niv-inline-note{right:-8px}.niv-annotation[data-x=center] .niv-inline-note{left:50%;transform:translateX(-50%)}.niv-annotation[data-y=down] .niv-inline-note{top:34px;bottom:auto}.niv-inline-note::after{position:absolute;width:9px;height:9px;background:rgba(32,33,37,.94);content:'';transform:rotate(45deg)}.niv-annotation[data-y=up] .niv-inline-note::after{bottom:-5px}.niv-annotation[data-y=down] .niv-inline-note::after{top:-5px}.niv-annotation[data-x=right] .niv-inline-note::after{left:13px}.niv-annotation[data-x=left] .niv-inline-note::after{right:13px}.niv-annotation[data-x=center] .niv-inline-note::after{left:calc(50% - 4px)}.niv-inline-index{display:grid;place-items:center;width:22px;height:22px;border-radius:50%;background:var(--dsw-alias-state-business-primary,#3964fe);color:#fff;font-size:10px;font-weight:700}.niv-inline-note textarea{box-sizing:border-box;width:100%;min-height:24px;max-height:92px;resize:none;overflow:auto;border:0;outline:0;background:transparent;color:#fff;font:inherit;font-size:12px;line-height:18px}.niv-inline-note textarea::placeholder{color:rgba(255,255,255,.44)}.niv-note-remove{display:grid;place-items:center;width:24px;height:24px;padding:0;border:0;border-radius:50%;background:transparent;color:rgba(255,255,255,.68);cursor:pointer}.niv-note-remove:hover{background:rgba(255,255,255,.1);color:#fff}\n.niv-nav{position:absolute;top:50%;z-index:4;width:42px;height:42px;padding:0;transform:translateY(-50%);background:rgba(48,49,53,.82);box-shadow:0 8px 24px rgba(0,0,0,.28);backdrop-filter:blur(16px)}.niv-prev{left:6px}.niv-next{right:6px}.niv-counter{position:absolute;bottom:8px;left:50%;padding:5px 10px;border-radius:999px;background:rgba(38,39,43,.86);color:rgba(255,255,255,.72);font-size:11px;transform:translateX(-50%);backdrop-filter:blur(16px)}\n.niv-hint{display:none}\n.niv-copy-notes{position:absolute;right:20px;bottom:22px;z-index:6;display:inline-flex;align-items:center;gap:6px;height:32px;padding:0 11px;border:1px solid rgba(255,255,255,.1);border-radius:999px;background:rgba(38,39,43,.86);color:rgba(255,255,255,.82);font:inherit;font-size:12px;cursor:pointer;backdrop-filter:blur(16px)}\n@media(max-width:760px){.niv-close-floating{top:12px;right:12px;width:40px;height:40px}.niv-workspace{padding:60px 12px 68px}.niv-image{max-width:calc(100vw - 24px);max-height:calc(100vh - 136px)}.niv-topbar{bottom:12px;max-width:calc(100vw - 24px)}.niv-button{padding:0 8px}.niv-button span.niv-label{display:none}.niv-inline-note{width:min(260px,calc(100vw - 40px))}.niv-copy-notes{display:none}}\n@media(prefers-reduced-motion:reduce){.niv-surface{transition:none}}\n`\n","import { useCallback, useEffect, useRef, useState } from 'react'\n\nconst clamp = (value, min, max) => Math.min(max, Math.max(min, value))\nconst fitted = () => ({ zoom: 1, x: 0, y: 0 })\n\n// Coordinates are relative to the fitted image; 100% uses its decoded pixel width.\nexport function useImageTransform(identity) {\n const [transform, setTransform] = useState(fitted)\n const [dragging, setDragging] = useState(false)\n const [pixelScale, setPixelScale] = useState(1)\n const stageRef = useRef(null), surfaceRef = useRef(null), imageRef = useRef(null)\n const transformRef = useRef(transform)\n const pointers = useRef(new Map()), gesture = useRef()\n transformRef.current = transform\n\n const resetGesture = useCallback(() => {\n pointers.current.clear()\n gesture.current = undefined\n setDragging(false)\n }, [])\n const fit = useCallback(() => { resetGesture(); setTransform(fitted()) }, [resetGesture])\n useEffect(() => { fit() }, [identity, fit])\n\n const actualScale = () => imageRef.current?.naturalWidth / Math.max(1, surfaceRef.current?.offsetWidth || 1) || 1\n const boundedPan = useCallback((zoom, x, y) => {\n const stage = stageRef.current, surface = surfaceRef.current\n if (!stage || !surface || zoom <= 1) return { x: 0, y: 0 }\n const limitX = Math.max(0, (surface.offsetWidth * zoom - stage.clientWidth) / 2)\n const limitY = Math.max(0, (surface.offsetHeight * zoom - stage.clientHeight) / 2)\n return { x: clamp(x, -limitX, limitX), y: clamp(y, -limitY, limitY) }\n }, [])\n const setZoomAt = useCallback((zoom, clientX, clientY) => {\n const stage = stageRef.current\n if (!stage) return\n setTransform(current => {\n const next = clamp(zoom, 0.5, Math.max(8, actualScale()))\n const box = stage.getBoundingClientRect()\n const px = clientX - box.left - box.width / 2, py = clientY - box.top - box.height / 2\n const ratio = next / current.zoom\n return { zoom: next, ...boundedPan(next, px - (px - current.x) * ratio, py - (py - current.y) * ratio) }\n })\n }, [boundedPan])\n const actual = useCallback(() => {\n if (!imageRef.current?.naturalWidth) return\n resetGesture()\n setTransform({ zoom: actualScale(), x: 0, y: 0 })\n }, [resetGesture])\n const measure = useCallback(() => {\n setPixelScale(1 / actualScale())\n setTransform(current => ({ ...current, ...boundedPan(current.zoom, current.x, current.y) }))\n }, [boundedPan])\n useEffect(() => {\n if (!stageRef.current || !surfaceRef.current) return\n const observer = new ResizeObserver(measure)\n observer.observe(stageRef.current)\n observer.observe(surfaceRef.current)\n measure()\n return () => observer.disconnect()\n }, [identity, measure])\n useEffect(() => {\n const stage = stageRef.current\n if (!stage) return\n const wheel = event => {\n event.preventDefault()\n const delta = event.deltaY * (event.deltaMode === 1 ? 16 : event.deltaMode === 2 ? stage.clientHeight : 1)\n setZoomAt(transformRef.current.zoom * Math.exp(-delta * 0.0015), event.clientX, event.clientY)\n }\n stage.addEventListener('wheel', wheel, { passive: false })\n return () => stage.removeEventListener('wheel', wheel)\n }, [identity, setZoomAt])\n\n const onPointerDown = event => {\n if (event.button !== 0 || event.target.closest('button,a,input,textarea,select')) return\n const current = transformRef.current\n if (event.pointerType === 'mouse' && current.zoom <= 1) return\n event.currentTarget.setPointerCapture(event.pointerId)\n pointers.current.set(event.pointerId, { x: event.clientX, y: event.clientY })\n if (pointers.current.size === 2) {\n const [a, b] = [...pointers.current.values()]\n gesture.current = { kind: 'pinch', distance: Math.hypot(a.x - b.x, a.y - b.y), transform: current }\n } else {\n gesture.current = { kind: 'pan', x: event.clientX, y: event.clientY, transform: current }\n }\n setDragging(true)\n }\n const onPointerMove = event => {\n if (!pointers.current.has(event.pointerId)) return\n pointers.current.set(event.pointerId, { x: event.clientX, y: event.clientY })\n const start = gesture.current\n if (start?.kind === 'pinch' && pointers.current.size >= 2) {\n const [a, b] = [...pointers.current.values()]\n setZoomAt(start.transform.zoom * Math.hypot(a.x - b.x, a.y - b.y) / Math.max(1, start.distance), (a.x + b.x) / 2, (a.y + b.y) / 2)\n } else if (start?.kind === 'pan') {\n setTransform({ zoom: start.transform.zoom, ...boundedPan(start.transform.zoom, start.transform.x + event.clientX - start.x, start.transform.y + event.clientY - start.y) })\n }\n }\n const endPointer = event => {\n pointers.current.delete(event.pointerId)\n const remaining = [...pointers.current.values()][0]\n if (remaining) gesture.current = { kind: 'pan', ...remaining, transform: transformRef.current }\n else resetGesture()\n }\n return { transform, transformRef, dragging, pixelScale, stageRef, surfaceRef, imageRef, fit, actual, measure, setZoomAt, resetGesture,\n pointerHandlers: { onPointerDown, onPointerMove, onPointerUp: endPointer, onPointerCancel: endPointer, onLostPointerCapture: endPointer } }\n}\n","import {\n useCallback, useEffect, useRef, useState, useSyncExternalStore,\n} from 'react'\nimport {\n IconChevronLeftOutline14, IconChevronRightOutline14, IconCloseOutline16,\n IconCopyOutline16, IconDownloadOutline16, IconEditOutline16, IconFullscreenOutline16,\n} from '@deepseek-ai/dsh-client-ui-primitives'\nimport {\n imageItemsForButton, NativeImageViewerService, nativeImageButton,\n} from './viewer.js'\nimport { CSS as VIEWER_CSS } from './styles.js'\nimport { useImageTransform } from './image-transform.js'\n\nexport const name = 'dsh-image-viewer'\nexport const inject = ['locale', 'slots']\n\nconst LOCALES = {\n en: {\n dialog: 'Image viewer', close: 'Close', fit: 'Fit', actual: '100%', download: 'Download',\n annotate: 'Mark region', cancelAnnotate: 'Cancel marking', regions: 'Region notes', regionHint: 'Click a point on the image, then add a note.',\n note: 'Region {value}', notePlaceholder: 'Describe what should change here…', removeNote: 'Remove region note',\n hideNotes: 'Hide notes', copyNotes: 'Copy notes', copied: 'Copied', previous: 'Previous image', next: 'Next image',\n zoomHint: 'Wheel to zoom · drag to pan · double-click for 100%', preparing: 'Preparing…', failed: 'Could not prepare this image.',\n loading: 'Loading image…', loadFailed: 'Image could not be loaded.', retry: 'Retry', copyFailed: 'Copy failed. Try again.',\n },\n zh: {\n dialog: '图片查看器', close: '关闭', fit: '适应窗口', actual: '原始大小', download: '下载',\n annotate: '标记区域', cancelAnnotate: '取消标记', regions: '区域备注', regionHint: '点击图片中的位置,然后填写备注。',\n note: '区域 {value}', notePlaceholder: '描述这里需要怎样调整…', removeNote: '删除区域备注',\n hideNotes: '收起备注', copyNotes: '复制备注', copied: '已复制', previous: '上一张图片', next: '下一张图片',\n zoomHint: '滚轮缩放 · 拖动查看 · 双击切换原始大小', preparing: '正在准备…', failed: '暂时无法准备这张图片。',\n loading: '正在加载图片…', loadFailed: '图片加载失败。', retry: '重试', copyFailed: '复制失败,请重试。',\n },\n}\n\nconst fill = (value, variables) => Object.entries(variables).reduce(\n (text, [key, replacement]) => text.replaceAll(`{${key}}`, String(replacement)),\n value,\n)\nconst clamp = (value, min, max) => Math.min(max, Math.max(min, value))\nconst bytesLabel = bytes => bytes === undefined ? undefined : bytes < 1024 * 1024\n ? `${Math.max(0.1, bytes / 1024).toLocaleString(undefined, { maximumFractionDigits: 1 })} KB`\n : `${(bytes / 1024 / 1024).toLocaleString(undefined, { maximumFractionDigits: 1 })} MB`\nconst downloadName = name => {\n const cleaned = String(name || 'image.png').replace(/[<>:\"/\\\\|?*\\u0000-\\u001f]/gu, '-').replace(/[. ]+$/u, '').trim()\n return cleaned === '' ? 'image.png' : cleaned\n}\nconst noteText = (annotations, t) => annotations.map((annotation, index) => {\n const label = fill(t('note'), { value: index + 1 })\n return `${label} (${Math.round(annotation.x * 100)}%, ${Math.round(annotation.y * 100)}%): ${annotation.note.trim()}`\n}).filter(line => !line.endsWith(': ')).join('\\n')\n\nfunction ViewerAction({ action, annotations, item, service }) {\n const [state, setState] = useState('idle')\n const invoke = async () => {\n if (state === 'pending') return\n setState('pending')\n try {\n await action.onInvoke({ annotations, item, src: item.src })\n setState('idle')\n if (action.closeOnSuccess) service.close()\n } catch { setState('failed') }\n }\n const label = state === 'pending' ? action.pendingLabel : state === 'failed' ? action.errorLabel : action.label\n return <button type=\"button\" className=\"niv-button\" disabled={state === 'pending'} onClick={() => { void invoke() }}><span className=\"niv-label\">{label}</span></button>\n}\n\nfunction ViewerDownload({ download, item, t }) {\n const [state, setState] = useState('idle')\n const invoke = async () => {\n if (state === 'pending') return\n setState('pending')\n try { await download.onInvoke({ item, src: item.src }); setState('idle') } catch { setState('failed') }\n }\n const label = state === 'pending' ? download.pendingLabel ?? t('preparing') : state === 'failed' ? download.errorLabel ?? t('failed') : t('download')\n return <button type=\"button\" className=\"niv-download\" disabled={state === 'pending'} onClick={() => { void invoke() }}><IconDownloadOutline16 /><span className=\"niv-label\">{label}</span></button>\n}\n\nfunction ViewerOverlay({ service, t }) {\n const request = useSyncExternalStore(service.subscribe, service.getSnapshot)\n const [index, setIndex] = useState(0)\n const [imageState, setImageState] = useState('loading')\n const [attempt, setAttempt] = useState(0)\n const [copyFailed, setCopyFailed] = useState(false)\n const [annotating, setAnnotating] = useState(false)\n const [annotationsByImage, setAnnotationsByImage] = useState(service.getAnnotationsSnapshot)\n const annotationsByImageRef = useRef(annotationsByImage)\n const [selected, setSelected] = useState()\n const [focusNote, setFocusNote] = useState()\n const [copied, setCopied] = useState(false)\n const rootRef = useRef(null)\n annotationsByImageRef.current = annotationsByImage\n\n useEffect(() => {\n if (request === undefined) return\n setIndex(request.index)\n setAnnotating(false)\n setSelected(undefined)\n setCopied(false)\n }, [request?.revision])\n\n const item = request?.items[index]\n const { transform, transformRef, dragging, pixelScale, stageRef, surfaceRef, imageRef, fit, actual, measure, setZoomAt, resetGesture, pointerHandlers } = useImageTransform(`${request?.revision}:${item?.id}:${item?.src}`)\n const annotations = item === undefined ? [] : annotationsByImage[item.id] ?? []\n const setAnnotations = useCallback((update) => {\n if (item === undefined) return\n const previous = annotationsByImageRef.current[item.id] ?? []\n const next = typeof update === 'function' ? update(previous) : update\n const snapshot = { ...annotationsByImageRef.current, [item.id]: next }\n annotationsByImageRef.current = snapshot\n service.setAnnotations(item.id, next)\n setAnnotationsByImage(snapshot)\n }, [item?.id, service])\n\n useEffect(() => {\n if (request === undefined) return undefined\n const previousOverflow = document.body.style.overflow\n document.body.style.overflow = 'hidden'\n rootRef.current?.focus()\n const onKeyDown = event => {\n if (event.defaultPrevented || event.isComposing) return\n if (event.key === 'Escape') {\n event.preventDefault()\n event.stopPropagation()\n if (event.target instanceof Element && event.target.closest('.niv-inline-note') !== null) {\n setSelected(undefined)\n return\n }\n service.close()\n return\n }\n if (event.ctrlKey || event.metaKey || event.altKey) return\n const editing = event.target instanceof Element && event.target.closest('input,textarea,select,[contenteditable=\"true\"]') !== null\n if (!editing && ['ArrowLeft', 'ArrowRight', '+', '=', '-', 'f', 'F'].includes(event.key)) event.stopPropagation()\n if (!editing && event.key === 'ArrowLeft' && request.items.length > 1) {\n event.preventDefault(); setIndex(value => (value - 1 + request.items.length) % request.items.length)\n } else if (!editing && event.key === 'ArrowRight' && request.items.length > 1) {\n event.preventDefault(); setIndex(value => (value + 1) % request.items.length)\n } else if (!editing && (event.key === '+' || event.key === '=')) {\n event.preventDefault(); const box = stageRef.current?.getBoundingClientRect(); if (box) setZoomAt(transformRef.current.zoom * 1.2, box.left + box.width / 2, box.top + box.height / 2)\n } else if (!editing && event.key === '-') {\n event.preventDefault(); const box = stageRef.current?.getBoundingClientRect(); if (box) setZoomAt(transformRef.current.zoom / 1.2, box.left + box.width / 2, box.top + box.height / 2)\n } else if (!editing && event.key.toLowerCase() === 'f') {\n event.preventDefault(); fit()\n } else if (event.key === 'Tab') {\n const controls = [...rootRef.current.querySelectorAll('button:not(:disabled),a[href],input:not(:disabled),textarea:not(:disabled),[tabindex]:not([tabindex=\"-1\"])')]\n const first = controls[0]\n const last = controls.at(-1)\n if (event.shiftKey && (document.activeElement === first || document.activeElement === rootRef.current)) { event.preventDefault(); last?.focus() }\n else if (!event.shiftKey && (document.activeElement === last || document.activeElement === rootRef.current)) { event.preventDefault(); first?.focus() }\n }\n }\n const root = rootRef.current\n root.addEventListener('keydown', onKeyDown)\n return () => {\n document.body.style.overflow = previousOverflow\n root.removeEventListener('keydown', onKeyDown)\n }\n }, [request, service, fit, setZoomAt])\n\n useEffect(() => {\n if (focusNote === undefined) return\n const field = rootRef.current?.querySelector(`[data-note-id=\"${CSS.escape(focusNote)}\"] textarea`)\n field?.focus()\n setFocusNote(undefined)\n }, [focusNote, selected, annotations.length])\n\n useEffect(() => {\n setAnnotating(false)\n setSelected(undefined)\n setImageState('loading')\n setCopyFailed(false)\n setCopied(false)\n }, [item?.id, item?.src, request?.revision])\n\n const addAnnotation = event => {\n if (!annotating || event.target.closest('.niv-annotation')) return\n const bounds = surfaceRef.current?.getBoundingClientRect()\n if (bounds === undefined) return\n const annotation = {\n id: crypto.randomUUID(),\n x: clamp((event.clientX - bounds.left) / bounds.width, 0, 1),\n y: clamp((event.clientY - bounds.top) / bounds.height, 0, 1),\n note: '',\n }\n setAnnotations(current => [...current, annotation])\n setAnnotating(false)\n setSelected(annotation.id)\n setFocusNote(annotation.id)\n }\n const copyNotes = async () => {\n const text = noteText(annotations, t)\n if (text === '') return\n try {\n await navigator.clipboard.writeText(text)\n setCopied(true)\n setCopyFailed(false)\n } catch { setCopyFailed(true) }\n }\n\n if (request === undefined || item === undefined) return null\n const meta = [item.width && item.height ? `${item.width} × ${item.height}` : undefined, bytesLabel(item.bytes)].filter(Boolean).join(' · ')\n const showCounter = request.items.length > 1\n return <div ref={rootRef} className=\"niv-root\" role=\"dialog\" aria-modal=\"true\" aria-label={t('dialog')} tabIndex={-1}>\n <div className=\"niv-title\"><strong>{item.name}</strong>{meta !== '' ? <small>{meta}</small> : null}</div>\n <header className=\"niv-topbar\" role=\"toolbar\" aria-label={t('dialog')}>\n <div className=\"niv-actions\">\n {request.annotations ? <button type=\"button\" className=\"niv-button\" data-active={annotating} aria-label={annotating ? t('cancelAnnotate') : t('annotate')} aria-pressed={annotating} disabled={imageState !== 'ready'} onClick={() => { resetGesture(); setAnnotating(value => !value) }}><IconEditOutline16 /><span className=\"niv-label\">{annotating ? t('cancelAnnotate') : t('annotate')}</span></button> : null}\n {annotations.length > 0 ? <button type=\"button\" className=\"niv-button\" data-active={selected !== undefined} onClick={() => { const first = annotations[0]; setSelected(current => current === undefined ? first.id : undefined); if (selected === undefined) setFocusNote(first.id) }}>{annotations.length} <span className=\"niv-label\">{t('regions')}</span></button> : null}\n <button type=\"button\" className=\"niv-button\" aria-label={t('fit')} onClick={fit}><IconFullscreenOutline16 /><span className=\"niv-label\">{t('fit')}</span></button>\n <button type=\"button\" className=\"niv-button\" disabled={imageState !== 'ready'} onClick={actual}>{t('actual')}</button>\n <span className=\"niv-zoom\">{Math.round(transform.zoom * pixelScale * 100)}%</span>\n {item.download === undefined\n ? <a className=\"niv-download\" href={item.src} download={downloadName(item.name)}><IconDownloadOutline16 /><span className=\"niv-label\">{t('download')}</span></a>\n : <ViewerDownload key={item.src} download={item.download} item={item} t={t} />}\n {item.actions.map(action => <ViewerAction action={action} annotations={annotations} item={item} service={service} key={`${item.src}:${action.id}`} />)}\n </div>\n </header>\n <button type=\"button\" className=\"niv-close-floating\" aria-label={t('close')} onClick={() => service.close()}><IconCloseOutline16 /></button>\n <div className=\"niv-workspace\">\n <main ref={stageRef} className=\"niv-stage\" data-dragging={dragging} data-annotating={annotating} onClick={event => { if (event.target === event.currentTarget && !annotating && transform.zoom === 1) service.close() }} {...(!annotating && imageState === 'ready' ? pointerHandlers : {})} onDoubleClick={event => { if (event.target.closest('button,a,input,textarea') || imageState !== 'ready') return; if (transform.zoom === 1) actual(); else fit() }}>\n {imageState !== 'ready' ? <div className=\"niv-load-status\" role={imageState === 'error' ? 'alert' : 'status'}>{t(imageState === 'error' ? 'loadFailed' : 'loading')}{imageState === 'error' ? <button type=\"button\" className=\"niv-button\" onClick={() => { setImageState('loading'); setAttempt(value => value + 1) }}>{t('retry')}</button> : null}</div> : null}\n <div ref={surfaceRef} className=\"niv-surface\" onClick={addAnnotation} style={{ visibility: imageState === 'ready' ? 'visible' : 'hidden', transform: `translate3d(${transform.x}px,${transform.y}px,0) scale(${transform.zoom})` }}>\n <img key={`${request.revision}:${item.id}:${attempt}`} ref={imageRef} className=\"niv-image\" src={item.src} alt={item.name} draggable=\"false\" onLoad={() => { setImageState('ready'); measure() }} onError={() => setImageState('error')} />\n {annotations.map((annotation, position) => <div className=\"niv-annotation\" data-x={annotation.x < 0.38 ? 'right' : annotation.x > 0.62 ? 'left' : 'center'} data-y={annotation.y < 0.28 ? 'down' : 'up'} style={{ left: `${annotation.x * 100}%`, top: `${annotation.y * 100}%`, transform: `translate(-50%,-50%) scale(${1 / transform.zoom})` }} key={annotation.id}>\n <button type=\"button\" className=\"niv-pin\" data-active={selected === annotation.id} aria-label={fill(t('note'), { value: position + 1 })} onClick={event => { event.stopPropagation(); const opening = selected !== annotation.id; setSelected(opening ? annotation.id : undefined); if (opening) setFocusNote(annotation.id) }}>{position + 1}</button>\n {selected === annotation.id ? <div className=\"niv-inline-note\" data-note-id={annotation.id} onClick={event => event.stopPropagation()}>\n <span className=\"niv-inline-index\">{position + 1}</span>\n <textarea value={annotation.note} rows={1} aria-label={fill(t('note'), { value: position + 1 })} placeholder={t('notePlaceholder')} onChange={event => { const note = event.target.value; setAnnotations(current => current.map(entry => entry.id === annotation.id ? { ...entry, note } : entry)) }} onKeyDown={event => {\n if ((event.key === 'Enter' && !event.shiftKey) || event.key === 'Escape') {\n event.preventDefault()\n event.stopPropagation()\n event.nativeEvent?.stopImmediatePropagation?.()\n setSelected(undefined)\n }\n }} />\n <button type=\"button\" className=\"niv-note-remove\" aria-label={t('removeNote')} onClick={event => { event.stopPropagation(); setAnnotations(current => current.filter(entry => entry.id !== annotation.id)); setSelected(undefined) }}><IconCloseOutline16 /></button>\n </div> : null}\n </div>)}\n </div>\n {showCounter ? <>\n <button type=\"button\" className=\"niv-button niv-icon-only niv-nav niv-prev\" aria-label={t('previous')} onClick={event => { event.stopPropagation(); setIndex(value => (value - 1 + request.items.length) % request.items.length) }}><IconChevronLeftOutline14 /></button>\n <button type=\"button\" className=\"niv-button niv-icon-only niv-nav niv-next\" aria-label={t('next')} onClick={event => { event.stopPropagation(); setIndex(value => (value + 1) % request.items.length) }}><IconChevronRightOutline14 /></button>\n <span className=\"niv-counter\">{index + 1} / {request.items.length}</span>\n </> : annotating ? <span className=\"niv-hint\">{t('regionHint')}</span> : transform.zoom === 1 ? <span className=\"niv-hint\">{t('zoomHint')}</span> : null}\n </main>\n {annotations.some(annotation => annotation.note.trim() !== '') ? <button type=\"button\" className=\"niv-copy-notes\" onClick={() => { void copyNotes() }}><IconCopyOutline16 />{copyFailed ? t('copyFailed') : copied ? t('copied') : t('copyNotes')}</button> : null}\n </div>\n </div>\n}\n\nfunction installOfficialImageBridge(service) {\n const onClick = event => {\n const match = nativeImageButton(event.target)\n if (match === undefined) return\n const { items, index } = imageItemsForButton(match)\n event.preventDefault()\n event.stopPropagation()\n event.stopImmediatePropagation()\n service.open({ items, index, opener: match.button, source: 'dsh-native', annotations: true })\n }\n document.addEventListener('click', onClick, true)\n return () => { document.removeEventListener('click', onClick, true) }\n}\n\nexport function apply(ctx) {\n const service = new NativeImageViewerService()\n ctx.effect(() => ctx.locale.register(name, LOCALES), `${name}: dictionaries`)\n ctx.effect(() => {\n const style = document.createElement('style')\n style.dataset.plugin = name\n style.textContent = VIEWER_CSS\n document.head.appendChild(style)\n return () => { style.remove() }\n }, `${name}: styles`)\n ctx.effect(() => {\n const disposeService = ctx.reflect.provide('nativeImageViewer', service)\n const disposeBridge = installOfficialImageBridge(service)\n return () => { disposeBridge(); void disposeService() }\n }, `${name}: optional viewer service and native image bridge`)\n const t = ctx.locale.bind(name)\n ctx.slots.inject('shell.overlay', () => ctx.slots.register({\n name: 'shell.overlay', id: name, order: 20, inject: () => ({ service, t }),\n }, ViewerOverlay))\n}\n\nexport { NativeImageViewerService, normalizeViewerRequest } from './viewer.js'\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-image-viewer",
3
- "version": "0.1.0-beta.10",
3
+ "version": "0.1.0-beta.11",
4
4
  "description": "Zoom, pan, download, gallery, and region-note image viewer for DeepSeek Harness",
5
5
  "keywords": [
6
6
  "dsh-plugin",
@@ -63,10 +63,10 @@
63
63
  },
64
64
  "peerDependencies": {
65
65
  "@deepseek-ai/cordis": "4.0.1 || 4.0.2",
66
- "@deepseek-ai/dsh-client-locale": "0.1.1-rc.2 || 0.1.2-rc.1 || 0.1.2-alpha.2 || 0.1.2-alpha.3 || 0.1.3-alpha.2",
67
- "@deepseek-ai/dsh-client-ui-layout": "0.1.1-rc.2 || 0.1.2-rc.1 || 0.1.2-alpha.2 || 0.1.2-alpha.3 || 0.1.3-alpha.2",
68
- "@deepseek-ai/dsh-client-ui-primitives": "0.1.1-rc.2 || 0.1.2-rc.1 || 0.1.2-alpha.2 || 0.1.2-alpha.3 || 0.1.3-alpha.2",
69
- "@deepseek-ai/dsh-client-ui-slots": "0.1.1-rc.2 || 0.1.2-rc.1 || 0.1.2-alpha.2 || 0.1.2-alpha.3 || 0.1.3-alpha.2",
66
+ "@deepseek-ai/dsh-client-locale": "0.1.1-rc.2 || 0.1.2-rc.1 || 0.1.2-alpha.2 || 0.1.2-alpha.3 || 0.1.3-alpha.2 || 0.1.5-alpha.1",
67
+ "@deepseek-ai/dsh-client-ui-layout": "0.1.1-rc.2 || 0.1.2-rc.1 || 0.1.2-alpha.2 || 0.1.2-alpha.3 || 0.1.3-alpha.2 || 0.1.5-alpha.1",
68
+ "@deepseek-ai/dsh-client-ui-primitives": "0.1.1-rc.2 || 0.1.2-rc.1 || 0.1.2-alpha.2 || 0.1.2-alpha.3 || 0.1.3-alpha.2 || 0.1.5-alpha.1",
69
+ "@deepseek-ai/dsh-client-ui-slots": "0.1.1-rc.2 || 0.1.2-rc.1 || 0.1.2-alpha.2 || 0.1.2-alpha.3 || 0.1.3-alpha.2 || 0.1.5-alpha.1",
70
70
  "react": "^18.2.0"
71
71
  },
72
72
  "peerDependenciesMeta": {
@@ -97,6 +97,7 @@
97
97
  "@deepseek-ai/dsh-client-ui-slots": "0.1.2-rc.1",
98
98
  "playwright": "1.62.1",
99
99
  "react": "18.3.1",
100
+ "react-dom": "18.3.1",
100
101
  "tsdown": "0.22.2"
101
102
  },
102
103
  "scripts": {