Allow to disable all hotkeys

Added angular2-hotkeys dependency inside PeerTube, to tweak some
settings

It will also allow us to support non latin keyboard in the future as we
can choose the "mouse trap" dependency
pull/6000/head
Chocobozzz 2023-10-09 15:33:19 +02:00
parent e6b455b4ea
commit 50e415e12e
No known key found for this signature in database
GPG Key ID: 583A612D890159BE
20 changed files with 332 additions and 141 deletions

View File

@ -195,7 +195,6 @@
"path-browserify",
"deep-merge",
"escape-string-regexp",
"mousetrap",
"is-plain-object",
"parse-srcset",
"deepmerge",

View File

@ -84,7 +84,6 @@
"@wdio/mocha-framework": "^8.10.4",
"@wdio/shared-store-service": "^8.10.5",
"@wdio/spec-reporter": "^8.10.5",
"angular2-hotkeys": "^13.1.0",
"angularx-qrcode": "16.0.0",
"babel-loader": "^9.1.0",
"bootstrap": "^5.1.3",
@ -126,6 +125,7 @@
"socket.io-client": "^4.5.4",
"stylelint": "^15.1.0",
"stylelint-config-sass-guidelines": "^10.0.0",
"tinykeys": "^2.1.0",
"ts-loader": "^9.3.0",
"ts-node": "^10.9.1",
"tslib": "^2.4.0",

View File

@ -1,9 +1,8 @@
import { Hotkey, HotkeysService } from 'angular2-hotkeys'
import { Subscription } from 'rxjs'
import { catchError, distinctUntilChanged, map, switchMap } from 'rxjs/operators'
import { Component, OnDestroy, OnInit, ViewChild } from '@angular/core'
import { ActivatedRoute } from '@angular/router'
import { AuthService, MarkdownService, Notifier, RestExtractor, ScreenService } from '@app/core'
import { AuthService, MarkdownService, Notifier, RestExtractor, ScreenService, Hotkey, HotkeysService } from '@app/core'
import { Account, ListOverflowItem, VideoChannel, VideoChannelService, VideoService } from '@app/shared/shared-main'
import { BlocklistService } from '@app/shared/shared-moderation'
import { SupportModalComponent } from '@app/shared/shared-support-modal'
@ -77,12 +76,12 @@ export class VideoChannelsComponent implements OnInit, OnDestroy {
})
this.hotkeys = [
new Hotkey('S', (event: KeyboardEvent): boolean => {
if (this.subscribeButton.subscribed) this.subscribeButton.unsubscribe()
new Hotkey('Shift+s', () => {
if (this.subscribeButton.isSubscribedToAll()) this.subscribeButton.unsubscribe()
else this.subscribeButton.subscribe()
return false
}, undefined, $localize`Subscribe to the account`)
}, $localize`Subscribe to the account`)
]
if (this.isUserLoggedIn()) this.hotkeysService.add(this.hotkeys)

View File

@ -1,7 +1,6 @@
import { Hotkey, HotkeysService } from 'angular2-hotkeys'
import { Observable } from 'rxjs'
import { Component, EventEmitter, Input, OnChanges, OnDestroy, OnInit, Output } from '@angular/core'
import { Notifier, ScreenService } from '@app/core'
import { Notifier, ScreenService, Hotkey, HotkeysService } from '@app/core'
import { VideoDetails, VideoService } from '@app/shared/shared-main'
import { UserVideoRateType } from '@peertube/peertube-models'
@ -41,15 +40,15 @@ export class VideoRateComponent implements OnInit, OnChanges, OnDestroy {
if (this.isUserLoggedIn) {
this.hotkeys = [
new Hotkey('shift+l', () => {
new Hotkey('Shift+l', () => {
this.setLike()
return false
}, undefined, $localize`Like the video`),
}, $localize`Like the video`),
new Hotkey('shift+d', () => {
new Hotkey('Shift+d', () => {
this.setDislike()
return false
}, undefined, $localize`Dislike the video`)
}, $localize`Dislike the video`)
]
this.hotkeysService.add(this.hotkeys)

View File

@ -1,4 +1,3 @@
import { Hotkey, HotkeysService } from 'angular2-hotkeys'
import { forkJoin, map, Observable, of, Subscription, switchMap } from 'rxjs'
import { PlatformLocation } from '@angular/common'
import { Component, ElementRef, Inject, LOCALE_ID, NgZone, OnDestroy, OnInit, ViewChild } from '@angular/core'
@ -13,6 +12,8 @@ import {
RestExtractor,
ScreenService,
ServerService,
Hotkey,
HotkeysService,
User,
UserService
} from '@app/core'
@ -866,33 +867,33 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
this.hotkeys = [
// These hotkeys are managed by the player
new Hotkey('f', e => e, undefined, $localize`Enter/exit fullscreen`),
new Hotkey('space', e => e, undefined, $localize`Play/Pause the video`),
new Hotkey('m', e => e, undefined, $localize`Mute/unmute the video`),
new Hotkey('f', e => e, $localize`Enter/exit fullscreen`),
new Hotkey('space', e => e, $localize`Play/Pause the video`),
new Hotkey('m', e => e, $localize`Mute/unmute the video`),
new Hotkey('up', e => e, undefined, $localize`Increase the volume`),
new Hotkey('down', e => e, undefined, $localize`Decrease the volume`),
new Hotkey('up', e => e, $localize`Increase the volume`),
new Hotkey('down', e => e, $localize`Decrease the volume`),
new Hotkey('t', e => {
this.theaterEnabled = !this.theaterEnabled
return false
}, undefined, $localize`Toggle theater mode`)
}, $localize`Toggle theater mode`)
]
if (!video.isLive) {
this.hotkeys = this.hotkeys.concat([
// These hotkeys are also managed by the player but only for VOD
new Hotkey('0-9', e => e, undefined, $localize`Skip to a percentage of the video: 0 is 0% and 9 is 90%`),
new Hotkey('0-9', e => e, $localize`Skip to a percentage of the video: 0 is 0% and 9 is 90%`),
new Hotkey('right', e => e, undefined, $localize`Seek the video forward`),
new Hotkey('left', e => e, undefined, $localize`Seek the video backward`),
new Hotkey('right', e => e, $localize`Seek the video forward`),
new Hotkey('left', e => e, $localize`Seek the video backward`),
new Hotkey('>', e => e, undefined, $localize`Increase playback rate`),
new Hotkey('<', e => e, undefined, $localize`Decrease playback rate`),
new Hotkey('>', e => e, $localize`Increase playback rate`),
new Hotkey('<', e => e, $localize`Decrease playback rate`),
new Hotkey(',', e => e, undefined, $localize`Navigate in the video to the previous frame`),
new Hotkey('.', e => e, undefined, $localize`Navigate in the video to the next frame`)
new Hotkey(',', e => e, $localize`Navigate in the video to the previous frame`),
new Hotkey('.', e => e, $localize`Navigate in the video to the next frame`)
])
}
@ -903,7 +904,7 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
else this.subscribeButton.subscribe()
return false
}, undefined, $localize`Subscribe to the account`)
}, $localize`Subscribe to the account`)
])
}

View File

@ -2,7 +2,7 @@
<a i18n class="visually-hidden-focusable skip-to-content" href="#content" (click)="$event.preventDefault(); mainContent.focus()">Skip to main content</a>
<my-hotkeys-cheatsheet (hotkeysModalStateChange)="onHotkeysModalStateChange($event)"></my-hotkeys-cheatsheet>
<my-hotkeys-cheat-sheet (hotkeysModalStateChange)="onHotkeysModalStateChange($event)"></my-hotkeys-cheat-sheet>
<div
class="peertube-container"

View File

@ -1,4 +1,3 @@
import { Hotkey, HotkeysService } from 'angular2-hotkeys'
import { delay, forkJoin } from 'rxjs'
import { filter, first, map } from 'rxjs/operators'
import { DOCUMENT, getLocaleDirection, PlatformLocation } from '@angular/common'
@ -15,7 +14,9 @@ import {
ServerService,
ThemeService,
User,
UserLocalStorageService
UserLocalStorageService,
Hotkey,
HotkeysService
} from '@app/core'
import { HooksService } from '@app/core/plugins/hooks.service'
import { PluginService } from '@app/core/plugins/plugin.service'
@ -313,40 +314,40 @@ export class AppComponent implements OnInit, AfterViewInit {
private initHotkeys () {
this.hotkeysService.add([
new Hotkey([ '/', 's' ], (event: KeyboardEvent): boolean => {
new Hotkey([ '/', 's' ], () => {
document.getElementById('search-video').focus()
return false
}, undefined, $localize`Focus the search bar`),
}, $localize`Focus the search bar`),
new Hotkey('b', (event: KeyboardEvent): boolean => {
new Hotkey('b', () => {
this.menu.toggleMenu()
return false
}, undefined, $localize`Toggle the left menu`),
}, $localize`Toggle the left menu`),
new Hotkey('g o', (event: KeyboardEvent): boolean => {
new Hotkey('g o', () => {
this.router.navigate([ '/videos/overview' ])
return false
}, undefined, $localize`Go to the discover videos page`),
}, $localize`Go to the discover videos page`),
new Hotkey('g t', (event: KeyboardEvent): boolean => {
new Hotkey('g t', () => {
this.router.navigate([ '/videos/trending' ])
return false
}, undefined, $localize`Go to the trending videos page`),
}, $localize`Go to the trending videos page`),
new Hotkey('g r', (event: KeyboardEvent): boolean => {
new Hotkey('g r', () => {
this.router.navigate([ '/videos/recently-added' ])
return false
}, undefined, $localize`Go to the recently added videos page`),
}, $localize`Go to the recently added videos page`),
new Hotkey('g l', (event: KeyboardEvent): boolean => {
new Hotkey('g l', () => {
this.router.navigate([ '/videos/local' ])
return false
}, undefined, $localize`Go to the local videos page`),
}, $localize`Go to the local videos page`),
new Hotkey('g u', (event: KeyboardEvent): boolean => {
new Hotkey('g u', () => {
this.router.navigate([ '/videos/upload' ])
return false
}, undefined, $localize`Go to the videos upload page`)
}, $localize`Go to the videos upload page`)
])
}

View File

@ -26,6 +26,7 @@ import { SharedGlobalIconModule } from './shared/shared-icons'
import { SharedInstanceModule } from './shared/shared-instance'
import { SharedMainModule } from './shared/shared-main'
import { SharedUserInterfaceSettingsModule } from './shared/shared-user-settings'
import { HotkeysCheatSheetComponent } from './hotkeys'
registerLocaleData(localeOc, 'oc')
@ -63,7 +64,9 @@ export function loadConfigFactory (server: ServerService, pluginService: PluginS
CustomModalComponent,
AdminWelcomeModalComponent,
InstanceConfigWarningModalComponent,
ConfirmComponent
ConfirmComponent,
HotkeysCheatSheetComponent
],
imports: [

View File

@ -1,4 +1,4 @@
import { Hotkey, HotkeysService } from 'angular2-hotkeys'
import { Hotkey, HotkeysService } from '@app/core'
import { Observable, ReplaySubject, Subject, throwError as observableThrowError } from 'rxjs'
import { catchError, map, mergeMap, share, tap } from 'rxjs/operators'
import { HttpClient, HttpErrorResponse, HttpHeaders, HttpParams } from '@angular/common/http'
@ -57,22 +57,22 @@ export class AuthService {
// Set HotKeys
this.hotkeys = [
new Hotkey('m s', (event: KeyboardEvent): boolean => {
new Hotkey('m s', e => {
this.router.navigate([ '/videos/subscriptions' ])
return false
}, undefined, $localize`Go to my subscriptions`),
new Hotkey('m v', (event: KeyboardEvent): boolean => {
}, $localize`Go to my subscriptions`),
new Hotkey('m v', e => {
this.router.navigate([ '/my-library/videos' ])
return false
}, undefined, $localize`Go to my videos`),
new Hotkey('m i', (event: KeyboardEvent): boolean => {
}, $localize`Go to my videos`),
new Hotkey('m i', e => {
this.router.navigate([ '/my-library/video-imports' ])
return false
}, undefined, $localize`Go to my imports`),
new Hotkey('m c', (event: KeyboardEvent): boolean => {
}, $localize`Go to my imports`),
new Hotkey('m c', e => {
this.router.navigate([ '/my-library/video-channels' ])
return false
}, undefined, $localize`Go to my channels`)
}, $localize`Go to my channels`)
]
}

View File

@ -1,4 +1,3 @@
import { HotkeyModule } from 'angular2-hotkeys'
import { MessageService } from 'primeng/api'
import { ToastModule } from 'primeng/toast'
import { CommonModule } from '@angular/common'
@ -8,7 +7,6 @@ import { PeerTubeSocket } from '@app/core/notification/peertube-socket.service'
import { HooksService, PluginService } from '@app/core/plugins'
import { AuthService } from './auth'
import { ConfirmService } from './confirm'
import { CheatSheetComponent } from './hotkeys'
import { MenuService } from './menu'
import { throwIfAlreadyLoaded } from './module-import-guard'
import { Notifier } from './notification'
@ -32,30 +30,23 @@ import { ServerService } from './server'
import { ThemeService } from './theme'
import { UserLocalStorageService, UserService } from './users'
import { LocalStorageService, ScreenService, SessionStorageService } from './wrappers'
import { HotkeysService } from './hotkeys'
@NgModule({
imports: [
CommonModule,
BrowserAnimationsModule,
ToastModule,
HotkeyModule.forRoot({
cheatSheetCloseEsc: true,
cheatSheetDescription: $localize`Show/hide this help menu`,
cheatSheetCloseEscDescription: $localize`Hide this help menu`
})
ToastModule
],
declarations: [
CheatSheetComponent,
HomepageRedirectComponent
],
exports: [
ToastModule,
CheatSheetComponent,
HomepageRedirectComponent
],
@ -97,7 +88,9 @@ import { LocalStorageService, ScreenService, SessionStorageService } from './wra
ScrollService,
MetaService,
MetaGuard
MetaGuard,
HotkeysService
]
})
export class CoreModule {

View File

@ -0,0 +1,59 @@
// Thanks to https://github.com/brtnshrdr/angular2-hotkeys
import { arrayify } from '@peertube/peertube-core-utils'
export class Hotkey {
private formattedHotkey: string[]
static symbolize (combo: string): string {
const map: any = {
command: '\u2318', // ⌘
shift: '\u21E7', // ⇧
left: '\u2190', // ←
right: '\u2192', // →
up: '\u2191', // ↑
down: '\u2193', // ↓
return: '\u23CE', // ⏎
backspace: '\u232B' // ⌫
}
const comboSplit: string[] = combo.split('+')
for (let i = 0; i < comboSplit.length; i++) {
// try to resolve command / ctrl based on OS:
if (comboSplit[i] === 'mod') {
if (window.navigator?.platform.includes('Mac')) {
comboSplit[i] = 'command'
} else {
comboSplit[i] = 'ctrl'
}
}
comboSplit[i] = map[comboSplit[i]] || comboSplit[i]
}
return comboSplit.join(' + ')
}
constructor (
public combo: string | string[],
public callback: (event: KeyboardEvent, combo: string) => any | boolean,
public description?: string | Function
) {
this.combo = arrayify(combo)
this.description = description || ''
}
get formatted (): string[] {
if (!this.formattedHotkey) {
const sequence: string[] = [ ...this.combo ]
for (let i = 0; i < sequence.length; i++) {
sequence[i] = Hotkey.symbolize(sequence[i])
}
this.formattedHotkey = sequence
}
return this.formattedHotkey
}
}

View File

@ -1,53 +0,0 @@
import { Hotkey, HotkeysService } from 'angular2-hotkeys'
import { Subscription } from 'rxjs'
import { Component, EventEmitter, Input, OnDestroy, OnInit, Output } from '@angular/core'
@Component({
selector: 'my-hotkeys-cheatsheet',
templateUrl: './hotkeys.component.html',
styleUrls: [ './hotkeys.component.scss' ]
})
export class CheatSheetComponent implements OnInit, OnDestroy {
@Input() title = $localize`Keyboard Shortcuts:`
@Output() hotkeysModalStateChange = new EventEmitter<boolean>()
helpVisible = false
subscription: Subscription
hotkeys: Hotkey[]
constructor (
private hotkeysService: HotkeysService
) {}
public ngOnInit (): void {
this.subscription = this.hotkeysService.cheatSheetToggle.subscribe((isOpen) => {
if (isOpen !== false) {
this.hotkeys = this.hotkeysService.hotkeys.filter(hotkey => hotkey.description)
}
if (isOpen === false) {
this.helpVisible = false
} else {
this.toggleHelpVisible()
}
this.hotkeysModalStateChange.emit(this.helpVisible)
})
}
public ngOnDestroy (): void {
if (this.subscription) {
this.subscription.unsubscribe()
}
}
public toggleCheatSheet (): void {
this.hotkeysService.cheatSheetToggle.next(!this.helpVisible)
}
public toggleHelpVisible (): void {
this.helpVisible = !this.helpVisible
}
}

View File

@ -0,0 +1,121 @@
// Thanks to https://github.com/brtnshrdr/angular2-hotkeys
import { Injectable } from '@angular/core'
import { Hotkey } from './hotkey.model'
import { Subject } from 'rxjs'
import { tinykeys } from 'tinykeys'
import debug from 'debug'
const debugLogger = debug('peertube:hotkeys')
@Injectable()
export class HotkeysService {
cheatSheetToggle = new Subject<boolean>()
private hotkeys: Hotkey[] = []
private preventIn = [ 'INPUT', 'SELECT', 'TEXTAREA' ]
private disabled = false
private removeTinyKeysStore = new Map<Hotkey, (() => void)[]>()
constructor () {
this.initCheatSheet()
}
private initCheatSheet () {
debugLogger('Init hotkeys')
this.add([
new Hotkey(
[ '?', 'Shift+?' ],
() => this.cheatSheetToggle.next(undefined),
$localize`Show / hide this help menu`
),
new Hotkey(
'escape',
() => this.cheatSheetToggle.next(false),
$localize`Hide this help menu`
)
])
}
add (hotkey: Hotkey): Hotkey
add (hotkey: Hotkey[]): Hotkey[]
add (hotkey: Hotkey | Hotkey[]): Hotkey[] | Hotkey {
if (Array.isArray(hotkey)) {
return hotkey.map(h => this.add(h))
}
this.remove(hotkey)
this.hotkeys.push(hotkey)
for (const combo of hotkey.combo) {
debugLogger('Adding hotkey ' + hotkey.formatted)
const removeTinyKey = tinykeys(window, {
[combo]: event => {
if (this.disabled) return
const target = event.target as Element
const nodeName: string = target.nodeName.toUpperCase()
if (this.preventIn.includes(nodeName)) {
return
}
const result = hotkey.callback.apply(this, [ event, combo ])
if (result === false) {
event.preventDefault()
event.stopPropagation()
}
}
})
if (!this.removeTinyKeysStore.has(hotkey)) {
this.removeTinyKeysStore.set(hotkey, [])
}
this.removeTinyKeysStore.get(hotkey).push(removeTinyKey)
}
return hotkey
}
remove (hotkey: Hotkey | Hotkey[]) {
if (Array.isArray(hotkey)) {
for (const h of hotkey) {
this.remove(h)
}
return
}
this.hotkeys = this.hotkeys.filter(h => h !== hotkey)
const removeHandlers = this.removeTinyKeysStore.get(hotkey)
if (removeHandlers) {
debugLogger('Removing hotkey ' + hotkey.formatted)
for (const removeHandler of removeHandlers) {
removeHandler()
}
}
this.removeTinyKeysStore.delete(hotkey)
}
getHotkeys () {
return this.hotkeys
}
disableHotkeys () {
this.disabled = true
}
enableHotkeys () {
this.disabled = false
}
}

View File

@ -1 +1,2 @@
export * from './hotkeys.component'
export * from './hotkey.model'
export * from './hotkeys.service'

View File

@ -2,6 +2,13 @@
<div class="cfp-hotkeys">
<h1 class="cfp-hotkeys-title">{{ title }}</h1>
<div class="d-flex justify-content-center m-3">
<my-peertube-checkbox
inputName="enable-hotkeys" [(ngModel)]="hotkeysEnabled" (ngModelChange)="onHotkeysEnabledChange()"
i18n-labelText labelText="Enable hotkeys in this web browser"
></my-peertube-checkbox>
</div>
<ul role="presentation">
<li *ngFor="let hotkey of hotkeys">
<div class="cfp-hotkeys-keys">

View File

@ -0,0 +1,74 @@
import { Subscription } from 'rxjs'
import { Component, EventEmitter, Input, OnDestroy, OnInit, Output } from '@angular/core'
import { LocalStorageService, HotkeysService, Hotkey } from '@app/core'
@Component({
selector: 'my-hotkeys-cheat-sheet',
templateUrl: './hotkeys-cheat-sheet.component.html',
styleUrls: [ './hotkeys-cheat-sheet.component.scss' ]
})
export class HotkeysCheatSheetComponent implements OnInit, OnDestroy {
@Input() title = $localize`Keyboard Shortcuts`
@Output() hotkeysModalStateChange = new EventEmitter<boolean>()
hotkeysEnabled = true
helpVisible = false
subscription: Subscription
hotkeys: Hotkey[]
private readonly localStorageHotkeysDisabledKey = 'peertube-hotkeys-disabled'
constructor (
private hotkeysService: HotkeysService,
private localStorage: LocalStorageService
) {}
ngOnInit () {
if (this.localStorage.getItem(this.localStorageHotkeysDisabledKey) === 'true') {
this.hotkeysEnabled = false
this.hotkeysService.disableHotkeys()
}
this.subscription = this.hotkeysService.cheatSheetToggle.subscribe(isOpen => {
if (isOpen !== false) {
this.hotkeys = this.hotkeysService.getHotkeys().filter(hotkey => hotkey.description)
}
if (isOpen === false) {
this.helpVisible = false
} else {
this.toggleHelpVisible()
}
this.hotkeysModalStateChange.emit(this.helpVisible)
})
}
ngOnDestroy () {
if (this.subscription) {
this.subscription.unsubscribe()
}
}
toggleCheatSheet () {
this.hotkeysService.cheatSheetToggle.next(!this.helpVisible)
}
toggleHelpVisible () {
this.helpVisible = !this.helpVisible
}
onHotkeysEnabledChange () {
if (!this.hotkeysEnabled) {
this.localStorage.setItem(this.localStorageHotkeysDisabledKey, 'true')
this.hotkeysService.disableHotkeys()
return
}
this.hotkeysService.enableHotkeys()
this.localStorage.removeItem(this.localStorageHotkeysDisabledKey)
}
}

View File

@ -0,0 +1 @@
export * from './hotkeys-cheat-sheet.component'

View File

@ -1,4 +1,3 @@
import { HotkeysService } from 'angular2-hotkeys'
import * as debug from 'debug'
import { forkJoin, Subscription } from 'rxjs'
import { first, switchMap } from 'rxjs/operators'
@ -10,6 +9,7 @@ import {
AuthStatus,
AuthUser,
HooksService,
HotkeysService,
MenuSection,
MenuService,
RedirectService,

View File

@ -2460,11 +2460,6 @@
resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-10.0.1.tgz#2f4f65bb08bc368ac39c96da7b2f09140b26851b"
integrity sha512-/fvYntiO1GeICvqbQ3doGDIP97vWmvFt83GKguJ6prmQM2iXZfFcq6YE8KteFyRtX2/h5Hf91BYvPodJKFYv5Q==
"@types/mousetrap@^1.6.9":
version "1.6.11"
resolved "https://registry.yarnpkg.com/@types/mousetrap/-/mousetrap-1.6.11.tgz#ef9620160fdcefcb85bccda8aaa3e84d7429376d"
integrity sha512-F0oAily9Q9QQpv9JKxKn0zMKfOo36KHCW7myYsmUyf2t0g+sBTbG3UleTPoguHdE1z3GLFr3p7/wiOio52QFjQ==
"@types/ms@*":
version "0.7.31"
resolved "https://registry.yarnpkg.com/@types/ms/-/ms-0.7.31.tgz#31b7ca6407128a3d2bbc27fe2d21b345397f6197"
@ -3293,15 +3288,6 @@ ajv@^6.10.0, ajv@^6.12.4, ajv@^6.12.5:
json-schema-traverse "^0.4.1"
uri-js "^4.2.2"
angular2-hotkeys@^13.1.0:
version "13.4.0"
resolved "https://registry.yarnpkg.com/angular2-hotkeys/-/angular2-hotkeys-13.4.0.tgz#a96676466936556655cd64f92e1f5cd3aeac8e10"
integrity sha512-WvkouvdXtTYw3tpuaoEVF+ue41pvI2XSa8m4tVRPLzAblT/f7PG0uQO4npyjVw3oDIc7qnFkQR+oqGl1KM1eow==
dependencies:
"@types/mousetrap" "^1.6.9"
mousetrap "^1.6.5"
tslib "^2.3.1"
angularx-qrcode@16.0.0:
version "16.0.0"
resolved "https://registry.yarnpkg.com/angularx-qrcode/-/angularx-qrcode-16.0.0.tgz#c637924b8d8f9cc344216caa80adf3810ccd5e5b"
@ -8186,11 +8172,6 @@ moment@^2.10.2:
resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.4.tgz#3dbe052889fe7c1b2ed966fcb3a77328964ef108"
integrity sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==
mousetrap@^1.6.5:
version "1.6.5"
resolved "https://registry.yarnpkg.com/mousetrap/-/mousetrap-1.6.5.tgz#8a766d8c272b08393d5f56074e0b5ec183485bf9"
integrity sha512-QNo4kEepaIBwiT8CDhP98umTetp+JNfQYBWvC1pc6/OAibuXtRcxZ58Qz8skvEHYvURne/7R8T5VoOI7rDsEUA==
mpd-parser@0.22.1, mpd-parser@^0.22.1:
version "0.22.1"
resolved "https://registry.yarnpkg.com/mpd-parser/-/mpd-parser-0.22.1.tgz#bc2bf7d3e56368e4b0121035b055675401871521"
@ -10789,6 +10770,11 @@ thunky@^1.0.2:
resolved "https://registry.yarnpkg.com/thunky/-/thunky-1.1.0.tgz#5abaf714a9405db0504732bbccd2cedd9ef9537d"
integrity sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==
tinykeys@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/tinykeys/-/tinykeys-2.1.0.tgz#1341563e92a7fac9ca90053fddaf2b7553500298"
integrity sha512-/MESnqBD1xItZJn5oGQ4OsNORQgJfPP96XSGoyu4eLpwpL0ifO0SYR5OD76u0YMhMXsqkb0UqvI9+yXTh4xv8Q==
tmp@0.2.1, tmp@~0.2.1:
version "0.2.1"
resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.2.1.tgz#8457fc3037dcf4719c251367a1af6500ee1ccf14"