Fix lint, make design more consistent, add license texts,
All checks were successful
Queue Release Build / prepare (push) Successful in 11s
Deploy Web Apps / deploy (push) Successful in 14m0s
Queue Release Build / build-linux (push) Successful in 35m41s
Queue Release Build / build-windows (push) Successful in 28m53s
Queue Release Build / finalize (push) Successful in 2m6s
All checks were successful
Queue Release Build / prepare (push) Successful in 11s
Deploy Web Apps / deploy (push) Successful in 14m0s
Queue Release Build / build-linux (push) Successful in 35m41s
Queue Release Build / build-windows (push) Successful in 28m53s
Queue Release Build / finalize (push) Successful in 2m6s
This commit is contained in:
@@ -11,7 +11,7 @@
|
||||
</span>
|
||||
<button
|
||||
(click)="clearReply()"
|
||||
class="rounded p-1 hover:bg-secondary"
|
||||
class="grid h-6 w-6 place-items-center rounded transition-colors hover:bg-secondary"
|
||||
>
|
||||
<ng-icon
|
||||
name="lucideX"
|
||||
|
||||
@@ -55,7 +55,7 @@
|
||||
<div class="flex flex-col gap-2">
|
||||
<button
|
||||
(click)="saveEdit()"
|
||||
class="rounded p-1 text-primary hover:bg-primary/10"
|
||||
class="grid h-6 w-6 place-items-center rounded text-primary transition-colors hover:bg-primary/10"
|
||||
>
|
||||
<ng-icon
|
||||
name="lucideCheck"
|
||||
@@ -64,7 +64,7 @@
|
||||
</button>
|
||||
<button
|
||||
(click)="cancelEdit()"
|
||||
class="rounded p-1 text-muted-foreground hover:bg-secondary"
|
||||
class="grid h-6 w-6 place-items-center rounded text-muted-foreground transition-colors hover:bg-secondary"
|
||||
>
|
||||
<ng-icon
|
||||
name="lucideX"
|
||||
@@ -108,7 +108,7 @@
|
||||
<div class="absolute right-2 top-2 flex gap-1 opacity-0 transition-opacity group-hover/img:opacity-100">
|
||||
<button
|
||||
(click)="openLightbox(att); $event.stopPropagation()"
|
||||
class="rounded-md bg-black/60 p-1.5 text-white backdrop-blur-sm transition-colors hover:bg-black/80"
|
||||
class="grid h-7 w-7 place-items-center rounded-md bg-black/60 text-white backdrop-blur-sm transition-colors hover:bg-black/80"
|
||||
title="View full size"
|
||||
>
|
||||
<ng-icon
|
||||
@@ -118,7 +118,7 @@
|
||||
</button>
|
||||
<button
|
||||
(click)="downloadAttachment(att); $event.stopPropagation()"
|
||||
class="rounded-md bg-black/60 p-1.5 text-white backdrop-blur-sm transition-colors hover:bg-black/80"
|
||||
class="grid h-7 w-7 place-items-center rounded-md bg-black/60 text-white backdrop-blur-sm transition-colors hover:bg-black/80"
|
||||
title="Download"
|
||||
>
|
||||
<ng-icon
|
||||
@@ -338,7 +338,7 @@
|
||||
<div class="relative">
|
||||
<button
|
||||
(click)="toggleEmojiPicker()"
|
||||
class="rounded-l-lg p-1.5 transition-colors hover:bg-secondary"
|
||||
class="grid h-8 w-8 place-items-center rounded-l-lg transition-colors hover:bg-secondary"
|
||||
>
|
||||
<ng-icon
|
||||
name="lucideSmile"
|
||||
@@ -362,7 +362,7 @@
|
||||
|
||||
<button
|
||||
(click)="requestReply()"
|
||||
class="p-1.5 transition-colors hover:bg-secondary"
|
||||
class="grid h-8 w-8 place-items-center transition-colors hover:bg-secondary"
|
||||
>
|
||||
<ng-icon
|
||||
name="lucideReply"
|
||||
@@ -373,7 +373,7 @@
|
||||
@if (isOwnMessage()) {
|
||||
<button
|
||||
(click)="startEdit()"
|
||||
class="p-1.5 transition-colors hover:bg-secondary"
|
||||
class="grid h-8 w-8 place-items-center transition-colors hover:bg-secondary"
|
||||
>
|
||||
<ng-icon
|
||||
name="lucideEdit"
|
||||
@@ -385,7 +385,7 @@
|
||||
@if (isOwnMessage() || isAdmin()) {
|
||||
<button
|
||||
(click)="requestDelete()"
|
||||
class="rounded-r-lg p-1.5 transition-colors hover:bg-destructive/10"
|
||||
class="grid h-8 w-8 place-items-center rounded-r-lg transition-colors hover:bg-destructive/10"
|
||||
>
|
||||
<ng-icon
|
||||
name="lucideTrash2"
|
||||
|
||||
@@ -55,7 +55,15 @@ const COMMON_EMOJIS = [
|
||||
'🔥',
|
||||
'👀'
|
||||
];
|
||||
const RICH_MARKDOWN_PATTERN = /(^|\n)(#{1,6}\s|>\s|[-*+]\s|\d+\.\s|```|~~~)|!\[[^\]]*\]\([^)]+\)|\[[^\]]+\]\([^)]+\)|`[^`\n]+`|\*\*[^*\n]+\*\*|__[^_\n]+__|\*[^*\n]+\*|_[^_\n]+_|(?:^|\n)\|.+\|/m;
|
||||
const RICH_MARKDOWN_PATTERNS = [
|
||||
/(^|\n)(#{1,6}\s|>\s|[-*+]\s|\d+\.\s|```|~~~)/m,
|
||||
/!\[[^\]]*\]\([^)]+\)/,
|
||||
/\[[^\]]+\]\([^)]+\)/,
|
||||
/`[^`\n]+`/,
|
||||
/\*\*[^*\n]+\*\*|__[^_\n]+__/,
|
||||
/\*[^*\n]+\*|_[^_\n]+_/,
|
||||
/(?:^|\n)\|.+\|/m
|
||||
];
|
||||
|
||||
interface ChatMessageAttachmentViewModel extends Attachment {
|
||||
isAudio: boolean;
|
||||
@@ -292,7 +300,7 @@ export class ChatMessageItemComponent {
|
||||
}
|
||||
|
||||
requiresRichMarkdown(content: string): boolean {
|
||||
return RICH_MARKDOWN_PATTERN.test(content);
|
||||
return RICH_MARKDOWN_PATTERNS.some((pattern) => pattern.test(content));
|
||||
}
|
||||
|
||||
formatBytes(bytes: number): string {
|
||||
|
||||
@@ -73,4 +73,4 @@ export class ChatMessageMarkdownComponent {
|
||||
|
||||
return PRISM_LANGUAGE_ALIASES[normalized] ?? normalized;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
<div class="absolute right-3 top-3 flex gap-2">
|
||||
<button
|
||||
(click)="downloadAttachment(lightboxAttachment()!)"
|
||||
class="rounded-lg bg-black/60 p-2 text-white backdrop-blur-sm transition-colors hover:bg-black/80"
|
||||
class="grid h-9 w-9 place-items-center rounded-lg bg-black/60 text-white backdrop-blur-sm transition-colors hover:bg-black/80"
|
||||
title="Download"
|
||||
>
|
||||
<ng-icon
|
||||
@@ -30,7 +30,7 @@
|
||||
</button>
|
||||
<button
|
||||
(click)="closeLightbox()"
|
||||
class="rounded-lg bg-black/60 p-2 text-white backdrop-blur-sm transition-colors hover:bg-black/80"
|
||||
class="grid h-9 w-9 place-items-center rounded-lg bg-black/60 text-white backdrop-blur-sm transition-colors hover:bg-black/80"
|
||||
title="Close"
|
||||
>
|
||||
<ng-icon
|
||||
|
||||
@@ -80,21 +80,21 @@
|
||||
</div>
|
||||
</div>
|
||||
} @else {
|
||||
<div class="grid grid-cols-2 gap-4 md:grid-cols-3 xl:grid-cols-4">
|
||||
<div class="columns-[12rem] gap-4">
|
||||
@for (gif of results(); track gif.id) {
|
||||
<button
|
||||
type="button"
|
||||
(click)="selectGif(gif)"
|
||||
class="group overflow-hidden rounded-2xl border border-border/80 bg-secondary/10 text-left shadow-sm transition-transform duration-200 hover:-translate-y-0.5 hover:border-primary/50 hover:bg-secondary/30"
|
||||
class="group mx-auto mb-4 inline-block w-full max-w-[15.5rem] break-inside-avoid align-top overflow-hidden rounded-2xl border border-border/80 bg-secondary/10 text-left shadow-sm transition-transform duration-200 hover:-translate-y-0.5 hover:border-primary/50 hover:bg-secondary/30"
|
||||
>
|
||||
<div
|
||||
class="relative overflow-hidden bg-secondary/30"
|
||||
[style.aspect-ratio]="gifAspectRatio(gif)"
|
||||
class="relative flex items-center justify-center overflow-hidden bg-secondary/30"
|
||||
[style.height.px]="gifCardHeight(gif)"
|
||||
>
|
||||
<img
|
||||
[appChatImageProxyFallback]="gif.previewUrl || gif.url"
|
||||
[alt]="gif.title || 'KLIPY GIF'"
|
||||
class="h-full w-full object-cover transition-transform duration-200 group-hover:scale-[1.03]"
|
||||
class="h-full w-full object-contain p-1.5 transition-transform duration-200 group-hover:scale-[1.03]"
|
||||
loading="lazy"
|
||||
/>
|
||||
<span
|
||||
|
||||
@@ -23,6 +23,12 @@ import {
|
||||
import { KlipyGif, KlipyService } from '../../application/klipy.service';
|
||||
import { ChatImageProxyFallbackDirective } from '../chat-image-proxy-fallback.directive';
|
||||
|
||||
const KLIPY_CARD_MIN_WIDTH = 140;
|
||||
const KLIPY_CARD_MAX_WIDTH = 248;
|
||||
const KLIPY_CARD_MIN_HEIGHT = 104;
|
||||
const KLIPY_CARD_MAX_HEIGHT = 220;
|
||||
const KLIPY_CARD_FALLBACK_SIZE = 160;
|
||||
|
||||
@Component({
|
||||
selector: 'app-klipy-gif-picker',
|
||||
standalone: true,
|
||||
@@ -106,12 +112,8 @@ export class KlipyGifPickerComponent implements OnInit, AfterViewInit, OnDestroy
|
||||
this.closed.emit(undefined);
|
||||
}
|
||||
|
||||
gifAspectRatio(gif: KlipyGif): string {
|
||||
if (gif.width > 0 && gif.height > 0) {
|
||||
return `${gif.width} / ${gif.height}`;
|
||||
}
|
||||
|
||||
return '1 / 1';
|
||||
gifCardHeight(gif: KlipyGif): number {
|
||||
return this.getGifCardSize(gif).height;
|
||||
}
|
||||
|
||||
private async loadResults(reset: boolean): Promise<void> {
|
||||
@@ -182,4 +184,32 @@ export class KlipyGifPickerComponent implements OnInit, AfterViewInit, OnDestroy
|
||||
this.searchTimer = null;
|
||||
}
|
||||
}
|
||||
|
||||
private getGifCardSize(gif: KlipyGif): { width: number; height: number } {
|
||||
if (gif.width <= 0 || gif.height <= 0) {
|
||||
return {
|
||||
width: KLIPY_CARD_FALLBACK_SIZE,
|
||||
height: KLIPY_CARD_FALLBACK_SIZE
|
||||
};
|
||||
}
|
||||
|
||||
const maxScale = Math.min(
|
||||
KLIPY_CARD_MAX_WIDTH / gif.width,
|
||||
KLIPY_CARD_MAX_HEIGHT / gif.height
|
||||
);
|
||||
const minScale = Math.max(
|
||||
KLIPY_CARD_MIN_WIDTH / gif.width,
|
||||
KLIPY_CARD_MIN_HEIGHT / gif.height
|
||||
);
|
||||
const scale = minScale <= maxScale
|
||||
? Math.min(maxScale, Math.max(minScale, 1))
|
||||
: maxScale;
|
||||
const scaledWidth = Math.round(gif.width * scale);
|
||||
const scaledHeight = Math.round(gif.height * scale);
|
||||
|
||||
return {
|
||||
width: Math.min(KLIPY_CARD_MAX_WIDTH, Math.max(KLIPY_CARD_MIN_WIDTH, scaledWidth)),
|
||||
height: Math.min(KLIPY_CARD_MAX_HEIGHT, Math.max(KLIPY_CARD_MIN_HEIGHT, scaledHeight))
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user