lightning-pose-app 1.8.1a3__py3-none-any.whl → 1.8.1a4__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (28) hide show
  1. lightning_pose_app-1.8.1a4.dist-info/METADATA +20 -0
  2. lightning_pose_app-1.8.1a4.dist-info/RECORD +29 -0
  3. litpose_app/config.py +22 -0
  4. litpose_app/deps.py +77 -0
  5. litpose_app/main.py +158 -300
  6. litpose_app/ngdist/ng_app/3rdpartylicenses.txt +11 -11
  7. litpose_app/ngdist/ng_app/index.html +3 -2
  8. litpose_app/ngdist/ng_app/{main-LJHMLKBL.js → main-VCJFCLFP.js} +221 -148
  9. litpose_app/ngdist/ng_app/main-VCJFCLFP.js.map +1 -0
  10. litpose_app/ngdist/ng_app/{styles-4V6RXJMC.css → styles-ZM27COY6.css} +11 -1
  11. litpose_app/ngdist/ng_app/styles-ZM27COY6.css.map +7 -0
  12. litpose_app/{run_ffprobe.py → routes/ffprobe.py} +164 -132
  13. litpose_app/{super_rglob.py → routes/files.py} +108 -48
  14. litpose_app/routes/project.py +72 -0
  15. litpose_app/routes/transcode.py +67 -0
  16. litpose_app/tasks/__init__.py +0 -0
  17. litpose_app/tasks/management.py +2 -0
  18. litpose_app/tasks/transcode_fine.py +7 -0
  19. litpose_app/transcode_fine.py +175 -0
  20. lightning_pose_app-1.8.1a3.dist-info/METADATA +0 -15
  21. lightning_pose_app-1.8.1a3.dist-info/RECORD +0 -21
  22. litpose_app/ngdist/ng_app/main-LJHMLKBL.js.map +0 -1
  23. litpose_app/ngdist/ng_app/styles-4V6RXJMC.css.map +0 -7
  24. {lightning_pose_app-1.8.1a3.dist-info → lightning_pose_app-1.8.1a4.dist-info}/WHEEL +0 -0
  25. /litpose_app/ngdist/ng_app/{app.component-UHVEDPZR.css.map → app.component-UAQUAGNZ.css.map} +0 -0
  26. /litpose_app/ngdist/ng_app/{project-settings.component-5IRK7U7U.css.map → project-settings.component-HKHIVUJR.css.map} +0 -0
  27. /litpose_app/ngdist/ng_app/{video-tile.component-XSYKMARQ.css.map → video-tile.component-RDL4BSJ4.css.map} +0 -0
  28. /litpose_app/ngdist/ng_app/{viewer-page.component-MRTIUFL2.css.map → viewer-page.component-KDHT6XH5.css.map} +0 -0
@@ -329,6 +329,7 @@
329
329
  --color-green-400: oklch(79.2% 0.209 151.711);
330
330
  --color-sky-100: oklch(95.1% 0.026 236.824);
331
331
  --color-sky-700: oklch(50% 0.134 242.749);
332
+ --color-gray-400: oklch(70.7% 0.022 261.325);
332
333
  --color-black: #000;
333
334
  --spacing: 0.25rem;
334
335
  --container-xs: 20rem;
@@ -444,8 +445,8 @@
444
445
  }
445
446
  }
446
447
  }
447
- </style><link rel="stylesheet" href="/static/styles-4V6RXJMC.css" media="print" onload="this.media='all'"><noscript><link rel="stylesheet" href="/static/styles-4V6RXJMC.css"></noscript></head>
448
+ </style><link rel="stylesheet" href="/static/styles-ZM27COY6.css" media="print" onload="this.media='all'"><noscript><link rel="stylesheet" href="/static/styles-ZM27COY6.css"></noscript></head>
448
449
  <body>
449
450
  <app-root></app-root>
450
- <script src="/static/main-LJHMLKBL.js" type="module"></script></body>
451
+ <script src="/static/main-VCJFCLFP.js" type="module"></script></body>
451
452
  </html>
@@ -40016,10 +40016,18 @@ var ProjectInfoService = class _ProjectInfoService {
40016
40016
  // undefined means not yet requested
40017
40017
  // null means requested and not present
40018
40018
  _projectInfo = void 0;
40019
+ // The directory that we are storing fine videos in.
40020
+ fineVideoDir = "";
40019
40021
  async loadProjectInfo() {
40020
- const response = await this.rpc.call("getProjectInfo");
40021
- this._projectInfo = response.projectInfo ? new ProjectInfo(response.projectInfo) : null;
40022
- this.setAllViews(this._projectInfo?.views ?? []);
40022
+ const promises = [];
40023
+ promises.push(this.rpc.call("getProjectInfo").then((response) => {
40024
+ this._projectInfo = response.projectInfo ? new ProjectInfo(response.projectInfo) : null;
40025
+ this.setAllViews(this._projectInfo?.views ?? []);
40026
+ }));
40027
+ promises.push(this.rpc.call("getFineVideoDir").then((response) => {
40028
+ this.fineVideoDir = response.path;
40029
+ }));
40030
+ return Promise.allSettled(promises);
40023
40031
  }
40024
40032
  get projectInfo() {
40025
40033
  return this._projectInfo;
@@ -40182,6 +40190,32 @@ var CsvParserService = class _CsvParserService {
40182
40190
  }], null, null);
40183
40191
  })();
40184
40192
 
40193
+ // src/app/utils/comparators.ts
40194
+ var compareStringArraysOrdered = (prev, curr) => {
40195
+ if (prev.length !== curr.length) {
40196
+ return false;
40197
+ }
40198
+ for (let i = 0; i < prev.length; i++) {
40199
+ if (prev[i] !== curr[i]) {
40200
+ return false;
40201
+ }
40202
+ }
40203
+ return true;
40204
+ };
40205
+ var createSessionViewComparator = (allViewsOrder) => {
40206
+ return (a, b) => {
40207
+ const indexA = allViewsOrder.indexOf(a.viewName);
40208
+ const indexB = allViewsOrder.indexOf(b.viewName);
40209
+ if (indexA === -1 && indexB === -1)
40210
+ return 0;
40211
+ if (indexA === -1)
40212
+ return 1;
40213
+ if (indexB === -1)
40214
+ return -1;
40215
+ return indexA - indexB;
40216
+ };
40217
+ };
40218
+
40185
40219
  // src/app/session.service.ts
40186
40220
  var SessionService = class _SessionService {
40187
40221
  rpc = inject(RpcService);
@@ -40196,7 +40230,7 @@ var SessionService = class _SessionService {
40196
40230
  const projectInfo = this.projectInfoService.projectInfo;
40197
40231
  const response = await this.rpc.call("rglob", {
40198
40232
  baseDir: projectInfo.data_dir,
40199
- pattern: "**/*.fine.mp4",
40233
+ pattern: "**/*.mp4",
40200
40234
  //temporary
40201
40235
  noDirs: true
40202
40236
  });
@@ -40260,7 +40294,7 @@ var SessionService = class _SessionService {
40260
40294
  this.projectInfoService.setAllKeypoints(allKeypoints);
40261
40295
  }
40262
40296
  getPredictionFilesForSession(sessionKey) {
40263
- const predictionFiles = this.predictionFiles.filter((p) => p.sessionKey === sessionKey || p.sessionKey === sessionKey.replace(/\.fine$/, ""));
40297
+ const predictionFiles = this.predictionFiles.filter((p) => p.sessionKey === sessionKey);
40264
40298
  return predictionFiles;
40265
40299
  }
40266
40300
  async getPredictionFile(pfile) {
@@ -40296,9 +40330,10 @@ var SessionService = class _SessionService {
40296
40330
  videoPath: projectInfo.data_dir + "/" + filename
40297
40331
  });
40298
40332
  }
40333
+ const cmp = createSessionViewComparator(this.projectInfoService.allViews());
40299
40334
  return Array.from(sessionKeyToItsViewFiles.entries()).map(([key, sessionViews]) => ({
40300
40335
  key: key.replace(/\.mp4$/, ""),
40301
- views: sessionViews
40336
+ views: [...sessionViews].sort(cmp)
40302
40337
  }));
40303
40338
  }
40304
40339
  static \u0275fac = function SessionService_Factory(__ngFactoryType__) {
@@ -54403,19 +54438,6 @@ var ViewerSessionsPanelComponent = class _ViewerSessionsPanelComponent {
54403
54438
  (typeof ngDevMode === "undefined" || ngDevMode) && \u0275setClassDebugInfo(ViewerSessionsPanelComponent, { className: "ViewerSessionsPanelComponent", filePath: "src/app/viewer/viewer-left-panel/viewer-sessions-panel.component.ts", lineNumber: 24 });
54404
54439
  })();
54405
54440
 
54406
- // src/app/utils/comparators.ts
54407
- var compareStringArraysOrdered = (prev, curr) => {
54408
- if (prev.length !== curr.length) {
54409
- return false;
54410
- }
54411
- for (let i = 0; i < prev.length; i++) {
54412
- if (prev[i] !== curr[i]) {
54413
- return false;
54414
- }
54415
- }
54416
- return true;
54417
- };
54418
-
54419
54441
  // src/app/view-settings.model.ts
54420
54442
  var ViewSettings = class _ViewSettings {
54421
54443
  _viewsShown = new BehaviorSubject([]);
@@ -54663,12 +54685,22 @@ var VideoPlayerControlsComponent = class _VideoPlayerControlsComponent {
54663
54685
  // src/app/components/video-player/video-tile/video-tile.component.ts
54664
54686
  var _c04 = ["videoEl"];
54665
54687
  var _c13 = ["*"];
54688
+ var videoErrorMessages = {
54689
+ [MediaError.MEDIA_ERR_ABORTED]: "Error: aborted.",
54690
+ [MediaError.MEDIA_ERR_NETWORK]: "Error: network error.",
54691
+ [MediaError.MEDIA_ERR_DECODE]: "Error: decoding error.",
54692
+ [MediaError.MEDIA_ERR_SRC_NOT_SUPPORTED]: "Error: 404 or unsupported video format."
54693
+ };
54666
54694
  var VideoTileComponent = class _VideoTileComponent {
54667
54695
  host;
54668
54696
  videoElement = null;
54669
54697
  src = input("");
54670
54698
  // Current time of the video element, for displaying for debug info.
54671
54699
  localCurrentTime = signal(0);
54700
+ videoErrorMessage = linkedSignal(() => {
54701
+ this.src();
54702
+ return "";
54703
+ });
54672
54704
  // the main state, injected from parent so it can be easily shared across video players
54673
54705
  videoPlayerState = inject(VideoPlayerState);
54674
54706
  contentEnd = new EventEmitter();
@@ -54699,6 +54731,11 @@ var VideoTileComponent = class _VideoTileComponent {
54699
54731
  scaleFactor = signal(1);
54700
54732
  // signal to hide content until video inited
54701
54733
  showProjectedContent = signal(false);
54734
+ onVideoTagError() {
54735
+ const code = this.videoElement?.nativeElement.error?.code;
54736
+ const videoErrorMessage = code ? videoErrorMessages[code] : "Unknown error, check dev logs.";
54737
+ this.videoErrorMessage.set(videoErrorMessage);
54738
+ }
54702
54739
  onLoadedMetadata() {
54703
54740
  this.videoMetadata.next({
54704
54741
  height: this.videoElement?.nativeElement.videoHeight ?? 1,
@@ -54729,7 +54766,7 @@ var VideoTileComponent = class _VideoTileComponent {
54729
54766
  let _t;
54730
54767
  \u0275\u0275queryRefresh(_t = \u0275\u0275loadQuery()) && (ctx.videoElement = _t.first);
54731
54768
  }
54732
- }, inputs: { src: [1, "src"] }, outputs: { contentEnd: "contentEnd" }, ngContentSelectors: _c13, decls: 6, vars: 3, consts: [["videoEl", ""], [1, "relative", "shadow-lg", "shadow-black/20", "bg-base-200"], ["preload", "auto", "muted", "", 3, "loadedmetadata", "timeupdate", "ended"], ["type", "video/mp4", 3, "src"]], template: function VideoTileComponent_Template(rf, ctx) {
54769
+ }, inputs: { src: [1, "src"] }, outputs: { contentEnd: "contentEnd" }, ngContentSelectors: _c13, decls: 10, vars: 7, consts: [["videoEl", ""], [1, "relative", "shadow-lg", "shadow-black/20", "bg-base-200"], ["preload", "auto", "muted", "", 3, "loadedmetadata", "timeupdate", "ended", "error", "src"], [1, "absolute", "inset-0", "overflow-auto", "p-2"], [1, "text-sm", "text-gray-400", "mt-2"]], template: function VideoTileComponent_Template(rf, ctx) {
54733
54770
  if (rf & 1) {
54734
54771
  const _r1 = \u0275\u0275getCurrentView();
54735
54772
  \u0275\u0275projectionDef();
@@ -54743,25 +54780,39 @@ var VideoTileComponent = class _VideoTileComponent {
54743
54780
  })("ended", function VideoTileComponent_Template_video_ended_1_listener() {
54744
54781
  \u0275\u0275restoreView(_r1);
54745
54782
  return \u0275\u0275resetView(ctx.onEnd());
54783
+ })("error", function VideoTileComponent_Template_video_error_1_listener() {
54784
+ \u0275\u0275restoreView(_r1);
54785
+ return \u0275\u0275resetView(ctx.onVideoTagError());
54746
54786
  });
54747
- \u0275\u0275element(3, "source", 3);
54787
+ \u0275\u0275text(3, " Your browser does not support the video tag. ");
54748
54788
  \u0275\u0275elementEnd();
54749
54789
  \u0275\u0275elementStart(4, "div");
54750
54790
  \u0275\u0275projection(5);
54751
- \u0275\u0275elementEnd()();
54791
+ \u0275\u0275elementEnd();
54792
+ \u0275\u0275elementStart(6, "div", 3);
54793
+ \u0275\u0275text(7);
54794
+ \u0275\u0275elementStart(8, "p", 4);
54795
+ \u0275\u0275text(9);
54796
+ \u0275\u0275elementEnd()()();
54752
54797
  }
54753
54798
  if (rf & 2) {
54754
- \u0275\u0275advance(3);
54755
- \u0275\u0275property("src", ctx.src(), \u0275\u0275sanitizeUrl);
54756
54799
  \u0275\u0275advance();
54800
+ \u0275\u0275property("src", ctx.src(), \u0275\u0275sanitizeUrl);
54801
+ \u0275\u0275advance(3);
54757
54802
  \u0275\u0275classProp("invisible", !ctx.showProjectedContent());
54803
+ \u0275\u0275advance(2);
54804
+ \u0275\u0275classProp("invisible", !ctx.videoErrorMessage());
54805
+ \u0275\u0275advance();
54806
+ \u0275\u0275textInterpolate1(" ", ctx.videoErrorMessage(), " ");
54807
+ \u0275\u0275advance(2);
54808
+ \u0275\u0275textInterpolate1("Source: ", ctx.src(), "");
54758
54809
  }
54759
- }, styles: ["\n\n[_nghost-%COMP%] {\n display: block;\n}\n/*# sourceMappingURL=/static/video-tile.component-XSYKMARQ.css.map */"], changeDetection: 0 });
54810
+ }, styles: ["\n\n[_nghost-%COMP%] {\n display: block;\n}\n/*# sourceMappingURL=/static/video-tile.component-RDL4BSJ4.css.map */"], changeDetection: 0 });
54760
54811
  };
54761
54812
  (() => {
54762
54813
  (typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(VideoTileComponent, [{
54763
54814
  type: Component,
54764
- args: [{ selector: "app-video-tile", imports: [], changeDetection: ChangeDetectionStrategy.OnPush, template: '<style>\r\n :host {\r\n /* Display for angular elements is inline by default.\r\n This is a problem when querying the host height, because it ends up being just 0.\r\n */\r\n display: block;\r\n }\r\n</style>\r\n\r\n<div class="relative shadow-lg shadow-black/20 bg-base-200">\r\n <!-- bg is visible during loading state -->\r\n <!-- wrapper to contain video + keypoints -->\r\n <video\r\n #videoEl\r\n preload="auto"\r\n (loadedmetadata)="onLoadedMetadata()"\r\n muted\r\n (timeupdate)="updateLocalCurrentTime()"\r\n (ended)="onEnd()"\r\n >\r\n <source [src]="src()" type="video/mp4" />\r\n </video>\r\n <div [class.invisible]="!showProjectedContent()">\r\n <!-- wrapper to hide content until video inited -->\r\n <ng-content></ng-content>\r\n </div>\r\n</div>\r\n<!--<p>T: {{ localCurrentTime() }}</p>-->\r\n', styles: ["/* angular:styles/component:css;cca0ce7e24de60b661844ed36e56ee3734a21f5d38966246b6b69d25889a81d9;/home/ksikka/lightning-pose-app/web_ui/src/app/components/video-player/video-tile/video-tile.component.html */\n:host {\n display: block;\n}\n/*# sourceMappingURL=/static/video-tile.component-XSYKMARQ.css.map */\n"] }]
54815
+ args: [{ selector: "app-video-tile", imports: [], changeDetection: ChangeDetectionStrategy.OnPush, template: '<style>\n :host {\n /* Display for angular elements is inline by default.\n This is a problem when querying the host height, because it ends up being just 0.\n */\n display: block;\n }\n</style>\n\n<div class="relative shadow-lg shadow-black/20 bg-base-200">\n <!-- bg is visible during loading state -->\n <!-- wrapper to contain video + keypoints -->\n <video\n #videoEl\n preload="auto"\n (loadedmetadata)="onLoadedMetadata()"\n muted\n (timeupdate)="updateLocalCurrentTime()"\n (ended)="onEnd()"\n (error)="onVideoTagError()"\n [src]="src()"\n >\n Your browser does not support the video tag.\n </video>\n <div [class.invisible]="!showProjectedContent()">\n <!-- wrapper to hide content until video inited -->\n <ng-content></ng-content>\n </div>\n\n <div\n class="absolute inset-0 overflow-auto p-2"\n [class.invisible]="!videoErrorMessage()"\n >\n {{ videoErrorMessage() }}\n <p class="text-sm text-gray-400 mt-2">Source: {{ src() }}</p>\n </div>\n</div>\n<!--<p>T: {{ localCurrentTime() }}</p>-->\n', styles: ["/* angular:styles/component:css;cca0ce7e24de60b661844ed36e56ee3734a21f5d38966246b6b69d25889a81d9;/home/ksikka/work/lightning-pose-app/web_ui/src/app/components/video-player/video-tile/video-tile.component.html */\n:host {\n display: block;\n}\n/*# sourceMappingURL=/static/video-tile.component-RDL4BSJ4.css.map */\n"] }]
54765
54816
  }], () => [{ type: ElementRef }], { videoElement: [{
54766
54817
  type: ViewChild,
54767
54818
  args: ["videoEl", { static: true }]
@@ -54770,7 +54821,7 @@ var VideoTileComponent = class _VideoTileComponent {
54770
54821
  }] });
54771
54822
  })();
54772
54823
  (() => {
54773
- (typeof ngDevMode === "undefined" || ngDevMode) && \u0275setClassDebugInfo(VideoTileComponent, { className: "VideoTileComponent", filePath: "src/app/components/video-player/video-tile/video-tile.component.ts", lineNumber: 39 });
54824
+ (typeof ngDevMode === "undefined" || ngDevMode) && \u0275setClassDebugInfo(VideoTileComponent, { className: "VideoTileComponent", filePath: "src/app/components/video-player/video-tile/video-tile.component.ts", lineNumber: 49 });
54774
54825
  })();
54775
54826
 
54776
54827
  // node_modules/@angular/cdk/fesm2022/drag-drop.mjs
@@ -58999,6 +59050,30 @@ var Pair = class _Pair {
58999
59050
  }
59000
59051
  };
59001
59052
 
59053
+ // src/app/utils/fine-video.service.ts
59054
+ var FineVideoService = class _FineVideoService {
59055
+ projectInfoService = inject(ProjectInfoService);
59056
+ fineVideoPath(videoPath) {
59057
+ if (!this.projectInfoService.fineVideoDir) {
59058
+ throw new Error("ProjectInfoService.fineVideoDir called but not yet initialized");
59059
+ }
59060
+ const filename = videoPath.split("/").pop();
59061
+ return "/app/v0/files/" + this.projectInfoService.fineVideoDir + "/" + filename;
59062
+ }
59063
+ static \u0275fac = function FineVideoService_Factory(__ngFactoryType__) {
59064
+ return new (__ngFactoryType__ || _FineVideoService)();
59065
+ };
59066
+ static \u0275prov = /* @__PURE__ */ \u0275\u0275defineInjectable({ token: _FineVideoService, factory: _FineVideoService.\u0275fac, providedIn: "root" });
59067
+ };
59068
+ (() => {
59069
+ (typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(FineVideoService, [{
59070
+ type: Injectable,
59071
+ args: [{
59072
+ providedIn: "root"
59073
+ }]
59074
+ }], null, null);
59075
+ })();
59076
+
59002
59077
  // src/app/viewer/viewer-center-panel/viewer-center-panel.component.ts
59003
59078
  var _forTrack03 = ($index, $item) => $item.videoSrc;
59004
59079
  function ViewerCenterPanelComponent_For_3_Template(rf, ctx) {
@@ -59034,6 +59109,7 @@ var ViewerCenterPanelComponent = class _ViewerCenterPanelComponent {
59034
59109
  csvParser = inject(CsvParserService);
59035
59110
  projectInfoService = inject(ProjectInfoService);
59036
59111
  loadingService = inject(LoadingService);
59112
+ fineVideoService = inject(FineVideoService);
59037
59113
  get currentFrame() {
59038
59114
  return this.videoPlayerState.currentFrameSignal;
59039
59115
  }
@@ -59075,7 +59151,7 @@ var ViewerCenterPanelComponent = class _ViewerCenterPanelComponent {
59075
59151
  });
59076
59152
  return {
59077
59153
  id: sessionView.viewName,
59078
- videoSrc: this.getVideoSrc(sessionView),
59154
+ videoSrc: this.fineVideoService.fineVideoPath(sessionView.videoPath),
59079
59155
  keypoints: filteredKeypoints
59080
59156
  };
59081
59157
  }
@@ -59112,9 +59188,6 @@ var ViewerCenterPanelComponent = class _ViewerCenterPanelComponent {
59112
59188
  const dataDir = this.projectInfoService.projectInfo?.data_dir;
59113
59189
  return dataDir + "/" + sessionKey.replace(/\*/g, view) + ".mp4";
59114
59190
  }
59115
- getVideoSrc(sessionView) {
59116
- return "/app/v0/files/" + sessionView.videoPath;
59117
- }
59118
59191
  async loadSession(sessionKey) {
59119
59192
  this.sessionKey.set(sessionKey);
59120
59193
  this.widgetModels.set([]);
@@ -59194,7 +59267,7 @@ var ViewerCenterPanelComponent = class _ViewerCenterPanelComponent {
59194
59267
  VideoPlayerControlsComponent,
59195
59268
  VideoTileComponent,
59196
59269
  KeypointContainerComponent
59197
- ], changeDetection: ChangeDetectionStrategy.OnPush, template: '<div class="flex flex-col h-full">\r\n <!-- Video content area -->\r\n <div\r\n class="w-full grow flex flex-wrap overflow-y-auto gap-4 *:max-w-xs justify-start items-start content-start p-4"\r\n >\r\n <!-- `track videosrc` makes the widget component get\r\n destroyed and recreated if videoSrc changes. Reactively\r\n changing videoSrc of an existing video does not work unfortunately. -->\r\n @for (w of filteredWidgetModels(); track w.videoSrc) {\r\n <div class="panel !border-none">\r\n <div class="px-2 py-1 text-sm flex justify-between">\r\n <h3>{{ w.id }}</h3>\r\n <span\r\n class="btn btn-circle btn-xs material-icons !text-sm"\r\n (click)="onWidgetCloseClick(w)"\r\n >close</span\r\n >\r\n </div>\r\n <app-video-tile [src]="w.videoSrc" class="w-auto">\r\n <app-keypoint-container\r\n [labelerMode]="false"\r\n [keypointModels]="w.keypoints()"\r\n ></app-keypoint-container>\r\n </app-video-tile>\r\n </div>\r\n }\r\n </div>\r\n\r\n <app-video-player-controls></app-video-player-controls>\r\n</div>\r\n' }]
59270
+ ], changeDetection: ChangeDetectionStrategy.OnPush, template: '<div class="flex flex-col h-full">\n <!-- Video content area -->\n <div\n class="w-full grow flex flex-wrap overflow-y-auto gap-4 *:max-w-xs justify-start items-start content-start p-4"\n >\n <!-- `track videosrc` makes the widget component get\n destroyed and recreated if videoSrc changes. Reactively\n changing videoSrc of an existing video does not work unfortunately. -->\n @for (w of filteredWidgetModels(); track w.videoSrc) {\n <div class="panel !border-none">\n <div class="px-2 py-1 text-sm flex justify-between">\n <h3>{{ w.id }}</h3>\n <span\n class="btn btn-circle btn-xs material-icons !text-sm"\n (click)="onWidgetCloseClick(w)"\n >close</span\n >\n </div>\n <app-video-tile [src]="w.videoSrc" class="w-auto">\n <app-keypoint-container\n [labelerMode]="false"\n [keypointModels]="w.keypoints()"\n ></app-keypoint-container>\n </app-video-tile>\n </div>\n }\n </div>\n\n <app-video-player-controls></app-video-player-controls>\n</div>\n' }]
59198
59271
  }], null, { viewSettings: [{
59199
59272
  type: Input
59200
59273
  }] });
@@ -59491,7 +59564,7 @@ var ViewerPageComponent = class _ViewerPageComponent {
59491
59564
  ViewerSessionsPanelComponent,
59492
59565
  ViewerCenterPanelComponent,
59493
59566
  LoadingBarComponent
59494
- ], styles: ["\n\n[_nghost-%COMP%] {\n flex-grow: 1;\n display: flex;\n}\n/*# sourceMappingURL=/static/viewer-page.component-MRTIUFL2.css.map */"], changeDetection: 0 });
59567
+ ], styles: ["\n\n[_nghost-%COMP%] {\n flex-grow: 1;\n display: flex;\n}\n/*# sourceMappingURL=/static/viewer-page.component-KDHT6XH5.css.map */"], changeDetection: 0 });
59495
59568
  };
59496
59569
  (() => {
59497
59570
  (typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(ViewerPageComponent, [{
@@ -59500,116 +59573,116 @@ var ViewerPageComponent = class _ViewerPageComponent {
59500
59573
  ViewerSessionsPanelComponent,
59501
59574
  ViewerCenterPanelComponent,
59502
59575
  LoadingBarComponent
59503
- ], changeDetection: ChangeDetectionStrategy.OnPush, providers: [VideoPlayerState, ViewSettings], template: `<style>\r
59504
- :host {\r
59505
- flex-grow: 1;\r
59506
- display: flex;\r
59507
- }\r
59508
- </style>\r
59509
- \r
59510
- @if (!isIniting()) {\r
59511
- <div class="grow flex flex-row items-stretch">\r
59512
- <!-- Left pane -->\r
59513
- <div\r
59514
- class="bg-base-200 w-60 xl:w-80 flex flex-col justify-between shadow-lg"\r
59515
- >\r
59516
- <div class="p-4">\r
59517
- <app-sessions-panel></app-sessions-panel>\r
59518
- </div>\r
59519
- <app-loading-bar class="h-20 p-4"></app-loading-bar>\r
59520
- </div>\r
59521
- \r
59522
- <!-- Center content -->\r
59523
- <app-viewer-center-panel\r
59524
- class="grow z-30"\r
59525
- [viewSettings]="viewSettings"\r
59526
- ></app-viewer-center-panel>\r
59527
- \r
59528
- <!-- Right pane -->\r
59529
- <div class="shrink-0 w-40 xl:w-60 h-full">\r
59530
- <div class="panel m-4">\r
59531
- <div>\r
59532
- <!-- panel header -->\r
59533
- <h1 class="px-2 mb-1">Views</h1>\r
59534
- </div>\r
59535
- <div class="panel-content max-h-60 overflow-y-auto">\r
59536
- <ul>\r
59537
- @for (cam of allViews; track cam) {\r
59538
- <li class="p-1 flex justify-between items-center">\r
59539
- <span>{{ cam }}</span>\r
59540
- <input\r
59541
- type="checkbox"\r
59542
- [checked]="viewSelectionModel.isSelected(cam)"\r
59543
- class="checkbox checkbox-xs"\r
59544
- (change)="onViewCheckboxChange($event, cam)"\r
59545
- />\r
59546
- </li>\r
59547
- }\r
59548
- </ul>\r
59549
- </div>\r
59550
- </div>\r
59551
- \r
59552
- <div class="panel m-4">\r
59553
- <div>\r
59554
- <!-- panel header -->\r
59555
- <h1 class="px-2 mb-1">Keypoints</h1>\r
59556
- </div>\r
59557
- <div class="panel-content max-h-60 overflow-y-auto">\r
59558
- <ul>\r
59559
- @for (kp of projectInfoService.allKeypoints(); track kp) {\r
59560
- <li class="p-1 flex justify-between items-center">\r
59561
- <span>{{ kp }}</span>\r
59562
- <input\r
59563
- type="checkbox"\r
59564
- [checked]="keypointSelectionModel.isSelected(kp)"\r
59565
- class="checkbox checkbox-xs"\r
59566
- (change)="onKeypointCheckboxChange($event, kp)"\r
59567
- />\r
59568
- </li>\r
59569
- }\r
59570
- </ul>\r
59571
- </div>\r
59572
- </div>\r
59573
- \r
59574
- <div class="panel m-4">\r
59575
- <div>\r
59576
- <!-- panel header -->\r
59577
- <h1 class="px-2 mb-1">Models</h1>\r
59578
- </div>\r
59579
- <div class="panel-content max-h-60 overflow-y-visible">\r
59580
- <ul>\r
59581
- @let modelSelectors =\r
59582
- viewSettings.modelsShown().length < 2\r
59583
- ? viewSettings.modelsShown().concat([noneOption])\r
59584
- : viewSettings.modelsShown();\r
59585
- <!-- it's important to track index, not model.\r
59586
- if we put model here, something weird happens when you clear all models and then set a model. -->\r
59587
- @for (model of modelSelectors; track $index) {\r
59588
- @let modelIndex = $index;\r
59589
- <li class="p-1 w-full">\r
59590
- <select\r
59591
- class="select select-sm w-full flex-grow"\r
59592
- [class.border-red-300]="modelIndex === 0"\r
59593
- [class.border-green-300]="modelIndex === 1"\r
59594
- (change)="onModelDropdownItemClick(modelIndex, $event)"\r
59595
- >\r
59596
- @let options =\r
59597
- [noneOption].concat(projectInfoService.allModels());\r
59598
- @for (dropdownItem of options; track dropdownItem) {\r
59599
- <option [selected]="dropdownItem === model">\r
59600
- {{ dropdownItem }}\r
59601
- </option>\r
59602
- }\r
59603
- </select>\r
59604
- </li>\r
59605
- }\r
59606
- </ul>\r
59607
- </div>\r
59608
- </div>\r
59609
- </div>\r
59610
- </div>\r
59611
- }\r
59612
- `, styles: ["/* angular:styles/component:css;1fa15f7a0ae1b0259233282b2d49fb361834bf7fd726c58584c186240f9d8f04;/home/ksikka/lightning-pose-app/web_ui/src/app/viewer/viewer-page/viewer-page.component.html */\n:host {\n flex-grow: 1;\n display: flex;\n}\n/*# sourceMappingURL=/static/viewer-page.component-MRTIUFL2.css.map */\n"] }]
59576
+ ], changeDetection: ChangeDetectionStrategy.OnPush, providers: [VideoPlayerState, ViewSettings], template: `<style>
59577
+ :host {
59578
+ flex-grow: 1;
59579
+ display: flex;
59580
+ }
59581
+ </style>
59582
+
59583
+ @if (!isIniting()) {
59584
+ <div class="grow flex flex-row items-stretch">
59585
+ <!-- Left pane -->
59586
+ <div
59587
+ class="bg-base-200 w-60 xl:w-80 flex flex-col justify-between shadow-lg"
59588
+ >
59589
+ <div class="p-4">
59590
+ <app-sessions-panel></app-sessions-panel>
59591
+ </div>
59592
+ <app-loading-bar class="h-20 p-4"></app-loading-bar>
59593
+ </div>
59594
+
59595
+ <!-- Center content -->
59596
+ <app-viewer-center-panel
59597
+ class="grow z-30"
59598
+ [viewSettings]="viewSettings"
59599
+ ></app-viewer-center-panel>
59600
+
59601
+ <!-- Right pane -->
59602
+ <div class="shrink-0 w-40 xl:w-60 h-full">
59603
+ <div class="panel m-4">
59604
+ <div>
59605
+ <!-- panel header -->
59606
+ <h1 class="px-2 mb-1">Views</h1>
59607
+ </div>
59608
+ <div class="panel-content max-h-60 overflow-y-auto">
59609
+ <ul>
59610
+ @for (cam of allViews; track cam) {
59611
+ <li class="p-1 flex justify-between items-center">
59612
+ <span>{{ cam }}</span>
59613
+ <input
59614
+ type="checkbox"
59615
+ [checked]="viewSelectionModel.isSelected(cam)"
59616
+ class="checkbox checkbox-xs"
59617
+ (change)="onViewCheckboxChange($event, cam)"
59618
+ />
59619
+ </li>
59620
+ }
59621
+ </ul>
59622
+ </div>
59623
+ </div>
59624
+
59625
+ <div class="panel m-4">
59626
+ <div>
59627
+ <!-- panel header -->
59628
+ <h1 class="px-2 mb-1">Keypoints</h1>
59629
+ </div>
59630
+ <div class="panel-content max-h-60 overflow-y-auto">
59631
+ <ul>
59632
+ @for (kp of projectInfoService.allKeypoints(); track kp) {
59633
+ <li class="p-1 flex justify-between items-center">
59634
+ <span>{{ kp }}</span>
59635
+ <input
59636
+ type="checkbox"
59637
+ [checked]="keypointSelectionModel.isSelected(kp)"
59638
+ class="checkbox checkbox-xs"
59639
+ (change)="onKeypointCheckboxChange($event, kp)"
59640
+ />
59641
+ </li>
59642
+ }
59643
+ </ul>
59644
+ </div>
59645
+ </div>
59646
+
59647
+ <div class="panel m-4">
59648
+ <div>
59649
+ <!-- panel header -->
59650
+ <h1 class="px-2 mb-1">Models</h1>
59651
+ </div>
59652
+ <div class="panel-content max-h-60 overflow-y-visible">
59653
+ <ul>
59654
+ @let modelSelectors =
59655
+ viewSettings.modelsShown().length < 2
59656
+ ? viewSettings.modelsShown().concat([noneOption])
59657
+ : viewSettings.modelsShown();
59658
+ <!-- it's important to track index, not model.
59659
+ if we put model here, something weird happens when you clear all models and then set a model. -->
59660
+ @for (model of modelSelectors; track $index) {
59661
+ @let modelIndex = $index;
59662
+ <li class="p-1 w-full">
59663
+ <select
59664
+ class="select select-sm w-full flex-grow"
59665
+ [class.border-red-300]="modelIndex === 0"
59666
+ [class.border-green-300]="modelIndex === 1"
59667
+ (change)="onModelDropdownItemClick(modelIndex, $event)"
59668
+ >
59669
+ @let options =
59670
+ [noneOption].concat(projectInfoService.allModels());
59671
+ @for (dropdownItem of options; track dropdownItem) {
59672
+ <option [selected]="dropdownItem === model">
59673
+ {{ dropdownItem }}
59674
+ </option>
59675
+ }
59676
+ </select>
59677
+ </li>
59678
+ }
59679
+ </ul>
59680
+ </div>
59681
+ </div>
59682
+ </div>
59683
+ </div>
59684
+ }
59685
+ `, styles: ["/* angular:styles/component:css;1fa15f7a0ae1b0259233282b2d49fb361834bf7fd726c58584c186240f9d8f04;/home/ksikka/work/lightning-pose-app/web_ui/src/app/viewer/viewer-page/viewer-page.component.html */\n:host {\n flex-grow: 1;\n display: flex;\n}\n/*# sourceMappingURL=/static/viewer-page.component-KDHT6XH5.css.map */\n"] }]
59613
59686
  }], () => [], { sessionKey: [{
59614
59687
  type: Input
59615
59688
  }] });
@@ -66177,7 +66250,7 @@ view2
66177
66250
  \u0275\u0275advance();
66178
66251
  \u0275\u0275property("disabled", !ctx.projectInfoForm.dirty);
66179
66252
  }
66180
- }, dependencies: [ReactiveFormsModule, \u0275NgNoValidate, DefaultValueAccessor, NgControlStatus, NgControlStatusGroup, FormGroupDirective, FormControlName], styles: ["\n\n[_ngcontent-%COMP%]::placeholder {\n color: color-mix(in oklch, currentColor 50%, #0000) !important;\n}\n/*# sourceMappingURL=/static/project-settings.component-5IRK7U7U.css.map */"], changeDetection: 0 });
66253
+ }, dependencies: [ReactiveFormsModule, \u0275NgNoValidate, DefaultValueAccessor, NgControlStatus, NgControlStatusGroup, FormGroupDirective, FormControlName], styles: ["\n\n[_ngcontent-%COMP%]::placeholder {\n color: color-mix(in oklch, currentColor 50%, #0000) !important;\n}\n/*# sourceMappingURL=/static/project-settings.component-HKHIVUJR.css.map */"], changeDetection: 0 });
66181
66254
  };
66182
66255
  (() => {
66183
66256
  (typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(ProjectSettingsComponent, [{
@@ -66261,7 +66334,7 @@ view2
66261
66334
  </button>
66262
66335
  </div>
66263
66336
  </form>
66264
- `, styles: ["/* angular:styles/component:css;5c3484fa86afc1ad339d983c727d8801928706c064dd49a4001f42c8eac3d364;/home/ksikka/lightning-pose-app/web_ui/src/app/project-settings/project-settings.component.html */\n::placeholder {\n color: color-mix(in oklch, currentColor 50%, #0000) !important;\n}\n/*# sourceMappingURL=/static/project-settings.component-5IRK7U7U.css.map */\n"] }]
66337
+ `, styles: ["/* angular:styles/component:css;5c3484fa86afc1ad339d983c727d8801928706c064dd49a4001f42c8eac3d364;/home/ksikka/work/lightning-pose-app/web_ui/src/app/project-settings/project-settings.component.html */\n::placeholder {\n color: color-mix(in oklch, currentColor 50%, #0000) !important;\n}\n/*# sourceMappingURL=/static/project-settings.component-HKHIVUJR.css.map */\n"] }]
66265
66338
  }], () => [], null);
66266
66339
  })();
66267
66340
  (() => {
@@ -66386,7 +66459,7 @@ var AppComponent = class _AppComponent {
66386
66459
  RouterLink,
66387
66460
  RouterLinkActive,
66388
66461
  ProjectSettingsComponent
66389
- ], styles: ["\n\n[_nghost-%COMP%] {\n display: flex;\n flex-direction: column;\n height: 100vh;\n}\n.navbar[_ngcontent-%COMP%] {\n min-height: 3rem;\n}\n/*# sourceMappingURL=/static/app.component-UHVEDPZR.css.map */"], changeDetection: 0 });
66462
+ ], styles: ["\n\n[_nghost-%COMP%] {\n display: flex;\n flex-direction: column;\n height: 100vh;\n}\n.navbar[_ngcontent-%COMP%] {\n min-height: 3rem;\n}\n/*# sourceMappingURL=/static/app.component-UAQUAGNZ.css.map */"], changeDetection: 0 });
66390
66463
  };
66391
66464
  (() => {
66392
66465
  (typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(AppComponent, [{
@@ -66396,7 +66469,7 @@ var AppComponent = class _AppComponent {
66396
66469
  RouterLink,
66397
66470
  RouterLinkActive,
66398
66471
  ProjectSettingsComponent
66399
- ], changeDetection: ChangeDetectionStrategy.OnPush, template: '<style>\n :host {\n display: flex;\n flex-direction: column;\n height: 100vh;\n }\n .navbar {\n min-height: 3rem; /* reduce from default of 4rem */\n }\n</style>\n<header>\n <nav class="navbar bg-base-200 shadow-lg">\n <div class="navbar-start">\n <span class="text-lg font-semibold">Lightning Pose</span>\n </div>\n\n <div class="navbar-center">\n <ul class="menu menu-horizontal p-0">\n <li>\n <a\n routerLink="/viewer"\n routerLinkActive="menu-active"\n ariaCurrentWhenActive="page"\n >Viewer</a\n >\n </li>\n <li>\n <a\n routerLink="/labeler"\n routerLinkActive="menu-active"\n ariaCurrentWhenActive="page"\n >Labeler</a\n >\n </li>\n </ul>\n </div>\n\n <div class="navbar-end">\n <button\n class="btn btn-ghost"\n tabindex="0"\n (click)="settingsDialogOpen.set(true)"\n (keydown.enter)="settingsDialogOpen.set(true)"\n >\n <span class="material-icons">settings</span>\n </button>\n </div>\n </nav>\n</header>\n\n<!-- Set min-height: 0 to allow it to shrink if its content is too large.\n Default min-height for flex items is auto. -->\n<main class="flex-grow flex min-h-0" [class.justify-center]="!hasBeenSetup()">\n @if (hasBeenSetup()) {\n <router-outlet />\n } @else {\n <app-project-settings\n [setupMode]="true"\n class="w-lg mt-8"\n ></app-project-settings>\n }\n</main>\n\n<!-- Settings dialog -->\n<dialog #settingsDialog class="modal">\n @if (settingsDialogOpen()) {\n <app-project-settings\n class="modal-box"\n (done)="settingsDialogOpen.set(false)"\n ></app-project-settings>\n\n <!-- Makes the dialog close when clicked from outside. -->\n <div class="modal-backdrop">\n <button (click)="settingsDialogOpen.set(false)">close</button>\n </div>\n }\n</dialog>\n', styles: ["/* angular:styles/component:css;22d8514f1dd5b50f33b3fb93fdb69668f78eeb349bd672e238e3ac9acfbbda19;/home/ksikka/lightning-pose-app/web_ui/src/app/app.component.html */\n:host {\n display: flex;\n flex-direction: column;\n height: 100vh;\n}\n.navbar {\n min-height: 3rem;\n}\n/*# sourceMappingURL=/static/app.component-UHVEDPZR.css.map */\n"] }]
66472
+ ], changeDetection: ChangeDetectionStrategy.OnPush, template: '<style>\n :host {\n display: flex;\n flex-direction: column;\n height: 100vh;\n }\n .navbar {\n min-height: 3rem; /* reduce from default of 4rem */\n }\n</style>\n<header>\n <nav class="navbar bg-base-200 shadow-lg">\n <div class="navbar-start">\n <span class="text-lg font-semibold">Lightning Pose</span>\n </div>\n\n <div class="navbar-center">\n <ul class="menu menu-horizontal p-0">\n <li>\n <a\n routerLink="/viewer"\n routerLinkActive="menu-active"\n ariaCurrentWhenActive="page"\n >Viewer</a\n >\n </li>\n <li>\n <a\n routerLink="/labeler"\n routerLinkActive="menu-active"\n ariaCurrentWhenActive="page"\n >Labeler</a\n >\n </li>\n </ul>\n </div>\n\n <div class="navbar-end">\n <button\n class="btn btn-ghost"\n tabindex="0"\n (click)="settingsDialogOpen.set(true)"\n (keydown.enter)="settingsDialogOpen.set(true)"\n >\n <span class="material-icons">settings</span>\n </button>\n </div>\n </nav>\n</header>\n\n<!-- Set min-height: 0 to allow it to shrink if its content is too large.\n Default min-height for flex items is auto. -->\n<main class="flex-grow flex min-h-0" [class.justify-center]="!hasBeenSetup()">\n @if (hasBeenSetup()) {\n <router-outlet />\n } @else {\n <app-project-settings\n [setupMode]="true"\n class="w-lg mt-8"\n ></app-project-settings>\n }\n</main>\n\n<!-- Settings dialog -->\n<dialog #settingsDialog class="modal">\n @if (settingsDialogOpen()) {\n <app-project-settings\n class="modal-box"\n (done)="settingsDialogOpen.set(false)"\n ></app-project-settings>\n\n <!-- Makes the dialog close when clicked from outside. -->\n <div class="modal-backdrop">\n <button (click)="settingsDialogOpen.set(false)">close</button>\n </div>\n }\n</dialog>\n', styles: ["/* angular:styles/component:css;22d8514f1dd5b50f33b3fb93fdb69668f78eeb349bd672e238e3ac9acfbbda19;/home/ksikka/work/lightning-pose-app/web_ui/src/app/app.component.html */\n:host {\n display: flex;\n flex-direction: column;\n height: 100vh;\n}\n.navbar {\n min-height: 3rem;\n}\n/*# sourceMappingURL=/static/app.component-UAQUAGNZ.css.map */\n"] }]
66400
66473
  }], () => [], null);
66401
66474
  })();
66402
66475
  (() => {
@@ -66405,4 +66478,4 @@ var AppComponent = class _AppComponent {
66405
66478
 
66406
66479
  // src/main.ts
66407
66480
  bootstrapApplication(AppComponent, appConfig).catch((err) => console.error(err));
66408
- //# sourceMappingURL=main-LJHMLKBL.js.map
66481
+ //# sourceMappingURL=main-VCJFCLFP.js.map