@sudobility/sudojo_lib 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/hooks/index.d.ts +27 -0
- package/dist/hooks/index.d.ts.map +1 -0
- package/dist/hooks/index.js +27 -0
- package/dist/hooks/index.js.map +1 -0
- package/dist/hooks/useDailyGame.d.ts +64 -0
- package/dist/hooks/useDailyGame.d.ts.map +1 -0
- package/dist/hooks/useDailyGame.js +120 -0
- package/dist/hooks/useDailyGame.js.map +1 -0
- package/dist/hooks/useGame.d.ts +104 -0
- package/dist/hooks/useGame.d.ts.map +1 -0
- package/dist/hooks/useGame.js +546 -0
- package/dist/hooks/useGame.js.map +1 -0
- package/dist/hooks/useGamePersistence.d.ts +63 -0
- package/dist/hooks/useGamePersistence.d.ts.map +1 -0
- package/dist/hooks/useGamePersistence.js +124 -0
- package/dist/hooks/useGamePersistence.js.map +1 -0
- package/dist/hooks/useGameTeaching.d.ts +89 -0
- package/dist/hooks/useGameTeaching.d.ts.map +1 -0
- package/dist/hooks/useGameTeaching.js +230 -0
- package/dist/hooks/useGameTeaching.js.map +1 -0
- package/dist/hooks/useGameTimer.d.ts +43 -0
- package/dist/hooks/useGameTimer.d.ts.map +1 -0
- package/dist/hooks/useGameTimer.js +68 -0
- package/dist/hooks/useGameTimer.js.map +1 -0
- package/dist/hooks/useHint.d.ts +105 -0
- package/dist/hooks/useHint.d.ts.map +1 -0
- package/dist/hooks/useHint.js +179 -0
- package/dist/hooks/useHint.js.map +1 -0
- package/dist/hooks/useLearning.d.ts +100 -0
- package/dist/hooks/useLearning.d.ts.map +1 -0
- package/dist/hooks/useLearning.js +127 -0
- package/dist/hooks/useLearning.js.map +1 -0
- package/dist/hooks/useLevelGame.d.ts +68 -0
- package/dist/hooks/useLevelGame.d.ts.map +1 -0
- package/dist/hooks/useLevelGame.js +128 -0
- package/dist/hooks/useLevelGame.js.map +1 -0
- package/dist/hooks/useLevels.d.ts +93 -0
- package/dist/hooks/useLevels.d.ts.map +1 -0
- package/dist/hooks/useLevels.js +98 -0
- package/dist/hooks/useLevels.js.map +1 -0
- package/dist/hooks/useLocalStorage.d.ts +26 -0
- package/dist/hooks/useLocalStorage.d.ts.map +1 -0
- package/dist/hooks/useLocalStorage.js +75 -0
- package/dist/hooks/useLocalStorage.js.map +1 -0
- package/dist/hooks/useSudoku.d.ts +125 -0
- package/dist/hooks/useSudoku.d.ts.map +1 -0
- package/dist/hooks/useSudoku.js +655 -0
- package/dist/hooks/useSudoku.js.map +1 -0
- package/dist/hooks/useTechniques.d.ts +92 -0
- package/dist/hooks/useTechniques.d.ts.map +1 -0
- package/dist/hooks/useTechniques.js +108 -0
- package/dist/hooks/useTechniques.js.map +1 -0
- package/dist/index.d.ts +42 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +64 -0
- package/dist/index.js.map +1 -0
- package/dist/test/setup.d.ts +2 -0
- package/dist/test/setup.d.ts.map +1 -0
- package/dist/test/setup.js +35 -0
- package/dist/test/setup.js.map +1 -0
- package/dist/types/display.d.ts +142 -0
- package/dist/types/display.d.ts.map +1 -0
- package/dist/types/display.js +94 -0
- package/dist/types/display.js.map +1 -0
- package/dist/types/game.d.ts +201 -0
- package/dist/types/game.d.ts.map +1 -0
- package/dist/types/game.js +26 -0
- package/dist/types/game.js.map +1 -0
- package/dist/types/gamePersistence.d.ts +31 -0
- package/dist/types/gamePersistence.d.ts.map +1 -0
- package/dist/types/gamePersistence.js +15 -0
- package/dist/types/gamePersistence.js.map +1 -0
- package/dist/types/index.d.ts +18 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +11 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/progress.d.ts +43 -0
- package/dist/types/progress.d.ts.map +1 -0
- package/dist/types/progress.js +19 -0
- package/dist/types/progress.js.map +1 -0
- package/dist/types/settings.d.ts +19 -0
- package/dist/types/settings.d.ts.map +1 -0
- package/dist/types/settings.js +12 -0
- package/dist/types/settings.js.map +1 -0
- package/dist/types/subscription.d.ts +27 -0
- package/dist/types/subscription.d.ts.map +1 -0
- package/dist/types/subscription.js +8 -0
- package/dist/types/subscription.js.map +1 -0
- package/dist/types/sudoku.d.ts +147 -0
- package/dist/types/sudoku.d.ts.map +1 -0
- package/dist/types/sudoku.js +125 -0
- package/dist/types/sudoku.js.map +1 -0
- package/dist/utils/auth.d.ts +25 -0
- package/dist/utils/auth.d.ts.map +1 -0
- package/dist/utils/auth.js +47 -0
- package/dist/utils/auth.js.map +1 -0
- package/dist/utils/board.d.ts +116 -0
- package/dist/utils/board.d.ts.map +1 -0
- package/dist/utils/board.js +291 -0
- package/dist/utils/board.js.map +1 -0
- package/dist/utils/index.d.ts +18 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +23 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/progress.d.ts +44 -0
- package/dist/utils/progress.d.ts.map +1 -0
- package/dist/utils/progress.js +104 -0
- package/dist/utils/progress.js.map +1 -0
- package/dist/utils/scramble.d.ts +35 -0
- package/dist/utils/scramble.d.ts.map +1 -0
- package/dist/utils/scramble.js +270 -0
- package/dist/utils/scramble.js.map +1 -0
- package/dist/utils/subscription.d.ts +82 -0
- package/dist/utils/subscription.d.ts.map +1 -0
- package/dist/utils/subscription.js +117 -0
- package/dist/utils/subscription.js.map +1 -0
- package/dist/utils/sudokuPresenter.d.ts +62 -0
- package/dist/utils/sudokuPresenter.d.ts.map +1 -0
- package/dist/utils/sudokuPresenter.js +403 -0
- package/dist/utils/sudokuPresenter.js.map +1 -0
- package/dist/utils/sudokuScrambler.d.ts +61 -0
- package/dist/utils/sudokuScrambler.d.ts.map +1 -0
- package/dist/utils/sudokuScrambler.js +223 -0
- package/dist/utils/sudokuScrambler.js.map +1 -0
- package/dist/utils/technique.d.ts +41 -0
- package/dist/utils/technique.d.ts.map +1 -0
- package/dist/utils/technique.js +106 -0
- package/dist/utils/technique.js.map +1 -0
- package/dist/utils/theme.d.ts +31 -0
- package/dist/utils/theme.d.ts.map +1 -0
- package/dist/utils/theme.js +40 -0
- package/dist/utils/theme.js.map +1 -0
- package/dist/utils/time.d.ts +22 -0
- package/dist/utils/time.d.ts.map +1 -0
- package/dist/utils/time.js +41 -0
- package/dist/utils/time.js.map +1 -0
- package/dist/utils/validation.d.ts +107 -0
- package/dist/utils/validation.d.ts.map +1 -0
- package/dist/utils/validation.js +248 -0
- package/dist/utils/validation.js.map +1 -0
- package/package.json +79 -0
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hook for getting hints from the Sudoku solver
|
|
3
|
+
*
|
|
4
|
+
* Matches Kotlin HintInteractor behavior:
|
|
5
|
+
* - Stores all hint steps from the API response
|
|
6
|
+
* - Tracks current step index
|
|
7
|
+
* - next() advances to next step (if available)
|
|
8
|
+
* - previous() goes back to previous step
|
|
9
|
+
* - getHint() fetches new hints OR advances to next step if hints exist
|
|
10
|
+
*/
|
|
11
|
+
import { type ClientConfig, type SolverHintStep } from '@sudobility/sudojo_client';
|
|
12
|
+
import type { NetworkClient } from '@sudobility/types';
|
|
13
|
+
/** Board data returned when applying a hint */
|
|
14
|
+
export interface HintBoardData {
|
|
15
|
+
/** 81-character user input string */
|
|
16
|
+
user: string;
|
|
17
|
+
/** Pencilmarks string (comma-separated) */
|
|
18
|
+
pencilmarks: string | null;
|
|
19
|
+
/** Whether auto-pencilmarks are enabled */
|
|
20
|
+
autoPencilmarks: boolean;
|
|
21
|
+
}
|
|
22
|
+
export interface UseHintOptions {
|
|
23
|
+
/** Network client for API calls */
|
|
24
|
+
networkClient: NetworkClient;
|
|
25
|
+
/** Solver client configuration */
|
|
26
|
+
config: ClientConfig;
|
|
27
|
+
/** Original puzzle string (81 chars) */
|
|
28
|
+
puzzle: string;
|
|
29
|
+
/** Current user input string (81 chars) */
|
|
30
|
+
userInput: string;
|
|
31
|
+
/** Current pencilmarks (comma-separated or empty) */
|
|
32
|
+
pencilmarks?: string;
|
|
33
|
+
/** Whether auto-pencilmarks were generated */
|
|
34
|
+
autoPencilmarks?: boolean;
|
|
35
|
+
}
|
|
36
|
+
export interface UseHintResult {
|
|
37
|
+
/** Current hint step being displayed */
|
|
38
|
+
hint: SolverHintStep | null;
|
|
39
|
+
/** All hint steps (for showing progress like "Step 1 of 3") */
|
|
40
|
+
hints: SolverHintStep[] | null;
|
|
41
|
+
/** Current step index (0-based) */
|
|
42
|
+
stepIndex: number;
|
|
43
|
+
/** Total number of steps */
|
|
44
|
+
totalSteps: number;
|
|
45
|
+
/** Whether a hint request is in progress */
|
|
46
|
+
isLoading: boolean;
|
|
47
|
+
/** Error message if hint request failed */
|
|
48
|
+
error: string | null;
|
|
49
|
+
/** Request hints or advance to next step */
|
|
50
|
+
getHint: () => Promise<void>;
|
|
51
|
+
/** Go to next hint step */
|
|
52
|
+
nextStep: () => void;
|
|
53
|
+
/** Go to previous hint step */
|
|
54
|
+
previousStep: () => void;
|
|
55
|
+
/** Clear all hints */
|
|
56
|
+
clearHint: () => void;
|
|
57
|
+
/** Apply the hint to the board - returns board data and clears hint */
|
|
58
|
+
applyHint: () => HintBoardData | null;
|
|
59
|
+
/** Whether there are more steps after current */
|
|
60
|
+
hasNextStep: boolean;
|
|
61
|
+
/** Whether there are steps before current */
|
|
62
|
+
hasPreviousStep: boolean;
|
|
63
|
+
/** Whether the hint can be applied (on last step) */
|
|
64
|
+
canApply: boolean;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Hook for getting hints from the solver API
|
|
68
|
+
*
|
|
69
|
+
* Matches Kotlin HintInteractor behavior:
|
|
70
|
+
* - First call to getHint() fetches hints from API
|
|
71
|
+
* - Subsequent calls advance to next step (if available)
|
|
72
|
+
* - Use nextStep()/previousStep() to navigate manually
|
|
73
|
+
* - clearHint() resets everything
|
|
74
|
+
*
|
|
75
|
+
* @param options - Puzzle state for hint generation
|
|
76
|
+
* @returns Hint state and controls
|
|
77
|
+
*
|
|
78
|
+
* @example
|
|
79
|
+
* ```tsx
|
|
80
|
+
* const { hint, isLoading, getHint, nextStep, clearHint, applyHint } = useHint({
|
|
81
|
+
* networkClient,
|
|
82
|
+
* config,
|
|
83
|
+
* puzzle: originalPuzzle,
|
|
84
|
+
* userInput: currentInput,
|
|
85
|
+
* pencilmarks: currentPencilmarks,
|
|
86
|
+
* });
|
|
87
|
+
*
|
|
88
|
+
* // Get hint button
|
|
89
|
+
* <Button onClick={getHint} disabled={isLoading}>Get Hint</Button>
|
|
90
|
+
*
|
|
91
|
+
* // Display hint
|
|
92
|
+
* {hint && <HintDisplay step={hint} />}
|
|
93
|
+
*
|
|
94
|
+
* // Apply hint to board
|
|
95
|
+
* const handleApply = () => {
|
|
96
|
+
* const boardData = applyHint();
|
|
97
|
+
* if (boardData) {
|
|
98
|
+
* setUserInput(boardData.user);
|
|
99
|
+
* setPencilmarks(boardData.pencilmarks);
|
|
100
|
+
* }
|
|
101
|
+
* };
|
|
102
|
+
* ```
|
|
103
|
+
*/
|
|
104
|
+
export declare function useHint({ networkClient, config, puzzle, userInput, pencilmarks, autoPencilmarks, }: UseHintOptions): UseHintResult;
|
|
105
|
+
//# sourceMappingURL=useHint.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useHint.d.ts","sourceRoot":"","sources":["../../src/hooks/useHint.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,EACL,KAAK,YAAY,EAIjB,KAAK,cAAc,EACpB,MAAM,2BAA2B,CAAC;AACnC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAEvD,+CAA+C;AAC/C,MAAM,WAAW,aAAa;IAC5B,qCAAqC;IACrC,IAAI,EAAE,MAAM,CAAC;IACb,2CAA2C;IAC3C,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,2CAA2C;IAC3C,eAAe,EAAE,OAAO,CAAC;CAC1B;AAED,MAAM,WAAW,cAAc;IAC7B,mCAAmC;IACnC,aAAa,EAAE,aAAa,CAAC;IAC7B,kCAAkC;IAClC,MAAM,EAAE,YAAY,CAAC;IACrB,wCAAwC;IACxC,MAAM,EAAE,MAAM,CAAC;IACf,2CAA2C;IAC3C,SAAS,EAAE,MAAM,CAAC;IAClB,qDAAqD;IACrD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,8CAA8C;IAC9C,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AAED,MAAM,WAAW,aAAa;IAC5B,wCAAwC;IACxC,IAAI,EAAE,cAAc,GAAG,IAAI,CAAC;IAC5B,+DAA+D;IAC/D,KAAK,EAAE,cAAc,EAAE,GAAG,IAAI,CAAC;IAC/B,mCAAmC;IACnC,SAAS,EAAE,MAAM,CAAC;IAClB,4BAA4B;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,4CAA4C;IAC5C,SAAS,EAAE,OAAO,CAAC;IACnB,2CAA2C;IAC3C,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,4CAA4C;IAC5C,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7B,2BAA2B;IAC3B,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,+BAA+B;IAC/B,YAAY,EAAE,MAAM,IAAI,CAAC;IACzB,sBAAsB;IACtB,SAAS,EAAE,MAAM,IAAI,CAAC;IACtB,uEAAuE;IACvE,SAAS,EAAE,MAAM,aAAa,GAAG,IAAI,CAAC;IACtC,iDAAiD;IACjD,WAAW,EAAE,OAAO,CAAC;IACrB,6CAA6C;IAC7C,eAAe,EAAE,OAAO,CAAC;IACzB,qDAAqD;IACrD,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AACH,wBAAgB,OAAO,CAAC,EACtB,aAAa,EACb,MAAM,EACN,MAAM,EACN,SAAS,EACT,WAAW,EACX,eAAuB,GACxB,EAAE,cAAc,GAAG,aAAa,CA2IhC"}
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hook for getting hints from the Sudoku solver
|
|
3
|
+
*
|
|
4
|
+
* Matches Kotlin HintInteractor behavior:
|
|
5
|
+
* - Stores all hint steps from the API response
|
|
6
|
+
* - Tracks current step index
|
|
7
|
+
* - next() advances to next step (if available)
|
|
8
|
+
* - previous() goes back to previous step
|
|
9
|
+
* - getHint() fetches new hints OR advances to next step if hints exist
|
|
10
|
+
*/
|
|
11
|
+
import { useCallback, useRef, useState } from 'react';
|
|
12
|
+
import { createSudojoSolverClient, } from '@sudobility/sudojo_client';
|
|
13
|
+
/**
|
|
14
|
+
* Hook for getting hints from the solver API
|
|
15
|
+
*
|
|
16
|
+
* Matches Kotlin HintInteractor behavior:
|
|
17
|
+
* - First call to getHint() fetches hints from API
|
|
18
|
+
* - Subsequent calls advance to next step (if available)
|
|
19
|
+
* - Use nextStep()/previousStep() to navigate manually
|
|
20
|
+
* - clearHint() resets everything
|
|
21
|
+
*
|
|
22
|
+
* @param options - Puzzle state for hint generation
|
|
23
|
+
* @returns Hint state and controls
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* ```tsx
|
|
27
|
+
* const { hint, isLoading, getHint, nextStep, clearHint, applyHint } = useHint({
|
|
28
|
+
* networkClient,
|
|
29
|
+
* config,
|
|
30
|
+
* puzzle: originalPuzzle,
|
|
31
|
+
* userInput: currentInput,
|
|
32
|
+
* pencilmarks: currentPencilmarks,
|
|
33
|
+
* });
|
|
34
|
+
*
|
|
35
|
+
* // Get hint button
|
|
36
|
+
* <Button onClick={getHint} disabled={isLoading}>Get Hint</Button>
|
|
37
|
+
*
|
|
38
|
+
* // Display hint
|
|
39
|
+
* {hint && <HintDisplay step={hint} />}
|
|
40
|
+
*
|
|
41
|
+
* // Apply hint to board
|
|
42
|
+
* const handleApply = () => {
|
|
43
|
+
* const boardData = applyHint();
|
|
44
|
+
* if (boardData) {
|
|
45
|
+
* setUserInput(boardData.user);
|
|
46
|
+
* setPencilmarks(boardData.pencilmarks);
|
|
47
|
+
* }
|
|
48
|
+
* };
|
|
49
|
+
* ```
|
|
50
|
+
*/
|
|
51
|
+
export function useHint({ networkClient, config, puzzle, userInput, pencilmarks, autoPencilmarks = false, }) {
|
|
52
|
+
const [hints, setHints] = useState(null);
|
|
53
|
+
const [stepIndex, setStepIndex] = useState(0);
|
|
54
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
55
|
+
const [error, setError] = useState(null);
|
|
56
|
+
// Track puzzle state to detect when we need to re-fetch
|
|
57
|
+
const lastPuzzleStateRef = useRef('');
|
|
58
|
+
// Store board data from API response for applying hint
|
|
59
|
+
const boardDataRef = useRef(null);
|
|
60
|
+
// Compute current hint step
|
|
61
|
+
const hint = hints?.[stepIndex] ?? null;
|
|
62
|
+
const totalSteps = hints?.length ?? 0;
|
|
63
|
+
const hasNextStep = stepIndex < totalSteps - 1;
|
|
64
|
+
const hasPreviousStep = stepIndex > 0;
|
|
65
|
+
// Can apply when on last step and have board data
|
|
66
|
+
const canApply = hints !== null &&
|
|
67
|
+
stepIndex === totalSteps - 1 &&
|
|
68
|
+
boardDataRef.current !== null;
|
|
69
|
+
// Fetch hints from API
|
|
70
|
+
const fetchHints = useCallback(async () => {
|
|
71
|
+
setIsLoading(true);
|
|
72
|
+
setError(null);
|
|
73
|
+
try {
|
|
74
|
+
const client = createSudojoSolverClient(networkClient, config);
|
|
75
|
+
const solveOptions = {
|
|
76
|
+
original: puzzle,
|
|
77
|
+
user: userInput,
|
|
78
|
+
autoPencilmarks,
|
|
79
|
+
...(pencilmarks !== undefined && { pencilmarks }),
|
|
80
|
+
};
|
|
81
|
+
const response = await client.solve(solveOptions);
|
|
82
|
+
if (response.success && response.data?.hints?.length) {
|
|
83
|
+
setHints(response.data.hints);
|
|
84
|
+
setStepIndex(0);
|
|
85
|
+
// Store board data for applying hint later
|
|
86
|
+
boardDataRef.current = response.data.board ?? null;
|
|
87
|
+
// Track the puzzle state we fetched for
|
|
88
|
+
lastPuzzleStateRef.current = `${puzzle}|${userInput}|${pencilmarks ?? ''}`;
|
|
89
|
+
}
|
|
90
|
+
else if (response.error) {
|
|
91
|
+
setError(response.error);
|
|
92
|
+
setHints(null);
|
|
93
|
+
boardDataRef.current = null;
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
setError('No hints available');
|
|
97
|
+
setHints(null);
|
|
98
|
+
boardDataRef.current = null;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
catch (err) {
|
|
102
|
+
setError(err instanceof Error ? err.message : 'Failed to get hint');
|
|
103
|
+
setHints(null);
|
|
104
|
+
boardDataRef.current = null;
|
|
105
|
+
}
|
|
106
|
+
finally {
|
|
107
|
+
setIsLoading(false);
|
|
108
|
+
}
|
|
109
|
+
}, [networkClient, config, puzzle, userInput, pencilmarks, autoPencilmarks]);
|
|
110
|
+
// Get hint: fetch if no hints or puzzle changed, otherwise advance
|
|
111
|
+
const getHint = useCallback(async () => {
|
|
112
|
+
const currentPuzzleState = `${puzzle}|${userInput}|${pencilmarks ?? ''}`;
|
|
113
|
+
if (hints === null || lastPuzzleStateRef.current !== currentPuzzleState) {
|
|
114
|
+
// No hints or puzzle state changed - fetch new hints
|
|
115
|
+
await fetchHints();
|
|
116
|
+
}
|
|
117
|
+
else if (hasNextStep) {
|
|
118
|
+
// Have hints and more steps - advance to next
|
|
119
|
+
setStepIndex(prev => prev + 1);
|
|
120
|
+
}
|
|
121
|
+
// If at last step and hints exist, do nothing (stay on last step)
|
|
122
|
+
}, [hints, hasNextStep, fetchHints, puzzle, userInput, pencilmarks]);
|
|
123
|
+
// Navigate to next step
|
|
124
|
+
const nextStep = useCallback(() => {
|
|
125
|
+
if (hasNextStep) {
|
|
126
|
+
setStepIndex(prev => prev + 1);
|
|
127
|
+
}
|
|
128
|
+
}, [hasNextStep]);
|
|
129
|
+
// Navigate to previous step
|
|
130
|
+
const previousStep = useCallback(() => {
|
|
131
|
+
if (hasPreviousStep) {
|
|
132
|
+
setStepIndex(prev => prev - 1);
|
|
133
|
+
}
|
|
134
|
+
}, [hasPreviousStep]);
|
|
135
|
+
// Clear all hints
|
|
136
|
+
const clearHint = useCallback(() => {
|
|
137
|
+
setHints(null);
|
|
138
|
+
setStepIndex(0);
|
|
139
|
+
setError(null);
|
|
140
|
+
lastPuzzleStateRef.current = '';
|
|
141
|
+
boardDataRef.current = null;
|
|
142
|
+
}, []);
|
|
143
|
+
// Apply the hint to the board - returns board data and clears hint
|
|
144
|
+
const applyHint = useCallback(() => {
|
|
145
|
+
const board = boardDataRef.current;
|
|
146
|
+
if (!board?.user) {
|
|
147
|
+
return null;
|
|
148
|
+
}
|
|
149
|
+
const result = {
|
|
150
|
+
user: board.user,
|
|
151
|
+
pencilmarks: board.pencilmarks?.pencilmarks ?? null,
|
|
152
|
+
autoPencilmarks: board.pencilmarks?.auto ?? false,
|
|
153
|
+
};
|
|
154
|
+
// Clear hints after applying (matches Kotlin: result = null)
|
|
155
|
+
setHints(null);
|
|
156
|
+
setStepIndex(0);
|
|
157
|
+
setError(null);
|
|
158
|
+
lastPuzzleStateRef.current = '';
|
|
159
|
+
boardDataRef.current = null;
|
|
160
|
+
return result;
|
|
161
|
+
}, []);
|
|
162
|
+
return {
|
|
163
|
+
hint,
|
|
164
|
+
hints,
|
|
165
|
+
stepIndex,
|
|
166
|
+
totalSteps,
|
|
167
|
+
isLoading,
|
|
168
|
+
error,
|
|
169
|
+
getHint,
|
|
170
|
+
nextStep,
|
|
171
|
+
previousStep,
|
|
172
|
+
clearHint,
|
|
173
|
+
applyHint,
|
|
174
|
+
hasNextStep,
|
|
175
|
+
hasPreviousStep,
|
|
176
|
+
canApply,
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
//# sourceMappingURL=useHint.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useHint.js","sourceRoot":"","sources":["../../src/hooks/useHint.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hook for fetching and managing learning materials for techniques
|
|
3
|
+
*/
|
|
4
|
+
import type { Learning } from '@sudobility/sudojo_types';
|
|
5
|
+
import type { NetworkClient } from '@sudobility/types';
|
|
6
|
+
import { type SudojoAuth, type SudojoConfig } from '@sudobility/sudojo_client';
|
|
7
|
+
export interface UseLearningOptions {
|
|
8
|
+
/** Network client for API calls */
|
|
9
|
+
networkClient: NetworkClient;
|
|
10
|
+
/** Sudojo API configuration */
|
|
11
|
+
config: SudojoConfig;
|
|
12
|
+
/** Auth credentials (optional for public data) */
|
|
13
|
+
auth?: SudojoAuth;
|
|
14
|
+
/** Optional technique UUID to filter learning materials */
|
|
15
|
+
techniqueUuid?: string;
|
|
16
|
+
/** Optional language code to filter learning materials */
|
|
17
|
+
languageCode?: string;
|
|
18
|
+
/** Whether to enable the query */
|
|
19
|
+
enabled?: boolean;
|
|
20
|
+
}
|
|
21
|
+
export interface UseLearningResult {
|
|
22
|
+
/** All learning materials (optionally filtered) */
|
|
23
|
+
learningMaterials: Learning[];
|
|
24
|
+
/** Whether learning materials are loading */
|
|
25
|
+
isLoading: boolean;
|
|
26
|
+
/** Error if loading failed */
|
|
27
|
+
error: Error | null;
|
|
28
|
+
/** Refetch learning materials */
|
|
29
|
+
refetch: () => void;
|
|
30
|
+
/** Get learning material by UUID */
|
|
31
|
+
getLearningByUuid: (uuid: string) => Learning | undefined;
|
|
32
|
+
/** Learning materials sorted by index */
|
|
33
|
+
sortedLearning: Learning[];
|
|
34
|
+
/** Group learning materials by technique UUID */
|
|
35
|
+
learningByTechnique: Map<string, Learning[]>;
|
|
36
|
+
/** Group learning materials by language code */
|
|
37
|
+
learningByLanguage: Map<string, Learning[]>;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Hook for fetching and managing learning materials
|
|
41
|
+
*
|
|
42
|
+
* @param options - Hook options
|
|
43
|
+
* @returns Learning materials data and utilities
|
|
44
|
+
*
|
|
45
|
+
* @example
|
|
46
|
+
* ```tsx
|
|
47
|
+
* function LearningContent({ techniqueUuid }: { techniqueUuid: string }) {
|
|
48
|
+
* const { learningMaterials, isLoading, sortedLearning } = useLearning({
|
|
49
|
+
* networkClient,
|
|
50
|
+
* config: { baseUrl: 'https://api.sudojo.com' },
|
|
51
|
+
* techniqueUuid,
|
|
52
|
+
* languageCode: 'en',
|
|
53
|
+
* });
|
|
54
|
+
*
|
|
55
|
+
* if (isLoading) return <Loading />;
|
|
56
|
+
*
|
|
57
|
+
* return (
|
|
58
|
+
* <div>
|
|
59
|
+
* {sortedLearning.map(item => (
|
|
60
|
+
* <div key={item.uuid}>
|
|
61
|
+
* <p>{item.text}</p>
|
|
62
|
+
* {item.image_url && <img src={item.image_url} alt="" />}
|
|
63
|
+
* </div>
|
|
64
|
+
* ))}
|
|
65
|
+
* </div>
|
|
66
|
+
* );
|
|
67
|
+
* }
|
|
68
|
+
* ```
|
|
69
|
+
*/
|
|
70
|
+
export declare function useLearning(options: UseLearningOptions): UseLearningResult;
|
|
71
|
+
export interface UseLearningItemOptions {
|
|
72
|
+
/** Network client for API calls */
|
|
73
|
+
networkClient: NetworkClient;
|
|
74
|
+
/** Sudojo API configuration */
|
|
75
|
+
config: SudojoConfig;
|
|
76
|
+
/** Auth credentials (optional for public data) */
|
|
77
|
+
auth?: SudojoAuth;
|
|
78
|
+
/** Learning item UUID to fetch */
|
|
79
|
+
learningUuid: string;
|
|
80
|
+
/** Whether to enable the query */
|
|
81
|
+
enabled?: boolean;
|
|
82
|
+
}
|
|
83
|
+
export interface UseLearningItemResult {
|
|
84
|
+
/** The fetched learning item */
|
|
85
|
+
learningItem: Learning | null;
|
|
86
|
+
/** Whether learning item is loading */
|
|
87
|
+
isLoading: boolean;
|
|
88
|
+
/** Error if loading failed */
|
|
89
|
+
error: Error | null;
|
|
90
|
+
/** Refetch learning item */
|
|
91
|
+
refetch: () => void;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Hook for fetching a single learning item by UUID
|
|
95
|
+
*
|
|
96
|
+
* @param options - Hook options
|
|
97
|
+
* @returns Learning item data
|
|
98
|
+
*/
|
|
99
|
+
export declare function useLearningItem(options: UseLearningItemOptions): UseLearningItemResult;
|
|
100
|
+
//# sourceMappingURL=useLearning.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useLearning.d.ts","sourceRoot":"","sources":["../../src/hooks/useLearning.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AACzD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AACvD,OAAO,EACL,KAAK,UAAU,EACf,KAAK,YAAY,EAGlB,MAAM,2BAA2B,CAAC;AAKnC,MAAM,WAAW,kBAAkB;IACjC,mCAAmC;IACnC,aAAa,EAAE,aAAa,CAAC;IAC7B,+BAA+B;IAC/B,MAAM,EAAE,YAAY,CAAC;IACrB,kDAAkD;IAClD,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB,2DAA2D;IAC3D,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,0DAA0D;IAC1D,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,kCAAkC;IAClC,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,iBAAiB;IAChC,mDAAmD;IACnD,iBAAiB,EAAE,QAAQ,EAAE,CAAC;IAC9B,6CAA6C;IAC7C,SAAS,EAAE,OAAO,CAAC;IACnB,8BAA8B;IAC9B,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;IACpB,iCAAiC;IACjC,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,oCAAoC;IACpC,iBAAiB,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,QAAQ,GAAG,SAAS,CAAC;IAC1D,yCAAyC;IACzC,cAAc,EAAE,QAAQ,EAAE,CAAC;IAC3B,iDAAiD;IACjD,mBAAmB,EAAE,GAAG,CAAC,MAAM,EAAE,QAAQ,EAAE,CAAC,CAAC;IAC7C,gDAAgD;IAChD,kBAAkB,EAAE,GAAG,CAAC,MAAM,EAAE,QAAQ,EAAE,CAAC,CAAC;CAC7C;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,kBAAkB,GAAG,iBAAiB,CAiF1E;AAED,MAAM,WAAW,sBAAsB;IACrC,mCAAmC;IACnC,aAAa,EAAE,aAAa,CAAC;IAC7B,+BAA+B;IAC/B,MAAM,EAAE,YAAY,CAAC;IACrB,kDAAkD;IAClD,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB,kCAAkC;IAClC,YAAY,EAAE,MAAM,CAAC;IACrB,kCAAkC;IAClC,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,qBAAqB;IACpC,gCAAgC;IAChC,YAAY,EAAE,QAAQ,GAAG,IAAI,CAAC;IAC9B,uCAAuC;IACvC,SAAS,EAAE,OAAO,CAAC;IACnB,8BAA8B;IAC9B,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;IACpB,4BAA4B;IAC5B,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAC7B,OAAO,EAAE,sBAAsB,GAC9B,qBAAqB,CAgCvB"}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hook for fetching and managing learning materials for techniques
|
|
3
|
+
*/
|
|
4
|
+
import { useMemo } from 'react';
|
|
5
|
+
import { useSudojoLearning, useSudojoLearningItem, } from '@sudobility/sudojo_client';
|
|
6
|
+
/** Default empty auth for public endpoints */
|
|
7
|
+
const DEFAULT_AUTH = { accessToken: '' };
|
|
8
|
+
/**
|
|
9
|
+
* Hook for fetching and managing learning materials
|
|
10
|
+
*
|
|
11
|
+
* @param options - Hook options
|
|
12
|
+
* @returns Learning materials data and utilities
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```tsx
|
|
16
|
+
* function LearningContent({ techniqueUuid }: { techniqueUuid: string }) {
|
|
17
|
+
* const { learningMaterials, isLoading, sortedLearning } = useLearning({
|
|
18
|
+
* networkClient,
|
|
19
|
+
* config: { baseUrl: 'https://api.sudojo.com' },
|
|
20
|
+
* techniqueUuid,
|
|
21
|
+
* languageCode: 'en',
|
|
22
|
+
* });
|
|
23
|
+
*
|
|
24
|
+
* if (isLoading) return <Loading />;
|
|
25
|
+
*
|
|
26
|
+
* return (
|
|
27
|
+
* <div>
|
|
28
|
+
* {sortedLearning.map(item => (
|
|
29
|
+
* <div key={item.uuid}>
|
|
30
|
+
* <p>{item.text}</p>
|
|
31
|
+
* {item.image_url && <img src={item.image_url} alt="" />}
|
|
32
|
+
* </div>
|
|
33
|
+
* ))}
|
|
34
|
+
* </div>
|
|
35
|
+
* );
|
|
36
|
+
* }
|
|
37
|
+
* ```
|
|
38
|
+
*/
|
|
39
|
+
export function useLearning(options) {
|
|
40
|
+
const { networkClient, config, auth = DEFAULT_AUTH, techniqueUuid, languageCode, enabled = true, } = options;
|
|
41
|
+
const queryParams = useMemo(() => {
|
|
42
|
+
// Only return params if at least one filter is provided
|
|
43
|
+
if (!techniqueUuid && !languageCode)
|
|
44
|
+
return undefined;
|
|
45
|
+
return {
|
|
46
|
+
technique_uuid: techniqueUuid ?? null,
|
|
47
|
+
language_code: languageCode ?? null,
|
|
48
|
+
};
|
|
49
|
+
}, [techniqueUuid, languageCode]);
|
|
50
|
+
const { data, isLoading, error, refetch } = useSudojoLearning(networkClient, config, auth, queryParams, { enabled });
|
|
51
|
+
const learningMaterials = useMemo(() => {
|
|
52
|
+
if (!data?.success || !data.data)
|
|
53
|
+
return [];
|
|
54
|
+
return data.data;
|
|
55
|
+
}, [data]);
|
|
56
|
+
const sortedLearning = useMemo(() => {
|
|
57
|
+
return [...learningMaterials].sort((a, b) => {
|
|
58
|
+
// First sort by technique UUID, then by index
|
|
59
|
+
if (a.technique_uuid !== b.technique_uuid) {
|
|
60
|
+
return (a.technique_uuid ?? '').localeCompare(b.technique_uuid ?? '');
|
|
61
|
+
}
|
|
62
|
+
return a.index - b.index;
|
|
63
|
+
});
|
|
64
|
+
}, [learningMaterials]);
|
|
65
|
+
const getLearningByUuid = useMemo(() => {
|
|
66
|
+
const learningMap = new Map(learningMaterials.map(l => [l.uuid, l]));
|
|
67
|
+
return (uuid) => learningMap.get(uuid);
|
|
68
|
+
}, [learningMaterials]);
|
|
69
|
+
const learningByTechnique = useMemo(() => {
|
|
70
|
+
const byTechnique = new Map();
|
|
71
|
+
for (const learning of sortedLearning) {
|
|
72
|
+
const key = learning.technique_uuid ?? '';
|
|
73
|
+
const existing = byTechnique.get(key) ?? [];
|
|
74
|
+
existing.push(learning);
|
|
75
|
+
byTechnique.set(key, existing);
|
|
76
|
+
}
|
|
77
|
+
return byTechnique;
|
|
78
|
+
}, [sortedLearning]);
|
|
79
|
+
const learningByLanguage = useMemo(() => {
|
|
80
|
+
const byLanguage = new Map();
|
|
81
|
+
for (const learning of sortedLearning) {
|
|
82
|
+
const key = learning.language_code;
|
|
83
|
+
const existing = byLanguage.get(key) ?? [];
|
|
84
|
+
existing.push(learning);
|
|
85
|
+
byLanguage.set(key, existing);
|
|
86
|
+
}
|
|
87
|
+
return byLanguage;
|
|
88
|
+
}, [sortedLearning]);
|
|
89
|
+
return {
|
|
90
|
+
learningMaterials,
|
|
91
|
+
isLoading,
|
|
92
|
+
error: error ?? null,
|
|
93
|
+
refetch: () => {
|
|
94
|
+
refetch();
|
|
95
|
+
},
|
|
96
|
+
getLearningByUuid,
|
|
97
|
+
sortedLearning,
|
|
98
|
+
learningByTechnique,
|
|
99
|
+
learningByLanguage,
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Hook for fetching a single learning item by UUID
|
|
104
|
+
*
|
|
105
|
+
* @param options - Hook options
|
|
106
|
+
* @returns Learning item data
|
|
107
|
+
*/
|
|
108
|
+
export function useLearningItem(options) {
|
|
109
|
+
const { networkClient, config, auth = DEFAULT_AUTH, learningUuid, enabled = true, } = options;
|
|
110
|
+
const { data, isLoading, error, refetch } = useSudojoLearningItem(networkClient, config, auth, learningUuid, {
|
|
111
|
+
enabled: enabled && !!learningUuid,
|
|
112
|
+
});
|
|
113
|
+
const learningItem = useMemo(() => {
|
|
114
|
+
if (!data?.success || !data.data)
|
|
115
|
+
return null;
|
|
116
|
+
return data.data;
|
|
117
|
+
}, [data]);
|
|
118
|
+
return {
|
|
119
|
+
learningItem,
|
|
120
|
+
isLoading,
|
|
121
|
+
error: error ?? null,
|
|
122
|
+
refetch: () => {
|
|
123
|
+
refetch();
|
|
124
|
+
},
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
//# sourceMappingURL=useLearning.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useLearning.js","sourceRoot":"","sources":["../../src/hooks/useLearning.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hook for fetching and managing game board for a specific level
|
|
3
|
+
* Handles auth and subscription status with automatic refresh
|
|
4
|
+
*/
|
|
5
|
+
import type { Board } from '@sudobility/sudojo_types';
|
|
6
|
+
import type { NetworkClient } from '@sudobility/types';
|
|
7
|
+
import { type SudojoAuth, type SudojoConfig } from '@sudobility/sudojo_client';
|
|
8
|
+
/** Game fetch status indicating what screen to show */
|
|
9
|
+
export type GameFetchStatus = 'loading' | 'success' | 'auth_required' | 'subscription_required' | 'error';
|
|
10
|
+
export interface UseLevelGameOptions {
|
|
11
|
+
/** Network client for API calls */
|
|
12
|
+
networkClient: NetworkClient;
|
|
13
|
+
/** Sudojo API configuration */
|
|
14
|
+
config: SudojoConfig;
|
|
15
|
+
/** Auth credentials with access token */
|
|
16
|
+
auth: SudojoAuth;
|
|
17
|
+
/** Level UUID to fetch game for */
|
|
18
|
+
levelId: string;
|
|
19
|
+
/** Whether subscription is currently active */
|
|
20
|
+
subscriptionActive?: boolean;
|
|
21
|
+
/** Whether to enable the query */
|
|
22
|
+
enabled?: boolean;
|
|
23
|
+
}
|
|
24
|
+
export interface UseLevelGameResult {
|
|
25
|
+
/** The game board data */
|
|
26
|
+
board: Board | null;
|
|
27
|
+
/** Current status of the game fetch */
|
|
28
|
+
status: GameFetchStatus;
|
|
29
|
+
/** Whether data is loading */
|
|
30
|
+
isLoading: boolean;
|
|
31
|
+
/** Error if fetch failed */
|
|
32
|
+
error: Error | null;
|
|
33
|
+
/** Refetch the game board */
|
|
34
|
+
refetch: () => void;
|
|
35
|
+
/** Fetch a new random board for this level */
|
|
36
|
+
nextPuzzle: () => void;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Hook for fetching a game board for a specific level
|
|
40
|
+
*
|
|
41
|
+
* Automatically refetches when auth token or subscription status changes.
|
|
42
|
+
* Returns status indicating whether auth or subscription is required.
|
|
43
|
+
*
|
|
44
|
+
* @param options - Hook options
|
|
45
|
+
* @returns Game board data and status
|
|
46
|
+
*
|
|
47
|
+
* @example
|
|
48
|
+
* ```tsx
|
|
49
|
+
* function LevelPlayPage({ levelId }: { levelId: string }) {
|
|
50
|
+
* const { board, status, refetch, nextPuzzle } = useLevelGame({
|
|
51
|
+
* networkClient,
|
|
52
|
+
* config,
|
|
53
|
+
* auth,
|
|
54
|
+
* levelId,
|
|
55
|
+
* subscriptionActive: subscription.isActive,
|
|
56
|
+
* });
|
|
57
|
+
*
|
|
58
|
+
* if (status === 'loading') return <Loading />;
|
|
59
|
+
* if (status === 'auth_required') return <AuthRequired onLogin={openAuthModal} />;
|
|
60
|
+
* if (status === 'subscription_required') return <SubscriptionPaywall onSuccess={refetch} />;
|
|
61
|
+
* if (status === 'error') return <Error />;
|
|
62
|
+
*
|
|
63
|
+
* return <SudokuGame puzzle={board.board} solution={board.solution} />;
|
|
64
|
+
* }
|
|
65
|
+
* ```
|
|
66
|
+
*/
|
|
67
|
+
export declare function useLevelGame(options: UseLevelGameOptions): UseLevelGameResult;
|
|
68
|
+
//# sourceMappingURL=useLevelGame.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useLevelGame.d.ts","sourceRoot":"","sources":["../../src/hooks/useLevelGame.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,0BAA0B,CAAC;AACtD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AACvD,OAAO,EACL,KAAK,UAAU,EACf,KAAK,YAAY,EAElB,MAAM,2BAA2B,CAAC;AAGnC,uDAAuD;AACvD,MAAM,MAAM,eAAe,GACvB,SAAS,GACT,SAAS,GACT,eAAe,GACf,uBAAuB,GACvB,OAAO,CAAC;AAuDZ,MAAM,WAAW,mBAAmB;IAClC,mCAAmC;IACnC,aAAa,EAAE,aAAa,CAAC;IAC7B,+BAA+B;IAC/B,MAAM,EAAE,YAAY,CAAC;IACrB,yCAAyC;IACzC,IAAI,EAAE,UAAU,CAAC;IACjB,mCAAmC;IACnC,OAAO,EAAE,MAAM,CAAC;IAChB,+CAA+C;IAC/C,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,kCAAkC;IAClC,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,kBAAkB;IACjC,0BAA0B;IAC1B,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;IACpB,uCAAuC;IACvC,MAAM,EAAE,eAAe,CAAC;IACxB,8BAA8B;IAC9B,SAAS,EAAE,OAAO,CAAC;IACnB,4BAA4B;IAC5B,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;IACpB,6BAA6B;IAC7B,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,8CAA8C;IAC9C,UAAU,EAAE,MAAM,IAAI,CAAC;CACxB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,mBAAmB,GAAG,kBAAkB,CAmF7E"}
|