diff --git a/frontend/__tests__/input/handlers/insert-text.spec.ts b/frontend/__tests__/input/handlers/insert-text.spec.ts new file mode 100644 index 000000000000..c1558d3baf3b --- /dev/null +++ b/frontend/__tests__/input/handlers/insert-text.spec.ts @@ -0,0 +1,182 @@ +import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; +import { + getInputElementValue, + setInputElementValue, +} from "../../../src/ts/input/input-element"; +import { onInsertText } from "../../../src/ts/input/handlers/insert-text"; +import { + getLigatureCompletion, + getMatchingLigatureOverride, + resetPendingLigatureCompletion, +} from "../../../src/ts/input/helpers/ligatures"; + +const mocks = vi.hoisted(() => ({ + currentWord: "", + input: { + current: "", + syncWithInputElement: vi.fn(), + }, + incrementKeypressErrors: vi.fn(), +})); + +vi.mock("../../../src/ts/test/test-ui", () => ({ + afterTestTextInput: vi.fn(), +})); +vi.mock("../../../src/ts/test/test-state", () => ({ + activeWordIndex: 0, + isActive: true, +})); +vi.mock("../../../src/ts/test/test-logic", () => ({ + areAllTestWordsGenerated: vi.fn(() => false), + startTest: vi.fn(), +})); +vi.mock("../../../src/ts/test/test-input", () => ({ + input: mocks.input, + corrected: { update: vi.fn() }, + incrementAccuracy: vi.fn(), + incrementKeypressCount: vi.fn(), + incrementKeypressErrors: mocks.incrementKeypressErrors, + pushKeypressWord: vi.fn(), + pushMissedWord: vi.fn(), + setBurstStart: vi.fn(), + setCurrentNotAfk: vi.fn(), +})); +vi.mock("../../../src/ts/test/test-words", () => ({ + words: { + getCurrentText: vi.fn(() => mocks.currentWord), + }, +})); +vi.mock("../../../src/ts/input/helpers/fail-or-finish", () => ({ + checkIfFailedDueToDifficulty: vi.fn(), + checkIfFailedDueToMinBurst: vi.fn(), + checkIfFinished: vi.fn(), +})); +vi.mock("../../../src/ts/test/funbox/list", () => ({ + findSingleActiveFunboxWithFunction: vi.fn(), + isFunboxActiveWithProperty: vi.fn(() => false), +})); +vi.mock("../../../src/ts/test/replay", () => ({ + addReplayEvent: vi.fn(), +})); +vi.mock("../../../src/ts/config/store", () => ({ + Config: { + blindMode: false, + keymapMode: "off", + language: "english", + mode: "words", + oppositeShiftMode: "off", + stopOnError: "off", + }, +})); +vi.mock("../../../src/ts/events/keymap", () => ({ + flash: vi.fn(), +})); +vi.mock("../../../src/ts/test/weak-spot", () => ({ + updateScore: vi.fn(), +})); +vi.mock("../../../src/ts/legacy-states/composition", () => ({ + getComposing: vi.fn(() => false), + getData: vi.fn(() => ""), +})); +vi.mock("../../../src/ts/input/state", () => ({ + getIncorrectShiftsInARow: vi.fn(() => 0), + incrementIncorrectShiftsInARow: vi.fn(), + isCorrectShiftUsed: vi.fn(() => true), + resetIncorrectShiftsInARow: vi.fn(), +})); +vi.mock("../../../src/ts/states/notifications", () => ({ + showNoticeNotification: vi.fn(), +})); +vi.mock("../../../src/ts/input/helpers/word-navigation", () => ({ + goToNextWord: vi.fn(async () => ({ + increasedWordIndex: false, + lastBurst: null, + })), +})); +vi.mock("../../../src/ts/input/handlers/before-insert-text", () => ({ + onBeforeInsertText: vi.fn(), +})); + +describe("insert-text ligature input overrides", () => { + beforeEach(() => { + mocks.currentWord = ""; + mocks.input.current = ""; + mocks.input.syncWithInputElement.mockImplementation(() => { + mocks.input.current = getInputElementValue().inputValue; + }); + setInputElementValue(""); + }); + + afterEach(() => { + vi.clearAllMocks(); + resetPendingLigatureCompletion(); + setInputElementValue(""); + }); + + it.each([ + ["o", "œ", "œ"], + ["O", "Œ", "Œ"], + ["a", "æ", "æ"], + ["A", "Æ", "Æ"], + ])( + "normalizes '%s' to '%s' when target is '%s'", + (data, target, expected) => { + expect(getMatchingLigatureOverride(data, target)).toBe(expected); + }, + ); + + it.each([ + ["œ", "e"], + ["Œ", "E"], + ["æ", "e"], + ["Æ", "E"], + ])("gets completion '%s' after '%s'", (target, completion) => { + expect(getLigatureCompletion(target)).toBe(completion); + }); + + it("does not normalize unrelated input", () => { + expect(getMatchingLigatureOverride("e", "œ")).toBeNull(); + expect(getLigatureCompletion("o")).toBeNull(); + }); + + it("removes the completion character and keeps input state synced", async () => { + mocks.currentWord = "œuvre"; + + setInputElementValue("o"); + await onInsertText({ + now: performance.now(), + data: "o", + }); + + setInputElementValue("œe"); + + await onInsertText({ + now: performance.now(), + data: "e", + }); + + expect(getInputElementValue().inputValue).toBe("œ"); + expect(mocks.input.current).toBe("œ"); + expect(mocks.input.syncWithInputElement).toHaveBeenCalledTimes(2); + expect(mocks.incrementKeypressErrors).not.toHaveBeenCalled(); + }); + + it("penalizes skipping the ligature completion character", async () => { + mocks.currentWord = "œuvre"; + + setInputElementValue("o"); + await onInsertText({ + now: performance.now(), + data: "o", + }); + + setInputElementValue("œu"); + await onInsertText({ + now: performance.now(), + data: "u", + }); + + expect(mocks.input.current).toBe("œu"); + expect(mocks.incrementKeypressErrors).toHaveBeenCalledOnce(); + }); +}); diff --git a/frontend/src/ts/input/handlers/insert-text.ts b/frontend/src/ts/input/handlers/insert-text.ts index ff19d06333e9..24931ff21a61 100644 --- a/frontend/src/ts/input/handlers/insert-text.ts +++ b/frontend/src/ts/input/handlers/insert-text.ts @@ -37,6 +37,12 @@ import { isCharCorrect, shouldInsertSpaceCharacter, } from "../helpers/validation"; +import { + getLigatureCompletion, + getMatchingLigatureOverride, + getPendingLigatureCompletionStatus, + setPendingLigatureCompletion, +} from "../helpers/ligatures"; const charOverrides = new Map([ ["…", "..."], @@ -82,6 +88,16 @@ export async function onInsertText(options: OnInsertTextParams): Promise { return; } + const pendingLigatureCompletionStatus = getPendingLigatureCompletionStatus( + options.data, + TestInput.input.current, + ); + if (pendingLigatureCompletionStatus === "complete") { + setInputElementValue(inputValue.slice(0, -options.data.length)); + TestInput.input.syncWithInputElement(); + return; + } + const charOverride = charOverrides.get(options.data); if ( charOverride !== undefined && @@ -140,13 +156,15 @@ export async function onInsertText(options: OnInsertTextParams): Promise { currentWord[(testInput + data).length - 1] ?? "", ); const correct = - funboxCorrect ?? - isCharCorrect({ - data, - inputValue: testInput, - targetWord: currentWord, - correctShiftUsed, - }); + pendingLigatureCompletionStatus === "skipped" + ? false + : (funboxCorrect ?? + isCharCorrect({ + data, + inputValue: testInput, + targetWord: currentWord, + correctShiftUsed, + })); // word navigation check const noSpaceForce = @@ -301,6 +319,20 @@ function normalizeDataAndUpdateInputIfNeeded( ) { replaceInputElementLastValueChar(targetChar); normalizedData = targetChar; + } else { + const ligatureOverride = getMatchingLigatureOverride(data, targetChar); + if (ligatureOverride !== null) { + replaceInputElementLastValueChar(ligatureOverride); + normalizedData = ligatureOverride; + + const ligatureCompletion = getLigatureCompletion(targetChar); + if (ligatureCompletion !== null) { + setPendingLigatureCompletion( + ligatureCompletion, + testInput.length + ligatureOverride.length, + ); + } + } } return normalizedData; } diff --git a/frontend/src/ts/input/helpers/ligatures.ts b/frontend/src/ts/input/helpers/ligatures.ts new file mode 100644 index 000000000000..85edb0067c0f --- /dev/null +++ b/frontend/src/ts/input/helpers/ligatures.ts @@ -0,0 +1,62 @@ +const ligatureInputOverrides = new Map([ + ["œ", "oe"], + ["Œ", "OE"], + ["æ", "ae"], + ["Æ", "AE"], +]); + +let pendingLigatureCompletion: { + completion: string; + inputLength: number; +} | null = null; + +type PendingLigatureCompletionStatus = "complete" | "skipped" | null; + +export function getMatchingLigatureOverride( + data: string, + targetChar: string | undefined, +): string | null { + if (targetChar === undefined) return null; + + const override = ligatureInputOverrides.get(targetChar); + if (override?.[0] !== data) return null; + + return targetChar; +} + +export function getLigatureCompletion( + targetChar: string | undefined, +): string | null { + if (targetChar === undefined) return null; + + const override = ligatureInputOverrides.get(targetChar); + return override?.slice(1) ?? null; +} + +export function setPendingLigatureCompletion( + completion: string, + inputLength: number, +): void { + pendingLigatureCompletion = { completion, inputLength }; +} + +export function resetPendingLigatureCompletion(): void { + pendingLigatureCompletion = null; +} + +export function getPendingLigatureCompletionStatus( + data: string, + currentInput: string, +): PendingLigatureCompletionStatus { + if (pendingLigatureCompletion === null) return null; + + if (currentInput.length !== pendingLigatureCompletion.inputLength) { + resetPendingLigatureCompletion(); + return null; + } + + const completionMatched = data === pendingLigatureCompletion.completion; + resetPendingLigatureCompletion(); + + return completionMatched ? "complete" : "skipped"; +} diff --git a/package.json b/package.json index 2d29994e8943..a4d142202004 100644 --- a/package.json +++ b/package.json @@ -99,7 +99,8 @@ "packageManager": "pnpm@10.28.1", "pnpm": { "overrides": { - "postcss": "8.5.8" + "postcss": "8.5.8", + "re2": "1.24.1" } } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a14f78f2320f..a6ff9a884bc5 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -6,6 +6,7 @@ settings: overrides: postcss: 8.5.8 + re2: 1.24.1 importers: @@ -2853,6 +2854,10 @@ packages: resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} engines: {node: '>=12'} + '@isaacs/fs-minipass@4.0.1': + resolution: {integrity: sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==} + engines: {node: '>=18.0.0'} + '@joshwooding/vite-plugin-react-docgen-typescript@0.6.4': resolution: {integrity: sha512-6PyZBYKnnVNqOSB0YFly+62R7dmov8segT27A+RVTBVd4iAE6kbW9QBJGlyR2yG4D4ohzhZSTIu7BK1UTtmFFA==} peerDependencies: @@ -2988,14 +2993,6 @@ packages: '@nothing-but/utils@0.17.0': resolution: {integrity: sha512-TuCHcHLOqDL0SnaAxACfuRHBNRgNJcNn9X0GiH5H3YSDBVquCr3qEIG3FOQAuMyZCbu9w8nk2CHhOsn7IvhIwQ==} - '@npmcli/agent@2.2.2': - resolution: {integrity: sha512-OrcNPXdpSl9UX7qPVRWbmWMCSXrcDa2M9DvrbOTj7ao1S4PlqVFYv9/yLKMkrJKZ/V5A/kDBC690or307i26Og==} - engines: {node: ^16.14.0 || >=18.0.0} - - '@npmcli/fs@3.1.1': - resolution: {integrity: sha512-q9CRWjpHCMIh5sVyefoD1cA7PkvILqCZsnSOEUUivORLjxCO/Irmue2DprETiNgEqktDBZaM1Bi+jrarx1XdCg==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - '@npmcli/map-workspaces@3.0.6': resolution: {integrity: sha512-tkYs0OYnzQm6iIRdfy+LcLBjcKuQCeE5YLb8KnrIlutJfheNaPvPpgoFEyEFgbjzl5PLZ3IA/BWAwRU0eHuQDA==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} @@ -5045,6 +5042,10 @@ packages: resolution: {integrity: sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + abbrev@4.0.0: + resolution: {integrity: sha512-a1wflyaL0tHtJSmLSOVybYhy22vRih4eduhhrkcjgrWGnRfrZtovJ2FRjxuTtkkj47O/baf0R86QU5OuYpz8fA==} + engines: {node: ^20.17.0 || >=22.9.0} + abort-controller@3.0.0: resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} engines: {node: '>=6.5'} @@ -5553,10 +5554,6 @@ packages: resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} engines: {node: '>=8'} - cacache@18.0.4: - resolution: {integrity: sha512-B+L5iIa9mgcjLbliir2th36yEwPftrzteHYujzsx3dFP/31GCHcIeS8f5MGd80odLOjaOvSpU3EEAmRQptkxLQ==} - engines: {node: ^16.14.0 || >=18.0.0} - cacheable@2.3.4: resolution: {integrity: sha512-djgxybDbw9fL/ZWMI3+CE8ZilNxcwFkVtDc1gJ+IlOSSWkSMPQabhV/XCHTQ6pwwN6aivXPZ43omTooZiX06Ew==} @@ -5675,6 +5672,10 @@ packages: resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==} engines: {node: '>=10'} + chownr@3.0.0: + resolution: {integrity: sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==} + engines: {node: '>=18'} + chromatic@13.3.5: resolution: {integrity: sha512-MzPhxpl838qJUo0A55osCF2ifwPbjcIPeElr1d4SHcjnHoIcg7l1syJDrAYK/a+PcCBrOGi06jPNpQAln5hWgw==} hasBin: true @@ -6522,9 +6523,6 @@ packages: resolution: {integrity: sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==} engines: {node: '>=18'} - err-code@2.0.3: - resolution: {integrity: sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==} - error-ex@1.3.4: resolution: {integrity: sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==} @@ -7009,10 +7007,6 @@ packages: resolution: {integrity: sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==} engines: {node: '>= 8'} - fs-minipass@3.0.3: - resolution: {integrity: sha512-XUBA9XClHbnJWSfBzjkm6RvPsyg3sryZt06BEQoXcF7EK/xpGaQYJgQKDJSUH5SGZ76Y7pFx1QBnXz09rU5Fbw==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - fs.realpath@1.0.0: resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} @@ -7375,9 +7369,6 @@ packages: htmlparser2@9.1.0: resolution: {integrity: sha512-5zfg6mHUoaer/97TxnGpxmbR7zJtPwIYFMZ/H5ucTlPZhKvtum05yiPK3Mgai3a0DyVxv7qYqoweaEd2nrYQzQ==} - http-cache-semantics@4.1.1: - resolution: {integrity: sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==} - http-errors@2.0.0: resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==} engines: {node: '>= 0.8'} @@ -7516,8 +7507,9 @@ packages: resolution: {integrity: sha512-M1WuAmb7pn9zdFRtQYk26ZBoY043Sse0wVDdk4Bppr+JOXyQYybdtvK+l9wUibhtjdjvtoiNy8tk+EgsYIUqKg==} engines: {node: '>=12.0.0'} - install-artifact-from-github@1.3.5: - resolution: {integrity: sha512-gZHC7f/cJgXz7MXlHFBxPVMsvIbev1OQN1uKQYKVJDydGNm9oYf9JstbU4Atnh/eSvk41WtEovoRm+8IF686xg==} + install-artifact-from-github@1.6.0: + resolution: {integrity: sha512-wKsuzN8fy8QK7iEUqyWTQmvZ1QFGPn1xyl3/1iIIDthDjS7Hn9HoPwHlNakZirWbCsbad0lZMkr6Xfbpe1pUzw==} + engines: {node: '>=18'} hasBin: true internal-slot@1.1.0: @@ -7649,9 +7641,6 @@ packages: resolution: {integrity: sha512-EdOZCr0NsGE00Pot+x1ZFx9MJK3C6wy91geZpXwvwexDLJvA4nzYyZf7r+EIwSeVsOLDdBz7ATg9NqKTzuNYuQ==} engines: {node: '>= 4'} - is-lambda@1.0.1: - resolution: {integrity: sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==} - is-map@2.0.3: resolution: {integrity: sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==} engines: {node: '>= 0.4'} @@ -7809,9 +7798,9 @@ packages: isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} - isexe@3.1.1: - resolution: {integrity: sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==} - engines: {node: '>=16'} + isexe@4.0.0: + resolution: {integrity: sha512-FFUtZMpoZ8RqHS3XeXEmHWLA4thH+ZxCv2lOiPIn1Xc7CxrqhWzNSDzD+/chS/zbYezmiwWLdQC09JdQKmthOw==} + engines: {node: '>=20'} isomorphic-fetch@3.0.0: resolution: {integrity: sha512-qvUtwJ3j6qwsF3jLxkZ72qCgjMysPzDfeV240JHiGZsANBYd+EEuu35v7dfrJ9Up0Ak07D7GGSkGhCHTqg/5wA==} @@ -8406,10 +8395,6 @@ packages: resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} engines: {node: '>=10'} - make-fetch-happen@13.0.1: - resolution: {integrity: sha512-cKTUFc/rbKUd/9meOvgrpJ2WrNzymt6jfRDdwg5UCnVzv9dTpEj9JS5m3wtziXVCjluIXyL8pcaukYqezIzZQA==} - engines: {node: ^16.14.0 || >=18.0.0} - map-obj@2.0.0: resolution: {integrity: sha512-TzQSV2DiMYgoF5RycneKVUzIa9bQsj/B3tTgsE3dOGqlzHnGIDaC7XBE7grnA+8kZPnfqSGFe95VHc2oc0VFUQ==} engines: {node: '>=4'} @@ -8577,26 +8562,6 @@ packages: minimist@1.2.8: resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} - minipass-collect@2.0.1: - resolution: {integrity: sha512-D7V8PO9oaz7PWGLbCACuI1qEOsq7UKfLotx/C0Aet43fCUB/wfQ7DYeq2oR/svFJGYDHPr38SHATeaj/ZoKHKw==} - engines: {node: '>=16 || 14 >=14.17'} - - minipass-fetch@3.0.5: - resolution: {integrity: sha512-2N8elDQAtSnFV0Dk7gt15KHsS0Fyz6CbYZ360h0WTYV1Ty46li3rAXVOQj1THMNLdmrD9Vt5pBPtWtVkpwGBqg==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - - minipass-flush@1.0.5: - resolution: {integrity: sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==} - engines: {node: '>= 8'} - - minipass-pipeline@1.2.4: - resolution: {integrity: sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==} - engines: {node: '>=8'} - - minipass-sized@1.0.3: - resolution: {integrity: sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==} - engines: {node: '>=8'} - minipass@3.3.6: resolution: {integrity: sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==} engines: {node: '>=8'} @@ -8617,6 +8582,10 @@ packages: resolution: {integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==} engines: {node: '>= 8'} + minizlib@3.1.0: + resolution: {integrity: sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==} + engines: {node: '>= 18'} + mjml-accordion@4.15.0: resolution: {integrity: sha512-SXhyfxylwF6tT8Ls9XUhbBc3tUIr5DHnhBLC44+hPgOXkogkjfkxVcX9j0Gel8umuHeFQ3+kGagW1NtiwW/v1w==} @@ -8848,6 +8817,9 @@ packages: nan@2.20.0: resolution: {integrity: sha512-bk3gXBZDGILuuo/6sKtr0DQmSThYHLtNCdSdXk9YkxD/jK6X2vmCyyXBBxyqZ4XcnzTyYEAThfX3DCEnLf6igw==} + nan@2.27.0: + resolution: {integrity: sha512-hC+0LidcL3XE4rp1C4H54KujgXKzbfyTngZTwBByQxsOxCEKZT0MPQ4hOKUH2jU1OYstqdDH4onyHPDzcV0XdQ==} + nanoid@3.3.11: resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} @@ -8864,10 +8836,6 @@ packages: resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==} engines: {node: '>= 0.6'} - negotiator@0.6.4: - resolution: {integrity: sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==} - engines: {node: '>= 0.6'} - negotiator@1.0.0: resolution: {integrity: sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==} engines: {node: '>= 0.6'} @@ -8928,9 +8896,9 @@ packages: resolution: {integrity: sha512-s+w+rBWnpTMwSFbaE0UXsRlg7hU4FjekKU4eyAih5T8nJuNZT1nNsskXpxmeqSK9UzkBl6UgRlnKc8hz8IEqOw==} hasBin: true - node-gyp@10.2.0: - resolution: {integrity: sha512-sp3FonBAaFe4aYTcFdZUn2NYkbP7xroPGYvQmP4Nl5PxamznItBnNCgjrVTKrEfQynInMsJvZrdmqUnysCJ8rw==} - engines: {node: ^16.14.0 || >=18.0.0} + node-gyp@12.3.0: + resolution: {integrity: sha512-QNcUWM+HgJplcPzBvFBZ9VXacyGZ4+VTOb80PwWR+TlVzoHbRKULNEzpRsnaoxG3Wzr7Qh7BYxGDU3CbKib2Yg==} + engines: {node: ^20.17.0 || >=22.9.0} hasBin: true node-readfiles@0.2.0: @@ -8962,6 +8930,11 @@ packages: engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} hasBin: true + nopt@9.0.0: + resolution: {integrity: sha512-Zhq3a+yFKrYwSBluL4H9XP3m3y5uvQkB/09CwDruCiRmR/UJYnn9W4R48ry0uGC70aeTPKLynBtscP9efFFcPw==} + engines: {node: ^20.17.0 || >=22.9.0} + hasBin: true + normalize-package-data@6.0.2: resolution: {integrity: sha512-V6gygoYb/5EmNI+MEGrWkC+e6+Rr7mTmfHrxDbLzxQogBkgzo76rkok0Am6thgSF7Mv2nLOajAJj5vDJZEFn7g==} engines: {node: ^16.14.0 || >=18.0.0} @@ -9499,9 +9472,9 @@ packages: resolution: {integrity: sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q==} engines: {node: '>=6'} - proc-log@4.2.0: - resolution: {integrity: sha512-g8+OnU/L2v+wyiVK+D5fA34J7EH8jZ8DDlvwhRCMxmMj7UCBvxiO1mGeN+36JXIKF4zevU4kRBd8lVgG9vLelA==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + proc-log@6.1.0: + resolution: {integrity: sha512-iG+GYldRf2BQ0UDUAd6JQ/RwzaQy6mXmsk/IzlYyal4A4SNFw54MeH4/tLkF4I5WoWG9SQwuqWzS99jaFQHBuQ==} + engines: {node: ^20.17.0 || >=22.9.0} process-nextick-args@2.0.1: resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} @@ -9524,10 +9497,6 @@ packages: promise-breaker@6.0.0: resolution: {integrity: sha512-BthzO9yTPswGf7etOBiHCVuugs2N01/Q/94dIPls48z2zCmrnDptUUZzfIb+41xq0MnYZ/BzmOd6ikDR4ibNZA==} - promise-retry@2.0.1: - resolution: {integrity: sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==} - engines: {node: '>=10'} - prop-types@15.8.1: resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} @@ -9635,8 +9604,9 @@ packages: resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==} hasBin: true - re2@1.21.3: - resolution: {integrity: sha512-GI+KoGkHT4kxTaX+9p0FgNB1XUnCndO9slG5qqeEoZ7kbf6Dk6ohQVpmwKVeSp7LPLn+g6Q3BaCopz4oHuBDuQ==} + re2@1.24.1: + resolution: {integrity: sha512-uRl9cLDKuobJQp+6lVz7E3AyVszubUJ0fqAMWout4ocUWTIFvdHgpqLwwMh/vuNGGGJGh2p2mJZJIQr9am9M/A==} + engines: {node: '>=22'} react-docgen-typescript@2.4.0: resolution: {integrity: sha512-ZtAp5XTO5HRzQctjPU0ybY0RRCQO19X/8fxn3w7y2VVTUbGHDKULPTL4ky3vB05euSgG5NpALhEhDPvQ56wvXg==} @@ -10314,10 +10284,6 @@ packages: resolution: {integrity: sha512-r1X4KsBGedJqo7h8F5c4Ybpcr5RjyP+aWIG007uBPRjmdQWfEiVLzSK71Zji1B9sKxwaCvD8y8cwSkYrlLiRRg==} engines: {node: '>=10.16.0'} - ssri@10.0.6: - resolution: {integrity: sha512-MGrFH9Z4NP9Iyhqn16sDtBpRRNJ0Y2hNa6D65h736fVSaPCHr4DM4sWUNvVaSuC+0OBGhwsrydQwmgfg5LncqQ==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - stack-trace@0.0.10: resolution: {integrity: sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==} @@ -10664,6 +10630,10 @@ packages: resolution: {integrity: sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==} engines: {node: '>=10'} + tar@7.5.15: + resolution: {integrity: sha512-dzGK0boVlC4W5QFuQN1EFSl3bIDYsk7Tj40U6eIBnK2k/8ml7TZ5agbI5j5+qnoVcAA+rNtBml8SEiLxZpNqRQ==} + engines: {node: '>=18'} + tcp-port-used@1.0.2: resolution: {integrity: sha512-l7ar8lLUD3XS1V2lfoJlCBaeoaWo/2xfYt81hM7VlvR4RrMVFqfmzfhLVk40hAb368uitje5gPtBRL1m/DGvLA==} @@ -11045,6 +11015,10 @@ packages: resolution: {integrity: sha512-lVLNosgqo5EkGqh5XUDhGfsMSoO8K0BAN0TyJLvwNRSl4xWGZlCVYsAIpa/OpA3TvmnM01GWcoKmc3ZWo5wKKA==} engines: {node: '>=18.17'} + undici@6.25.0: + resolution: {integrity: sha512-ZgpWDC5gmNiuY9CnLVXEH8rl50xhRCuLNA97fAUnKi8RRuV4E6KG31pDTsLVUKnohJE0I3XDrTeEydAXRw47xg==} + engines: {node: '>=18.17'} + undici@7.16.0: resolution: {integrity: sha512-QEg3HPMll0o3t2ourKwOeUAZ159Kn9mx5pnzHRQO8+Wixmh88YdZRiIwat0iNzNNXn0yoEtXJqFpyW7eM8BV7g==} engines: {node: '>=20.18.1'} @@ -11080,14 +11054,6 @@ packages: resolution: {integrity: sha512-wH590V9VNgYH9g3lH9wWjTrUoKsjLF6sGLjhR4sH1LWpLmCOH0Zf7PukhDA8BiS7KHe4oPNkcTHqYkj7SOGUOw==} engines: {node: '>=20'} - unique-filename@3.0.0: - resolution: {integrity: sha512-afXhuC55wkAmZ0P18QsVE6kp8JaxrEokN2HGIoIVv2ijHQd419H0+6EigAFcIzXeMIkcIkNBpB3L/DXB3cTS/g==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - - unique-slug@4.0.0: - resolution: {integrity: sha512-WrcA6AyEfqDX5bWige/4NQfPZMtASNVxdmWR76WESYQVAACSgWcR6e9i0mofqqBxYFtL4oAxPIptY73/0YE1DQ==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - unique-string@2.0.0: resolution: {integrity: sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==} engines: {node: '>=8'} @@ -11498,9 +11464,9 @@ packages: engines: {node: '>= 8'} hasBin: true - which@4.0.0: - resolution: {integrity: sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==} - engines: {node: ^16.13.0 || >=18.0.0} + which@6.0.1: + resolution: {integrity: sha512-oGLe46MIrCRqX7ytPUf66EAYvdeMIZYn3WaocqqKZAxrBpkqHfL/qvTyJ/bTk5+AqHCjXmrv3CEWgy368zhRUg==} + engines: {node: ^20.17.0 || >=22.9.0} hasBin: true why-is-node-running@2.3.0: @@ -11669,6 +11635,10 @@ packages: yallist@4.0.0: resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + yallist@5.0.0: + resolution: {integrity: sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==} + engines: {node: '>=18'} + yaml-ast-parser@0.0.43: resolution: {integrity: sha512-2PTINUwsRqSd+s8XxKaJWQlUuEMHJQyEuh2edBbW8KNJz0SJPwUSD2zRWqezFEdN7IzAgeuYHFUCF7o8zRdZ0A==} @@ -13971,6 +13941,11 @@ snapshots: wrap-ansi: 8.1.0 wrap-ansi-cjs: wrap-ansi@7.0.0 + '@isaacs/fs-minipass@4.0.1': + dependencies: + minipass: 7.1.3 + optional: true + '@joshwooding/vite-plugin-react-docgen-typescript@0.6.4(typescript@6.0.2)(vite@7.3.2(@types/node@24.9.1)(jiti@2.6.1)(lightningcss@1.32.0)(sass@1.98.0)(terser@5.47.1)(tsx@4.21.0)(yaml@2.8.3))': dependencies: glob: 13.0.6 @@ -14104,22 +14079,6 @@ snapshots: '@nothing-but/utils@0.17.0': {} - '@npmcli/agent@2.2.2': - dependencies: - agent-base: 7.1.4 - http-proxy-agent: 7.0.2 - https-proxy-agent: 7.0.6 - lru-cache: 10.4.3 - socks-proxy-agent: 8.0.4 - transitivePeerDependencies: - - supports-color - optional: true - - '@npmcli/fs@3.1.1': - dependencies: - semver: 7.7.4 - optional: true - '@npmcli/map-workspaces@3.0.6': dependencies: '@npmcli/name-from-folder': 2.0.0 @@ -16245,6 +16204,9 @@ snapshots: abbrev@2.0.0: {} + abbrev@4.0.0: + optional: true + abort-controller@3.0.0: dependencies: event-target-shim: 5.0.1 @@ -16811,22 +16773,6 @@ snapshots: cac@6.7.14: {} - cacache@18.0.4: - dependencies: - '@npmcli/fs': 3.1.1 - fs-minipass: 3.0.3 - glob: 10.4.5 - lru-cache: 10.4.3 - minipass: 7.1.2 - minipass-collect: 2.0.1 - minipass-flush: 1.0.5 - minipass-pipeline: 1.2.4 - p-map: 4.0.0 - ssri: 10.0.6 - tar: 6.2.1 - unique-filename: 3.0.0 - optional: true - cacheable@2.3.4: dependencies: '@cacheable/memory': 2.0.8 @@ -16961,6 +16907,9 @@ snapshots: chownr@2.0.0: {} + chownr@3.0.0: + optional: true + chromatic@13.3.5: {} ci-info@2.0.0: {} @@ -17787,9 +17736,6 @@ snapshots: environment@1.1.0: {} - err-code@2.0.3: - optional: true - error-ex@1.3.4: dependencies: is-arrayish: 0.2.1 @@ -18675,11 +18621,6 @@ snapshots: dependencies: minipass: 3.3.6 - fs-minipass@3.0.3: - dependencies: - minipass: 7.1.2 - optional: true - fs.realpath@1.0.0: {} fsevents@2.3.2: @@ -19129,9 +19070,6 @@ snapshots: domutils: 3.1.0 entities: 4.5.0 - http-cache-semantics@4.1.1: - optional: true - http-errors@2.0.0: dependencies: depd: 2.0.0 @@ -19280,7 +19218,7 @@ snapshots: through: 2.3.8 wrap-ansi: 6.2.0 - install-artifact-from-github@1.3.5: + install-artifact-from-github@1.6.0: optional: true internal-slot@1.1.0: @@ -19427,9 +19365,6 @@ snapshots: is-iterable@1.1.1: {} - is-lambda@1.0.1: - optional: true - is-map@2.0.3: {} is-module@1.0.0: {} @@ -19546,7 +19481,7 @@ snapshots: isexe@2.0.0: {} - isexe@3.1.1: + isexe@4.0.0: optional: true isomorphic-fetch@3.0.0(encoding@0.1.13): @@ -20156,24 +20091,6 @@ snapshots: dependencies: semver: 7.7.4 - make-fetch-happen@13.0.1: - dependencies: - '@npmcli/agent': 2.2.2 - cacache: 18.0.4 - http-cache-semantics: 4.1.1 - is-lambda: 1.0.1 - minipass: 7.1.2 - minipass-fetch: 3.0.5 - minipass-flush: 1.0.5 - minipass-pipeline: 1.2.4 - negotiator: 0.6.4 - proc-log: 4.2.0 - promise-retry: 2.0.1 - ssri: 10.0.6 - transitivePeerDependencies: - - supports-color - optional: true - map-obj@2.0.0: {} mark.js@8.11.1: {} @@ -20299,35 +20216,6 @@ snapshots: minimist@1.2.8: {} - minipass-collect@2.0.1: - dependencies: - minipass: 7.1.2 - optional: true - - minipass-fetch@3.0.5: - dependencies: - minipass: 7.1.2 - minipass-sized: 1.0.3 - minizlib: 2.1.2 - optionalDependencies: - encoding: 0.1.13 - optional: true - - minipass-flush@1.0.5: - dependencies: - minipass: 3.3.6 - optional: true - - minipass-pipeline@1.2.4: - dependencies: - minipass: 3.3.6 - optional: true - - minipass-sized@1.0.3: - dependencies: - minipass: 3.3.6 - optional: true - minipass@3.3.6: dependencies: yallist: 4.0.0 @@ -20343,6 +20231,11 @@ snapshots: minipass: 3.3.6 yallist: 4.0.0 + minizlib@3.1.0: + dependencies: + minipass: 7.1.3 + optional: true + mjml-accordion@4.15.0(encoding@0.1.13): dependencies: '@babel/runtime': 7.28.4 @@ -20745,6 +20638,9 @@ snapshots: nan@2.20.0: optional: true + nan@2.27.0: + optional: true + nanoid@3.3.11: {} natural-compare@1.4.0: {} @@ -20758,9 +20654,6 @@ snapshots: negotiator@0.6.3: {} - negotiator@0.6.4: - optional: true - negotiator@1.0.0: {} neo-async@2.6.2: {} @@ -20813,20 +20706,18 @@ snapshots: detect-libc: 2.1.2 optional: true - node-gyp@10.2.0: + node-gyp@12.3.0: dependencies: env-paths: 2.2.1 exponential-backoff: 3.1.1 - glob: 10.4.5 graceful-fs: 4.2.11 - make-fetch-happen: 13.0.1 - nopt: 7.2.1 - proc-log: 4.2.0 + nopt: 9.0.0 + proc-log: 6.1.0 semver: 7.7.4 - tar: 6.2.1 - which: 4.0.0 - transitivePeerDependencies: - - supports-color + tar: 7.5.15 + tinyglobby: 0.2.16 + undici: 6.25.0 + which: 6.0.1 optional: true node-readfiles@0.2.0: @@ -20862,6 +20753,11 @@ snapshots: dependencies: abbrev: 2.0.0 + nopt@9.0.0: + dependencies: + abbrev: 4.0.0 + optional: true + normalize-package-data@6.0.2: dependencies: hosted-git-info: 7.0.2 @@ -21429,7 +21325,7 @@ snapshots: prismjs@1.29.0: {} - proc-log@4.2.0: + proc-log@6.1.0: optional: true process-nextick-args@2.0.1: {} @@ -21447,12 +21343,6 @@ snapshots: promise-breaker@6.0.0: {} - promise-retry@2.0.1: - dependencies: - err-code: 2.0.3 - retry: 0.12.0 - optional: true - prop-types@15.8.1: dependencies: loose-envify: 1.4.0 @@ -21587,13 +21477,11 @@ snapshots: minimist: 1.2.8 strip-json-comments: 2.0.1 - re2@1.21.3: + re2@1.24.1: dependencies: - install-artifact-from-github: 1.3.5 - nan: 2.20.0 - node-gyp: 10.2.0 - transitivePeerDependencies: - - supports-color + install-artifact-from-github: 1.6.0 + nan: 2.27.0 + node-gyp: 12.3.0 optional: true react-docgen-typescript@2.4.0(typescript@6.0.2): @@ -22469,11 +22357,6 @@ snapshots: cpu-features: 0.0.10 nan: 2.20.0 - ssri@10.0.6: - dependencies: - minipass: 7.1.2 - optional: true - stack-trace@0.0.10: {} stackback@0.0.2: {} @@ -22835,7 +22718,7 @@ snapshots: router: 1.3.8 update-notifier-cjs: 5.1.6(encoding@0.1.13) optionalDependencies: - re2: 1.21.3 + re2: 1.24.1 transitivePeerDependencies: - encoding - supports-color @@ -22966,6 +22849,15 @@ snapshots: mkdirp: 1.0.4 yallist: 4.0.0 + tar@7.5.15: + dependencies: + '@isaacs/fs-minipass': 4.0.1 + chownr: 3.0.0 + minipass: 7.1.3 + minizlib: 3.1.0 + yallist: 5.0.0 + optional: true + tcp-port-used@1.0.2: dependencies: debug: 4.3.1 @@ -23338,6 +23230,9 @@ snapshots: undici@6.24.0: {} + undici@6.25.0: + optional: true + undici@7.16.0: {} unescape-js@1.1.4: @@ -23361,16 +23256,6 @@ snapshots: unicorn-magic@0.4.0: {} - unique-filename@3.0.0: - dependencies: - unique-slug: 4.0.0 - optional: true - - unique-slug@4.0.0: - dependencies: - imurmurhash: 0.1.4 - optional: true - unique-string@2.0.0: dependencies: crypto-random-string: 2.0.0 @@ -23948,9 +23833,9 @@ snapshots: dependencies: isexe: 2.0.0 - which@4.0.0: + which@6.0.1: dependencies: - isexe: 3.1.1 + isexe: 4.0.0 optional: true why-is-node-running@2.3.0: @@ -24161,6 +24046,9 @@ snapshots: yallist@4.0.0: {} + yallist@5.0.0: + optional: true + yaml-ast-parser@0.0.43: {} yaml@1.10.2: {}