Allow Ctrl+K to open Kavita's search window (--input-hint-border-color, --input-hint-text-color)

This commit is contained in:
Joseph Milazzo 2025-05-10 06:26:01 -05:00
parent db94fe4e27
commit 82d5d98c03
7 changed files with 58 additions and 17 deletions

View file

@ -4,14 +4,18 @@ using API.DTOs.Scrobbling;
namespace API.DTOs.KavitaPlus.ExternalMetadata; namespace API.DTOs.KavitaPlus.ExternalMetadata;
#nullable enable #nullable enable
/// <summary>
/// Represents a request to match some series from Kavita to an external id which K+ uses.
/// </summary>
internal sealed record MatchSeriesRequestDto internal sealed record MatchSeriesRequestDto
{ {
public string SeriesName { get; set; } public required string SeriesName { get; set; }
public ICollection<string> AlternativeNames { get; set; } public ICollection<string> AlternativeNames { get; set; } = [];
public int Year { get; set; } = 0; public int Year { get; set; } = 0;
public string Query { get; set; } public string? Query { get; set; }
public int? AniListId { get; set; } public int? AniListId { get; set; }
public long? MalId { get; set; } public long? MalId { get; set; }
public string? HardcoverId { get; set; } public string? HardcoverId { get; set; }
public int? CbrId { get; set; }
public PlusMediaFormat Format { get; set; } public PlusMediaFormat Format { get; set; }
} }

View file

@ -226,7 +226,7 @@ public class ExternalMetadataService : IExternalMetadataService
AlternativeNames = altNames.Where(s => !string.IsNullOrEmpty(s)).ToList(), AlternativeNames = altNames.Where(s => !string.IsNullOrEmpty(s)).ToList(),
Year = series.Metadata.ReleaseYear, Year = series.Metadata.ReleaseYear,
AniListId = potentialAnilistId ?? ScrobblingService.GetAniListId(series), AniListId = potentialAnilistId ?? ScrobblingService.GetAniListId(series),
MalId = potentialMalId ?? ScrobblingService.GetMalId(series), MalId = potentialMalId ?? ScrobblingService.GetMalId(series)
}; };
var token = (await _unitOfWork.UserRepository.GetDefaultAdminUser()).AniListAccessToken; var token = (await _unitOfWork.UserRepository.GetDefaultAdminUser()).AniListAccessToken;

View file

@ -15,6 +15,10 @@
} @else { } @else {
<button type="button" class="btn-close" [attr.aria-label]="t('close')" (click)="resetField()"></button> <button type="button" class="btn-close" [attr.aria-label]="t('close')" (click)="resetField()"></button>
} }
} @else {
<div class="input-hint">
Ctrl+K
</div>
} }
</div> </div>
</div> </div>

View file

@ -9,6 +9,17 @@
right: 5px; right: 5px;
} }
.input-hint {
font-size: 0.8rem;
margin-top: 3px;
margin-bottom: 3px;
margin-right: 9px;
border: 1px solid var(--input-hint-border-color, lightgrey);
color: var(--input-hint-text-color);
border-radius: 4px;
padding: 2px;
}
.typeahead-input { .typeahead-input {
border: 1px solid transparent; border: 1px solid transparent;

View file

@ -100,7 +100,9 @@ export class GroupedTypeaheadComponent implements OnInit {
hasFocus: boolean = false; hasFocus: boolean = false;
typeaheadForm: FormGroup = new FormGroup({}); typeaheadForm: FormGroup = new FormGroup({
typeahead: new FormControl('', []),
});
includeChapterAndFiles: boolean = false; includeChapterAndFiles: boolean = false;
prevSearchTerm: string = ''; prevSearchTerm: string = '';
searchSettingsForm = new FormGroup(({'includeExtras': new FormControl(false)})); searchSettingsForm = new FormGroup(({'includeExtras': new FormControl(false)}));
@ -121,22 +123,37 @@ export class GroupedTypeaheadComponent implements OnInit {
this.close(); this.close();
} }
@HostListener('window:keydown', ['$event']) @HostListener('document:keydown', ['$event'])
handleKeyPress(event: KeyboardEvent) { handleKeyPress(event: KeyboardEvent) {
if (!this.hasFocus) { return; }
const isCtrlOrMeta = event.ctrlKey || event.metaKey;
switch(event.key) { switch(event.key) {
case KEY_CODES.ESC_KEY: case KEY_CODES.ESC_KEY:
if (!this.hasFocus) { return; }
this.close(); this.close();
event.stopPropagation(); event.stopPropagation();
break; break;
case KEY_CODES.K:
if (isCtrlOrMeta) {
if (this.inputElem.nativeElement) {
event.preventDefault();
event.stopPropagation();
this.inputElem.nativeElement.focus();
this.inputElem.nativeElement.click();
}
}
break;
default: default:
break; break;
} }
} }
ngOnInit(): void { ngOnInit(): void {
this.typeaheadForm.addControl('typeahead', new FormControl(this.initialValue, [])); this.typeaheadForm.get('typeahead')?.setValue(this.initialValue);
this.cdRef.markForCheck(); this.cdRef.markForCheck();
this.searchSettingsForm.get('includeExtras')!.valueChanges.pipe( this.searchSettingsForm.get('includeExtras')!.valueChanges.pipe(

View file

@ -6,7 +6,7 @@ import { MangaFormat } from 'src/app/_models/manga-format';
import {PaginatedResult} from 'src/app/_models/pagination'; import {PaginatedResult} from 'src/app/_models/pagination';
import {Series} from 'src/app/_models/series'; import {Series} from 'src/app/_models/series';
import {Volume} from 'src/app/_models/volume'; import {Volume} from 'src/app/_models/volume';
import {translate, TranslocoService} from "@jsverse/transloco"; import {translate} from "@jsverse/transloco";
import {debounceTime, ReplaySubject, shareReplay} from "rxjs"; import {debounceTime, ReplaySubject, shareReplay} from "rxjs";
export enum KEY_CODES { export enum KEY_CODES {
@ -21,6 +21,7 @@ export enum KEY_CODES {
B = 'b', B = 'b',
F = 'f', F = 'f',
H = 'h', H = 'h',
K = 'k',
BACKSPACE = 'Backspace', BACKSPACE = 'Backspace',
DELETE = 'Delete', DELETE = 'Delete',
SHIFT = 'Shift' SHIFT = 'Shift'

View file

@ -439,4 +439,8 @@
/** Series Detail **/ /** Series Detail **/
--detail-subtitle-color: lightgrey; --detail-subtitle-color: lightgrey;
/** Search **/
--input-hint-border-color: #aeaeae;
--input-hint-text-color: lightgrey;
} }