@ozdao/martyrs 0.2.493 → 0.2.495

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 (80) hide show
  1. package/dist/_virtual/index.cjs +4 -4
  2. package/dist/_virtual/index.js +4 -4
  3. package/dist/_virtual/index2.cjs +4 -4
  4. package/dist/_virtual/index2.js +4 -4
  5. package/dist/builder.cjs +43 -88
  6. package/dist/builder.js +45 -90
  7. package/dist/martyrs/src/components/FieldTags/FieldTags.vue.cjs +1 -1
  8. package/dist/martyrs/src/components/FieldTags/FieldTags.vue.js +1 -1
  9. package/dist/martyrs/src/modules/community/components/pages/CreateBlogPost.vue.cjs +1 -1
  10. package/dist/martyrs/src/modules/community/components/pages/CreateBlogPost.vue.js +1 -1
  11. package/dist/martyrs/src/modules/globals/globals.client.cjs +1 -1
  12. package/dist/martyrs/src/modules/globals/globals.client.cjs.map +1 -1
  13. package/dist/martyrs/src/modules/globals/globals.client.js +1 -1
  14. package/dist/martyrs/src/modules/globals/globals.client.js.map +1 -1
  15. package/dist/martyrs/src/modules/globals/views/classes/globals.i18n.cjs +1 -1
  16. package/dist/martyrs/src/modules/globals/views/classes/globals.i18n.js +1 -1
  17. package/dist/martyrs/src/modules/globals/views/components/layouts/Client.vue.cjs +28 -13
  18. package/dist/martyrs/src/modules/globals/views/components/layouts/Client.vue.cjs.map +1 -1
  19. package/dist/martyrs/src/modules/globals/views/components/layouts/Client.vue.js +28 -13
  20. package/dist/martyrs/src/modules/globals/views/components/layouts/Client.vue.js.map +1 -1
  21. package/dist/martyrs/src/modules/globals/views/router/scrollBehavior.cjs +1 -1
  22. package/dist/martyrs/src/modules/globals/views/router/scrollBehavior.cjs.map +1 -1
  23. package/dist/martyrs/src/modules/globals/views/router/scrollBehavior.js +1 -1
  24. package/dist/martyrs/src/modules/globals/views/router/scrollBehavior.js.map +1 -1
  25. package/dist/martyrs/src/modules/music/components/cards/TrackListCard.vue.cjs +1 -1
  26. package/dist/martyrs/src/modules/music/components/cards/TrackListCard.vue.cjs.map +1 -1
  27. package/dist/martyrs/src/modules/music/components/cards/TrackListCard.vue.js +1 -1
  28. package/dist/martyrs/src/modules/music/components/cards/TrackListCard.vue.js.map +1 -1
  29. package/dist/martyrs/src/modules/music/components/forms/SearchForm.vue.cjs +2 -2
  30. package/dist/martyrs/src/modules/music/components/forms/SearchForm.vue.cjs.map +1 -1
  31. package/dist/martyrs/src/modules/music/components/forms/SearchForm.vue.js +2 -2
  32. package/dist/martyrs/src/modules/music/components/forms/SearchForm.vue.js.map +1 -1
  33. package/dist/martyrs/src/modules/music/components/pages/Album.vue.cjs +86 -81
  34. package/dist/martyrs/src/modules/music/components/pages/Album.vue.cjs.map +1 -1
  35. package/dist/martyrs/src/modules/music/components/pages/Album.vue.js +88 -83
  36. package/dist/martyrs/src/modules/music/components/pages/Album.vue.js.map +1 -1
  37. package/dist/martyrs/src/modules/music/components/pages/Artist.vue.cjs +4 -4
  38. package/dist/martyrs/src/modules/music/components/pages/Artist.vue.cjs.map +1 -1
  39. package/dist/martyrs/src/modules/music/components/pages/Artist.vue.js +4 -4
  40. package/dist/martyrs/src/modules/music/components/pages/Artist.vue.js.map +1 -1
  41. package/dist/martyrs/src/modules/music/components/pages/MusicLibrary.vue.cjs +5 -5
  42. package/dist/martyrs/src/modules/music/components/pages/MusicLibrary.vue.cjs.map +1 -1
  43. package/dist/martyrs/src/modules/music/components/pages/MusicLibrary.vue.js +5 -5
  44. package/dist/martyrs/src/modules/music/components/pages/MusicLibrary.vue.js.map +1 -1
  45. package/dist/martyrs/src/modules/music/components/pages/Playlist.vue.cjs +12 -9
  46. package/dist/martyrs/src/modules/music/components/pages/Playlist.vue.cjs.map +1 -1
  47. package/dist/martyrs/src/modules/music/components/pages/Playlist.vue.js +12 -9
  48. package/dist/martyrs/src/modules/music/components/pages/Playlist.vue.js.map +1 -1
  49. package/dist/martyrs/src/modules/music/components/pages/SearchResults.vue.cjs +7 -7
  50. package/dist/martyrs/src/modules/music/components/pages/SearchResults.vue.cjs.map +1 -1
  51. package/dist/martyrs/src/modules/music/components/pages/SearchResults.vue.js +7 -7
  52. package/dist/martyrs/src/modules/music/components/pages/SearchResults.vue.js.map +1 -1
  53. package/dist/martyrs/src/modules/music/components/pages/Track.vue.cjs +43 -37
  54. package/dist/martyrs/src/modules/music/components/pages/Track.vue.cjs.map +1 -1
  55. package/dist/martyrs/src/modules/music/components/pages/Track.vue.js +45 -39
  56. package/dist/martyrs/src/modules/music/components/pages/Track.vue.js.map +1 -1
  57. package/dist/martyrs/src/modules/music/music.client.cjs.map +1 -1
  58. package/dist/martyrs/src/modules/music/music.client.js.map +1 -1
  59. package/dist/martyrs/src/modules/notifications/notifications.client.cjs +1 -1
  60. package/dist/martyrs/src/modules/notifications/notifications.client.cjs.map +1 -1
  61. package/dist/martyrs/src/modules/notifications/notifications.client.js +1 -1
  62. package/dist/martyrs/src/modules/notifications/notifications.client.js.map +1 -1
  63. package/dist/style.css +5 -5
  64. package/package.json +1 -1
  65. package/src/builder/rspack/rspack.config.spa.client.js +0 -44
  66. package/src/builder/rspack/rspack.config.ssr.client.js +41 -41
  67. package/src/modules/globals/globals.client.js +1 -2
  68. package/src/modules/globals/views/components/layouts/Client.vue +13 -11
  69. package/src/modules/globals/views/router/scrollBehavior.js +1 -1
  70. package/src/modules/music/components/SidebarMusic.vue +7 -7
  71. package/src/modules/music/components/cards/TrackListCard.vue +1 -1
  72. package/src/modules/music/components/forms/SearchForm.vue +1 -1
  73. package/src/modules/music/components/pages/Album.vue +26 -29
  74. package/src/modules/music/components/pages/Artist.vue +4 -4
  75. package/src/modules/music/components/pages/MusicLibrary.vue +5 -5
  76. package/src/modules/music/components/pages/Playlist.vue +9 -9
  77. package/src/modules/music/components/pages/SearchResults.vue +6 -6
  78. package/src/modules/music/components/pages/Track.vue +22 -20
  79. package/src/modules/music/music.client.js +1 -1
  80. package/src/modules/notifications/notifications.client.js +1 -1
@@ -6,9 +6,9 @@ import Media from "../../../../components/Media/Media.vue.js";
6
6
  import _sfc_main$5 from "../../../../components/Dropdown/Dropdown.vue.js";
7
7
  import _sfc_main$b from "../../../../components/Feed/Feed.vue.js";
8
8
  import _sfc_main$2 from "../../../icons/navigation/IconPlay.vue.js";
9
- import _sfc_main$3 from "../../../icons/navigation/IconLike.vue.js";
9
+ import _sfc_main$4 from "../../../icons/navigation/IconLike.vue.js";
10
10
  import _sfc_main$6 from "../../../icons/navigation/IconEllipsis.vue.js";
11
- import _sfc_main$4 from "../../../icons/navigation/IconShuffle.vue.js";
11
+ import _sfc_main$3 from "../../../icons/navigation/IconShuffle.vue.js";
12
12
  import _sfc_main$8 from "../../../icons/entities/IconCalendar.vue.js";
13
13
  import IconClock from "../../../icons/entities/IconTime.vue.js";
14
14
  import _sfc_main$a from "../../../icons/actions/IconShow.vue.js";
@@ -48,7 +48,7 @@ const _hoisted_15 = {
48
48
  };
49
49
  const _hoisted_16 = { class: "h1 mn-b-medium" };
50
50
  const _hoisted_17 = { class: "flex gap-small mn-b-medium" };
51
- const _hoisted_18 = { class: "dropdown-menu bg-dark pd-small radius-medium shadow-big mn-t-thin" };
51
+ const _hoisted_18 = { class: "dropdown-menu bg-white pd-small radius-medium shadow-big mn-t-thin" };
52
52
  const _hoisted_19 = { class: "artists-section mn-b-big" };
53
53
  const _hoisted_20 = {
54
54
  key: 0,
@@ -64,41 +64,37 @@ const _hoisted_24 = { class: "flex items-center gap-thin" };
64
64
  const _hoisted_25 = { class: "t-large" };
65
65
  const _hoisted_26 = { class: "metadata-grid grid cols-2 gap-small mn-b-big" };
66
66
  const _hoisted_27 = { class: "metadata-card bg-light pd-medium radius-medium flex items-center gap-medium" };
67
- const _hoisted_28 = { class: "icon-wrapper bg-primary-transp-20 w-3r h-3r radius-small flex-center" };
68
- const _hoisted_29 = { class: "t-medium" };
69
- const _hoisted_30 = { class: "metadata-card bg-light pd-medium radius-medium flex items-center gap-medium" };
70
- const _hoisted_31 = { class: "icon-wrapper bg-primary-transp-20 w-3r h-3r radius-small flex-center" };
71
- const _hoisted_32 = { class: "t-medium" };
72
- const _hoisted_33 = {
67
+ const _hoisted_28 = { class: "t-medium" };
68
+ const _hoisted_29 = { class: "metadata-card bg-light pd-medium radius-medium flex items-center gap-medium" };
69
+ const _hoisted_30 = { class: "t-medium" };
70
+ const _hoisted_31 = {
73
71
  key: 0,
74
72
  class: "metadata-card bg-light pd-medium radius-medium flex items-center gap-medium"
75
73
  };
76
- const _hoisted_34 = { class: "icon-wrapper bg-primary-transp-20 w-3r h-3r radius-small flex-center" };
77
- const _hoisted_35 = { class: "t-medium" };
78
- const _hoisted_36 = { class: "metadata-card bg-light pd-medium radius-medium flex items-center gap-medium" };
79
- const _hoisted_37 = { class: "icon-wrapper bg-primary-transp-20 w-3r h-3r radius-small flex-center" };
80
- const _hoisted_38 = { class: "t-medium" };
81
- const _hoisted_39 = {
74
+ const _hoisted_32 = { class: "t-medium" };
75
+ const _hoisted_33 = { class: "metadata-card bg-light pd-medium radius-medium flex items-center gap-medium" };
76
+ const _hoisted_34 = { class: "t-medium" };
77
+ const _hoisted_35 = {
82
78
  key: 0,
83
79
  class: "tags-section mn-b-medium"
84
80
  };
85
- const _hoisted_40 = { class: "flex gap-thin flex-wrap" };
86
- const _hoisted_41 = {
81
+ const _hoisted_36 = { class: "flex gap-thin flex-wrap" };
82
+ const _hoisted_37 = {
87
83
  key: 1,
88
84
  class: "description-section bg-light pd-medium radius-medium mn-b-medium"
89
85
  };
90
- const _hoisted_42 = { class: "t-transp" };
91
- const _hoisted_43 = {
86
+ const _hoisted_38 = { class: "t-transp" };
87
+ const _hoisted_39 = {
92
88
  key: 3,
93
89
  class: "tracks-section mn-t-big"
94
90
  };
95
- const _hoisted_44 = { class: "bg-light radius-medium o-hidden" };
96
- const _hoisted_45 = {
91
+ const _hoisted_40 = { class: "bg-light radius-medium o-hidden" };
92
+ const _hoisted_41 = {
97
93
  key: 4,
98
94
  class: "more-albums-section mn-t-big"
99
95
  };
100
- const _hoisted_46 = { class: "flex justify-between items-center mn-b-medium" };
101
- const _hoisted_47 = { class: "flex flex-nowrap gap-small o-x-scroll overscroll-behavior-x-contain scroll-behavior-smooth scroll-snap-type-x-mandatory scroll-hide" };
96
+ const _hoisted_42 = { class: "flex justify-between items-center mn-b-medium" };
97
+ const _hoisted_43 = { class: "flex flex-nowrap gap-small o-x-scroll overscroll-behavior-x-contain scroll-behavior-smooth scroll-snap-type-x-mandatory scroll-hide" };
102
98
  const _sfc_main = {
103
99
  __name: "Album",
104
100
  emits: ["page-loading", "page-loaded"],
@@ -261,40 +257,46 @@ const _sfc_main = {
261
257
  onClick: playAlbum,
262
258
  color: "primary",
263
259
  size: "medium",
264
- class: "flex-1 flex-center gap-thin"
260
+ class: "flex-1 t-white bg-black radius-thin flex-center gap-thin"
265
261
  }, {
266
262
  default: withCtx(() => [
267
- createVNode(_sfc_main$2, { class: "w-1r h-1r" }),
263
+ createVNode(_sfc_main$2, {
264
+ fill: "rgb(var(--white))",
265
+ class: "i-medium"
266
+ }),
268
267
  _cache[4] || (_cache[4] = createTextVNode(" Play All "))
269
268
  ]),
270
269
  _: 1
271
270
  }),
272
271
  createVNode(_sfc_main$1, {
273
- onClick: toggleFavorite,
274
- color: isFavorite.value ? "danger" : "transp",
272
+ onClick: shufflePlay,
273
+ color: "primary",
275
274
  size: "medium",
276
- class: "w-3r h-3r radius-full"
275
+ class: "flex-1 bg-light radius-thin flex-center gap-thin"
277
276
  }, {
278
277
  default: withCtx(() => [
279
- createVNode(_sfc_main$3, {
280
- class: "w-1-25r h-1-25r",
281
- fill: isFavorite.value
282
- }, null, 8, ["fill"])
278
+ createVNode(_sfc_main$3, { class: "i-medium" }),
279
+ _cache[5] || (_cache[5] = createTextVNode(" Shuffle "))
283
280
  ]),
284
281
  _: 1
285
- }, 8, ["color"]),
282
+ }),
286
283
  createVNode(_sfc_main$1, {
287
- onClick: shufflePlay,
288
- color: "transp",
284
+ onClick: toggleFavorite,
285
+ color: "primary",
289
286
  size: "medium",
290
- class: "w-3r h-3r radius-full"
287
+ class: "flex-1 bg-light radius-thin flex-center gap-thin"
291
288
  }, {
292
289
  default: withCtx(() => [
293
- createVNode(_sfc_main$4, { class: "w-1-25r h-1-25r" })
290
+ createVNode(_sfc_main$4, {
291
+ class: "i-medium",
292
+ fill: isFavorite.value
293
+ }, null, 8, ["fill"]),
294
+ createTextVNode(" " + toDisplayString(isFavorite.value ? "Liked" : "Like"), 1)
294
295
  ]),
295
296
  _: 1
296
297
  }),
297
298
  createVNode(_sfc_main$5, {
299
+ label: { component: _sfc_main$6, class: "bg-light radius-thin pd-thin i-big" },
298
300
  modelValue: showDropdown.value,
299
301
  "onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => showDropdown.value = $event),
300
302
  class: "relative"
@@ -319,7 +321,7 @@ const _sfc_main = {
319
321
  size: "small",
320
322
  class: "w-100 justify-start"
321
323
  }, {
322
- default: withCtx(() => _cache[5] || (_cache[5] = [
324
+ default: withCtx(() => _cache[6] || (_cache[6] = [
323
325
  createTextVNode(" Add to Queue ")
324
326
  ])),
325
327
  _: 1
@@ -330,20 +332,31 @@ const _sfc_main = {
330
332
  size: "small",
331
333
  class: "w-100 justify-start"
332
334
  }, {
333
- default: withCtx(() => _cache[6] || (_cache[6] = [
335
+ default: withCtx(() => _cache[7] || (_cache[7] = [
334
336
  createTextVNode(" Copy Link ")
335
337
  ])),
336
338
  _: 1
337
339
  }),
340
+ createVNode(_sfc_main$1, {
341
+ onClick: _ctx.addToPlaylist,
342
+ color: "transp",
343
+ size: "small",
344
+ class: "w-100 justify-start"
345
+ }, {
346
+ default: withCtx(() => _cache[8] || (_cache[8] = [
347
+ createTextVNode(" Add to Playlist ")
348
+ ])),
349
+ _: 1
350
+ }, 8, ["onClick"]),
338
351
  isOwner.value ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [
339
- _cache[9] || (_cache[9] = createElementVNode("hr", { class: "mn-v-thin border-dark-transp-10" }, null, -1)),
352
+ _cache[11] || (_cache[11] = createElementVNode("hr", { class: "mn-v-thin border-dark-transp-10" }, null, -1)),
340
353
  createVNode(_sfc_main$1, {
341
354
  onClick: editAlbum,
342
355
  color: "transp",
343
356
  size: "small",
344
357
  class: "w-100 justify-start"
345
358
  }, {
346
- default: withCtx(() => _cache[7] || (_cache[7] = [
359
+ default: withCtx(() => _cache[9] || (_cache[9] = [
347
360
  createTextVNode(" Edit Album ")
348
361
  ])),
349
362
  _: 1
@@ -354,7 +367,7 @@ const _sfc_main = {
354
367
  size: "small",
355
368
  class: "w-100 justify-start"
356
369
  }, {
357
- default: withCtx(() => _cache[8] || (_cache[8] = [
370
+ default: withCtx(() => _cache[10] || (_cache[10] = [
358
371
  createTextVNode(" Delete Album ")
359
372
  ])),
360
373
  _: 1
@@ -363,7 +376,7 @@ const _sfc_main = {
363
376
  ])
364
377
  ]),
365
378
  _: 1
366
- }, 8, ["modelValue"])
379
+ }, 8, ["label", "modelValue"])
367
380
  ]),
368
381
  createElementVNode("div", _hoisted_19, [
369
382
  album.value.artists && album.value.artists.length > 1 ? (openBlock(), createElementBlock("h3", _hoisted_20, "Artists")) : createCommentVNode("", true),
@@ -374,7 +387,7 @@ const _sfc_main = {
374
387
  class: "artist-card bg-light pd-medium radius-medium flex items-center gap-medium"
375
388
  }, [
376
389
  createVNode(_component_router_link, {
377
- to: `/artist/${artist.url}`,
390
+ to: { name: "artist", params: { url: artist.url } },
378
391
  class: "flex items-center gap-medium flex-1 hover-opacity"
379
392
  }, {
380
393
  default: withCtx(() => [
@@ -394,7 +407,7 @@ const _sfc_main = {
394
407
  class: "w-1r h-1r t-primary"
395
408
  })) : createCommentVNode("", true)
396
409
  ]),
397
- _cache[10] || (_cache[10] = createElementVNode("span", { class: "t-small t-transp" }, "Artist", -1))
410
+ _cache[12] || (_cache[12] = createElementVNode("span", { class: "t-small t-transp" }, "Artist", -1))
398
411
  ])
399
412
  ]),
400
413
  _: 2
@@ -416,45 +429,37 @@ const _sfc_main = {
416
429
  ]),
417
430
  createElementVNode("div", _hoisted_26, [
418
431
  createElementVNode("div", _hoisted_27, [
419
- createElementVNode("div", _hoisted_28, [
420
- createVNode(_sfc_main$8, { class: "w-1-5r h-1-5r t-primary" })
421
- ]),
432
+ createVNode(_sfc_main$8, { class: "i-regular t-primary" }),
422
433
  createElementVNode("div", null, [
423
- _cache[11] || (_cache[11] = createElementVNode("div", { class: "t-small t-transp t-uppercase" }, "Released", -1)),
424
- createElementVNode("div", _hoisted_29, toDisplayString(formatDate(album.value.releaseDate)), 1)
434
+ _cache[13] || (_cache[13] = createElementVNode("div", { class: "t-small t-transp t-uppercase" }, "Released", -1)),
435
+ createElementVNode("div", _hoisted_28, toDisplayString(formatDate(album.value.releaseDate)), 1)
425
436
  ])
426
437
  ]),
427
- createElementVNode("div", _hoisted_30, [
428
- createElementVNode("div", _hoisted_31, [
429
- createVNode(IconClock, { class: "w-1-5r h-1-5r t-primary" })
430
- ]),
438
+ createElementVNode("div", _hoisted_29, [
439
+ createVNode(IconClock, { class: "i-regular t-primary" }),
431
440
  createElementVNode("div", null, [
432
- _cache[12] || (_cache[12] = createElementVNode("div", { class: "t-small t-transp t-uppercase" }, "Duration", -1)),
433
- createElementVNode("div", _hoisted_32, toDisplayString(totalDuration.value), 1)
441
+ _cache[14] || (_cache[14] = createElementVNode("div", { class: "t-small t-transp t-uppercase" }, "Duration", -1)),
442
+ createElementVNode("div", _hoisted_30, toDisplayString(totalDuration.value), 1)
434
443
  ])
435
444
  ]),
436
- album.value.label ? (openBlock(), createElementBlock("div", _hoisted_33, [
437
- createElementVNode("div", _hoisted_34, [
438
- createVNode(_sfc_main$9, { class: "w-1-5r h-1-5r t-primary" })
439
- ]),
445
+ album.value.label ? (openBlock(), createElementBlock("div", _hoisted_31, [
446
+ createVNode(_sfc_main$9, { class: "i-regular t-primary" }),
440
447
  createElementVNode("div", null, [
441
- _cache[13] || (_cache[13] = createElementVNode("div", { class: "t-small t-transp t-uppercase" }, "Label", -1)),
442
- createElementVNode("div", _hoisted_35, toDisplayString(album.value.label), 1)
448
+ _cache[15] || (_cache[15] = createElementVNode("div", { class: "t-small t-transp t-uppercase" }, "Label", -1)),
449
+ createElementVNode("div", _hoisted_32, toDisplayString(album.value.label), 1)
443
450
  ])
444
451
  ])) : createCommentVNode("", true),
445
- createElementVNode("div", _hoisted_36, [
446
- createElementVNode("div", _hoisted_37, [
447
- createVNode(_sfc_main$a, { class: "w-1-5r h-1-5r t-primary" })
448
- ]),
452
+ createElementVNode("div", _hoisted_33, [
453
+ createVNode(_sfc_main$a, { class: "i-regular t-primary" }),
449
454
  createElementVNode("div", null, [
450
- _cache[14] || (_cache[14] = createElementVNode("div", { class: "t-small t-transp t-uppercase" }, "Visibility", -1)),
451
- createElementVNode("div", _hoisted_38, toDisplayString(album.value.isPublic ? "Public" : "Private"), 1)
455
+ _cache[16] || (_cache[16] = createElementVNode("div", { class: "t-small t-transp t-uppercase" }, "Visibility", -1)),
456
+ createElementVNode("div", _hoisted_34, toDisplayString(album.value.isPublic ? "Public" : "Private"), 1)
452
457
  ])
453
458
  ])
454
459
  ]),
455
- album.value.genres && album.value.genres.length || album.value.tags && album.value.tags.length ? (openBlock(), createElementBlock("div", _hoisted_39, [
456
- _cache[15] || (_cache[15] = createElementVNode("h3", { class: "t-medium mn-b-small" }, "Genres & Tags", -1)),
457
- createElementVNode("div", _hoisted_40, [
460
+ album.value.genres && album.value.genres.length || album.value.tags && album.value.tags.length ? (openBlock(), createElementBlock("div", _hoisted_35, [
461
+ _cache[17] || (_cache[17] = createElementVNode("h3", { class: "t-medium mn-b-small" }, "Genres & Tags", -1)),
462
+ createElementVNode("div", _hoisted_36, [
458
463
  (openBlock(true), createElementBlock(Fragment, null, renderList(album.value.genres, (genre) => {
459
464
  return openBlock(), createElementBlock("span", {
460
465
  key: genre,
@@ -469,14 +474,14 @@ const _sfc_main = {
469
474
  }), 128))
470
475
  ])
471
476
  ])) : createCommentVNode("", true),
472
- album.value.description ? (openBlock(), createElementBlock("div", _hoisted_41, [
473
- _cache[16] || (_cache[16] = createElementVNode("h3", { class: "t-medium mn-b-small" }, "About", -1)),
474
- createElementVNode("p", _hoisted_42, toDisplayString(album.value.description), 1)
477
+ album.value.description ? (openBlock(), createElementBlock("div", _hoisted_37, [
478
+ _cache[18] || (_cache[18] = createElementVNode("h3", { class: "t-medium mn-b-small" }, "About", -1)),
479
+ createElementVNode("p", _hoisted_38, toDisplayString(album.value.description), 1)
475
480
  ])) : createCommentVNode("", true)
476
481
  ])
477
482
  ])) : createCommentVNode("", true),
478
- !_ctx.isLoading && album.value && albumTracks.value.length ? (openBlock(), createElementBlock("section", _hoisted_43, [
479
- _cache[17] || (_cache[17] = createElementVNode("h2", { class: "h2 mn-b-medium" }, "Tracklist", -1)),
483
+ !_ctx.isLoading && album.value && albumTracks.value.length ? (openBlock(), createElementBlock("section", _hoisted_39, [
484
+ _cache[19] || (_cache[19] = createElementVNode("h2", { class: "h2 mn-b-medium" }, "Tracklist", -1)),
480
485
  createVNode(_sfc_main$b, {
481
486
  store: {
482
487
  read: () => _ctx.Promise.resolve(albumTracks.value),
@@ -493,7 +498,7 @@ const _sfc_main = {
493
498
  }
494
499
  }, {
495
500
  default: withCtx(({ items }) => [
496
- createElementVNode("div", _hoisted_44, [
501
+ createElementVNode("div", _hoisted_40, [
497
502
  (openBlock(true), createElementBlock(Fragment, null, renderList(items, (track, index) => {
498
503
  return openBlock(), createBlock(_sfc_main$c, {
499
504
  key: track._id,
@@ -508,21 +513,21 @@ const _sfc_main = {
508
513
  _: 1
509
514
  }, 8, ["store", "items"])
510
515
  ])) : createCommentVNode("", true),
511
- !_ctx.isLoading && album.value && moreAlbums.value.length ? (openBlock(), createElementBlock("section", _hoisted_45, [
512
- createElementVNode("div", _hoisted_46, [
513
- _cache[19] || (_cache[19] = createElementVNode("h2", { class: "h2" }, "More Albums", -1)),
516
+ !_ctx.isLoading && album.value && moreAlbums.value.length ? (openBlock(), createElementBlock("section", _hoisted_41, [
517
+ createElementVNode("div", _hoisted_42, [
518
+ _cache[21] || (_cache[21] = createElementVNode("h2", { class: "h2" }, "More Albums", -1)),
514
519
  album.value.artists && album.value.artists[0] ? (openBlock(), createBlock(_component_router_link, {
515
520
  key: 0,
516
- to: `/artist/${album.value.artists[0].url}`,
521
+ to: { name: "artist", params: { url: album.value.artists[0].url } },
517
522
  class: "t-primary hover-opacity"
518
523
  }, {
519
- default: withCtx(() => _cache[18] || (_cache[18] = [
524
+ default: withCtx(() => _cache[20] || (_cache[20] = [
520
525
  createTextVNode(" See all ")
521
526
  ])),
522
527
  _: 1
523
528
  }, 8, ["to"])) : createCommentVNode("", true)
524
529
  ]),
525
- createElementVNode("div", _hoisted_47, [
530
+ createElementVNode("div", _hoisted_43, [
526
531
  (openBlock(true), createElementBlock(Fragment, null, renderList(moreAlbums.value, (relatedAlbum) => {
527
532
  return openBlock(), createElementBlock("li", {
528
533
  key: album.value._id,
@@ -1 +1 @@
1
- {"version":3,"file":"Album.vue.js","sources":["../../../../../../../src/modules/music/components/pages/Album.vue"],"sourcesContent":["<!-- components/pages/Album.vue -->\n<template>\n <div class=\"album-page pd-small\">\n <!-- Loading -->\n <div v-if=\"isLoading\" class=\"w-100 h-25r flex-center flex\">\n <Loader />\n </div>\n \n <!-- Not Found -->\n <div v-if=\"hasLoaded && !album\" class=\"t-center pd-big\">\n <h2 class=\"\">Album not found</h2>\n <p class=\"t-transp t-medium\">The album you're looking for doesn't exist or has been removed.</p>\n </div>\n \n <!-- Album Content -->\n <div v-if=\"album\" class=\"album-content cols-2-fit-content mobile:cols-1 gap-big\">\n <!-- Left Column - Cover & Stats -->\n <div class=\"pos-sticky pos-t-0 mobile:pos-relative album-cover-section\">\n <!-- Cover -->\n <div class=\"cover-container relative mn-b-medium radius-big overflow-hidden shadow-big\">\n <Media \n :url=\"album.coverArt || '/logo/logo-placeholder.jpg'\"\n :alt=\"album.title\"\n class=\"aspect-1x1 w-100 radius-medium o-hidden\"\n />\n </div>\n\n <!-- Quick Stats -->\n <div class=\"stats-grid grid cols-2 gap-small\">\n <div class=\"stat-card bg-light pd-medium radius-medium t-center\">\n <div class=\" mn-b-thin\">{{ album.totalTracks || 0 }}</div>\n <div class=\"t-small t-transp t-uppercase\">Tracks</div>\n </div>\n <div class=\"stat-card bg-light pd-medium radius-medium t-center\">\n <div class=\" mn-b-thin\">{{ formatNumber(album.views) }}</div>\n <div class=\"t-small t-transp t-uppercase\">Views</div>\n </div>\n </div>\n </div>\n\n <!-- Right Column - Album Details -->\n <div class=\"album-details-section\">\n <!-- Album Type Badge -->\n <div class=\"flex items-center gap-small mn-b-small\">\n <span class=\"badge bg-primary-transp-20 t-primary pd-thin-big radius-small t-small t-uppercase\">\n {{ album.type }}\n </span>\n <span v-if=\"album.status === 'published'\" class=\"badge bg-success-transp-20 t-success pd-thin-big radius-small t-small\">\n Published\n </span>\n </div>\n\n <!-- Album Title -->\n <h1 class=\"h1 mn-b-medium\">{{ album.title }}</h1>\n\n <!-- Action Buttons -->\n <div class=\"flex gap-small mn-b-medium\">\n <Button\n @click=\"playAlbum\"\n color=\"primary\"\n size=\"medium\"\n class=\"flex-1 flex-center gap-thin\"\n >\n <IconPlay class=\"w-1r h-1r\" />\n Play All\n </Button>\n \n <Button\n @click=\"toggleFavorite\"\n :color=\"isFavorite ? 'danger' : 'transp'\"\n size=\"medium\"\n class=\"w-3r h-3r radius-full\"\n >\n <IconLike class=\"w-1-25r h-1-25r\" :fill=\"isFavorite\" />\n </Button>\n \n <Button\n @click=\"shufflePlay\"\n color=\"transp\"\n size=\"medium\"\n class=\"w-3r h-3r radius-full\"\n >\n <IconShuffle class=\"w-1-25r h-1-25r\" />\n </Button>\n \n <Dropdown v-model=\"showDropdown\" class=\"relative\">\n <template #trigger>\n <Button color=\"transp\" size=\"medium\" class=\"w-3r h-3r radius-full\">\n <IconEllipsis class=\"w-1-25r h-1-25r\" />\n </Button>\n </template>\n <template #default>\n <div class=\"dropdown-menu bg-dark pd-small radius-medium shadow-big mn-t-thin\">\n <Button @click=\"addToQueue\" color=\"transp\" size=\"small\" class=\"w-100 justify-start\">\n Add to Queue\n </Button>\n <Button @click=\"copyLink\" color=\"transp\" size=\"small\" class=\"w-100 justify-start\">\n Copy Link\n </Button>\n <template v-if=\"isOwner\">\n <hr class=\"mn-v-thin border-dark-transp-10\" />\n <Button @click=\"editAlbum\" color=\"transp\" size=\"small\" class=\"w-100 justify-start\">\n Edit Album\n </Button>\n <Button @click=\"deleteAlbum\" color=\"danger\" size=\"small\" class=\"w-100 justify-start\">\n Delete Album\n </Button>\n </template>\n </div>\n </template>\n </Dropdown>\n </div>\n\n <!-- Artists Cards -->\n <div class=\"artists-section mn-b-big\">\n <h3 class=\"t-medium mn-b-small\" v-if=\"album.artists && album.artists.length > 1\">Artists</h3>\n <div class=\"flex flex-col gap-small\">\n <div \n v-for=\"artist in album.artists\" \n :key=\"artist._id\"\n class=\"artist-card bg-light pd-medium radius-medium flex items-center gap-medium\"\n >\n <router-link \n :to=\"`/artist/${artist.url}`\"\n class=\"flex items-center gap-medium flex-1 hover-opacity\"\n >\n <div class=\"artist-avatar\">\n <Media \n v-if=\"artist.photoUrl\"\n :url=\"artist.photoUrl\"\n :alt=\"artist.name\"\n class=\"w-4r h-4r radius-full object-cover\"\n />\n <div v-else class=\"w-4r h-4r radius-full bg-primary flex-center \">\n {{ artist.name.charAt(0) }}\n </div>\n </div>\n <div>\n <div class=\"flex items-center gap-thin\">\n <span class=\"t-large \">{{ artist.name }}</span>\n <IconVerified v-if=\"artist.isVerified\" class=\"w-1r h-1r t-primary\" />\n </div>\n <span class=\"t-small t-transp\">Artist</span>\n </div>\n </router-link>\n <Button \n v-if=\"!isOwner\"\n @click=\"() => toggleFollowArtist(artist._id)\"\n :color=\"followedArtists.includes(artist._id) ? 'primary' : 'transp'\"\n size=\"small\"\n >\n {{ followedArtists.includes(artist._id) ? 'Following' : 'Follow' }}\n </Button>\n </div>\n </div>\n </div>\n\n <!-- Metadata Cards -->\n <div class=\"metadata-grid grid cols-2 gap-small mn-b-big\">\n <!-- Release Date -->\n <div class=\"metadata-card bg-light pd-medium radius-medium flex items-center gap-medium\">\n <div class=\"icon-wrapper bg-primary-transp-20 w-3r h-3r radius-small flex-center\">\n <IconCalendar class=\"w-1-5r h-1-5r t-primary\" />\n </div>\n <div>\n <div class=\"t-small t-transp t-uppercase\">Released</div>\n <div class=\"t-medium \">{{ formatDate(album.releaseDate) }}</div>\n </div>\n </div>\n\n <!-- Total Duration -->\n <div class=\"metadata-card bg-light pd-medium radius-medium flex items-center gap-medium\">\n <div class=\"icon-wrapper bg-primary-transp-20 w-3r h-3r radius-small flex-center\">\n <IconClock class=\"w-1-5r h-1-5r t-primary\" />\n </div>\n <div>\n <div class=\"t-small t-transp t-uppercase\">Duration</div>\n <div class=\"t-medium \">{{ totalDuration }}</div>\n </div>\n </div>\n\n <!-- Label -->\n <div v-if=\"album.label\" class=\"metadata-card bg-light pd-medium radius-medium flex items-center gap-medium\">\n <div class=\"icon-wrapper bg-primary-transp-20 w-3r h-3r radius-small flex-center\">\n <IconDisc class=\"w-1-5r h-1-5r t-primary\" />\n </div>\n <div>\n <div class=\"t-small t-transp t-uppercase\">Label</div>\n <div class=\"t-medium \">{{ album.label }}</div>\n </div>\n </div>\n\n <!-- Visibility -->\n <div class=\"metadata-card bg-light pd-medium radius-medium flex items-center gap-medium\">\n <div class=\"icon-wrapper bg-primary-transp-20 w-3r h-3r radius-small flex-center\">\n <IconEye class=\"w-1-5r h-1-5r t-primary\" />\n </div>\n <div>\n <div class=\"t-small t-transp t-uppercase\">Visibility</div>\n <div class=\"t-medium \">{{ album.isPublic ? 'Public' : 'Private' }}</div>\n </div>\n </div>\n </div>\n\n <!-- Genres & Tags -->\n <div v-if=\"(album.genres && album.genres.length) || (album.tags && album.tags.length)\" class=\"tags-section mn-b-medium\">\n <h3 class=\"t-medium mn-b-small\">Genres & Tags</h3>\n <div class=\"flex gap-thin flex-wrap\">\n <span \n v-for=\"genre in album.genres\" \n :key=\"genre\"\n class=\"tag bg-primary-transp-20 t-primary pd-thin-big radius-small t-small hover-bg-primary-transp-30 cursor-pointer\"\n >\n {{ genre }}\n </span>\n <span \n v-for=\"tag in album.tags\" \n :key=\"tag\"\n class=\"tag bg-light t-transp pd-thin-big radius-small t-small hover-bg-light cursor-pointer\"\n >\n #{{ tag }}\n </span>\n </div>\n </div>\n\n <!-- Description -->\n <div v-if=\"album.description\" class=\"description-section bg-light pd-medium radius-medium mn-b-medium\">\n <h3 class=\"t-medium mn-b-small\">About</h3>\n <p class=\"t-transp\">{{ album.description }}</p>\n </div>\n </div>\n </div>\n\n <!-- Album Tracks -->\n <section v-if=\"!isLoading && album && albumTracks.length\" class=\"tracks-section mn-t-big\">\n <h2 class=\"h2 mn-b-medium\">Tracklist</h2>\n <Feed\n :store=\"{\n read: () => Promise.resolve(albumTracks),\n state: { isLoading: false }\n }\"\n :external=\"true\"\n :items=\"albumTracks\"\n :states=\"{\n empty: {\n title: 'No tracks in album',\n description: 'This album appears to be empty',\n class: 'pd-medium t-center'\n }\n }\"\n >\n <template #default=\"{ items }\">\n <div class=\"bg-light radius-medium o-hidden\">\n <TrackListCard\n v-for=\"(track, index) in items\"\n :key=\"track._id\"\n :track=\"track\"\n :index=\"index + 1\"\n :showAlbum=\"false\"\n :showCover=\"false\"\n />\n </div>\n </template>\n </Feed>\n </section>\n\n <!-- More from Artists -->\n <section v-if=\"!isLoading && album && moreAlbums.length\" class=\"more-albums-section mn-t-big\">\n <div class=\"flex justify-between items-center mn-b-medium\">\n <h2 class=\"h2\">More Albums</h2>\n <router-link \n v-if=\"album.artists && album.artists[0]\"\n :to=\"`/artist/${album.artists[0].url}`\" \n class=\"t-primary hover-opacity\"\n >\n See all\n </router-link>\n </div>\n <div class=\"flex flex-nowrap gap-small o-x-scroll overscroll-behavior-x-contain scroll-behavior-smooth scroll-snap-type-x-mandatory scroll-hide\"\n >\n <li v-for=\"relatedAlbum in moreAlbums\" :key=\"album._id\" class=\"flex-none scroll-snap-align-start\">\n <AlbumCard :album=\"relatedAlbum\" class=\"w-min-15r transition-cubic-in-out\" />\n </li>\n </div>\n </section>\n </div>\n</template>\n\n<script setup>\nimport { ref, computed, onMounted, watch } from 'vue';\nimport { useRoute, useRouter } from 'vue-router';\nimport Button from '@martyrs/src/components/Button/Button.vue';\nimport Loader from '@martyrs/src/components/Loader/Loader.vue';\nimport Media from '@martyrs/src/components/Media/Media.vue';\nimport Dropdown from '@martyrs/src/components/Dropdown/Dropdown.vue';\nimport Feed from '@martyrs/src/components/Feed/Feed.vue';\n\n// Icons\nimport IconPlay from '@martyrs/src/modules/icons/navigation/IconPlay.vue';\nimport IconLike from '@martyrs/src/modules/icons/navigation/IconLike.vue';\nimport IconEllipsis from '@martyrs/src/modules/icons/navigation/IconEllipsis.vue';\nimport IconShuffle from '@martyrs/src/modules/icons/navigation/IconShuffle.vue';\nimport IconCalendar from '@martyrs/src/modules/icons/entities/IconCalendar.vue';\nimport IconClock from '@martyrs/src/modules/icons/entities/IconTime.vue';\nimport IconEye from '@martyrs/src/modules/icons/actions/IconShow.vue';\nimport IconDisc from '@martyrs/src/modules/icons/entities/IconMusic.vue';\nimport IconVerified from '@martyrs/src/modules/icons/navigation/IconCheckmark.vue';\n\n// Components\nimport TrackListCard from '../cards/TrackListCard.vue';\nimport AlbumCard from '../cards/AlbumCard.vue';\n\n// Store\nimport { state as albumsState, actions as albumsActions } from '../../store/albums.js';\nimport { actions as playerActions } from '../../store/player.js';\nimport { state as authState } from '@martyrs/src/modules/auth/views/store/auth.js';\n\nconst route = useRoute();\nconst router = useRouter();\n\n// Emits\nconst emits = defineEmits(['page-loading', 'page-loaded']);\n\n// State\nconst hasLoaded = ref(false);\nconst isFavorite = ref(false);\nconst showDropdown = ref(false);\nconst followedArtists = ref([]);\nconst moreAlbums = ref([]);\n\n// Clear state\nalbumsState.currentAlbum = null;\nalbumsState.currentAlbumTracks = [];\n\n// Computed\nconst album = computed(() => albumsState.currentAlbum);\nconst albumTracks = computed(() => albumsState.currentAlbumTracks || []);\n\nconst isOwner = computed(() => {\n return album.value?.owner?.target === authState.user?._id;\n});\n\nconst totalDuration = computed(() => {\n if (!albumTracks.value.length) return '0:00';\n const totalSeconds = albumTracks.value.reduce((sum, track) => sum + (track.duration || 0), 0);\n return formatDuration(totalSeconds);\n});\n\n// Format helpers\nconst formatDate = (dateString) => {\n if (!dateString) return 'Unknown';\n return new Date(dateString).toLocaleDateString('en-US', {\n year: 'numeric',\n month: 'long',\n day: 'numeric'\n });\n};\n\nconst formatDuration = (seconds) => {\n if (!seconds) return '0:00';\n const h = Math.floor(seconds / 3600);\n const m = Math.floor((seconds % 3600) / 60);\n const s = Math.floor(seconds % 60);\n \n if (h > 0) {\n return `${h}:${m.toString().padStart(2, '0')}:${s.toString().padStart(2, '0')}`;\n }\n return `${m}:${s.toString().padStart(2, '0')}`;\n};\n\nconst formatNumber = (num) => {\n if (!num) return '0';\n if (num >= 1000000) {\n return (num / 1000000).toFixed(1) + 'M';\n } else if (num >= 1000) {\n return (num / 1000).toFixed(1) + 'K';\n }\n return num.toString();\n};\n\n// Actions\nconst playAlbum = () => {\n if (albumTracks.value && albumTracks.value.length > 0) {\n playerActions.setQueue(albumTracks.value);\n }\n};\n\nconst shufflePlay = () => {\n if (albumTracks.value && albumTracks.value.length > 0) {\n const shuffled = [...albumTracks.value].sort(() => Math.random() - 0.5);\n playerActions.setQueue(shuffled);\n }\n};\n\nconst toggleFavorite = () => {\n isFavorite.value = !isFavorite.value;\n // TODO: Implement actual saving\n};\n\nconst toggleFollowArtist = (artistId) => {\n const index = followedArtists.value.indexOf(artistId);\n if (index > -1) {\n followedArtists.value.splice(index, 1);\n } else {\n followedArtists.value.push(artistId);\n }\n // TODO: Implement actual following\n};\n\nconst addToQueue = () => {\n if (albumTracks.value.length > 0) {\n albumTracks.value.forEach(track => {\n playerActions.addToQueue(track);\n });\n showDropdown.value = false;\n }\n};\n\nconst editAlbum = () => {\n router.push({ name: 'album-edit', params: { url: album.value.url } });\n};\n\nconst deleteAlbum = async () => {\n if (confirm('Are you sure you want to delete this album?')) {\n try {\n await albumsActions.deleteAlbum(album.value._id);\n router.push({ name: 'music-library' });\n } catch (error) {\n console.error('Failed to delete album:', error);\n }\n }\n};\n\nconst copyLink = () => {\n navigator.clipboard.writeText(window.location.href);\n showDropdown.value = false;\n};\n\n// Data fetching\nconst fetchAlbumData = async () => {\n try {\n await albumsActions.fetchAlbumByUrl(route.params.url);\n \n // Fetch more albums from the same artists\n if (album.value?.artists?.length) {\n const artistIds = album.value.artists.map(a => a._id);\n const albums = await albumsActions.fetchAlbums({\n artist: { $in: artistIds },\n status: 'published',\n isPublic: true,\n limit: 6\n });\n \n // Filter out current album\n moreAlbums.value = albums.filter(a => a._id !== album.value._id).slice(0, 5);\n }\n } catch (error) {\n console.error('Error fetching album data:', error);\n }\n};\n\n// Lifecycle\nonMounted(async () => {\n emits('page-loading');\n \n await fetchAlbumData();\n \n hasLoaded.value = true;\n emits('page-loaded');\n});\n</script>\n\n<style scoped>\n</style>"],"names":["albumsState","authState","playerActions","albumsActions"],"mappings":"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"}
1
+ {"version":3,"file":"Album.vue.js","sources":["../../../../../../../src/modules/music/components/pages/Album.vue"],"sourcesContent":["<!-- components/pages/Album.vue -->\n<template>\n <div class=\"album-page pd-small\">\n <!-- Loading -->\n <div v-if=\"isLoading\" class=\"w-100 h-25r flex-center flex\">\n <Loader />\n </div>\n \n <!-- Not Found -->\n <div v-if=\"hasLoaded && !album\" class=\"t-center pd-big\">\n <h2 class=\"\">Album not found</h2>\n <p class=\"t-transp t-medium\">The album you're looking for doesn't exist or has been removed.</p>\n </div>\n \n <!-- Album Content -->\n <div v-if=\"album\" class=\"album-content cols-2-fit-content mobile:cols-1 gap-big\">\n <!-- Left Column - Cover & Stats -->\n <div class=\"pos-sticky pos-t-0 mobile:pos-relative album-cover-section\">\n <!-- Cover -->\n <div class=\"cover-container relative mn-b-medium radius-big overflow-hidden shadow-big\">\n <Media \n :url=\"album.coverArt || '/logo/logo-placeholder.jpg'\"\n :alt=\"album.title\"\n class=\"aspect-1x1 w-100 radius-medium o-hidden\"\n />\n </div>\n\n <!-- Quick Stats -->\n <div class=\"stats-grid grid cols-2 gap-small\">\n <div class=\"stat-card bg-light pd-medium radius-medium t-center\">\n <div class=\" mn-b-thin\">{{ album.totalTracks || 0 }}</div>\n <div class=\"t-small t-transp t-uppercase\">Tracks</div>\n </div>\n <div class=\"stat-card bg-light pd-medium radius-medium t-center\">\n <div class=\" mn-b-thin\">{{ formatNumber(album.views) }}</div>\n <div class=\"t-small t-transp t-uppercase\">Views</div>\n </div>\n </div>\n </div>\n\n <!-- Right Column - Album Details -->\n <div class=\"album-details-section\">\n <!-- Album Type Badge -->\n <div class=\"flex items-center gap-small mn-b-small\">\n <span class=\"badge bg-primary-transp-20 t-primary pd-thin-big radius-small t-small t-uppercase\">\n {{ album.type }}\n </span>\n <span v-if=\"album.status === 'published'\" class=\"badge bg-success-transp-20 t-success pd-thin-big radius-small t-small\">\n Published\n </span>\n </div>\n\n <!-- Album Title -->\n <h1 class=\"h1 mn-b-medium\">{{ album.title }}</h1>\n\n <!-- Action Buttons -->\n <div class=\"flex gap-small mn-b-medium\">\n <Button\n @click=\"playAlbum\"\n color=\"primary\"\n size=\"medium\"\n class=\"flex-1 t-white bg-black radius-thin flex-center gap-thin\"\n >\n <IconPlay fill=\"rgb(var(--white))\" class=\"i-medium\" />\n Play All\n </Button>\n\n <Button\n @click=\"shufflePlay\"\n color=\"primary\"\n size=\"medium\"\n class=\"flex-1 bg-light radius-thin flex-center gap-thin\"\n >\n <IconShuffle class=\"i-medium\" />\n Shuffle\n </Button>\n\n <Button\n @click=\"toggleFavorite\"\n color=\"primary\"\n size=\"medium\"\n class=\"flex-1 bg-light radius-thin flex-center gap-thin\"\n >\n <IconLike class=\"i-medium\" :fill=\"isFavorite\" />\n {{isFavorite ? 'Liked' : 'Like'}}\n </Button>\n\n <Dropdown :label=\"{component: IconEllipsis, class: 'bg-light radius-thin pd-thin i-big' }\" v-model=\"showDropdown\" class=\"relative\">\n <template #trigger>\n <Button color=\"transp\" size=\"medium\" class=\"w-3r h-3r radius-full\">\n <IconEllipsis class=\"w-1-25r h-1-25r\" />\n </Button>\n </template>\n <template #default>\n <div class=\"dropdown-menu bg-white pd-small radius-medium shadow-big mn-t-thin\">\n <Button @click=\"addToQueue\" color=\"transp\" size=\"small\" class=\"w-100 justify-start\">\n Add to Queue\n </Button>\n <Button @click=\"copyLink\" color=\"transp\" size=\"small\" class=\"w-100 justify-start\">\n Copy Link\n </Button>\n <Button @click=\"addToPlaylist\" color=\"transp\" size=\"small\" class=\"w-100 justify-start\">\n Add to Playlist\n </Button>\n <template v-if=\"isOwner\">\n <hr class=\"mn-v-thin border-dark-transp-10\" />\n <Button @click=\"editAlbum\" color=\"transp\" size=\"small\" class=\"w-100 justify-start\">\n Edit Album\n </Button>\n <Button @click=\"deleteAlbum\" color=\"danger\" size=\"small\" class=\"w-100 justify-start\">\n Delete Album\n </Button>\n </template>\n </div>\n </template>\n </Dropdown>\n </div>\n\n <!-- Artists Cards -->\n <div class=\"artists-section mn-b-big\">\n <h3 class=\"t-medium mn-b-small\" v-if=\"album.artists && album.artists.length > 1\">Artists</h3>\n <div class=\"flex flex-col gap-small\">\n <div \n v-for=\"artist in album.artists\" \n :key=\"artist._id\"\n class=\"artist-card bg-light pd-medium radius-medium flex items-center gap-medium\"\n >\n <router-link \n :to=\"{ name: 'artist', params: { url: artist.url } }\"\n class=\"flex items-center gap-medium flex-1 hover-opacity\"\n >\n <div class=\"artist-avatar\">\n <Media \n v-if=\"artist.photoUrl\"\n :url=\"artist.photoUrl\"\n :alt=\"artist.name\"\n class=\"w-4r h-4r radius-full object-cover\"\n />\n <div v-else class=\"w-4r h-4r radius-full bg-primary flex-center \">\n {{ artist.name.charAt(0) }}\n </div>\n </div>\n <div>\n <div class=\"flex items-center gap-thin\">\n <span class=\"t-large \">{{ artist.name }}</span>\n <IconVerified v-if=\"artist.isVerified\" class=\"w-1r h-1r t-primary\" />\n </div>\n <span class=\"t-small t-transp\">Artist</span>\n </div>\n </router-link>\n <Button \n v-if=\"!isOwner\"\n @click=\"() => toggleFollowArtist(artist._id)\"\n :color=\"followedArtists.includes(artist._id) ? 'primary' : 'transp'\"\n size=\"small\"\n >\n {{ followedArtists.includes(artist._id) ? 'Following' : 'Follow' }}\n </Button>\n </div>\n </div>\n </div>\n\n <!-- Metadata Cards -->\n <div class=\"metadata-grid grid cols-2 gap-small mn-b-big\">\n <!-- Release Date -->\n <div class=\"metadata-card bg-light pd-medium radius-medium flex items-center gap-medium\">\n <IconCalendar class=\"i-regular t-primary\" />\n <div>\n <div class=\"t-small t-transp t-uppercase\">Released</div>\n <div class=\"t-medium \">{{ formatDate(album.releaseDate) }}</div>\n </div>\n </div>\n\n <!-- Total Duration -->\n <div class=\"metadata-card bg-light pd-medium radius-medium flex items-center gap-medium\">\n <IconClock class=\"i-regular t-primary\" />\n <div>\n <div class=\"t-small t-transp t-uppercase\">Duration</div>\n <div class=\"t-medium \">{{ totalDuration }}</div>\n </div>\n </div>\n\n <!-- Label -->\n <div v-if=\"album.label\" class=\"metadata-card bg-light pd-medium radius-medium flex items-center gap-medium\">\n <IconDisc class=\"i-regular t-primary\" />\n <div>\n <div class=\"t-small t-transp t-uppercase\">Label</div>\n <div class=\"t-medium \">{{ album.label }}</div>\n </div>\n </div>\n\n <!-- Visibility -->\n <div class=\"metadata-card bg-light pd-medium radius-medium flex items-center gap-medium\">\n <IconEye class=\"i-regular t-primary\" />\n <div>\n <div class=\"t-small t-transp t-uppercase\">Visibility</div>\n <div class=\"t-medium \">{{ album.isPublic ? 'Public' : 'Private' }}</div>\n </div>\n </div>\n </div>\n\n <!-- Genres & Tags -->\n <div v-if=\"(album.genres && album.genres.length) || (album.tags && album.tags.length)\" class=\"tags-section mn-b-medium\">\n <h3 class=\"t-medium mn-b-small\">Genres & Tags</h3>\n <div class=\"flex gap-thin flex-wrap\">\n <span \n v-for=\"genre in album.genres\" \n :key=\"genre\"\n class=\"tag bg-primary-transp-20 t-primary pd-thin-big radius-small t-small hover-bg-primary-transp-30 cursor-pointer\"\n >\n {{ genre }}\n </span>\n <span \n v-for=\"tag in album.tags\" \n :key=\"tag\"\n class=\"tag bg-light t-transp pd-thin-big radius-small t-small hover-bg-light cursor-pointer\"\n >\n #{{ tag }}\n </span>\n </div>\n </div>\n\n <!-- Description -->\n <div v-if=\"album.description\" class=\"description-section bg-light pd-medium radius-medium mn-b-medium\">\n <h3 class=\"t-medium mn-b-small\">About</h3>\n <p class=\"t-transp\">{{ album.description }}</p>\n </div>\n </div>\n </div>\n\n <!-- Album Tracks -->\n <section v-if=\"!isLoading && album && albumTracks.length\" class=\"tracks-section mn-t-big\">\n <h2 class=\"h2 mn-b-medium\">Tracklist</h2>\n <Feed\n :store=\"{\n read: () => Promise.resolve(albumTracks),\n state: { isLoading: false }\n }\"\n :external=\"true\"\n :items=\"albumTracks\"\n :states=\"{\n empty: {\n title: 'No tracks in album',\n description: 'This album appears to be empty',\n class: 'pd-medium t-center'\n }\n }\"\n >\n <template #default=\"{ items }\">\n <div class=\"bg-light radius-medium o-hidden\">\n <TrackListCard\n v-for=\"(track, index) in items\"\n :key=\"track._id\"\n :track=\"track\"\n :index=\"index + 1\"\n :showAlbum=\"false\"\n :showCover=\"false\"\n />\n </div>\n </template>\n </Feed>\n </section>\n\n <!-- More from Artists -->\n <section v-if=\"!isLoading && album && moreAlbums.length\" class=\"more-albums-section mn-t-big\">\n <div class=\"flex justify-between items-center mn-b-medium\">\n <h2 class=\"h2\">More Albums</h2>\n <router-link \n v-if=\"album.artists && album.artists[0]\"\n :to=\"{ name: 'artist', params: { url: album.artists[0].url } }\" \n class=\"t-primary hover-opacity\"\n >\n See all\n </router-link>\n </div>\n <div class=\"flex flex-nowrap gap-small o-x-scroll overscroll-behavior-x-contain scroll-behavior-smooth scroll-snap-type-x-mandatory scroll-hide\"\n >\n <li v-for=\"relatedAlbum in moreAlbums\" :key=\"album._id\" class=\"flex-none scroll-snap-align-start\">\n <AlbumCard :album=\"relatedAlbum\" class=\"w-min-15r transition-cubic-in-out\" />\n </li>\n </div>\n </section>\n </div>\n</template>\n\n<script setup>\nimport { ref, computed, onMounted, watch } from 'vue';\nimport { useRoute, useRouter } from 'vue-router';\nimport Button from '@martyrs/src/components/Button/Button.vue';\nimport Loader from '@martyrs/src/components/Loader/Loader.vue';\nimport Media from '@martyrs/src/components/Media/Media.vue';\nimport Dropdown from '@martyrs/src/components/Dropdown/Dropdown.vue';\nimport Feed from '@martyrs/src/components/Feed/Feed.vue';\n\n// Icons\nimport IconPlay from '@martyrs/src/modules/icons/navigation/IconPlay.vue';\nimport IconLike from '@martyrs/src/modules/icons/navigation/IconLike.vue';\nimport IconEllipsis from '@martyrs/src/modules/icons/navigation/IconEllipsis.vue';\nimport IconShuffle from '@martyrs/src/modules/icons/navigation/IconShuffle.vue';\nimport IconCalendar from '@martyrs/src/modules/icons/entities/IconCalendar.vue';\nimport IconClock from '@martyrs/src/modules/icons/entities/IconTime.vue';\nimport IconEye from '@martyrs/src/modules/icons/actions/IconShow.vue';\nimport IconDisc from '@martyrs/src/modules/icons/entities/IconMusic.vue';\nimport IconVerified from '@martyrs/src/modules/icons/navigation/IconCheckmark.vue';\n\n// Components\nimport TrackListCard from '../cards/TrackListCard.vue';\nimport AlbumCard from '../cards/AlbumCard.vue';\n\n// Store\nimport { state as albumsState, actions as albumsActions } from '../../store/albums.js';\nimport { actions as playerActions } from '../../store/player.js';\nimport { state as authState } from '@martyrs/src/modules/auth/views/store/auth.js';\n\nconst route = useRoute();\nconst router = useRouter();\n\n// Emits\nconst emits = defineEmits(['page-loading', 'page-loaded']);\n\n// State\nconst hasLoaded = ref(false);\nconst isFavorite = ref(false);\nconst showDropdown = ref(false);\nconst followedArtists = ref([]);\nconst moreAlbums = ref([]);\n\n// Clear state\nalbumsState.currentAlbum = null;\nalbumsState.currentAlbumTracks = [];\n\n// Computed\nconst album = computed(() => albumsState.currentAlbum);\nconst albumTracks = computed(() => albumsState.currentAlbumTracks || []);\n\nconst isOwner = computed(() => {\n return album.value?.owner?.target === authState.user?._id;\n});\n\nconst totalDuration = computed(() => {\n if (!albumTracks.value.length) return '0:00';\n const totalSeconds = albumTracks.value.reduce((sum, track) => sum + (track.duration || 0), 0);\n return formatDuration(totalSeconds);\n});\n\n// Format helpers\nconst formatDate = (dateString) => {\n if (!dateString) return 'Unknown';\n return new Date(dateString).toLocaleDateString('en-US', {\n year: 'numeric',\n month: 'long',\n day: 'numeric'\n });\n};\n\nconst formatDuration = (seconds) => {\n if (!seconds) return '0:00';\n const h = Math.floor(seconds / 3600);\n const m = Math.floor((seconds % 3600) / 60);\n const s = Math.floor(seconds % 60);\n \n if (h > 0) {\n return `${h}:${m.toString().padStart(2, '0')}:${s.toString().padStart(2, '0')}`;\n }\n return `${m}:${s.toString().padStart(2, '0')}`;\n};\n\nconst formatNumber = (num) => {\n if (!num) return '0';\n if (num >= 1000000) {\n return (num / 1000000).toFixed(1) + 'M';\n } else if (num >= 1000) {\n return (num / 1000).toFixed(1) + 'K';\n }\n return num.toString();\n};\n\n// Actions\nconst playAlbum = () => {\n if (albumTracks.value && albumTracks.value.length > 0) {\n playerActions.setQueue(albumTracks.value);\n }\n};\n\nconst shufflePlay = () => {\n if (albumTracks.value && albumTracks.value.length > 0) {\n const shuffled = [...albumTracks.value].sort(() => Math.random() - 0.5);\n playerActions.setQueue(shuffled);\n }\n};\n\nconst toggleFavorite = () => {\n isFavorite.value = !isFavorite.value;\n // TODO: Implement actual saving\n};\n\nconst toggleFollowArtist = (artistId) => {\n const index = followedArtists.value.indexOf(artistId);\n if (index > -1) {\n followedArtists.value.splice(index, 1);\n } else {\n followedArtists.value.push(artistId);\n }\n // TODO: Implement actual following\n};\n\nconst addToQueue = () => {\n if (albumTracks.value.length > 0) {\n albumTracks.value.forEach(track => {\n playerActions.addToQueue(track);\n });\n showDropdown.value = false;\n }\n};\n\nconst editAlbum = () => {\n router.push({ name: 'album-edit', params: { url: album.value.url } });\n};\n\nconst deleteAlbum = async () => {\n if (confirm('Are you sure you want to delete this album?')) {\n try {\n await albumsActions.deleteAlbum(album.value._id);\n router.push({ name: 'music-library' });\n } catch (error) {\n console.error('Failed to delete album:', error);\n }\n }\n};\n\nconst copyLink = () => {\n navigator.clipboard.writeText(window.location.href);\n showDropdown.value = false;\n};\n\n// Data fetching\nconst fetchAlbumData = async () => {\n try {\n await albumsActions.fetchAlbumByUrl(route.params.url);\n \n // Fetch more albums from the same artists\n if (album.value?.artists?.length) {\n const artistIds = album.value.artists.map(a => a._id);\n const albums = await albumsActions.fetchAlbums({\n artist: { $in: artistIds },\n status: 'published',\n isPublic: true,\n limit: 6\n });\n \n // Filter out current album\n moreAlbums.value = albums.filter(a => a._id !== album.value._id).slice(0, 5);\n }\n } catch (error) {\n console.error('Error fetching album data:', error);\n }\n};\n\n// Lifecycle\nonMounted(async () => {\n emits('page-loading');\n \n await fetchAlbumData();\n \n hasLoaded.value = true;\n emits('page-loaded');\n});\n</script>\n\n<style scoped>\n</style>"],"names":["albumsState","authState","playerActions","albumsActions"],"mappings":"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"}
@@ -24,9 +24,9 @@ const _hoisted_6 = { class: "w-15r h-15r radius-medium o-hidden mn-r-medium bs-b
24
24
  const _hoisted_7 = ["src"];
25
25
  const _hoisted_8 = {
26
26
  key: 1,
27
- class: "w-100 h-100 bg-white flex-center flex"
27
+ class: "w-100 h-100 bg-black flex-center flex"
28
28
  };
29
- const _hoisted_9 = { class: "h1" };
29
+ const _hoisted_9 = { class: "h1 t-white" };
30
30
  const _hoisted_10 = { class: "mobile:t-center" };
31
31
  const _hoisted_11 = { class: "flex-v-center flex-nowrap flex mn-b-small mobile:flex-center" };
32
32
  const _hoisted_12 = { class: "h1 mn-r-small" };
@@ -188,7 +188,7 @@ const _sfc_main = {
188
188
  style: vue.normalizeStyle(artist.value.coverUrl ? `background-image: url(${_ctx.FILE_SERVER_URL + artist.value.coverUrl}); background-size: cover; background-position: center;` : "")
189
189
  }, [
190
190
  vue.createElementVNode("div", {
191
- class: vue.normalizeClass(["pos-absolute pos-t-0 pos-l-0 w-100 h-100 bg-blur-small", artist.value.coverUrl ? "bg-black-transp-50" : "bg-black"])
191
+ class: vue.normalizeClass(["pos-absolute pos-t-0 pos-l-0 w-100 h-100 bg-blur-small", artist.value.coverUrl ? "bg-black-transp-50" : "bg-dark"])
192
192
  }, null, 2),
193
193
  isOwner.value ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_4, [
194
194
  vue.createVNode(Button.default, {
@@ -218,7 +218,7 @@ const _sfc_main = {
218
218
  vue.createElementVNode("div", _hoisted_6, [
219
219
  artist.value.photoUrl ? (vue.openBlock(), vue.createElementBlock("img", {
220
220
  key: 0,
221
- src: _ctx.FILE_SERVER_URL + artist.value.photoUrl,
221
+ src: _ctx.FILE_SERVER_URL + (artist.value.photoUrl || "/logo/logo-placeholder.jpg"),
222
222
  alt: "Artist photo",
223
223
  class: "w-100 h-100 object-fit-cover"
224
224
  }, null, 8, _hoisted_7)) : (vue.openBlock(), vue.createElementBlock("div", _hoisted_8, [
@@ -1 +1 @@
1
- {"version":3,"file":"Artist.vue.cjs","sources":["../../../../../../../src/modules/music/components/pages/Artist.vue"],"sourcesContent":["<template>\n <div class=\"w-100 pos-relative\">\n \n <!-- Artist not found -->\n <div v-if=\"hasLoaded && !artist\" class=\"flex-center flex-column flex pd-extra\">\n <h2 class=\"h2 mn-b-medium\">Artist Not Found</h2>\n <p class=\"p-medium t-transp mn-b-medium\">The artist you are looking for doesn't exist or may have been removed.</p>\n <Button\n :submit=\"() => router.push({ name: 'music-home' })\"\n class=\"bg-main \"\n :showSucces=\"false\"\n :showLoader=\"false\"\n >\n Back to Music\n </Button>\n </div>\n \n <!-- Artist content -->\n <div v-if=\"artist\">\n <!-- Cover image -->\n <div \n class=\"w-100 t-white h-50vh pos-relative\"\n :style=\"artist.coverUrl ? `background-image: url(${FILE_SERVER_URL + artist.coverUrl}); background-size: cover; background-position: center;` : ''\"\n >\n <div class=\"pos-absolute pos-t-0 pos-l-0 w-100 h-100 bg-blur-small\" :class=\"artist.coverUrl ? 'bg-black-transp-50' : 'bg-black'\"></div>\n \n <!-- Artist actions for edit/manage -->\n <div v-if=\"isOwner\" class=\"pos-absolute pos-t-medium pos-r-medium z-index-1\">\n <Button\n @click=\"router.push({ name: 'artist-edit', params: { url: artist.url } })\"\n class=\"bg-main mn-r-small\"\n :showSucces=\"false\"\n :showLoader=\"false\"\n >\n Edit Artist\n </Button>\n \n <Button\n @click=\"router.push({ name: 'artist-manage-content', params: { artistId: artist._id } })\"\n class=\"bg-white t-black\"\n :showSucces=\"false\"\n :showLoader=\"false\"\n >\n Manage Content\n </Button>\n </div>\n \n <!-- Artist profile info -->\n <div class=\"flex-v-center t-white pos-absolute pos-b-0 pos-l-0 w-100 pd-medium z-index-1 flex mobile:flex-column mobile:flex-h-center\">\n <div class=\"w-15r h-15r radius-medium o-hidden mn-r-medium bs-black mobile:mn-r-0 mobile:mn-b-medium\">\n <img\n v-if=\"artist.photoUrl\"\n :src=\"FILE_SERVER_URL + artist.photoUrl\"\n alt=\"Artist photo\"\n class=\"w-100 h-100 object-fit-cover\"\n />\n <div v-else class=\"w-100 h-100 bg-white flex-center flex\">\n <span class=\"h1\">{{ artist?.name?.[0] || 'A' }}</span>\n </div>\n </div>\n \n <div class=\" mobile:t-center\">\n <div class=\"flex-v-center flex-nowrap flex mn-b-small mobile:flex-center\">\n <h1 class=\"h1 mn-r-small\">{{ artist.name }}</h1>\n <span v-if=\"artist.isVerified\" class=\"bg-main-nano pd-micro radius-small\">\n ✓ Verified\n </span>\n </div>\n \n <p v-if=\"artist.location\" class=\"p-medium mn-b-small\">{{ artist.location }}</p>\n \n <!-- Social media links -->\n <div class=\"flex flex-nowrap gap-small mobile:flex-center\">\n <a \n v-if=\"artist.socials.telegram\" \n :href=\"`https://t.me/${artist.socials.telegram}`\" \n target=\"_blank\"\n class=\"bg-white t-black flex-center flex w-2r h-2r radius-extra\"\n >\n <span>T</span>\n </a>\n \n <a \n v-if=\"artist.socials.twitter\" \n :href=\"`https://twitter.com/${artist.socials.twitter}`\" \n target=\"_blank\"\n class=\"bg-white t-black flex-center flex w-2r h-2r radius-extra\"\n >\n <span>𝕏</span>\n </a>\n \n <a \n v-if=\"artist.socials.instagram\" \n :href=\"`https://instagram.com/${artist.socials.instagram}`\" \n target=\"_blank\"\n class=\"bg-white t-black flex-center flex w-2r h-2r radius-extra\"\n >\n <span>I</span>\n </a>\n \n <a \n v-if=\"artist.socials.facebook\" \n :href=\"`https://facebook.com/${artist.socials.facebook}`\" \n target=\"_blank\"\n class=\"bg-white t-black flex-center flex w-2r h-2r radius-extra\"\n >\n <span>F</span>\n </a>\n \n <a \n v-if=\"artist.website\" \n :href=\"artist.website\" \n target=\"_blank\"\n class=\"bg-white t-black flex-center flex w-2r h-2r radius-extra\"\n >\n <span>W</span>\n </a>\n </div>\n </div>\n </div>\n </div>\n \n <!-- Main content -->\n <div class=\"pd-medium\">\n <div class=\"cols-2-1_2 gap-medium mobile:cols-1\">\n <!-- Left column - Bio and details -->\n <div>\n <div class=\"bg-light pd-medium radius-medium mn-b-medium\">\n <h2 class=\"h3 mn-b-small\">Biography</h2>\n <p v-if=\"artist.bio\" class=\"p-medium\">{{ artist.bio }}</p>\n <p v-else class=\"p-medium t-transp\">No biography available for this artist.</p>\n </div>\n \n <!-- Genres -->\n <div v-if=\"genres.length > 0\" class=\"bg-light pd-medium radius-medium\">\n <h2 class=\"h3 mn-b-small\">Genres</h2>\n <div class=\"flex flex-wrap gap-small\">\n <span \n v-for=\"genre in genres\" \n :key=\"genre._id\"\n class=\"bg-white pd-thin radius-medium\"\n >\n {{ genre.name }}\n </span>\n </div>\n </div>\n </div>\n \n <!-- Right column - Discography -->\n <div>\n <!-- Albums section -->\n <div v-if=\"discography.albums.length > 0\" class=\"bg-light pd-medium radius-medium mn-b-medium\">\n <h2 class=\"h3 mn-b-medium\">Albums</h2>\n \n <div class=\"cols-2 gap-small mobile:cols-1\">\n <div\n v-for=\"album in discography.albums\"\n :key=\"album._id\"\n class=\"bg-white pd-small radius-medium flex-v-center flex cursor-pointer hover-bg-white\"\n @click=\"router.push({ name: 'album', params: { url: album.url } })\"\n >\n <div class=\"w-3r h-3r radius-small o-hidden mn-r-small\">\n <img\n v-if=\"album.coverUrl\"\n :src=\"FILE_SERVER_URL + album.coverUrl\"\n alt=\"Album cover\"\n class=\"w-100 h-100 object-fit-cover\"\n />\n <div v-else class=\"w-100 h-100 bg-light flex-center flex\">\n <span>A</span>\n </div>\n </div>\n \n <div class=\"w-100 o-hidden\">\n <p class=\"p-medium t-truncate\">{{ album.title }}</p>\n <p class=\"p-small t-transp\">{{ formatDate(album.releaseDate) }}</p>\n </div>\n </div>\n </div>\n \n <Button\n v-if=\"discography.albums.length > 4\"\n @click=\"router.push({ name: 'artist-albums', params: { artistId: artist._id } })\"\n class=\"mn-t-small w-100 bg-white t-black\"\n :showSucces=\"false\"\n :showLoader=\"false\"\n >\n View All Albums\n </Button>\n </div>\n \n <!-- Singles section -->\n <div v-if=\"discography.singles.length > 0\" class=\"bg-light pd-medium radius-medium\">\n <h2 class=\"h3 mn-b-medium\">Singles & EPs</h2>\n \n <div class=\"cols-1 gap-small\">\n <div\n v-for=\"single in discography.singles\"\n :key=\"single._id\"\n class=\"bg-white pd-small radius-medium flex-v-center flex cursor-pointer hover-bg-white\"\n @click=\"router.push({ name: 'track', params: { url: single.url } })\"\n >\n <div class=\"w-3r h-3r radius-small o-hidden mn-r-small\">\n <img\n v-if=\"single.coverUrl\"\n :src=\"FILE_SERVER_URL + single.coverUrl\"\n alt=\"Single cover\"\n class=\"w-100 h-100 object-fit-cover\"\n />\n <div v-else class=\"w-100 h-100 bg-light flex-center flex\">\n <span>S</span>\n </div>\n </div>\n \n <div class=\"w-100 o-hidden\">\n <p class=\"p-medium t-truncate\">{{ single.title }}</p>\n <p class=\"p-small t-transp\">{{ formatDate(single.releaseDate) }}</p>\n </div>\n </div>\n </div>\n \n <Button\n v-if=\"discography.singles.length > 5\"\n @click=\"router.push({ name: 'artist-singles', params: { artistId: artist._id } })\"\n class=\"mn-t-small w-100 bg-white t-black\"\n :showSucces=\"false\"\n :showLoader=\"false\"\n >\n View All Singles & EPs\n </Button>\n </div>\n \n <!-- Popular Tracks Section -->\n <div v-if=\"artistTracks.length > 0\" class=\"bg-light pd-medium radius-medium mn-b-medium\">\n <h2 class=\"h3 mn-b-medium\">Popular Tracks</h2>\n \n <div class=\"bg-white radius-medium o-hidden\">\n <TrackListCard\n v-for=\"(track, index) in artistTracks\"\n :key=\"track._id\"\n :track=\"track\"\n :index=\"index\"\n :showAlbum=\"true\"\n :showCover=\"true\"\n />\n </div>\n </div>\n \n <!-- No discography yet -->\n <div \n v-if=\"discography.albums.length === 0 && discography.singles.length === 0\" \n class=\"bg-light pd-medium radius-medium t-center\"\n >\n <p class=\"p-medium mn-b-small\">No releases yet</p>\n <p class=\"p-small t-transp\">This artist hasn't released any albums or singles yet.</p>\n \n <Button\n v-if=\"isOwner\"\n @click=\"router.push({ name: 'release-create', query: { artistId: artist._id } })\"\n class=\"mn-t-medium bg-main \"\n :showSucces=\"false\"\n :showLoader=\"false\"\n >\n Add Release\n </Button>\n </div>\n </div>\n </div>\n \n <!-- Related Artists -->\n <div v-if=\"relatedArtists.length > 0\" class=\"mn-t-medium\">\n <h2 class=\"h3 mn-b-medium\">Fans Also Like</h2>\n \n <div class=\"cols-5 gap-medium mobile:cols-2\">\n <div\n v-for=\"relatedArtist in relatedArtists\"\n :key=\"relatedArtist._id\"\n class=\"t-center cursor-pointer\"\n @click=\"router.push({ name: 'artist', params: { url: relatedArtist.url } })\"\n >\n <div class=\"w-100 aspect-1x1 radius-medium o-hidden mn-b-small\">\n <img\n v-if=\"relatedArtist.photoUrl\"\n :src=\"FILE_SERVER_URL + relatedArtist.photoUrl\"\n alt=\"Artist photo\"\n class=\"w-100 h-100 object-fit-cover\"\n />\n <div v-else class=\"w-100 h-100 bg-light flex-center flex\">\n <span>{{ relatedArtist?.name?.[0] || 'A' }}</span>\n </div>\n </div>\n \n <p class=\"p-medium t-truncate\">{{ relatedArtist.name }}</p>\n </div>\n </div>\n </div>\n </div>\n </div>\n </div>\n</template>\n\n<script setup>\nimport { ref, computed, onMounted } from 'vue';\nimport { useRouter, useRoute } from 'vue-router';\n\n// Import Martyrs components\nimport Button from '@martyrs/src/components/Button/Button.vue';\nimport Loader from '@martyrs/src/components/Loader/Loader.vue';\nimport TrackListCard from '../cards/TrackListCard.vue';\n\n// Import store\nimport * as artistsStore from '../../store/artists';\n// import * as genreStore from '../../store/genres'; // Assuming you have a genre store\nimport * as auth from '@martyrs/src/modules/auth/views/store/auth.js';\nimport * as globals from '@martyrs/src/modules/globals/views/store/globals.js';\n\n// Import mixins\nimport { useGlobalMixins } from '@martyrs/src/modules/globals/views/mixins/mixins.js';\nconst { formatDate } = useGlobalMixins();\n\n// Router and route\nconst router = useRouter();\nconst route = useRoute();\n\n// Emits\nconst emits = defineEmits(['page-loading', 'page-loaded']);\n\n// State\nconst genres = ref([]);\nconst hasLoaded = ref(false);\n\n// Computed\nconst artist = computed(() => {\n return artistsStore.state.currentArtist;\n});\n\nconst discography = computed(() => {\n return artistsStore.state.discography;\n});\n\nconst relatedArtists = computed(() => {\n return artistsStore.state.relatedArtists;\n});\n\nconst artistTracks = computed(() => {\n return artistsStore.state.discography.tracks || [];\n});\n\nconst isOwner = computed(() => {\n if (!artist.value || !auth.state.user._id) return false;\n \n // Check if current user is the creator of the artist\n return artist.value.creator?.target?._id === auth.state.user._id;\n});\n\n// Clear current artist state\nartistsStore.state.currentArtist = null;\n// Clear discography state\nartistsStore.state.discography = {\n albums: [],\n singles: [],\n tracks: []\n};\nartistsStore.state.relatedArtists = [];\n\n// Methods\nconst fetchArtist = async () => {\n try {\n // Get URL from route params\n const url = route.params.url;\n if (!url) {\n throw new Error('Artist URL is required');\n }\n \n await artistsStore.actions.fetchArtistByUrl(url);\n } catch (error) {\n console.error('Error fetching artist:', error);\n globals.actions.setError({\n message: 'Failed to load artist'\n });\n }\n};\n\n// Lifecycle hooks\nonMounted(async () => {\n emits('page-loading');\n \n await fetchArtist();\n \n hasLoaded.value = true;\n emits('page-loaded');\n});\n</script>"],"names":["useGlobalMixins","useRouter","useRoute","ref","computed","artistsStore.state","auth.state","artistsStore.actions","globals.actions","onMounted"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8TA,UAAM,EAAE,WAAU,IAAKA,uBAAe;AAGtC,UAAM,SAASC,UAAAA,UAAS;AACxB,UAAM,QAAQC,UAAAA,SAAQ;AAGtB,UAAM,QAAQ;AAGd,UAAM,SAASC,IAAAA,IAAI,EAAE;AACrB,UAAM,YAAYA,IAAAA,IAAI,KAAK;AAG3B,UAAM,SAASC,IAAAA,SAAS,MAAM;AAC5B,aAAOC,QAAAA,MAAmB;AAAA,IAC5B,CAAC;AAED,UAAM,cAAcD,IAAAA,SAAS,MAAM;AACjC,aAAOC,QAAAA,MAAmB;AAAA,IAC5B,CAAC;AAED,UAAM,iBAAiBD,IAAAA,SAAS,MAAM;AACpC,aAAOC,QAAAA,MAAmB;AAAA,IAC5B,CAAC;AAED,UAAM,eAAeD,IAAAA,SAAS,MAAM;AAClC,aAAOC,QAAAA,MAAmB,YAAY,UAAU,CAAA;AAAA,IAClD,CAAC;AAED,UAAM,UAAUD,IAAAA,SAAS,MAAM;AAC7B,UAAI,CAAC,OAAO,SAAS,CAACE,KAAAA,MAAW,KAAK,IAAK,QAAO;AAGlD,aAAO,OAAO,MAAM,SAAS,QAAQ,QAAQA,KAAAA,MAAW,KAAK;AAAA,IAC/D,CAAC;AAGDD,YAAAA,MAAmB,gBAAgB;AAEnCA,YAAAA,MAAmB,cAAc;AAAA,MAC/B,QAAQ,CAAA;AAAA,MACR,SAAS,CAAA;AAAA,MACT,QAAQ,CAAA;AAAA,IACV;AACAA,YAAAA,MAAmB,iBAAiB,CAAA;AAGpC,UAAM,cAAc,YAAY;AAC9B,UAAI;AAEF,cAAM,MAAM,MAAM,OAAO;AACzB,YAAI,CAAC,KAAK;AACR,gBAAM,IAAI,MAAM,wBAAwB;AAAA,QAC1C;AAEA,cAAME,QAAAA,QAAqB,iBAAiB,GAAG;AAAA,MACjD,SAAS,OAAO;AACd,gBAAQ,MAAM,0BAA0B,KAAK;AAC7CC,gBAAAA,QAAgB,SAAS;AAAA,UACvB,SAAS;AAAA,QACf,CAAK;AAAA,MACH;AAAA,IACF;AAGAC,QAAAA,UAAU,YAAY;AACpB,YAAM,cAAc;AAEpB,YAAM,YAAW;AAEjB,gBAAU,QAAQ;AAClB,YAAM,aAAa;AAAA,IACrB,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"Artist.vue.cjs","sources":["../../../../../../../src/modules/music/components/pages/Artist.vue"],"sourcesContent":["<template>\n <div class=\"w-100 pos-relative\">\n \n <!-- Artist not found -->\n <div v-if=\"hasLoaded && !artist\" class=\"flex-center flex-column flex pd-extra\">\n <h2 class=\"h2 mn-b-medium\">Artist Not Found</h2>\n <p class=\"p-medium t-transp mn-b-medium\">The artist you are looking for doesn't exist or may have been removed.</p>\n <Button\n :submit=\"() => router.push({ name: 'music-home' })\"\n class=\"bg-main \"\n :showSucces=\"false\"\n :showLoader=\"false\"\n >\n Back to Music\n </Button>\n </div>\n \n <!-- Artist content -->\n <div v-if=\"artist\">\n <!-- Cover image -->\n <div \n class=\"w-100 t-white h-50vh pos-relative\"\n :style=\"artist.coverUrl ? `background-image: url(${FILE_SERVER_URL + artist.coverUrl}); background-size: cover; background-position: center;` : ''\"\n >\n <div class=\"pos-absolute pos-t-0 pos-l-0 w-100 h-100 bg-blur-small\" :class=\"artist.coverUrl ? 'bg-black-transp-50' : 'bg-dark'\"></div>\n \n <!-- Artist actions for edit/manage -->\n <div v-if=\"isOwner\" class=\"pos-absolute pos-t-medium pos-r-medium z-index-1\">\n <Button\n @click=\"router.push({ name: 'artist-edit', params: { url: artist.url } })\"\n class=\"bg-main mn-r-small\"\n :showSucces=\"false\"\n :showLoader=\"false\"\n >\n Edit Artist\n </Button>\n \n <Button\n @click=\"router.push({ name: 'artist-manage-content', params: { artistId: artist._id } })\"\n class=\"bg-white t-black\"\n :showSucces=\"false\"\n :showLoader=\"false\"\n >\n Manage Content\n </Button>\n </div>\n \n <!-- Artist profile info -->\n <div class=\"flex-v-center t-white pos-absolute pos-b-0 pos-l-0 w-100 pd-medium z-index-1 flex mobile:flex-column mobile:flex-h-center\">\n <div class=\"w-15r h-15r radius-medium o-hidden mn-r-medium bs-black mobile:mn-r-0 mobile:mn-b-medium\">\n <img\n v-if=\"artist.photoUrl\"\n :src=\"FILE_SERVER_URL + (artist.photoUrl || '/logo/logo-placeholder.jpg')\"\n alt=\"Artist photo\"\n class=\"w-100 h-100 object-fit-cover\"\n />\n <div v-else class=\"w-100 h-100 bg-black flex-center flex\">\n <span class=\"h1 t-white\">{{ artist?.name?.[0] || 'A' }}</span>\n </div>\n </div>\n \n <div class=\" mobile:t-center\">\n <div class=\"flex-v-center flex-nowrap flex mn-b-small mobile:flex-center\">\n <h1 class=\"h1 mn-r-small\">{{ artist.name }}</h1>\n <span v-if=\"artist.isVerified\" class=\"bg-main-nano pd-micro radius-small\">\n ✓ Verified\n </span>\n </div>\n \n <p v-if=\"artist.location\" class=\"p-medium mn-b-small\">{{ artist.location }}</p>\n \n <!-- Social media links -->\n <div class=\"flex flex-nowrap gap-small mobile:flex-center\">\n <a \n v-if=\"artist.socials.telegram\" \n :href=\"`https://t.me/${artist.socials.telegram}`\" \n target=\"_blank\"\n class=\"bg-white t-black flex-center flex w-2r h-2r radius-extra\"\n >\n <span>T</span>\n </a>\n \n <a \n v-if=\"artist.socials.twitter\" \n :href=\"`https://twitter.com/${artist.socials.twitter}`\" \n target=\"_blank\"\n class=\"bg-white t-black flex-center flex w-2r h-2r radius-extra\"\n >\n <span>𝕏</span>\n </a>\n \n <a \n v-if=\"artist.socials.instagram\" \n :href=\"`https://instagram.com/${artist.socials.instagram}`\" \n target=\"_blank\"\n class=\"bg-white t-black flex-center flex w-2r h-2r radius-extra\"\n >\n <span>I</span>\n </a>\n \n <a \n v-if=\"artist.socials.facebook\" \n :href=\"`https://facebook.com/${artist.socials.facebook}`\" \n target=\"_blank\"\n class=\"bg-white t-black flex-center flex w-2r h-2r radius-extra\"\n >\n <span>F</span>\n </a>\n \n <a \n v-if=\"artist.website\" \n :href=\"artist.website\" \n target=\"_blank\"\n class=\"bg-white t-black flex-center flex w-2r h-2r radius-extra\"\n >\n <span>W</span>\n </a>\n </div>\n </div>\n </div>\n </div>\n \n <!-- Main content -->\n <div class=\"pd-medium\">\n <div class=\"cols-2-1_2 gap-medium mobile:cols-1\">\n <!-- Left column - Bio and details -->\n <div>\n <div class=\"bg-light pd-medium radius-medium mn-b-medium\">\n <h2 class=\"h3 mn-b-small\">Biography</h2>\n <p v-if=\"artist.bio\" class=\"p-medium\">{{ artist.bio }}</p>\n <p v-else class=\"p-medium t-transp\">No biography available for this artist.</p>\n </div>\n \n <!-- Genres -->\n <div v-if=\"genres.length > 0\" class=\"bg-light pd-medium radius-medium\">\n <h2 class=\"h3 mn-b-small\">Genres</h2>\n <div class=\"flex flex-wrap gap-small\">\n <span \n v-for=\"genre in genres\" \n :key=\"genre._id\"\n class=\"bg-white pd-thin radius-medium\"\n >\n {{ genre.name }}\n </span>\n </div>\n </div>\n </div>\n \n <!-- Right column - Discography -->\n <div>\n <!-- Albums section -->\n <div v-if=\"discography.albums.length > 0\" class=\"bg-light pd-medium radius-medium mn-b-medium\">\n <h2 class=\"h3 mn-b-medium\">Albums</h2>\n \n <div class=\"cols-2 gap-small mobile:cols-1\">\n <div\n v-for=\"album in discography.albums\"\n :key=\"album._id\"\n class=\"bg-white pd-small radius-medium flex-v-center flex cursor-pointer hover-bg-white\"\n @click=\"router.push({ name: 'album', params: { url: album.url } })\"\n >\n <div class=\"w-3r h-3r radius-small o-hidden mn-r-small\">\n <img\n v-if=\"album.coverUrl\"\n :src=\"FILE_SERVER_URL + album.coverUrl\"\n alt=\"Album cover\"\n class=\"w-100 h-100 object-fit-cover\"\n />\n <div v-else class=\"w-100 h-100 bg-light flex-center flex\">\n <span>A</span>\n </div>\n </div>\n \n <div class=\"w-100 o-hidden\">\n <p class=\"p-medium t-truncate\">{{ album.title }}</p>\n <p class=\"p-small t-transp\">{{ formatDate(album.releaseDate) }}</p>\n </div>\n </div>\n </div>\n \n <Button\n v-if=\"discography.albums.length > 4\"\n @click=\"router.push({ name: 'artist-albums', params: { artistId: artist._id } })\"\n class=\"mn-t-small w-100 bg-white t-black\"\n :showSucces=\"false\"\n :showLoader=\"false\"\n >\n View All Albums\n </Button>\n </div>\n \n <!-- Singles section -->\n <div v-if=\"discography.singles.length > 0\" class=\"bg-light pd-medium radius-medium\">\n <h2 class=\"h3 mn-b-medium\">Singles & EPs</h2>\n \n <div class=\"cols-1 gap-small\">\n <div\n v-for=\"single in discography.singles\"\n :key=\"single._id\"\n class=\"bg-white pd-small radius-medium flex-v-center flex cursor-pointer hover-bg-white\"\n @click=\"router.push({ name: 'track', params: { url: single.url } })\"\n >\n <div class=\"w-3r h-3r radius-small o-hidden mn-r-small\">\n <img\n v-if=\"single.coverUrl\"\n :src=\"FILE_SERVER_URL + single.coverUrl\"\n alt=\"Single cover\"\n class=\"w-100 h-100 object-fit-cover\"\n />\n <div v-else class=\"w-100 h-100 bg-light flex-center flex\">\n <span>S</span>\n </div>\n </div>\n \n <div class=\"w-100 o-hidden\">\n <p class=\"p-medium t-truncate\">{{ single.title }}</p>\n <p class=\"p-small t-transp\">{{ formatDate(single.releaseDate) }}</p>\n </div>\n </div>\n </div>\n \n <Button\n v-if=\"discography.singles.length > 5\"\n @click=\"router.push({ name: 'artist-singles', params: { artistId: artist._id } })\"\n class=\"mn-t-small w-100 bg-white t-black\"\n :showSucces=\"false\"\n :showLoader=\"false\"\n >\n View All Singles & EPs\n </Button>\n </div>\n \n <!-- Popular Tracks Section -->\n <div v-if=\"artistTracks.length > 0\" class=\"bg-light pd-medium radius-medium mn-b-medium\">\n <h2 class=\"h3 mn-b-medium\">Popular Tracks</h2>\n \n <div class=\"bg-white radius-medium o-hidden\">\n <TrackListCard\n v-for=\"(track, index) in artistTracks\"\n :key=\"track._id\"\n :track=\"track\"\n :index=\"index\"\n :showAlbum=\"true\"\n :showCover=\"true\"\n />\n </div>\n </div>\n \n <!-- No discography yet -->\n <div \n v-if=\"discography.albums.length === 0 && discography.singles.length === 0\" \n class=\"bg-light pd-medium radius-medium t-center\"\n >\n <p class=\"p-medium mn-b-small\">No releases yet</p>\n <p class=\"p-small t-transp\">This artist hasn't released any albums or singles yet.</p>\n \n <Button\n v-if=\"isOwner\"\n @click=\"router.push({ name: 'release-create', query: { artistId: artist._id } })\"\n class=\"mn-t-medium bg-main \"\n :showSucces=\"false\"\n :showLoader=\"false\"\n >\n Add Release\n </Button>\n </div>\n </div>\n </div>\n \n <!-- Related Artists -->\n <div v-if=\"relatedArtists.length > 0\" class=\"mn-t-medium\">\n <h2 class=\"h3 mn-b-medium\">Fans Also Like</h2>\n \n <div class=\"cols-5 gap-medium mobile:cols-2\">\n <div\n v-for=\"relatedArtist in relatedArtists\"\n :key=\"relatedArtist._id\"\n class=\"t-center cursor-pointer\"\n @click=\"router.push({ name: 'artist', params: { url: relatedArtist.url } })\"\n >\n <div class=\"w-100 aspect-1x1 radius-medium o-hidden mn-b-small\">\n <img\n v-if=\"relatedArtist.photoUrl\"\n :src=\"FILE_SERVER_URL + relatedArtist.photoUrl\"\n alt=\"Artist photo\"\n class=\"w-100 h-100 object-fit-cover\"\n />\n <div v-else class=\"w-100 h-100 bg-light flex-center flex\">\n <span>{{ relatedArtist?.name?.[0] || 'A' }}</span>\n </div>\n </div>\n \n <p class=\"p-medium t-truncate\">{{ relatedArtist.name }}</p>\n </div>\n </div>\n </div>\n </div>\n </div>\n </div>\n</template>\n\n<script setup>\nimport { ref, computed, onMounted } from 'vue';\nimport { useRouter, useRoute } from 'vue-router';\n\n// Import Martyrs components\nimport Button from '@martyrs/src/components/Button/Button.vue';\nimport Loader from '@martyrs/src/components/Loader/Loader.vue';\nimport TrackListCard from '../cards/TrackListCard.vue';\n\n// Import store\nimport * as artistsStore from '../../store/artists';\n// import * as genreStore from '../../store/genres'; // Assuming you have a genre store\nimport * as auth from '@martyrs/src/modules/auth/views/store/auth.js';\nimport * as globals from '@martyrs/src/modules/globals/views/store/globals.js';\n\n// Import mixins\nimport { useGlobalMixins } from '@martyrs/src/modules/globals/views/mixins/mixins.js';\nconst { formatDate } = useGlobalMixins();\n\n// Router and route\nconst router = useRouter();\nconst route = useRoute();\n\n// Emits\nconst emits = defineEmits(['page-loading', 'page-loaded']);\n\n// State\nconst genres = ref([]);\nconst hasLoaded = ref(false);\n\n// Computed\nconst artist = computed(() => {\n return artistsStore.state.currentArtist;\n});\n\nconst discography = computed(() => {\n return artistsStore.state.discography;\n});\n\nconst relatedArtists = computed(() => {\n return artistsStore.state.relatedArtists;\n});\n\nconst artistTracks = computed(() => {\n return artistsStore.state.discography.tracks || [];\n});\n\nconst isOwner = computed(() => {\n if (!artist.value || !auth.state.user._id) return false;\n \n // Check if current user is the creator of the artist\n return artist.value.creator?.target?._id === auth.state.user._id;\n});\n\n// Clear current artist state\nartistsStore.state.currentArtist = null;\n// Clear discography state\nartistsStore.state.discography = {\n albums: [],\n singles: [],\n tracks: []\n};\nartistsStore.state.relatedArtists = [];\n\n// Methods\nconst fetchArtist = async () => {\n try {\n // Get URL from route params\n const url = route.params.url;\n if (!url) {\n throw new Error('Artist URL is required');\n }\n \n await artistsStore.actions.fetchArtistByUrl(url);\n } catch (error) {\n console.error('Error fetching artist:', error);\n globals.actions.setError({\n message: 'Failed to load artist'\n });\n }\n};\n\n// Lifecycle hooks\nonMounted(async () => {\n emits('page-loading');\n \n await fetchArtist();\n \n hasLoaded.value = true;\n emits('page-loaded');\n});\n</script>"],"names":["useGlobalMixins","useRouter","useRoute","ref","computed","artistsStore.state","auth.state","artistsStore.actions","globals.actions","onMounted"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8TA,UAAM,EAAE,WAAU,IAAKA,uBAAe;AAGtC,UAAM,SAASC,UAAAA,UAAS;AACxB,UAAM,QAAQC,UAAAA,SAAQ;AAGtB,UAAM,QAAQ;AAGd,UAAM,SAASC,IAAAA,IAAI,EAAE;AACrB,UAAM,YAAYA,IAAAA,IAAI,KAAK;AAG3B,UAAM,SAASC,IAAAA,SAAS,MAAM;AAC5B,aAAOC,QAAAA,MAAmB;AAAA,IAC5B,CAAC;AAED,UAAM,cAAcD,IAAAA,SAAS,MAAM;AACjC,aAAOC,QAAAA,MAAmB;AAAA,IAC5B,CAAC;AAED,UAAM,iBAAiBD,IAAAA,SAAS,MAAM;AACpC,aAAOC,QAAAA,MAAmB;AAAA,IAC5B,CAAC;AAED,UAAM,eAAeD,IAAAA,SAAS,MAAM;AAClC,aAAOC,QAAAA,MAAmB,YAAY,UAAU,CAAA;AAAA,IAClD,CAAC;AAED,UAAM,UAAUD,IAAAA,SAAS,MAAM;AAC7B,UAAI,CAAC,OAAO,SAAS,CAACE,KAAAA,MAAW,KAAK,IAAK,QAAO;AAGlD,aAAO,OAAO,MAAM,SAAS,QAAQ,QAAQA,KAAAA,MAAW,KAAK;AAAA,IAC/D,CAAC;AAGDD,YAAAA,MAAmB,gBAAgB;AAEnCA,YAAAA,MAAmB,cAAc;AAAA,MAC/B,QAAQ,CAAA;AAAA,MACR,SAAS,CAAA;AAAA,MACT,QAAQ,CAAA;AAAA,IACV;AACAA,YAAAA,MAAmB,iBAAiB,CAAA;AAGpC,UAAM,cAAc,YAAY;AAC9B,UAAI;AAEF,cAAM,MAAM,MAAM,OAAO;AACzB,YAAI,CAAC,KAAK;AACR,gBAAM,IAAI,MAAM,wBAAwB;AAAA,QAC1C;AAEA,cAAME,QAAAA,QAAqB,iBAAiB,GAAG;AAAA,MACjD,SAAS,OAAO;AACd,gBAAQ,MAAM,0BAA0B,KAAK;AAC7CC,gBAAAA,QAAgB,SAAS;AAAA,UACvB,SAAS;AAAA,QACf,CAAK;AAAA,MACH;AAAA,IACF;AAGAC,QAAAA,UAAU,YAAY;AACpB,YAAM,cAAc;AAEpB,YAAM,YAAW;AAEjB,gBAAU,QAAQ;AAClB,YAAM,aAAa;AAAA,IACrB,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}