@eclipse-lyra/extension-media-viewer 0.7.53 → 0.7.54

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/dist/index.js CHANGED
@@ -5,7 +5,7 @@ extensionRegistry.registerExtension({
5
5
  id: pkg.name,
6
6
  name: t.EXT_MEDIAVIEWER_NAME,
7
7
  description: t.EXT_MEDIAVIEWER_DESC,
8
- loader: () => import("./media-viewer-extension-Cx-1gOxs.js"),
8
+ loader: () => import("./media-viewer-extension-B7CpFkFz.js"),
9
9
  icon: "image"
10
10
  });
11
11
  //# sourceMappingURL=index.js.map
@@ -47,11 +47,10 @@ editorRegistry.registerEditorInputHandler({
47
47
  data: input,
48
48
  key: input.getWorkspacePath(),
49
49
  icon: getFileIcon(input.getName()),
50
- noOverflow: false,
51
50
  state: {}
52
51
  };
53
- editorInput.component = () => html`
54
- <lyra-media-viewer .input=${editorInput}></lyra-media-viewer>`;
52
+ editorInput.component = (id) => html`
53
+ <lyra-media-viewer id="${id}" .input=${editorInput}></lyra-media-viewer>`;
55
54
  return editorInput;
56
55
  },
57
56
  ranking: 1e3
@@ -59,6 +58,7 @@ editorRegistry.registerEditorInputHandler({
59
58
  let LyraMediaViewer = class extends LyraPart {
60
59
  constructor() {
61
60
  super(...arguments);
61
+ this.scrollMode = "native";
62
62
  this.imageNaturalWidth = 0;
63
63
  this.imageNaturalHeight = 0;
64
64
  this.overlays = [];
@@ -111,7 +111,7 @@ let LyraMediaViewer = class extends LyraPart {
111
111
  this.mediaUrl = url;
112
112
  this.overlays = [];
113
113
  }
114
- render() {
114
+ renderContent() {
115
115
  if (!this.mediaUrl) {
116
116
  return html`
117
117
  <div style="display: flex; align-items: center; justify-content: center; height: 100%;">
@@ -121,15 +121,9 @@ let LyraMediaViewer = class extends LyraPart {
121
121
  }
122
122
  const isImage = this.getIsImage();
123
123
  if (isImage) {
124
- const w = this.imageNaturalWidth || 1;
125
- const h = this.imageNaturalHeight || 1;
126
- const aspectRatio = w / h;
127
124
  return html`
128
125
  <div class="image-container">
129
- <div
130
- class="image-wrapper"
131
- style="aspect-ratio: ${aspectRatio};"
132
- >
126
+ <div class="image-wrapper">
133
127
  <img
134
128
  class="media-image"
135
129
  src="${this.mediaUrl}"
@@ -192,8 +186,11 @@ LyraMediaViewer.styles = css`
192
186
 
193
187
  .image-wrapper {
194
188
  position: relative;
189
+ width: 100%;
190
+ height: 100%;
195
191
  max-width: 100%;
196
192
  max-height: 100%;
193
+ overflow: hidden;
197
194
  }
198
195
 
199
196
  .media-iframe-container {
@@ -272,4 +269,4 @@ LyraMediaViewer = __decorateClass([
272
269
  export {
273
270
  LyraMediaViewer
274
271
  };
275
- //# sourceMappingURL=media-viewer-extension-Cx-1gOxs.js.map
272
+ //# sourceMappingURL=media-viewer-extension-B7CpFkFz.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"media-viewer-extension-B7CpFkFz.js","sources":["../src/media-viewer-extension.ts"],"sourcesContent":["import { customElement, property, state } from \"lit/decorators.js\";\nimport { css, html, nothing } from \"lit\";\nimport { LyraPart, EditorInput, editorRegistry, File } from \"@eclipse-lyra/core\";\n\nconst SUPPORTED_EXTENSIONS = [\n '.pdf',\n '.jpg', '.jpeg', '.png', '.gif', '.svg', '.webp', '.bmp', '.ico'\n];\n\nconst getFileIcon = (fileName: string): string => {\n const lowerName = fileName.toLowerCase();\n if (lowerName.endsWith('.pdf')) return 'file-pdf';\n if (lowerName.match(/\\.(jpg|jpeg|png|gif|webp|bmp|ico|svg)$/)) return 'image';\n return 'file';\n};\n\nconst isSupportedMediaFile = (file: File): boolean => {\n const fileName = file.getName().toLowerCase();\n return SUPPORTED_EXTENSIONS.some(ext => fileName.endsWith(ext));\n};\n\nconst IMAGE_EXTENSIONS = ['.jpg', '.jpeg', '.png', '.gif', '.svg', '.webp', '.bmp', '.ico'];\n\nconst isImageFile = (file: File): boolean => {\n const name = file.getName().toLowerCase();\n return IMAGE_EXTENSIONS.some(ext => name.endsWith(ext));\n};\n\n/** Normalized bounding box overlay (0–1). Used by extensions to draw on the image. */\nexport interface BboxOverlay {\n type: 'bbox';\n left: number;\n top: number;\n width: number;\n height: number;\n label?: string;\n /** CSS color for border and label (e.g. by category). */\n color?: string;\n}\n\n/** Segment mask overlay: PNG data URL (alpha where segment is), drawn over the image. */\nexport interface MaskOverlay {\n type: 'mask';\n /** Data URL of the mask image (e.g. image/png with alpha). */\n dataUrl: string;\n label?: string;\n color?: string;\n}\n\nexport type MediaViewerOverlay = BboxOverlay | MaskOverlay;\n\neditorRegistry.registerEditorInputHandler({\n editorId: \"system.media-viewer\",\n label: \"Media viewer\",\n canHandle: input => input instanceof File && isSupportedMediaFile(input),\n handle: async (input: File) => {\n const editorInput = {\n title: input.getWorkspacePath(),\n data: input,\n key: input.getWorkspacePath(),\n icon: getFileIcon(input.getName()),\n state: {},\n } as EditorInput\n editorInput.component = (id: string) => html`\n <lyra-media-viewer id=\"${id}\" .input=${editorInput}></lyra-media-viewer>`\n return editorInput;\n },\n ranking: 1000\n})\n\n@customElement('lyra-media-viewer')\nexport class LyraMediaViewer extends LyraPart {\n protected scrollMode: 'scroller' | 'native' = 'native';\n @property({attribute: false})\n public input?: EditorInput\n\n @state()\n private mediaUrl?: string\n\n @state()\n private imageNaturalWidth = 0\n\n @state()\n private imageNaturalHeight = 0\n\n @state()\n private overlays: MediaViewerOverlay[] = []\n\n getMediaUrl(): string | undefined {\n return this.mediaUrl;\n }\n\n getIsImage(): boolean {\n return !!(this.input?.data instanceof File && this.mediaUrl && isImageFile(this.input.data));\n }\n\n getImageDimensions(): { width: number; height: number } {\n return { width: this.imageNaturalWidth, height: this.imageNaturalHeight };\n }\n\n /** Set overlays drawn on the image (e.g. from object detection or other extensions). Coordinates in 0–1. */\n setOverlays(overlays: MediaViewerOverlay[]): void {\n this.overlays = overlays ?? [];\n }\n\n getOverlays(): MediaViewerOverlay[] {\n return [...this.overlays];\n }\n\n clearOverlays(): void {\n this.overlays = [];\n }\n\n protected doClose() {\n if (this.mediaUrl && this.mediaUrl.startsWith('blob:')) {\n URL.revokeObjectURL(this.mediaUrl)\n }\n this.input = undefined\n this.mediaUrl = undefined\n this.overlays = []\n this.imageNaturalWidth = 0\n this.imageNaturalHeight = 0\n }\n\n protected async doInitUI() {\n await this.loadMedia()\n }\n\n private async loadMedia() {\n if (!this.input?.data || !(this.input.data instanceof File)) {\n return\n }\n\n const file = this.input.data\n const url = await file.getContents({uri: true}) as string\n this.mediaUrl = url\n this.overlays = []\n }\n\n private onImageLoad = (e: Event): void => {\n const img = e.target as HTMLImageElement;\n if (img?.naturalWidth && img?.naturalHeight) {\n this.imageNaturalWidth = img.naturalWidth;\n this.imageNaturalHeight = img.naturalHeight;\n }\n }\n\n protected renderContent() {\n if (!this.mediaUrl) {\n return html`\n <div style=\"display: flex; align-items: center; justify-content: center; height: 100%;\">\n <wa-spinner></wa-spinner>\n </div>\n `\n }\n\n const isImage = this.getIsImage();\n if (isImage) {\n return html`\n <div class=\"image-container\">\n <div class=\"image-wrapper\">\n <img\n class=\"media-image\"\n src=\"${this.mediaUrl}\"\n alt=\"\"\n @load=${this.onImageLoad}\n />\n ${this.overlays.length > 0 ? html`\n <div class=\"overlay-layer\">\n ${this.overlays.map((o) => o.type === 'bbox' ? html`\n <div\n class=\"overlay-bbox\"\n style=\"left: ${o.left * 100}%; top: ${o.top * 100}%; width: ${o.width * 100}%; height: ${o.height * 100}%;${o.color ? ` --overlay-color: ${o.color};` : ''}\"\n title=\"${o.label ?? ''}\"\n >\n ${o.label ? html`<span class=\"overlay-label\">${o.label}</span>` : nothing}\n </div>\n ` : o.type === 'mask' ? html`\n <img\n class=\"overlay-mask\"\n src=\"${o.dataUrl}\"\n alt=\"${o.label ?? ''}\"\n title=\"${o.label ?? ''}\"\n />\n ` : nothing)}\n </div>\n ` : nothing}\n </div>\n </div>\n `;\n }\n\n return html`\n <div class=\"media-iframe-container\">\n <iframe\n src=\"${this.mediaUrl}\"\n class=\"media-iframe\"\n title=\"Media Viewer\"></iframe>\n </div>\n `\n }\n\n static styles = css`\n :host {\n display: flex;\n flex-direction: column;\n position: relative;\n width: 100%;\n height: 100%;\n background: var(--wa-color-surface-default, #1a1a1a);\n }\n\n .image-container {\n position: relative;\n width: 100%;\n height: 100%;\n display: flex;\n align-items: center;\n justify-content: center;\n overflow: hidden;\n }\n\n .image-wrapper {\n position: relative;\n width: 100%;\n height: 100%;\n max-width: 100%;\n max-height: 100%;\n overflow: hidden;\n }\n\n .media-iframe-container {\n position: absolute;\n inset: 0;\n min-height: 0;\n }\n\n .media-iframe {\n display: block;\n width: 100%;\n height: 100%;\n border: 0;\n }\n\n .media-image {\n display: block;\n width: 100%;\n height: 100%;\n object-fit: contain;\n }\n\n .overlay-layer {\n position: absolute;\n inset: 0;\n pointer-events: none;\n }\n\n .overlay-bbox {\n position: absolute;\n border: 2px solid var(--overlay-color, var(--wa-color-brand-fill, #0d6efd));\n background: color-mix(in srgb, var(--overlay-color, #0d6efd) 15%, transparent);\n pointer-events: auto;\n box-sizing: border-box;\n }\n\n .overlay-label {\n position: absolute;\n left: 0;\n top: -1.25em;\n font-size: 0.7rem;\n white-space: nowrap;\n background: var(--overlay-color, var(--wa-color-brand-fill, #0d6efd));\n color: var(--wa-color-surface-default, #fff);\n padding: 1px 4px;\n border-radius: 2px;\n }\n\n .overlay-mask {\n position: absolute;\n inset: 0;\n width: 100%;\n height: 100%;\n object-fit: fill;\n pointer-events: none;\n }\n `\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'lyra-media-viewer': LyraMediaViewer\n }\n}\n\n"],"names":[],"mappings":";;;;;;;;;;;;;AAIA,MAAM,uBAAuB;AAAA,EACzB;AAAA,EACA;AAAA,EAAQ;AAAA,EAAS;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAS;AAAA,EAAQ;AAC9D;AAEA,MAAM,cAAc,CAAC,aAA6B;AAC9C,QAAM,YAAY,SAAS,YAAA;AAC3B,MAAI,UAAU,SAAS,MAAM,EAAG,QAAO;AACvC,MAAI,UAAU,MAAM,wCAAwC,EAAG,QAAO;AACtE,SAAO;AACX;AAEA,MAAM,uBAAuB,CAAC,SAAwB;AAClD,QAAM,WAAW,KAAK,QAAA,EAAU,YAAA;AAChC,SAAO,qBAAqB,KAAK,CAAA,QAAO,SAAS,SAAS,GAAG,CAAC;AAClE;AAEA,MAAM,mBAAmB,CAAC,QAAQ,SAAS,QAAQ,QAAQ,QAAQ,SAAS,QAAQ,MAAM;AAE1F,MAAM,cAAc,CAAC,SAAwB;AACzC,QAAM,OAAO,KAAK,QAAA,EAAU,YAAA;AAC5B,SAAO,iBAAiB,KAAK,CAAA,QAAO,KAAK,SAAS,GAAG,CAAC;AAC1D;AAyBA,eAAe,2BAA2B;AAAA,EACtC,UAAU;AAAA,EACV,OAAO;AAAA,EACP,WAAW,CAAA,UAAS,iBAAiB,QAAQ,qBAAqB,KAAK;AAAA,EACvE,QAAQ,OAAO,UAAgB;AAC3B,UAAM,cAAc;AAAA,MAChB,OAAO,MAAM,iBAAA;AAAA,MACb,MAAM;AAAA,MACN,KAAK,MAAM,iBAAA;AAAA,MACX,MAAM,YAAY,MAAM,SAAS;AAAA,MACjC,OAAO,CAAA;AAAA,IAAC;AAEZ,gBAAY,YAAY,CAAC,OAAe;AAAA,qCACX,EAAE,YAAY,WAAW;AACtD,WAAO;AAAA,EACX;AAAA,EACA,SAAS;AACb,CAAC;AAGM,IAAM,kBAAN,cAA8B,SAAS;AAAA,EAAvC,cAAA;AAAA,UAAA,GAAA,SAAA;AACH,SAAU,aAAoC;AAQ9C,SAAQ,oBAAoB;AAG5B,SAAQ,qBAAqB;AAG7B,SAAQ,WAAiC,CAAA;AAqDzC,SAAQ,cAAc,CAAC,MAAmB;AACtC,YAAM,MAAM,EAAE;AACd,UAAI,KAAK,gBAAgB,KAAK,eAAe;AACzC,aAAK,oBAAoB,IAAI;AAC7B,aAAK,qBAAqB,IAAI;AAAA,MAClC;AAAA,IACJ;AAAA,EAAA;AAAA,EAzDA,cAAkC;AAC9B,WAAO,KAAK;AAAA,EAChB;AAAA,EAEA,aAAsB;AAClB,WAAO,CAAC,EAAE,KAAK,OAAO,gBAAgB,QAAQ,KAAK,YAAY,YAAY,KAAK,MAAM,IAAI;AAAA,EAC9F;AAAA,EAEA,qBAAwD;AACpD,WAAO,EAAE,OAAO,KAAK,mBAAmB,QAAQ,KAAK,mBAAA;AAAA,EACzD;AAAA;AAAA,EAGA,YAAY,UAAsC;AAC9C,SAAK,WAAW,YAAY,CAAA;AAAA,EAChC;AAAA,EAEA,cAAoC;AAChC,WAAO,CAAC,GAAG,KAAK,QAAQ;AAAA,EAC5B;AAAA,EAEA,gBAAsB;AAClB,SAAK,WAAW,CAAA;AAAA,EACpB;AAAA,EAEU,UAAU;AAChB,QAAI,KAAK,YAAY,KAAK,SAAS,WAAW,OAAO,GAAG;AACpD,UAAI,gBAAgB,KAAK,QAAQ;AAAA,IACrC;AACA,SAAK,QAAQ;AACb,SAAK,WAAW;AAChB,SAAK,WAAW,CAAA;AAChB,SAAK,oBAAoB;AACzB,SAAK,qBAAqB;AAAA,EAC9B;AAAA,EAEA,MAAgB,WAAW;AACvB,UAAM,KAAK,UAAA;AAAA,EACf;AAAA,EAEA,MAAc,YAAY;AACtB,QAAI,CAAC,KAAK,OAAO,QAAQ,EAAE,KAAK,MAAM,gBAAgB,OAAO;AACzD;AAAA,IACJ;AAEA,UAAM,OAAO,KAAK,MAAM;AACxB,UAAM,MAAM,MAAM,KAAK,YAAY,EAAC,KAAK,MAAK;AAC9C,SAAK,WAAW;AAChB,SAAK,WAAW,CAAA;AAAA,EACpB;AAAA,EAUU,gBAAgB;AACtB,QAAI,CAAC,KAAK,UAAU;AAChB,aAAO;AAAA;AAAA;AAAA;AAAA;AAAA,IAKX;AAEA,UAAM,UAAU,KAAK,WAAA;AACrB,QAAI,SAAS;AACT,aAAO;AAAA;AAAA;AAAA;AAAA;AAAA,mCAKgB,KAAK,QAAQ;AAAA;AAAA,oCAEZ,KAAK,WAAW;AAAA;AAAA,0BAE1B,KAAK,SAAS,SAAS,IAAI;AAAA;AAAA,kCAEnB,KAAK,SAAS,IAAI,CAAC,MAAM,EAAE,SAAS,SAAS;AAAA;AAAA;AAAA,uDAGxB,EAAE,OAAO,GAAG,WAAW,EAAE,MAAM,GAAG,aAAa,EAAE,QAAQ,GAAG,cAAc,EAAE,SAAS,GAAG,KAAK,EAAE,QAAQ,qBAAqB,EAAE,KAAK,MAAM,EAAE;AAAA,iDACjJ,EAAE,SAAS,EAAE;AAAA;AAAA,0CAEpB,EAAE,QAAQ,mCAAmC,EAAE,KAAK,YAAY,OAAO;AAAA;AAAA,oCAE7E,EAAE,SAAS,SAAS;AAAA;AAAA;AAAA,+CAGT,EAAE,OAAO;AAAA,+CACT,EAAE,SAAS,EAAE;AAAA,iDACX,EAAE,SAAS,EAAE;AAAA;AAAA,oCAE1B,OAAO,CAAC;AAAA;AAAA,4BAEhB,OAAO;AAAA;AAAA;AAAA;AAAA,IAI3B;AAEA,WAAO;AAAA;AAAA;AAAA,2BAGY,KAAK,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA,EAKpC;AAsFJ;AAvNa,gBAmIF,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAhIT,gBAAA;AAAA,EADN,SAAS,EAAC,WAAW,MAAA,CAAM;AAAA,GAFnB,gBAGF,WAAA,SAAA,CAAA;AAGC,gBAAA;AAAA,EADP,MAAA;AAAM,GALE,gBAMD,WAAA,YAAA,CAAA;AAGA,gBAAA;AAAA,EADP,MAAA;AAAM,GARE,gBASD,WAAA,qBAAA,CAAA;AAGA,gBAAA;AAAA,EADP,MAAA;AAAM,GAXE,gBAYD,WAAA,sBAAA,CAAA;AAGA,gBAAA;AAAA,EADP,MAAA;AAAM,GAdE,gBAeD,WAAA,YAAA,CAAA;AAfC,kBAAN,gBAAA;AAAA,EADN,cAAc,mBAAmB;AAAA,GACrB,eAAA;"}
@@ -20,6 +20,7 @@ export interface MaskOverlay {
20
20
  }
21
21
  export type MediaViewerOverlay = BboxOverlay | MaskOverlay;
22
22
  export declare class LyraMediaViewer extends LyraPart {
23
+ protected scrollMode: 'scroller' | 'native';
23
24
  input?: EditorInput;
24
25
  private mediaUrl?;
25
26
  private imageNaturalWidth;
@@ -39,7 +40,7 @@ export declare class LyraMediaViewer extends LyraPart {
39
40
  protected doInitUI(): Promise<void>;
40
41
  private loadMedia;
41
42
  private onImageLoad;
42
- protected render(): import('lit-html').TemplateResult<1>;
43
+ protected renderContent(): import('lit-html').TemplateResult<1>;
43
44
  static styles: import('lit').CSSResult;
44
45
  }
45
46
  declare global {
@@ -1 +1 @@
1
- {"version":3,"file":"media-viewer-extension.d.ts","sourceRoot":"","sources":["../src/media-viewer-extension.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAwB,MAAM,oBAAoB,CAAC;AA0BjF,sFAAsF;AACtF,MAAM,WAAW,WAAW;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,yDAAyD;IACzD,KAAK,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,yFAAyF;AACzF,MAAM,WAAW,WAAW;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,8DAA8D;IAC9D,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,MAAM,kBAAkB,GAAG,WAAW,GAAG,WAAW,CAAC;AAsB3D,qBACa,eAAgB,SAAQ,QAAQ;IAElC,KAAK,CAAC,EAAE,WAAW,CAAA;IAG1B,OAAO,CAAC,QAAQ,CAAC,CAAQ;IAGzB,OAAO,CAAC,iBAAiB,CAAI;IAG7B,OAAO,CAAC,kBAAkB,CAAI;IAG9B,OAAO,CAAC,QAAQ,CAA2B;IAE3C,WAAW,IAAI,MAAM,GAAG,SAAS;IAIjC,UAAU,IAAI,OAAO;IAIrB,kBAAkB,IAAI;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE;IAIvD,4GAA4G;IAC5G,WAAW,CAAC,QAAQ,EAAE,kBAAkB,EAAE,GAAG,IAAI;IAIjD,WAAW,IAAI,kBAAkB,EAAE;IAInC,aAAa,IAAI,IAAI;IAIrB,SAAS,CAAC,OAAO;cAWD,QAAQ;YAIV,SAAS;IAWvB,OAAO,CAAC,WAAW,CAMlB;IAED,SAAS,CAAC,MAAM;IA6DhB,MAAM,CAAC,MAAM,0BAgFZ;CACJ;AAED,OAAO,CAAC,MAAM,CAAC;IACX,UAAU,qBAAqB;QAC3B,mBAAmB,EAAE,eAAe,CAAA;KACvC;CACJ"}
1
+ {"version":3,"file":"media-viewer-extension.d.ts","sourceRoot":"","sources":["../src/media-viewer-extension.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAwB,MAAM,oBAAoB,CAAC;AA0BjF,sFAAsF;AACtF,MAAM,WAAW,WAAW;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,yDAAyD;IACzD,KAAK,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,yFAAyF;AACzF,MAAM,WAAW,WAAW;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,8DAA8D;IAC9D,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,MAAM,kBAAkB,GAAG,WAAW,GAAG,WAAW,CAAC;AAqB3D,qBACa,eAAgB,SAAQ,QAAQ;IACzC,SAAS,CAAC,UAAU,EAAE,UAAU,GAAG,QAAQ,CAAY;IAEhD,KAAK,CAAC,EAAE,WAAW,CAAA;IAG1B,OAAO,CAAC,QAAQ,CAAC,CAAQ;IAGzB,OAAO,CAAC,iBAAiB,CAAI;IAG7B,OAAO,CAAC,kBAAkB,CAAI;IAG9B,OAAO,CAAC,QAAQ,CAA2B;IAE3C,WAAW,IAAI,MAAM,GAAG,SAAS;IAIjC,UAAU,IAAI,OAAO;IAIrB,kBAAkB,IAAI;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE;IAIvD,4GAA4G;IAC5G,WAAW,CAAC,QAAQ,EAAE,kBAAkB,EAAE,GAAG,IAAI;IAIjD,WAAW,IAAI,kBAAkB,EAAE;IAInC,aAAa,IAAI,IAAI;IAIrB,SAAS,CAAC,OAAO;cAWD,QAAQ;YAIV,SAAS;IAWvB,OAAO,CAAC,WAAW,CAMlB;IAED,SAAS,CAAC,aAAa;IAuDvB,MAAM,CAAC,MAAM,0BAmFZ;CACJ;AAED,OAAO,CAAC,MAAM,CAAC;IACX,UAAU,qBAAqB;QAC3B,mBAAmB,EAAE,eAAe,CAAA;KACvC;CACJ"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eclipse-lyra/extension-media-viewer",
3
- "version": "0.7.53",
3
+ "version": "0.7.54",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "exports": {
@@ -1 +0,0 @@
1
- {"version":3,"file":"media-viewer-extension-Cx-1gOxs.js","sources":["../src/media-viewer-extension.ts"],"sourcesContent":["import { customElement, property, state } from \"lit/decorators.js\";\nimport { css, html, nothing } from \"lit\";\nimport { LyraPart, EditorInput, editorRegistry, File } from \"@eclipse-lyra/core\";\n\nconst SUPPORTED_EXTENSIONS = [\n '.pdf',\n '.jpg', '.jpeg', '.png', '.gif', '.svg', '.webp', '.bmp', '.ico'\n];\n\nconst getFileIcon = (fileName: string): string => {\n const lowerName = fileName.toLowerCase();\n if (lowerName.endsWith('.pdf')) return 'file-pdf';\n if (lowerName.match(/\\.(jpg|jpeg|png|gif|webp|bmp|ico|svg)$/)) return 'image';\n return 'file';\n};\n\nconst isSupportedMediaFile = (file: File): boolean => {\n const fileName = file.getName().toLowerCase();\n return SUPPORTED_EXTENSIONS.some(ext => fileName.endsWith(ext));\n};\n\nconst IMAGE_EXTENSIONS = ['.jpg', '.jpeg', '.png', '.gif', '.svg', '.webp', '.bmp', '.ico'];\n\nconst isImageFile = (file: File): boolean => {\n const name = file.getName().toLowerCase();\n return IMAGE_EXTENSIONS.some(ext => name.endsWith(ext));\n};\n\n/** Normalized bounding box overlay (0–1). Used by extensions to draw on the image. */\nexport interface BboxOverlay {\n type: 'bbox';\n left: number;\n top: number;\n width: number;\n height: number;\n label?: string;\n /** CSS color for border and label (e.g. by category). */\n color?: string;\n}\n\n/** Segment mask overlay: PNG data URL (alpha where segment is), drawn over the image. */\nexport interface MaskOverlay {\n type: 'mask';\n /** Data URL of the mask image (e.g. image/png with alpha). */\n dataUrl: string;\n label?: string;\n color?: string;\n}\n\nexport type MediaViewerOverlay = BboxOverlay | MaskOverlay;\n\neditorRegistry.registerEditorInputHandler({\n editorId: \"system.media-viewer\",\n label: \"Media viewer\",\n canHandle: input => input instanceof File && isSupportedMediaFile(input),\n handle: async (input: File) => {\n const editorInput = {\n title: input.getWorkspacePath(),\n data: input,\n key: input.getWorkspacePath(),\n icon: getFileIcon(input.getName()),\n noOverflow: false,\n state: {},\n } as EditorInput\n editorInput.component = () => html`\n <lyra-media-viewer .input=${editorInput}></lyra-media-viewer>`\n return editorInput;\n },\n ranking: 1000\n})\n\n@customElement('lyra-media-viewer')\nexport class LyraMediaViewer extends LyraPart {\n @property({attribute: false})\n public input?: EditorInput\n\n @state()\n private mediaUrl?: string\n\n @state()\n private imageNaturalWidth = 0\n\n @state()\n private imageNaturalHeight = 0\n\n @state()\n private overlays: MediaViewerOverlay[] = []\n\n getMediaUrl(): string | undefined {\n return this.mediaUrl;\n }\n\n getIsImage(): boolean {\n return !!(this.input?.data instanceof File && this.mediaUrl && isImageFile(this.input.data));\n }\n\n getImageDimensions(): { width: number; height: number } {\n return { width: this.imageNaturalWidth, height: this.imageNaturalHeight };\n }\n\n /** Set overlays drawn on the image (e.g. from object detection or other extensions). Coordinates in 0–1. */\n setOverlays(overlays: MediaViewerOverlay[]): void {\n this.overlays = overlays ?? [];\n }\n\n getOverlays(): MediaViewerOverlay[] {\n return [...this.overlays];\n }\n\n clearOverlays(): void {\n this.overlays = [];\n }\n\n protected doClose() {\n if (this.mediaUrl && this.mediaUrl.startsWith('blob:')) {\n URL.revokeObjectURL(this.mediaUrl)\n }\n this.input = undefined\n this.mediaUrl = undefined\n this.overlays = []\n this.imageNaturalWidth = 0\n this.imageNaturalHeight = 0\n }\n\n protected async doInitUI() {\n await this.loadMedia()\n }\n\n private async loadMedia() {\n if (!this.input?.data || !(this.input.data instanceof File)) {\n return\n }\n\n const file = this.input.data\n const url = await file.getContents({uri: true}) as string\n this.mediaUrl = url\n this.overlays = []\n }\n\n private onImageLoad = (e: Event): void => {\n const img = e.target as HTMLImageElement;\n if (img?.naturalWidth && img?.naturalHeight) {\n this.imageNaturalWidth = img.naturalWidth;\n this.imageNaturalHeight = img.naturalHeight;\n }\n }\n\n protected render() {\n if (!this.mediaUrl) {\n return html`\n <div style=\"display: flex; align-items: center; justify-content: center; height: 100%;\">\n <wa-spinner></wa-spinner>\n </div>\n `\n }\n\n const isImage = this.getIsImage();\n if (isImage) {\n const w = this.imageNaturalWidth || 1;\n const h = this.imageNaturalHeight || 1;\n const aspectRatio = w / h;\n return html`\n <div class=\"image-container\">\n <div\n class=\"image-wrapper\"\n style=\"aspect-ratio: ${aspectRatio};\"\n >\n <img\n class=\"media-image\"\n src=\"${this.mediaUrl}\"\n alt=\"\"\n @load=${this.onImageLoad}\n />\n ${this.overlays.length > 0 ? html`\n <div class=\"overlay-layer\">\n ${this.overlays.map((o) => o.type === 'bbox' ? html`\n <div\n class=\"overlay-bbox\"\n style=\"left: ${o.left * 100}%; top: ${o.top * 100}%; width: ${o.width * 100}%; height: ${o.height * 100}%;${o.color ? ` --overlay-color: ${o.color};` : ''}\"\n title=\"${o.label ?? ''}\"\n >\n ${o.label ? html`<span class=\"overlay-label\">${o.label}</span>` : nothing}\n </div>\n ` : o.type === 'mask' ? html`\n <img\n class=\"overlay-mask\"\n src=\"${o.dataUrl}\"\n alt=\"${o.label ?? ''}\"\n title=\"${o.label ?? ''}\"\n />\n ` : nothing)}\n </div>\n ` : nothing}\n </div>\n </div>\n `;\n }\n\n return html`\n <div class=\"media-iframe-container\">\n <iframe\n src=\"${this.mediaUrl}\"\n class=\"media-iframe\"\n title=\"Media Viewer\"></iframe>\n </div>\n `\n }\n\n static styles = css`\n :host {\n display: flex;\n flex-direction: column;\n position: relative;\n width: 100%;\n height: 100%;\n background: var(--wa-color-surface-default, #1a1a1a);\n }\n\n .image-container {\n position: relative;\n width: 100%;\n height: 100%;\n display: flex;\n align-items: center;\n justify-content: center;\n overflow: hidden;\n }\n\n .image-wrapper {\n position: relative;\n max-width: 100%;\n max-height: 100%;\n }\n\n .media-iframe-container {\n position: absolute;\n inset: 0;\n min-height: 0;\n }\n\n .media-iframe {\n display: block;\n width: 100%;\n height: 100%;\n border: 0;\n }\n\n .media-image {\n display: block;\n width: 100%;\n height: 100%;\n object-fit: contain;\n }\n\n .overlay-layer {\n position: absolute;\n inset: 0;\n pointer-events: none;\n }\n\n .overlay-bbox {\n position: absolute;\n border: 2px solid var(--overlay-color, var(--wa-color-brand-fill, #0d6efd));\n background: color-mix(in srgb, var(--overlay-color, #0d6efd) 15%, transparent);\n pointer-events: auto;\n box-sizing: border-box;\n }\n\n .overlay-label {\n position: absolute;\n left: 0;\n top: -1.25em;\n font-size: 0.7rem;\n white-space: nowrap;\n background: var(--overlay-color, var(--wa-color-brand-fill, #0d6efd));\n color: var(--wa-color-surface-default, #fff);\n padding: 1px 4px;\n border-radius: 2px;\n }\n\n .overlay-mask {\n position: absolute;\n inset: 0;\n width: 100%;\n height: 100%;\n object-fit: fill;\n pointer-events: none;\n }\n `\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'lyra-media-viewer': LyraMediaViewer\n }\n}\n\n"],"names":[],"mappings":"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"}