Skip to content

Commit 7d699d9

Browse files
committed
Sync lora favorites/preferences
1 parent c2d2889 commit 7d699d9

File tree

4 files changed

+406
-28
lines changed

4 files changed

+406
-28
lines changed

src/app/home/options/options.component.css

Lines changed: 96 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,7 @@
198198
/* Set a minimum height to prevent layout shifts */
199199
max-width: 600px;
200200
/* Enforce a consistent card width */
201+
position: relative;
201202
}
202203

203204
.lora-image {
@@ -240,7 +241,8 @@
240241
align-items: center;
241242
margin-bottom: 10px;
242243
/* flex-wrap: nowrap; Prevent wrapping */
243-
max-width: 30%;
244+
width: 100%;
245+
max-width: 100%;
244246
/* Ensure it doesn't exceed the container width */
245247
}
246248

@@ -261,6 +263,90 @@
261263
/* Adjust based on image and button width */
262264
}
263265

266+
.lora-filter-row {
267+
display: flex;
268+
align-items: center;
269+
gap: 8px;
270+
margin-bottom: 10px;
271+
}
272+
273+
.lora-tag-filter {
274+
flex: 1 1 auto;
275+
min-width: 220px;
276+
}
277+
278+
.lora-filter-controls {
279+
display: flex;
280+
align-items: center;
281+
gap: 8px;
282+
flex: 0 0 auto;
283+
}
284+
285+
.lora-sort-select {
286+
min-width: 140px;
287+
}
288+
289+
.lora-favorites-toggle {
290+
height: 30px;
291+
width: 30px;
292+
padding: 0;
293+
display: inline-flex;
294+
align-items: center;
295+
justify-content: center;
296+
}
297+
298+
.lora-favorites-toggle:disabled {
299+
opacity: 0.5;
300+
cursor: not-allowed;
301+
}
302+
303+
.lora-clear-filters {
304+
color: #6c757d;
305+
opacity: 0.65;
306+
text-decoration: none;
307+
padding: 0 4px;
308+
}
309+
310+
.lora-clear-filters:hover {
311+
opacity: 0.9;
312+
}
313+
314+
.lora-favorite-btn {
315+
position: absolute;
316+
top: 6px;
317+
right: 6px;
318+
width: 26px;
319+
height: 26px;
320+
border: none;
321+
border-radius: 999px;
322+
background: rgba(0, 0, 0, 0.35);
323+
color: #ffffff;
324+
display: flex;
325+
align-items: center;
326+
justify-content: center;
327+
opacity: 0.7;
328+
transition: opacity 0.2s ease, transform 0.2s ease;
329+
}
330+
331+
.lora-favorite-btn:disabled {
332+
opacity: 0.4;
333+
cursor: not-allowed;
334+
}
335+
336+
.lora-favorite-btn.is-favorite {
337+
color: #ffd966;
338+
opacity: 1;
339+
}
340+
341+
.lora-favorite-btn:hover {
342+
opacity: 1;
343+
transform: scale(1.05);
344+
}
345+
346+
.selected-loras-panel {
347+
margin-top: 12px;
348+
}
349+
264350
/* Menu dropdowns */
265351
#collapseExample {
266352
margin-left: 20px;
@@ -493,6 +579,15 @@
493579
.d-flex.justify-content-between.mb-3 input {
494580
margin-bottom: 0px;
495581
}
582+
583+
.lora-filter-row {
584+
flex-wrap: wrap;
585+
}
586+
587+
.lora-filter-controls {
588+
width: 100%;
589+
justify-content: flex-start;
590+
}
496591
}
497592

498593
/* Queue Selector Styles */

src/app/home/options/options.component.html

Lines changed: 49 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -571,14 +571,32 @@ <h5>Load LoRAs?</h5>
571571
</span>
572572
</p>
573573

574-
<!-- Tag Selection Buttons -->
575-
<p-multiSelect [options]="loraTagOptions" [(ngModel)]="selectedTags" optionLabel="optionLabel"
576-
optionValue="optionValue" [virtualScroll]="true" [virtualScrollItemSize]="43" placeholder="Filter Loras by tags"
577-
[style]="{ width: '100%', marginBottom: '10px' }" (onChange)="filterLoras()">
578-
</p-multiSelect>
574+
<!-- Tag Selection Buttons + Sort/Favorites Controls -->
575+
<div class="lora-filter-row">
576+
<p-multiSelect [options]="loraTagOptions" [(ngModel)]="selectedTags" optionLabel="optionLabel"
577+
optionValue="optionValue" [virtualScroll]="true" [virtualScrollItemSize]="43" placeholder="Filter Loras by tags"
578+
styleClass="lora-tag-filter" [style]="{ width: '100%' }" (onChange)="filterLoras()">
579+
</p-multiSelect>
580+
<div class="lora-filter-controls">
581+
<button type="button" class="btn btn-outline-secondary btn-sm lora-favorites-toggle"
582+
[class.active]="showFavoriteLorasOnly" (click)="toggleFavoriteLorasOnly()"
583+
[disabled]="!isLoggedIn"
584+
[attr.title]="isLoggedIn ? 'Show favorites only' : 'Login required to view favorites'">
585+
<i class="bi" [ngClass]="showFavoriteLorasOnly ? 'bi-star-fill' : 'bi-star'"></i>
586+
</button>
587+
<select class="form-select form-select-sm lora-sort-select" [(ngModel)]="loraSortOption"
588+
(ngModelChange)="onLoraSortChange($event)">
589+
<option *ngFor="let option of loraSortOptions" [ngValue]="option.value">{{ option.label }}</option>
590+
</select>
591+
<button type="button" class="btn btn-link btn-sm lora-clear-filters" (click)="clearLoraFilters()"
592+
title="Clear LoRA filters">
593+
<i class="bi bi-x-circle"></i>
594+
</button>
595+
</div>
596+
</div>
579597

580598
<!-- Search and Add Custom LoRA -->
581-
<div class="d-flex justify-content-between mb-3">
599+
<div class="d-flex justify-content-between mb-3 lora-search-row">
582600
<input type="text" class="form-control w-80" placeholder="Search LoRAs..." [(ngModel)]="loraSearchQuery"
583601
(ngModelChange)="filterLoras()">
584602
</div>
@@ -599,30 +617,41 @@ <h5>Full Lora Preview</h5>
599617
Request LoRAs!
600618
</button>
601619
<div *ngFor="let lora of filteredLoras" class="lora-card">
620+
<button class="lora-favorite-btn" type="button" (click)="toggleLoraFavorite(lora, $event)"
621+
[disabled]="!isLoggedIn"
622+
[class.is-favorite]="isLoraFavorite(lora)"
623+
[attr.title]="isLoggedIn ? 'Favorite LoRA' : 'Login required to favorite'">
624+
<i class="bi" [ngClass]="isLoraFavorite(lora) ? 'bi-star-fill' : 'bi-star'"></i>
625+
</button>
602626
<img [src]="lora.image_url" [alt]="lora.name" class="lora-image"
603627
(click)="openImageModalLoraPreview(lora.image_url)">
604628
<div class="lora-info">
605629
<h5 class="lora-name">{{ lora.name }}</h5>
606630
<p class="lora-version">{{ lora.version }}</p>
607631
</div>
608-
<button class="btn btn-primary btn-sm w-100" (click)="selectLora(lora)">Select</button>
632+
<button *ngIf="!isLoraSelected(lora)" class="btn btn-primary btn-sm w-100" (click)="selectLora(lora)">
633+
Select
634+
</button>
635+
<button *ngIf="isLoraSelected(lora)" class="btn btn-danger btn-sm w-100" (click)="removeLora(lora)">
636+
Remove
637+
</button>
609638
</div>
610639
</div>
640+
</div>
611641

612-
<!-- Selected LoRA Display with Sliders -->
613-
<div *ngIf="selectedLoras.length > 0" class="mt-3">
614-
<h6>Selected LoRAs:</h6>
615-
<div *ngFor="let loraItem of selectedLoras" class="selected-lora-item">
616-
<div class="d-flex align-items-center">
617-
<img [src]="loraItem.image_url" [alt]="loraItem.name" class="selected-lora-image">
618-
<div class="selected-lora-details">
619-
<h5 class="lora-name">{{ loraItem.name }}</h5>
620-
<input type="range" min="0" max="1.5" step="0.1" [(ngModel)]="loraItem.strength"
621-
(ngModelChange)="updateStrength(loraItem, loraItem.strength)">
622-
<span>Strength: {{ loraItem.strength }}</span>
623-
</div>
624-
<button class="btn btn-danger btn-sm remove-button" (click)="removeLora(loraItem)">Remove</button>
642+
<!-- Selected LoRA Display with Sliders -->
643+
<div *ngIf="selectedLoras.length > 0" class="selected-loras-panel">
644+
<h6>Selected LoRAs:</h6>
645+
<div *ngFor="let loraItem of selectedLoras" class="selected-lora-item">
646+
<div class="d-flex align-items-center">
647+
<img [src]="loraItem.image_url" [alt]="loraItem.name" class="selected-lora-image">
648+
<div class="selected-lora-details">
649+
<h5 class="lora-name">{{ loraItem.name }}</h5>
650+
<input type="range" min="0" max="1.5" step="0.1" [(ngModel)]="loraItem.strength"
651+
(ngModelChange)="updateStrength(loraItem, loraItem.strength)">
652+
<span>Strength: {{ loraItem.strength }}</span>
625653
</div>
654+
<button class="btn btn-danger btn-sm remove-button" (click)="removeLora(loraItem)">Remove</button>
626655
</div>
627656
</div>
628657
</div>

0 commit comments

Comments
 (0)