feat(chat): 39 nuove chat bubble (253-291) + paginator soundboard

- 39 bubble custom con relativo pointer colorato (colore campionato dal
  fondo di ogni bubble) + regole CSS in Chats.css (resa in-stanza border-image
  + anteprima nel selettore)
- selezionabili via chat.styles dell'ui-config (lato server)
- soundboard: paginazione 9/pagina (griglia 3x3) con frecce + indicatore,
  cosi la card non cresce a dismisura
This commit is contained in:
medievalshell
2026-05-31 04:01:04 +02:00
parent fa71e8eb4a
commit 7745c5f66b
80 changed files with 506 additions and 12 deletions
Binary file not shown.

After

Width:  |  Height:  |  Size: 695 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 113 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 113 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 806 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 113 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 113 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 551 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 113 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 880 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 113 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 113 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 381 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 113 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 891 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 113 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 113 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 798 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 113 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 645 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 113 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 632 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 113 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 656 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 113 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 534 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 113 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 625 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 113 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 113 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 661 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 113 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 113 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 113 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 113 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 325 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 113 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 113 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 326 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 113 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 455 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 113 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 533 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 113 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 326 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 113 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 408 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 112 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 825 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 113 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 831 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 113 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 113 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 113 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 113 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 408 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 113 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 906 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 113 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 113 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 573 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 113 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 879 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 113 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 548 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 113 B

+38 -12
View File
@@ -10,6 +10,18 @@ export const SoundboardView: FC<{}> = () =>
const [ isVisible, setIsVisible ] = useState(false); const [ isVisible, setIsVisible ] = useState(false);
const { enabled, sounds, lastPlayed, play } = useSoundboard(); const { enabled, sounds, lastPlayed, play } = useSoundboard();
const PAGE_SIZE = 9;
const [ page, setPage ] = useState(0);
const totalPages = Math.max(1, Math.ceil(sounds.length / PAGE_SIZE));
// Clamp the page if the sound list shrinks (or on first load).
useEffect(() =>
{
if(page > (totalPages - 1)) setPage(0);
}, [ totalPages, page ]);
const pageSounds = sounds.slice(page * PAGE_SIZE, (page * PAGE_SIZE) + PAGE_SIZE);
useEffect(() => useEffect(() =>
{ {
const linkTracker: ILinkEventTracker = { const linkTracker: ILinkEventTracker = {
@@ -49,18 +61,32 @@ export const SoundboardView: FC<{}> = () =>
{ !sounds.length && { !sounds.length &&
<Text small className="text-black/50">{ LocalizeText('soundboard.empty') }</Text> } <Text small className="text-black/50">{ LocalizeText('soundboard.empty') }</Text> }
{ !!sounds.length && { !!sounds.length &&
<div className="grid grid-cols-3 gap-2"> <>
{ sounds.map(sound => ( <div className="grid grid-cols-3 gap-2">
<button { pageSounds.map(sound => (
key={ sound.id } <button
onClick={ () => play(sound) } key={ sound.id }
title={ sound.name } onClick={ () => play(sound) }
className="flex h-20 cursor-pointer flex-col items-center justify-center gap-1 rounded-lg bg-[#3a7bb5] px-2 text-white shadow transition-transform hover:bg-[#336ea3] active:scale-95"> title={ sound.name }
<span className="text-2xl leading-none">🔊</span> className="flex h-20 cursor-pointer flex-col items-center justify-center gap-1 rounded-lg bg-[#3a7bb5] px-2 text-white shadow transition-transform hover:bg-[#336ea3] active:scale-95">
<span className="line-clamp-2 text-center text-[11px] font-bold leading-tight">{ sound.name }</span> <span className="text-2xl leading-none">🔊</span>
</button> <span className="line-clamp-2 text-center text-[11px] font-bold leading-tight">{ sound.name }</span>
)) } </button>
</div> } )) }
</div>
{ totalPages > 1 &&
<Flex alignItems="center" justifyContent="center" gap={ 2 } className="select-none pt-1">
<button
disabled={ page === 0 }
onClick={ () => setPage(p => Math.max(0, p - 1)) }
className="cursor-pointer rounded bg-[#3a7bb5] px-3 py-1 text-sm font-bold text-white hover:bg-[#336ea3] disabled:cursor-default disabled:opacity-40"></button>
<Text small bold className="min-w-[44px] text-center text-[#2f6f95]">{ page + 1 } / { totalPages }</Text>
<button
disabled={ page >= (totalPages - 1) }
onClick={ () => setPage(p => Math.min(totalPages - 1, p + 1)) }
className="cursor-pointer rounded bg-[#3a7bb5] px-3 py-1 text-sm font-bold text-white hover:bg-[#336ea3] disabled:cursor-default disabled:opacity-40"></button>
</Flex> }
</> }
{ lastPlayed && { lastPlayed &&
<Flex alignItems="center" justifyContent="center" className="pt-1"> <Flex alignItems="center" justifyContent="center" className="pt-1">
<Text small className="text-[#2f6f95]"> <Text small className="text-[#2f6f95]">
+468
View File
@@ -807,6 +807,318 @@
} }
} }
&.bubble-253 {
border-image-source: url('@/assets/images/chat/chatbubbles/bubble_253.png');
.pointer {
background: url('@/assets/images/chat/chatbubbles/bubble_253_pointer.png');
}
}
&.bubble-254 {
border-image-source: url('@/assets/images/chat/chatbubbles/bubble_254.png');
.pointer {
background: url('@/assets/images/chat/chatbubbles/bubble_254_pointer.png');
}
}
&.bubble-255 {
border-image-source: url('@/assets/images/chat/chatbubbles/bubble_255.png');
.pointer {
background: url('@/assets/images/chat/chatbubbles/bubble_255_pointer.png');
}
}
&.bubble-256 {
border-image-source: url('@/assets/images/chat/chatbubbles/bubble_256.png');
.pointer {
background: url('@/assets/images/chat/chatbubbles/bubble_256_pointer.png');
}
}
&.bubble-257 {
border-image-source: url('@/assets/images/chat/chatbubbles/bubble_257.png');
.pointer {
background: url('@/assets/images/chat/chatbubbles/bubble_257_pointer.png');
}
}
&.bubble-258 {
border-image-source: url('@/assets/images/chat/chatbubbles/bubble_258.png');
.pointer {
background: url('@/assets/images/chat/chatbubbles/bubble_258_pointer.png');
}
}
&.bubble-259 {
border-image-source: url('@/assets/images/chat/chatbubbles/bubble_259.png');
.pointer {
background: url('@/assets/images/chat/chatbubbles/bubble_259_pointer.png');
}
}
&.bubble-260 {
border-image-source: url('@/assets/images/chat/chatbubbles/bubble_260.png');
.pointer {
background: url('@/assets/images/chat/chatbubbles/bubble_260_pointer.png');
}
}
&.bubble-261 {
border-image-source: url('@/assets/images/chat/chatbubbles/bubble_261.png');
.pointer {
background: url('@/assets/images/chat/chatbubbles/bubble_261_pointer.png');
}
}
&.bubble-262 {
border-image-source: url('@/assets/images/chat/chatbubbles/bubble_262.png');
.pointer {
background: url('@/assets/images/chat/chatbubbles/bubble_262_pointer.png');
}
}
&.bubble-263 {
border-image-source: url('@/assets/images/chat/chatbubbles/bubble_263.png');
.pointer {
background: url('@/assets/images/chat/chatbubbles/bubble_263_pointer.png');
}
}
&.bubble-264 {
border-image-source: url('@/assets/images/chat/chatbubbles/bubble_264.png');
.pointer {
background: url('@/assets/images/chat/chatbubbles/bubble_264_pointer.png');
}
}
&.bubble-265 {
border-image-source: url('@/assets/images/chat/chatbubbles/bubble_265.png');
.pointer {
background: url('@/assets/images/chat/chatbubbles/bubble_265_pointer.png');
}
}
&.bubble-266 {
border-image-source: url('@/assets/images/chat/chatbubbles/bubble_266.png');
.pointer {
background: url('@/assets/images/chat/chatbubbles/bubble_266_pointer.png');
}
}
&.bubble-267 {
border-image-source: url('@/assets/images/chat/chatbubbles/bubble_267.png');
.pointer {
background: url('@/assets/images/chat/chatbubbles/bubble_267_pointer.png');
}
}
&.bubble-268 {
border-image-source: url('@/assets/images/chat/chatbubbles/bubble_268.png');
.pointer {
background: url('@/assets/images/chat/chatbubbles/bubble_268_pointer.png');
}
}
&.bubble-269 {
border-image-source: url('@/assets/images/chat/chatbubbles/bubble_269.png');
.pointer {
background: url('@/assets/images/chat/chatbubbles/bubble_269_pointer.png');
}
}
&.bubble-270 {
border-image-source: url('@/assets/images/chat/chatbubbles/bubble_270.png');
.pointer {
background: url('@/assets/images/chat/chatbubbles/bubble_270_pointer.png');
}
}
&.bubble-271 {
border-image-source: url('@/assets/images/chat/chatbubbles/bubble_271.png');
.pointer {
background: url('@/assets/images/chat/chatbubbles/bubble_271_pointer.png');
}
}
&.bubble-272 {
border-image-source: url('@/assets/images/chat/chatbubbles/bubble_272.png');
.pointer {
background: url('@/assets/images/chat/chatbubbles/bubble_272_pointer.png');
}
}
&.bubble-273 {
border-image-source: url('@/assets/images/chat/chatbubbles/bubble_273.png');
.pointer {
background: url('@/assets/images/chat/chatbubbles/bubble_273_pointer.png');
}
}
&.bubble-274 {
border-image-source: url('@/assets/images/chat/chatbubbles/bubble_274.png');
.pointer {
background: url('@/assets/images/chat/chatbubbles/bubble_274_pointer.png');
}
}
&.bubble-275 {
border-image-source: url('@/assets/images/chat/chatbubbles/bubble_275.png');
.pointer {
background: url('@/assets/images/chat/chatbubbles/bubble_275_pointer.png');
}
}
&.bubble-276 {
border-image-source: url('@/assets/images/chat/chatbubbles/bubble_276.png');
.pointer {
background: url('@/assets/images/chat/chatbubbles/bubble_276_pointer.png');
}
}
&.bubble-277 {
border-image-source: url('@/assets/images/chat/chatbubbles/bubble_277.png');
.pointer {
background: url('@/assets/images/chat/chatbubbles/bubble_277_pointer.png');
}
}
&.bubble-278 {
border-image-source: url('@/assets/images/chat/chatbubbles/bubble_278.png');
.pointer {
background: url('@/assets/images/chat/chatbubbles/bubble_278_pointer.png');
}
}
&.bubble-279 {
border-image-source: url('@/assets/images/chat/chatbubbles/bubble_279.png');
.pointer {
background: url('@/assets/images/chat/chatbubbles/bubble_279_pointer.png');
}
}
&.bubble-280 {
border-image-source: url('@/assets/images/chat/chatbubbles/bubble_280.png');
.pointer {
background: url('@/assets/images/chat/chatbubbles/bubble_280_pointer.png');
}
}
&.bubble-281 {
border-image-source: url('@/assets/images/chat/chatbubbles/bubble_281.png');
.pointer {
background: url('@/assets/images/chat/chatbubbles/bubble_281_pointer.png');
}
}
&.bubble-282 {
border-image-source: url('@/assets/images/chat/chatbubbles/bubble_282.png');
.pointer {
background: url('@/assets/images/chat/chatbubbles/bubble_282_pointer.png');
}
}
&.bubble-283 {
border-image-source: url('@/assets/images/chat/chatbubbles/bubble_283.png');
.pointer {
background: url('@/assets/images/chat/chatbubbles/bubble_283_pointer.png');
}
}
&.bubble-284 {
border-image-source: url('@/assets/images/chat/chatbubbles/bubble_284.png');
.pointer {
background: url('@/assets/images/chat/chatbubbles/bubble_284_pointer.png');
}
}
&.bubble-285 {
border-image-source: url('@/assets/images/chat/chatbubbles/bubble_285.png');
.pointer {
background: url('@/assets/images/chat/chatbubbles/bubble_285_pointer.png');
}
}
&.bubble-286 {
border-image-source: url('@/assets/images/chat/chatbubbles/bubble_286.png');
.pointer {
background: url('@/assets/images/chat/chatbubbles/bubble_286_pointer.png');
}
}
&.bubble-287 {
border-image-source: url('@/assets/images/chat/chatbubbles/bubble_287.png');
.pointer {
background: url('@/assets/images/chat/chatbubbles/bubble_287_pointer.png');
}
}
&.bubble-288 {
border-image-source: url('@/assets/images/chat/chatbubbles/bubble_288.png');
.pointer {
background: url('@/assets/images/chat/chatbubbles/bubble_288_pointer.png');
}
}
&.bubble-289 {
border-image-source: url('@/assets/images/chat/chatbubbles/bubble_289.png');
.pointer {
background: url('@/assets/images/chat/chatbubbles/bubble_289_pointer.png');
}
}
&.bubble-290 {
border-image-source: url('@/assets/images/chat/chatbubbles/bubble_290.png');
.pointer {
background: url('@/assets/images/chat/chatbubbles/bubble_290_pointer.png');
}
}
&.bubble-291 {
border-image-source: url('@/assets/images/chat/chatbubbles/bubble_291.png');
.pointer {
background: url('@/assets/images/chat/chatbubbles/bubble_291_pointer.png');
}
}
&.bubble-200, &.bubble-200,
&.bubble-201, &.bubble-201,
&.bubble-202, &.bubble-202,
@@ -1810,4 +2122,160 @@
background: center / contain no-repeat url('@/assets/images/chat/chatbubbles/bubble_252_extra.png'); background: center / contain no-repeat url('@/assets/images/chat/chatbubbles/bubble_252_extra.png');
} }
} }
&.bubble-253 {
background-image: url('@/assets/images/chat/chatbubbles/bubble_253.png');
}
&.bubble-254 {
background-image: url('@/assets/images/chat/chatbubbles/bubble_254.png');
}
&.bubble-255 {
background-image: url('@/assets/images/chat/chatbubbles/bubble_255.png');
}
&.bubble-256 {
background-image: url('@/assets/images/chat/chatbubbles/bubble_256.png');
}
&.bubble-257 {
background-image: url('@/assets/images/chat/chatbubbles/bubble_257.png');
}
&.bubble-258 {
background-image: url('@/assets/images/chat/chatbubbles/bubble_258.png');
}
&.bubble-259 {
background-image: url('@/assets/images/chat/chatbubbles/bubble_259.png');
}
&.bubble-260 {
background-image: url('@/assets/images/chat/chatbubbles/bubble_260.png');
}
&.bubble-261 {
background-image: url('@/assets/images/chat/chatbubbles/bubble_261.png');
}
&.bubble-262 {
background-image: url('@/assets/images/chat/chatbubbles/bubble_262.png');
}
&.bubble-263 {
background-image: url('@/assets/images/chat/chatbubbles/bubble_263.png');
}
&.bubble-264 {
background-image: url('@/assets/images/chat/chatbubbles/bubble_264.png');
}
&.bubble-265 {
background-image: url('@/assets/images/chat/chatbubbles/bubble_265.png');
}
&.bubble-266 {
background-image: url('@/assets/images/chat/chatbubbles/bubble_266.png');
}
&.bubble-267 {
background-image: url('@/assets/images/chat/chatbubbles/bubble_267.png');
}
&.bubble-268 {
background-image: url('@/assets/images/chat/chatbubbles/bubble_268.png');
}
&.bubble-269 {
background-image: url('@/assets/images/chat/chatbubbles/bubble_269.png');
}
&.bubble-270 {
background-image: url('@/assets/images/chat/chatbubbles/bubble_270.png');
}
&.bubble-271 {
background-image: url('@/assets/images/chat/chatbubbles/bubble_271.png');
}
&.bubble-272 {
background-image: url('@/assets/images/chat/chatbubbles/bubble_272.png');
}
&.bubble-273 {
background-image: url('@/assets/images/chat/chatbubbles/bubble_273.png');
}
&.bubble-274 {
background-image: url('@/assets/images/chat/chatbubbles/bubble_274.png');
}
&.bubble-275 {
background-image: url('@/assets/images/chat/chatbubbles/bubble_275.png');
}
&.bubble-276 {
background-image: url('@/assets/images/chat/chatbubbles/bubble_276.png');
}
&.bubble-277 {
background-image: url('@/assets/images/chat/chatbubbles/bubble_277.png');
}
&.bubble-278 {
background-image: url('@/assets/images/chat/chatbubbles/bubble_278.png');
}
&.bubble-279 {
background-image: url('@/assets/images/chat/chatbubbles/bubble_279.png');
}
&.bubble-280 {
background-image: url('@/assets/images/chat/chatbubbles/bubble_280.png');
}
&.bubble-281 {
background-image: url('@/assets/images/chat/chatbubbles/bubble_281.png');
}
&.bubble-282 {
background-image: url('@/assets/images/chat/chatbubbles/bubble_282.png');
}
&.bubble-283 {
background-image: url('@/assets/images/chat/chatbubbles/bubble_283.png');
}
&.bubble-284 {
background-image: url('@/assets/images/chat/chatbubbles/bubble_284.png');
}
&.bubble-285 {
background-image: url('@/assets/images/chat/chatbubbles/bubble_285.png');
}
&.bubble-286 {
background-image: url('@/assets/images/chat/chatbubbles/bubble_286.png');
}
&.bubble-287 {
background-image: url('@/assets/images/chat/chatbubbles/bubble_287.png');
}
&.bubble-288 {
background-image: url('@/assets/images/chat/chatbubbles/bubble_288.png');
}
&.bubble-289 {
background-image: url('@/assets/images/chat/chatbubbles/bubble_289.png');
}
&.bubble-290 {
background-image: url('@/assets/images/chat/chatbubbles/bubble_290.png');
}
&.bubble-291 {
background-image: url('@/assets/images/chat/chatbubbles/bubble_291.png');
}
} }