@sudobility/sudojo_lib 0.0.201 → 0.0.203
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/admin/adminApi.d.ts +34 -0
- package/dist/admin/adminApi.d.ts.map +1 -0
- package/dist/admin/adminApi.js +59 -0
- package/dist/admin/adminApi.js.map +1 -0
- package/dist/admin/boardRules.d.ts +75 -0
- package/dist/admin/boardRules.d.ts.map +1 -0
- package/dist/admin/boardRules.js +127 -0
- package/dist/admin/boardRules.js.map +1 -0
- package/dist/admin/boardStrings.d.ts +55 -0
- package/dist/admin/boardStrings.d.ts.map +1 -0
- package/dist/admin/boardStrings.js +99 -0
- package/dist/admin/boardStrings.js.map +1 -0
- package/dist/admin/examples.d.ts +58 -0
- package/dist/admin/examples.d.ts.map +1 -0
- package/dist/admin/examples.js +104 -0
- package/dist/admin/examples.js.map +1 -0
- package/dist/admin/extraction.d.ts +87 -0
- package/dist/admin/extraction.d.ts.map +1 -0
- package/dist/admin/extraction.js +141 -0
- package/dist/admin/extraction.js.map +1 -0
- package/dist/admin/index.d.ts +32 -0
- package/dist/admin/index.d.ts.map +1 -0
- package/dist/admin/index.js +22 -0
- package/dist/admin/index.js.map +1 -0
- package/dist/admin/jobs.d.ts +108 -0
- package/dist/admin/jobs.d.ts.map +1 -0
- package/dist/admin/jobs.js +435 -0
- package/dist/admin/jobs.js.map +1 -0
- package/dist/admin/regenerateHints.d.ts +16 -0
- package/dist/admin/regenerateHints.d.ts.map +1 -0
- package/dist/admin/regenerateHints.js +43 -0
- package/dist/admin/regenerateHints.js.map +1 -0
- package/dist/admin/types.d.ts +42 -0
- package/dist/admin/types.d.ts.map +1 -0
- package/dist/admin/types.js +21 -0
- package/dist/admin/types.js.map +1 -0
- package/dist/admin/useAdminJob.d.ts +28 -0
- package/dist/admin/useAdminJob.d.ts.map +1 -0
- package/dist/admin/useAdminJob.js +99 -0
- package/dist/admin/useAdminJob.js.map +1 -0
- package/dist/admin/useAdminStats.d.ts +46 -0
- package/dist/admin/useAdminStats.d.ts.map +1 -0
- package/dist/admin/useAdminStats.js +91 -0
- package/dist/admin/useAdminStats.js.map +1 -0
- package/dist/admin/useBoardGenerator.d.ts +29 -0
- package/dist/admin/useBoardGenerator.d.ts.map +1 -0
- package/dist/admin/useBoardGenerator.js +38 -0
- package/dist/admin/useBoardGenerator.js.map +1 -0
- package/dist/admin/useBoardTechniquesUpdater.d.ts +14 -0
- package/dist/admin/useBoardTechniquesUpdater.d.ts.map +1 -0
- package/dist/admin/useBoardTechniquesUpdater.js +28 -0
- package/dist/admin/useBoardTechniquesUpdater.js.map +1 -0
- package/dist/admin/useExampleCreator.d.ts +29 -0
- package/dist/admin/useExampleCreator.d.ts.map +1 -0
- package/dist/admin/useExampleCreator.js +58 -0
- package/dist/admin/useExampleCreator.js.map +1 -0
- package/dist/admin/useSingleBoardExtractor.d.ts +18 -0
- package/dist/admin/useSingleBoardExtractor.d.ts.map +1 -0
- package/dist/admin/useSingleBoardExtractor.js +19 -0
- package/dist/admin/useSingleBoardExtractor.js.map +1 -0
- package/dist/admin/useTechniqueExtractor.d.ts +26 -0
- package/dist/admin/useTechniqueExtractor.d.ts.map +1 -0
- package/dist/admin/useTechniqueExtractor.js +37 -0
- package/dist/admin/useTechniqueExtractor.js.map +1 -0
- package/dist/context/SudojoApiContext.d.ts +58 -0
- package/dist/context/SudojoApiContext.d.ts.map +1 -0
- package/dist/context/SudojoApiContext.js +52 -0
- package/dist/context/SudojoApiContext.js.map +1 -0
- package/dist/context/SudojoApiProvider.d.ts +21 -0
- package/dist/context/SudojoApiProvider.d.ts.map +1 -0
- package/dist/context/SudojoApiProvider.js +15 -0
- package/dist/context/SudojoApiProvider.js.map +1 -0
- package/dist/context/index.d.ts +3 -0
- package/dist/context/index.d.ts.map +1 -1
- package/dist/context/index.js +2 -0
- package/dist/context/index.js.map +1 -1
- package/dist/hooks/index.d.ts +24 -1
- package/dist/hooks/index.d.ts.map +1 -1
- package/dist/hooks/index.js +22 -0
- package/dist/hooks/index.js.map +1 -1
- package/dist/hooks/useBoardEntry.d.ts +65 -5
- package/dist/hooks/useBoardEntry.d.ts.map +1 -1
- package/dist/hooks/useBoardEntry.js +124 -27
- package/dist/hooks/useBoardEntry.js.map +1 -1
- package/dist/hooks/useBoardScan.d.ts +27 -0
- package/dist/hooks/useBoardScan.d.ts.map +1 -0
- package/dist/hooks/useBoardScan.js +27 -0
- package/dist/hooks/useBoardScan.js.map +1 -0
- package/dist/hooks/useCommunities.d.ts +6 -4
- package/dist/hooks/useCommunities.d.ts.map +1 -1
- package/dist/hooks/useCommunities.js +6 -1
- package/dist/hooks/useCommunities.js.map +1 -1
- package/dist/hooks/useCompletionTrigger.d.ts +23 -0
- package/dist/hooks/useCompletionTrigger.d.ts.map +1 -0
- package/dist/hooks/useCompletionTrigger.js +39 -0
- package/dist/hooks/useCompletionTrigger.js.map +1 -0
- package/dist/hooks/useDailyGame.d.ts +11 -6
- package/dist/hooks/useDailyGame.d.ts.map +1 -1
- package/dist/hooks/useDailyGame.js +20 -61
- package/dist/hooks/useDailyGame.js.map +1 -1
- package/dist/hooks/useDeleteAccount.d.ts +72 -0
- package/dist/hooks/useDeleteAccount.d.ts.map +1 -0
- package/dist/hooks/useDeleteAccount.js +71 -0
- package/dist/hooks/useDeleteAccount.js.map +1 -0
- package/dist/hooks/useEnteredGameSession.d.ts +73 -0
- package/dist/hooks/useEnteredGameSession.d.ts.map +1 -0
- package/dist/hooks/useEnteredGameSession.js +155 -0
- package/dist/hooks/useEnteredGameSession.js.map +1 -0
- package/dist/hooks/useGameSession.d.ts +8 -4
- package/dist/hooks/useGameSession.d.ts.map +1 -1
- package/dist/hooks/useGameSession.js +4 -1
- package/dist/hooks/useGameSession.js.map +1 -1
- package/dist/hooks/useGameTeaching.d.ts.map +1 -1
- package/dist/hooks/useGameTeaching.js +11 -13
- package/dist/hooks/useGameTeaching.js.map +1 -1
- package/dist/hooks/useHint.d.ts +7 -7
- package/dist/hooks/useHint.d.ts.map +1 -1
- package/dist/hooks/useHint.js +18 -9
- package/dist/hooks/useHint.js.map +1 -1
- package/dist/hooks/useIsSiteAdmin.d.ts +39 -0
- package/dist/hooks/useIsSiteAdmin.d.ts.map +1 -0
- package/dist/hooks/useIsSiteAdmin.js +31 -0
- package/dist/hooks/useIsSiteAdmin.js.map +1 -0
- package/dist/hooks/useLearning.d.ts +8 -8
- package/dist/hooks/useLearning.d.ts.map +1 -1
- package/dist/hooks/useLearning.js +5 -2
- package/dist/hooks/useLearning.js.map +1 -1
- package/dist/hooks/useLevelGame.d.ts +7 -7
- package/dist/hooks/useLevelGame.d.ts.map +1 -1
- package/dist/hooks/useLevelGame.js +13 -49
- package/dist/hooks/useLevelGame.js.map +1 -1
- package/dist/hooks/useLevels.d.ts +8 -8
- package/dist/hooks/useLevels.d.ts.map +1 -1
- package/dist/hooks/useLevels.js +7 -3
- package/dist/hooks/useLevels.js.map +1 -1
- package/dist/hooks/usePracticeGame.d.ts +53 -0
- package/dist/hooks/usePracticeGame.d.ts.map +1 -0
- package/dist/hooks/usePracticeGame.js +72 -0
- package/dist/hooks/usePracticeGame.js.map +1 -0
- package/dist/hooks/usePuzzleDistribution.d.ts +2 -0
- package/dist/hooks/usePuzzleDistribution.d.ts.map +1 -1
- package/dist/hooks/usePuzzleDistribution.js +4 -14
- package/dist/hooks/usePuzzleDistribution.js.map +1 -1
- package/dist/hooks/usePuzzleProgress.d.ts +37 -0
- package/dist/hooks/usePuzzleProgress.d.ts.map +1 -0
- package/dist/hooks/usePuzzleProgress.js +48 -0
- package/dist/hooks/usePuzzleProgress.js.map +1 -0
- package/dist/hooks/usePuzzleSession.d.ts +215 -0
- package/dist/hooks/usePuzzleSession.d.ts.map +1 -0
- package/dist/hooks/usePuzzleSession.js +358 -0
- package/dist/hooks/usePuzzleSession.js.map +1 -0
- package/dist/hooks/useResumeGame.d.ts +70 -0
- package/dist/hooks/useResumeGame.d.ts.map +1 -0
- package/dist/hooks/useResumeGame.js +123 -0
- package/dist/hooks/useResumeGame.js.map +1 -0
- package/dist/hooks/useStrategies.d.ts +39 -0
- package/dist/hooks/useStrategies.d.ts.map +1 -0
- package/dist/hooks/useStrategies.js +34 -0
- package/dist/hooks/useStrategies.js.map +1 -0
- package/dist/hooks/useSudoku.d.ts +5 -0
- package/dist/hooks/useSudoku.d.ts.map +1 -1
- package/dist/hooks/useSudoku.js +7 -1
- package/dist/hooks/useSudoku.js.map +1 -1
- package/dist/hooks/useTechniqueByPath.d.ts +46 -0
- package/dist/hooks/useTechniqueByPath.d.ts.map +1 -0
- package/dist/hooks/useTechniqueByPath.js +47 -0
- package/dist/hooks/useTechniqueByPath.js.map +1 -0
- package/dist/hooks/useTechniqueExample.d.ts +60 -0
- package/dist/hooks/useTechniqueExample.d.ts.map +1 -0
- package/dist/hooks/useTechniqueExample.js +63 -0
- package/dist/hooks/useTechniqueExample.js.map +1 -0
- package/dist/hooks/useTechniques.d.ts +8 -8
- package/dist/hooks/useTechniques.d.ts.map +1 -1
- package/dist/hooks/useTechniques.js +9 -21
- package/dist/hooks/useTechniques.js.map +1 -1
- package/dist/index.d.ts +418 -11
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +276 -10
- package/dist/index.js.map +1 -1
- package/dist/types/currentGame.d.ts +9 -1
- package/dist/types/currentGame.d.ts.map +1 -1
- package/dist/types/currentGame.js.map +1 -1
- package/dist/utils/auth.d.ts +6 -0
- package/dist/utils/auth.d.ts.map +1 -1
- package/dist/utils/auth.js +8 -0
- package/dist/utils/auth.js.map +1 -1
- package/dist/utils/community.d.ts +64 -0
- package/dist/utils/community.d.ts.map +1 -0
- package/dist/utils/community.js +66 -0
- package/dist/utils/community.js.map +1 -0
- package/dist/utils/date.d.ts +37 -0
- package/dist/utils/date.d.ts.map +1 -0
- package/dist/utils/date.js +68 -0
- package/dist/utils/date.js.map +1 -0
- package/dist/utils/entityTranslate.d.ts +120 -0
- package/dist/utils/entityTranslate.d.ts.map +1 -0
- package/dist/utils/entityTranslate.js +146 -0
- package/dist/utils/entityTranslate.js.map +1 -0
- package/dist/utils/gameFetchStatus.d.ts +74 -0
- package/dist/utils/gameFetchStatus.d.ts.map +1 -0
- package/dist/utils/gameFetchStatus.js +118 -0
- package/dist/utils/gameFetchStatus.js.map +1 -0
- package/dist/utils/gamification.d.ts +36 -0
- package/dist/utils/gamification.d.ts.map +1 -0
- package/dist/utils/gamification.js +33 -0
- package/dist/utils/gamification.js.map +1 -0
- package/dist/utils/hintAccess.d.ts +64 -0
- package/dist/utils/hintAccess.d.ts.map +1 -0
- package/dist/utils/hintAccess.js +57 -0
- package/dist/utils/hintAccess.js.map +1 -0
- package/dist/utils/index.d.ts +31 -10
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +20 -9
- package/dist/utils/index.js.map +1 -1
- package/dist/utils/language.d.ts +123 -0
- package/dist/utils/language.d.ts.map +1 -0
- package/dist/utils/language.js +95 -0
- package/dist/utils/language.js.map +1 -0
- package/dist/utils/localizedHint.d.ts +60 -0
- package/dist/utils/localizedHint.d.ts.map +1 -1
- package/dist/utils/localizedHint.js +90 -0
- package/dist/utils/localizedHint.js.map +1 -1
- package/dist/utils/progress.d.ts +10 -1
- package/dist/utils/progress.d.ts.map +1 -1
- package/dist/utils/progress.js +24 -0
- package/dist/utils/progress.js.map +1 -1
- package/dist/utils/scannedBoard.d.ts +79 -0
- package/dist/utils/scannedBoard.d.ts.map +1 -0
- package/dist/utils/scannedBoard.js +96 -0
- package/dist/utils/scannedBoard.js.map +1 -0
- package/dist/utils/shareUrl.d.ts +38 -15
- package/dist/utils/shareUrl.d.ts.map +1 -1
- package/dist/utils/shareUrl.js +75 -26
- package/dist/utils/shareUrl.js.map +1 -1
- package/dist/utils/subscription.d.ts +118 -0
- package/dist/utils/subscription.d.ts.map +1 -1
- package/dist/utils/subscription.js +212 -0
- package/dist/utils/subscription.js.map +1 -1
- package/dist/utils/sudokuPresenter.d.ts +42 -0
- package/dist/utils/sudokuPresenter.d.ts.map +1 -1
- package/dist/utils/sudokuPresenter.js +114 -1
- package/dist/utils/sudokuPresenter.js.map +1 -1
- package/dist/utils/sudokuScrambler.d.ts +8 -0
- package/dist/utils/sudokuScrambler.d.ts.map +1 -1
- package/dist/utils/sudokuScrambler.js +12 -0
- package/dist/utils/sudokuScrambler.js.map +1 -1
- package/dist/utils/technique.d.ts +112 -1
- package/dist/utils/technique.d.ts.map +1 -1
- package/dist/utils/technique.js +293 -1
- package/dist/utils/technique.js.map +1 -1
- package/dist/utils/techniqueBitmask.d.ts +31 -0
- package/dist/utils/techniqueBitmask.d.ts.map +1 -0
- package/dist/utils/techniqueBitmask.js +53 -0
- package/dist/utils/techniqueBitmask.js.map +1 -0
- package/dist/utils/techniqueExample.d.ts +42 -0
- package/dist/utils/techniqueExample.d.ts.map +1 -0
- package/dist/utils/techniqueExample.js +57 -0
- package/dist/utils/techniqueExample.js.map +1 -0
- package/dist/utils/theme.d.ts +2 -0
- package/dist/utils/theme.d.ts.map +1 -1
- package/dist/utils/theme.js +2 -0
- package/dist/utils/theme.js.map +1 -1
- package/package.json +7 -7
package/dist/utils/technique.js
CHANGED
|
@@ -1,5 +1,297 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Technique
|
|
2
|
+
* Technique and strategy utilities: path aliases, dependencies, grouping,
|
|
3
|
+
* slug lists, difficulty tiers, and the localized learning content.
|
|
3
4
|
*/
|
|
4
5
|
export { getTechniqueIconUrl } from '@sudobility/sudojo_types';
|
|
6
|
+
// =============================================================================
|
|
7
|
+
// Technique path aliases
|
|
8
|
+
// =============================================================================
|
|
9
|
+
/**
|
|
10
|
+
* API `path` → public slug, for techniques whose API path differs from the
|
|
11
|
+
* public URL slug and their key in `techniques.json`. The web app also
|
|
12
|
+
* 301-redirects `/techniques/3d-medusa` to `/techniques/medusa-coloring`.
|
|
13
|
+
*/
|
|
14
|
+
const API_TO_CANONICAL_TECHNIQUE_PATH = {
|
|
15
|
+
'3d-medusa': 'medusa-coloring',
|
|
16
|
+
};
|
|
17
|
+
const CANONICAL_TO_API_TECHNIQUE_PATH = Object.fromEntries(Object.entries(API_TO_CANONICAL_TECHNIQUE_PATH).map(([apiPath, canonicalPath]) => [canonicalPath, apiPath]));
|
|
18
|
+
// eslint-disable-next-line no-redeclare -- overload
|
|
19
|
+
export function toCanonicalTechniquePath(path) {
|
|
20
|
+
if (!path)
|
|
21
|
+
return undefined;
|
|
22
|
+
return API_TO_CANONICAL_TECHNIQUE_PATH[path] || path;
|
|
23
|
+
}
|
|
24
|
+
// eslint-disable-next-line no-redeclare -- overload
|
|
25
|
+
export function toApiTechniquePath(path) {
|
|
26
|
+
if (!path)
|
|
27
|
+
return undefined;
|
|
28
|
+
return CANONICAL_TO_API_TECHNIQUE_PATH[path] || path;
|
|
29
|
+
}
|
|
30
|
+
/** Whether two technique paths name the same technique, aliases included. */
|
|
31
|
+
export function isSameTechniquePath(a, b) {
|
|
32
|
+
if (!a || !b)
|
|
33
|
+
return false;
|
|
34
|
+
return toCanonicalTechniquePath(a) === toCanonicalTechniquePath(b);
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Find a technique by path, accepting either the API path or the canonical
|
|
38
|
+
* slug (`3d-medusa` and `medusa-coloring` both find 3D Medusa).
|
|
39
|
+
*/
|
|
40
|
+
export function findTechniqueByPath(techniques, path) {
|
|
41
|
+
if (!path)
|
|
42
|
+
return undefined;
|
|
43
|
+
return techniques.find(t => isSameTechniquePath(t.path, path));
|
|
44
|
+
}
|
|
45
|
+
// =============================================================================
|
|
46
|
+
// Dependencies
|
|
47
|
+
// =============================================================================
|
|
48
|
+
/**
|
|
49
|
+
* Parse a technique's comma-separated `dependencies` field into technique ids.
|
|
50
|
+
* Blank and non-numeric entries are dropped.
|
|
51
|
+
*/
|
|
52
|
+
export function parseTechniqueDependencies(dependencies) {
|
|
53
|
+
if (!dependencies)
|
|
54
|
+
return [];
|
|
55
|
+
return dependencies
|
|
56
|
+
.split(',')
|
|
57
|
+
.map(s => parseInt(s.trim(), 10))
|
|
58
|
+
.filter(n => !isNaN(n));
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* The techniques a technique builds on ("Requires"), in the order of `all`.
|
|
62
|
+
*/
|
|
63
|
+
export function getTechniqueDependencies(technique, all) {
|
|
64
|
+
const ids = parseTechniqueDependencies(technique?.dependencies);
|
|
65
|
+
if (ids.length === 0)
|
|
66
|
+
return [];
|
|
67
|
+
return all.filter(t => ids.includes(t.technique));
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* The techniques that list this one as a dependency ("Learn after"),
|
|
71
|
+
* in the order of `all`.
|
|
72
|
+
*/
|
|
73
|
+
export function getDependentTechniques(technique, all) {
|
|
74
|
+
if (!technique)
|
|
75
|
+
return [];
|
|
76
|
+
const id = technique.technique;
|
|
77
|
+
return all.filter(t => parseTechniqueDependencies(t.dependencies).includes(id));
|
|
78
|
+
}
|
|
79
|
+
// =============================================================================
|
|
80
|
+
// Grouping and sorting
|
|
81
|
+
// =============================================================================
|
|
82
|
+
/** Sort techniques by level (unassigned first), then by technique number. */
|
|
83
|
+
export function sortTechniquesByLevel(techniques) {
|
|
84
|
+
return [...techniques].sort((a, b) => {
|
|
85
|
+
if (a.level !== b.level) {
|
|
86
|
+
return (a.level ?? 0) - (b.level ?? 0);
|
|
87
|
+
}
|
|
88
|
+
return a.technique - b.technique;
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Group techniques by level, each group sorted by technique number, keys in
|
|
93
|
+
* ascending level order. This is what useTechniques' `techniquesByLevel` returns.
|
|
94
|
+
*
|
|
95
|
+
* @param options.includeUnassigned - Keep techniques with no level under key 0
|
|
96
|
+
* (default true, like useTechniques). The web TechniquesPage drops them: pass false.
|
|
97
|
+
*/
|
|
98
|
+
export function groupTechniquesByLevel(techniques, options = {}) {
|
|
99
|
+
const { includeUnassigned = true } = options;
|
|
100
|
+
const byLevel = new Map();
|
|
101
|
+
for (const technique of sortTechniquesByLevel(techniques)) {
|
|
102
|
+
const key = technique.level ?? 0;
|
|
103
|
+
if (key === 0 && !includeUnassigned)
|
|
104
|
+
continue;
|
|
105
|
+
const existing = byLevel.get(key) ?? [];
|
|
106
|
+
existing.push(technique);
|
|
107
|
+
byLevel.set(key, existing);
|
|
108
|
+
}
|
|
109
|
+
return byLevel;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* The techniques in a strategy, sorted by technique number (as the web
|
|
113
|
+
* StrategiesPage and the RN strategy screens list them).
|
|
114
|
+
*/
|
|
115
|
+
export function getTechniquesForStrategy(techniques, strategyId) {
|
|
116
|
+
if (strategyId == null)
|
|
117
|
+
return [];
|
|
118
|
+
return techniques
|
|
119
|
+
.filter(t => t.strategy_id === strategyId)
|
|
120
|
+
.sort((a, b) => a.technique - b.technique);
|
|
121
|
+
}
|
|
122
|
+
/** Find a strategy by its URL stub. */
|
|
123
|
+
export function findStrategyByStub(strategies, stub) {
|
|
124
|
+
if (!stub)
|
|
125
|
+
return undefined;
|
|
126
|
+
return strategies.find(s => s.stub === stub);
|
|
127
|
+
}
|
|
128
|
+
// =============================================================================
|
|
129
|
+
// Slug lists
|
|
130
|
+
// =============================================================================
|
|
131
|
+
/**
|
|
132
|
+
* Public slugs of all 60 techniques, in footer / sitemap order (canonical
|
|
133
|
+
* slugs, e.g. `medusa-coloring`). Same list as sudojo_app's useFooterConfig
|
|
134
|
+
* and SitemapPage.
|
|
135
|
+
*/
|
|
136
|
+
export const TECHNIQUE_SLUGS = [
|
|
137
|
+
'full-house',
|
|
138
|
+
'hidden-single',
|
|
139
|
+
'naked-single',
|
|
140
|
+
'hidden-pair',
|
|
141
|
+
'naked-pair',
|
|
142
|
+
'locked-candidates',
|
|
143
|
+
'hidden-triple',
|
|
144
|
+
'naked-triple',
|
|
145
|
+
'hidden-quad',
|
|
146
|
+
'naked-quad',
|
|
147
|
+
'x-wing',
|
|
148
|
+
'swordfish',
|
|
149
|
+
'jellyfish',
|
|
150
|
+
'xy-wing',
|
|
151
|
+
'finned-x-wing',
|
|
152
|
+
'squirmbag',
|
|
153
|
+
'finned-swordfish',
|
|
154
|
+
'finned-jellyfish',
|
|
155
|
+
'xyz-wing',
|
|
156
|
+
'wxyz-wing',
|
|
157
|
+
'almost-locked-sets',
|
|
158
|
+
'finned-squirmbag',
|
|
159
|
+
'als-chain',
|
|
160
|
+
'skyscraper',
|
|
161
|
+
'two-string-kite',
|
|
162
|
+
'empty-rectangle',
|
|
163
|
+
'simple-coloring',
|
|
164
|
+
'w-wing',
|
|
165
|
+
'remote-pairs',
|
|
166
|
+
'unique-rectangle-type-1',
|
|
167
|
+
'unique-rectangle-type-2',
|
|
168
|
+
'bug-1',
|
|
169
|
+
'sue-de-coq',
|
|
170
|
+
'als-xz',
|
|
171
|
+
'x-cycles',
|
|
172
|
+
'forcing-chains',
|
|
173
|
+
'medusa-coloring',
|
|
174
|
+
'crane',
|
|
175
|
+
'unique-rectangle-type-3',
|
|
176
|
+
'unique-rectangle-type-4',
|
|
177
|
+
'unique-rectangle-type-5',
|
|
178
|
+
'x-chain',
|
|
179
|
+
'xy-chain',
|
|
180
|
+
'vwxyz-wing',
|
|
181
|
+
'uvwxyz-wing',
|
|
182
|
+
'tuvwxyz-wing',
|
|
183
|
+
'stuvwxyz-wing',
|
|
184
|
+
'aic',
|
|
185
|
+
'forcing-net',
|
|
186
|
+
'avoidable-rectangle',
|
|
187
|
+
'sashimi-x-wing',
|
|
188
|
+
'sashimi-swordfish',
|
|
189
|
+
'sashimi-jellyfish',
|
|
190
|
+
'hidden-unique-rectangle',
|
|
191
|
+
'firework',
|
|
192
|
+
'death-blossom',
|
|
193
|
+
'franken-x-wing',
|
|
194
|
+
'franken-swordfish',
|
|
195
|
+
'franken-jellyfish',
|
|
196
|
+
'grouped-x-cycles',
|
|
197
|
+
];
|
|
198
|
+
/** Stubs of the 17 strategies, in difficulty order (sudojo_app's footer list). */
|
|
199
|
+
export const STRATEGY_SLUGS = [
|
|
200
|
+
'naked-subsets',
|
|
201
|
+
'hidden-subsets',
|
|
202
|
+
'locked-candidates',
|
|
203
|
+
'basic-fish',
|
|
204
|
+
'finned-sashimi-fish',
|
|
205
|
+
'franken-fish',
|
|
206
|
+
'single-digit-patterns',
|
|
207
|
+
'wings',
|
|
208
|
+
'unique-rectangles',
|
|
209
|
+
'coloring',
|
|
210
|
+
'remote-pairs',
|
|
211
|
+
'almost-locked-sets',
|
|
212
|
+
'bug-plus-one',
|
|
213
|
+
'firework',
|
|
214
|
+
'single-digit-chains',
|
|
215
|
+
'multi-digit-chains',
|
|
216
|
+
'forcing',
|
|
217
|
+
];
|
|
218
|
+
/**
|
|
219
|
+
* Tier for a strategy's `difficulty`: ≤3 beginner, ≤5 intermediate,
|
|
220
|
+
* ≤9 advanced, ≤12 expert, otherwise master.
|
|
221
|
+
*/
|
|
222
|
+
export function getStrategyDifficultyTier(difficulty) {
|
|
223
|
+
if (difficulty <= 3)
|
|
224
|
+
return 'beginner';
|
|
225
|
+
if (difficulty <= 5)
|
|
226
|
+
return 'intermediate';
|
|
227
|
+
if (difficulty <= 9)
|
|
228
|
+
return 'advanced';
|
|
229
|
+
if (difficulty <= 12)
|
|
230
|
+
return 'expert';
|
|
231
|
+
return 'master';
|
|
232
|
+
}
|
|
233
|
+
/** i18n key (default namespace) for a strategy's tier, e.g. `strategy.expert`. */
|
|
234
|
+
export function getStrategyDifficultyKey(difficulty) {
|
|
235
|
+
return `strategy.${getStrategyDifficultyTier(difficulty)}`;
|
|
236
|
+
}
|
|
237
|
+
/** Most sections a strategy can have in `strategies.json`. */
|
|
238
|
+
const MAX_STRATEGY_SECTIONS = 20;
|
|
239
|
+
/**
|
|
240
|
+
* Translate `key`, returning '' on a miss (missing key echoed back or empty).
|
|
241
|
+
*/
|
|
242
|
+
function translateOrEmpty(t, key) {
|
|
243
|
+
const translated = t(key, { defaultValue: '' });
|
|
244
|
+
return translated && translated !== key ? translated : '';
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* Collect the sections of a strategy from the `strategies` namespace:
|
|
248
|
+
* `${stub}.1.title` / `${stub}.1.text`, `${stub}.2.*`, ... up to 20, stopping
|
|
249
|
+
* at the first missing title.
|
|
250
|
+
*
|
|
251
|
+
* @param t - Translate function for the `strategies` namespace
|
|
252
|
+
*/
|
|
253
|
+
export function getStrategySections(t, stub) {
|
|
254
|
+
if (!stub)
|
|
255
|
+
return [];
|
|
256
|
+
const result = [];
|
|
257
|
+
for (let n = 1; n <= MAX_STRATEGY_SECTIONS; n++) {
|
|
258
|
+
const key = `${stub}.${n}`;
|
|
259
|
+
const title = translateOrEmpty(t, `${key}.title`);
|
|
260
|
+
if (!title)
|
|
261
|
+
break;
|
|
262
|
+
const text = translateOrEmpty(t, `${key}.text`);
|
|
263
|
+
result.push({
|
|
264
|
+
title,
|
|
265
|
+
text,
|
|
266
|
+
paragraphs: text.split('\n').filter(p => p.trim()),
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
return result;
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* Read a technique's overview, how-it-works steps and tips.
|
|
273
|
+
*
|
|
274
|
+
* The path is canonicalized (`3d-medusa` → `medusa-coloring`), which is how
|
|
275
|
+
* `techniques.json` is keyed.
|
|
276
|
+
*
|
|
277
|
+
* @param t - Translate function for the `techniques` namespace
|
|
278
|
+
*/
|
|
279
|
+
export function parseTechniqueContent(t, path) {
|
|
280
|
+
const key = toCanonicalTechniquePath(path);
|
|
281
|
+
if (!key)
|
|
282
|
+
return { overview: '', howItWorks: [], tips: [] };
|
|
283
|
+
const howItWorks = translateOrEmpty(t, `${key}.how-it-works`);
|
|
284
|
+
const tips = translateOrEmpty(t, `${key}.tips`);
|
|
285
|
+
return {
|
|
286
|
+
overview: translateOrEmpty(t, `${key}.overview`),
|
|
287
|
+
howItWorks: howItWorks
|
|
288
|
+
.split('\n')
|
|
289
|
+
.filter(Boolean)
|
|
290
|
+
.map(step => step.replace(/^\d+\.\s*/, '')),
|
|
291
|
+
tips: tips
|
|
292
|
+
.split(/(?<=\.)\s+/)
|
|
293
|
+
.filter(Boolean)
|
|
294
|
+
.map(tip => tip.trim()),
|
|
295
|
+
};
|
|
296
|
+
}
|
|
5
297
|
//# sourceMappingURL=technique.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"technique.js","sourceRoot":"","sources":["../../src/utils/technique.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC","sourcesContent":["/**\n * Technique-related utilities\n */\n\nexport { getTechniqueIconUrl } from '@sudobility/sudojo_types';\n"]}
|
|
1
|
+
{"version":3,"file":"technique.js","sourceRoot":"","sources":["../../src/utils/technique.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Technique and strategy utilities: path aliases, dependencies, grouping,\n * slug lists, difficulty tiers, and the localized learning content.\n */\n\nimport type { Strategy, Technique } from '@sudobility/sudojo_types';\nimport type { TranslateFunction } from './localizedHint';\n\nexport { getTechniqueIconUrl } from '@sudobility/sudojo_types';\n\n// =============================================================================\n// Technique path aliases\n// =============================================================================\n\n/**\n * API `path` → public slug, for techniques whose API path differs from the\n * public URL slug and their key in `techniques.json`. The web app also\n * 301-redirects `/techniques/3d-medusa` to `/techniques/medusa-coloring`.\n */\nconst API_TO_CANONICAL_TECHNIQUE_PATH: Readonly<Record<string, string>> = {\n '3d-medusa': 'medusa-coloring',\n};\n\nconst CANONICAL_TO_API_TECHNIQUE_PATH: Readonly<Record<string, string>> =\n Object.fromEntries(\n Object.entries(API_TO_CANONICAL_TECHNIQUE_PATH).map(\n ([apiPath, canonicalPath]) => [canonicalPath, apiPath]\n )\n );\n\n/**\n * The public slug for a technique path (URL, share link, locale key).\n * `3d-medusa` → `medusa-coloring`; other paths are returned unchanged.\n */\nexport function toCanonicalTechniquePath(path: string): string;\n// eslint-disable-next-line no-redeclare -- overload\nexport function toCanonicalTechniquePath(\n path: string | null | undefined\n): string | undefined;\n// eslint-disable-next-line no-redeclare -- overload\nexport function toCanonicalTechniquePath(\n path: string | null | undefined\n): string | undefined {\n if (!path) return undefined;\n return API_TO_CANONICAL_TECHNIQUE_PATH[path] || path;\n}\n\n/**\n * The API's `path` for a technique slug. `medusa-coloring` → `3d-medusa`;\n * other paths are returned unchanged.\n */\nexport function toApiTechniquePath(path: string): string;\n// eslint-disable-next-line no-redeclare -- overload\nexport function toApiTechniquePath(\n path: string | null | undefined\n): string | undefined;\n// eslint-disable-next-line no-redeclare -- overload\nexport function toApiTechniquePath(\n path: string | null | undefined\n): string | undefined {\n if (!path) return undefined;\n return CANONICAL_TO_API_TECHNIQUE_PATH[path] || path;\n}\n\n/** Whether two technique paths name the same technique, aliases included. */\nexport function isSameTechniquePath(\n a: string | null | undefined,\n b: string | null | undefined\n): boolean {\n if (!a || !b) return false;\n return toCanonicalTechniquePath(a) === toCanonicalTechniquePath(b);\n}\n\n/**\n * Find a technique by path, accepting either the API path or the canonical\n * slug (`3d-medusa` and `medusa-coloring` both find 3D Medusa).\n */\nexport function findTechniqueByPath<T extends Pick<Technique, 'path'>>(\n techniques: readonly T[],\n path: string | null | undefined\n): T | undefined {\n if (!path) return undefined;\n return techniques.find(t => isSameTechniquePath(t.path, path));\n}\n\n// =============================================================================\n// Dependencies\n// =============================================================================\n\n/**\n * Parse a technique's comma-separated `dependencies` field into technique ids.\n * Blank and non-numeric entries are dropped.\n */\nexport function parseTechniqueDependencies(\n dependencies: string | null | undefined\n): number[] {\n if (!dependencies) return [];\n return dependencies\n .split(',')\n .map(s => parseInt(s.trim(), 10))\n .filter(n => !isNaN(n));\n}\n\n/**\n * The techniques a technique builds on (\"Requires\"), in the order of `all`.\n */\nexport function getTechniqueDependencies<\n T extends Pick<Technique, 'technique'>,\n>(\n technique: Pick<Technique, 'dependencies'> | null | undefined,\n all: readonly T[]\n): T[] {\n const ids = parseTechniqueDependencies(technique?.dependencies);\n if (ids.length === 0) return [];\n return all.filter(t => ids.includes(t.technique));\n}\n\n/**\n * The techniques that list this one as a dependency (\"Learn after\"),\n * in the order of `all`.\n */\nexport function getDependentTechniques<\n T extends Pick<Technique, 'technique' | 'dependencies'>,\n>(\n technique: Pick<Technique, 'technique'> | null | undefined,\n all: readonly T[]\n): T[] {\n if (!technique) return [];\n const id = technique.technique;\n return all.filter(t =>\n parseTechniqueDependencies(t.dependencies).includes(id)\n );\n}\n\n// =============================================================================\n// Grouping and sorting\n// =============================================================================\n\n/** Sort techniques by level (unassigned first), then by technique number. */\nexport function sortTechniquesByLevel<\n T extends Pick<Technique, 'technique' | 'level'>,\n>(techniques: readonly T[]): T[] {\n return [...techniques].sort((a, b) => {\n if (a.level !== b.level) {\n return (a.level ?? 0) - (b.level ?? 0);\n }\n return a.technique - b.technique;\n });\n}\n\n/**\n * Group techniques by level, each group sorted by technique number, keys in\n * ascending level order. This is what useTechniques' `techniquesByLevel` returns.\n *\n * @param options.includeUnassigned - Keep techniques with no level under key 0\n * (default true, like useTechniques). The web TechniquesPage drops them: pass false.\n */\nexport function groupTechniquesByLevel<\n T extends Pick<Technique, 'technique' | 'level'>,\n>(\n techniques: readonly T[],\n options: { includeUnassigned?: boolean } = {}\n): Map<number, T[]> {\n const { includeUnassigned = true } = options;\n const byLevel = new Map<number, T[]>();\n for (const technique of sortTechniquesByLevel(techniques)) {\n const key = technique.level ?? 0;\n if (key === 0 && !includeUnassigned) continue;\n const existing = byLevel.get(key) ?? [];\n existing.push(technique);\n byLevel.set(key, existing);\n }\n return byLevel;\n}\n\n/**\n * The techniques in a strategy, sorted by technique number (as the web\n * StrategiesPage and the RN strategy screens list them).\n */\nexport function getTechniquesForStrategy<\n T extends Pick<Technique, 'technique' | 'strategy_id'>,\n>(techniques: readonly T[], strategyId: number | null | undefined): T[] {\n if (strategyId == null) return [];\n return techniques\n .filter(t => t.strategy_id === strategyId)\n .sort((a, b) => a.technique - b.technique);\n}\n\n/** Find a strategy by its URL stub. */\nexport function findStrategyByStub<S extends Pick<Strategy, 'stub'>>(\n strategies: readonly S[],\n stub: string | null | undefined\n): S | undefined {\n if (!stub) return undefined;\n return strategies.find(s => s.stub === stub);\n}\n\n// =============================================================================\n// Slug lists\n// =============================================================================\n\n/**\n * Public slugs of all 60 techniques, in footer / sitemap order (canonical\n * slugs, e.g. `medusa-coloring`). Same list as sudojo_app's useFooterConfig\n * and SitemapPage.\n */\nexport const TECHNIQUE_SLUGS = [\n 'full-house',\n 'hidden-single',\n 'naked-single',\n 'hidden-pair',\n 'naked-pair',\n 'locked-candidates',\n 'hidden-triple',\n 'naked-triple',\n 'hidden-quad',\n 'naked-quad',\n 'x-wing',\n 'swordfish',\n 'jellyfish',\n 'xy-wing',\n 'finned-x-wing',\n 'squirmbag',\n 'finned-swordfish',\n 'finned-jellyfish',\n 'xyz-wing',\n 'wxyz-wing',\n 'almost-locked-sets',\n 'finned-squirmbag',\n 'als-chain',\n 'skyscraper',\n 'two-string-kite',\n 'empty-rectangle',\n 'simple-coloring',\n 'w-wing',\n 'remote-pairs',\n 'unique-rectangle-type-1',\n 'unique-rectangle-type-2',\n 'bug-1',\n 'sue-de-coq',\n 'als-xz',\n 'x-cycles',\n 'forcing-chains',\n 'medusa-coloring',\n 'crane',\n 'unique-rectangle-type-3',\n 'unique-rectangle-type-4',\n 'unique-rectangle-type-5',\n 'x-chain',\n 'xy-chain',\n 'vwxyz-wing',\n 'uvwxyz-wing',\n 'tuvwxyz-wing',\n 'stuvwxyz-wing',\n 'aic',\n 'forcing-net',\n 'avoidable-rectangle',\n 'sashimi-x-wing',\n 'sashimi-swordfish',\n 'sashimi-jellyfish',\n 'hidden-unique-rectangle',\n 'firework',\n 'death-blossom',\n 'franken-x-wing',\n 'franken-swordfish',\n 'franken-jellyfish',\n 'grouped-x-cycles',\n] as const;\n\n/** A public technique slug. */\nexport type TechniqueSlug = (typeof TECHNIQUE_SLUGS)[number];\n\n/** Stubs of the 17 strategies, in difficulty order (sudojo_app's footer list). */\nexport const STRATEGY_SLUGS = [\n 'naked-subsets',\n 'hidden-subsets',\n 'locked-candidates',\n 'basic-fish',\n 'finned-sashimi-fish',\n 'franken-fish',\n 'single-digit-patterns',\n 'wings',\n 'unique-rectangles',\n 'coloring',\n 'remote-pairs',\n 'almost-locked-sets',\n 'bug-plus-one',\n 'firework',\n 'single-digit-chains',\n 'multi-digit-chains',\n 'forcing',\n] as const;\n\n/** A strategy stub. */\nexport type StrategySlug = (typeof STRATEGY_SLUGS)[number];\n\n// =============================================================================\n// Strategy difficulty and content\n// =============================================================================\n\n/** Difficulty tier of a strategy, shown as a badge. */\nexport type StrategyDifficultyTier =\n | 'beginner'\n | 'intermediate'\n | 'advanced'\n | 'expert'\n | 'master';\n\n/**\n * Tier for a strategy's `difficulty`: ≤3 beginner, ≤5 intermediate,\n * ≤9 advanced, ≤12 expert, otherwise master.\n */\nexport function getStrategyDifficultyTier(\n difficulty: number\n): StrategyDifficultyTier {\n if (difficulty <= 3) return 'beginner';\n if (difficulty <= 5) return 'intermediate';\n if (difficulty <= 9) return 'advanced';\n if (difficulty <= 12) return 'expert';\n return 'master';\n}\n\n/** i18n key (default namespace) for a strategy's tier, e.g. `strategy.expert`. */\nexport function getStrategyDifficultyKey(difficulty: number): string {\n return `strategy.${getStrategyDifficultyTier(difficulty)}`;\n}\n\n/** Most sections a strategy can have in `strategies.json`. */\nconst MAX_STRATEGY_SECTIONS = 20;\n\n/** One section of a strategy's localized description. */\nexport interface StrategySection {\n /** Section title */\n title: string;\n /** Section body ('' when missing) */\n text: string;\n /** Non-blank lines of `text` */\n paragraphs: string[];\n}\n\n/**\n * Translate `key`, returning '' on a miss (missing key echoed back or empty).\n */\nfunction translateOrEmpty(t: TranslateFunction, key: string): string {\n const translated = t(key, { defaultValue: '' });\n return translated && translated !== key ? translated : '';\n}\n\n/**\n * Collect the sections of a strategy from the `strategies` namespace:\n * `${stub}.1.title` / `${stub}.1.text`, `${stub}.2.*`, ... up to 20, stopping\n * at the first missing title.\n *\n * @param t - Translate function for the `strategies` namespace\n */\nexport function getStrategySections(\n t: TranslateFunction,\n stub: string | null | undefined\n): StrategySection[] {\n if (!stub) return [];\n const result: StrategySection[] = [];\n for (let n = 1; n <= MAX_STRATEGY_SECTIONS; n++) {\n const key = `${stub}.${n}`;\n const title = translateOrEmpty(t, `${key}.title`);\n if (!title) break;\n const text = translateOrEmpty(t, `${key}.text`);\n result.push({\n title,\n text,\n paragraphs: text.split('\\n').filter(p => p.trim()),\n });\n }\n return result;\n}\n\n/** A technique's localized learning content from the `techniques` namespace. */\nexport interface TechniqueContent {\n /** `${path}.overview`, or '' */\n overview: string;\n /** `${path}.how-it-works`, one entry per line, leading \"1. \" numbering removed */\n howItWorks: string[];\n /** `${path}.tips`, split into sentences */\n tips: string[];\n}\n\n/**\n * Read a technique's overview, how-it-works steps and tips.\n *\n * The path is canonicalized (`3d-medusa` → `medusa-coloring`), which is how\n * `techniques.json` is keyed.\n *\n * @param t - Translate function for the `techniques` namespace\n */\nexport function parseTechniqueContent(\n t: TranslateFunction,\n path: string | null | undefined\n): TechniqueContent {\n const key = toCanonicalTechniquePath(path);\n if (!key) return { overview: '', howItWorks: [], tips: [] };\n const howItWorks = translateOrEmpty(t, `${key}.how-it-works`);\n const tips = translateOrEmpty(t, `${key}.tips`);\n return {\n overview: translateOrEmpty(t, `${key}.overview`),\n howItWorks: howItWorks\n .split('\\n')\n .filter(Boolean)\n .map(step => step.replace(/^\\d+\\.\\s*/, '')),\n tips: tips\n .split(/(?<=\\.)\\s+/)\n .filter(Boolean)\n .map(tip => tip.trim()),\n };\n}\n"]}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Exact technique bitmasks for game pages.
|
|
3
|
+
*
|
|
4
|
+
* Bit N = technique id N (1-60). A mask with any id >= 54 set exceeds 2^53, so
|
|
5
|
+
* the numeric `techniques` field from the API is lossy. The API also sends an
|
|
6
|
+
* exact base-10 `techniques_bitmask` string: read it into a `bigint`, and send
|
|
7
|
+
* it back as a string. Built on sudojo_types' techniqueBitmaskOf /
|
|
8
|
+
* parseTechniqueBitmask / formatTechniqueBitmask.
|
|
9
|
+
*/
|
|
10
|
+
import { type TechniqueBitmaskSource } from '@sudobility/sudojo_types';
|
|
11
|
+
/**
|
|
12
|
+
* The exact bitmask of a board, daily, validate result or game meta.
|
|
13
|
+
*
|
|
14
|
+
* Unlike `techniqueBitmaskOf`, this never throws, because it runs inside page
|
|
15
|
+
* effects: a malformed `techniques_bitmask` falls back to the numeric field,
|
|
16
|
+
* and an invalid number falls back to `0n`.
|
|
17
|
+
*/
|
|
18
|
+
export declare function exactTechniqueBitmask(source: TechniqueBitmaskSource | null | undefined): bigint;
|
|
19
|
+
/** The exact bitmask as the base-10 string that API requests take. */
|
|
20
|
+
export declare function techniqueBitmaskString(source: TechniqueBitmaskSource | null | undefined): string;
|
|
21
|
+
/**
|
|
22
|
+
* The `techniques` / `techniques_bitmask` pair to copy onto game meta
|
|
23
|
+
* (CurrentGameMeta) or a resumed Board/Daily. Keeps the (lossy) number and adds
|
|
24
|
+
* the exact string, upgrading legacy meta that only has the number. Both are
|
|
25
|
+
* null when the source carries no bitmask.
|
|
26
|
+
*/
|
|
27
|
+
export declare function techniqueFieldsOf(source: TechniqueBitmaskSource | null | undefined): {
|
|
28
|
+
techniques: number | null;
|
|
29
|
+
techniques_bitmask: string | null;
|
|
30
|
+
};
|
|
31
|
+
//# sourceMappingURL=techniqueBitmask.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"techniqueBitmask.d.ts","sourceRoot":"","sources":["../../src/utils/techniqueBitmask.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAIL,KAAK,sBAAsB,EAC5B,MAAM,0BAA0B,CAAC;AAElC;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,sBAAsB,GAAG,IAAI,GAAG,SAAS,GAChD,MAAM,CAWR;AAED,sEAAsE;AACtE,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,sBAAsB,GAAG,IAAI,GAAG,SAAS,GAChD,MAAM,CAER;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,sBAAsB,GAAG,IAAI,GAAG,SAAS,GAChD;IACD,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAC;CACnC,CAWA"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Exact technique bitmasks for game pages.
|
|
3
|
+
*
|
|
4
|
+
* Bit N = technique id N (1-60). A mask with any id >= 54 set exceeds 2^53, so
|
|
5
|
+
* the numeric `techniques` field from the API is lossy. The API also sends an
|
|
6
|
+
* exact base-10 `techniques_bitmask` string: read it into a `bigint`, and send
|
|
7
|
+
* it back as a string. Built on sudojo_types' techniqueBitmaskOf /
|
|
8
|
+
* parseTechniqueBitmask / formatTechniqueBitmask.
|
|
9
|
+
*/
|
|
10
|
+
import { formatTechniqueBitmask, parseTechniqueBitmask, techniqueBitmaskOf, } from '@sudobility/sudojo_types';
|
|
11
|
+
/**
|
|
12
|
+
* The exact bitmask of a board, daily, validate result or game meta.
|
|
13
|
+
*
|
|
14
|
+
* Unlike `techniqueBitmaskOf`, this never throws, because it runs inside page
|
|
15
|
+
* effects: a malformed `techniques_bitmask` falls back to the numeric field,
|
|
16
|
+
* and an invalid number falls back to `0n`.
|
|
17
|
+
*/
|
|
18
|
+
export function exactTechniqueBitmask(source) {
|
|
19
|
+
if (!source)
|
|
20
|
+
return 0n;
|
|
21
|
+
try {
|
|
22
|
+
return techniqueBitmaskOf(source);
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
try {
|
|
26
|
+
return parseTechniqueBitmask(source.techniques);
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
return 0n;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
/** The exact bitmask as the base-10 string that API requests take. */
|
|
34
|
+
export function techniqueBitmaskString(source) {
|
|
35
|
+
return formatTechniqueBitmask(exactTechniqueBitmask(source));
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* The `techniques` / `techniques_bitmask` pair to copy onto game meta
|
|
39
|
+
* (CurrentGameMeta) or a resumed Board/Daily. Keeps the (lossy) number and adds
|
|
40
|
+
* the exact string, upgrading legacy meta that only has the number. Both are
|
|
41
|
+
* null when the source carries no bitmask.
|
|
42
|
+
*/
|
|
43
|
+
export function techniqueFieldsOf(source) {
|
|
44
|
+
if (!source ||
|
|
45
|
+
(source.techniques == null && source.techniques_bitmask == null)) {
|
|
46
|
+
return { techniques: null, techniques_bitmask: null };
|
|
47
|
+
}
|
|
48
|
+
return {
|
|
49
|
+
techniques: source.techniques ?? null,
|
|
50
|
+
techniques_bitmask: techniqueBitmaskString(source),
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=techniqueBitmask.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"techniqueBitmask.js","sourceRoot":"","sources":["../../src/utils/techniqueBitmask.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EACL,sBAAsB,EACtB,qBAAqB,EACrB,kBAAkB,GAEnB,MAAM,0BAA0B,CAAC;AAElC;;;;;;GAMG;AACH,MAAM,UAAU,qBAAqB,CACnC,MAAiD;IAEjD,IAAI,CAAC,MAAM;QAAE,OAAO,EAAE,CAAC;IACvB,IAAI,CAAC;QACH,OAAO,kBAAkB,CAAC,MAAM,CAAC,CAAC;IACpC,CAAC;IAAC,MAAM,CAAC;QACP,IAAI,CAAC;YACH,OAAO,qBAAqB,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;QAClD,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,EAAE,CAAC;QACZ,CAAC;IACH,CAAC;AACH,CAAC;AAED,sEAAsE;AACtE,MAAM,UAAU,sBAAsB,CACpC,MAAiD;IAEjD,OAAO,sBAAsB,CAAC,qBAAqB,CAAC,MAAM,CAAC,CAAC,CAAC;AAC/D,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,iBAAiB,CAC/B,MAAiD;IAKjD,IACE,CAAC,MAAM;QACP,CAAC,MAAM,CAAC,UAAU,IAAI,IAAI,IAAI,MAAM,CAAC,kBAAkB,IAAI,IAAI,CAAC,EAChE,CAAC;QACD,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,kBAAkB,EAAE,IAAI,EAAE,CAAC;IACxD,CAAC;IACD,OAAO;QACL,UAAU,EAAE,MAAM,CAAC,UAAU,IAAI,IAAI;QACrC,kBAAkB,EAAE,sBAAsB,CAAC,MAAM,CAAC;KACnD,CAAC;AACJ,CAAC","sourcesContent":["/**\n * Exact technique bitmasks for game pages.\n *\n * Bit N = technique id N (1-60). A mask with any id >= 54 set exceeds 2^53, so\n * the numeric `techniques` field from the API is lossy. The API also sends an\n * exact base-10 `techniques_bitmask` string: read it into a `bigint`, and send\n * it back as a string. Built on sudojo_types' techniqueBitmaskOf /\n * parseTechniqueBitmask / formatTechniqueBitmask.\n */\n\nimport {\n formatTechniqueBitmask,\n parseTechniqueBitmask,\n techniqueBitmaskOf,\n type TechniqueBitmaskSource,\n} from '@sudobility/sudojo_types';\n\n/**\n * The exact bitmask of a board, daily, validate result or game meta.\n *\n * Unlike `techniqueBitmaskOf`, this never throws, because it runs inside page\n * effects: a malformed `techniques_bitmask` falls back to the numeric field,\n * and an invalid number falls back to `0n`.\n */\nexport function exactTechniqueBitmask(\n source: TechniqueBitmaskSource | null | undefined\n): bigint {\n if (!source) return 0n;\n try {\n return techniqueBitmaskOf(source);\n } catch {\n try {\n return parseTechniqueBitmask(source.techniques);\n } catch {\n return 0n;\n }\n }\n}\n\n/** The exact bitmask as the base-10 string that API requests take. */\nexport function techniqueBitmaskString(\n source: TechniqueBitmaskSource | null | undefined\n): string {\n return formatTechniqueBitmask(exactTechniqueBitmask(source));\n}\n\n/**\n * The `techniques` / `techniques_bitmask` pair to copy onto game meta\n * (CurrentGameMeta) or a resumed Board/Daily. Keeps the (lossy) number and adds\n * the exact string, upgrading legacy meta that only has the number. Both are\n * null when the source carries no bitmask.\n */\nexport function techniqueFieldsOf(\n source: TechniqueBitmaskSource | null | undefined\n): {\n techniques: number | null;\n techniques_bitmask: string | null;\n} {\n if (\n !source ||\n (source.techniques == null && source.techniques_bitmask == null)\n ) {\n return { techniques: null, techniques_bitmask: null };\n }\n return {\n techniques: source.techniques ?? null,\n techniques_bitmask: techniqueBitmaskString(source),\n };\n}\n"]}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Technique example walkthrough: turn a stored example/practice (board,
|
|
3
|
+
* pencilmarks, solution, hint_data) into the cards the technique screens
|
|
4
|
+
* render (web and RN TechniqueExample).
|
|
5
|
+
*/
|
|
6
|
+
import type { WalkthroughStep } from './techniqueWalkthrough';
|
|
7
|
+
import { type TranslateFunction } from './localizedHint';
|
|
8
|
+
/** The stored puzzle a walkthrough is built from (example or practice). */
|
|
9
|
+
export interface TechniqueExampleSource {
|
|
10
|
+
board: string;
|
|
11
|
+
pencilmarks: string | null;
|
|
12
|
+
solution: string;
|
|
13
|
+
hint_data: string | null;
|
|
14
|
+
}
|
|
15
|
+
/** Translation functions a walkthrough needs (i18next `t`s). */
|
|
16
|
+
export interface TechniqueWalkthroughTranslations {
|
|
17
|
+
/** The 'hints' namespace */
|
|
18
|
+
tHints: TranslateFunction;
|
|
19
|
+
/** The 'techniques' namespace */
|
|
20
|
+
tTechniques: TranslateFunction;
|
|
21
|
+
/** The default namespace */
|
|
22
|
+
tCommon: TranslateFunction;
|
|
23
|
+
}
|
|
24
|
+
/** A built walkthrough: one card per step, each with its heading. */
|
|
25
|
+
export interface TechniqueWalkthrough {
|
|
26
|
+
/** Cards: starting board, one per solver step, final result */
|
|
27
|
+
steps: WalkthroughStep[];
|
|
28
|
+
/**
|
|
29
|
+
* Heading per card ('' = none): 'Starting position' for the first card,
|
|
30
|
+
* 'Result' for the last, the step's `headings.<path>` otherwise.
|
|
31
|
+
*/
|
|
32
|
+
stepHeadings: string[];
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Build the walkthrough cards and headings for a stored example.
|
|
36
|
+
*
|
|
37
|
+
* The "Look for {technique}" name comes from the technique's own title when a
|
|
38
|
+
* path is given (falling back to the first step's title), else the first
|
|
39
|
+
* step's localized title (RN's rule; the web used the raw title there).
|
|
40
|
+
*/
|
|
41
|
+
export declare function buildTechniqueWalkthrough(source: TechniqueExampleSource | null | undefined, translations: TechniqueWalkthroughTranslations, techniquePath?: string): TechniqueWalkthrough;
|
|
42
|
+
//# sourceMappingURL=techniqueExample.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"techniqueExample.d.ts","sourceRoot":"","sources":["../../src/utils/techniqueExample.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAO9D,OAAO,EAIL,KAAK,iBAAiB,EACvB,MAAM,iBAAiB,CAAC;AAEzB,2EAA2E;AAC3E,MAAM,WAAW,sBAAsB;IACrC,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAED,gEAAgE;AAChE,MAAM,WAAW,gCAAgC;IAC/C,4BAA4B;IAC5B,MAAM,EAAE,iBAAiB,CAAC;IAC1B,iCAAiC;IACjC,WAAW,EAAE,iBAAiB,CAAC;IAC/B,4BAA4B;IAC5B,OAAO,EAAE,iBAAiB,CAAC;CAC5B;AAED,qEAAqE;AACrE,MAAM,WAAW,oBAAoB;IACnC,+DAA+D;IAC/D,KAAK,EAAE,eAAe,EAAE,CAAC;IACzB;;;OAGG;IACH,YAAY,EAAE,MAAM,EAAE,CAAC;CACxB;AAED;;;;;;GAMG;AACH,wBAAgB,yBAAyB,CACvC,MAAM,EAAE,sBAAsB,GAAG,IAAI,GAAG,SAAS,EACjD,YAAY,EAAE,gCAAgC,EAC9C,aAAa,CAAC,EAAE,MAAM,GACrB,oBAAoB,CAwDtB"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Technique example walkthrough: turn a stored example/practice (board,
|
|
3
|
+
* pencilmarks, solution, hint_data) into the cards the technique screens
|
|
4
|
+
* render (web and RN TechniqueExample).
|
|
5
|
+
*/
|
|
6
|
+
import { buildWalkthroughSteps, parseHintData, parsePracticeBoard, } from './techniqueWalkthrough';
|
|
7
|
+
import { createNamespacedTranslate } from './entityTranslate';
|
|
8
|
+
import { getLocalizedHintTitle, getStepHeadingLocalization, localizedField, } from './localizedHint';
|
|
9
|
+
/**
|
|
10
|
+
* Build the walkthrough cards and headings for a stored example.
|
|
11
|
+
*
|
|
12
|
+
* The "Look for {technique}" name comes from the technique's own title when a
|
|
13
|
+
* path is given (falling back to the first step's title), else the first
|
|
14
|
+
* step's localized title (RN's rule; the web used the raw title there).
|
|
15
|
+
*/
|
|
16
|
+
export function buildTechniqueWalkthrough(source, translations, techniquePath) {
|
|
17
|
+
if (!source)
|
|
18
|
+
return { steps: [], stepHeadings: [] };
|
|
19
|
+
const { tHints, tTechniques, tCommon } = translations;
|
|
20
|
+
// localizeHintStep asks tCommon for 'techniques.<path>.title'; route that
|
|
21
|
+
// prefix to the techniques namespace, as the apps' tMerged did.
|
|
22
|
+
const tMerged = createNamespacedTranslate(tCommon, {
|
|
23
|
+
techniques: tTechniques,
|
|
24
|
+
});
|
|
25
|
+
const initialBoard = parsePracticeBoard(source.board, source.pencilmarks, source.solution);
|
|
26
|
+
const hintData = parseHintData(source.hint_data);
|
|
27
|
+
const firstStep = hintData?.steps?.[0];
|
|
28
|
+
const techniqueName = techniquePath
|
|
29
|
+
? tTechniques(`${techniquePath}.title`, {
|
|
30
|
+
defaultValue: firstStep?.title || 'technique',
|
|
31
|
+
})
|
|
32
|
+
: firstStep
|
|
33
|
+
? getLocalizedHintTitle(tMerged, firstStep)
|
|
34
|
+
: tTechniques('technique', { defaultValue: 'technique' });
|
|
35
|
+
const lookForText = tTechniques('lookFor', { technique: techniqueName });
|
|
36
|
+
const resultText = tTechniques('resultAfterApplying', {
|
|
37
|
+
defaultValue: 'Result after applying the technique.',
|
|
38
|
+
});
|
|
39
|
+
const steps = buildWalkthroughSteps(initialBoard, hintData, lookForText, resultText, tHints, tMerged, techniquePath);
|
|
40
|
+
const last = steps.length - 1;
|
|
41
|
+
const stepHeadings = steps.map((step, index) => {
|
|
42
|
+
if (index === 0) {
|
|
43
|
+
return tTechniques('walkthroughStartHeading', {
|
|
44
|
+
defaultValue: 'Starting position',
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
if (index === last) {
|
|
48
|
+
return tTechniques('walkthroughResultHeading', {
|
|
49
|
+
defaultValue: 'Result',
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
const loc = getStepHeadingLocalization(step.hint);
|
|
53
|
+
return loc ? localizedField(tHints, loc, '') : '';
|
|
54
|
+
});
|
|
55
|
+
return { steps, stepHeadings };
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=techniqueExample.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"techniqueExample.js","sourceRoot":"","sources":["../../src/utils/techniqueExample.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EACL,qBAAqB,EACrB,aAAa,EACb,kBAAkB,GACnB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,yBAAyB,EAAE,MAAM,mBAAmB,CAAC;AAC9D,OAAO,EACL,qBAAqB,EACrB,0BAA0B,EAC1B,cAAc,GAEf,MAAM,iBAAiB,CAAC;AA+BzB;;;;;;GAMG;AACH,MAAM,UAAU,yBAAyB,CACvC,MAAiD,EACjD,YAA8C,EAC9C,aAAsB;IAEtB,IAAI,CAAC,MAAM;QAAE,OAAO,EAAE,KAAK,EAAE,EAAE,EAAE,YAAY,EAAE,EAAE,EAAE,CAAC;IACpD,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,OAAO,EAAE,GAAG,YAAY,CAAC;IACtD,0EAA0E;IAC1E,gEAAgE;IAChE,MAAM,OAAO,GAAG,yBAAyB,CAAC,OAAO,EAAE;QACjD,UAAU,EAAE,WAAW;KACxB,CAAC,CAAC;IAEH,MAAM,YAAY,GAAG,kBAAkB,CACrC,MAAM,CAAC,KAAK,EACZ,MAAM,CAAC,WAAW,EAClB,MAAM,CAAC,QAAQ,CAChB,CAAC;IACF,MAAM,QAAQ,GAAG,aAAa,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;IACjD,MAAM,SAAS,GAAG,QAAQ,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC;IAEvC,MAAM,aAAa,GAAG,aAAa;QACjC,CAAC,CAAC,WAAW,CAAC,GAAG,aAAa,QAAQ,EAAE;YACpC,YAAY,EAAE,SAAS,EAAE,KAAK,IAAI,WAAW;SAC9C,CAAC;QACJ,CAAC,CAAC,SAAS;YACT,CAAC,CAAC,qBAAqB,CAAC,OAAO,EAAE,SAAS,CAAC;YAC3C,CAAC,CAAC,WAAW,CAAC,WAAW,EAAE,EAAE,YAAY,EAAE,WAAW,EAAE,CAAC,CAAC;IAC9D,MAAM,WAAW,GAAG,WAAW,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,aAAa,EAAE,CAAC,CAAC;IACzE,MAAM,UAAU,GAAG,WAAW,CAAC,qBAAqB,EAAE;QACpD,YAAY,EAAE,sCAAsC;KACrD,CAAC,CAAC;IAEH,MAAM,KAAK,GAAG,qBAAqB,CACjC,YAAY,EACZ,QAAQ,EACR,WAAW,EACX,UAAU,EACV,MAAM,EACN,OAAO,EACP,aAAa,CACd,CAAC;IAEF,MAAM,IAAI,GAAG,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC;IAC9B,MAAM,YAAY,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE;QAC7C,IAAI,KAAK,KAAK,CAAC,EAAE,CAAC;YAChB,OAAO,WAAW,CAAC,yBAAyB,EAAE;gBAC5C,YAAY,EAAE,mBAAmB;aAClC,CAAC,CAAC;QACL,CAAC;QACD,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;YACnB,OAAO,WAAW,CAAC,0BAA0B,EAAE;gBAC7C,YAAY,EAAE,QAAQ;aACvB,CAAC,CAAC;QACL,CAAC;QACD,MAAM,GAAG,GAAG,0BAA0B,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAClD,OAAO,GAAG,CAAC,CAAC,CAAC,cAAc,CAAC,MAAM,EAAE,GAAG,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IACpD,CAAC,CAAC,CAAC;IAEH,OAAO,EAAE,KAAK,EAAE,YAAY,EAAE,CAAC;AACjC,CAAC","sourcesContent":["/**\n * Technique example walkthrough: turn a stored example/practice (board,\n * pencilmarks, solution, hint_data) into the cards the technique screens\n * render (web and RN TechniqueExample).\n */\n\nimport type { WalkthroughStep } from './techniqueWalkthrough';\nimport {\n buildWalkthroughSteps,\n parseHintData,\n parsePracticeBoard,\n} from './techniqueWalkthrough';\nimport { createNamespacedTranslate } from './entityTranslate';\nimport {\n getLocalizedHintTitle,\n getStepHeadingLocalization,\n localizedField,\n type TranslateFunction,\n} from './localizedHint';\n\n/** The stored puzzle a walkthrough is built from (example or practice). */\nexport interface TechniqueExampleSource {\n board: string;\n pencilmarks: string | null;\n solution: string;\n hint_data: string | null;\n}\n\n/** Translation functions a walkthrough needs (i18next `t`s). */\nexport interface TechniqueWalkthroughTranslations {\n /** The 'hints' namespace */\n tHints: TranslateFunction;\n /** The 'techniques' namespace */\n tTechniques: TranslateFunction;\n /** The default namespace */\n tCommon: TranslateFunction;\n}\n\n/** A built walkthrough: one card per step, each with its heading. */\nexport interface TechniqueWalkthrough {\n /** Cards: starting board, one per solver step, final result */\n steps: WalkthroughStep[];\n /**\n * Heading per card ('' = none): 'Starting position' for the first card,\n * 'Result' for the last, the step's `headings.<path>` otherwise.\n */\n stepHeadings: string[];\n}\n\n/**\n * Build the walkthrough cards and headings for a stored example.\n *\n * The \"Look for {technique}\" name comes from the technique's own title when a\n * path is given (falling back to the first step's title), else the first\n * step's localized title (RN's rule; the web used the raw title there).\n */\nexport function buildTechniqueWalkthrough(\n source: TechniqueExampleSource | null | undefined,\n translations: TechniqueWalkthroughTranslations,\n techniquePath?: string\n): TechniqueWalkthrough {\n if (!source) return { steps: [], stepHeadings: [] };\n const { tHints, tTechniques, tCommon } = translations;\n // localizeHintStep asks tCommon for 'techniques.<path>.title'; route that\n // prefix to the techniques namespace, as the apps' tMerged did.\n const tMerged = createNamespacedTranslate(tCommon, {\n techniques: tTechniques,\n });\n\n const initialBoard = parsePracticeBoard(\n source.board,\n source.pencilmarks,\n source.solution\n );\n const hintData = parseHintData(source.hint_data);\n const firstStep = hintData?.steps?.[0];\n\n const techniqueName = techniquePath\n ? tTechniques(`${techniquePath}.title`, {\n defaultValue: firstStep?.title || 'technique',\n })\n : firstStep\n ? getLocalizedHintTitle(tMerged, firstStep)\n : tTechniques('technique', { defaultValue: 'technique' });\n const lookForText = tTechniques('lookFor', { technique: techniqueName });\n const resultText = tTechniques('resultAfterApplying', {\n defaultValue: 'Result after applying the technique.',\n });\n\n const steps = buildWalkthroughSteps(\n initialBoard,\n hintData,\n lookForText,\n resultText,\n tHints,\n tMerged,\n techniquePath\n );\n\n const last = steps.length - 1;\n const stepHeadings = steps.map((step, index) => {\n if (index === 0) {\n return tTechniques('walkthroughStartHeading', {\n defaultValue: 'Starting position',\n });\n }\n if (index === last) {\n return tTechniques('walkthroughResultHeading', {\n defaultValue: 'Result',\n });\n }\n const loc = getStepHeadingLocalization(step.hint);\n return loc ? localizedField(tHints, loc, '') : '';\n });\n\n return { steps, stepHeadings };\n}\n"]}
|
package/dist/utils/theme.d.ts
CHANGED
|
@@ -7,6 +7,8 @@ export type ThemePreference = 'light' | 'dark' | 'system';
|
|
|
7
7
|
export type ResolvedTheme = 'light' | 'dark';
|
|
8
8
|
/** Storage key for theme preference */
|
|
9
9
|
export declare const THEME_STORAGE_KEY = "sudojo-theme";
|
|
10
|
+
/** Storage key for the font size preference (the web ThemeProvider's `fontSizeStorageKey`) */
|
|
11
|
+
export declare const FONT_SIZE_STORAGE_KEY = "sudojo-font-size";
|
|
10
12
|
/**
|
|
11
13
|
* Detect system color scheme preference
|
|
12
14
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"theme.d.ts","sourceRoot":"","sources":["../../src/utils/theme.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,8BAA8B;AAC9B,MAAM,MAAM,eAAe,GAAG,OAAO,GAAG,MAAM,GAAG,QAAQ,CAAC;AAE1D,6CAA6C;AAC7C,MAAM,MAAM,aAAa,GAAG,OAAO,GAAG,MAAM,CAAC;AAE7C,uCAAuC;AACvC,eAAO,MAAM,iBAAiB,iBAAiB,CAAC;AAEhD;;;;GAIG;AACH,wBAAgB,cAAc,IAAI,aAAa,CAK9C;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAC1B,UAAU,EAAE,eAAe,EAC3B,WAAW,EAAE,aAAa,GACzB,aAAa,CAKf;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CACpC,KAAK,EAAE,OAAO,GACb,KAAK,IAAI,eAAe,CAE1B"}
|
|
1
|
+
{"version":3,"file":"theme.d.ts","sourceRoot":"","sources":["../../src/utils/theme.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,8BAA8B;AAC9B,MAAM,MAAM,eAAe,GAAG,OAAO,GAAG,MAAM,GAAG,QAAQ,CAAC;AAE1D,6CAA6C;AAC7C,MAAM,MAAM,aAAa,GAAG,OAAO,GAAG,MAAM,CAAC;AAE7C,uCAAuC;AACvC,eAAO,MAAM,iBAAiB,iBAAiB,CAAC;AAEhD,8FAA8F;AAC9F,eAAO,MAAM,qBAAqB,qBAAqB,CAAC;AAExD;;;;GAIG;AACH,wBAAgB,cAAc,IAAI,aAAa,CAK9C;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAC1B,UAAU,EAAE,eAAe,EAC3B,WAAW,EAAE,aAAa,GACzB,aAAa,CAKf;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CACpC,KAAK,EAAE,OAAO,GACb,KAAK,IAAI,eAAe,CAE1B"}
|
package/dist/utils/theme.js
CHANGED
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
*/
|
|
4
4
|
/** Storage key for theme preference */
|
|
5
5
|
export const THEME_STORAGE_KEY = 'sudojo-theme';
|
|
6
|
+
/** Storage key for the font size preference (the web ThemeProvider's `fontSizeStorageKey`) */
|
|
7
|
+
export const FONT_SIZE_STORAGE_KEY = 'sudojo-font-size';
|
|
6
8
|
/**
|
|
7
9
|
* Detect system color scheme preference
|
|
8
10
|
*
|
package/dist/utils/theme.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"theme.js","sourceRoot":"","sources":["../../src/utils/theme.ts"],"names":[],"mappings":"AAAA;;GAEG;AAQH,uCAAuC;AACvC,MAAM,CAAC,MAAM,iBAAiB,GAAG,cAAc,CAAC;AAEhD;;;;GAIG;AACH,MAAM,UAAU,cAAc;IAC5B,IAAI,OAAO,MAAM,KAAK,WAAW;QAAE,OAAO,OAAO,CAAC;IAClD,OAAO,MAAM,CAAC,UAAU,CAAC,8BAA8B,CAAC,CAAC,OAAO;QAC9D,CAAC,CAAC,MAAM;QACR,CAAC,CAAC,OAAO,CAAC;AACd,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,YAAY,CAC1B,UAA2B,EAC3B,WAA0B;IAE1B,IAAI,UAAU,KAAK,QAAQ,EAAE,CAAC;QAC5B,OAAO,WAAW,CAAC;IACrB,CAAC;IACD,OAAO,UAAU,CAAC;AACpB,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,sBAAsB,CACpC,KAAc;IAEd,OAAO,KAAK,KAAK,OAAO,IAAI,KAAK,KAAK,MAAM,IAAI,KAAK,KAAK,QAAQ,CAAC;AACrE,CAAC","sourcesContent":["/**\n * Theme utilities\n */\n\n/** Available theme options */\nexport type ThemePreference = 'light' | 'dark' | 'system';\n\n/** Resolved theme (without system option) */\nexport type ResolvedTheme = 'light' | 'dark';\n\n/** Storage key for theme preference */\nexport const THEME_STORAGE_KEY = 'sudojo-theme';\n\n/**\n * Detect system color scheme preference\n *\n * @returns 'dark' if system prefers dark mode, 'light' otherwise\n */\nexport function getSystemTheme(): ResolvedTheme {\n if (typeof window === 'undefined') return 'light';\n return window.matchMedia('(prefers-color-scheme: dark)').matches\n ? 'dark'\n : 'light';\n}\n\n/**\n * Resolve theme preference to actual theme\n *\n * @param preference - User's theme preference\n * @param systemTheme - Current system theme\n * @returns Resolved theme ('light' or 'dark')\n */\nexport function resolveTheme(\n preference: ThemePreference,\n systemTheme: ResolvedTheme\n): ResolvedTheme {\n if (preference === 'system') {\n return systemTheme;\n }\n return preference;\n}\n\n/**\n * Check if a value is a valid theme preference\n *\n * @param value - Value to check\n * @returns Whether value is a valid theme preference\n */\nexport function isValidThemePreference(\n value: unknown\n): value is ThemePreference {\n return value === 'light' || value === 'dark' || value === 'system';\n}\n"]}
|
|
1
|
+
{"version":3,"file":"theme.js","sourceRoot":"","sources":["../../src/utils/theme.ts"],"names":[],"mappings":"AAAA;;GAEG;AAQH,uCAAuC;AACvC,MAAM,CAAC,MAAM,iBAAiB,GAAG,cAAc,CAAC;AAEhD,8FAA8F;AAC9F,MAAM,CAAC,MAAM,qBAAqB,GAAG,kBAAkB,CAAC;AAExD;;;;GAIG;AACH,MAAM,UAAU,cAAc;IAC5B,IAAI,OAAO,MAAM,KAAK,WAAW;QAAE,OAAO,OAAO,CAAC;IAClD,OAAO,MAAM,CAAC,UAAU,CAAC,8BAA8B,CAAC,CAAC,OAAO;QAC9D,CAAC,CAAC,MAAM;QACR,CAAC,CAAC,OAAO,CAAC;AACd,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,YAAY,CAC1B,UAA2B,EAC3B,WAA0B;IAE1B,IAAI,UAAU,KAAK,QAAQ,EAAE,CAAC;QAC5B,OAAO,WAAW,CAAC;IACrB,CAAC;IACD,OAAO,UAAU,CAAC;AACpB,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,sBAAsB,CACpC,KAAc;IAEd,OAAO,KAAK,KAAK,OAAO,IAAI,KAAK,KAAK,MAAM,IAAI,KAAK,KAAK,QAAQ,CAAC;AACrE,CAAC","sourcesContent":["/**\n * Theme utilities\n */\n\n/** Available theme options */\nexport type ThemePreference = 'light' | 'dark' | 'system';\n\n/** Resolved theme (without system option) */\nexport type ResolvedTheme = 'light' | 'dark';\n\n/** Storage key for theme preference */\nexport const THEME_STORAGE_KEY = 'sudojo-theme';\n\n/** Storage key for the font size preference (the web ThemeProvider's `fontSizeStorageKey`) */\nexport const FONT_SIZE_STORAGE_KEY = 'sudojo-font-size';\n\n/**\n * Detect system color scheme preference\n *\n * @returns 'dark' if system prefers dark mode, 'light' otherwise\n */\nexport function getSystemTheme(): ResolvedTheme {\n if (typeof window === 'undefined') return 'light';\n return window.matchMedia('(prefers-color-scheme: dark)').matches\n ? 'dark'\n : 'light';\n}\n\n/**\n * Resolve theme preference to actual theme\n *\n * @param preference - User's theme preference\n * @param systemTheme - Current system theme\n * @returns Resolved theme ('light' or 'dark')\n */\nexport function resolveTheme(\n preference: ThemePreference,\n systemTheme: ResolvedTheme\n): ResolvedTheme {\n if (preference === 'system') {\n return systemTheme;\n }\n return preference;\n}\n\n/**\n * Check if a value is a valid theme preference\n *\n * @param value - Value to check\n * @returns Whether value is a valid theme preference\n */\nexport function isValidThemePreference(\n value: unknown\n): value is ThemePreference {\n return value === 'light' || value === 'dark' || value === 'system';\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sudobility/sudojo_lib",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.203",
|
|
4
4
|
"description": "Business logic library for Sudojo app",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -29,8 +29,8 @@
|
|
|
29
29
|
"test:watch": "vitest --watch",
|
|
30
30
|
"lint": "eslint src --ext .ts,.tsx",
|
|
31
31
|
"lint:fix": "eslint src --ext .ts,.tsx --fix",
|
|
32
|
-
"format": "prettier --write src/**/*.ts",
|
|
33
|
-
"format:check": "prettier --check src/**/*.ts",
|
|
32
|
+
"format": "prettier --write \"src/**/*.ts\"",
|
|
33
|
+
"format:check": "prettier --check \"src/**/*.ts\"",
|
|
34
34
|
"typecheck": "tsc --noEmit",
|
|
35
35
|
"typecheck:watch": "tsc --noEmit --watch",
|
|
36
36
|
"check-all": "bun run lint && bun run typecheck && bun run test:unit",
|
|
@@ -47,8 +47,8 @@
|
|
|
47
47
|
"license": "BUSL-1.1",
|
|
48
48
|
"devDependencies": {
|
|
49
49
|
"@sudobility/di": "^1.5.68",
|
|
50
|
-
"@sudobility/sudojo_client": "^0.0.
|
|
51
|
-
"@sudobility/sudojo_types": "^1.2.
|
|
50
|
+
"@sudobility/sudojo_client": "^0.0.162",
|
|
51
|
+
"@sudobility/sudojo_types": "^1.2.72",
|
|
52
52
|
"@sudobility/types": "^1.9.68",
|
|
53
53
|
"@tanstack/react-query": "^5.90.11",
|
|
54
54
|
"@testing-library/dom": "^10.4.1",
|
|
@@ -73,8 +73,8 @@
|
|
|
73
73
|
},
|
|
74
74
|
"peerDependencies": {
|
|
75
75
|
"@sudobility/di": "^1.5.68",
|
|
76
|
-
"@sudobility/sudojo_client": "^0.0.
|
|
77
|
-
"@sudobility/sudojo_types": "^1.2.
|
|
76
|
+
"@sudobility/sudojo_client": "^0.0.162",
|
|
77
|
+
"@sudobility/sudojo_types": "^1.2.72",
|
|
78
78
|
"@sudobility/types": "^1.9.68",
|
|
79
79
|
"@tanstack/react-query": ">=5.0.0",
|
|
80
80
|
"react": ">=18.0.0",
|