🚀 Socket Launch Week Day 5:Introducing Repository Access Permissions and Custom Roles.Learn more
Sign In

@taylorvance/tv-shared-runtime

Package Overview
Dependencies
Maintainers
1
Versions
6
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@taylorvance/tv-shared-runtime - npm Package Compare versions

Comparing version
0.5.0
to
0.6.0
+13
-6
dist/hotkeys.d.ts
import { type HotkeyCallback, type Keys, type Options } from 'react-hotkeys-hook';
import type { DependencyList, MutableRefObject } from 'react';
import type { DependencyList, RefObject } from 'react';
export type FormTags = 'input' | 'textarea' | 'select' | 'INPUT' | 'TEXTAREA' | 'SELECT';

@@ -8,3 +8,3 @@ export interface HotkeyBinding {

}
export interface KonamiOptions {
export interface KeySequenceOptions {
document?: Document;

@@ -17,7 +17,14 @@ enableOnContentEditable?: boolean;

}
export interface KeySequenceBinding {
callback: (event: KeyboardEvent) => void;
sequence: readonly string[];
}
export type KonamiOptions = KeySequenceOptions;
declare const KONAMI_CODE_SEQUENCE: readonly ["up", "up", "down", "down", "left", "right", "left", "right", "b", "a"];
export { KONAMI_CODE_SEQUENCE };
export declare function useHotkeys<T extends HTMLElement>(keys: Keys, callback: HotkeyCallback, options?: Options, deps?: DependencyList): MutableRefObject<T | null>;
export declare function useHotkeys<T extends HTMLElement>(bindings: readonly HotkeyBinding[], options?: Options, deps?: DependencyList): MutableRefObject<T | null>;
export declare function useKonami<T extends HTMLElement>(callback: (event: KeyboardEvent) => void, options?: KonamiOptions, deps?: DependencyList): MutableRefObject<T | null>;
export { KONAMI_CODE_SEQUENCE, };
export declare function useHotkeys<T extends HTMLElement>(keys: Keys, callback: HotkeyCallback, options?: Options, deps?: DependencyList): RefObject<T | null>;
export declare function useHotkeys<T extends HTMLElement>(bindings: readonly HotkeyBinding[], options?: Options, deps?: DependencyList): RefObject<T | null>;
export declare function useKonami<T extends HTMLElement>(callback: (event: KeyboardEvent) => void, options?: KonamiOptions, deps?: DependencyList): RefObject<T | null>;
export declare function useKeySequence<T extends HTMLElement>(sequence: readonly string[], callback: (event: KeyboardEvent) => void, options?: KeySequenceOptions, deps?: DependencyList): RefObject<T | null>;
export declare function useKeySequence<T extends HTMLElement>(bindings: readonly KeySequenceBinding[], options?: KeySequenceOptions, deps?: DependencyList): RefObject<T | null>;
//# sourceMappingURL=hotkeys.d.ts.map

@@ -1,1 +0,1 @@

{"version":3,"file":"hotkeys.d.ts","sourceRoot":"","sources":["../src/hotkeys.ts"],"names":[],"mappings":"AACA,OAAO,EAEL,KAAK,cAAc,EACnB,KAAK,IAAI,EACT,KAAK,OAAO,EACb,MAAM,oBAAoB,CAAC;AAC5B,OAAO,KAAK,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,OAAO,CAAC;AAE9D,MAAM,MAAM,QAAQ,GAAG,OAAO,GAAG,UAAU,GAAG,QAAQ,GAAG,OAAO,GAAG,UAAU,GAAG,QAAQ,CAAC;AAEzF,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,cAAc,CAAC;IACzB,IAAI,EAAE,IAAI,CAAC;CACZ;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,uBAAuB,CAAC,EAAE,OAAO,CAAC;IAClC,gBAAgB,CAAC,EAAE,SAAS,QAAQ,EAAE,GAAG,OAAO,CAAC;IACjD,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAgCD,QAAA,MAAM,oBAAoB,mFAWhB,CAAC;AAyHX,OAAO,EAAE,oBAAoB,EAAE,CAAC;AAEhC,wBAAgB,UAAU,CAAC,CAAC,SAAS,WAAW,EAC9C,IAAI,EAAE,IAAI,EACV,QAAQ,EAAE,cAAc,EACxB,OAAO,CAAC,EAAE,OAAO,EACjB,IAAI,CAAC,EAAE,cAAc,GACpB,gBAAgB,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;AAC9B,wBAAgB,UAAU,CAAC,CAAC,SAAS,WAAW,EAC9C,QAAQ,EAAE,SAAS,aAAa,EAAE,EAClC,OAAO,CAAC,EAAE,OAAO,EACjB,IAAI,CAAC,EAAE,cAAc,GACpB,gBAAgB,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;AAyD9B,wBAAgB,SAAS,CAAC,CAAC,SAAS,WAAW,EAC7C,QAAQ,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,EACxC,OAAO,CAAC,EAAE,aAAa,EACvB,IAAI,CAAC,EAAE,cAAc,GACpB,gBAAgB,CAAC,CAAC,GAAG,IAAI,CAAC,CAwF5B"}
{"version":3,"file":"hotkeys.d.ts","sourceRoot":"","sources":["../src/hotkeys.ts"],"names":[],"mappings":"AACA,OAAO,EAEL,KAAK,cAAc,EACnB,KAAK,IAAI,EACT,KAAK,OAAO,EACb,MAAM,oBAAoB,CAAC;AAC5B,OAAO,KAAK,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvD,MAAM,MAAM,QAAQ,GAAG,OAAO,GAAG,UAAU,GAAG,QAAQ,GAAG,OAAO,GAAG,UAAU,GAAG,QAAQ,CAAC;AAEzF,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,cAAc,CAAC;IACzB,IAAI,EAAE,IAAI,CAAC;CACZ;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,uBAAuB,CAAC,EAAE,OAAO,CAAC;IAClC,gBAAgB,CAAC,EAAE,SAAS,QAAQ,EAAE,GAAG,OAAO,CAAC;IACjD,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAC;IACzC,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;CAC7B;AAED,MAAM,MAAM,aAAa,GAAG,kBAAkB,CAAC;AAgC/C,QAAA,MAAM,oBAAoB,mFAWhB,CAAC;AAgIX,OAAO,EACL,oBAAoB,GACrB,CAAC;AAEF,wBAAgB,UAAU,CAAC,CAAC,SAAS,WAAW,EAC9C,IAAI,EAAE,IAAI,EACV,QAAQ,EAAE,cAAc,EACxB,OAAO,CAAC,EAAE,OAAO,EACjB,IAAI,CAAC,EAAE,cAAc,GACpB,SAAS,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;AACvB,wBAAgB,UAAU,CAAC,CAAC,SAAS,WAAW,EAC9C,QAAQ,EAAE,SAAS,aAAa,EAAE,EAClC,OAAO,CAAC,EAAE,OAAO,EACjB,IAAI,CAAC,EAAE,cAAc,GACpB,SAAS,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;AAyDvB,wBAAgB,SAAS,CAAC,CAAC,SAAS,WAAW,EAC7C,QAAQ,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,EACxC,OAAO,CAAC,EAAE,aAAa,EACvB,IAAI,CAAC,EAAE,cAAc,GACpB,SAAS,CAAC,CAAC,GAAG,IAAI,CAAC,CAErB;AAED,wBAAgB,cAAc,CAAC,CAAC,SAAS,WAAW,EAClD,QAAQ,EAAE,SAAS,MAAM,EAAE,EAC3B,QAAQ,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,EACxC,OAAO,CAAC,EAAE,kBAAkB,EAC5B,IAAI,CAAC,EAAE,cAAc,GACpB,SAAS,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;AACvB,wBAAgB,cAAc,CAAC,CAAC,SAAS,WAAW,EAClD,QAAQ,EAAE,SAAS,kBAAkB,EAAE,EACvC,OAAO,CAAC,EAAE,kBAAkB,EAC5B,IAAI,CAAC,EAAE,cAAc,GACpB,SAAS,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC"}

@@ -5,3 +5,3 @@ import { useCallback, useEffect, useRef } from 'react';

const DEFAULT_SPLIT_KEY = ',';
const KONAMI_TIMEOUT_MS = 1000;
const KEY_SEQUENCE_TIMEOUT_MS = 1000;
const hotkeyKeyAliases = {

@@ -47,2 +47,4 @@ ' ': 'space',

&& value.every((item) => typeof item === 'object' && item !== null && 'callback' in item && 'keys' in item));
const isKeySequenceBindingArray = (value) => (Array.isArray(value)
&& value.every((item) => typeof item === 'object' && item !== null && 'callback' in item && 'sequence' in item));
const isDependencyList = (value) => (Array.isArray(value));

@@ -110,3 +112,3 @@ const normalizeKey = (key) => {

};
export { KONAMI_CODE_SEQUENCE };
export { KONAMI_CODE_SEQUENCE, };
export function useHotkeys(keysOrBindings, callbackOrOptions, maybeOptions, maybeDeps) {

@@ -146,9 +148,35 @@ if (!isBindingArray(keysOrBindings)) {

export function useKonami(callback, options, deps) {
return useKeySequence(KONAMI_CODE_SEQUENCE, callback, options, deps);
}
export function useKeySequence(sequenceOrBindings, callbackOrOptions, maybeOptions, maybeDeps) {
const scopeRef = useRef(null);
const callbackRef = useRef(callback);
const progressRef = useRef(0);
const lastKeyTimestampRef = useRef(0);
const memoizedCallback = useCallback(callback, deps ?? []);
const timeoutMs = options?.timeoutMs ?? KONAMI_TIMEOUT_MS;
callbackRef.current = deps ? memoizedCallback : callback;
const bindingsRef = useRef([]);
const stateRef = useRef([]);
const deps = (typeof callbackOrOptions === 'function'
? maybeDeps
: isDependencyList(maybeOptions)
? maybeOptions
: maybeDeps);
const options = (typeof callbackOrOptions === 'function'
? isDependencyList(maybeOptions)
? undefined
: maybeOptions
: isDependencyList(callbackOrOptions)
? undefined
: callbackOrOptions);
const timeoutMs = options?.timeoutMs ?? KEY_SEQUENCE_TIMEOUT_MS;
const memoizedCallback = useCallback(typeof callbackOrOptions === 'function' ? callbackOrOptions : () => { }, deps ?? []);
bindingsRef.current = isKeySequenceBindingArray(sequenceOrBindings)
? sequenceOrBindings.map((binding) => ({
callback: binding.callback,
sequence: binding.sequence.map((key) => normalizeKey(key)),
}))
: [{
callback: deps ? memoizedCallback : callbackOrOptions,
sequence: sequenceOrBindings.map((key) => normalizeKey(key)),
}];
stateRef.current = bindingsRef.current.map((_, index) => ({
lastKeyTimestamp: stateRef.current[index]?.lastKeyTimestamp ?? null,
progress: stateRef.current[index]?.progress ?? 0,
}));
useEffect(() => {

@@ -173,27 +201,42 @@ if (options?.enabled === false) {

const normalizedKey = normalizeKey(event.key);
const expectedKey = KONAMI_CODE_SEQUENCE[progressRef.current];
if (!expectedKey) {
progressRef.current = 0;
lastKeyTimestampRef.current = 0;
return;
}
if (lastKeyTimestampRef.current > 0
&& Date.now() - lastKeyTimestampRef.current > timeoutMs) {
progressRef.current = 0;
}
if (normalizedKey === expectedKey) {
progressRef.current += 1;
lastKeyTimestampRef.current = Date.now();
if (progressRef.current === KONAMI_CODE_SEQUENCE.length) {
if (options?.preventDefault) {
event.preventDefault();
const now = Date.now();
const matchedBindings = bindingsRef.current.filter((binding, index) => {
const bindingState = stateRef.current[index] ?? {
lastKeyTimestamp: null,
progress: 0,
};
stateRef.current[index] = bindingState;
const expectedKey = binding.sequence[bindingState.progress];
if (!expectedKey) {
bindingState.progress = 0;
bindingState.lastKeyTimestamp = null;
return false;
}
if (bindingState.lastKeyTimestamp !== null
&& now - bindingState.lastKeyTimestamp > timeoutMs) {
bindingState.progress = 0;
}
if (normalizedKey === binding.sequence[bindingState.progress]) {
bindingState.progress += 1;
bindingState.lastKeyTimestamp = now;
if (bindingState.progress === binding.sequence.length) {
bindingState.progress = 0;
bindingState.lastKeyTimestamp = null;
return true;
}
callbackRef.current(event);
progressRef.current = 0;
lastKeyTimestampRef.current = 0;
return false;
}
bindingState.progress = normalizedKey === binding.sequence[0] ? 1 : 0;
bindingState.lastKeyTimestamp = bindingState.progress > 0 ? now : null;
return false;
});
if (matchedBindings.length === 0) {
return;
}
progressRef.current = normalizedKey === KONAMI_CODE_SEQUENCE[0] ? 1 : 0;
lastKeyTimestampRef.current = progressRef.current > 0 ? Date.now() : 0;
if (options?.preventDefault) {
event.preventDefault();
}
for (const binding of matchedBindings) {
binding.callback(event);
}
};

@@ -200,0 +243,0 @@ hotkeyDocument.addEventListener('keydown', handleKeyDown);

@@ -5,3 +5,3 @@ export { BrandBadge, brandBadgeClassNames, type BrandBadgeProps, } from './BrandBadge.js';

export { createProjectStorage, type ProjectStorageEntry, type ProjectStorage, type ProjectStorageOptions, type StorageKeyPart, type StorageLike, } from './storage.js';
export { KONAMI_CODE_SEQUENCE, useHotkeys, useKonami, type HotkeyBinding, type KonamiOptions, } from './hotkeys.js';
export { KONAMI_CODE_SEQUENCE, useHotkeys, useKonami, type HotkeyBinding, type KonamiOptions, type KeySequenceBinding, type KeySequenceOptions, useKeySequence, } from './hotkeys.js';
//# sourceMappingURL=index.d.ts.map

@@ -1,1 +0,1 @@

{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,UAAU,EACV,oBAAoB,EACpB,KAAK,eAAe,GACrB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,wBAAwB,EACxB,mBAAmB,EACnB,cAAc,GACf,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,cAAc,EACd,KAAK,mBAAmB,GACzB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,oBAAoB,EACpB,KAAK,mBAAmB,EACxB,KAAK,cAAc,EACnB,KAAK,qBAAqB,EAC1B,KAAK,cAAc,EACnB,KAAK,WAAW,GACjB,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,oBAAoB,EACpB,UAAU,EACV,SAAS,EACT,KAAK,aAAa,EAClB,KAAK,aAAa,GACnB,MAAM,cAAc,CAAC"}
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,UAAU,EACV,oBAAoB,EACpB,KAAK,eAAe,GACrB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,wBAAwB,EACxB,mBAAmB,EACnB,cAAc,GACf,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,cAAc,EACd,KAAK,mBAAmB,GACzB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,oBAAoB,EACpB,KAAK,mBAAmB,EACxB,KAAK,cAAc,EACnB,KAAK,qBAAqB,EAC1B,KAAK,cAAc,EACnB,KAAK,WAAW,GACjB,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,oBAAoB,EACpB,UAAU,EACV,SAAS,EACT,KAAK,aAAa,EAClB,KAAK,aAAa,EAClB,KAAK,kBAAkB,EACvB,KAAK,kBAAkB,EACvB,cAAc,GACf,MAAM,cAAc,CAAC"}

@@ -5,2 +5,2 @@ export { BrandBadge, brandBadgeClassNames, } from './BrandBadge.js';

export { createProjectStorage, } from './storage.js';
export { KONAMI_CODE_SEQUENCE, useHotkeys, useKonami, } from './hotkeys.js';
export { KONAMI_CODE_SEQUENCE, useHotkeys, useKonami, useKeySequence, } from './hotkeys.js';
{
"name": "@taylorvance/tv-shared-runtime",
"version": "0.5.0",
"version": "0.6.0",
"description": "Shared React runtime primitives for Taylor Vance portfolio projects.",

@@ -5,0 +5,0 @@ "type": "module",

@@ -19,2 +19,3 @@ # `@taylorvance/tv-shared-runtime`

- `useHotkeys`
- `useKeySequence`
- `useKonami`

@@ -165,2 +166,19 @@ - `KONAMI_CODE_SEQUENCE`

For hidden multi-key sequences, `useKeySequence` supports either one sequence or multiple bindings with shared timeout and scope rules:
```tsx
import { useKeySequence } from '@taylorvance/tv-shared-runtime';
export function DebugPanel() {
useKeySequence([
{ sequence: ['d', 'e', 'b', 'u', 'g'], callback: () => setDebugMode(true) },
{ sequence: ['r', 'g', 'b'], callback: () => setRainbowMode(true) },
], { timeoutMs: 1_500 });
return null;
}
```
`timeoutMs` defaults to `1000` and applies between each correct key press, not across the whole sequence.
For an easy easter-egg path, `useKonami` exposes a built-in Konami listener with the same optional scoping model:

@@ -167,0 +185,0 @@