Automate AUR publish in tagged release workflow (#22)

This commit is contained in:
2026-03-14 19:49:46 -07:00
committed by GitHub
parent 99f4d2baaf
commit 9eed37420e
36 changed files with 641 additions and 722 deletions

View File

@@ -25,20 +25,17 @@ test('controller status indicator shows once when a controller is first detected
classList,
};
const indicator = createControllerStatusIndicator(
{ controllerStatusToast: toast } as never,
{
durationMs: 1500,
setTimeout: (callback: () => void) => {
const id = nextTimerId++;
scheduled.set(id, callback);
return id as never;
},
clearTimeout: (id) => {
scheduled.delete(id as never as number);
},
const indicator = createControllerStatusIndicator({ controllerStatusToast: toast } as never, {
durationMs: 1500,
setTimeout: (callback: () => void) => {
const id = nextTimerId++;
scheduled.set(id, callback);
return id as never;
},
);
clearTimeout: (id) => {
scheduled.delete(id as never as number);
},
});
indicator.update({
connectedGamepads: [],
@@ -78,13 +75,10 @@ test('controller status indicator announces newly detected controllers after sta
classList: createClassList(['hidden']),
};
const indicator = createControllerStatusIndicator(
{ controllerStatusToast: toast } as never,
{
setTimeout: () => 1 as never,
clearTimeout: () => {},
},
);
const indicator = createControllerStatusIndicator({ controllerStatusToast: toast } as never, {
setTimeout: () => 1 as never,
clearTimeout: () => {},
});
indicator.update({
connectedGamepads: [{ id: 'pad-1', index: 0, mapping: 'standard', connected: true }],

View File

@@ -58,7 +58,9 @@ export function createControllerStatusIndicator(
(device) => device.id === snapshot.activeGamepadId,
);
const announcedDevice =
newDevices.find((device) => device.id === snapshot.activeGamepadId) ?? newDevices[0] ?? activeDevice;
newDevices.find((device) => device.id === snapshot.activeGamepadId) ??
newDevices[0] ??
activeDevice;
show(`Controller detected: ${getDeviceLabel(announcedDevice)}`);
}

View File

@@ -39,8 +39,11 @@ function createControllerConfig(
buttonIndices?: Partial<ResolvedControllerConfig['buttonIndices']>;
} = {},
): ResolvedControllerConfig {
const { bindings: bindingOverrides, buttonIndices: buttonIndexOverrides, ...restOverrides } =
overrides;
const {
bindings: bindingOverrides,
buttonIndices: buttonIndexOverrides,
...restOverrides
} = overrides;
return {
enabled: true,
preferredGamepadId: '',
@@ -90,7 +93,11 @@ function createControllerConfig(
test('gamepad controller selects the first connected controller by default', () => {
const updates: string[] = [];
const controller = createGamepadController({
getGamepads: () => [null, createGamepad('pad-2', { index: 1 }), createGamepad('pad-3', { index: 2 })],
getGamepads: () => [
null,
createGamepad('pad-2', { index: 1 }),
createGamepad('pad-3', { index: 2 }),
],
getConfig: () => createControllerConfig(),
getKeyboardModeEnabled: () => false,
getLookupWindowOpen: () => false,
@@ -310,13 +317,12 @@ test('gamepad controller maps L1 play-current, R1 next-audio, and popup navigati
buttons[7] = { value: 0.9, pressed: true, touched: true };
const controller = createGamepadController({
getGamepads: () =>
[
createGamepad('pad-1', {
axes: [0, -0.75, 0.1, 0, 0.8],
buttons,
}),
],
getGamepads: () => [
createGamepad('pad-1', {
axes: [0, -0.75, 0.1, 0, 0.8],
buttons,
}),
],
getConfig: () =>
createControllerConfig({
bindings: {
@@ -352,7 +358,10 @@ test('gamepad controller maps L1 play-current, R1 next-audio, and popup navigati
assert.equal(calls.includes('prev-audio'), false);
assert.equal(calls.includes('toggle-mpv-pause'), true);
assert.equal(calls.includes('quit-mpv'), true);
assert.deepEqual(scrollCalls.map((value) => Math.round(value)), [-67]);
assert.deepEqual(
scrollCalls.map((value) => Math.round(value)),
[-67],
);
assert.equal(calls.includes('jump:160'), true);
});
@@ -492,7 +501,10 @@ test('gamepad controller maps d-pad left/right to selection and d-pad up/down to
controller.poll(100);
assert.deepEqual(selectionCalls, [1]);
assert.deepEqual(scrollCalls.map((value) => Math.round(value)), [-90]);
assert.deepEqual(
scrollCalls.map((value) => Math.round(value)),
[-90],
);
});
test('gamepad controller maps d-pad axes 6 and 7 to selection and popup scroll', () => {
@@ -524,7 +536,10 @@ test('gamepad controller maps d-pad axes 6 and 7 to selection and popup scroll',
controller.poll(100);
assert.deepEqual(selectionCalls, [1]);
assert.deepEqual(scrollCalls.map((value) => Math.round(value)), [-90]);
assert.deepEqual(
scrollCalls.map((value) => Math.round(value)),
[-90],
);
});
test('gamepad controller trigger analog mode uses trigger values above threshold', () => {

View File

@@ -159,10 +159,7 @@ function resolveDpadValue(
);
}
function resolveDpadAxisValue(
gamepad: GamepadLike,
axisIndex: number,
): number {
function resolveDpadAxisValue(gamepad: GamepadLike, axisIndex: number): number {
const value = resolveGamepadAxis(gamepad, axisIndex);
if (Math.abs(value) < 0.5) {
return 0;
@@ -175,7 +172,12 @@ function resolveDpadHorizontalValue(gamepad: GamepadLike, triggerDeadzone: numbe
if (axisValue !== 0) {
return axisValue;
}
return resolveDpadValue(gamepad, DPAD_BUTTON_INDEX.left, DPAD_BUTTON_INDEX.right, triggerDeadzone);
return resolveDpadValue(
gamepad,
DPAD_BUTTON_INDEX.left,
DPAD_BUTTON_INDEX.right,
triggerDeadzone,
);
}
function resolveDpadVerticalValue(gamepad: GamepadLike, triggerDeadzone: number): number {
@@ -201,7 +203,12 @@ function createHoldState(): HoldState {
};
}
function shouldFireHeldAction(state: HoldState, now: number, repeatDelayMs: number, repeatIntervalMs: number): boolean {
function shouldFireHeldAction(
state: HoldState,
now: number,
repeatDelayMs: number,
repeatIntervalMs: number,
): boolean {
if (!state.initialFired) {
state.initialFired = true;
state.lastFireAt = now;
@@ -305,11 +312,7 @@ export function createGamepadController(options: GamepadControllerOptions) {
}
}
function handleSelectionAxis(
value: number,
now: number,
config: ResolvedControllerConfig,
): void {
function handleSelectionAxis(value: number, now: number, config: ResolvedControllerConfig): void {
const activationThreshold = Math.max(config.stickDeadzone, 0.55);
if (Math.abs(value) < activationThreshold) {
resetHeldAction(selectionHold);
@@ -327,11 +330,7 @@ export function createGamepadController(options: GamepadControllerOptions) {
}
}
function handleJumpAxis(
value: number,
now: number,
config: ResolvedControllerConfig,
): void {
function handleJumpAxis(value: number, now: number, config: ResolvedControllerConfig): void {
const activationThreshold = Math.max(config.stickDeadzone, 0.55);
if (Math.abs(value) < activationThreshold) {
resetHeldAction(jumpHold);
@@ -418,9 +417,7 @@ export function createGamepadController(options: GamepadControllerOptions) {
}
const interactionAllowed =
config.enabled &&
options.getKeyboardModeEnabled() &&
!options.getInteractionBlocked();
config.enabled && options.getKeyboardModeEnabled() && !options.getInteractionBlocked();
if (config.enabled) {
handleButtonEdge(
config.bindings.toggleKeyboardOnlyMode,

View File

@@ -3,10 +3,7 @@ import test from 'node:test';
import { createKeyboardHandlers } from './keyboard.js';
import { createRendererState } from '../state.js';
import {
YOMITAN_POPUP_COMMAND_EVENT,
YOMITAN_POPUP_HIDDEN_EVENT,
} from '../yomitan-popup.js';
import { YOMITAN_POPUP_COMMAND_EVENT, YOMITAN_POPUP_HIDDEN_EVENT } from '../yomitan-popup.js';
type CommandEventDetail = {
type?: string;
@@ -478,14 +475,11 @@ test('keyboard mode: controller helpers dispatch popup audio play/cycle and scro
assert.equal(handlers.cyclePopupAudioSourceForController(1), true);
assert.equal(handlers.scrollPopupByController(48, -24), true);
assert.deepEqual(
testGlobals.commandEvents.slice(-3),
[
{ type: 'playCurrentAudio' },
{ type: 'cycleAudioSource', direction: 1 },
{ type: 'scrollBy', deltaX: 48, deltaY: -24 },
],
);
assert.deepEqual(testGlobals.commandEvents.slice(-3), [
{ type: 'playCurrentAudio' },
{ type: 'cycleAudioSource', direction: 1 },
{ type: 'scrollBy', deltaX: 48, deltaY: -24 },
]);
} finally {
testGlobals.restore();
}
@@ -531,7 +525,8 @@ test('keyboard mode: Alt+Shift+C opens controller debug modal even while popup i
});
test('keyboard mode: controller select modal handles arrow keys before yomitan popup', async () => {
const { ctx, testGlobals, handlers, controllerSelectKeydownCount } = createKeyboardHandlerHarness();
const { ctx, testGlobals, handlers, controllerSelectKeydownCount } =
createKeyboardHandlerHarness();
try {
await handlers.setupMpvInputForwarding();

View File

@@ -187,7 +187,9 @@ export function createKeyboardHandlers(
);
}
function clearKeyboardSelectedWordClasses(wordNodes: HTMLElement[] = getSubtitleWordNodes()): void {
function clearKeyboardSelectedWordClasses(
wordNodes: HTMLElement[] = getSubtitleWordNodes(),
): void {
for (const wordNode of wordNodes) {
wordNode.classList.remove(KEYBOARD_SELECTED_WORD_CLASS);
}

View File

@@ -106,7 +106,10 @@ test('controller debug modal renders active controller axes, buttons, and config
assert.match(ctx.dom.controllerDebugStatus.textContent, /pad-1/);
assert.match(ctx.dom.controllerDebugSummary.textContent, /standard/);
assert.match(ctx.dom.controllerDebugAxes.textContent, /axis\[0\] = 0\.500/);
assert.match(ctx.dom.controllerDebugButtons.textContent, /button\[0\] value=1\.000 pressed=true/);
assert.match(
ctx.dom.controllerDebugButtons.textContent,
/button\[0\] value=1\.000 pressed=true/,
);
assert.match(ctx.dom.controllerDebugButtonIndices.textContent, /"buttonIndices": \{/);
assert.match(ctx.dom.controllerDebugButtonIndices.textContent, /"select": 6/);
assert.match(ctx.dom.controllerDebugButtonIndices.textContent, /"leftStickPress": 9/);
@@ -224,8 +227,14 @@ test('controller debug modal copies buttonIndices config to clipboard', async ()
await new Promise((resolve) => setTimeout(resolve, 0));
assert.deepEqual(copied, [ctx.dom.controllerDebugButtonIndices.textContent]);
assert.match(ctx.dom.controllerDebugStatus.textContent, /Copied controller buttonIndices config/);
assert.match(ctx.dom.controllerDebugToast.textContent, /Copied controller buttonIndices config/);
assert.match(
ctx.dom.controllerDebugStatus.textContent,
/Copied controller buttonIndices config/,
);
assert.match(
ctx.dom.controllerDebugToast.textContent,
/Copied controller buttonIndices config/,
);
assert.equal(ctx.dom.controllerDebugToast.classList.contains('hidden'), false);
} finally {
Object.defineProperty(globalThis, 'window', { configurable: true, value: previousWindow });

View File

@@ -18,21 +18,19 @@ function formatButtons(
}
function formatButtonIndices(
value:
| {
select: number;
buttonSouth: number;
buttonEast: number;
buttonNorth: number;
buttonWest: number;
leftShoulder: number;
rightShoulder: number;
leftStickPress: number;
rightStickPress: number;
leftTrigger: number;
rightTrigger: number;
}
| null,
value: {
select: number;
buttonSouth: number;
buttonEast: number;
buttonNorth: number;
buttonWest: number;
leftShoulder: number;
rightShoulder: number;
leftStickPress: number;
rightStickPress: number;
leftTrigger: number;
rightTrigger: number;
} | null,
): string {
if (!value) {
return 'No controller config loaded.';
@@ -97,7 +95,9 @@ export function createControllerDebugModal(
);
setStatus(
activeDevice?.id ??
(ctx.state.connectedGamepads.length > 0 ? 'Controller connected.' : 'No controller detected.'),
(ctx.state.connectedGamepads.length > 0
? 'Controller connected.'
: 'No controller detected.'),
);
ctx.dom.controllerDebugSummary.textContent =
ctx.state.connectedGamepads.length > 0

View File

@@ -45,7 +45,9 @@ export function createControllerSelectModal(
syncSelectedControllerId();
return;
}
const preferredIndex = ctx.state.connectedGamepads.findIndex((device) => device.id === preferredId);
const preferredIndex = ctx.state.connectedGamepads.findIndex(
(device) => device.id === preferredId,
);
if (preferredIndex >= 0) {
ctx.state.controllerDeviceSelectedIndex = preferredIndex;
syncSelectedControllerId();

View File

@@ -63,8 +63,7 @@ body {
padding: 8px 12px;
border-radius: 999px;
border: 1px solid rgba(138, 213, 202, 0.45);
background:
linear-gradient(135deg, rgba(10, 44, 40, 0.94), rgba(8, 28, 33, 0.94));
background: linear-gradient(135deg, rgba(10, 44, 40, 0.94), rgba(8, 28, 33, 0.94));
color: rgba(228, 255, 251, 0.98);
font-size: 12px;
font-weight: 700;

View File

@@ -166,7 +166,9 @@ export function resolveRendererDom(): RendererDom {
controllerDebugSummary: getRequiredElement<HTMLDivElement>('controllerDebugSummary'),
controllerDebugAxes: getRequiredElement<HTMLPreElement>('controllerDebugAxes'),
controllerDebugButtons: getRequiredElement<HTMLPreElement>('controllerDebugButtons'),
controllerDebugButtonIndices: getRequiredElement<HTMLPreElement>('controllerDebugButtonIndices'),
controllerDebugButtonIndices: getRequiredElement<HTMLPreElement>(
'controllerDebugButtonIndices',
),
sessionHelpModal: getRequiredElement<HTMLDivElement>('sessionHelpModal'),
sessionHelpClose: getRequiredElement<HTMLButtonElement>('sessionHelpClose'),