@jax-data-science/sci-image-visualizer 0.2.16 → 0.2.17

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/esm2022/index.mjs CHANGED
@@ -26,6 +26,9 @@ export { CellposeSegmenterService } from './lib/toolbar/segmentation/cellpose-se
26
26
  export * from './lib/contracts/instance-segmenter.contract';
27
27
  export { YoloSegmenterService } from './lib/toolbar/segmentation/yolo-segmenter.service';
28
28
  export { YoloDetectToolService, YOLO_REGION_SOURCE } from './lib/toolbar/segmentation/yolo-detect-tool.service';
29
+ export * from './lib/contracts/semantic-segmenter.contract';
30
+ export { RetinalSegmenterService } from './lib/toolbar/segmentation/retinal-segmenter.service';
31
+ export { RetinalLayerToolService, RETINAL_REGION_SOURCE, } from './lib/toolbar/segmentation/retinal-layer-tool.service';
29
32
  // ── Generic client-side image utilities (shared with the host's pipeline) ──
30
33
  export { ProcessingImage } from './lib/processing/processing-image';
31
34
  export { cropImage } from './lib/processing/image-crop';
@@ -34,6 +37,8 @@ export { ImageConverterService } from './lib/processing/image-converter.service'
34
37
  export { SAM_MODELS, DEFAULT_SAM_MODEL_ID, getSamModel, isSamModelReady, setSamModelUrls, setDefaultSamModel, getDefaultSamModelId, } from './lib/toolbar/segmentation/sam-model-registry';
35
38
  // ── YOLO instance-segmentation model registry (same repointing story) ─────
36
39
  export { YOLO_MODELS, DEFAULT_YOLO_MODEL_ID, getYoloModel, isYoloModelReady, setYoloModelUrls, setDefaultYoloModel, getDefaultYoloModelId, } from './lib/toolbar/segmentation/yolo-model-registry';
40
+ // ── Retinal-layer semantic-segmentation registry (same repointing story) ──
41
+ export { RETINAL_MODELS, DEFAULT_RETINAL_MODEL_ID, getRetinalModel, isRetinalModelReady, readyRetinalModels, setRetinalModelUrls, setDefaultRetinalModel, getDefaultRetinalModelId, } from './lib/toolbar/segmentation/retinal-model-registry';
37
42
  // ── onnxruntime-web WASM location (host overrides once, at app init) ───────
38
43
  export { setOrtWasmBase, getOrtWasmBase } from './lib/toolbar/segmentation/ort-runtime-config';
39
44
  // ── Neutral data models ──────────────────────────────────────────────────
@@ -54,4 +59,4 @@ export { VisualizerComponent } from './lib/visualizer.component';
54
59
  export { RegionEditorComponent } from './lib/region-editor/region-editor.component';
55
60
  export { HexColorPickerComponent } from './lib/hex-color-picker/hex-color-picker.component';
56
61
  export { ChannelHistogramComponent } from './lib/channel-histogram/channel-histogram.component';
57
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7OztHQU1HO0FBRUgsNEVBQTRFO0FBQzVFLGNBQWMscUNBQXFDLENBQUM7QUFDcEQsY0FBYyw0Q0FBNEMsQ0FBQztBQUMzRCxjQUFjLHdDQUF3QyxDQUFDO0FBQ3ZELGNBQWMsd0NBQXdDLENBQUM7QUFDdkQsY0FBYyxzQ0FBc0MsQ0FBQztBQUNyRCxjQUFjLHdDQUF3QyxDQUFDO0FBQ3ZELGNBQWMsZ0RBQWdELENBQUM7QUFDL0QsY0FBYyw0QkFBNEIsQ0FBQztBQUMzQyxjQUFjLGdDQUFnQyxDQUFDO0FBQy9DLGNBQWMsMkJBQTJCLENBQUM7QUFDMUMsY0FBYyxnQ0FBZ0MsQ0FBQztBQUMvQyxjQUFjLHVDQUF1QyxDQUFDO0FBQ3RELGNBQWMseUNBQXlDLENBQUM7QUFDeEQsY0FBYywrQkFBK0IsQ0FBQztBQUM5QyxjQUFjLDhCQUE4QixDQUFDO0FBQzdDLGNBQWMseUNBQXlDLENBQUM7QUFDeEQsT0FBTyxFQUFFLHdCQUF3QixFQUFFLE1BQU0sdURBQXVELENBQUM7QUFDakcsY0FBYyw2Q0FBNkMsQ0FBQztBQUM1RCxPQUFPLEVBQUUsb0JBQW9CLEVBQUUsTUFBTSxtREFBbUQsQ0FBQztBQUN6RixPQUFPLEVBQUUscUJBQXFCLEVBQUUsa0JBQWtCLEVBQUUsTUFBTSxxREFBcUQsQ0FBQztBQUVoSCw4RUFBOEU7QUFDOUUsT0FBTyxFQUFFLGVBQWUsRUFBRSxNQUFNLG1DQUFtQyxDQUFDO0FBQ3BFLE9BQU8sRUFBRSxTQUFTLEVBQW9CLE1BQU0sNkJBQTZCLENBQUM7QUFDMUUsT0FBTyxFQUFFLHFCQUFxQixFQUFFLE1BQU0sMENBQTBDLENBQUM7QUFFakYsNkVBQTZFO0FBQzdFLE9BQU8sRUFDTCxVQUFVLEVBQUUsb0JBQW9CLEVBQUUsV0FBVyxFQUFFLGVBQWUsRUFBRSxlQUFlLEVBQy9FLGtCQUFrQixFQUFFLG9CQUFvQixHQUN6QyxNQUFNLCtDQUErQyxDQUFDO0FBRXZELDZFQUE2RTtBQUM3RSxPQUFPLEVBQ0wsV0FBVyxFQUFFLHFCQUFxQixFQUFFLFlBQVksRUFBRSxnQkFBZ0IsRUFBRSxnQkFBZ0IsRUFDcEYsbUJBQW1CLEVBQUUscUJBQXFCLEdBQzNDLE1BQU0sZ0RBQWdELENBQUM7QUFFeEQsOEVBQThFO0FBQzlFLE9BQU8sRUFBRSxjQUFjLEVBQUUsY0FBYyxFQUFFLE1BQU0sK0NBQStDLENBQUM7QUFFL0YsNEVBQTRFO0FBQzVFLGNBQWMscUJBQXFCLENBQUM7QUFDcEMsY0FBYywyQkFBMkIsQ0FBQztBQUMxQyxPQUFPLEVBQUUsY0FBYyxFQUFFLE1BQU0sb0JBQW9CLENBQUM7QUFFcEQsNEVBQTRFO0FBQzVFLE9BQU8sRUFBRSxtQkFBbUIsRUFBRSxNQUFNLDRCQUE0QixDQUFDO0FBQ2pFLE9BQU8sRUFBRSx3QkFBd0IsRUFBRSxNQUFNLGtDQUFrQyxDQUFDO0FBQzVFLDZFQUE2RTtBQUM3RSxtRUFBbUU7QUFDbkUsT0FBTyxFQUFFLG9CQUFvQixFQUFFLE1BQU0sNkJBQTZCLENBQUM7QUFFbkUsNkVBQTZFO0FBQzdFLDBFQUEwRTtBQUMxRSw4RUFBOEU7QUFDOUUsNkRBQTZEO0FBQzdELE9BQU8sRUFBRSxtQkFBbUIsRUFBRSxNQUFNLDRCQUE0QixDQUFDO0FBQ2pFLE9BQU8sRUFBRSxxQkFBcUIsRUFBRSxNQUFNLDZDQUE2QyxDQUFDO0FBQ3BGLE9BQU8sRUFBRSx1QkFBdUIsRUFBRSxNQUFNLG1EQUFtRCxDQUFDO0FBQzVGLE9BQU8sRUFBRSx5QkFBeUIsRUFBRSxNQUFNLHFEQUFxRCxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBQdWJsaWMgQVBJIG9mIHRoZSBqYXgtaW1hZ2UtdmlzdWFsaXphdGlvbiBsaWJyYXJ5LiBFeHRlcm5hbCBjb25zdW1lcnMgaW1wb3J0XG4gKiBPTkxZIGZyb20gaGVyZSAoYEBqYXgtZGF0YS1zY2llbmNlL3NjaS1pbWFnZS12aXN1YWxpemVyYCk7IGV2ZXJ5dGhpbmcgZWxzZSB1bmRlciBgbGliL2AgaXNcbiAqIGludGVybmFsLiBUaGUgc3VyZmFjZSBpcyBjb250cmFjdHMgKyB0b2tlbnMgKyBuZXV0cmFsIG1vZGVscyArIHRoZSBBbmd1bGFyXG4gKiBtb2R1bGUsIHBsdXMgdGhlIHJvdXRpbmcgc2VydmljZSB0aGUgaG9zdCdzIGNvbXBvc2l0aW9uIHJvb3QgYmluZHMgdGhlIHRva2Vuc1xuICogdG8gKGB1c2VFeGlzdGluZ2ApLlxuICovXG5cbi8vIOKUgOKUgCBDb250cmFjdHMgJiBESSB0b2tlbnMg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSAXG5leHBvcnQgKiBmcm9tICcuL2xpYi9jb250cmFjdHMvdmlzdWFsaXplci5jb250cmFjdCc7XG5leHBvcnQgKiBmcm9tICcuL2xpYi9jb250cmFjdHMvcmVnaW9uLWVkaXRvci1hcGkuY29udHJhY3QnO1xuZXhwb3J0ICogZnJvbSAnLi9saWIvY29udHJhY3RzL3BvcnRzL2ltYWdlLXN0YXRlLnBvcnQnO1xuZXhwb3J0ICogZnJvbSAnLi9saWIvY29udHJhY3RzL3BvcnRzL3RpbGUtYWNjZXNzLnBvcnQnO1xuZXhwb3J0ICogZnJvbSAnLi9saWIvY29udHJhY3RzL3BvcnRzL3JlZ2lvbi1pby5wb3J0JztcbmV4cG9ydCAqIGZyb20gJy4vbGliL2NvbnRyYWN0cy9wb3J0cy9wcmVmZXJlbmNlcy5wb3J0JztcbmV4cG9ydCAqIGZyb20gJy4vbGliL2NvbnRyYWN0cy9jaGFubmVsLWhpc3RvZ3JhbS1hcGkuY29udHJhY3QnO1xuZXhwb3J0ICogZnJvbSAnLi9saWIvY29udHJhY3RzL3Zpei1jb25maWcnO1xuZXhwb3J0ICogZnJvbSAnLi9saWIvY29udHJhY3RzL2ltYWdlLmNvbnRyYWN0JztcbmV4cG9ydCAqIGZyb20gJy4vbGliL2NvbnRyYWN0cy9wbG90LXR5cGUnO1xuZXhwb3J0ICogZnJvbSAnLi9saWIvY29udHJhY3RzL3Rvb2xiYXItY29uZmlnJztcbmV4cG9ydCAqIGZyb20gJy4vbGliL2NvbnRyYWN0cy9jYXBhYmlsaXRpZXMuY29udHJhY3QnO1xuZXhwb3J0ICogZnJvbSAnLi9saWIvY29udHJhY3RzL3JlZ2lvbi1vdmVybGF5LmNvbnRyYWN0JztcbmV4cG9ydCAqIGZyb20gJy4vbGliL2NvbnRyYWN0cy9kaXNwbGF5LXR5cGVzJztcbmV4cG9ydCAqIGZyb20gJy4vbGliL2NvbnRyYWN0cy9zYW0uY29udHJhY3QnO1xuZXhwb3J0ICogZnJvbSAnLi9saWIvY29udHJhY3RzL2NlbGwtc2VnbWVudGVyLmNvbnRyYWN0JztcbmV4cG9ydCB7IENlbGxwb3NlU2VnbWVudGVyU2VydmljZSB9IGZyb20gJy4vbGliL3Rvb2xiYXIvc2VnbWVudGF0aW9uL2NlbGxwb3NlLXNlZ21lbnRlci5zZXJ2aWNlJztcbmV4cG9ydCAqIGZyb20gJy4vbGliL2NvbnRyYWN0cy9pbnN0YW5jZS1zZWdtZW50ZXIuY29udHJhY3QnO1xuZXhwb3J0IHsgWW9sb1NlZ21lbnRlclNlcnZpY2UgfSBmcm9tICcuL2xpYi90b29sYmFyL3NlZ21lbnRhdGlvbi95b2xvLXNlZ21lbnRlci5zZXJ2aWNlJztcbmV4cG9ydCB7IFlvbG9EZXRlY3RUb29sU2VydmljZSwgWU9MT19SRUdJT05fU09VUkNFIH0gZnJvbSAnLi9saWIvdG9vbGJhci9zZWdtZW50YXRpb24veW9sby1kZXRlY3QtdG9vbC5zZXJ2aWNlJztcblxuLy8g4pSA4pSAIEdlbmVyaWMgY2xpZW50LXNpZGUgaW1hZ2UgdXRpbGl0aWVzIChzaGFyZWQgd2l0aCB0aGUgaG9zdCdzIHBpcGVsaW5lKSDilIDilIBcbmV4cG9ydCB7IFByb2Nlc3NpbmdJbWFnZSB9IGZyb20gJy4vbGliL3Byb2Nlc3NpbmcvcHJvY2Vzc2luZy1pbWFnZSc7XG5leHBvcnQgeyBjcm9wSW1hZ2UsIENyb3BJbWFnZU9wdGlvbnMgfSBmcm9tICcuL2xpYi9wcm9jZXNzaW5nL2ltYWdlLWNyb3AnO1xuZXhwb3J0IHsgSW1hZ2VDb252ZXJ0ZXJTZXJ2aWNlIH0gZnJvbSAnLi9saWIvcHJvY2Vzc2luZy9pbWFnZS1jb252ZXJ0ZXIuc2VydmljZSc7XG5cbi8vIOKUgOKUgCBTQU0gbW9kZWwgcmVnaXN0cnkgKGhvc3QgY29uZmlndXJlcyBob3N0ZWQgT05OWCBVUkxzIG9uY2UgYXZhaWxhYmxlKSDilIDilIBcbmV4cG9ydCB7XG4gIFNBTV9NT0RFTFMsIERFRkFVTFRfU0FNX01PREVMX0lELCBnZXRTYW1Nb2RlbCwgaXNTYW1Nb2RlbFJlYWR5LCBzZXRTYW1Nb2RlbFVybHMsXG4gIHNldERlZmF1bHRTYW1Nb2RlbCwgZ2V0RGVmYXVsdFNhbU1vZGVsSWQsXG59IGZyb20gJy4vbGliL3Rvb2xiYXIvc2VnbWVudGF0aW9uL3NhbS1tb2RlbC1yZWdpc3RyeSc7XG5cbi8vIOKUgOKUgCBZT0xPIGluc3RhbmNlLXNlZ21lbnRhdGlvbiBtb2RlbCByZWdpc3RyeSAoc2FtZSByZXBvaW50aW5nIHN0b3J5KSDilIDilIDilIDilIDilIBcbmV4cG9ydCB7XG4gIFlPTE9fTU9ERUxTLCBERUZBVUxUX1lPTE9fTU9ERUxfSUQsIGdldFlvbG9Nb2RlbCwgaXNZb2xvTW9kZWxSZWFkeSwgc2V0WW9sb01vZGVsVXJscyxcbiAgc2V0RGVmYXVsdFlvbG9Nb2RlbCwgZ2V0RGVmYXVsdFlvbG9Nb2RlbElkLCBZb2xvTW9kZWxEZWYsXG59IGZyb20gJy4vbGliL3Rvb2xiYXIvc2VnbWVudGF0aW9uL3lvbG8tbW9kZWwtcmVnaXN0cnknO1xuXG4vLyDilIDilIAgb25ueHJ1bnRpbWUtd2ViIFdBU00gbG9jYXRpb24gKGhvc3Qgb3ZlcnJpZGVzIG9uY2UsIGF0IGFwcCBpbml0KSDilIDilIDilIDilIDilIDilIDilIBcbmV4cG9ydCB7IHNldE9ydFdhc21CYXNlLCBnZXRPcnRXYXNtQmFzZSB9IGZyb20gJy4vbGliL3Rvb2xiYXIvc2VnbWVudGF0aW9uL29ydC1ydW50aW1lLWNvbmZpZyc7XG5cbi8vIOKUgOKUgCBOZXV0cmFsIGRhdGEgbW9kZWxzIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgFxuZXhwb3J0ICogZnJvbSAnLi9saWIvbW9kZWxzL3JlZ2lvbic7XG5leHBvcnQgKiBmcm9tICcuL2xpYi9tb2RlbHMvY2xhc3MtcHJlc2V0JztcbmV4cG9ydCB7IFNoYXBlU2VsZWN0aW9uIH0gZnJvbSAnLi9saWIvbW9kZWxzL3NoYXBlJztcblxuLy8g4pSA4pSAIEFuZ3VsYXIgbW9kdWxlICsgY29tcG9zaXRpb24tcm9vdCBzZXJ2aWNlIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgFxuZXhwb3J0IHsgVmlzdWFsaXphdGlvbk1vZHVsZSB9IGZyb20gJy4vbGliL3Zpc3VhbGl6YXRpb24ubW9kdWxlJztcbmV4cG9ydCB7IFJvdXRpbmdWaXN1YWxpemVyU2VydmljZSB9IGZyb20gJy4vbGliL3JvdXRpbmctdmlzdWFsaXplci5zZXJ2aWNlJztcbi8vIFByb3ZpZGVyIGZhY3RvcnkgZm9yIGFuIGlzb2xhdGVkLCBjb21wb25lbnQtc2NvcGVkIHZpZXdlciBpbnN0YW5jZSAoZS5nLiBhXG4vLyBtb2RhbCB0aGF0IG11c3Qgbm90IHNoYXJlIHRoZSBtYWluIHZpZXdlcidzIHJlZ2lvbi9pbWFnZSBzdGF0ZSkuXG5leHBvcnQgeyBwcm92aWRlVmlzdWFsaXphdGlvbiB9IGZyb20gJy4vbGliL3Byb3ZpZGUtdmlzdWFsaXphdGlvbic7XG5cbi8vIOKUgOKUgCBQdWJsaWMgY29tcG9uZW50cyAoZXhwb3J0ZWQgYnkgVmlzdWFsaXphdGlvbk1vZHVsZSkg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSAXG4vLyBuZy1wYWNrYWdyIHJlcXVpcmVzIG1vZHVsZS1leHBvcnRlZCBjb21wb25lbnRzIHRvIGJlIHJlYWNoYWJsZSBmcm9tIHRoZVxuLy8gZW50cnkgcG9pbnQgc28gY29uc3VtZXJzIGdldCB0aGVpciB0eXBlczsgdGhlc2UgYXJlIHRoZSBlbWJlZGRhYmxlIGVsZW1lbnRzXG4vLyAoYDx2aXN1YWxpemVyPmAsIGA8cmVnaW9uLWVkaXRvcj5gLCBgPGhleC1jb2xvci1waWNrZXI+YCkuXG5leHBvcnQgeyBWaXN1YWxpemVyQ29tcG9uZW50IH0gZnJvbSAnLi9saWIvdmlzdWFsaXplci5jb21wb25lbnQnO1xuZXhwb3J0IHsgUmVnaW9uRWRpdG9yQ29tcG9uZW50IH0gZnJvbSAnLi9saWIvcmVnaW9uLWVkaXRvci9yZWdpb24tZWRpdG9yLmNvbXBvbmVudCc7XG5leHBvcnQgeyBIZXhDb2xvclBpY2tlckNvbXBvbmVudCB9IGZyb20gJy4vbGliL2hleC1jb2xvci1waWNrZXIvaGV4LWNvbG9yLXBpY2tlci5jb21wb25lbnQnO1xuZXhwb3J0IHsgQ2hhbm5lbEhpc3RvZ3JhbUNvbXBvbmVudCB9IGZyb20gJy4vbGliL2NoYW5uZWwtaGlzdG9ncmFtL2NoYW5uZWwtaGlzdG9ncmFtLmNvbXBvbmVudCc7XG4iXX0=
62
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7OztHQU1HO0FBRUgsNEVBQTRFO0FBQzVFLGNBQWMscUNBQXFDLENBQUM7QUFDcEQsY0FBYyw0Q0FBNEMsQ0FBQztBQUMzRCxjQUFjLHdDQUF3QyxDQUFDO0FBQ3ZELGNBQWMsd0NBQXdDLENBQUM7QUFDdkQsY0FBYyxzQ0FBc0MsQ0FBQztBQUNyRCxjQUFjLHdDQUF3QyxDQUFDO0FBQ3ZELGNBQWMsZ0RBQWdELENBQUM7QUFDL0QsY0FBYyw0QkFBNEIsQ0FBQztBQUMzQyxjQUFjLGdDQUFnQyxDQUFDO0FBQy9DLGNBQWMsMkJBQTJCLENBQUM7QUFDMUMsY0FBYyxnQ0FBZ0MsQ0FBQztBQUMvQyxjQUFjLHVDQUF1QyxDQUFDO0FBQ3RELGNBQWMseUNBQXlDLENBQUM7QUFDeEQsY0FBYywrQkFBK0IsQ0FBQztBQUM5QyxjQUFjLDhCQUE4QixDQUFDO0FBQzdDLGNBQWMseUNBQXlDLENBQUM7QUFDeEQsT0FBTyxFQUFFLHdCQUF3QixFQUFFLE1BQU0sdURBQXVELENBQUM7QUFDakcsY0FBYyw2Q0FBNkMsQ0FBQztBQUM1RCxPQUFPLEVBQUUsb0JBQW9CLEVBQUUsTUFBTSxtREFBbUQsQ0FBQztBQUN6RixPQUFPLEVBQUUscUJBQXFCLEVBQUUsa0JBQWtCLEVBQUUsTUFBTSxxREFBcUQsQ0FBQztBQUNoSCxjQUFjLDZDQUE2QyxDQUFDO0FBQzVELE9BQU8sRUFBRSx1QkFBdUIsRUFBRSxNQUFNLHNEQUFzRCxDQUFDO0FBQy9GLE9BQU8sRUFDTCx1QkFBdUIsRUFDdkIscUJBQXFCLEdBQ3RCLE1BQU0sdURBQXVELENBQUM7QUFFL0QsOEVBQThFO0FBQzlFLE9BQU8sRUFBRSxlQUFlLEVBQUUsTUFBTSxtQ0FBbUMsQ0FBQztBQUNwRSxPQUFPLEVBQUUsU0FBUyxFQUFvQixNQUFNLDZCQUE2QixDQUFDO0FBQzFFLE9BQU8sRUFBRSxxQkFBcUIsRUFBRSxNQUFNLDBDQUEwQyxDQUFDO0FBRWpGLDZFQUE2RTtBQUM3RSxPQUFPLEVBQ0wsVUFBVSxFQUFFLG9CQUFvQixFQUFFLFdBQVcsRUFBRSxlQUFlLEVBQUUsZUFBZSxFQUMvRSxrQkFBa0IsRUFBRSxvQkFBb0IsR0FDekMsTUFBTSwrQ0FBK0MsQ0FBQztBQUV2RCw2RUFBNkU7QUFDN0UsT0FBTyxFQUNMLFdBQVcsRUFBRSxxQkFBcUIsRUFBRSxZQUFZLEVBQUUsZ0JBQWdCLEVBQUUsZ0JBQWdCLEVBQ3BGLG1CQUFtQixFQUFFLHFCQUFxQixHQUMzQyxNQUFNLGdEQUFnRCxDQUFDO0FBRXhELDZFQUE2RTtBQUM3RSxPQUFPLEVBQ0wsY0FBYyxFQUFFLHdCQUF3QixFQUFFLGVBQWUsRUFBRSxtQkFBbUIsRUFDOUUsa0JBQWtCLEVBQUUsbUJBQW1CLEVBQUUsc0JBQXNCLEVBQUUsd0JBQXdCLEdBRTFGLE1BQU0sbURBQW1ELENBQUM7QUFFM0QsOEVBQThFO0FBQzlFLE9BQU8sRUFBRSxjQUFjLEVBQUUsY0FBYyxFQUFFLE1BQU0sK0NBQStDLENBQUM7QUFFL0YsNEVBQTRFO0FBQzVFLGNBQWMscUJBQXFCLENBQUM7QUFDcEMsY0FBYywyQkFBMkIsQ0FBQztBQUMxQyxPQUFPLEVBQUUsY0FBYyxFQUFFLE1BQU0sb0JBQW9CLENBQUM7QUFFcEQsNEVBQTRFO0FBQzVFLE9BQU8sRUFBRSxtQkFBbUIsRUFBRSxNQUFNLDRCQUE0QixDQUFDO0FBQ2pFLE9BQU8sRUFBRSx3QkFBd0IsRUFBRSxNQUFNLGtDQUFrQyxDQUFDO0FBQzVFLDZFQUE2RTtBQUM3RSxtRUFBbUU7QUFDbkUsT0FBTyxFQUFFLG9CQUFvQixFQUFFLE1BQU0sNkJBQTZCLENBQUM7QUFFbkUsNkVBQTZFO0FBQzdFLDBFQUEwRTtBQUMxRSw4RUFBOEU7QUFDOUUsNkRBQTZEO0FBQzdELE9BQU8sRUFBRSxtQkFBbUIsRUFBRSxNQUFNLDRCQUE0QixDQUFDO0FBQ2pFLE9BQU8sRUFBRSxxQkFBcUIsRUFBRSxNQUFNLDZDQUE2QyxDQUFDO0FBQ3BGLE9BQU8sRUFBRSx1QkFBdUIsRUFBRSxNQUFNLG1EQUFtRCxDQUFDO0FBQzVGLE9BQU8sRUFBRSx5QkFBeUIsRUFBRSxNQUFNLHFEQUFxRCxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBQdWJsaWMgQVBJIG9mIHRoZSBqYXgtaW1hZ2UtdmlzdWFsaXphdGlvbiBsaWJyYXJ5LiBFeHRlcm5hbCBjb25zdW1lcnMgaW1wb3J0XG4gKiBPTkxZIGZyb20gaGVyZSAoYEBqYXgtZGF0YS1zY2llbmNlL3NjaS1pbWFnZS12aXN1YWxpemVyYCk7IGV2ZXJ5dGhpbmcgZWxzZSB1bmRlciBgbGliL2AgaXNcbiAqIGludGVybmFsLiBUaGUgc3VyZmFjZSBpcyBjb250cmFjdHMgKyB0b2tlbnMgKyBuZXV0cmFsIG1vZGVscyArIHRoZSBBbmd1bGFyXG4gKiBtb2R1bGUsIHBsdXMgdGhlIHJvdXRpbmcgc2VydmljZSB0aGUgaG9zdCdzIGNvbXBvc2l0aW9uIHJvb3QgYmluZHMgdGhlIHRva2Vuc1xuICogdG8gKGB1c2VFeGlzdGluZ2ApLlxuICovXG5cbi8vIOKUgOKUgCBDb250cmFjdHMgJiBESSB0b2tlbnMg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSAXG5leHBvcnQgKiBmcm9tICcuL2xpYi9jb250cmFjdHMvdmlzdWFsaXplci5jb250cmFjdCc7XG5leHBvcnQgKiBmcm9tICcuL2xpYi9jb250cmFjdHMvcmVnaW9uLWVkaXRvci1hcGkuY29udHJhY3QnO1xuZXhwb3J0ICogZnJvbSAnLi9saWIvY29udHJhY3RzL3BvcnRzL2ltYWdlLXN0YXRlLnBvcnQnO1xuZXhwb3J0ICogZnJvbSAnLi9saWIvY29udHJhY3RzL3BvcnRzL3RpbGUtYWNjZXNzLnBvcnQnO1xuZXhwb3J0ICogZnJvbSAnLi9saWIvY29udHJhY3RzL3BvcnRzL3JlZ2lvbi1pby5wb3J0JztcbmV4cG9ydCAqIGZyb20gJy4vbGliL2NvbnRyYWN0cy9wb3J0cy9wcmVmZXJlbmNlcy5wb3J0JztcbmV4cG9ydCAqIGZyb20gJy4vbGliL2NvbnRyYWN0cy9jaGFubmVsLWhpc3RvZ3JhbS1hcGkuY29udHJhY3QnO1xuZXhwb3J0ICogZnJvbSAnLi9saWIvY29udHJhY3RzL3Zpei1jb25maWcnO1xuZXhwb3J0ICogZnJvbSAnLi9saWIvY29udHJhY3RzL2ltYWdlLmNvbnRyYWN0JztcbmV4cG9ydCAqIGZyb20gJy4vbGliL2NvbnRyYWN0cy9wbG90LXR5cGUnO1xuZXhwb3J0ICogZnJvbSAnLi9saWIvY29udHJhY3RzL3Rvb2xiYXItY29uZmlnJztcbmV4cG9ydCAqIGZyb20gJy4vbGliL2NvbnRyYWN0cy9jYXBhYmlsaXRpZXMuY29udHJhY3QnO1xuZXhwb3J0ICogZnJvbSAnLi9saWIvY29udHJhY3RzL3JlZ2lvbi1vdmVybGF5LmNvbnRyYWN0JztcbmV4cG9ydCAqIGZyb20gJy4vbGliL2NvbnRyYWN0cy9kaXNwbGF5LXR5cGVzJztcbmV4cG9ydCAqIGZyb20gJy4vbGliL2NvbnRyYWN0cy9zYW0uY29udHJhY3QnO1xuZXhwb3J0ICogZnJvbSAnLi9saWIvY29udHJhY3RzL2NlbGwtc2VnbWVudGVyLmNvbnRyYWN0JztcbmV4cG9ydCB7IENlbGxwb3NlU2VnbWVudGVyU2VydmljZSB9IGZyb20gJy4vbGliL3Rvb2xiYXIvc2VnbWVudGF0aW9uL2NlbGxwb3NlLXNlZ21lbnRlci5zZXJ2aWNlJztcbmV4cG9ydCAqIGZyb20gJy4vbGliL2NvbnRyYWN0cy9pbnN0YW5jZS1zZWdtZW50ZXIuY29udHJhY3QnO1xuZXhwb3J0IHsgWW9sb1NlZ21lbnRlclNlcnZpY2UgfSBmcm9tICcuL2xpYi90b29sYmFyL3NlZ21lbnRhdGlvbi95b2xvLXNlZ21lbnRlci5zZXJ2aWNlJztcbmV4cG9ydCB7IFlvbG9EZXRlY3RUb29sU2VydmljZSwgWU9MT19SRUdJT05fU09VUkNFIH0gZnJvbSAnLi9saWIvdG9vbGJhci9zZWdtZW50YXRpb24veW9sby1kZXRlY3QtdG9vbC5zZXJ2aWNlJztcbmV4cG9ydCAqIGZyb20gJy4vbGliL2NvbnRyYWN0cy9zZW1hbnRpYy1zZWdtZW50ZXIuY29udHJhY3QnO1xuZXhwb3J0IHsgUmV0aW5hbFNlZ21lbnRlclNlcnZpY2UgfSBmcm9tICcuL2xpYi90b29sYmFyL3NlZ21lbnRhdGlvbi9yZXRpbmFsLXNlZ21lbnRlci5zZXJ2aWNlJztcbmV4cG9ydCB7XG4gIFJldGluYWxMYXllclRvb2xTZXJ2aWNlLFxuICBSRVRJTkFMX1JFR0lPTl9TT1VSQ0UsXG59IGZyb20gJy4vbGliL3Rvb2xiYXIvc2VnbWVudGF0aW9uL3JldGluYWwtbGF5ZXItdG9vbC5zZXJ2aWNlJztcblxuLy8g4pSA4pSAIEdlbmVyaWMgY2xpZW50LXNpZGUgaW1hZ2UgdXRpbGl0aWVzIChzaGFyZWQgd2l0aCB0aGUgaG9zdCdzIHBpcGVsaW5lKSDilIDilIBcbmV4cG9ydCB7IFByb2Nlc3NpbmdJbWFnZSB9IGZyb20gJy4vbGliL3Byb2Nlc3NpbmcvcHJvY2Vzc2luZy1pbWFnZSc7XG5leHBvcnQgeyBjcm9wSW1hZ2UsIENyb3BJbWFnZU9wdGlvbnMgfSBmcm9tICcuL2xpYi9wcm9jZXNzaW5nL2ltYWdlLWNyb3AnO1xuZXhwb3J0IHsgSW1hZ2VDb252ZXJ0ZXJTZXJ2aWNlIH0gZnJvbSAnLi9saWIvcHJvY2Vzc2luZy9pbWFnZS1jb252ZXJ0ZXIuc2VydmljZSc7XG5cbi8vIOKUgOKUgCBTQU0gbW9kZWwgcmVnaXN0cnkgKGhvc3QgY29uZmlndXJlcyBob3N0ZWQgT05OWCBVUkxzIG9uY2UgYXZhaWxhYmxlKSDilIDilIBcbmV4cG9ydCB7XG4gIFNBTV9NT0RFTFMsIERFRkFVTFRfU0FNX01PREVMX0lELCBnZXRTYW1Nb2RlbCwgaXNTYW1Nb2RlbFJlYWR5LCBzZXRTYW1Nb2RlbFVybHMsXG4gIHNldERlZmF1bHRTYW1Nb2RlbCwgZ2V0RGVmYXVsdFNhbU1vZGVsSWQsXG59IGZyb20gJy4vbGliL3Rvb2xiYXIvc2VnbWVudGF0aW9uL3NhbS1tb2RlbC1yZWdpc3RyeSc7XG5cbi8vIOKUgOKUgCBZT0xPIGluc3RhbmNlLXNlZ21lbnRhdGlvbiBtb2RlbCByZWdpc3RyeSAoc2FtZSByZXBvaW50aW5nIHN0b3J5KSDilIDilIDilIDilIDilIBcbmV4cG9ydCB7XG4gIFlPTE9fTU9ERUxTLCBERUZBVUxUX1lPTE9fTU9ERUxfSUQsIGdldFlvbG9Nb2RlbCwgaXNZb2xvTW9kZWxSZWFkeSwgc2V0WW9sb01vZGVsVXJscyxcbiAgc2V0RGVmYXVsdFlvbG9Nb2RlbCwgZ2V0RGVmYXVsdFlvbG9Nb2RlbElkLCBZb2xvTW9kZWxEZWYsXG59IGZyb20gJy4vbGliL3Rvb2xiYXIvc2VnbWVudGF0aW9uL3lvbG8tbW9kZWwtcmVnaXN0cnknO1xuXG4vLyDilIDilIAgUmV0aW5hbC1sYXllciBzZW1hbnRpYy1zZWdtZW50YXRpb24gcmVnaXN0cnkgKHNhbWUgcmVwb2ludGluZyBzdG9yeSkg4pSA4pSAXG5leHBvcnQge1xuICBSRVRJTkFMX01PREVMUywgREVGQVVMVF9SRVRJTkFMX01PREVMX0lELCBnZXRSZXRpbmFsTW9kZWwsIGlzUmV0aW5hbE1vZGVsUmVhZHksXG4gIHJlYWR5UmV0aW5hbE1vZGVscywgc2V0UmV0aW5hbE1vZGVsVXJscywgc2V0RGVmYXVsdFJldGluYWxNb2RlbCwgZ2V0RGVmYXVsdFJldGluYWxNb2RlbElkLFxuICBSZXRpbmFsTW9kZWxEZWYsXG59IGZyb20gJy4vbGliL3Rvb2xiYXIvc2VnbWVudGF0aW9uL3JldGluYWwtbW9kZWwtcmVnaXN0cnknO1xuXG4vLyDilIDilIAgb25ueHJ1bnRpbWUtd2ViIFdBU00gbG9jYXRpb24gKGhvc3Qgb3ZlcnJpZGVzIG9uY2UsIGF0IGFwcCBpbml0KSDilIDilIDilIDilIDilIDilIDilIBcbmV4cG9ydCB7IHNldE9ydFdhc21CYXNlLCBnZXRPcnRXYXNtQmFzZSB9IGZyb20gJy4vbGliL3Rvb2xiYXIvc2VnbWVudGF0aW9uL29ydC1ydW50aW1lLWNvbmZpZyc7XG5cbi8vIOKUgOKUgCBOZXV0cmFsIGRhdGEgbW9kZWxzIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgFxuZXhwb3J0ICogZnJvbSAnLi9saWIvbW9kZWxzL3JlZ2lvbic7XG5leHBvcnQgKiBmcm9tICcuL2xpYi9tb2RlbHMvY2xhc3MtcHJlc2V0JztcbmV4cG9ydCB7IFNoYXBlU2VsZWN0aW9uIH0gZnJvbSAnLi9saWIvbW9kZWxzL3NoYXBlJztcblxuLy8g4pSA4pSAIEFuZ3VsYXIgbW9kdWxlICsgY29tcG9zaXRpb24tcm9vdCBzZXJ2aWNlIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgFxuZXhwb3J0IHsgVmlzdWFsaXphdGlvbk1vZHVsZSB9IGZyb20gJy4vbGliL3Zpc3VhbGl6YXRpb24ubW9kdWxlJztcbmV4cG9ydCB7IFJvdXRpbmdWaXN1YWxpemVyU2VydmljZSB9IGZyb20gJy4vbGliL3JvdXRpbmctdmlzdWFsaXplci5zZXJ2aWNlJztcbi8vIFByb3ZpZGVyIGZhY3RvcnkgZm9yIGFuIGlzb2xhdGVkLCBjb21wb25lbnQtc2NvcGVkIHZpZXdlciBpbnN0YW5jZSAoZS5nLiBhXG4vLyBtb2RhbCB0aGF0IG11c3Qgbm90IHNoYXJlIHRoZSBtYWluIHZpZXdlcidzIHJlZ2lvbi9pbWFnZSBzdGF0ZSkuXG5leHBvcnQgeyBwcm92aWRlVmlzdWFsaXphdGlvbiB9IGZyb20gJy4vbGliL3Byb3ZpZGUtdmlzdWFsaXphdGlvbic7XG5cbi8vIOKUgOKUgCBQdWJsaWMgY29tcG9uZW50cyAoZXhwb3J0ZWQgYnkgVmlzdWFsaXphdGlvbk1vZHVsZSkg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSAXG4vLyBuZy1wYWNrYWdyIHJlcXVpcmVzIG1vZHVsZS1leHBvcnRlZCBjb21wb25lbnRzIHRvIGJlIHJlYWNoYWJsZSBmcm9tIHRoZVxuLy8gZW50cnkgcG9pbnQgc28gY29uc3VtZXJzIGdldCB0aGVpciB0eXBlczsgdGhlc2UgYXJlIHRoZSBlbWJlZGRhYmxlIGVsZW1lbnRzXG4vLyAoYDx2aXN1YWxpemVyPmAsIGA8cmVnaW9uLWVkaXRvcj5gLCBgPGhleC1jb2xvci1waWNrZXI+YCkuXG5leHBvcnQgeyBWaXN1YWxpemVyQ29tcG9uZW50IH0gZnJvbSAnLi9saWIvdmlzdWFsaXplci5jb21wb25lbnQnO1xuZXhwb3J0IHsgUmVnaW9uRWRpdG9yQ29tcG9uZW50IH0gZnJvbSAnLi9saWIvcmVnaW9uLWVkaXRvci9yZWdpb24tZWRpdG9yLmNvbXBvbmVudCc7XG5leHBvcnQgeyBIZXhDb2xvclBpY2tlckNvbXBvbmVudCB9IGZyb20gJy4vbGliL2hleC1jb2xvci1waWNrZXIvaGV4LWNvbG9yLXBpY2tlci5jb21wb25lbnQnO1xuZXhwb3J0IHsgQ2hhbm5lbEhpc3RvZ3JhbUNvbXBvbmVudCB9IGZyb20gJy4vbGliL2NoYW5uZWwtaGlzdG9ncmFtL2NoYW5uZWwtaGlzdG9ncmFtLmNvbXBvbmVudCc7XG4iXX0=
@@ -0,0 +1,11 @@
1
+ import { InjectionToken, inject } from '@angular/core';
2
+ import { RetinalSegmenterService } from '../toolbar/segmentation/retinal-segmenter.service';
3
+ /**
4
+ * DI token for the semantic segmenter, defaulting to the in-library
5
+ * jax-ai-js-backed implementation. Hosts override it to supply their own.
6
+ */
7
+ export const SEMANTIC_SEGMENTER = new InjectionToken('SEMANTIC_SEGMENTER', {
8
+ providedIn: 'root',
9
+ factory: () => inject(RetinalSegmenterService),
10
+ });
11
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2VtYW50aWMtc2VnbWVudGVyLmNvbnRyYWN0LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vc3JjL2xpYi9jb250cmFjdHMvc2VtYW50aWMtc2VnbWVudGVyLmNvbnRyYWN0LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxjQUFjLEVBQUUsTUFBTSxFQUFFLE1BQU0sZUFBZSxDQUFDO0FBRXZELE9BQU8sRUFBRSx1QkFBdUIsRUFBRSxNQUFNLG1EQUFtRCxDQUFDO0FBNEc1Rjs7O0dBR0c7QUFDSCxNQUFNLENBQUMsTUFBTSxrQkFBa0IsR0FBRyxJQUFJLGNBQWMsQ0FBcUIsb0JBQW9CLEVBQUU7SUFDN0YsVUFBVSxFQUFFLE1BQU07SUFDbEIsT0FBTyxFQUFFLEdBQUcsRUFBRSxDQUFDLE1BQU0sQ0FBQyx1QkFBdUIsQ0FBQztDQUMvQyxDQUFDLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBJbmplY3Rpb25Ub2tlbiwgaW5qZWN0IH0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XG5cbmltcG9ydCB7IFJldGluYWxTZWdtZW50ZXJTZXJ2aWNlIH0gZnJvbSAnLi4vdG9vbGJhci9zZWdtZW50YXRpb24vcmV0aW5hbC1zZWdtZW50ZXIuc2VydmljZSc7XG5cbi8qKlxuICogUG9ydCBmb3IgYSAqKnNlbWFudGljKiogc2VnbWVudGVyIOKAlCBvbmUgdGhhdCBhc3NpZ25zIGV2ZXJ5IHBpeGVsIGEgY2xhc3MgZnJvbVxuICogYSBmaXhlZCBzZXQsIHdpdGggbm8gbm90aW9uIG9mIHNlcGFyYXRlIG9iamVjdHMuXG4gKlxuICogVGhlIHRoaXJkIHNpYmxpbmcgb2Yge0BsaW5rIElDZWxsU2VnbWVudGVyfSBhbmQge0BsaW5rIElJbnN0YW5jZVNlZ21lbnRlcn0sXG4gKiBhbmQgZ2VudWluZWx5IGEgZGlmZmVyZW50IHNoYXBlIGZyb20gYm90aC4gQ2VsbHBvc2Ugc2VwYXJhdGVzIHRvdWNoaW5nXG4gKiBpbnN0YW5jZXMgb2Ygb25lIGNsYXNzOyBZT0xPIHJldHVybnMgb3ZlcmxhcHBpbmcgY2xhc3NpZmllZCBvYmplY3RzOyB0aGlzXG4gKiByZXR1cm5zICpsYXllcnMqIOKAlCByZWdpb25zIHRoYXQgdGlsZSB0aGUgaW1hZ2UsIG5ldmVyIG92ZXJsYXAsIGFuZCB3aGVyZSB0d29cbiAqIGRpc2Nvbm5lY3RlZCBwYXRjaGVzIG9mIHRoZSBzYW1lIGNsYXNzIGFyZSB0aGUgc2FtZSBmaW5kaW5nIHJhdGhlciB0aGFuIHR3b1xuICogb2JqZWN0cy4gVGhlcmUgaXMgbm8gY29uZmlkZW5jZSBwZXIgb2JqZWN0IGFuZCBubyBOTVMsIGJlY2F1c2UgdGhlcmUgYXJlIG5vXG4gKiBvYmplY3RzLlxuICpcbiAqIFJlc3VsdHMgYXJlIGRlbGl2ZXJlZCBhcyBwb2x5Z29uIHJpbmdzIGluICoqaW1hZ2UgcGl4ZWxzKiosIHNvIGEgY2FsbGVyIGNhblxuICogYnVpbGQge0BsaW5rIFJlZ2lvbn1zIGRpcmVjdGx5IOKAlCB0aGUgc2FtZSBjdXJyZW5jeSB0aGUgb3RoZXIgdHdvIHBvcnRzIHVzZS5cbiAqL1xuXG4vKiogQSBjbG9zZWQgcmluZzsgdGhlIGxhc3QgcG9pbnQgaXMgTk9UIGEgcmVwZWF0IG9mIHRoZSBmaXJzdC4gKi9cbmV4cG9ydCB0eXBlIFNlbWFudGljUmluZyA9IEFycmF5PFtudW1iZXIsIG51bWJlcl0+O1xuXG4vKiogT25lIGNvbm5lY3RlZCBjb21wb25lbnQgb2Ygb25lIGNsYXNzLCB3aXRoIGFueSBpbnRlcmlvciB2b2lkcy4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgU2VtYW50aWNSZWdpb24ge1xuICBleHRlcmlvcjogU2VtYW50aWNSaW5nO1xuICBob2xlczogU2VtYW50aWNSaW5nW107XG4gIGNsYXNzSWQ6IG51bWJlcjtcbiAgY2xhc3NOYW1lOiBzdHJpbmc7XG4gIC8qKiBBcmVhIGluIHBpeGVscywgaG9sZXMgYWxyZWFkeSBzdWJ0cmFjdGVkLiAqL1xuICBhcmVhOiBudW1iZXI7XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgU2VtYW50aWNTZWdtZW50YXRpb24ge1xuICByZWdpb25zOiBTZW1hbnRpY1JlZ2lvbltdO1xuICAvKiogVG90YWwgcGl4ZWxzIGFzc2lnbmVkIHRvIGVhY2ggY2xhc3MsIGluZGV4ZWQgYnkgY2xhc3MgaWQuICovXG4gIGNsYXNzQXJlYXM6IG51bWJlcltdO1xuICAvKiogQ2xhc3MgbmFtZXMgaW5kZXhlZCBieSBjbGFzcyBpZCwgZnJvbSB0aGUgbW9kZWwncyBvd24gbWV0YWRhdGEuICovXG4gIGNsYXNzTmFtZXM6IHN0cmluZ1tdO1xuICB3aWR0aDogbnVtYmVyO1xuICBoZWlnaHQ6IG51bWJlcjtcbiAgLyoqXG4gICAqIEZyYWN0aW9uIG9mIHBpeGVscyB0aGF0IG1ldCBubyBjbGFzcydzIHRocmVzaG9sZC5cbiAgICpcbiAgICogV29ydGggc3VyZmFjaW5nIHJhdGhlciB0aGFuIGRpc2NhcmRpbmc6IHRoZXNlIG1vZGVscyBhcmUgdGhyZXNob2xkZWQgcGVyXG4gICAqIGNsYXNzIHJhdGhlciB0aGFuIGFyZ21heGVkLCBzbyBhIGhpZ2ggdmFsdWUgbWVhbnMgdGhlIG1vZGVsIHdhcyB1bnN1cmVcbiAgICogZXZlcnl3aGVyZSDigJQgd2hpY2ggaXMgdGhlIHNpZ25hdHVyZSBvZiB3cm9uZyBwcmVwcm9jZXNzaW5nICh3cm9uZyBpbnB1dFxuICAgKiBzY2FsZSwgd3JvbmcgZ3JleXNjYWxlKSBsb25nIGJlZm9yZSB0aGUgcG9seWdvbnMgbG9vayBvYnZpb3VzbHkgd3JvbmcuXG4gICAqL1xuICB1bmFzc2lnbmVkRnJhY3Rpb246IG51bWJlcjtcbn1cblxuZXhwb3J0IGludGVyZmFjZSBTZW1hbnRpY1NlZ21lbnRPcHRpb25zIHtcbiAgLyoqIFdoaWNoIHJlZ2lzdGVyZWQgbW9kZWwgdG8gcnVuLiBEZWZhdWx0cyB0byB0aGUgcmVnaXN0cnkncyBkZWZhdWx0LiAqL1xuICBtb2RlbElkPzogc3RyaW5nO1xuICAvKipcbiAgICogU2NhbGUgdG8gcnVuIGF0LCBhcyBhIGRpdmlzb3Igb2YgZnVsbCByZXNvbHV0aW9uIOKAlCB0aGUgc2FtZSBtZWFuaW5nIHRoZVxuICAgKiBzZXJ2ZXIgZ2l2ZXMgaXQuXG4gICAqXG4gICAqIE1hdHRlcnMgYXMgbXVjaCBoZXJlIGFzIGZvciBhIGRldGVjdG9yOiB0aGVzZSBjaGVja3BvaW50cyB3ZXJlIHRyYWluZWQgb25cbiAgICogNDB4IG9yIDIweCBwYXRjaGVzLCBhbmQgYSBsYXllciBpbWFnZWQgYXQgdGhlIHdyb25nIHNjYWxlIGRvZXMgbm90IGxvb2tcbiAgICogbGlrZSB0aGUgbGF5ZXIgdGhlIG1vZGVsIGxlYXJuZWQuXG4gICAqL1xuICBkb3duc2FtcGxpbmdGYWN0b3I/OiBudW1iZXI7XG4gIC8qKlxuICAgKiBNaW5pbXVtIHByb2JhYmlsaXR5IGZvciBhIHBpeGVsIHRvIGJlIGFzc2lnbmVkIHRvIGEgY2xhc3MsIGRlZmF1bHQgMC41LlxuICAgKlxuICAgKiBOb3QgYSBwbGFpbiBhcmdtYXguIFRoZSByZWZlcmVuY2Ugd29ya2VyIHRocmVzaG9sZHMgZWFjaCBjbGFzcyBjaGFubmVsXG4gICAqIGluZGVwZW5kZW50bHkgYXQgMC41OyBiZWNhdXNlIHRoZSBtb2RlbCdzIGxhc3QgbGF5ZXIgaXMgYSBzb2Z0bWF4IGF0IG1vc3RcbiAgICogb25lIGNsYXNzIGNhbiBjbGVhciB0aGF0LCBzbyB0aGlzIGlzIGFyZ21heCAqcGx1cyBhIGNvbmZpZGVuY2UgZmxvb3IqIOKAlFxuICAgKiBwaXhlbHMgd2hlcmUgdGhlIHdpbm5lciBpcyB3ZWFrIGFyZSBsZWZ0IHVuYXNzaWduZWQgcmF0aGVyIHRoYW4gZm9yY2VkIGludG9cbiAgICogdGhlIGJlc3Qgb2YgYSBiYWQgc2V0LiBMb3dlcmluZyBpdCB0b3dhcmQgMC4yNSBhcHByb2FjaGVzIGEgdHJ1ZSBhcmdtYXggYW5kXG4gICAqIHZpc2libHkgZmF0dGVucyBldmVyeSBsYXllci5cbiAgICovXG4gIGNsYXNzVGhyZXNob2xkPzogbnVtYmVyO1xuICAvKiogS2VlcCBvbmx5IHRoZXNlIGNsYXNzIGlkczsgb21pdCBmb3IgZXZlcnl0aGluZyBleGNlcHQgYmFja2dyb3VuZC4gKi9cbiAgY2xhc3NGaWx0ZXI/OiBudW1iZXJbXTtcbiAgLyoqIERvdWdsYXMtUGV1Y2tlciB0b2xlcmFuY2UgaW4gcGl4ZWxzIGZvciBvdXRsaW5lIHNpbXBsaWZpY2F0aW9uOyAwIGRpc2FibGVzLiAqL1xuICBzaW1wbGlmeVRvbGVyYW5jZT86IG51bWJlcjtcbiAgLyoqIERyb3AgcmVnaW9ucyB3aG9zZSBhcmVhIGlzIGJlbG93IHRoaXMsIGluIHNxdWFyZSBwaXhlbHMuICovXG4gIG1pbkFyZWE/OiBudW1iZXI7XG4gIC8qKlxuICAgKiBDb29wZXJhdGl2ZSBjYW5jZWxsYXRpb24sIGNoZWNrZWQgYXQgcGF0Y2ggYm91bmRhcmllcyDigJQgc28gaXQgdGFrZXMgZWZmZWN0XG4gICAqIHdpdGhpbiByb3VnaGx5IG9uZSBwYXRjaCByYXRoZXIgdGhhbiBpbnN0YW50bHkuXG4gICAqL1xuICBzaWduYWw/OiBBYm9ydFNpZ25hbDtcbn1cblxuLyoqIFByb2dyZXNzL3N0YXR1cyBjYWxsYmFja3MgKGFsbCBvcHRpb25hbCkuIE1pcnJvcnMge0BsaW5rIEluc3RhbmNlU2VnbWVudFByb2dyZXNzfS4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgU2VtYW50aWNTZWdtZW50UHJvZ3Jlc3Mge1xuICAvKiogTW9kZWwtZG93bmxvYWQgcHJvZ3Jlc3MsIDAuLjEgKGZpcnN0IHJ1biBvbmx5KS4gKi9cbiAgb25Qcm9ncmVzcz86IChmcmFjdGlvbjogbnVtYmVyKSA9PiB2b2lkO1xuICAvKipcbiAgICogUmF3IGRvd25sb2FkIGJ5dGUgY291bnRzLCBmb3IgaG9zdHMgdGhhdCByZW5kZXIgYSBzaXplIHJhdGhlciB0aGFuIGEgYmFyZVxuICAgKiBwZXJjZW50YWdlLiBgdG90YWxgIGlzIG51bGwgd2hlbiB0aGUgcmVzcG9uc2UgY2FycmllZCBubyBDb250ZW50LUxlbmd0aC5cbiAgICovXG4gIG9uQnl0ZXM/OiAobG9hZGVkOiBudW1iZXIsIHRvdGFsOiBudW1iZXIgfCBudWxsKSA9PiB2b2lkO1xuICAvKiogSHVtYW4tcmVhZGFibGUgcGhhc2UsIGUuZy4gJ1NlZ21lbnRpbmcgcGF0Y2ggMy844oCmJy4gKi9cbiAgb25TdGF0dXM/OiAoc3RhdHVzOiBzdHJpbmcpID0+IHZvaWQ7XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgSVNlbWFudGljU2VnbWVudGVyIHtcbiAgLyoqIEFzc2lnbiBldmVyeSBwaXhlbCBhIGNsYXNzIGFuZCByZXR1cm4gdGhlIHJlc3VsdGluZyByZWdpb25zLiAqL1xuICBzZWdtZW50U2VtYW50aWMoXG4gICAgaW1hZ2U6IHsgZGF0YTogVWludDhDbGFtcGVkQXJyYXk7IHdpZHRoOiBudW1iZXI7IGhlaWdodDogbnVtYmVyIH0sXG4gICAgb3B0cz86IFNlbWFudGljU2VnbWVudE9wdGlvbnMsXG4gICAgcHJvZ3Jlc3M/OiBTZW1hbnRpY1NlZ21lbnRQcm9ncmVzcyxcbiAgKTogUHJvbWlzZTxTZW1hbnRpY1NlZ21lbnRhdGlvbj47XG59XG5cbi8qKlxuICogREkgdG9rZW4gZm9yIHRoZSBzZW1hbnRpYyBzZWdtZW50ZXIsIGRlZmF1bHRpbmcgdG8gdGhlIGluLWxpYnJhcnlcbiAqIGpheC1haS1qcy1iYWNrZWQgaW1wbGVtZW50YXRpb24uIEhvc3RzIG92ZXJyaWRlIGl0IHRvIHN1cHBseSB0aGVpciBvd24uXG4gKi9cbmV4cG9ydCBjb25zdCBTRU1BTlRJQ19TRUdNRU5URVIgPSBuZXcgSW5qZWN0aW9uVG9rZW48SVNlbWFudGljU2VnbWVudGVyPignU0VNQU5USUNfU0VHTUVOVEVSJywge1xuICBwcm92aWRlZEluOiAncm9vdCcsXG4gIGZhY3Rvcnk6ICgpID0+IGluamVjdChSZXRpbmFsU2VnbWVudGVyU2VydmljZSksXG59KTtcbiJdfQ==
@@ -0,0 +1,197 @@
1
+ import { Inject, Injectable, Optional } from '@angular/core';
2
+ import { BehaviorSubject, firstValueFrom } from 'rxjs';
3
+ import { Region, Polygon } from '../../models/region';
4
+ import { TILE_ACCESS_PORT } from '../../contracts/ports/tile-access.port';
5
+ import * as i0 from "@angular/core";
6
+ /** Marks regions this tool produced, so a re-run can replace only its own. */
7
+ export const RETINAL_REGION_SOURCE = 'retinal-layer';
8
+ /**
9
+ * Retinal-layer segmentation over the current view.
10
+ *
11
+ * Sibling of {@link YoloDetectToolService}, and structured the same way: run on
12
+ * whatever is on screen, map the results onto full-image coordinates through
13
+ * the viewer's source rect, and commit them as regions. The difference is what
14
+ * comes back — layers that tile the field rather than discrete objects, so
15
+ * every region is one connected component of one layer and there is no
16
+ * per-object confidence to filter on.
17
+ *
18
+ * Exposes the same `status$` / `busy$` / `progress$` surface as the cellpose and
19
+ * YOLO tools so the host's shared segmentation toast drives it unchanged.
20
+ */
21
+ export class RetinalLayerToolService {
22
+ constructor(
23
+ /** Server-side crop, so segmentation can run at the model's scale rather
24
+ * than the viewer's. Optional: without it the tool falls back to the
25
+ * displayed pixels, which is correct but scale-dependent. */
26
+ tileAccess = null) {
27
+ this.tileAccess = tileAccess;
28
+ this.status$ = new BehaviorSubject('');
29
+ this.busy$ = new BehaviorSubject(false);
30
+ this.progress$ = new BehaviorSubject(-1);
31
+ }
32
+ /**
33
+ * Segment the current view and commit the layers as regions.
34
+ *
35
+ * @returns the number of regions added.
36
+ */
37
+ async segmentInView(viz, segmenter, opts = {}) {
38
+ const pixels = viz.getDisplayedPixelData?.();
39
+ if (!pixels || pixels.width === 0 || pixels.height === 0) {
40
+ this.status$.next('No image on screen to segment.');
41
+ return 0;
42
+ }
43
+ this.busy$.next(true);
44
+ this.progress$.next(-1);
45
+ try {
46
+ const rect = viz.getDisplayedSourceRect?.() ?? null;
47
+ const target = Number(opts.downsamplingFactor ?? 0) || 0;
48
+ const viewFactor = rect && pixels.width > 0 ? rect.width / pixels.width : 1;
49
+ // Re-crop at the model's scale when the view is coarser than it wants.
50
+ // The image is tile-backed, so that detail exists server-side even though
51
+ // the displayed pixels have discarded it.
52
+ let source = { data: pixels.data, width: pixels.width, height: pixels.height };
53
+ if (rect && target > 0 && target < viewFactor * 0.98) {
54
+ this.status$.next('Fetching image at model resolution…');
55
+ const hi = await this.fetchCropAtScale(rect, target);
56
+ if (hi)
57
+ source = hi;
58
+ }
59
+ const result = await segmenter.segmentSemantic(source, opts, {
60
+ onProgress: (f) => this.progress$.next(f),
61
+ onStatus: (s) => this.status$.next(s),
62
+ });
63
+ // View pixels -> full-resolution image pixels. Without this every region
64
+ // would sit at view-local coordinates, i.e. the wrong place on the slide
65
+ // for any zoomed or panned view.
66
+ const offsetX = rect ? rect.x : 0;
67
+ const offsetY = rect ? rect.y : 0;
68
+ // Against the pixels actually inferred on, which may be the re-crop.
69
+ const ratioX = rect && source.width > 0 ? rect.width / source.width : 1;
70
+ const ratioY = rect && source.height > 0 ? rect.height / source.height : 1;
71
+ // View pixels -> image pixels, for any ring.
72
+ const mapRing = (ring) => ring.map(([x, y]) => [Math.round(offsetX + x * ratioX), Math.round(offsetY + y * ratioY)]);
73
+ const added = [];
74
+ for (const r of result.regions) {
75
+ if (r.exterior.length < 3)
76
+ continue;
77
+ const exterior = mapRing(r.exterior);
78
+ added.push(this.makeRegion(exterior.map(([x]) => x), exterior.map(([, y]) => y), r.className,
79
+ // Holes are real findings here, not noise: a layer with a void
80
+ // through it is a donut, and dropping the interior rings fills it
81
+ // in silently. Rings that survive simplification to fewer than 3
82
+ // points cannot bound anything, so they are skipped rather than
83
+ // emitted as degenerate geometry.
84
+ r.holes.filter((h) => h.length >= 3).map(mapRing)));
85
+ }
86
+ if (added.length === 0) {
87
+ // Distinguish "found nothing" from "was unsure everywhere". The second
88
+ // is the signature of wrong preprocessing or the wrong magnification,
89
+ // and reads as a silent no-op unless it is said out loud.
90
+ this.status$.next(result.unassignedFraction > 0.9
91
+ ? 'No layers found — the model was unsure across the whole view. ' +
92
+ 'Check the magnification matches the model.'
93
+ : 'No layers found.');
94
+ return 0;
95
+ }
96
+ // Replace this tool's own previous output, keep everything else.
97
+ // Hand-drawn regions and other tools' output carry a different `source`
98
+ // (or none) and are untouched.
99
+ const kept = (viz.getRegions() ?? []).filter((x) => x.source !== RETINAL_REGION_SOURCE);
100
+ viz.setRegions([...kept, ...added]);
101
+ this.status$.next(`Added ${added.length} region(s).`);
102
+ return added.length;
103
+ }
104
+ catch (err) {
105
+ this.status$.next(err instanceof Error ? err.message : 'Retinal layer segmentation failed.');
106
+ return 0;
107
+ }
108
+ finally {
109
+ this.busy$.next(false);
110
+ this.progress$.next(-1);
111
+ }
112
+ }
113
+ /**
114
+ * Re-crop `rect` at `1/target` of full resolution. Returns null on any
115
+ * failure, so the caller falls back to the displayed pixels — a coarser crop
116
+ * beats a failed run.
117
+ */
118
+ async fetchCropAtScale(rect, target) {
119
+ if (!this.tileAccess)
120
+ return null;
121
+ let w = Math.round(rect.width / target);
122
+ let h = Math.round(rect.height / target);
123
+ // A factor of 1 over a whole-slide ROI would ask for hundreds of megapixels.
124
+ const MAX_PIXELS = 16_777_216; // 4096²
125
+ const pixels = w * h;
126
+ if (pixels > MAX_PIXELS) {
127
+ const k = Math.sqrt(MAX_PIXELS / pixels);
128
+ w = Math.round(w * k);
129
+ h = Math.round(h * k);
130
+ }
131
+ if (w < 64 || h < 64)
132
+ return null;
133
+ try {
134
+ const roi = {
135
+ x: Math.round(rect.x),
136
+ y: Math.round(rect.y),
137
+ width: Math.round(rect.width),
138
+ height: Math.round(rect.height),
139
+ };
140
+ const screen = { x: 0, y: 0, width: w, height: h };
141
+ const bytes = await firstValueFrom(this.tileAccess.zoomOnRegion(roi, screen, 0));
142
+ if (!bytes || bytes.byteLength === 0)
143
+ return null;
144
+ const bitmap = await createImageBitmap(new Blob([bytes]));
145
+ const canvas = document.createElement('canvas');
146
+ canvas.width = bitmap.width;
147
+ canvas.height = bitmap.height;
148
+ const ctx = canvas.getContext('2d');
149
+ if (!ctx)
150
+ return null;
151
+ ctx.drawImage(bitmap, 0, 0);
152
+ // Read the dimensions BEFORE close(): closing an ImageBitmap zeroes its
153
+ // width/height, so returning them afterwards yields a 0x0 image with a
154
+ // full pixel buffer — inference then runs on nothing and finds nothing.
155
+ const width = bitmap.width;
156
+ const height = bitmap.height;
157
+ const out = ctx.getImageData(0, 0, width, height);
158
+ bitmap.close?.();
159
+ return { data: out.data, width, height };
160
+ }
161
+ catch {
162
+ return null;
163
+ }
164
+ }
165
+ makeRegion(xs, ys, className, holes = []) {
166
+ const poly = new Polygon();
167
+ poly.npoints = xs.length;
168
+ poly.xpoints = xs;
169
+ poly.ypoints = ys;
170
+ poly.coordinates = xs.map((x, i) => [x, ys[i]]);
171
+ poly.closed = true;
172
+ // Same closed-ring convention as the exterior: no repeated closing point.
173
+ // Left unset when empty so a solid region does not carry an empty array
174
+ // through export and round-tripping.
175
+ if (holes.length)
176
+ poly.holes = holes;
177
+ const region = new Region();
178
+ region.bounds = poly;
179
+ region.source = RETINAL_REGION_SOURCE;
180
+ // The class name drives preset colour resolution; leaving `color` unset lets
181
+ // an existing preset for this layer win.
182
+ region.label = className;
183
+ return region;
184
+ }
185
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.9", ngImport: i0, type: RetinalLayerToolService, deps: [{ token: TILE_ACCESS_PORT, optional: true }], target: i0.ɵɵFactoryTarget.Injectable }); }
186
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.3.9", ngImport: i0, type: RetinalLayerToolService, providedIn: 'root' }); }
187
+ }
188
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.9", ngImport: i0, type: RetinalLayerToolService, decorators: [{
189
+ type: Injectable,
190
+ args: [{ providedIn: 'root' }]
191
+ }], ctorParameters: () => [{ type: undefined, decorators: [{
192
+ type: Optional
193
+ }, {
194
+ type: Inject,
195
+ args: [TILE_ACCESS_PORT]
196
+ }] }] });
197
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmV0aW5hbC1sYXllci10b29sLnNlcnZpY2UuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi9zcmMvbGliL3Rvb2xiYXIvc2VnbWVudGF0aW9uL3JldGluYWwtbGF5ZXItdG9vbC5zZXJ2aWNlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxNQUFNLEVBQUUsVUFBVSxFQUFFLFFBQVEsRUFBRSxNQUFNLGVBQWUsQ0FBQztBQUM3RCxPQUFPLEVBQUUsZUFBZSxFQUFFLGNBQWMsRUFBRSxNQUFNLE1BQU0sQ0FBQztBQUV2RCxPQUFPLEVBQUUsTUFBTSxFQUFFLE9BQU8sRUFBYSxNQUFNLHFCQUFxQixDQUFDO0FBQ2pFLE9BQU8sRUFBRSxnQkFBZ0IsRUFBdUIsTUFBTSx3Q0FBd0MsQ0FBQzs7QUFPL0YsOEVBQThFO0FBQzlFLE1BQU0sQ0FBQyxNQUFNLHFCQUFxQixHQUFHLGVBQWUsQ0FBQztBQUVyRDs7Ozs7Ozs7Ozs7O0dBWUc7QUFFSCxNQUFNLE9BQU8sdUJBQXVCO0lBS2xDO0lBQ0U7O2tFQUU4RDtJQUNoQixhQUFvQyxJQUFJO1FBQXhDLGVBQVUsR0FBVixVQUFVLENBQThCO1FBUi9FLFlBQU8sR0FBRyxJQUFJLGVBQWUsQ0FBUyxFQUFFLENBQUMsQ0FBQztRQUMxQyxVQUFLLEdBQUcsSUFBSSxlQUFlLENBQVUsS0FBSyxDQUFDLENBQUM7UUFDNUMsY0FBUyxHQUFHLElBQUksZUFBZSxDQUFTLENBQUMsQ0FBQyxDQUFDLENBQUM7SUFPbEQsQ0FBQztJQUVKOzs7O09BSUc7SUFDSCxLQUFLLENBQUMsYUFBYSxDQUNqQixHQUFnQixFQUNoQixTQUE2QixFQUM3QixPQUErQixFQUFFO1FBRWpDLE1BQU0sTUFBTSxHQUFHLEdBQUcsQ0FBQyxxQkFBcUIsRUFBRSxFQUFFLENBQUM7UUFDN0MsSUFBSSxDQUFDLE1BQU0sSUFBSSxNQUFNLENBQUMsS0FBSyxLQUFLLENBQUMsSUFBSSxNQUFNLENBQUMs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YmxlLCBPcHRpb25hbCB9IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xuaW1wb3J0IHsgQmVoYXZpb3JTdWJqZWN0LCBmaXJzdFZhbHVlRnJvbSB9IGZyb20gJ3J4anMnO1xuXG5pbXBvcnQgeyBSZWdpb24sIFBvbHlnb24sIFJlY3RhbmdsZSB9IGZyb20gJy4uLy4uL21vZGVscy9yZWdpb24nO1xuaW1wb3J0IHsgVElMRV9BQ0NFU1NfUE9SVCwgdHlwZSBUaWxlQWNjZXNzUG9ydCB9IGZyb20gJy4uLy4uL2NvbnRyYWN0cy9wb3J0cy90aWxlLWFjY2Vzcy5wb3J0JztcbmltcG9ydCB0eXBlIHsgSVZpc3VhbGl6ZXIgfSBmcm9tICcuLi8uLi9jb250cmFjdHMvdmlzdWFsaXplci5jb250cmFjdCc7XG5pbXBvcnQgdHlwZSB7XG4gIElTZW1hbnRpY1NlZ21lbnRlcixcbiAgU2VtYW50aWNTZWdtZW50T3B0aW9ucyxcbn0gZnJvbSAnLi4vLi4vY29udHJhY3RzL3NlbWFudGljLXNlZ21lbnRlci5jb250cmFjdCc7XG5cbi8qKiBNYXJrcyByZWdpb25zIHRoaXMgdG9vbCBwcm9kdWNlZCwgc28gYSByZS1ydW4gY2FuIHJlcGxhY2Ugb25seSBpdHMgb3duLiAqL1xuZXhwb3J0IGNvbnN0IFJFVElOQUxfUkVHSU9OX1NPVVJDRSA9ICdyZXRpbmFsLWxheWVyJztcblxuLyoqXG4gKiBSZXRpbmFsLWxheWVyIHNlZ21lbnRhdGlvbiBvdmVyIHRoZSBjdXJyZW50IHZpZXcuXG4gKlxuICogU2libGluZyBvZiB7QGxpbmsgWW9sb0RldGVjdFRvb2xTZXJ2aWNlfSwgYW5kIHN0cnVjdHVyZWQgdGhlIHNhbWUgd2F5OiBydW4gb25cbiAqIHdoYXRldmVyIGlzIG9uIHNjcmVlbiwgbWFwIHRoZSByZXN1bHRzIG9udG8gZnVsbC1pbWFnZSBjb29yZGluYXRlcyB0aHJvdWdoXG4gKiB0aGUgdmlld2VyJ3Mgc291cmNlIHJlY3QsIGFuZCBjb21taXQgdGhlbSBhcyByZWdpb25zLiBUaGUgZGlmZmVyZW5jZSBpcyB3aGF0XG4gKiBjb21lcyBiYWNrIOKAlCBsYXllcnMgdGhhdCB0aWxlIHRoZSBmaWVsZCByYXRoZXIgdGhhbiBkaXNjcmV0ZSBvYmplY3RzLCBzb1xuICogZXZlcnkgcmVnaW9uIGlzIG9uZSBjb25uZWN0ZWQgY29tcG9uZW50IG9mIG9uZSBsYXllciBhbmQgdGhlcmUgaXMgbm9cbiAqIHBlci1vYmplY3QgY29uZmlkZW5jZSB0byBmaWx0ZXIgb24uXG4gKlxuICogRXhwb3NlcyB0aGUgc2FtZSBgc3RhdHVzJGAgLyBgYnVzeSRgIC8gYHByb2dyZXNzJGAgc3VyZmFjZSBhcyB0aGUgY2VsbHBvc2UgYW5kXG4gKiBZT0xPIHRvb2xzIHNvIHRoZSBob3N0J3Mgc2hhcmVkIHNlZ21lbnRhdGlvbiB0b2FzdCBkcml2ZXMgaXQgdW5jaGFuZ2VkLlxuICovXG5ASW5qZWN0YWJsZSh7IHByb3ZpZGVkSW46ICdyb290JyB9KVxuZXhwb3J0IGNsYXNzIFJldGluYWxMYXllclRvb2xTZXJ2aWNlIHtcbiAgcmVhZG9ubHkgc3RhdHVzJCA9IG5ldyBCZWhhdmlvclN1YmplY3Q8c3RyaW5nPignJyk7XG4gIHJlYWRvbmx5IGJ1c3kkID0gbmV3IEJlaGF2aW9yU3ViamVjdDxib29sZWFuPihmYWxzZSk7XG4gIHJlYWRvbmx5IHByb2dyZXNzJCA9IG5ldyBCZWhhdmlvclN1YmplY3Q8bnVtYmVyPigtMSk7XG5cbiAgY29uc3RydWN0b3IoXG4gICAgLyoqIFNlcnZlci1zaWRlIGNyb3AsIHNvIHNlZ21lbnRhdGlvbiBjYW4gcnVuIGF0IHRoZSBtb2RlbCdzIHNjYWxlIHJhdGhlclxuICAgICAqICB0aGFuIHRoZSB2aWV3ZXIncy4gT3B0aW9uYWw6IHdpdGhvdXQgaXQgdGhlIHRvb2wgZmFsbHMgYmFjayB0byB0aGVcbiAgICAgKiAgZGlzcGxheWVkIHBpeGVscywgd2hpY2ggaXMgY29ycmVjdCBidXQgc2NhbGUtZGVwZW5kZW50LiAqL1xuICAgIEBPcHRpb25hbCgpIEBJbmplY3QoVElMRV9BQ0NFU1NfUE9SVCkgcHJpdmF0ZSB0aWxlQWNjZXNzOiBUaWxlQWNjZXNzUG9ydCB8IG51bGwgPSBudWxsLFxuICApIHt9XG5cbiAgLyoqXG4gICAqIFNlZ21lbnQgdGhlIGN1cnJlbnQgdmlldyBhbmQgY29tbWl0IHRoZSBsYXllcnMgYXMgcmVnaW9ucy5cbiAgICpcbiAgICogQHJldHVybnMgdGhlIG51bWJlciBvZiByZWdpb25zIGFkZGVkLlxuICAgKi9cbiAgYXN5bmMgc2VnbWVudEluVmlldyhcbiAgICB2aXo6IElWaXN1YWxpemVyLFxuICAgIHNlZ21lbnRlcjogSVNlbWFudGljU2VnbWVudGVyLFxuICAgIG9wdHM6IFNlbWFudGljU2VnbWVudE9wdGlvbnMgPSB7fSxcbiAgKTogUHJvbWlzZTxudW1iZXI+IHtcbiAgICBjb25zdCBwaXhlbHMgPSB2aXouZ2V0RGlzcGxheWVkUGl4ZWxEYXRhPy4oKTtcbiAgICBpZiAoIXBpeGVscyB8fCBwaXhlbHMud2lkdGggPT09IDAgfHwgcGl4ZWxzLmhlaWdodCA9PT0gMCkge1xuICAgICAgdGhpcy5zdGF0dXMkLm5leHQoJ05vIGltYWdlIG9uIHNjcmVlbiB0byBzZWdtZW50LicpO1xuICAgICAgcmV0dXJuIDA7XG4gICAgfVxuXG4gICAgdGhpcy5idXN5JC5uZXh0KHRydWUpO1xuICAgIHRoaXMucHJvZ3Jlc3MkLm5leHQoLTEpO1xuICAgIHRyeSB7XG4gICAgICBjb25zdCByZWN0ID0gdml6LmdldERpc3BsYXllZFNvdXJjZVJlY3Q/LigpID8/IG51bGw7XG4gICAgICBjb25zdCB0YXJnZXQgPSBOdW1iZXIob3B0cy5kb3duc2FtcGxpbmdGYWN0b3IgPz8gMCkgfHwgMDtcbiAgICAgIGNvbnN0IHZpZXdGYWN0b3IgPSByZWN0ICYmIHBpeGVscy53aWR0aCA+IDAgPyByZWN0LndpZHRoIC8gcGl4ZWxzLndpZHRoIDogMTtcblxuICAgICAgLy8gUmUtY3JvcCBhdCB0aGUgbW9kZWwncyBzY2FsZSB3aGVuIHRoZSB2aWV3IGlzIGNvYXJzZXIgdGhhbiBpdCB3YW50cy5cbiAgICAgIC8vIFRoZSBpbWFnZSBpcyB0aWxlLWJhY2tlZCwgc28gdGhhdCBkZXRhaWwgZXhpc3RzIHNlcnZlci1zaWRlIGV2ZW4gdGhvdWdoXG4gICAgICAvLyB0aGUgZGlzcGxheWVkIHBpeGVscyBoYXZlIGRpc2NhcmRlZCBpdC5cbiAgICAgIGxldCBzb3VyY2UgPSB7IGRhdGE6IHBpeGVscy5kYXRhLCB3aWR0aDogcGl4ZWxzLndpZHRoLCBoZWlnaHQ6IHBpeGVscy5oZWlnaHQgfTtcbiAgICAgIGlmIChyZWN0ICYmIHRhcmdldCA+IDAgJiYgdGFyZ2V0IDwgdmlld0ZhY3RvciAqIDAuOTgpIHtcbiAgICAgICAgdGhpcy5zdGF0dXMkLm5leHQoJ0ZldGNoaW5nIGltYWdlIGF0IG1vZGVsIHJlc29sdXRpb27igKYnKTtcbiAgICAgICAgY29uc3QgaGkgPSBhd2FpdCB0aGlzLmZldGNoQ3JvcEF0U2NhbGUocmVjdCwgdGFyZ2V0KTtcbiAgICAgICAgaWYgKGhpKSBzb3VyY2UgPSBoaTtcbiAgICAgIH1cblxuICAgICAgY29uc3QgcmVzdWx0ID0gYXdhaXQgc2VnbWVudGVyLnNlZ21lbnRTZW1hbnRpYyhzb3VyY2UsIG9wdHMsIHtcbiAgICAgICAgb25Qcm9ncmVzczogKGYpID0+IHRoaXMucHJvZ3Jlc3MkLm5leHQoZiksXG4gICAgICAgIG9uU3RhdHVzOiAocykgPT4gdGhpcy5zdGF0dXMkLm5leHQocyksXG4gICAgICB9KTtcblxuICAgICAgLy8gVmlldyBwaXhlbHMgLT4gZnVsbC1yZXNvbHV0aW9uIGltYWdlIHBpeGVscy4gV2l0aG91dCB0aGlzIGV2ZXJ5IHJlZ2lvblxuICAgICAgLy8gd291bGQgc2l0IGF0IHZpZXctbG9jYWwgY29vcmRpbmF0ZXMsIGkuZS4gdGhlIHdyb25nIHBsYWNlIG9uIHRoZSBzbGlkZVxuICAgICAgLy8gZm9yIGFueSB6b29tZWQgb3IgcGFubmVkIHZpZXcuXG4gICAgICBjb25zdCBvZmZzZXRYID0gcmVjdCA/IHJlY3QueCA6IDA7XG4gICAgICBjb25zdCBvZmZzZXRZID0gcmVjdCA/IHJlY3QueSA6IDA7XG4gICAgICAvLyBBZ2FpbnN0IHRoZSBwaXhlbHMgYWN0dWFsbHkgaW5mZXJyZWQgb24sIHdoaWNoIG1heSBiZSB0aGUgcmUtY3JvcC5cbiAgICAgIGNvbnN0IHJhdGlvWCA9IHJlY3QgJiYgc291cmNlLndpZHRoID4gMCA/IHJlY3Qud2lkdGggLyBzb3VyY2Uud2lkdGggOiAxO1xuICAgICAgY29uc3QgcmF0aW9ZID0gcmVjdCAmJiBzb3VyY2UuaGVpZ2h0ID4gMCA/IHJlY3QuaGVpZ2h0IC8gc291cmNlLmhlaWdodCA6IDE7XG5cbiAgICAgIC8vIFZpZXcgcGl4ZWxzIC0+IGltYWdlIHBpeGVscywgZm9yIGFueSByaW5nLlxuICAgICAgY29uc3QgbWFwUmluZyA9IChyaW5nOiBBcnJheTxbbnVtYmVyLCBudW1iZXJdPik6IG51bWJlcltdW10gPT5cbiAgICAgICAgcmluZy5tYXAoKFt4LCB5XSkgPT4gW01hdGgucm91bmQob2Zmc2V0WCArIHggKiByYXRpb1gpLCBNYXRoLnJvdW5kKG9mZnNldFkgKyB5ICogcmF0aW9ZKV0pO1xuXG4gICAgICBjb25zdCBhZGRlZDogUmVnaW9uW10gPSBbXTtcbiAgICAgIGZvciAoY29uc3QgciBvZiByZXN1bHQucmVnaW9ucykge1xuICAgICAgICBpZiAoci5leHRlcmlvci5sZW5ndGggPCAzKSBjb250aW51ZTtcbiAgICAgICAgY29uc3QgZXh0ZXJpb3IgPSBtYXBSaW5nKHIuZXh0ZXJpb3IpO1xuICAgICAgICBhZGRlZC5wdXNoKFxuICAgICAgICAgIHRoaXMubWFrZVJlZ2lvbihcbiAgICAgICAgICAgIGV4dGVyaW9yLm1hcCgoW3hdKSA9PiB4KSxcbiAgICAgICAgICAgIGV4dGVyaW9yLm1hcCgoWywgeV0pID0+IHkpLFxuICAgICAgICAgICAgci5jbGFzc05hbWUsXG4gICAgICAgICAgICAvLyBIb2xlcyBhcmUgcmVhbCBmaW5kaW5ncyBoZXJlLCBub3Qgbm9pc2U6IGEgbGF5ZXIgd2l0aCBhIHZvaWRcbiAgICAgICAgICAgIC8vIHRocm91Z2ggaXQgaXMgYSBkb251dCwgYW5kIGRyb3BwaW5nIHRoZSBpbnRlcmlvciByaW5ncyBmaWxscyBpdFxuICAgICAgICAgICAgLy8gaW4gc2lsZW50bHkuIFJpbmdzIHRoYXQgc3Vydml2ZSBzaW1wbGlmaWNhdGlvbiB0byBmZXdlciB0aGFuIDNcbiAgICAgICAgICAgIC8vIHBvaW50cyBjYW5ub3QgYm91bmQgYW55dGhpbmcsIHNvIHRoZXkgYXJlIHNraXBwZWQgcmF0aGVyIHRoYW5cbiAgICAgICAgICAgIC8vIGVtaXR0ZWQgYXMgZGVnZW5lcmF0ZSBnZW9tZXRyeS5cbiAgICAgICAgICAgIHIuaG9sZXMuZmlsdGVyKChoKSA9PiBoLmxlbmd0aCA+PSAzKS5tYXAobWFwUmluZyksXG4gICAgICAgICAgKSxcbiAgICAgICAgKTtcbiAgICAgIH1cblxuICAgICAgaWYgKGFkZGVkLmxlbmd0aCA9PT0gMCkge1xuICAgICAgICAvLyBEaXN0aW5ndWlzaCBcImZvdW5kIG5vdGhpbmdcIiBmcm9tIFwid2FzIHVuc3VyZSBldmVyeXdoZXJlXCIuIFRoZSBzZWNvbmRcbiAgICAgICAgLy8gaXMgdGhlIHNpZ25hdHVyZSBvZiB3cm9uZyBwcmVwcm9jZXNzaW5nIG9yIHRoZSB3cm9uZyBtYWduaWZpY2F0aW9uLFxuICAgICAgICAvLyBhbmQgcmVhZHMgYXMgYSBzaWxlbnQgbm8tb3AgdW5sZXNzIGl0IGlzIHNhaWQgb3V0IGxvdWQuXG4gICAgICAgIHRoaXMuc3RhdHVzJC5uZXh0KFxuICAgICAgICAgIHJlc3VsdC51bmFzc2lnbmVkRnJhY3Rpb24gPiAwLjlcbiAgICAgICAgICAgID8gJ05vIGxheWVycyBmb3VuZCDigJQgdGhlIG1vZGVsIHdhcyB1bnN1cmUgYWNyb3NzIHRoZSB3aG9sZSB2aWV3LiAnICtcbiAgICAgICAgICAgICAgICAnQ2hlY2sgdGhlIG1hZ25pZmljYXRpb24gbWF0Y2hlcyB0aGUgbW9kZWwuJ1xuICAgICAgICAgICAgOiAnTm8gbGF5ZXJzIGZvdW5kLicsXG4gICAgICAgICk7XG4gICAgICAgIHJldHVybiAwO1xuICAgICAgfVxuXG4gICAgICAvLyBSZXBsYWNlIHRoaXMgdG9vbCdzIG93biBwcmV2aW91cyBvdXRwdXQsIGtlZXAgZXZlcnl0aGluZyBlbHNlLlxuICAgICAgLy8gSGFuZC1kcmF3biByZWdpb25zIGFuZCBvdGhlciB0b29scycgb3V0cHV0IGNhcnJ5IGEgZGlmZmVyZW50IGBzb3VyY2VgXG4gICAgICAvLyAob3Igbm9uZSkgYW5kIGFyZSB1bnRvdWNoZWQuXG4gICAgICBjb25zdCBrZXB0ID0gKHZpei5nZXRSZWdpb25zKCkgPz8gW10pLmZpbHRlcigoeCkgPT4geC5zb3VyY2UgIT09IFJFVElOQUxfUkVHSU9OX1NPVVJDRSk7XG4gICAgICB2aXouc2V0UmVnaW9ucyhbLi4ua2VwdCwgLi4uYWRkZWRdKTtcbiAgICAgIHRoaXMuc3RhdHVzJC5uZXh0KGBBZGRlZCAke2FkZGVkLmxlbmd0aH0gcmVnaW9uKHMpLmApO1xuICAgICAgcmV0dXJuIGFkZGVkLmxlbmd0aDtcbiAgICB9IGNhdGNoIChlcnIpIHtcbiAgICAgIHRoaXMuc3RhdHVzJC5uZXh0KGVyciBpbnN0YW5jZW9mIEVycm9yID8gZXJyLm1lc3NhZ2UgOiAnUmV0aW5hbCBsYXllciBzZWdtZW50YXRpb24gZmFpbGVkLicpO1xuICAgICAgcmV0dXJuIDA7XG4gICAgfSBmaW5hbGx5IHtcbiAgICAgIHRoaXMuYnVzeSQubmV4dChmYWxzZSk7XG4gICAgICB0aGlzLnByb2dyZXNzJC5uZXh0KC0xKTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogUmUtY3JvcCBgcmVjdGAgYXQgYDEvdGFyZ2V0YCBvZiBmdWxsIHJlc29sdXRpb24uIFJldHVybnMgbnVsbCBvbiBhbnlcbiAgICogZmFpbHVyZSwgc28gdGhlIGNhbGxlciBmYWxscyBiYWNrIHRvIHRoZSBkaXNwbGF5ZWQgcGl4ZWxzIOKAlCBhIGNvYXJzZXIgY3JvcFxuICAgKiBiZWF0cyBhIGZhaWxlZCBydW4uXG4gICAqL1xuICBwcml2YXRlIGFzeW5jIGZldGNoQ3JvcEF0U2NhbGUoXG4gICAgcmVjdDogeyB4OiBudW1iZXI7IHk6IG51bWJlcjsgd2lkdGg6IG51bWJlcjsgaGVpZ2h0OiBudW1iZXIgfSxcbiAgICB0YXJnZXQ6IG51bWJlcixcbiAgKTogUHJvbWlzZTx7IGRhdGE6IFVpbnQ4Q2xhbXBlZEFycmF5OyB3aWR0aDogbnVtYmVyOyBoZWlnaHQ6IG51bWJlciB9IHwgbnVsbD4ge1xuICAgIGlmICghdGhpcy50aWxlQWNjZXNzKSByZXR1cm4gbnVsbDtcbiAgICBsZXQgdyA9IE1hdGgucm91bmQocmVjdC53aWR0aCAvIHRhcmdldCk7XG4gICAgbGV0IGggPSBNYXRoLnJvdW5kKHJlY3QuaGVpZ2h0IC8gdGFyZ2V0KTtcblxuICAgIC8vIEEgZmFjdG9yIG9mIDEgb3ZlciBhIHdob2xlLXNsaWRlIFJPSSB3b3VsZCBhc2sgZm9yIGh1bmRyZWRzIG9mIG1lZ2FwaXhlbHMuXG4gICAgY29uc3QgTUFYX1BJWEVMUyA9IDE2Xzc3N18yMTY7IC8vIDQwOTbCslxuICAgIGNvbnN0IHBpeGVscyA9IHcgKiBoO1xuICAgIGlmIChwaXhlbHMgPiBNQVhfUElYRUxTKSB7XG4gICAgICBjb25zdCBrID0gTWF0aC5zcXJ0KE1BWF9QSVhFTFMgLyBwaXhlbHMpO1xuICAgICAgdyA9IE1hdGgucm91bmQodyAqIGspO1xuICAgICAgaCA9IE1hdGgucm91bmQoaCAqIGspO1xuICAgIH1cbiAgICBpZiAodyA8IDY0IHx8IGggPCA2NCkgcmV0dXJuIG51bGw7XG5cbiAgICB0cnkge1xuICAgICAgY29uc3Qgcm9pID0ge1xuICAgICAgICB4OiBNYXRoLnJvdW5kKHJlY3QueCksXG4gICAgICAgIHk6IE1hdGgucm91bmQocmVjdC55KSxcbiAgICAgICAgd2lkdGg6IE1hdGgucm91bmQocmVjdC53aWR0aCksXG4gICAgICAgIGhlaWdodDogTWF0aC5yb3VuZChyZWN0LmhlaWdodCksXG4gICAgICB9IGFzIFJlY3RhbmdsZTtcbiAgICAgIGNvbnN0IHNjcmVlbiA9IHsgeDogMCwgeTogMCwgd2lkdGg6IHcsIGhlaWdodDogaCB9IGFzIFJlY3RhbmdsZTtcbiAgICAgIGNvbnN0IGJ5dGVzID0gYXdhaXQgZmlyc3RWYWx1ZUZyb20odGhpcy50aWxlQWNjZXNzLnpvb21PblJlZ2lvbihyb2ksIHNjcmVlbiwgMCkpO1xuICAgICAgaWYgKCFieXRlcyB8fCBieXRlcy5ieXRlTGVuZ3RoID09PSAwKSByZXR1cm4gbnVsbDtcblxuICAgICAgY29uc3QgYml0bWFwID0gYXdhaXQgY3JlYXRlSW1hZ2VCaXRtYXAobmV3IEJsb2IoW2J5dGVzXSkpO1xuICAgICAgY29uc3QgY2FudmFzID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnY2FudmFzJyk7XG4gICAgICBjYW52YXMud2lkdGggPSBiaXRtYXAud2lkdGg7XG4gICAgICBjYW52YXMuaGVpZ2h0ID0gYml0bWFwLmhlaWdodDtcbiAgICAgIGNvbnN0IGN0eCA9IGNhbnZhcy5nZXRDb250ZXh0KCcyZCcpO1xuICAgICAgaWYgKCFjdHgpIHJldHVybiBudWxsO1xuICAgICAgY3R4LmRyYXdJbWFnZShiaXRtYXAsIDAsIDApO1xuICAgICAgLy8gUmVhZCB0aGUgZGltZW5zaW9ucyBCRUZPUkUgY2xvc2UoKTogY2xvc2luZyBhbiBJbWFnZUJpdG1hcCB6ZXJvZXMgaXRzXG4gICAgICAvLyB3aWR0aC9oZWlnaHQsIHNvIHJldHVybmluZyB0aGVtIGFmdGVyd2FyZHMgeWllbGRzIGEgMHgwIGltYWdlIHdpdGggYVxuICAgICAgLy8gZnVsbCBwaXhlbCBidWZmZXIg4oCUIGluZmVyZW5jZSB0aGVuIHJ1bnMgb24gbm90aGluZyBhbmQgZmluZHMgbm90aGluZy5cbiAgICAgIGNvbnN0IHdpZHRoID0gYml0bWFwLndpZHRoO1xuICAgICAgY29uc3QgaGVpZ2h0ID0gYml0bWFwLmhlaWdodDtcbiAgICAgIGNvbnN0IG91dCA9IGN0eC5nZXRJbWFnZURhdGEoMCwgMCwgd2lkdGgsIGhlaWdodCk7XG4gICAgICBiaXRtYXAuY2xvc2U/LigpO1xuICAgICAgcmV0dXJuIHsgZGF0YTogb3V0LmRhdGEsIHdpZHRoLCBoZWlnaHQgfTtcbiAgICB9IGNhdGNoIHtcbiAgICAgIHJldHVybiBudWxsO1xuICAgIH1cbiAgfVxuXG4gIHByaXZhdGUgbWFrZVJlZ2lvbih4czogbnVtYmVyW10sIHlzOiBudW1iZXJbXSwgY2xhc3NOYW1lOiBzdHJpbmcsIGhvbGVzOiBudW1iZXJbXVtdW10gPSBbXSk6IFJlZ2lvbiB7XG4gICAgY29uc3QgcG9seSA9IG5ldyBQb2x5Z29uKCk7XG4gICAgcG9seS5ucG9pbnRzID0geHMubGVuZ3RoO1xuICAgIHBvbHkueHBvaW50cyA9IHhzO1xuICAgIHBvbHkueXBvaW50cyA9IHlzO1xuICAgIHBvbHkuY29vcmRpbmF0ZXMgPSB4cy5tYXAoKHgsIGkpID0+IFt4LCB5c1tpXV0pO1xuICAgIHBvbHkuY2xvc2VkID0gdHJ1ZTtcbiAgICAvLyBTYW1lIGNsb3NlZC1yaW5nIGNvbnZlbnRpb24gYXMgdGhlIGV4dGVyaW9yOiBubyByZXBlYXRlZCBjbG9zaW5nIHBvaW50LlxuICAgIC8vIExlZnQgdW5zZXQgd2hlbiBlbXB0eSBzbyBhIHNvbGlkIHJlZ2lvbiBkb2VzIG5vdCBjYXJyeSBhbiBlbXB0eSBhcnJheVxuICAgIC8vIHRocm91Z2ggZXhwb3J0IGFuZCByb3VuZC10cmlwcGluZy5cbiAgICBpZiAoaG9sZXMubGVuZ3RoKSBwb2x5LmhvbGVzID0gaG9sZXM7XG5cbiAgICBjb25zdCByZWdpb24gPSBuZXcgUmVnaW9uKCk7XG4gICAgcmVnaW9uLmJvdW5kcyA9IHBvbHk7XG4gICAgcmVnaW9uLnNvdXJjZSA9IFJFVElOQUxfUkVHSU9OX1NPVVJDRTtcbiAgICAvLyBUaGUgY2xhc3MgbmFtZSBkcml2ZXMgcHJlc2V0IGNvbG91ciByZXNvbHV0aW9uOyBsZWF2aW5nIGBjb2xvcmAgdW5zZXQgbGV0c1xuICAgIC8vIGFuIGV4aXN0aW5nIHByZXNldCBmb3IgdGhpcyBsYXllciB3aW4uXG4gICAgcmVnaW9uLmxhYmVsID0gY2xhc3NOYW1lO1xuICAgIHJldHVybiByZWdpb247XG4gIH1cbn1cbiJdfQ==
@@ -0,0 +1,119 @@
1
+ /**
2
+ * Registry of retinal-layer semantic-segmentation models.
3
+ *
4
+ * Mirrors {@link YOLO_MODELS}: public HF-hosted ONNX by default, repointable at
5
+ * private hosting via {@link setRetinalModelUrls}.
6
+ *
7
+ * ONLY ONE CHECKPOINT IS ENABLED, AND THAT IS DELIBERATE
8
+ * The server offers four. Measured against the ground-truth masks shipped
9
+ * alongside them, only VNet reproduces them:
10
+ *
11
+ * | checkpoint | mIoU |
12
+ * |---|---|
13
+ * | **VNet 2D** | **0.9061** |
14
+ * | ResUNet-a 2D (base) | 0.3065 |
15
+ * | ResUNet-a 2D 20x | 0.2869 |
16
+ * | ResUNet-a 2D 40x | 0.2718 |
17
+ *
18
+ * The ResUNet-a numbers are not quantization damage — their ONNX exports match
19
+ * their own Keras originals exactly. They disagree with the *masks*, folding
20
+ * ground-truth class 1 into their class 2 and class 3 into background, and no
21
+ * relabelling rescues them (the best of all 24 class permutations reaches only
22
+ * 0.36). Either those masks belong to a different model generation or the class
23
+ * definitions moved — an open question for whoever trained them.
24
+ *
25
+ * So their weights stay unpublished (their Hub repos are private) and their
26
+ * entries here carry an empty `modelUrl`, which {@link isRetinalModelReady}
27
+ * reports as not-ready. They are listed rather than deleted so a host shows
28
+ * them as unavailable-in-browser instead of pretending the server's choice does
29
+ * not exist — and so enabling one later is a URL, not a code change.
30
+ *
31
+ * Export tooling: the sibling `browser-onnx-tools` project.
32
+ */
33
+ const HF = 'https://huggingface.co/Ballon999';
34
+ const RESUNET_UNAVAILABLE = 'Not available in the browser: this checkpoint scores mIoU ~0.3 against its own ' +
35
+ 'ground-truth masks, pending a review of its class definitions. Run this step on the server.';
36
+ export const RETINAL_MODELS = [
37
+ {
38
+ id: 'vnet-2d-retinal',
39
+ label: 'VNet 2D (40x)',
40
+ modelUrl: `${HF}/vnet-2d-retinal-layer-onnx/resolve/main/model.fp16w.onnx`,
41
+ patchSize: 512,
42
+ // Nearly 6x the YOLO checkpoints. Worth surfacing before a user on a
43
+ // metered connection triggers it by clicking a toolbar button.
44
+ sizeMb: 590,
45
+ miou: 0.9061,
46
+ },
47
+ {
48
+ id: 'resunet-a-2d-retinal',
49
+ label: 'ResUNet-a 2D (40x)',
50
+ modelUrl: '',
51
+ patchSize: 512,
52
+ sizeMb: 102,
53
+ miou: 0.3065,
54
+ unavailableReason: RESUNET_UNAVAILABLE,
55
+ },
56
+ {
57
+ id: 'resunet-a-2d-retinal-20x',
58
+ label: 'ResUNet-a 2D (20x)',
59
+ modelUrl: '',
60
+ patchSize: 256,
61
+ sizeMb: 102,
62
+ miou: 0.2869,
63
+ unavailableReason: RESUNET_UNAVAILABLE,
64
+ },
65
+ {
66
+ id: 'resunet-a-2d-retinal-40x',
67
+ label: 'ResUNet-a 2D (40x, alt)',
68
+ modelUrl: '',
69
+ patchSize: 512,
70
+ sizeMb: 102,
71
+ miou: 0.2718,
72
+ unavailableReason: RESUNET_UNAVAILABLE,
73
+ },
74
+ ];
75
+ export const DEFAULT_RETINAL_MODEL_ID = 'vnet-2d-retinal';
76
+ let defaultModelId = DEFAULT_RETINAL_MODEL_ID;
77
+ /** Look up a model definition by id. Returns undefined for an unknown id. */
78
+ export function getRetinalModel(id) {
79
+ return RETINAL_MODELS.find((m) => m.id === id);
80
+ }
81
+ /** Whether a model has usable weights configured. */
82
+ export function isRetinalModelReady(id) {
83
+ const m = getRetinalModel(id);
84
+ return !!m && m.modelUrl.trim().length > 0;
85
+ }
86
+ /** Only the models a browser can actually run. */
87
+ export function readyRetinalModels() {
88
+ return RETINAL_MODELS.filter((m) => m.modelUrl.trim().length > 0);
89
+ }
90
+ /**
91
+ * Repoint a model at different hosting (a GCS bucket, a same-origin proxy), or
92
+ * enable one of the disabled entries. Call once at app init, before first use.
93
+ * An empty `modelUrl` disables the model.
94
+ */
95
+ export function setRetinalModelUrls(id, urls) {
96
+ const m = getRetinalModel(id);
97
+ if (!m)
98
+ return;
99
+ if (urls.modelUrl !== undefined)
100
+ m.modelUrl = urls.modelUrl;
101
+ if (urls.metaUrl !== undefined)
102
+ m.metaUrl = urls.metaUrl;
103
+ // A host that supplies weights has answered the question the reason
104
+ // describes. Decided from the model's post-update state rather than from the
105
+ // argument: a whitespace-only url, or a call that sets only `metaUrl`, leaves
106
+ // the model just as disabled — and clearing the reason there would strip the
107
+ // one explanation the user gets while `isRetinalModelReady` still says no.
108
+ if (isRetinalModelReady(id))
109
+ delete m.unavailableReason;
110
+ }
111
+ /** Change which model runs when a caller does not name one. */
112
+ export function setDefaultRetinalModel(id) {
113
+ if (getRetinalModel(id))
114
+ defaultModelId = id;
115
+ }
116
+ export function getDefaultRetinalModelId() {
117
+ return defaultModelId;
118
+ }
119
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmV0aW5hbC1tb2RlbC1yZWdpc3RyeS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uL3NyYy9saWIvdG9vbGJhci9zZWdtZW50YXRpb24vcmV0aW5hbC1tb2RlbC1yZWdpc3RyeS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQStCRztBQXNCSCxNQUFNLEVBQUUsR0FBRyxrQ0FBa0MsQ0FBQztBQUU5QyxNQUFNLG1CQUFtQixHQUN2QixpRkFBaUY7SUFDakYsNkZBQTZGLENBQUM7QUFFaEcsTUFBTSxDQUFDLE1BQU0sY0FBYyxHQUFzQjtJQUMvQztRQUNFLEVBQUUsRUFBRSxpQkFBaUI7UUFDckIsS0FBSyxFQUFFLGVBQWU7UUFDdEIsUUFBUSxFQUFFLEdBQUcsRUFBRSwyREFBMkQ7UUFDMUUsU0FBUyxFQUFFLEdBQUc7UUFDZCxxRUFBcUU7UUFDckUsK0RBQStEO1FBQy9ELE1BQU0sRUFBRSxHQUFHO1FBQ1gsSUFBSSxFQUFFLE1BQU07S0FDYjtJQUNEO1FBQ0UsRUFBRSxFQUFFLHNCQUFzQjtRQUMxQixLQUFLLEVBQUUsb0JBQW9CO1FBQzNCLFFBQVEsRUFBRSxFQUFFO1FBQ1osU0FBUyxFQUFFLEdBQUc7UUFDZCxNQUFNLEVBQUUsR0FBRztRQUNYLElBQUksRUFBRSxNQUFNO1FBQ1osaUJBQWlCLEVBQUUsbUJBQW1CO0tBQ3ZDO0lBQ0Q7UUFDRSxFQUFFLEVBQUUsMEJBQTBCO1FBQzlCLEtBQUssRUFBRSxvQkFBb0I7UUFDM0IsUUFBUSxFQUFFLEVBQUU7UUFDWixTQUFTLEVBQUUsR0FBRztRQUNkLE1BQU0sRUFBRSxHQUFHO1FBQ1gsSUFBSSxFQUFFLE1BQU07UUFDWixpQkFBaUIsRUFBRSxtQkFBbUI7S0FDdkM7SUFDRDtRQUNFLEVBQUUsRUFBRSwwQkFBMEI7UUFDOUIsS0FBSyxFQUFFLHlCQUF5QjtRQUNoQyxRQUFRLEVBQUUsRUFBRTtRQUNaLFNBQVMsRUFBRSxHQUFHO1FBQ2QsTUFBTSxFQUFFLEdBQUc7UUFDWCxJQUFJLEVBQUUsTUFBTTtRQUNaLGlCQUFpQixFQUFFLG1CQUFtQjtLQUN2QztDQUNGLENBQUM7QUFFRixNQUFNLENBQUMsTUFBTSx3QkFBd0IsR0FBRyxpQkFBaUIsQ0FBQztBQUUxRCxJQUFJLGNBQWMsR0FBRyx3QkFBd0IsQ0FBQztBQUU5Qyw2RUFBNkU7QUFDN0UsTUFBTSxVQUFVLGVBQWUsQ0FBQyxFQUFVO0lBQ3hDLE9BQU8sY0FBYyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsRUFBRSxFQUFFLENBQUMsQ0FBQyxDQUFDLEVBQUUsS0FBSyxFQUFFLENBQUMsQ0FBQztBQUNqRCxDQUFDO0FBRUQscURBQXFEO0FBQ3JELE1BQU0sVUFBVSxtQkFBbUIsQ0FBQyxFQUFVO0lBQzVDLE1BQU0sQ0FBQyxHQUFHLGVBQWUsQ0FBQyxFQUFFLENBQUMsQ0FBQztJQUM5QixPQUFPLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxDQUFDLFFBQVEsQ0FBQyxJQUFJLEVBQUUsQ0FBQyxNQUFNLEdBQUcsQ0FBQyxDQUFDO0FBQzdDLENBQUM7QUFFRCxrREFBa0Q7QUFDbEQsTUFBTSxVQUFVLGtCQUFrQjtJQUNoQyxPQUFPLGNBQWMsQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDLEVBQUUsRUFBRSxDQUFDLENBQUMsQ0FBQyxRQUFRLENBQUMsSUFBSSxFQUFFLENBQUMsTUFBTSxHQUFHLENBQUMsQ0FBQyxDQUFDO0FBQ3BFLENBQUM7QUFFRDs7OztHQUlHO0FBQ0gsTUFBTSxVQUFVLG1CQUFtQixDQUFDLEVBQVUsRUFBRSxJQUE2QztJQUMzRixNQUFNLENBQUMsR0FBRyxlQUFlLENBQUMsRUFBRSxDQUFDLENBQUM7SUFDOUIsSUFBSSxDQUFDLENBQUM7UUFBRSxPQUFPO0lBQ2YsSUFBSSxJQUFJLENBQUMsUUFBUSxLQUFLLFNBQVM7UUFBRSxDQUFDLENBQUMsUUFBUSxHQUFHLElBQUksQ0FBQyxRQUFRLENBQUM7SUFDNUQsSUFBSSxJQUFJLENBQUMsT0FBTyxLQUFLLFNBQVM7UUFBRSxDQUFDLENBQUMsT0FBTyxHQUFHLElBQUksQ0FBQyxPQUFPLENBQUM7SUFDekQsb0VBQW9FO0lBQ3BFLDZFQUE2RTtJQUM3RSw4RUFBOEU7SUFDOUUsNkVBQTZFO0lBQzdFLDJFQUEyRTtJQUMzRSxJQUFJLG1CQUFtQixDQUFDLEVBQUUsQ0FBQztRQUFFLE9BQU8sQ0FBQyxDQUFDLGlCQUFpQixDQUFDO0FBQzFELENBQUM7QUFFRCwrREFBK0Q7QUFDL0QsTUFBTSxVQUFVLHNCQUFzQixDQUFDLEVBQVU7SUFDL0MsSUFBSSxlQUFlLENBQUMsRUFBRSxDQUFDO1FBQUUsY0FBYyxHQUFHLEVBQUUsQ0FBQztBQUMvQyxDQUFDO0FBRUQsTUFBTSxVQUFVLHdCQUF3QjtJQUN0QyxPQUFPLGNBQWMsQ0FBQztBQUN4QixDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBSZWdpc3RyeSBvZiByZXRpbmFsLWxheWVyIHNlbWFudGljLXNlZ21lbnRhdGlvbiBtb2RlbHMuXG4gKlxuICogTWlycm9ycyB7QGxpbmsgWU9MT19NT0RFTFN9OiBwdWJsaWMgSEYtaG9zdGVkIE9OTlggYnkgZGVmYXVsdCwgcmVwb2ludGFibGUgYXRcbiAqIHByaXZhdGUgaG9zdGluZyB2aWEge0BsaW5rIHNldFJldGluYWxNb2RlbFVybHN9LlxuICpcbiAqIE9OTFkgT05FIENIRUNLUE9JTlQgSVMgRU5BQkxFRCwgQU5EIFRIQVQgSVMgREVMSUJFUkFURVxuICogVGhlIHNlcnZlciBvZmZlcnMgZm91ci4gTWVhc3VyZWQgYWdhaW5zdCB0aGUgZ3JvdW5kLXRydXRoIG1hc2tzIHNoaXBwZWRcbiAqIGFsb25nc2lkZSB0aGVtLCBvbmx5IFZOZXQgcmVwcm9kdWNlcyB0aGVtOlxuICpcbiAqIHwgY2hlY2twb2ludCB8IG1Jb1UgfFxuICogfC0tLXwtLS18XG4gKiB8ICoqVk5ldCAyRCoqIHwgKiowLjkwNjEqKiB8XG4gKiB8IFJlc1VOZXQtYSAyRCAoYmFzZSkgfCAwLjMwNjUgfFxuICogfCBSZXNVTmV0LWEgMkQgMjB4IHwgMC4yODY5IHxcbiAqIHwgUmVzVU5ldC1hIDJEIDQweCB8IDAuMjcxOCB8XG4gKlxuICogVGhlIFJlc1VOZXQtYSBudW1iZXJzIGFyZSBub3QgcXVhbnRpemF0aW9uIGRhbWFnZSDigJQgdGhlaXIgT05OWCBleHBvcnRzIG1hdGNoXG4gKiB0aGVpciBvd24gS2VyYXMgb3JpZ2luYWxzIGV4YWN0bHkuIFRoZXkgZGlzYWdyZWUgd2l0aCB0aGUgKm1hc2tzKiwgZm9sZGluZ1xuICogZ3JvdW5kLXRydXRoIGNsYXNzIDEgaW50byB0aGVpciBjbGFzcyAyIGFuZCBjbGFzcyAzIGludG8gYmFja2dyb3VuZCwgYW5kIG5vXG4gKiByZWxhYmVsbGluZyByZXNjdWVzIHRoZW0gKHRoZSBiZXN0IG9mIGFsbCAyNCBjbGFzcyBwZXJtdXRhdGlvbnMgcmVhY2hlcyBvbmx5XG4gKiAwLjM2KS4gRWl0aGVyIHRob3NlIG1hc2tzIGJlbG9uZyB0byBhIGRpZmZlcmVudCBtb2RlbCBnZW5lcmF0aW9uIG9yIHRoZSBjbGFzc1xuICogZGVmaW5pdGlvbnMgbW92ZWQg4oCUIGFuIG9wZW4gcXVlc3Rpb24gZm9yIHdob2V2ZXIgdHJhaW5lZCB0aGVtLlxuICpcbiAqIFNvIHRoZWlyIHdlaWdodHMgc3RheSB1bnB1Ymxpc2hlZCAodGhlaXIgSHViIHJlcG9zIGFyZSBwcml2YXRlKSBhbmQgdGhlaXJcbiAqIGVudHJpZXMgaGVyZSBjYXJyeSBhbiBlbXB0eSBgbW9kZWxVcmxgLCB3aGljaCB7QGxpbmsgaXNSZXRpbmFsTW9kZWxSZWFkeX1cbiAqIHJlcG9ydHMgYXMgbm90LXJlYWR5LiBUaGV5IGFyZSBsaXN0ZWQgcmF0aGVyIHRoYW4gZGVsZXRlZCBzbyBhIGhvc3Qgc2hvd3NcbiAqIHRoZW0gYXMgdW5hdmFpbGFibGUtaW4tYnJvd3NlciBpbnN0ZWFkIG9mIHByZXRlbmRpbmcgdGhlIHNlcnZlcidzIGNob2ljZSBkb2VzXG4gKiBub3QgZXhpc3Qg4oCUIGFuZCBzbyBlbmFibGluZyBvbmUgbGF0ZXIgaXMgYSBVUkwsIG5vdCBhIGNvZGUgY2hhbmdlLlxuICpcbiAqIEV4cG9ydCB0b29saW5nOiB0aGUgc2libGluZyBgYnJvd3Nlci1vbm54LXRvb2xzYCBwcm9qZWN0LlxuICovXG5cbmV4cG9ydCBpbnRlcmZhY2UgUmV0aW5hbE1vZGVsRGVmIHtcbiAgaWQ6IHN0cmluZztcbiAgbGFiZWw6IHN0cmluZztcbiAgLyoqIFVSTCBvZiB0aGUgYC5vbm54YCB3ZWlnaHRzLiAqKkVtcHR5IGRpc2FibGVzIHRoZSBtb2RlbC4qKiAqL1xuICBtb2RlbFVybDogc3RyaW5nO1xuICAvKipcbiAgICogVVJMIG9mIHRoZSBgbW9kZWwuanNvbmAgc2lkZWNhci4gT21pdCB0byB1c2UgYG1vZGVsLmpzb25gIGFsb25nc2lkZSB0aGVcbiAgICogd2VpZ2h0cywgd2hpY2ggaXMgaG93IGV2ZXJ5IHB1Ymxpc2hlZCByZXBvIGlzIGxhaWQgb3V0LlxuICAgKi9cbiAgbWV0YVVybD86IHN0cmluZztcbiAgLyoqIFBhdGNoIHNpemUgdGhlIGNoZWNrcG9pbnQgd2FzIHRyYWluZWQgYXQsIGZvciBjYWxsZXJzIHRoYXQgc2l6ZSByZWFkcyB0byBpdC4gKi9cbiAgcGF0Y2hTaXplOiBudW1iZXI7XG4gIC8qKiBEb3dubG9hZCBzaXplIG9mIHRoZSBgZnAxNndgIHdlaWdodHMsIE1CIOKAlCB0aGVzZSBhcmUgbGFyZ2UgZW5vdWdoIHRvIHdhcm4gYWJvdXQuICovXG4gIHNpemVNYjogbnVtYmVyO1xuICAvKiogTWVhc3VyZWQgbUlvVSBhZ2FpbnN0IHRoZSBidWNrZXQncyBncm91bmQtdHJ1dGggbWFza3MuICovXG4gIG1pb3U6IG51bWJlcjtcbiAgLyoqIFNldCB3aGVuIHRoZSBtb2RlbCBpcyBrbm93aW5nbHkgZGlzYWJsZWQ7IHNob3duIHRvIHRoZSB1c2VyLiAqL1xuICB1bmF2YWlsYWJsZVJlYXNvbj86IHN0cmluZztcbn1cblxuY29uc3QgSEYgPSAnaHR0cHM6Ly9odWdnaW5nZmFjZS5jby9CYWxsb245OTknO1xuXG5jb25zdCBSRVNVTkVUX1VOQVZBSUxBQkxFID1cbiAgJ05vdCBhdmFpbGFibGUgaW4gdGhlIGJyb3dzZXI6IHRoaXMgY2hlY2twb2ludCBzY29yZXMgbUlvVSB+MC4zIGFnYWluc3QgaXRzIG93biAnICtcbiAgJ2dyb3VuZC10cnV0aCBtYXNrcywgcGVuZGluZyBhIHJldmlldyBvZiBpdHMgY2xhc3MgZGVmaW5pdGlvbnMuIFJ1biB0aGlzIHN0ZXAgb24gdGhlIHNlcnZlci4nO1xuXG5leHBvcnQgY29uc3QgUkVUSU5BTF9NT0RFTFM6IFJldGluYWxNb2RlbERlZltdID0gW1xuICB7XG4gICAgaWQ6ICd2bmV0LTJkLXJldGluYWwnLFxuICAgIGxhYmVsOiAnVk5ldCAyRCAoNDB4KScsXG4gICAgbW9kZWxVcmw6IGAke0hGfS92bmV0LTJkLXJldGluYWwtbGF5ZXItb25ueC9yZXNvbHZlL21haW4vbW9kZWwuZnAxNncub25ueGAsXG4gICAgcGF0Y2hTaXplOiA1MTIsXG4gICAgLy8gTmVhcmx5IDZ4IHRoZSBZT0xPIGNoZWNrcG9pbnRzLiBXb3J0aCBzdXJmYWNpbmcgYmVmb3JlIGEgdXNlciBvbiBhXG4gICAgLy8gbWV0ZXJlZCBjb25uZWN0aW9uIHRyaWdnZXJzIGl0IGJ5IGNsaWNraW5nIGEgdG9vbGJhciBidXR0b24uXG4gICAgc2l6ZU1iOiA1OTAsXG4gICAgbWlvdTogMC45MDYxLFxuICB9LFxuICB7XG4gICAgaWQ6ICdyZXN1bmV0LWEtMmQtcmV0aW5hbCcsXG4gICAgbGFiZWw6ICdSZXNVTmV0LWEgMkQgKDQweCknLFxuICAgIG1vZGVsVXJsOiAnJyxcbiAgICBwYXRjaFNpemU6IDUxMixcbiAgICBzaXplTWI6IDEwMixcbiAgICBtaW91OiAwLjMwNjUsXG4gICAgdW5hdmFpbGFibGVSZWFzb246IFJFU1VORVRfVU5BVkFJTEFCTEUsXG4gIH0sXG4gIHtcbiAgICBpZDogJ3Jlc3VuZXQtYS0yZC1yZXRpbmFsLTIweCcsXG4gICAgbGFiZWw6ICdSZXNVTmV0LWEgMkQgKDIweCknLFxuICAgIG1vZGVsVXJsOiAnJyxcbiAgICBwYXRjaFNpemU6IDI1NixcbiAgICBzaXplTWI6IDEwMixcbiAgICBtaW91OiAwLjI4NjksXG4gICAgdW5hdmFpbGFibGVSZWFzb246IFJFU1VORVRfVU5BVkFJTEFCTEUsXG4gIH0sXG4gIHtcbiAgICBpZDogJ3Jlc3VuZXQtYS0yZC1yZXRpbmFsLTQweCcsXG4gICAgbGFiZWw6ICdSZXNVTmV0LWEgMkQgKDQweCwgYWx0KScsXG4gICAgbW9kZWxVcmw6ICcnLFxuICAgIHBhdGNoU2l6ZTogNTEyLFxuICAgIHNpemVNYjogMTAyLFxuICAgIG1pb3U6IDAuMjcxOCxcbiAgICB1bmF2YWlsYWJsZVJlYXNvbjogUkVTVU5FVF9VTkFWQUlMQUJMRSxcbiAgfSxcbl07XG5cbmV4cG9ydCBjb25zdCBERUZBVUxUX1JFVElOQUxfTU9ERUxfSUQgPSAndm5ldC0yZC1yZXRpbmFsJztcblxubGV0IGRlZmF1bHRNb2RlbElkID0gREVGQVVMVF9SRVRJTkFMX01PREVMX0lEO1xuXG4vKiogTG9vayB1cCBhIG1vZGVsIGRlZmluaXRpb24gYnkgaWQuIFJldHVybnMgdW5kZWZpbmVkIGZvciBhbiB1bmtub3duIGlkLiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGdldFJldGluYWxNb2RlbChpZDogc3RyaW5nKTogUmV0aW5hbE1vZGVsRGVmIHwgdW5kZWZpbmVkIHtcbiAgcmV0dXJuIFJFVElOQUxfTU9ERUxTLmZpbmQoKG0pID0+IG0uaWQgPT09IGlkKTtcbn1cblxuLyoqIFdoZXRoZXIgYSBtb2RlbCBoYXMgdXNhYmxlIHdlaWdodHMgY29uZmlndXJlZC4gKi9cbmV4cG9ydCBmdW5jdGlvbiBpc1JldGluYWxNb2RlbFJlYWR5KGlkOiBzdHJpbmcpOiBib29sZWFuIHtcbiAgY29uc3QgbSA9IGdldFJldGluYWxNb2RlbChpZCk7XG4gIHJldHVybiAhIW0gJiYgbS5tb2RlbFVybC50cmltKCkubGVuZ3RoID4gMDtcbn1cblxuLyoqIE9ubHkgdGhlIG1vZGVscyBhIGJyb3dzZXIgY2FuIGFjdHVhbGx5IHJ1bi4gKi9cbmV4cG9ydCBmdW5jdGlvbiByZWFkeVJldGluYWxNb2RlbHMoKTogUmV0aW5hbE1vZGVsRGVmW10ge1xuICByZXR1cm4gUkVUSU5BTF9NT0RFTFMuZmlsdGVyKChtKSA9PiBtLm1vZGVsVXJsLnRyaW0oKS5sZW5ndGggPiAwKTtcbn1cblxuLyoqXG4gKiBSZXBvaW50IGEgbW9kZWwgYXQgZGlmZmVyZW50IGhvc3RpbmcgKGEgR0NTIGJ1Y2tldCwgYSBzYW1lLW9yaWdpbiBwcm94eSksIG9yXG4gKiBlbmFibGUgb25lIG9mIHRoZSBkaXNhYmxlZCBlbnRyaWVzLiBDYWxsIG9uY2UgYXQgYXBwIGluaXQsIGJlZm9yZSBmaXJzdCB1c2UuXG4gKiBBbiBlbXB0eSBgbW9kZWxVcmxgIGRpc2FibGVzIHRoZSBtb2RlbC5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHNldFJldGluYWxNb2RlbFVybHMoaWQ6IHN0cmluZywgdXJsczogeyBtb2RlbFVybD86IHN0cmluZzsgbWV0YVVybD86IHN0cmluZyB9KTogdm9pZCB7XG4gIGNvbnN0IG0gPSBnZXRSZXRpbmFsTW9kZWwoaWQpO1xuICBpZiAoIW0pIHJldHVybjtcbiAgaWYgKHVybHMubW9kZWxVcmwgIT09IHVuZGVmaW5lZCkgbS5tb2RlbFVybCA9IHVybHMubW9kZWxVcmw7XG4gIGlmICh1cmxzLm1ldGFVcmwgIT09IHVuZGVmaW5lZCkgbS5tZXRhVXJsID0gdXJscy5tZXRhVXJsO1xuICAvLyBBIGhvc3QgdGhhdCBzdXBwbGllcyB3ZWlnaHRzIGhhcyBhbnN3ZXJlZCB0aGUgcXVlc3Rpb24gdGhlIHJlYXNvblxuICAvLyBkZXNjcmliZXMuIERlY2lkZWQgZnJvbSB0aGUgbW9kZWwncyBwb3N0LXVwZGF0ZSBzdGF0ZSByYXRoZXIgdGhhbiBmcm9tIHRoZVxuICAvLyBhcmd1bWVudDogYSB3aGl0ZXNwYWNlLW9ubHkgdXJsLCBvciBhIGNhbGwgdGhhdCBzZXRzIG9ubHkgYG1ldGFVcmxgLCBsZWF2ZXNcbiAgLy8gdGhlIG1vZGVsIGp1c3QgYXMgZGlzYWJsZWQg4oCUIGFuZCBjbGVhcmluZyB0aGUgcmVhc29uIHRoZXJlIHdvdWxkIHN0cmlwIHRoZVxuICAvLyBvbmUgZXhwbGFuYXRpb24gdGhlIHVzZXIgZ2V0cyB3aGlsZSBgaXNSZXRpbmFsTW9kZWxSZWFkeWAgc3RpbGwgc2F5cyBuby5cbiAgaWYgKGlzUmV0aW5hbE1vZGVsUmVhZHkoaWQpKSBkZWxldGUgbS51bmF2YWlsYWJsZVJlYXNvbjtcbn1cblxuLyoqIENoYW5nZSB3aGljaCBtb2RlbCBydW5zIHdoZW4gYSBjYWxsZXIgZG9lcyBub3QgbmFtZSBvbmUuICovXG5leHBvcnQgZnVuY3Rpb24gc2V0RGVmYXVsdFJldGluYWxNb2RlbChpZDogc3RyaW5nKTogdm9pZCB7XG4gIGlmIChnZXRSZXRpbmFsTW9kZWwoaWQpKSBkZWZhdWx0TW9kZWxJZCA9IGlkO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gZ2V0RGVmYXVsdFJldGluYWxNb2RlbElkKCk6IHN0cmluZyB7XG4gIHJldHVybiBkZWZhdWx0TW9kZWxJZDtcbn1cbiJdfQ==