feat: implement right-click context menu for style customization and enhance real-time stats tracking

This commit is contained in:
arussac
2026-05-31 14:47:40 +02:00
parent ccacd16edb
commit 1a76e9076c
9 changed files with 389 additions and 35 deletions

View File

@@ -3,7 +3,7 @@
<div class="message-item">
<!-- Auteur + horodatage -->
<div class="message-meta">
<span class="ip-wrap">
<span class="ip-wrap" @contextmenu.prevent="openIpMenu($event, message.authorIp)" title="Clic droit pour personnaliser">
<span v-if="petsLeft(message)" class="pet">{{ petsLeft(message) }}</span>
<span class="ip" :style="ipStyle(message)">{{ message.authorIp }}</span>
<span v-if="petsRight(message)" class="pet">{{ petsRight(message) }}</span>
@@ -30,7 +30,7 @@
:key="reply.id"
class="reply"
>
<span class="ip-wrap">
<span class="ip-wrap" @contextmenu.prevent="openIpMenu($event, reply.authorIp)" title="Clic droit pour personnaliser">
<span v-if="petsLeft(reply)" class="pet pet--sm">{{ petsLeft(reply) }}</span>
<span class="ip reply-ip" :style="ipStyle(reply)">{{ reply.authorIp }}</span>
<span v-if="petsRight(reply)" class="pet pet--sm">{{ petsRight(reply) }}</span>
@@ -52,8 +52,10 @@
<script setup lang="ts">
import type { Message, Reply } from '@/composables/useMessages';
import { getIpColorWithPerks, getIpGlowWithPerks } from '@/composables/ipColor';
import { getIpColorWithPerks, getIpGlowWithPerks, getIpColor, getIpGlow } from '@/composables/ipColor';
import { usePerks } from '@/composables/usePerks';
import { openContextMenu } from '@/composables/useContextMenu';
import { useCustomStyles, IP_COLOR_OPTIONS, PET_OPTIONS } from '@/composables/useCustomStyles';
import RichContent from './RichContent.vue';
import MessageAttachments from './MessageAttachments.vue';
@@ -61,21 +63,28 @@ defineProps<{ message: Message }>();
defineEmits<{ reply: [payload: { id: string; authorIp: string }] }>();
const { perksFor } = usePerks();
const { prefs } = useCustomStyles();
/** Perks for an author: prefer the perks embedded in the payload, else the store. */
function perksOf(m: Reply): any {
return m.authorPerks ?? perksFor(m.authorIp);
}
function ipStyle(m: Reply) {
const ip = m.authorIp;
const colorOverride = prefs.ipColors[ip];
if (colorOverride && colorOverride !== 'auto') {
return { color: colorOverride, textShadow: getIpGlow(colorOverride) };
}
const p = perksOf(m);
return {
color: getIpColorWithPerks(m.authorIp, p),
textShadow: getIpGlowWithPerks(m.authorIp, p),
color: getIpColorWithPerks(ip, p),
textShadow: getIpGlowWithPerks(ip, p),
};
}
function petsLeft(m: Reply): string {
const ip = m.authorIp;
if (ip in prefs.ipPets) return prefs.ipPets[ip]; // '' = aucun pet
const pets = perksOf(m)?.pets ?? [];
return pets
.filter((x: any) => x.position === 'left' || x.position === 'both')
@@ -83,6 +92,8 @@ function petsLeft(m: Reply): string {
.join('');
}
function petsRight(m: Reply): string {
const ip = m.authorIp;
if (ip in prefs.ipPets) return ''; // override = pet gauche uniquement
const pets = perksOf(m)?.pets ?? [];
return pets
.filter((x: any) => x.position === 'right' || x.position === 'both')
@@ -90,6 +101,36 @@ function petsRight(m: Reply): string {
.join('');
}
function openIpMenu(e: MouseEvent, ip: string): void {
const currentColor = prefs.ipColors[ip] ?? 'auto';
const currentPet = ip in prefs.ipPets ? prefs.ipPets[ip] : '__inherit__';
openContextMenu({
x: e.clientX,
y: e.clientY,
title: ip,
items: [
{ value: '__h_color', label: 'Couleur', isHeader: true },
...IP_COLOR_OPTIONS.map((o) => ({ value: `color:${o.value}`, label: o.label, swatch: o.swatch })),
{ value: '__h_pet', label: 'Pet', isHeader: true },
{ value: 'pet:__inherit__', label: ' défaut (perk)' },
...PET_OPTIONS.map((o) => ({ value: `pet:${o.value}`, label: o.label })),
],
current: currentColor !== 'auto' ? `color:${currentColor}` : `pet:${currentPet}`,
onSelect: (v) => {
if (v.startsWith('color:')) {
prefs.ipColors[ip] = v.slice(6);
} else if (v.startsWith('pet:')) {
const pet = v.slice(4);
if (pet === '__inherit__') {
delete prefs.ipPets[ip];
} else {
prefs.ipPets[ip] = pet;
}
}
},
});
}
function fmt(date: string): string {
return new Date(date).toLocaleTimeString('fr-FR', { hour: '2-digit', minute: '2-digit' });
}