@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.
- package/package.json +64 -0
- package/src/category/i18n/en.ts +9 -0
- package/src/category/i18n/es.ts +9 -0
- package/src/category/i18n/fr.ts +9 -0
- package/src/category/index.ts +34 -0
- package/src/category/seo.astro +15 -0
- package/src/components/PreviewNavSidebar.astro +116 -0
- package/src/components/PreviewToolbar.astro +143 -0
- package/src/data.ts +6 -0
- package/src/env.d.ts +5 -0
- package/src/index.ts +27 -0
- package/src/layouts/PreviewLayout.astro +117 -0
- package/src/pages/[locale]/[slug].astro +146 -0
- package/src/pages/[locale].astro +251 -0
- package/src/pages/index.astro +4 -0
- package/src/tests/faq_count.test.ts +19 -0
- package/src/tests/locale_completeness.test.ts +42 -0
- package/src/tests/mocks/astro_mock.js +2 -0
- package/src/tests/no_h1_in_components.test.ts +48 -0
- package/src/tests/seo_length.test.ts +22 -0
- package/src/tests/tool_validation.test.ts +17 -0
- package/src/tool/bead-pattern-generator/bibliography.astro +18 -0
- package/src/tool/bead-pattern-generator/component.astro +372 -0
- package/src/tool/bead-pattern-generator/i18n/en.ts +61 -0
- package/src/tool/bead-pattern-generator/i18n/es.ts +68 -0
- package/src/tool/bead-pattern-generator/i18n/fr.ts +61 -0
- package/src/tool/bead-pattern-generator/index.ts +37 -0
- package/src/tool/bead-pattern-generator/seo.astro +14 -0
- package/src/tool/bead-pattern-generator/style.css +511 -0
- package/src/tool/dice-roller/bibliography.astro +17 -0
- package/src/tool/dice-roller/component.astro +230 -0
- package/src/tool/dice-roller/i18n/en.ts +87 -0
- package/src/tool/dice-roller/i18n/es.ts +89 -0
- package/src/tool/dice-roller/i18n/fr.ts +87 -0
- package/src/tool/dice-roller/index.ts +37 -0
- package/src/tool/dice-roller/seo.astro +14 -0
- package/src/tool/dice-roller/style.css +482 -0
- package/src/tool/excuse-generator/bibliography.astro +18 -0
- package/src/tool/excuse-generator/component.astro +140 -0
- package/src/tool/excuse-generator/i18n/en.ts +80 -0
- package/src/tool/excuse-generator/i18n/es.ts +84 -0
- package/src/tool/excuse-generator/i18n/fr.ts +80 -0
- package/src/tool/excuse-generator/index.ts +42 -0
- package/src/tool/excuse-generator/seo.astro +14 -0
- package/src/tool/excuse-generator/style.css +316 -0
- package/src/tool/fortune-cookie/bibliography.astro +18 -0
- package/src/tool/fortune-cookie/component.astro +299 -0
- package/src/tool/fortune-cookie/i18n/en.ts +85 -0
- package/src/tool/fortune-cookie/i18n/es.ts +90 -0
- package/src/tool/fortune-cookie/i18n/fr.ts +85 -0
- package/src/tool/fortune-cookie/index.ts +40 -0
- package/src/tool/fortune-cookie/seo.astro +14 -0
- package/src/tool/fortune-cookie/style.css +332 -0
- package/src/tool/synesthesia-painter/bibliography.astro +17 -0
- package/src/tool/synesthesia-painter/component.astro +110 -0
- package/src/tool/synesthesia-painter/i18n/en.ts +80 -0
- package/src/tool/synesthesia-painter/i18n/es.ts +82 -0
- package/src/tool/synesthesia-painter/i18n/fr.ts +80 -0
- package/src/tool/synesthesia-painter/index.ts +39 -0
- package/src/tool/synesthesia-painter/seo.astro +14 -0
- package/src/tool/synesthesia-painter/style.css +234 -0
- package/src/tool/zalgo-generator/bibliography.astro +18 -0
- package/src/tool/zalgo-generator/component.astro +195 -0
- package/src/tool/zalgo-generator/i18n/en.ts +60 -0
- package/src/tool/zalgo-generator/i18n/es.ts +67 -0
- package/src/tool/zalgo-generator/i18n/fr.ts +60 -0
- package/src/tool/zalgo-generator/index.ts +38 -0
- package/src/tool/zalgo-generator/seo.astro +14 -0
- package/src/tool/zalgo-generator/style.css +558 -0
- package/src/tools.ts +4 -0
- package/src/types.ts +72 -0
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import type { ExcuseGeneratorLocaleContent } from '../index';
|
|
2
|
+
|
|
3
|
+
export const content: ExcuseGeneratorLocaleContent = {
|
|
4
|
+
slug: 'generador-excusas',
|
|
5
|
+
title: 'Generador de Excusas',
|
|
6
|
+
description: 'Máquina de azar semántica para librarte de compromisos con estilo.',
|
|
7
|
+
faqTitle: 'Preguntas Frecuentes',
|
|
8
|
+
bibliographyTitle: 'Bibliografía del Ocio',
|
|
9
|
+
ui: {
|
|
10
|
+
title: 'Generador de Excusas Surrealistas',
|
|
11
|
+
description: 'Calculadora de coartadas de última generación.',
|
|
12
|
+
subjectLabel: 'Sujeto',
|
|
13
|
+
actionLabel: 'Acción',
|
|
14
|
+
victimLabel: 'Víctima',
|
|
15
|
+
generateBtn: 'GENERAR EXCUSA',
|
|
16
|
+
copyBtn: 'Copiar esta locura',
|
|
17
|
+
copied: '¡Copiado!',
|
|
18
|
+
starts: JSON.stringify([
|
|
19
|
+
"Mi gato", "Mi abuela", "El presidente", "Un alienígena", "Mi vecino del quinto",
|
|
20
|
+
"El wifi", "Mi horóscopo", "Un viajero del tiempo", "La lavadora", "Mi nevera",
|
|
21
|
+
"El espíritu de la navidad", "Mi sombra", "Un ninja", "La policía vegana", "Siri",
|
|
22
|
+
"Mi planta carnívora", "El repartidor de Amazon", "Mi clon malvado", "Un mago", "Godzilla"
|
|
23
|
+
]),
|
|
24
|
+
middles: JSON.stringify([
|
|
25
|
+
"ha declarado la guerra a", "se ha comido", "ha secuestrado a", "ha prendido fuego a",
|
|
26
|
+
"está organizando una fiesta en", "ha vomitado sobre", "se niega a abandonar",
|
|
27
|
+
"ha hackeado", "ha robado", "se ha enamorado de", "está meditando sobre",
|
|
28
|
+
"ha lanzado un hechizo a", "está protestando contra", "ha fundado una secta en",
|
|
29
|
+
"está bailando samba en", "ha invocado a un demonio en", "ha bloqueado el acceso a",
|
|
30
|
+
"ha convertido en oro", "está intentando vender", "ha escrito un libro sobre"
|
|
31
|
+
]),
|
|
32
|
+
ends: JSON.stringify([
|
|
33
|
+
"mis llaves de casa.", "mi voluntad de vivir.", "la puerta del garaje.",
|
|
34
|
+
"mis pantalones favoritos.", "el router del salón.", "mi único par de zapatos.",
|
|
35
|
+
"las leyes de la física.", "mi dignidad.", "el ascensor del edificio.",
|
|
36
|
+
"mi cuenta de Netflix.", "mi cepillo de dientes.", "las escrituras de mi casa.",
|
|
37
|
+
"mi colección de tazos.", "el freno de mano del coche.", "mis ganas de socializar.",
|
|
38
|
+
"mi reserva de café.", "la tapa del inodoro.", "el mando de la tele.",
|
|
39
|
+
"mis calcetines de la suerte.", "la civilización occidental."
|
|
40
|
+
])
|
|
41
|
+
},
|
|
42
|
+
seo: [
|
|
43
|
+
{ type: 'title', text: 'El Arte Científico de la Excusa Perfecta', level: 2 },
|
|
44
|
+
{ type: 'paragraph', html: 'Vivimos en la era de la hiperconexión. Tu teléfono vibra, tu reloj te notifica, y tu agenda social parece un juego de Tetris a punto de perder. La presión por decir "sí" a todo ha creado una epidemia de agotamiento social.' },
|
|
45
|
+
{ type: 'title', text: 'El Renacimiento del JOMO (Joy of Missing Out)', level: 3 },
|
|
46
|
+
{ type: 'paragraph', html: 'Mientras que el FOMO (Fear of Missing Out) dominó la década pasada, los expertos en bienestar digital ahora abogan por el <strong>JOMO: El Placer de Perderse Cosas</strong>. No se trata de aislamiento, sino de intencionalidad.' },
|
|
47
|
+
{ type: 'tip', title: 'La Fórmula de la Coartada Irrefutable', html: '<strong>El Sujeto Disociado:</strong> Nunca eres tú el culpable. Es "el wifi", "mi gato", "el universo". Desplaza la culpa hacia un ente externo.<br><strong>La Acción Hiperbólica:</strong> La situación debe ser lo suficientemente absurda o técnica como para que nadie pida detalles.<br><strong>El Bloqueo Físico:</strong> El resultado final debe ser binario: o voy o me quedo.' },
|
|
48
|
+
{ type: 'title', text: 'Historia Breve de la Excusa', level: 3 },
|
|
49
|
+
{ type: 'list', items: [
|
|
50
|
+
'<strong>Edad Media:</strong> "Mi caballo ha perdido una herradura" (Un clásico atemporal).',
|
|
51
|
+
'<strong>Revolución Industrial:</strong> "La máquina de vapor se ha sobrecalentado".',
|
|
52
|
+
'<strong>Era Digital:</strong> "Se me ha caído internet justo en medio de una actualización".'
|
|
53
|
+
]},
|
|
54
|
+
{ type: 'stats', items: [
|
|
55
|
+
{ value: '20', label: 'Sujetos posibles', icon: 'mdi:account-multiple' },
|
|
56
|
+
{ value: '20', label: 'Acciones disponibles', icon: 'mdi:lightning-bolt' },
|
|
57
|
+
{ value: '20', label: 'Desenlaces distintos', icon: 'mdi:door-open' },
|
|
58
|
+
{ value: '8.000', label: 'Combinaciones posibles', icon: 'mdi:shuffle-variant' },
|
|
59
|
+
], columns: 4 },
|
|
60
|
+
{ type: 'proscons', items: [
|
|
61
|
+
{ pro: 'Alivia la presión social al instante', con: 'El uso excesivo erosiona la confianza' },
|
|
62
|
+
{ pro: 'Protege tu energía y tus límites personales', con: 'Puede generar culpa si se usa sin cuidado' },
|
|
63
|
+
{ pro: 'El tono creativo y humorístico difunde la tensión', con: 'No apta para contextos formales o profesionales' },
|
|
64
|
+
]},
|
|
65
|
+
],
|
|
66
|
+
faq: [
|
|
67
|
+
{
|
|
68
|
+
question: '¿Puede este generador salvar mi matrimonio?',
|
|
69
|
+
answer: 'Aunque no somos terapeutas, evitar esa cena con los suegros utilizando una emergencia técnica plausible puede reducir la tensión significativamente.'
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
question: '¿Por qué frases tan surrealistas?',
|
|
73
|
+
answer: 'La estrategia se basa en la Disonancia Cognitiva. Si dices algo aburrido es comprobable. Si dices algo absurdo, la sorpresa bloquea la capacidad de enfado.'
|
|
74
|
+
}
|
|
75
|
+
],
|
|
76
|
+
bibliography: [
|
|
77
|
+
{ name: 'Manifesto del Procrastinador', url: 'https://example.com/manifesto' }
|
|
78
|
+
],
|
|
79
|
+
howTo: [
|
|
80
|
+
{ name: 'Generar', text: 'Pulsa el botón de generar para crear una nueva excusa.' },
|
|
81
|
+
{ name: 'Copiar', text: 'Haz clic en el botón de copiar para llevar la excusa a tu portapapeles.' }
|
|
82
|
+
],
|
|
83
|
+
schemas: []
|
|
84
|
+
};
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import type { ExcuseGeneratorLocaleContent } from '../index';
|
|
2
|
+
|
|
3
|
+
export const content: ExcuseGeneratorLocaleContent = {
|
|
4
|
+
slug: 'generateur-d-excuses',
|
|
5
|
+
title: 'Générateur d\'Excuses',
|
|
6
|
+
description: 'Une machine à sous sémantique pour vous débarrasser de vos engagements avec style. Générez des excuses surréalistes et irréfutables instantanément.',
|
|
7
|
+
faqTitle: 'Questions Fréquemment Posées',
|
|
8
|
+
bibliographyTitle: 'Bibliographie du Temps Libre',
|
|
9
|
+
ui: {
|
|
10
|
+
title: 'Générateur d\'Excuses Surréalistes',
|
|
11
|
+
description: 'Calculateur d\'alibis de nouvelle génération.',
|
|
12
|
+
subjectLabel: 'Sujet',
|
|
13
|
+
actionLabel: 'Action',
|
|
14
|
+
victimLabel: 'Victime',
|
|
15
|
+
generateBtn: 'GÉNÉRER L\'EXCUSE',
|
|
16
|
+
copyBtn: 'Copier cette folie',
|
|
17
|
+
copied: 'Copié !',
|
|
18
|
+
starts: JSON.stringify([
|
|
19
|
+
"Mon chat", "Ma grand-mère", "Le président", "Un extraterrestre", "Mon voisin du 5ème",
|
|
20
|
+
"Le Wi-Fi", "Mon horoscope", "Un voyageur temporel", "La machine à laver", "Mon frigo",
|
|
21
|
+
"L'esprit de Noël", "Mon ombre", "Un ninja", "La police du véganisme", "Siri",
|
|
22
|
+
"Ma plante carnivore", "Le livreur Amazon", "Mon clone maléfique", "Un magicien", "Godzilla"
|
|
23
|
+
]),
|
|
24
|
+
middles: JSON.stringify([
|
|
25
|
+
"a déclaré la guerre à", "a mangé", "a kidnappé", "a mis le feu à",
|
|
26
|
+
"organise une fête dans", "a vomi sur", "refuse de quitter",
|
|
27
|
+
"a piraté", "a volé", "est tombé amoureux de", "médite sur",
|
|
28
|
+
"a jeté un sort à", "proteste contre", "a fondé une secte dans",
|
|
29
|
+
"danse la samba dans", "a invoqué un démon dans", "a bloqué l'accès à",
|
|
30
|
+
"s'est transformé en or", "essaie de vendre", "a écrit un livre sur"
|
|
31
|
+
]),
|
|
32
|
+
ends: JSON.stringify([
|
|
33
|
+
"mes clés de maison.", "mon envie de vivre.", "la porte du garage.",
|
|
34
|
+
"mon pantalon préféré.", "le routeur du salon.", "ma seule paire de chaussures.",
|
|
35
|
+
"les lois de la physique.", "ma dignité.", "l'ascenseur de l'immeuble.",
|
|
36
|
+
"mon compte Netflix.", "ma brosse à dents.", "mes titres de propriété.",
|
|
37
|
+
"ma collection de Pogs.", "le frein à main de la voiture.", "mon envie de sociabiliser.",
|
|
38
|
+
"ma réserve de café.", "l'abattant des toilettes.", "la télécommande.",
|
|
39
|
+
"mes chaussettes porte-bonheur.", "la civilisation occidentale."
|
|
40
|
+
]),
|
|
41
|
+
faqTitle: 'FAQ',
|
|
42
|
+
bibliographyTitle: 'Références'
|
|
43
|
+
},
|
|
44
|
+
seo: [
|
|
45
|
+
{ type: 'title', text: 'L\'Art Scientifique de l\'Excuse Parfaite', level: 2 },
|
|
46
|
+
{ type: 'paragraph', html: 'Nous vivons à l\'ère de l\'hyper-connectivité. Votre téléphone vibre, votre montre vous notifie, et votre agenda ressemble à une partie de Tetris sur le point de s\'effondrer. La pression de dire "oui" à tout a créé une épidémie d\'épuisement social.' },
|
|
47
|
+
{ type: 'title', text: 'La Renaissance du JOMO (Joy of Missing Out)', level: 3 },
|
|
48
|
+
{ type: 'paragraph', html: 'Alors que le FOMO (Fear of Missing Out) a dominé la dernière décennie, les experts du bien-être numérique prônent désormais le <strong>JOMO : La Joie de Passer à Côté</strong>. Il ne s\'agit pas d\'isolement, mais d\'intentionnalité.' },
|
|
49
|
+
{ type: 'tip', title: 'La Formule de l\'Alibi Irréfutable', html: '<strong>Le Sujet Dissocié :</strong> Vous n\'êtes jamais le coupable. C\'est "le Wi-Fi", "mon chat", "l\'univers". Déplacez la responsabilité vers une entité externe.<br><strong>L\'Action Hyperbolique :</strong> La situation doit être assez absurde ou technique pour que personne ne demande de détails.<br><strong>Le Blocage Physique :</strong> Le résultat final doit être binaire : soit j\'y vais, soit je reste chez moi.' },
|
|
50
|
+
{ type: 'title', text: 'Une Brève Histoire de l\'Excuse', level: 3 },
|
|
51
|
+
{ type: 'list', items: [
|
|
52
|
+
'<strong>Moyen Âge :</strong> "Mon cheval a perdu un fer" (Un classique indémodable).',
|
|
53
|
+
'<strong>Révolution industrielle :</strong> "La machine à vapeur a surchauffé".',
|
|
54
|
+
'<strong>Ère numérique :</strong> "Mon internet a coupé en pleine mise à jour".',
|
|
55
|
+
]},
|
|
56
|
+
{ type: 'proscons', items: [
|
|
57
|
+
{ pro: 'Soulage instantanément la pression sociale', con: 'L\'usage excessif érode la confiance' },
|
|
58
|
+
{ pro: 'Protège votre énergie et vos limites', con: 'Peut générer de la culpabilité si utilisé sans précaution' },
|
|
59
|
+
{ pro: 'Le ton créatif et humoristique désamorce la tension', con: 'Inapproprié dans un contexte formel ou professionnel' },
|
|
60
|
+
]},
|
|
61
|
+
],
|
|
62
|
+
faq: [
|
|
63
|
+
{
|
|
64
|
+
question: 'Ce générateur peut-il sauver mon mariage ?',
|
|
65
|
+
answer: 'Bien que nous ne soyons pas thérapeutes, éviter ce dîner chez les beaux-parents grâce à une urgence technique plausible peut considérablement réduire les tensions. À utiliser avec parcimonie.'
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
question: 'Pourquoi des phrases aussi surréalistes ?',
|
|
69
|
+
answer: 'La stratégie repose sur la Dissonance Cognitive. Si vous dites quelque chose d\'ennuyeux, c\'est vérifiable. Si vous dites quelque chose d\'absurde, la surprise paralyse la capacité de colère.'
|
|
70
|
+
}
|
|
71
|
+
],
|
|
72
|
+
bibliography: [
|
|
73
|
+
{ name: 'Manifeste du Procrastinateur', url: 'https://example.com/manifesto' }
|
|
74
|
+
],
|
|
75
|
+
howTo: [
|
|
76
|
+
{ name: 'Générer', text: 'Cliquez sur le bouton pour créer une nouvelle excuse.' },
|
|
77
|
+
{ name: 'Copier', text: 'Cliquez sur le bouton de copie pour mettre l\'excuse dans votre presse-papiers.' }
|
|
78
|
+
],
|
|
79
|
+
schemas: []
|
|
80
|
+
};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { CreativeToolEntry, ToolLocaleContent, ToolDefinition } from '../../types';
|
|
2
|
+
import ExcuseGeneratorComponent from './component.astro';
|
|
3
|
+
import ExcuseGeneratorSEO from './seo.astro';
|
|
4
|
+
import ExcuseGeneratorBibliography from './bibliography.astro';
|
|
5
|
+
|
|
6
|
+
export interface ExcuseGeneratorUI {
|
|
7
|
+
[key: string]: string;
|
|
8
|
+
title: string;
|
|
9
|
+
description: string;
|
|
10
|
+
subjectLabel: string;
|
|
11
|
+
actionLabel: string;
|
|
12
|
+
victimLabel: string;
|
|
13
|
+
generateBtn: string;
|
|
14
|
+
copyBtn: string;
|
|
15
|
+
copied: string;
|
|
16
|
+
starts: string;
|
|
17
|
+
middles: string;
|
|
18
|
+
ends: string;
|
|
19
|
+
faqTitle: string;
|
|
20
|
+
bibliographyTitle: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export type ExcuseGeneratorLocaleContent = ToolLocaleContent<ExcuseGeneratorUI>;
|
|
24
|
+
|
|
25
|
+
export const excuseGenerator: CreativeToolEntry<ExcuseGeneratorUI> = {
|
|
26
|
+
id: 'excuse-generator',
|
|
27
|
+
icons: { bg: 'mdi:slot-machine', fg: 'mdi:emoticon-poop' },
|
|
28
|
+
i18n: {
|
|
29
|
+
es: () => import('./i18n/es').then((m) => m.content),
|
|
30
|
+
en: () => import('./i18n/en').then((m) => m.content),
|
|
31
|
+
fr: () => import('./i18n/fr').then((m) => m.content),
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export { ExcuseGeneratorComponent, ExcuseGeneratorSEO, ExcuseGeneratorBibliography };
|
|
36
|
+
|
|
37
|
+
export const EXCUSE_GENERATOR_TOOL: ToolDefinition = {
|
|
38
|
+
entry: excuseGenerator,
|
|
39
|
+
Component: ExcuseGeneratorComponent,
|
|
40
|
+
SEOComponent: ExcuseGeneratorSEO,
|
|
41
|
+
BibliographyComponent: ExcuseGeneratorBibliography,
|
|
42
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { SEORenderer } from '@jjlmoya/utils-shared';
|
|
3
|
+
import { excuseGenerator } 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 excuseGenerator.i18n[locale]?.();
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
{content && <SEORenderer content={{ locale, sections: content.seo }} />}
|
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
.excuse-generator {
|
|
2
|
+
--excuse-generator-bg: #fff;
|
|
3
|
+
--excuse-generator-bg-transparent: rgba(255, 255, 255, 0.8);
|
|
4
|
+
--excuse-generator-bg-muted: #f1f5f9;
|
|
5
|
+
--excuse-generator-text: #0f172a;
|
|
6
|
+
--excuse-generator-text-muted: #475569;
|
|
7
|
+
--excuse-generator-text-dim: #64748b;
|
|
8
|
+
--excuse-generator-border: #e2e8f0;
|
|
9
|
+
--excuse-generator-shadow: rgba(0, 0, 0, 0.05);
|
|
10
|
+
--excuse-generator-primary: #ec4899;
|
|
11
|
+
--excuse-generator-primary-on: #fff;
|
|
12
|
+
--excuse-generator-accent: #f43f5e;
|
|
13
|
+
--excuse-generator-success: #10b981;
|
|
14
|
+
--excuse-generator-warning: #f59e0b;
|
|
15
|
+
--excuse-generator-error: #f43f5e;
|
|
16
|
+
--excuse-generator-ring: rgba(236, 72, 153, 0.2);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
.theme-dark .excuse-generator {
|
|
20
|
+
--excuse-generator-bg: #0f172a;
|
|
21
|
+
--excuse-generator-bg-transparent: rgba(15, 23, 42, 0.8);
|
|
22
|
+
--excuse-generator-bg-muted: #1e293b;
|
|
23
|
+
--excuse-generator-text: #f8fafc;
|
|
24
|
+
--excuse-generator-text-muted: #94a3b8;
|
|
25
|
+
--excuse-generator-text-dim: #64748b;
|
|
26
|
+
--excuse-generator-border: #334155;
|
|
27
|
+
--excuse-generator-shadow: rgba(0, 0, 0, 0.3);
|
|
28
|
+
--excuse-generator-primary: #f472b6;
|
|
29
|
+
--excuse-generator-primary-on: #fff;
|
|
30
|
+
--excuse-generator-accent: #fb7185;
|
|
31
|
+
--excuse-generator-success: #34d399;
|
|
32
|
+
--excuse-generator-warning: #fbbf24;
|
|
33
|
+
--excuse-generator-error: #fb7185;
|
|
34
|
+
--excuse-generator-ring: rgba(244, 114, 182, 0.2);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
.excuse-generator-root {
|
|
38
|
+
max-width: 64rem;
|
|
39
|
+
margin-left: auto;
|
|
40
|
+
margin-right: auto;
|
|
41
|
+
padding: 1rem;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
@media (min-width: 768px) {
|
|
45
|
+
.excuse-generator-root {
|
|
46
|
+
padding: 2rem;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
.excuse-generator-card {
|
|
51
|
+
position: relative;
|
|
52
|
+
background-color: var(--excuse-generator-bg-transparent);
|
|
53
|
+
backdrop-filter: blur(24px);
|
|
54
|
+
border-radius: 2rem;
|
|
55
|
+
padding: 2rem;
|
|
56
|
+
box-shadow: 0 25px 50px -12px var(--excuse-generator-shadow);
|
|
57
|
+
border: 1px solid var(--excuse-generator-border);
|
|
58
|
+
min-height: 500px;
|
|
59
|
+
display: flex;
|
|
60
|
+
flex-direction: column;
|
|
61
|
+
align-items: center;
|
|
62
|
+
justify-content: center;
|
|
63
|
+
text-align: center;
|
|
64
|
+
overflow: hidden;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
@media (min-width: 768px) {
|
|
68
|
+
.excuse-generator-card {
|
|
69
|
+
padding: 3rem;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
.excuse-generator-blob {
|
|
74
|
+
position: absolute;
|
|
75
|
+
width: 24rem;
|
|
76
|
+
height: 24rem;
|
|
77
|
+
border-radius: 9999px;
|
|
78
|
+
filter: blur(64px);
|
|
79
|
+
pointer-events: none;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.excuse-generator-blob-1 {
|
|
83
|
+
top: 0;
|
|
84
|
+
right: 0;
|
|
85
|
+
transform: translate(50%, -50%);
|
|
86
|
+
background-color: rgba(244, 114, 182, 0.2);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
.excuse-generator-blob-2 {
|
|
90
|
+
bottom: 0;
|
|
91
|
+
left: 0;
|
|
92
|
+
transform: translate(-50%, 50%);
|
|
93
|
+
background-color: rgba(251, 113, 133, 0.2);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
.excuse-generator-slots {
|
|
97
|
+
position: relative;
|
|
98
|
+
z-index: 10;
|
|
99
|
+
width: 100%;
|
|
100
|
+
max-width: 80rem;
|
|
101
|
+
min-height: 200px;
|
|
102
|
+
display: flex;
|
|
103
|
+
flex-direction: column;
|
|
104
|
+
gap: 1rem;
|
|
105
|
+
margin-bottom: 3rem;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
@media (min-width: 1024px) {
|
|
109
|
+
.excuse-generator-slots {
|
|
110
|
+
flex-direction: row;
|
|
111
|
+
align-items: center;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
.excuse-generator-slot {
|
|
116
|
+
position: relative;
|
|
117
|
+
flex: 1;
|
|
118
|
+
min-width: 200px;
|
|
119
|
+
background-color: rgba(255, 255, 255, 0.6);
|
|
120
|
+
backdrop-filter: blur(4px);
|
|
121
|
+
border-radius: 1rem;
|
|
122
|
+
padding: 1.5rem;
|
|
123
|
+
border: 2px solid var(--excuse-generator-border);
|
|
124
|
+
box-shadow: 0 10px 15px -3px var(--excuse-generator-shadow);
|
|
125
|
+
display: flex;
|
|
126
|
+
align-items: center;
|
|
127
|
+
justify-content: center;
|
|
128
|
+
text-align: center;
|
|
129
|
+
transition-property: all;
|
|
130
|
+
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
|
|
131
|
+
transition-duration: 300ms;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
.theme-dark .excuse-generator-slot {
|
|
135
|
+
background-color: rgba(15, 23, 42, 0.6);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
.excuse-generator-slot:hover {
|
|
139
|
+
transform: scale(1.05);
|
|
140
|
+
background-color: var(--excuse-generator-bg);
|
|
141
|
+
border-color: var(--excuse-generator-primary);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
.excuse-generator-slot-label {
|
|
145
|
+
position: absolute;
|
|
146
|
+
top: 0.5rem;
|
|
147
|
+
left: 50%;
|
|
148
|
+
transform: translateX(-50%);
|
|
149
|
+
color: var(--excuse-generator-text-dim);
|
|
150
|
+
font-size: 0.75rem;
|
|
151
|
+
line-height: 1rem;
|
|
152
|
+
text-transform: uppercase;
|
|
153
|
+
letter-spacing: 0.1em;
|
|
154
|
+
opacity: 0.5;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
.excuse-generator-slot-text {
|
|
158
|
+
font-size: 1.5rem;
|
|
159
|
+
font-weight: 900;
|
|
160
|
+
color: var(--excuse-generator-primary);
|
|
161
|
+
line-height: 1.25;
|
|
162
|
+
overflow-wrap: break-word;
|
|
163
|
+
width: 100%;
|
|
164
|
+
transition-property: all;
|
|
165
|
+
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
|
|
166
|
+
transition-duration: 100ms;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
@media (min-width: 768px) {
|
|
170
|
+
.excuse-generator-slot-text {
|
|
171
|
+
font-size: 1.875rem;
|
|
172
|
+
line-height: 2.25rem;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
.excuse-generator-slot-text.blur {
|
|
177
|
+
filter: blur(3px);
|
|
178
|
+
opacity: 0.7;
|
|
179
|
+
transform: scale(0.95);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
.excuse-generator-separator {
|
|
183
|
+
display: none;
|
|
184
|
+
font-size: 2rem;
|
|
185
|
+
color: var(--excuse-generator-border);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
@media (min-width: 1024px) {
|
|
189
|
+
.excuse-generator-separator {
|
|
190
|
+
display: block;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
.excuse-generator-separator-mobile {
|
|
195
|
+
display: flex;
|
|
196
|
+
justify-content: center;
|
|
197
|
+
color: var(--excuse-generator-border);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
@media (min-width: 1024px) {
|
|
201
|
+
.excuse-generator-separator-mobile {
|
|
202
|
+
display: none;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
.excuse-generator-actions {
|
|
207
|
+
position: relative;
|
|
208
|
+
z-index: 10;
|
|
209
|
+
display: flex;
|
|
210
|
+
flex-direction: column;
|
|
211
|
+
gap: 1.5rem;
|
|
212
|
+
align-items: center;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
.excuse-generator-btn {
|
|
216
|
+
position: relative;
|
|
217
|
+
padding-left: 2.5rem;
|
|
218
|
+
padding-right: 2.5rem;
|
|
219
|
+
padding-top: 1.25rem;
|
|
220
|
+
padding-bottom: 1.25rem;
|
|
221
|
+
background-color: #0f172a;
|
|
222
|
+
border-radius: 1rem;
|
|
223
|
+
color: #fff;
|
|
224
|
+
font-weight: 900;
|
|
225
|
+
font-size: 1.25rem;
|
|
226
|
+
line-height: 1.75rem;
|
|
227
|
+
box-shadow: 0 25px 50px -12px rgba(236, 72, 153, 0.2);
|
|
228
|
+
border: none;
|
|
229
|
+
cursor: pointer;
|
|
230
|
+
overflow: hidden;
|
|
231
|
+
transition-property: all;
|
|
232
|
+
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
|
|
233
|
+
transition-duration: 300ms;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
.theme-dark .excuse-generator-btn {
|
|
237
|
+
background-color: #f8fafc;
|
|
238
|
+
color: #0f172a;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
.excuse-generator-btn:hover {
|
|
242
|
+
transform: translateY(-0.25rem);
|
|
243
|
+
box-shadow: 0 25px 50px -12px rgba(236, 72, 153, 0.4);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
.excuse-generator-btn:active {
|
|
247
|
+
transform: translateY(0);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
.excuse-generator-btn:disabled {
|
|
251
|
+
opacity: 0.7;
|
|
252
|
+
cursor: not-allowed;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
.excuse-generator-btn-content {
|
|
256
|
+
position: relative;
|
|
257
|
+
z-index: 10;
|
|
258
|
+
display: flex;
|
|
259
|
+
align-items: center;
|
|
260
|
+
gap: 0.75rem;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
.excuse-generator-btn-gradient {
|
|
264
|
+
position: absolute;
|
|
265
|
+
inset: 0;
|
|
266
|
+
background-image: linear-gradient(to right, var(--excuse-generator-primary), var(--excuse-generator-accent), var(--excuse-generator-primary));
|
|
267
|
+
opacity: 0;
|
|
268
|
+
transition-property: opacity;
|
|
269
|
+
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
|
|
270
|
+
transition-duration: 300ms;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
.excuse-generator-btn:hover .excuse-generator-btn-gradient {
|
|
274
|
+
opacity: 1;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
.excuse-generator-btn:hover .excuse-generator-btn-content {
|
|
278
|
+
color: var(--excuse-generator-primary-on);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
.excuse-generator-copy {
|
|
282
|
+
background: none;
|
|
283
|
+
border: none;
|
|
284
|
+
cursor: pointer;
|
|
285
|
+
color: var(--excuse-generator-text-dim);
|
|
286
|
+
font-size: 0.875rem;
|
|
287
|
+
line-height: 1.25rem;
|
|
288
|
+
font-weight: 700;
|
|
289
|
+
display: flex;
|
|
290
|
+
align-items: center;
|
|
291
|
+
gap: 0.5rem;
|
|
292
|
+
transition-property: all;
|
|
293
|
+
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
|
|
294
|
+
transition-duration: 200ms;
|
|
295
|
+
opacity: 0;
|
|
296
|
+
pointer-events: none;
|
|
297
|
+
transform: translateY(0.5rem);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
.excuse-generator-copy.visible {
|
|
301
|
+
opacity: 1;
|
|
302
|
+
pointer-events: auto;
|
|
303
|
+
transform: translateY(0);
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
.excuse-generator-copy:hover {
|
|
307
|
+
color: var(--excuse-generator-primary);
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
.excuse-generator-copy-success {
|
|
311
|
+
color: var(--excuse-generator-success);
|
|
312
|
+
font-weight: 700;
|
|
313
|
+
display: flex;
|
|
314
|
+
align-items: center;
|
|
315
|
+
gap: 0.5rem;
|
|
316
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { Bibliography as SharedBibliography } from '@jjlmoya/utils-shared';
|
|
3
|
+
import { fortuneCookie, type FortuneCookieLocaleContent } 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 localeContentLoader = (fortuneCookie.i18n as Record<string, () => Promise<FortuneCookieLocaleContent>>)[locale];
|
|
12
|
+
const content = localeContentLoader ? await localeContentLoader() : null;
|
|
13
|
+
if (!content) return null;
|
|
14
|
+
|
|
15
|
+
const { bibliography } = content;
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
<SharedBibliography links={bibliography} />
|