@jjlmoya/utils-cooking 1.38.0 → 1.40.0

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.
Files changed (63) hide show
  1. package/package.json +1 -1
  2. package/scripts/fix-translations.js +51 -0
  3. package/src/category/index.ts +5 -0
  4. package/src/entries.ts +6 -1
  5. package/src/index.ts +3 -0
  6. package/src/tests/i18n-titles.test.ts +2 -2
  7. package/src/tests/locale_completeness.test.ts +2 -2
  8. package/src/tests/sous-vide-pasteurization-curves.test.ts +66 -0
  9. package/src/tests/tool_validation.test.ts +2 -2
  10. package/src/tool/pectin-jam-setting-calculator/bibliography.astro +6 -0
  11. package/src/tool/pectin-jam-setting-calculator/bibliography.ts +10 -0
  12. package/src/tool/pectin-jam-setting-calculator/component.astro +170 -0
  13. package/src/tool/pectin-jam-setting-calculator/components/CalculatorInputs.astro +44 -0
  14. package/src/tool/pectin-jam-setting-calculator/components/DropTestVisualizer.astro +40 -0
  15. package/src/tool/pectin-jam-setting-calculator/components/FruitSelector.astro +38 -0
  16. package/src/tool/pectin-jam-setting-calculator/components/RecipeResults.astro +72 -0
  17. package/src/tool/pectin-jam-setting-calculator/entry.ts +26 -0
  18. package/src/tool/pectin-jam-setting-calculator/i18n/de.ts +248 -0
  19. package/src/tool/pectin-jam-setting-calculator/i18n/en.ts +248 -0
  20. package/src/tool/pectin-jam-setting-calculator/i18n/es.ts +248 -0
  21. package/src/tool/pectin-jam-setting-calculator/i18n/fr.ts +248 -0
  22. package/src/tool/pectin-jam-setting-calculator/i18n/id.ts +248 -0
  23. package/src/tool/pectin-jam-setting-calculator/i18n/it.ts +248 -0
  24. package/src/tool/pectin-jam-setting-calculator/i18n/ja.ts +248 -0
  25. package/src/tool/pectin-jam-setting-calculator/i18n/ko.ts +248 -0
  26. package/src/tool/pectin-jam-setting-calculator/i18n/nl.ts +248 -0
  27. package/src/tool/pectin-jam-setting-calculator/i18n/pl.ts +248 -0
  28. package/src/tool/pectin-jam-setting-calculator/i18n/pt.ts +248 -0
  29. package/src/tool/pectin-jam-setting-calculator/i18n/ru.ts +248 -0
  30. package/src/tool/pectin-jam-setting-calculator/i18n/sv.ts +248 -0
  31. package/src/tool/pectin-jam-setting-calculator/i18n/tr.ts +248 -0
  32. package/src/tool/pectin-jam-setting-calculator/i18n/zh.ts +248 -0
  33. package/src/tool/pectin-jam-setting-calculator/index.ts +11 -0
  34. package/src/tool/pectin-jam-setting-calculator/logic.ts +96 -0
  35. package/src/tool/pectin-jam-setting-calculator/pectin-jam-setting-calculator.css +730 -0
  36. package/src/tool/pectin-jam-setting-calculator/seo.astro +15 -0
  37. package/src/tool/sous-vide-pasteurization-curves/bibliography.astro +6 -0
  38. package/src/tool/sous-vide-pasteurization-curves/bibliography.ts +10 -0
  39. package/src/tool/sous-vide-pasteurization-curves/component.astro +275 -0
  40. package/src/tool/sous-vide-pasteurization-curves/components/Controls.astro +90 -0
  41. package/src/tool/sous-vide-pasteurization-curves/components/LethalityChart.astro +28 -0
  42. package/src/tool/sous-vide-pasteurization-curves/components/ResultsDisplay.astro +36 -0
  43. package/src/tool/sous-vide-pasteurization-curves/entry.ts +26 -0
  44. package/src/tool/sous-vide-pasteurization-curves/i18n/de.ts +323 -0
  45. package/src/tool/sous-vide-pasteurization-curves/i18n/en.ts +323 -0
  46. package/src/tool/sous-vide-pasteurization-curves/i18n/es.ts +323 -0
  47. package/src/tool/sous-vide-pasteurization-curves/i18n/fr.ts +323 -0
  48. package/src/tool/sous-vide-pasteurization-curves/i18n/id.ts +323 -0
  49. package/src/tool/sous-vide-pasteurization-curves/i18n/it.ts +323 -0
  50. package/src/tool/sous-vide-pasteurization-curves/i18n/ja.ts +323 -0
  51. package/src/tool/sous-vide-pasteurization-curves/i18n/ko.ts +323 -0
  52. package/src/tool/sous-vide-pasteurization-curves/i18n/nl.ts +323 -0
  53. package/src/tool/sous-vide-pasteurization-curves/i18n/pl.ts +154 -0
  54. package/src/tool/sous-vide-pasteurization-curves/i18n/pt.ts +323 -0
  55. package/src/tool/sous-vide-pasteurization-curves/i18n/ru.ts +154 -0
  56. package/src/tool/sous-vide-pasteurization-curves/i18n/sv.ts +154 -0
  57. package/src/tool/sous-vide-pasteurization-curves/i18n/tr.ts +154 -0
  58. package/src/tool/sous-vide-pasteurization-curves/i18n/zh.ts +323 -0
  59. package/src/tool/sous-vide-pasteurization-curves/index.ts +11 -0
  60. package/src/tool/sous-vide-pasteurization-curves/logic.ts +89 -0
  61. package/src/tool/sous-vide-pasteurization-curves/seo.astro +15 -0
  62. package/src/tool/sous-vide-pasteurization-curves/sous-vide-pasteurization-curves.css +456 -0
  63. package/src/tools.ts +5 -0
@@ -0,0 +1,456 @@
1
+ .sv-container {
2
+ --sv-bg-grad-start: rgba(6, 182, 212, 0.05);
3
+ --sv-bg-grad-end: rgba(248, 250, 252, 0.98);
4
+ --sv-border-main: rgba(6, 182, 212, 0.15);
5
+ --sv-shadow: 0 15px 30px -10px rgba(15, 23, 42, 0.08);
6
+ --sv-text-main: #334155;
7
+ --sv-text-muted: #64748b;
8
+ --sv-card-bg: rgba(255, 255, 255, 0.65);
9
+ --sv-card-border: rgba(6, 182, 212, 0.12);
10
+ --sv-toggle-bg: rgba(15, 23, 42, 0.03);
11
+ --sv-toggle-active-bg: #06b6d4;
12
+ --sv-toggle-active-color: #fff;
13
+ --sv-grid-line-color: rgba(15, 23, 42, 0.05);
14
+ --sv-axis-text-color: #64748b;
15
+ --sv-chart-bg: rgba(255, 255, 255, 0.8);
16
+ --sv-chart-border: rgba(6, 182, 212, 0.1);
17
+ --sv-cyan-badge: #0891b2;
18
+ --sv-slider-track: rgba(15, 23, 42, 0.08);
19
+ --sv-color-slate-900: #0f172a;
20
+ --sv-color-red-500: #ef4444;
21
+ --sv-color-red-300: #fca5a5;
22
+
23
+ display: flex;
24
+ flex-direction: column;
25
+ gap: 2rem;
26
+ width: 100%;
27
+ max-width: 1000px;
28
+ margin: 0 auto;
29
+ color: var(--sv-text-main);
30
+ }
31
+
32
+ .theme-dark .sv-container {
33
+ --sv-bg-grad-start: rgba(6, 182, 212, 0.12);
34
+ --sv-bg-grad-end: rgba(15, 23, 42, 0.98);
35
+ --sv-border-main: rgba(6, 182, 212, 0.25);
36
+ --sv-shadow: 0 20px 40px -15px rgba(0, 0, 0, 0.6);
37
+ --sv-text-main: #e2e8f0;
38
+ --sv-text-muted: #94a3b8;
39
+ --sv-card-bg: rgba(30, 41, 59, 0.45);
40
+ --sv-card-border: rgba(255, 255, 255, 0.08);
41
+ --sv-toggle-bg: rgba(15, 23, 42, 0.4);
42
+ --sv-toggle-active-bg: #06b6d4;
43
+ --sv-toggle-active-color: #0f172a;
44
+ --sv-grid-line-color: rgba(226, 232, 240, 0.06);
45
+ --sv-axis-text-color: #94a3b8;
46
+ --sv-chart-bg: rgba(15, 23, 42, 0.5);
47
+ --sv-chart-border: rgba(6, 182, 212, 0.15);
48
+ --sv-cyan-badge: #06b6d4;
49
+ --sv-slider-track: rgba(226, 232, 240, 0.1);
50
+ }
51
+
52
+ .sv-dashboard {
53
+ position: relative;
54
+ background: radial-gradient(circle at 50% 0%, var(--sv-bg-grad-start) 0%, var(--sv-bg-grad-end) 100%);
55
+ border: 1px solid var(--sv-border-main);
56
+ border-radius: 1.25rem;
57
+ padding: 2.5rem;
58
+ overflow: hidden;
59
+ box-shadow: var(--sv-shadow);
60
+ display: flex;
61
+ flex-direction: column;
62
+ gap: 2rem;
63
+ }
64
+
65
+ .sv-bg-particles {
66
+ position: absolute;
67
+ top: 0;
68
+ left: 0;
69
+ width: 100%;
70
+ height: 100%;
71
+ pointer-events: none;
72
+ z-index: 0;
73
+ opacity: 0.6;
74
+ }
75
+
76
+ .sv-bg-bubble {
77
+ position: absolute;
78
+ bottom: -20px;
79
+ background: radial-gradient(circle, rgba(6, 182, 212, 0.4) 0%, rgba(6, 182, 212, 0) 70%);
80
+ border-radius: 50%;
81
+ animation: sv-bubble-float 8s infinite linear;
82
+ }
83
+
84
+ @keyframes sv-bubble-float {
85
+ 0% {
86
+ transform: translateY(0) scale(0.5);
87
+ opacity: 0;
88
+ }
89
+ 10% {
90
+ opacity: 0.6;
91
+ }
92
+ 90% {
93
+ opacity: 0.6;
94
+ }
95
+ 100% {
96
+ transform: translateY(-500px) scale(1.2);
97
+ opacity: 0;
98
+ }
99
+ }
100
+
101
+ .sv-row-top {
102
+ position: relative;
103
+ z-index: 1;
104
+ width: 100%;
105
+ }
106
+
107
+ .sv-row-bottom {
108
+ position: relative;
109
+ z-index: 1;
110
+ display: grid;
111
+ grid-template-columns: 1fr;
112
+ gap: 2rem;
113
+ }
114
+
115
+ @media (min-width: 768px) {
116
+ .sv-row-bottom {
117
+ grid-template-columns: 1fr 1fr;
118
+ }
119
+ }
120
+
121
+ .sv-chart-panel {
122
+ background: var(--sv-chart-bg);
123
+ border: 1px solid var(--sv-chart-border);
124
+ border-radius: 1rem;
125
+ padding: 1.5rem;
126
+ display: flex;
127
+ flex-direction: column;
128
+ gap: 1rem;
129
+ backdrop-filter: blur(12px);
130
+ width: 100%;
131
+ box-sizing: border-box;
132
+ }
133
+
134
+ .sv-chart-title {
135
+ font-size: 1.1rem;
136
+ font-weight: 700;
137
+ color: var(--sv-cyan-badge);
138
+ letter-spacing: 0.05em;
139
+ text-transform: uppercase;
140
+ margin: 0;
141
+ }
142
+
143
+ .sv-svg-chart {
144
+ width: 100%;
145
+ max-height: 280px;
146
+ overflow: visible;
147
+ }
148
+
149
+ .sv-curve-path {
150
+ fill: none;
151
+ stroke: var(--sv-cyan-badge);
152
+ stroke-width: 3;
153
+ stroke-linecap: round;
154
+ filter: drop-shadow(0 0 8px rgba(6, 182, 212, 0.8));
155
+ }
156
+
157
+ .sv-grid-line {
158
+ stroke: var(--sv-grid-line-color);
159
+ stroke-width: 1;
160
+ }
161
+
162
+ .sv-axis-text {
163
+ fill: var(--sv-axis-text-color);
164
+ font-size: 10px;
165
+ }
166
+
167
+ .sv-controls-panel {
168
+ display: flex;
169
+ flex-direction: column;
170
+ gap: 1.5rem;
171
+ }
172
+
173
+ .sv-glass-card {
174
+ background: var(--sv-card-bg);
175
+ border: 1px solid var(--sv-card-border);
176
+ border-radius: 1rem;
177
+ padding: 1.5rem;
178
+ backdrop-filter: blur(16px);
179
+ position: relative;
180
+ overflow: hidden;
181
+ box-shadow: 0 4px 20px rgba(0, 0, 0, 0.02);
182
+ }
183
+
184
+ .theme-dark .sv-glass-card {
185
+ box-shadow: none;
186
+ }
187
+
188
+ .sv-glass-card::before {
189
+ content: '';
190
+ position: absolute;
191
+ top: 0;
192
+ left: -150%;
193
+ width: 50%;
194
+ height: 100%;
195
+ background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.05), transparent);
196
+ transform: skewX(-25deg);
197
+ animation: sv-shine 6s infinite linear;
198
+ }
199
+
200
+ @keyframes sv-shine {
201
+ 0% {
202
+ left: -150%;
203
+ }
204
+ 30% {
205
+ left: 150%;
206
+ }
207
+ 100% {
208
+ left: 150%;
209
+ }
210
+ }
211
+
212
+ .sv-input-group {
213
+ display: flex;
214
+ flex-direction: column;
215
+ gap: 0.75rem;
216
+ }
217
+
218
+ .sv-label-container {
219
+ display: flex;
220
+ justify-content: space-between;
221
+ align-items: center;
222
+ }
223
+
224
+ .sv-label {
225
+ font-weight: 600;
226
+ font-size: 0.9rem;
227
+ color: var(--sv-text-muted);
228
+ text-transform: uppercase;
229
+ letter-spacing: 0.05em;
230
+ }
231
+
232
+ .sv-value-badge {
233
+ font-weight: 800;
234
+ font-size: 1.25rem;
235
+ color: var(--sv-cyan-badge);
236
+ text-shadow: 0 0 10px rgba(6, 182, 212, 0.2);
237
+ }
238
+
239
+ .sv-slider {
240
+ -webkit-appearance: none;
241
+ appearance: none;
242
+ width: 100%;
243
+ height: 6px;
244
+ border-radius: 99px;
245
+ background: var(--sv-slider-track);
246
+ outline: none;
247
+ }
248
+
249
+ .sv-slider::-webkit-slider-thumb {
250
+ -webkit-appearance: none;
251
+ appearance: none;
252
+ width: 18px;
253
+ height: 18px;
254
+ border-radius: 50%;
255
+ background: var(--sv-toggle-active-bg);
256
+ cursor: pointer;
257
+ border: 2px solid var(--sv-toggle-active-color);
258
+ box-shadow: 0 0 10px rgba(6, 182, 212, 0.4);
259
+ transition: transform 0.1s ease;
260
+ }
261
+
262
+ .sv-slider::-webkit-slider-thumb:hover {
263
+ transform: scale(1.2);
264
+ }
265
+
266
+ .sv-toggle-grid {
267
+ display: grid;
268
+ grid-template-columns: repeat(3, 1fr);
269
+ gap: 0.5rem;
270
+ background: var(--sv-toggle-bg);
271
+ padding: 0.25rem;
272
+ border-radius: 0.5rem;
273
+ border: 1px solid rgba(255, 255, 255, 0.05);
274
+ }
275
+
276
+ #sv-system-toggle {
277
+ grid-template-columns: repeat(2, 1fr);
278
+ }
279
+
280
+ #sv-pathogen-toggle {
281
+ grid-template-columns: repeat(2, 1fr);
282
+ }
283
+
284
+ .sv-toggle-btn {
285
+ background: none;
286
+ border: none;
287
+ color: var(--sv-text-muted);
288
+ padding: 0.5rem;
289
+ font-size: 0.8rem;
290
+ font-weight: 600;
291
+ border-radius: 0.375rem;
292
+ cursor: pointer;
293
+ transition: all 0.2s ease;
294
+ text-align: center;
295
+ }
296
+
297
+ .sv-toggle-btn:hover {
298
+ color: var(--sv-text-main);
299
+ background: rgba(255, 255, 255, 0.05);
300
+ }
301
+
302
+ .sv-toggle-btn.active {
303
+ color: var(--sv-toggle-active-color);
304
+ background: var(--sv-toggle-active-bg);
305
+ box-shadow: 0 0 12px rgba(6, 182, 212, 0.4);
306
+ }
307
+
308
+ .sv-pathogen-grid {
309
+ display: grid;
310
+ grid-template-columns: repeat(2, 1fr);
311
+ gap: 0.5rem;
312
+ }
313
+
314
+ .sv-results-panel {
315
+ display: flex;
316
+ flex-direction: column;
317
+ gap: 2rem;
318
+ align-items: stretch;
319
+ }
320
+
321
+ .sv-results-hero {
322
+ display: flex;
323
+ flex-direction: column;
324
+ align-items: center;
325
+ justify-content: center;
326
+ gap: 0.75rem;
327
+ margin: 1rem 0;
328
+ }
329
+
330
+ .sv-hero-ring {
331
+ width: 120px;
332
+ height: 120px;
333
+ border-radius: 50%;
334
+ border: 4px solid var(--sv-cyan-badge);
335
+ display: flex;
336
+ flex-direction: column;
337
+ align-items: center;
338
+ justify-content: center;
339
+ box-shadow: 0 0 15px rgba(6, 182, 212, 0.15);
340
+ background: rgba(6, 182, 212, 0.02);
341
+ }
342
+
343
+ .sv-hero-value {
344
+ font-size: 2.5rem;
345
+ font-weight: 900;
346
+ color: var(--sv-cyan-badge);
347
+ line-height: 1;
348
+ }
349
+
350
+ .sv-hero-unit {
351
+ font-size: 0.8rem;
352
+ font-weight: 700;
353
+ text-transform: uppercase;
354
+ color: var(--sv-text-muted);
355
+ }
356
+
357
+ .sv-hero-label {
358
+ font-size: 0.95rem;
359
+ font-weight: 700;
360
+ text-transform: uppercase;
361
+ letter-spacing: 0.08em;
362
+ color: var(--sv-text-main);
363
+ }
364
+
365
+ .sv-results-grid {
366
+ display: grid;
367
+ grid-template-columns: 1fr 1fr;
368
+ gap: 1rem;
369
+ }
370
+
371
+ .sv-result-card {
372
+ background: rgba(15, 23, 42, 0.02);
373
+ border: 1px solid var(--sv-card-border);
374
+ border-radius: 0.75rem;
375
+ padding: 1rem;
376
+ display: flex;
377
+ flex-direction: column;
378
+ align-items: center;
379
+ justify-content: center;
380
+ gap: 0.5rem;
381
+ text-align: center;
382
+ }
383
+
384
+ .theme-dark .sv-result-card {
385
+ background: rgba(255, 255, 255, 0.02);
386
+ }
387
+
388
+ .sv-card-metric-label {
389
+ font-size: 0.75rem;
390
+ font-weight: 600;
391
+ text-transform: uppercase;
392
+ color: var(--sv-text-muted);
393
+ letter-spacing: 0.05em;
394
+ line-height: 1.3;
395
+ }
396
+
397
+ .sv-card-metric-value {
398
+ font-size: 1.25rem;
399
+ font-weight: 800;
400
+ color: var(--sv-text-main);
401
+ }
402
+
403
+ .sv-danger-alert {
404
+ background: rgba(239, 68, 68, 0.1);
405
+ border: 1px solid rgba(239, 68, 68, 0.25);
406
+ border-radius: 0.75rem;
407
+ padding: 1rem;
408
+ margin-top: 1rem;
409
+ display: flex;
410
+ flex-direction: column;
411
+ gap: 0.5rem;
412
+ animation: sv-pulse-danger 2s infinite ease-in-out;
413
+ }
414
+
415
+ @keyframes sv-pulse-danger {
416
+ 0% {
417
+ border-color: rgba(239, 68, 68, 0.2);
418
+ box-shadow: 0 0 0 rgba(239, 68, 68, 0);
419
+ }
420
+ 50% {
421
+ border-color: rgba(239, 68, 68, 0.5);
422
+ box-shadow: 0 0 15px rgba(239, 68, 68, 0.15);
423
+ }
424
+ 100% {
425
+ border-color: rgba(239, 68, 68, 0.2);
426
+ box-shadow: 0 0 0 rgba(239, 68, 68, 0);
427
+ }
428
+ }
429
+
430
+ .sv-danger-title {
431
+ color: var(--sv-color-red-500);
432
+ font-weight: 700;
433
+ font-size: 0.95rem;
434
+ text-transform: uppercase;
435
+ letter-spacing: 0.05em;
436
+ margin: 0;
437
+ }
438
+
439
+ .sv-danger-desc {
440
+ color: var(--sv-color-red-500);
441
+ font-size: 0.8rem;
442
+ line-height: 1.4;
443
+ margin: 0;
444
+ }
445
+
446
+ .theme-dark .sv-danger-desc {
447
+ color: var(--sv-color-red-300);
448
+ }
449
+
450
+ .sv-disclaimer {
451
+ font-size: 0.75rem;
452
+ color: var(--sv-text-muted);
453
+ line-height: 1.5;
454
+ text-align: center;
455
+ margin-top: 1rem;
456
+ }
package/src/tools.ts CHANGED
@@ -24,6 +24,8 @@ import { MACARON_DRYING_TOOL } from './tool/macaron-drying-predictor';
24
24
  import { BRIX_SORBET_DENSITY_CALCULATOR_TOOL } from './tool/brix-sorbet-density-calculator';
25
25
  import { OIL_SMOKE_POINT_TRACKER_TOOL } from './tool/oil-smoke-point-tracker';
26
26
  import { LEAVENER_ACID_NEUTRALIZER_TOOL } from './tool/leavener-acid-neutralizer';
27
+ import { PECTIN_JAM_SETTING_TOOL } from './tool/pectin-jam-setting-calculator';
28
+ import { SOUS_VIDE_PASTEURIZATION_TOOL } from './tool/sous-vide-pasteurization-curves';
27
29
 
28
30
  export const ALL_TOOLS: ToolDefinition[] = [
29
31
  AMERICAN_KITCHEN_CONVERTER_TOOL,
@@ -50,5 +52,8 @@ export const ALL_TOOLS: ToolDefinition[] = [
50
52
  BRIX_SORBET_DENSITY_CALCULATOR_TOOL,
51
53
  OIL_SMOKE_POINT_TRACKER_TOOL,
52
54
  LEAVENER_ACID_NEUTRALIZER_TOOL,
55
+ PECTIN_JAM_SETTING_TOOL,
56
+ SOUS_VIDE_PASTEURIZATION_TOOL,
53
57
  ];
54
58
 
59
+