Showing avatars on actor tags instead of sections. Slightly sized down scene page actor tiles, shrinking font size on long names.

This commit is contained in:
2026-07-03 17:59:44 +02:00
parent 4636a213b3
commit 930cc52373
2 changed files with 116 additions and 44 deletions

View File

@@ -74,7 +74,10 @@
</div>
<span class="label">
<span class="name ellipsis">{{ actor.name }}</span>
<span
class="name ellipsis"
:style="{ 'font-size': `${Math.max(0.9 + Math.min((17 - actor.name.length), 0) * 0.06, 0.65)}rem` }"
>{{ actor.name }}</span>
<img
v-if="actor.entity"
@@ -155,6 +158,7 @@ const favorited = ref(props.actor.stashes.some((actorStash) => actorStash.id ===
}
.label {
height: 1.75rem;
display: flex;
justify-content: space-between;
align-items: center;
@@ -168,7 +172,7 @@ const favorited = ref(props.actor.stashes.some((actorStash) => actorStash.id ===
}
.name {
padding: .35rem .25rem .35rem .5rem;
padding: 0 .25rem 0 .5rem;
}
.favicon {

View File

@@ -140,30 +140,41 @@
</li>
</ul>
<div class="tags">
<div
v-for="actorTags in tags"
:key="`tags-${actorTags.actor?.slug || 'scene'}`"
class="tags-section"
<ul class="tags nolist">
<li
v-for="tag in tags"
:key="`tag-${tag.id}`"
class="tag"
:class="{ 'has-actors': tag.actors.length > 0 }"
>
<ul class="nolist">
<li
v-if="actorTags.actor"
class="tags-actor"
>{{ actorTags.actor.name }}:</li>
<Link
:href="`/tag/${tag.slug}`"
class="tag-name nolink"
>{{ tag.name }}</Link>
<li
v-for="tag in actorTags.tags"
:key="`tag-${tag.id}`"
<span
v-for="tagActor in tag.actors"
:key="`tagactor-${tagActor.id}`"
v-tooltip="{
content: `Performed by ${tagActor.name}`,
triggers: ['hover', 'click'],
}"
class="tag-frame"
>
<img
v-if="tagActor.avatar"
class="tag-avatar"
:src="getPath(tagActor.avatar, 'thumbnail')"
>
<Link
:href="`/tag/${tag.slug}`"
class="tag nolink"
>{{ tag.name }}</Link>
</li>
</ul>
</div>
</div>
<Icon
v-else
icon="star-full"
class="tag-star"
/>
</span>
</li>
</ul>
<div
v-if="scene.movies.length > 0 || scene.series.length > 0"
@@ -424,6 +435,7 @@ import Cookies from 'js-cookie';
import { formatDate, formatDuration } from '#/utils/format.js';
import events from '#/src/events.js';
import processSummaryTemplate from '#/utils/process-summary-template.js';
import getPath from '#/src/get-path.js';
import Banner from '#/components/media/banner.vue';
import ActorTile from '#/components/actors/tile.vue';
@@ -450,23 +462,44 @@ const {
const { scene } = pageProps;
const tags = [
{
tags: scene.tags.filter((tag) => tag.actorId === null),
actor: null,
},
...scene.actors.map((actor) => ({
actor,
tags: scene.tags.filter((tag) => tag.actorId === actor.id),
})),
]
.filter((actorTags) => actorTags.tags.length > 0)
.toSorted((actorTagsA, actorTagsB) => {
const priorityA = actorTagsA.tags.reduce((acc, tag) => acc + tag.priority, 0) / actorTagsA.tags.length;
const priorityB = actorTagsB.tags.reduce((acc, tag) => acc + tag.priority, 0) / actorTagsB.tags.length;
/*
const tags = scene.tags.map((tag) => ({
...tag,
actor: scene.actors.find((actor) => actor.id === tag.actorId) || null,
}));
*/
return priorityB - priorityA;
});
const actorsById = Object.fromEntries(scene.actors.map((actor) => [actor.id, actor]));
const tags = Array.from(scene.tags
.reduce((acc, tag) => {
const accTag = acc.get(tag.id);
if (accTag && tag.actorId) {
return acc.set(tag.id, {
...tag,
actors: [...accTag.actors, actorsById[tag.actorId]].toSorted((actorA, actorB) => actorA.name.localeCompare(actorB.name)),
});
}
if (accTag) {
// shouldn't happen, but account for it
return acc;
}
if (tag.actorId) {
return acc.set(tag.id, {
...tag,
actors: [actorsById[tag.actorId]],
});
}
return acc.set(tag.id, {
...tag,
actors: [],
});
}, new Map())
.values());
const showSummaryDialog = ref(false);
@@ -671,7 +704,7 @@ function copySummary() {
.tags {
display: flex;
flex-wrap: wrap;
gap: .25rem 1rem;
gap: .35rem;
}
.tags-section {
@@ -691,25 +724,60 @@ function copySummary() {
overflow-x: auto;
.actor {
width: 10rem;
width: 9rem;
flex-shrink: 0;
}
}
.tag {
padding: .5rem;
display: flex;
border-radius: .25rem;
margin: 0 .25rem .25rem 0;
background: var(--background);
box-shadow: 0 0 3px var(--shadow-weak-30);
overflow: hidden;
&:hover {
box-shadow: 0 0 3px var(--shadow-weak-20);
}
}
.tag-name {
display: inline-flex;
align-items: center;
padding: .5rem;
&:hover {
color: var(--primary);
box-shadow: 0 0 3px var(--shadow-weak-20);
cursor: pointer;
}
}
.tag-frame {
display: inline-flex;
justify-content: center;
width: 2.25rem;
height: 2.25rem;
position: relative;
overflow: hidden;
&::after {
content: '';
position: absolute;
inset: 0;
box-shadow: inset 0 0 3px var(--shadow-weak-20);
pointer-events: none; /* so it doesn't block hover/click on the image */
}
}
.tag-avatar {
height: 350%;
}
.tag-star {
height: 100%;
fill: var(--primary);
}
.movies,
.series {
display: grid;