@jjlmoya/utils-creative 1.2.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 (71) hide show
  1. package/package.json +64 -0
  2. package/src/category/i18n/en.ts +9 -0
  3. package/src/category/i18n/es.ts +9 -0
  4. package/src/category/i18n/fr.ts +9 -0
  5. package/src/category/index.ts +34 -0
  6. package/src/category/seo.astro +15 -0
  7. package/src/components/PreviewNavSidebar.astro +116 -0
  8. package/src/components/PreviewToolbar.astro +143 -0
  9. package/src/data.ts +6 -0
  10. package/src/env.d.ts +5 -0
  11. package/src/index.ts +27 -0
  12. package/src/layouts/PreviewLayout.astro +117 -0
  13. package/src/pages/[locale]/[slug].astro +146 -0
  14. package/src/pages/[locale].astro +251 -0
  15. package/src/pages/index.astro +4 -0
  16. package/src/tests/faq_count.test.ts +19 -0
  17. package/src/tests/locale_completeness.test.ts +42 -0
  18. package/src/tests/mocks/astro_mock.js +2 -0
  19. package/src/tests/no_h1_in_components.test.ts +48 -0
  20. package/src/tests/seo_length.test.ts +22 -0
  21. package/src/tests/tool_validation.test.ts +17 -0
  22. package/src/tool/bead-pattern-generator/bibliography.astro +18 -0
  23. package/src/tool/bead-pattern-generator/component.astro +372 -0
  24. package/src/tool/bead-pattern-generator/i18n/en.ts +61 -0
  25. package/src/tool/bead-pattern-generator/i18n/es.ts +68 -0
  26. package/src/tool/bead-pattern-generator/i18n/fr.ts +61 -0
  27. package/src/tool/bead-pattern-generator/index.ts +37 -0
  28. package/src/tool/bead-pattern-generator/seo.astro +14 -0
  29. package/src/tool/bead-pattern-generator/style.css +511 -0
  30. package/src/tool/dice-roller/bibliography.astro +17 -0
  31. package/src/tool/dice-roller/component.astro +230 -0
  32. package/src/tool/dice-roller/i18n/en.ts +87 -0
  33. package/src/tool/dice-roller/i18n/es.ts +89 -0
  34. package/src/tool/dice-roller/i18n/fr.ts +87 -0
  35. package/src/tool/dice-roller/index.ts +37 -0
  36. package/src/tool/dice-roller/seo.astro +14 -0
  37. package/src/tool/dice-roller/style.css +482 -0
  38. package/src/tool/excuse-generator/bibliography.astro +18 -0
  39. package/src/tool/excuse-generator/component.astro +140 -0
  40. package/src/tool/excuse-generator/i18n/en.ts +80 -0
  41. package/src/tool/excuse-generator/i18n/es.ts +84 -0
  42. package/src/tool/excuse-generator/i18n/fr.ts +80 -0
  43. package/src/tool/excuse-generator/index.ts +42 -0
  44. package/src/tool/excuse-generator/seo.astro +14 -0
  45. package/src/tool/excuse-generator/style.css +316 -0
  46. package/src/tool/fortune-cookie/bibliography.astro +18 -0
  47. package/src/tool/fortune-cookie/component.astro +299 -0
  48. package/src/tool/fortune-cookie/i18n/en.ts +85 -0
  49. package/src/tool/fortune-cookie/i18n/es.ts +90 -0
  50. package/src/tool/fortune-cookie/i18n/fr.ts +85 -0
  51. package/src/tool/fortune-cookie/index.ts +40 -0
  52. package/src/tool/fortune-cookie/seo.astro +14 -0
  53. package/src/tool/fortune-cookie/style.css +332 -0
  54. package/src/tool/synesthesia-painter/bibliography.astro +17 -0
  55. package/src/tool/synesthesia-painter/component.astro +110 -0
  56. package/src/tool/synesthesia-painter/i18n/en.ts +80 -0
  57. package/src/tool/synesthesia-painter/i18n/es.ts +82 -0
  58. package/src/tool/synesthesia-painter/i18n/fr.ts +80 -0
  59. package/src/tool/synesthesia-painter/index.ts +39 -0
  60. package/src/tool/synesthesia-painter/seo.astro +14 -0
  61. package/src/tool/synesthesia-painter/style.css +234 -0
  62. package/src/tool/zalgo-generator/bibliography.astro +18 -0
  63. package/src/tool/zalgo-generator/component.astro +195 -0
  64. package/src/tool/zalgo-generator/i18n/en.ts +60 -0
  65. package/src/tool/zalgo-generator/i18n/es.ts +67 -0
  66. package/src/tool/zalgo-generator/i18n/fr.ts +60 -0
  67. package/src/tool/zalgo-generator/index.ts +38 -0
  68. package/src/tool/zalgo-generator/seo.astro +14 -0
  69. package/src/tool/zalgo-generator/style.css +558 -0
  70. package/src/tools.ts +4 -0
  71. package/src/types.ts +72 -0
@@ -0,0 +1,299 @@
1
+ ---
2
+ import { Icon } from 'astro-icon/components';
3
+ import './style.css';
4
+ import type { FortuneCookieUI } from './index';
5
+
6
+ interface Props {
7
+ ui: FortuneCookieUI;
8
+ }
9
+
10
+ const { ui } = Astro.props;
11
+
12
+ const ASSETS = {
13
+ whole: '/images/utilities/fortune/fc-whole.webp',
14
+ cracked1: '/images/utilities/fortune/fc-crack-1.webp',
15
+ cracked2: '/images/utilities/fortune/fc-crack-2.webp',
16
+ left: '/images/utilities/fortune/fc-left.webp',
17
+ right: '/images/utilities/fortune/fc-right.webp',
18
+ paper: '/images/utilities/fortune/fc-paper.webp',
19
+ crumbs: '/images/utilities/fortune/fc-crumbs.webp',
20
+ };
21
+ ---
22
+
23
+ <div id="fortune-cookie-root" class="fortune-cookie" data-ui={JSON.stringify(ui)}>
24
+ <div class="fortune-cookie-root">
25
+ <div class="fortune-cookie-card">
26
+ <div class="fortune-cookie-glow"></div>
27
+
28
+ <div class="fortune-cookie-content">
29
+ <h2 id="fortune-instruction" class="fortune-cookie-instruction">
30
+ {ui.instruction}
31
+ </h2>
32
+
33
+ <div id="fortune-cookie-stage" class="fortune-cookie-stage">
34
+ <div id="fortune-cookie-body" class="fortune-cookie-body">
35
+ <img
36
+ src={ASSETS.whole}
37
+ alt="Fortune Cookie"
38
+ class="fortune-cookie-img"
39
+ id="fortune-cookie-img"
40
+ loading="eager"
41
+ />
42
+ </div>
43
+
44
+ <div id="fortune-cookie-broken" class="fortune-cookie-broken hidden">
45
+ <img
46
+ src={ASSETS.left}
47
+ class="fortune-cookie-half fortune-cookie-half-left"
48
+ id="fortune-half-left"
49
+ loading="eager"
50
+ />
51
+ <img
52
+ src={ASSETS.right}
53
+ class="fortune-cookie-half fortune-cookie-half-right"
54
+ id="fortune-half-right"
55
+ loading="eager"
56
+ />
57
+ </div>
58
+
59
+ <div id="fortune-paper" class="fortune-cookie-paper">
60
+ <img src={ASSETS.paper} class="fortune-cookie-paper-img" />
61
+
62
+ <div class="fortune-cookie-paper-content">
63
+ <p id="fortune-text" class="fortune-cookie-text">
64
+ "..."
65
+ </p>
66
+ <div id="fortune-lucky-numbers" class="fortune-cookie-lucky-numbers"></div>
67
+ </div>
68
+ </div>
69
+
70
+ <div id="fortune-particles" class="fortune-cookie-particles"></div>
71
+ </div>
72
+
73
+ <div id="fortune-reset-ui" class="fortune-cookie-reset">
74
+ <p class="fortune-cookie-status">{ui.dailyStatus}</p>
75
+ <button id="fortune-share-btn" class="fortune-cookie-share-btn">
76
+ <Icon name="mdi:share-variant" /> {ui.shareBtn}
77
+ </button>
78
+ </div>
79
+ </div>
80
+ </div>
81
+ </div>
82
+ </div>
83
+
84
+ <script>
85
+ import type { FortuneCookieUI } from './index';
86
+
87
+ const root = document.getElementById('fortune-cookie-root') as HTMLElement;
88
+ const ui = JSON.parse(root.dataset.ui || '{}') as FortuneCookieUI;
89
+
90
+ const fortunes = JSON.parse(ui.fortunes || '[]') as string[];
91
+
92
+ function getLuckyNumbers() {
93
+ const nums = new Set<number>();
94
+ while (nums.size < 6) {
95
+ nums.add(Math.floor(Math.random() * 49) + 1);
96
+ }
97
+ return Array.from(nums).sort((a, b) => a - b);
98
+ }
99
+
100
+ class FortuneCookie {
101
+ private health = 3;
102
+ private isBroken = false;
103
+ private el = {
104
+ stage: document.getElementById('fortune-cookie-stage')!,
105
+ body: document.getElementById('fortune-cookie-body')!,
106
+ img: document.getElementById('fortune-cookie-img') as HTMLImageElement,
107
+ broken: document.getElementById('fortune-cookie-broken')!,
108
+ halfLeft: document.getElementById('fortune-half-left') as HTMLElement,
109
+ halfRight: document.getElementById('fortune-half-right') as HTMLElement,
110
+ paper: document.getElementById('fortune-paper')!,
111
+ text: document.getElementById('fortune-text')!,
112
+ numbers: document.getElementById('fortune-lucky-numbers')!,
113
+ instruction: document.getElementById('fortune-instruction')!,
114
+ particles: document.getElementById('fortune-particles')!,
115
+ resetUi: document.getElementById('fortune-reset-ui')!,
116
+ shareBtn: document.getElementById('fortune-share-btn') as HTMLButtonElement,
117
+ };
118
+
119
+ private assets = {
120
+ cracked1: '/images/utilities/fortune/fc-crack-1.webp',
121
+ cracked2: '/images/utilities/fortune/fc-crack-2.webp',
122
+ };
123
+
124
+ constructor() {
125
+ this.checkDaily();
126
+ this.el.stage.addEventListener('click', () => this.tap());
127
+ this.el.shareBtn?.addEventListener('click', () => this.shareFortune());
128
+ }
129
+
130
+ private async shareFortune() {
131
+ const text = this.el.text.innerText;
132
+ const nums = this.el.numbers.innerText.replace(/\n/g, ' ');
133
+
134
+ const shareTextCompiled = (ui.shareText || '')
135
+ .replace('$TEXT', text)
136
+ .replace('$NUMS', nums);
137
+
138
+ const shareData = {
139
+ title: ui.shareTitle || '',
140
+ text: shareTextCompiled,
141
+ url: window.location.href,
142
+ };
143
+
144
+ try {
145
+ if (navigator.share) {
146
+ await navigator.share(shareData);
147
+ } else {
148
+ await navigator.clipboard.writeText(`${shareData.text} ${shareData.url}`);
149
+ const originalText = this.el.shareBtn.innerHTML;
150
+ this.el.shareBtn.innerHTML = `<span>${ui.copied || ''}</span>`;
151
+ setTimeout(() => {
152
+ this.el.shareBtn.innerHTML = originalText;
153
+ }, 2000);
154
+ }
155
+ } catch (err) {
156
+ console.error('Error sharing:', err);
157
+ }
158
+ }
159
+
160
+ private checkDaily() {
161
+ const today = new Date().toISOString().split('T')[0];
162
+ const storedData = localStorage.getItem('jjlmoya-creative-fortune');
163
+
164
+ if (storedData) {
165
+ try {
166
+ const data = JSON.parse(storedData);
167
+ if (data.date === today) {
168
+ this.setupFortuneContent(data.msgIndex, data.nums);
169
+ this.instantOpen();
170
+ }
171
+ } catch {
172
+ localStorage.removeItem('jjlmoya-creative-fortune');
173
+ }
174
+ }
175
+ }
176
+
177
+ private prepareNewFortune(today: string) {
178
+ const index = Math.floor(Math.random() * fortunes.length);
179
+ const nums = getLuckyNumbers();
180
+
181
+ const fortuneData = {
182
+ date: today,
183
+ msgIndex: index,
184
+ nums: nums,
185
+ };
186
+ localStorage.setItem('jjlmoya-creative-fortune', JSON.stringify(fortuneData));
187
+
188
+ this.setupFortuneContent(index, nums);
189
+ }
190
+
191
+ private setupFortuneContent(index: number, nums: number[]) {
192
+ this.el.text.innerText = fortunes[index] || '';
193
+ this.el.numbers.innerHTML = nums
194
+ .map(
195
+ (n) =>
196
+ `<span class="fortune-cookie-number">${n}</span>`
197
+ )
198
+ .join('');
199
+ }
200
+
201
+ private tap() {
202
+ if (this.isBroken) return;
203
+
204
+ this.health--;
205
+ this.spawnCrumbs(5);
206
+
207
+ this.el.body.classList.remove('fortune-cookie-shake-1', 'fortune-cookie-shake-2', 'fortune-cookie-shake-3');
208
+ void this.el.body.offsetWidth;
209
+ this.el.body.classList.add(`fortune-cookie-shake-${4 - this.health}`);
210
+
211
+ if (this.health === 2) {
212
+ this.el.img.src = this.assets.cracked1;
213
+ } else if (this.health === 1) {
214
+ this.el.img.src = this.assets.cracked2;
215
+ } else if (this.health <= 0) {
216
+ this.breakOpen();
217
+ }
218
+ }
219
+
220
+ private breakOpen() {
221
+ this.isBroken = true;
222
+
223
+ const today = new Date().toISOString().split('T')[0] as string;
224
+ this.prepareNewFortune(today);
225
+
226
+ this.el.instruction.innerText = ui.title || '';
227
+
228
+ this.el.broken.classList.remove('hidden');
229
+ this.el.body.classList.add('hidden');
230
+
231
+ void this.el.broken.offsetWidth;
232
+
233
+ requestAnimationFrame(() => {
234
+ requestAnimationFrame(() => {
235
+ this.el.halfLeft.style.transform = 'translate(-120px, 60px) rotate(-45deg)';
236
+ this.el.halfLeft.style.opacity = '0';
237
+ this.el.halfRight.style.transform = 'translate(120px, 60px) rotate(45deg)';
238
+ this.el.halfRight.style.opacity = '0';
239
+ });
240
+ });
241
+
242
+ setTimeout(() => {
243
+ this.el.paper.classList.add('visible');
244
+ }, 100);
245
+
246
+ this.spawnCrumbs(20);
247
+
248
+ setTimeout(() => {
249
+ this.el.resetUi.classList.add('visible');
250
+ }, 1000);
251
+ }
252
+
253
+ private instantOpen() {
254
+ this.isBroken = true;
255
+ this.el.instruction.innerText = ui.title || '';
256
+ this.el.body.classList.add('hidden');
257
+ this.el.paper.classList.add('visible');
258
+ this.el.resetUi.classList.add('visible');
259
+ }
260
+
261
+ private spawnCrumbs(amount: number) {
262
+ for (let i = 0; i < amount; i++) {
263
+ const crumb = document.createElement('div');
264
+ crumb.classList.add('fortune-cookie-crumb');
265
+
266
+ const size = Math.random() * 4 + 2;
267
+ crumb.style.width = `${size}px`;
268
+ crumb.style.height = `${size}px`;
269
+ crumb.style.left = '50%';
270
+ crumb.style.top = '50%';
271
+
272
+ const angle = Math.random() * Math.PI * 2;
273
+ const velocity = Math.random() * 50 + 20;
274
+ const tx = Math.cos(angle) * velocity;
275
+ const ty = Math.sin(angle) * velocity;
276
+
277
+ this.el.particles.appendChild(crumb);
278
+
279
+ const anim = crumb.animate(
280
+ [
281
+ { transform: 'translate(-50%, -50%) scale(1)', opacity: 1 },
282
+ {
283
+ transform: `translate(calc(-50% + ${tx}px), calc(-50% + ${ty + 50}px)) scale(0)`,
284
+ opacity: 0,
285
+ },
286
+ ],
287
+ {
288
+ duration: 600 + Math.random() * 400,
289
+ easing: 'cubic-bezier(0,0,0.2,1)',
290
+ }
291
+ );
292
+
293
+ anim.onfinish = () => crumb.remove();
294
+ }
295
+ }
296
+ }
297
+
298
+ new FortuneCookie();
299
+ </script>
@@ -0,0 +1,85 @@
1
+ import type { FortuneCookieLocaleContent } from '../index';
2
+
3
+ export const content: FortuneCookieLocaleContent = {
4
+ slug: 'fortune-cookie',
5
+ title: 'Fortune Cookie',
6
+ description: 'Check your daily destiny and discover your lucky numbers. One fortune per day, revealed with a click.',
7
+ faqTitle: 'Frequently Asked Questions',
8
+ bibliographyTitle: 'Destiny Bibliography',
9
+ ui: {
10
+ title: 'Online Fortune Cookie',
11
+ description: 'Your daily digital oracle.',
12
+ instruction: 'Hit the cookie to open your destiny',
13
+ dailyStatus: 'Your destiny has been revealed for today.',
14
+ shareBtn: 'Share Wisdom',
15
+ shareTitle: 'My Fortune Cookie',
16
+ shareText: 'Destiny has spoken: "$TEXT"\nMy numbers: $NUMS\n\nDiscover your fortune here:',
17
+ copied: 'Copied!',
18
+ fortunes: JSON.stringify([
19
+ "The fortune you seek is in another cookie.",
20
+ "Don't count the days, make the days count.",
21
+ "A journey of a thousand miles begins with a single step.",
22
+ "Mistake is the preamble of discovery.",
23
+ "Your capacity to learn is your greatest asset.",
24
+ "Smile, the universe is watching you.",
25
+ "Patience is a tree with a bitter root but very sweet fruit.",
26
+ "Don't fear growing slowly, fear only staying still.",
27
+ "Today is the tomorrow you worried about yesterday.",
28
+ "Happiness is not something made. It comes from your own actions.",
29
+ "If you cannot change the direction of the wind, adjust your sails.",
30
+ "What you plant now, you will harvest later.",
31
+ "Creativity is intelligence having fun.",
32
+ "Don't look for errors, look for solutions.",
33
+ "Your attitude, not your aptitude, will determine your altitude.",
34
+ "Success is the sum of small efforts repeated day after day.",
35
+ "Believe you can and you're halfway there.",
36
+ "The only way to do great work is to love what you do.",
37
+ "Life is 10% what happens to you and 90% how you react to it.",
38
+ "Be the change you want to see in the world.",
39
+ "The best way to predict the future is to invent it.",
40
+ "Simplicity is the ultimate sophistication.",
41
+ "You are the master of your destiny and the captain of your soul.",
42
+ "Soon you will receive news that will change your perspective.",
43
+ "An unexpected trip will bring you great joys."
44
+ ]),
45
+ faqTitle: 'FAQ',
46
+ bibliographyTitle: 'References'
47
+ },
48
+ seo: [
49
+ { type: 'title', text: 'The Mystery of the Fortune Cookie', level: 2 },
50
+ { type: 'paragraph', html: 'Ever wondered where these curious cookies come from? Although we associate them with Chinese food, their origin is a fascinating journey between Japan and the United States. The fortune cookie as we know it today was likely invented in early 20th-century California, not China — where they are virtually unknown.' },
51
+ { type: 'tip', title: 'How Our Oracle Works', html: 'Each day you can open one cookie. The system saves your destiny so you remember it for 24 hours. The fortune is stored locally in your browser — completely private, no server involved. Come back tomorrow for a new prediction!' },
52
+ { type: 'title', text: 'The Philosophy of Oracles', level: 3 },
53
+ { type: 'paragraph', html: 'Humans have always sought guidance in uncertain times. From the Oracle of Delphi to the I Ching, fortune-telling systems serve a consistent psychological function: they give us permission to <strong>pause and reflect</strong>. A fortune cookie does not predict the future — it prompts you to think about it.' },
54
+ { type: 'list', items: [
55
+ '<strong>Ancient China:</strong> The I Ching (Book of Changes, ~1000 BC) used hexagrams to guide decisions and interpret destiny.',
56
+ '<strong>Classical Greece:</strong> The Oracle of Delphi attracted rulers and citizens alike with cryptic pronouncements interpreted as divine guidance.',
57
+ '<strong>Modern Japan:</strong> Omikuji fortune slips at Shinto shrines, tied to tree branches, blend fate with ritual.',
58
+ '<strong>20th-century USA:</strong> Fortune cookies emerged in Japanese-American communities before becoming synonymous with Chinese-American cuisine.',
59
+ ]},
60
+ { type: 'stats', items: [
61
+ { value: '3 billion+', label: 'Fortune cookies made annually', icon: 'mdi:cookie' },
62
+ { value: '~1000 BC', label: 'I Ching origin', icon: 'mdi:book-open-variant' },
63
+ { value: '25', label: 'Fortunes in this oracle', icon: 'mdi:star-shooting' },
64
+ { value: '1/day', label: 'One destiny per day', icon: 'mdi:calendar-today' },
65
+ ], columns: 4 },
66
+ ],
67
+ faq: [
68
+ {
69
+ question: 'Can I open more than one cookie per day?',
70
+ answer: 'Destiny only speaks once a day. We save your fortune on the device so it guides you throughout the day.'
71
+ },
72
+ {
73
+ question: 'Are the fortunes randomly generated?',
74
+ answer: 'Yes — a random fortune is selected each day and saved locally. Each of the 25 fortunes has an equal chance of being chosen, ensuring variety over time.'
75
+ }
76
+ ],
77
+ bibliography: [
78
+ { name: 'History of the Fortune Cookie', url: 'https://en.wikipedia.org/wiki/Fortune_cookie' }
79
+ ],
80
+ howTo: [
81
+ { name: 'Break the cookie', text: 'Click repeatedly on the cookie to crack it open.' },
82
+ { name: 'Read your fortune', text: 'Discover the hidden message inside and your lucky numbers for the day.' }
83
+ ],
84
+ schemas: []
85
+ };
@@ -0,0 +1,90 @@
1
+ import type { FortuneCookieLocaleContent } from '../index';
2
+
3
+ export const content: FortuneCookieLocaleContent = {
4
+ slug: 'galleta-fortuna',
5
+ title: 'Galleta de la Fortuna',
6
+ description: 'Consulta tu destino diario y descubre tus números de la suerte. Una fortuna al día, revelada con un clic.',
7
+ faqTitle: 'Preguntas Frecuentes',
8
+ bibliographyTitle: 'Bibliografía del Destino',
9
+ ui: {
10
+ title: 'Galleta de la Fortuna Online',
11
+ description: 'Tu oráculo digital diario.',
12
+ instruction: 'Golpea la galleta para abrir tu destino',
13
+ dailyStatus: 'Tu destino ha sido revelado por hoy.',
14
+ shareBtn: 'Compartir Sabiduría',
15
+ shareTitle: 'Mi Galleta de la Fortuna',
16
+ shareText: 'El destino me ha hablado: "$TEXT"\nMis números: $NUMS\n\nDescubre tu fortuna aquí:',
17
+ copied: '¡Copiado!',
18
+ fortunes: JSON.stringify([
19
+ "La suerte que buscas está en otra galleta.",
20
+ "No cuentes los días, haz que los días cuenten.",
21
+ "Un viaje de mil millas comienza con un solo paso.",
22
+ "El error es el preámbulo del descubrimiento.",
23
+ "Tu capacidad de aprender es tu mayor activo.",
24
+ "Sonríe, el universo te está mirando.",
25
+ "La paciencia es un árbol de raíz amarga pero de frutos muy dulces.",
26
+ "No temas crecer lentamente, teme solo quedarte quieto.",
27
+ "Hoy es el mañana que tanto te preocupaba ayer.",
28
+ "La felicidad no es algo hecho. Proviene de tus propias acciones.",
29
+ "Si no puedes cambiar la dirección del viento, ajusta tus velas.",
30
+ "Lo que plantas ahora, lo cosecharás más tarde.",
31
+ "La creatividad es la inteligencia divirtiéndose.",
32
+ "No busques errores, busca soluciones.",
33
+ "Tu actitud, no tu aptitud, determinará tu altitud.",
34
+ "El éxito es la suma de pequeños esfuerzos repetidos día tras día.",
35
+ "Cree que puedes y ya estarás a medio camino.",
36
+ "La única forma de hacer un gran trabajo es amar lo que haces.",
37
+ "La vida es un 10% lo que te sucede y un 90% cómo reaccionas ante ello.",
38
+ "Sé el cambio que quieres ver en el mundo.",
39
+ "La mejor manera de predecir el futuro es inventarlo.",
40
+ "La simplicidad es la máxima sofisticación.",
41
+ "Eres el dueño de tu destino y el capitán de tu alma.",
42
+ "Pronto recibirás una noticia que cambiará tu perspectiva.",
43
+ "Un viaje inesperado te traerá grandes alegrías."
44
+ ]),
45
+ faqTitle: 'FAQ',
46
+ bibliographyTitle: 'Referencias'
47
+ },
48
+ seo: [
49
+ { type: 'title', text: 'El Misterio de la Galleta de la Fortuna', level: 2 },
50
+ { type: 'paragraph', html: '¿Alguna vez te has preguntado de dónde vienen estas curiosas galletas? Aunque las asociamos con la comida china, su origen es un fascinante viaje entre Japón y los Estados Unidos. La galleta de la fortuna tal como la conocemos hoy fue probablemente inventada en la California de principios del siglo XX, no en China, donde son prácticamente desconocidas.' },
51
+ { type: 'tip', title: '¿Cómo funciona nuestro oráculo?', html: 'Cada día puedes abrir una galleta. El sistema guarda tu destino en el navegador de forma completamente privada, sin servidor. ¡Vuelve mañana para una nueva predicción!' },
52
+ { type: 'title', text: 'La Filosofía de los Oráculos', level: 3 },
53
+ { type: 'paragraph', html: 'Los seres humanos siempre han buscado orientación en tiempos de incertidumbre. Desde el Oráculo de Delfos hasta el I Ching, los sistemas de adivinación cumplen una función psicológica constante: nos dan permiso para <strong>detenernos y reflexionar</strong>. Una galleta de la fortuna no predice el futuro, sino que te invita a pensar en él.' },
54
+ { type: 'list', items: [
55
+ '<strong>China Antigua (~1000 a.C.):</strong> El I Ching (Libro de los Cambios) utilizaba hexagramas para orientar decisiones e interpretar el destino.',
56
+ '<strong>Grecia Clásica:</strong> El Oráculo de Delfos atraía a gobernantes y ciudadanos con pronunciamientos crípticos interpretados como guía divina.',
57
+ '<strong>Japón Moderno:</strong> Los omikuji, papelitos de la fortuna en los santuarios sintoístas, mezclan el destino con el ritual.',
58
+ '<strong>EE.UU. Siglo XX:</strong> Las galletas de la fortuna surgieron en comunidades japonés-americanas antes de convertirse en símbolo de la cocina chino-americana.',
59
+ ]},
60
+ { type: 'stats', items: [
61
+ { value: '+3.000M', label: 'Galletas fabricadas al año', icon: 'mdi:cookie' },
62
+ { value: '~1000 a.C.', label: 'Origen del I Ching', icon: 'mdi:book-open-variant' },
63
+ { value: '25', label: 'Fortunas en este oráculo', icon: 'mdi:star-shooting' },
64
+ { value: '1/día', label: 'Un destino diario', icon: 'mdi:calendar-today' },
65
+ ], columns: 4 },
66
+ { type: 'proscons', items: [
67
+ { pro: 'Momento de pausa y reflexión diaria', con: 'Sin base científica predictiva real' },
68
+ { pro: 'Inspiración positiva con frases atemporales', con: 'El mensaje es el mismo sin importar el contexto' },
69
+ { pro: 'Completamente privado, sin datos enviados al servidor', con: 'Solo una fortuna al día (¡diseño intencionado!)' },
70
+ ]},
71
+ ],
72
+ faq: [
73
+ {
74
+ question: '¿Puedo abrir más de una galleta al día?',
75
+ answer: 'El destino solo habla una vez al día. Guardamos tu fortuna en el dispositivo para que sea tu guía durante la jornada.'
76
+ },
77
+ {
78
+ question: '¿Las fortunas se generan aleatoriamente?',
79
+ answer: 'Sí: se selecciona una fortuna aleatoria cada día y se guarda localmente. Las 25 fortunas tienen la misma probabilidad de ser elegidas, asegurando variedad con el tiempo.'
80
+ }
81
+ ],
82
+ bibliography: [
83
+ { name: 'Historia de la Galleta de la Fortuna', url: 'https://es.wikipedia.org/wiki/Galleta_de_la_fortuna' }
84
+ ],
85
+ howTo: [
86
+ { name: 'Golpear', text: 'Haz clic varias veces sobre la galleta para romperla.' },
87
+ { name: 'Leer', text: 'Descubre el mensaje oculto en su interior y tus números de la suerte.' }
88
+ ],
89
+ schemas: []
90
+ };
@@ -0,0 +1,85 @@
1
+ import type { FortuneCookieLocaleContent } from '../index';
2
+
3
+ export const content: FortuneCookieLocaleContent = {
4
+ slug: 'biscuit-de-la-fortune',
5
+ title: 'Biscuit de la Fortune',
6
+ description: 'Consultez votre destin quotidien et découvrez vos numéros de chance. Un biscuit par jour, révélé d\'un simple clic.',
7
+ faqTitle: 'Questions Fréquemment Posées',
8
+ bibliographyTitle: 'Bibliographie du Destin',
9
+ ui: {
10
+ title: 'Biscuit de la Fortune en Ligne',
11
+ description: 'Votre oracle numérique quotidien.',
12
+ instruction: 'Cliquez sur le biscuit pour l\'ouvrir',
13
+ dailyStatus: 'Votre destin a été révélé pour aujourd\'hui.',
14
+ shareBtn: 'Partager la Sagesse',
15
+ shareTitle: 'Mon Biscuit de la Fortune',
16
+ shareText: 'Le destin a parlé : "$TEXT"\nMes numéros : $NUMS\n\nDécouvrez votre fortune ici :',
17
+ copied: 'Copié !',
18
+ fortunes: JSON.stringify([
19
+ "La fortune que vous cherchez se trouve dans un autre biscuit.",
20
+ "Ne comptez pas les jours, faites en sorte que les jours comptent.",
21
+ "Un voyage de mille lieues commence toujours par un premier pas.",
22
+ "L'erreur est le préambule de la découverte.",
23
+ "Votre capacité à apprendre est votre plus grand atout.",
24
+ "Souriez, l'univers vous regarde.",
25
+ "La patience est un arbre aux racines amères mais aux fruits très doux.",
26
+ "Ne craignez pas de grandir lentement, craignez seulement de rester immobile.",
27
+ "Aujourd'hui est le demain dont vous vous inquiétiez hier.",
28
+ "Le bonheur n'est pas quelque chose de tout fait. Il découle de vos propres actions.",
29
+ "Si vous ne pouvez pas changer la direction du vent, ajustez vos voiles.",
30
+ "Ce que vous plantez maintenant, vous le récolterez plus tard.",
31
+ "La créativité, c'est l'intelligence qui s'amuse.",
32
+ "Ne cherchez pas d'erreurs, cherchez des solutions.",
33
+ "C'est votre attitude, et non votre aptitude, qui déterminera votre altitude.",
34
+ "Le succès est la somme de petits efforts répétées jour après jour.",
35
+ "Croyez que vous le pouvez et vous aurez fait la moitié du chemin.",
36
+ "La seule façon de faire du bon travail est d'aimer ce que vous faites.",
37
+ "La vie, c'est 10 % ce qui vous arrive et 90 % la façon dont vous y réagissez.",
38
+ "Soyez le changement que vous voulez voir dans le monde.",
39
+ "La meilleure façon de prédire l'avenir est de l'inventer.",
40
+ "La simplicité est la sophistication suprême.",
41
+ "Vous êtes le maître de votre destin et le capitaine de votre âme.",
42
+ "Bientôt, vous recevrez des nouvelles qui changeront votre perspective.",
43
+ "Un voyage inattendu vous apportera de grandes joies."
44
+ ]),
45
+ faqTitle: 'FAQ',
46
+ bibliographyTitle: 'Références'
47
+ },
48
+ seo: [
49
+ { type: 'title', text: 'Le Mystère du Biscuit de la Fortune', level: 2 },
50
+ { type: 'paragraph', html: 'Vous êtes-vous déjà demandé d\'où viennent ces curieux biscuits ? Bien qu\'on les associe à la cuisine chinoise, leur origine est un voyage fascinant entre le Japon et les États-Unis. Le biscuit de la fortune tel que nous le connaissons aujourd\'hui a probablement été inventé au début du XXe siècle en Californie, pas en Chine — où ils sont pratiquement inconnus.' },
51
+ { type: 'tip', title: 'Fonctionnement de Notre Oracle', html: 'Chaque jour, vous pouvez ouvrir un seul biscuit. Le système enregistre votre destin pour que vous puissiez vous en souvenir pendant 24 heures. La fortune est stockée localement dans votre navigateur — totalement privé, aucun serveur n\'est impliqué. Revenez demain pour une nouvelle prédiction !' },
52
+ { type: 'title', text: 'La Philosophie des Oracles', level: 3 },
53
+ { type: 'paragraph', html: 'Les humains ont toujours cherché conseil en période d\'incertitude. De l\'Oracle de Delphes au Yi Jing, les systèmes de divination remplissent une fonction psychologique constante : ils nous autorisent à <strong>faire une pause et réfléchir</strong>. Un biscuit de la fortune ne prédit pas l\'avenir — il vous incite à y penser.' },
54
+ { type: 'list', items: [
55
+ '<strong>Chine Ancienne :</strong> Le Yi Jing (Livre des Transformations, ~1000 av. J.-C.) utilisait des hexagrammes pour guider les décisions et interpréter le destin.',
56
+ '<strong>Grèce Classique :</strong> L\'Oracle de Delphes attirait dirigeants et citoyens avec des déclarations cryptiques interprétées comme des conseils divins.',
57
+ '<strong>Japon Moderne :</strong> Les billets de fortune Omikuji dans les sanctuaires shinto, attachés aux branches d\'arbres, mêlent destin et rituel.',
58
+ '<strong>XXe siècle aux USA :</strong> Les biscuits de la fortune sont apparus dans les communautés nippo-américaines avant de devenir synonymes de la cuisine sino-américaine.',
59
+ ]},
60
+ { type: 'stats', items: [
61
+ { value: '3 milliards+', label: 'Biscuits fabriqués par an', icon: 'mdi:cookie' },
62
+ { value: '~1000 av. J.-C.', label: 'Origine du Yi Jing', icon: 'mdi:book-open-variant' },
63
+ { value: '25', label: 'Fortunes dans cet oracle', icon: 'mdi:star-shooting' },
64
+ { value: '1/jour', label: 'Un destin par jour', icon: 'mdi:calendar-today' },
65
+ ], columns: 4 },
66
+ ],
67
+ faq: [
68
+ {
69
+ question: 'Puis-je ouvrir plus d\'un biscuit par jour ?',
70
+ answer: 'Le destin ne parle qu\'une fois par jour. Nous enregistrons votre fortune sur l\'appareil pour qu\'elle vous guide tout au long de la journée.'
71
+ },
72
+ {
73
+ question: 'Les fortunes sont-elles générées aléatoirement ?',
74
+ answer: 'Oui — une fortune aléatoire est sélectionnée chaque jour et sauvegardée localement. Chacune des 25 fortunes a une chance égale d\'être choisie, garantissant une variété au fil du temps.'
75
+ }
76
+ ],
77
+ bibliography: [
78
+ { name: 'Histoire du Fortune Cookie', url: 'https://en.wikipedia.org/wiki/Fortune_cookie' }
79
+ ],
80
+ howTo: [
81
+ { name: 'Casser le biscuit', text: 'Cliquez de façon répétée sur le biscuit pour l\'ouvrir.' },
82
+ { name: 'Lire votre fortune', text: 'Découvrez le message caché à l\'intérieur et vos numéros de chance pour la journée.' }
83
+ ],
84
+ schemas: []
85
+ };
@@ -0,0 +1,40 @@
1
+ import type { CreativeToolEntry, ToolLocaleContent, ToolDefinition } from '../../types';
2
+ import FortuneCookieComponent from './component.astro';
3
+ import FortuneCookieSEO from './seo.astro';
4
+ import FortuneCookieBibliography from './bibliography.astro';
5
+
6
+ export interface FortuneCookieUI {
7
+ [key: string]: string;
8
+ instruction: string;
9
+ title: string;
10
+ description: string;
11
+ dailyStatus: string;
12
+ shareBtn: string;
13
+ shareTitle: string;
14
+ shareText: string;
15
+ copied: string;
16
+ fortunes: string;
17
+ faqTitle: string;
18
+ bibliographyTitle: string;
19
+ }
20
+
21
+ export type FortuneCookieLocaleContent = ToolLocaleContent<FortuneCookieUI>;
22
+
23
+ export const fortuneCookie: CreativeToolEntry<FortuneCookieUI> = {
24
+ id: 'fortune-cookie',
25
+ icons: { bg: 'mdi:cookie', fg: 'mdi:sparkles' },
26
+ i18n: {
27
+ es: () => import('./i18n/es').then((m) => m.content),
28
+ en: () => import('./i18n/en').then((m) => m.content),
29
+ fr: () => import('./i18n/fr').then((m) => m.content),
30
+ },
31
+ };
32
+
33
+ export { FortuneCookieComponent, FortuneCookieSEO, FortuneCookieBibliography };
34
+
35
+ export const FORTUNE_COOKIE_TOOL: ToolDefinition = {
36
+ entry: fortuneCookie,
37
+ Component: FortuneCookieComponent,
38
+ SEOComponent: FortuneCookieSEO,
39
+ BibliographyComponent: FortuneCookieBibliography,
40
+ };
@@ -0,0 +1,14 @@
1
+ ---
2
+ import { SEORenderer } from '@jjlmoya/utils-shared';
3
+ import { fortuneCookie } from './index';
4
+ import type { KnownLocale } from '../../types';
5
+
6
+ interface Props {
7
+ locale?: KnownLocale;
8
+ }
9
+
10
+ const { locale = 'es' } = Astro.props;
11
+ const content = await fortuneCookie.i18n[locale]?.();
12
+ ---
13
+
14
+ {content && <SEORenderer content={{ locale, sections: content.seo }} />}