diff --git a/Mindforge.Web/index.html b/Mindforge.Web/index.html index 1aedf9e..3812265 100644 --- a/Mindforge.Web/index.html +++ b/Mindforge.Web/index.html @@ -5,6 +5,9 @@ + + + Mindforge diff --git a/Mindforge.Web/src/app.css b/Mindforge.Web/src/app.css index 186e0ee..94bf2c6 100644 --- a/Mindforge.Web/src/app.css +++ b/Mindforge.Web/src/app.css @@ -3,60 +3,23 @@ flex-direction: column; align-items: center; justify-content: center; - animation: fadeIn 0.8s ease-out; + padding: 2rem 1rem; } .hero-title { - font-size: 4rem; - font-weight: 700; - text-transform: uppercase; - letter-spacing: 2px; - margin-bottom: 1rem; - text-shadow: 0 4px 15px rgba(0, 0, 0, 0.3); - background: linear-gradient(90deg, #f4f5f5, #00b4d8); - -webkit-background-clip: text; - background-clip: text; - -webkit-text-fill-color: transparent; + font-family: Georgia, "Times New Roman", serif; + font-size: clamp(32px, 5vw, 56px); + font-weight: 800; + letter-spacing: -.03em; + color: var(--ink); + margin-bottom: 0.5rem; } .hero-subtitle { - font-size: 1.5rem; - color: rgba(244, 245, 245, 0.8); - font-weight: 300; - letter-spacing: 1px; -} - -.module-content { - width: 100%; - max-width: 800px; - margin: 0 auto; - animation: slideUp 0.5s ease-out; -} - -.subtitle { - font-size: 1.2rem; - color: rgba(244, 245, 245, 0.8); - margin-bottom: 2rem; -} - -.placeholder-box { - background: rgba(255, 255, 255, 0.05); - border: 1px dashed rgba(255, 255, 255, 0.2); - border-radius: 12px; - padding: 4rem 2rem; - font-size: 1.5rem; - color: rgba(244, 245, 245, 0.5); + font-size: 18px; + color: var(--muted); + font-weight: 400; + max-width: 480px; text-align: center; - backdrop-filter: blur(4px); - -webkit-backdrop-filter: blur(4px); -} - -@keyframes fadeIn { - from { opacity: 0; } - to { opacity: 1; } -} - -@keyframes slideUp { - from { opacity: 0; transform: translateY(20px); } - to { opacity: 1; transform: translateY(0); } + line-height: 1.6; } diff --git a/Mindforge.Web/src/app.tsx b/Mindforge.Web/src/app.tsx index 70fa84c..04f4afa 100644 --- a/Mindforge.Web/src/app.tsx +++ b/Mindforge.Web/src/app.tsx @@ -11,32 +11,30 @@ export function App() { const [activeModule, setActiveModule] = useState<'home' | 'verificador' | 'flashcards' | 'revisao-flashcards' | 'revisao-espacada'>('home'); return ( - <> -
setActiveModule('home')} /> -
- -
-
-
- Mindforge Banner -

Mindforge - Forja Mental

-

Sua ferramenta de estudos para concursos.

-
+
+ +
+
setActiveModule('home')} /> +
+
+ Mindforge Banner +

Mindforge — Forja Mental

+

Sua ferramenta de estudos para concursos.

-
- -
-
- -
-
- -
-
- -
-
+
+
+ +
+
+ +
+
+ +
+
+ +
- + ); } diff --git a/Mindforge.Web/src/components/Button.css b/Mindforge.Web/src/components/Button.css index 64f5d55..6ddf1ed 100644 --- a/Mindforge.Web/src/components/Button.css +++ b/Mindforge.Web/src/components/Button.css @@ -1,32 +1,30 @@ .btn { - font-family: var(--font-main); - font-weight: 700; - font-size: 1rem; - padding: 0.8rem 1.5rem; - border-radius: 8px; - border: none; + font-family: inherit; + font-weight: 850; + font-size: 13px; + padding: 0 18px; + min-height: 42px; + border-radius: 999px; cursor: pointer; outline: none; - transition: all 0.3s ease; - backdrop-filter: blur(8px); - -webkit-backdrop-filter: blur(8px); - box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); - letter-spacing: 0.5px; + transition: .25s var(--ease); + letter-spacing: 0; + display: inline-flex; + align-items: center; + justify-content: center; + gap: 9px; + border: 1px solid rgba(96, 67, 28, .15); + background: rgba(255,255,255,.62); + color: #4b3b27; + box-shadow: inset 0 1px 0 rgba(255,255,255,.72); } -.btn-primary { - background: rgba(255, 255, 255, 0.1); - color: var(--color-text-creamy); - border: 1px solid rgba(255, 255, 255, 0.2); -} - -.btn-primary:hover { - background: rgba(255, 255, 255, 0.2); +.btn:hover { transform: translateY(-2px); - box-shadow: 0 6px 12px rgba(0, 0, 0, 0.15); + box-shadow: 0 10px 20px rgba(86, 57, 17, .12), inset 0 1px 0 rgba(255,255,255,.72); } -.btn-primary:active { +.btn:active { transform: translateY(0); } @@ -37,12 +35,21 @@ box-shadow: none; } -.btn-secondary { - background: transparent; - color: var(--color-text-creamy); - border: 1px solid rgba(255, 255, 255, 0.1); +.btn-primary { + background: linear-gradient(135deg, var(--blue), var(--blue-deep)); + color: #fff; + border-color: rgba(37, 95, 141, .3); + box-shadow: 0 16px 34px rgba(63, 44, 20, .18), inset 0 1px 0 rgba(255,255,255,.28); + font-weight: 950; + font-size: 14px; + min-height: 48px; } -.btn-secondary:hover { - background: rgba(255, 255, 255, 0.05); +.btn-primary:hover { + transform: translateY(-3px); + box-shadow: 0 22px 42px rgba(63, 44, 20, .22), inset 0 1px 0 rgba(255,255,255,.28); +} + +.btn-secondary { + background: rgba(255,255,255,.45); } diff --git a/Mindforge.Web/src/components/FileTreeComponent.css b/Mindforge.Web/src/components/FileTreeComponent.css index f221126..440a11b 100644 --- a/Mindforge.Web/src/components/FileTreeComponent.css +++ b/Mindforge.Web/src/components/FileTreeComponent.css @@ -1,11 +1,13 @@ .file-tree { - background: rgba(255, 255, 255, 0.04); - border: 1px solid rgba(255, 255, 255, 0.1); - border-radius: 8px; + background: rgba(255, 250, 239, .68); + border: 1px solid rgba(104, 69, 22, .13); + border-radius: var(--radius-md); padding: 0.75rem; max-height: 380px; overflow-y: auto; font-size: 0.9rem; + box-shadow: 0 12px 32px rgba(86, 57, 17, .06); + backdrop-filter: blur(12px); } .tree-folder { @@ -17,30 +19,30 @@ align-items: center; gap: 6px; padding: 4px 6px; - border-radius: 4px; + border-radius: 8px; cursor: pointer; user-select: none; - color: rgba(255, 255, 255, 0.85); - font-weight: 600; + color: var(--ink); + font-weight: 700; } .tree-folder-header:hover { - background: rgba(255, 255, 255, 0.07); + background: rgba(255,255,255,.52); } .tree-folder-arrow { font-size: 0.75rem; - color: rgba(255, 255, 255, 0.5); + color: var(--muted); width: 12px; } .tree-folder-name { - color: rgba(255, 255, 255, 0.85); + color: var(--ink); } .tree-folder-children { padding-left: 18px; - border-left: 1px solid rgba(255, 255, 255, 0.08); + border-left: 1px solid var(--line); margin-left: 6px; } @@ -53,18 +55,18 @@ align-items: center; gap: 8px; padding: 4px 6px; - border-radius: 4px; + border-radius: 8px; cursor: pointer; - color: rgba(255, 255, 255, 0.7); + color: var(--muted); } .tree-file-label:hover { - background: rgba(255, 255, 255, 0.06); - color: rgba(255, 255, 255, 0.9); + background: rgba(255,255,255,.45); + color: var(--ink); } .tree-file-label input[type="checkbox"] { - accent-color: var(--color-accent); + accent-color: var(--blue); width: 14px; height: 14px; cursor: pointer; @@ -79,11 +81,11 @@ .tree-error, .tree-empty { padding: 1rem; - color: rgba(255, 255, 255, 0.5); + color: var(--muted); font-size: 0.9rem; text-align: center; } .tree-error { - color: #ff7b72; + color: var(--red); } diff --git a/Mindforge.Web/src/components/FlashcardComponent.css b/Mindforge.Web/src/components/FlashcardComponent.css index 4d9a453..0a8cd5e 100644 --- a/Mindforge.Web/src/components/FlashcardComponent.css +++ b/Mindforge.Web/src/components/FlashcardComponent.css @@ -2,27 +2,37 @@ width: 100%; max-width: 900px; margin: 0 auto; - animation: slideUp 0.5s ease-out; display: flex; flex-direction: column; gap: 1.5rem; } .flashcard-title { - font-size: 2.5rem; + font-family: Georgia, "Times New Roman", serif; + font-size: clamp(24px, 3.5vw, 40px); + font-weight: 800; + letter-spacing: -.03em; + color: var(--ink); + margin: 0; +} + +.flashcard-subtitle { + color: var(--muted); + font-size: 15px; + line-height: 1.55; + max-width: 660px; } .flashcard-form { display: flex; flex-direction: column; gap: 1.2rem; - background: rgba(255, 255, 255, 0.06); + background: rgba(255, 250, 239, .68); padding: 2rem; - border-radius: 12px; - border: 1px solid rgba(255, 255, 255, 0.14); - box-shadow: 0 14px 36px rgba(0, 0, 0, 0.2); - backdrop-filter: blur(10px); - -webkit-backdrop-filter: blur(10px); + border-radius: var(--radius-lg); + border: 1px solid rgba(104, 69, 22, .13); + box-shadow: 0 18px 48px rgba(86, 57, 17, .09); + backdrop-filter: blur(16px); } .input-group { @@ -34,12 +44,13 @@ .input-group label { font-weight: 700; - color: var(--color-text-creamy); + color: var(--ink); + font-size: 0.9rem; } .selection-meta { font-size: 0.85rem; - color: rgba(255, 255, 255, 0.55); + color: var(--muted); margin-top: 0.4rem; } @@ -55,7 +66,7 @@ appearance: none; width: 100%; height: 8px; - background: rgba(0, 0, 0, 0.3); + background: rgba(95, 72, 35, .14); border-radius: 999px; outline: none; } @@ -66,34 +77,37 @@ width: 22px; height: 22px; border-radius: 50%; - background: #f4f5f5; - border: 2px solid rgba(var(--color-accent-rgb), 0.9); - box-shadow: 0 6px 16px rgba(var(--color-accent-rgb), 0.35); + background: var(--paper); + border: 2px solid var(--blue); + box-shadow: 0 6px 16px rgba(63, 124, 172, .25); cursor: pointer; } .amount-display { - font-weight: 700; - color: var(--color-accent); + font-weight: 800; + color: var(--blue-deep); min-width: 44px; text-align: right; font-size: 1.1rem; } .flashcard-error { - color: #ff9c96; - margin-top: 1rem; + color: var(--red); + font-size: 0.9rem; + background: rgba(183, 91, 77, 0.08); + border: 1px solid rgba(183, 91, 77, 0.2); + border-radius: var(--radius-md); + padding: 0.8rem 1rem; } .radio-group { display: flex; flex-wrap: wrap; - background: rgba(0, 0, 0, 0.28); + background: rgba(95, 72, 35, .08); padding: 4px; - border-radius: 12px; - border: 1px solid rgba(255, 255, 255, 0.12); + border-radius: var(--radius-md); + border: 1px solid var(--line); width: fit-content; - overflow: hidden; gap: 4px; } @@ -115,54 +129,57 @@ justify-content: center; padding: 0.8rem 1.2rem; cursor: pointer; - border-radius: 8px; + border-radius: 12px; font-size: 0.95rem; - font-weight: 700; - color: rgba(255, 255, 255, 0.65); - transition: all 0.25s ease; + font-weight: 800; + color: var(--muted); + transition: all .25s var(--ease); white-space: nowrap; } .radio-item input[type="radio"]:checked + .radio-label { - background: var(--color-accent); - color: #012f3b; - box-shadow: 0 4px 12px rgba(var(--color-accent-rgb), 0.35); + background: var(--blue); + color: #fff; + box-shadow: 0 8px 20px rgba(63, 124, 172, .25); } .radio-item:hover .radio-label { - color: rgba(255, 255, 255, 0.92); + color: var(--ink); } -.spinner-container { +.loading-indicator { display: flex; - flex-direction: column; align-items: center; + gap: 10px; justify-content: center; - padding: 2rem 0; - gap: 1rem; + padding: 2rem; + color: var(--muted); + font-size: 0.95rem; } -.spinner { - width: 50px; - height: 50px; - border: 4px solid rgba(255, 255, 255, 0.12); - border-left-color: var(--color-accent); +.loading-dot { + width: 10px; + height: 10px; border-radius: 50%; - animation: spin 1s linear infinite; + background: var(--blue); + box-shadow: 0 0 0 4px rgba(63, 124, 172, .16); } .flashcard-result-panel { - background: rgba(255, 255, 255, 0.06); - border-radius: 12px; - border: 1px solid rgba(255, 255, 255, 0.14); + background: rgba(255, 250, 239, .68); + border-radius: var(--radius-lg); + border: 1px solid rgba(104, 69, 22, .13); padding: 1.5rem; text-align: left; + box-shadow: 0 18px 48px rgba(86, 57, 17, .09); + backdrop-filter: blur(16px); } .flashcard-result-panel h3 { margin: 0 0 1rem 0; font-size: 1.1rem; - color: rgba(255, 255, 255, 0.94); + font-family: Georgia, serif; + color: var(--ink); } .flashcard-result-list { @@ -172,9 +189,9 @@ } .flashcard-result-item { - border: 1px solid rgba(255, 255, 255, 0.12); - background: rgba(0, 0, 0, 0.2); - border-radius: 10px; + border: 1px solid rgba(82, 54, 17, .12); + background: rgba(255,255,255,.52); + border-radius: var(--radius-md); padding: 0.85rem 1rem; } @@ -184,10 +201,12 @@ align-items: center; gap: 1rem; font-size: 0.95rem; + color: var(--ink); } .flashcard-result-header span { - color: rgba(255, 255, 255, 0.72); + color: var(--muted); + font-size: 0.85rem; } .flashcard-result-meta { @@ -196,16 +215,7 @@ flex-wrap: wrap; gap: 0.85rem; font-size: 0.85rem; - color: rgba(255, 255, 255, 0.65); -} - -@keyframes spin { - 0% { - transform: rotate(0deg); - } - 100% { - transform: rotate(360deg); - } + color: var(--muted); } @media (max-width: 720px) { diff --git a/Mindforge.Web/src/components/FlashcardComponent.tsx b/Mindforge.Web/src/components/FlashcardComponent.tsx index d487135..4f7f91c 100644 --- a/Mindforge.Web/src/components/FlashcardComponent.tsx +++ b/Mindforge.Web/src/components/FlashcardComponent.tsx @@ -49,8 +49,8 @@ export function FlashcardComponent() { return (
-

Gerador de Flashcards

-

Selecione os arquivos do repositorio para gerar bibliotecas de flashcards.

+

Gerador de Flashcards

+

Selecione os arquivos do repositorio para gerar bibliotecas de flashcards.

@@ -111,7 +111,7 @@ export function FlashcardComponent() {
- @@ -119,9 +119,9 @@ export function FlashcardComponent() {
{loading && ( -
-
-

Gerando os flashcards com IA e salvando no banco. Aguarde...

+
+ + Gerando os flashcards com IA e salvando no banco. Aguarde...
)} diff --git a/Mindforge.Web/src/components/FlashcardReviewComponent.css b/Mindforge.Web/src/components/FlashcardReviewComponent.css index cfa7b62..b732bf4 100644 --- a/Mindforge.Web/src/components/FlashcardReviewComponent.css +++ b/Mindforge.Web/src/components/FlashcardReviewComponent.css @@ -1,40 +1,47 @@ .review-container { width: 100%; - max-width: 900px; - margin: 0 auto; - display: flex; - flex-direction: column; - gap: 1.2rem; - animation: slideUp 0.45s ease-out; } .review-title { - font-size: 2.35rem; + font-family: Georgia, "Times New Roman", serif; + font-size: clamp(24px, 3.5vw, 40px); + font-weight: 800; + letter-spacing: -.03em; + color: var(--ink); + margin: 0 0 0.3rem; +} + +.review-subtitle { + color: var(--muted); + font-size: 15px; + line-height: 1.55; + max-width: 660px; + margin-bottom: 1.2rem; } .review-error { - color: #ff9c96; - text-align: left; - background: rgba(255, 69, 58, 0.12); - border: 1px solid rgba(255, 69, 58, 0.4); - border-radius: 10px; + color: var(--red); + font-size: 0.9rem; + background: rgba(183, 91, 77, 0.08); + border: 1px solid rgba(183, 91, 77, 0.2); + border-radius: var(--radius-md); padding: 0.8rem 1rem; + margin-bottom: 1rem; } -.review-select-panel, -.review-session-panel { - background: rgba(255, 255, 255, 0.06); - border: 1px solid rgba(255, 255, 255, 0.14); - border-radius: 12px; - padding: 1.25rem; +/* Library Selection */ +.review-select-panel { + background: rgba(255, 250, 239, .68); + border: 1px solid rgba(104, 69, 22, .13); + border-radius: var(--radius-lg); + padding: 1.5rem; + box-shadow: 0 18px 48px rgba(86, 57, 17, .09); + backdrop-filter: blur(16px); text-align: left; - backdrop-filter: blur(10px); - -webkit-backdrop-filter: blur(10px); - box-shadow: 0 14px 36px rgba(0, 0, 0, 0.18); } .review-state { - color: rgba(255, 255, 255, 0.72); + color: var(--muted); font-size: 0.95rem; } @@ -44,16 +51,17 @@ } .review-subject-section { - border: 1px solid rgba(255, 255, 255, 0.12); - background: rgba(0, 0, 0, 0.18); - border-radius: 10px; + border: 1px solid rgba(82, 54, 17, .10); + background: rgba(255,255,255,.45); + border-radius: var(--radius-md); padding: 0.9rem; } .review-subject-section h3 { margin: 0 0 0.8rem; font-size: 1rem; - color: rgba(255, 255, 255, 0.95); + font-family: Georgia, serif; + color: var(--ink); } .review-library-list { @@ -65,18 +73,18 @@ display: flex; align-items: flex-start; gap: 0.7rem; - border: 1px solid rgba(255, 255, 255, 0.08); - border-radius: 10px; + border: 1px solid rgba(82, 54, 17, .10); + border-radius: var(--radius-md); padding: 0.6rem 0.7rem; cursor: pointer; - background: rgba(255, 255, 255, 0.03); + background: rgba(255,255,255,.48); } .review-library-item input[type="checkbox"] { margin-top: 0.15rem; width: 16px; height: 16px; - accent-color: var(--color-accent); + accent-color: var(--blue); } .review-library-texts { @@ -87,10 +95,11 @@ .review-library-texts strong { font-size: 0.95rem; + color: var(--ink); } .review-library-texts span { - color: rgba(255, 255, 255, 0.68); + color: var(--muted); font-size: 0.84rem; } @@ -100,72 +109,548 @@ justify-content: flex-end; } -.review-progress { - display: flex; - align-items: center; - gap: 0.9rem; - margin-bottom: 0.8rem; +/* Session Panel - Content Grid */ +.content-grid { + display: grid; + grid-template-columns: minmax(0, 1fr) 330px; + gap: 24px; + align-items: start; } -.review-progress span { - font-size: 0.9rem; - color: rgba(255, 255, 255, 0.8); - min-width: 60px; -} - -.review-progress-bar { - width: 100%; - height: 8px; - border-radius: 999px; - background: rgba(0, 0, 0, 0.25); +/* Review Panel (main flashcard area) */ +.review-panel { + position: relative; + min-height: 650px; + padding: clamp(18px, 3vw, 34px); + border-radius: var(--radius-xl); + background: + linear-gradient(145deg, rgba(255,255,255,.54), rgba(255,240,202,.46)), + radial-gradient(circle at 15% 20%, rgba(255, 201, 101, .25), transparent 35%), + radial-gradient(circle at 90% 10%, rgba(63, 124, 172, .16), transparent 32%); + border: 1px solid rgba(104, 69, 22, .13); + box-shadow: var(--shadow); overflow: hidden; } -.review-progress-fill { - height: 100%; - background: linear-gradient(90deg, rgba(var(--color-accent-rgb), 0.8), rgba(var(--color-accent-rgb), 1)); +.review-panel::before, +.review-panel::after { + content: ""; + position: absolute; border-radius: 999px; - transition: width 0.25s ease; + pointer-events: none; + filter: blur(2px); + opacity: .5; } -.review-card { - border: 1px solid rgba(255, 255, 255, 0.16); - border-radius: 10px; - padding: 1rem; - background: rgba(0, 0, 0, 0.2); - min-height: 250px; +.review-panel::before { + width: 220px; + height: 220px; + right: -92px; + top: 100px; + background: rgba(63, 124, 172, .13); } -.review-card header { - margin-bottom: 0.8rem; +.review-panel::after { + width: 180px; + height: 180px; + left: -70px; + bottom: 60px; + background: rgba(199, 149, 57, .18); } -.review-card small { - color: rgba(255, 255, 255, 0.62); +.session-header { + position: relative; + z-index: 1; + display: flex; + justify-content: space-between; + align-items: flex-start; + gap: 16px; + margin-bottom: 24px; } -.review-card h3 { - margin: 0 0 0.4rem; - font-size: 0.98rem; - color: rgba(255, 255, 255, 0.9); +.session-title h3 { + margin: 0; + font-family: Georgia, serif; + font-size: clamp(24px, 3vw, 36px); + letter-spacing: -.04em; + color: var(--ink); } -.review-card p { - margin: 0 0 1rem; - font-size: 1rem; +.session-title p { + max-width: 660px; + color: var(--muted); + font-size: 15px; + line-height: 1.55; + margin: 4px 0 0; +} + +.score-pill { + min-width: 76px; + padding: 11px 12px; + border-radius: 18px; + background: rgba(255,255,255,.55); + border: 1px solid rgba(100, 65, 18, .12); + text-align: center; + box-shadow: inset 0 1px 0 rgba(255,255,255,.72); +} + +.score-pill b { + display: block; + font-size: 19px; + color: var(--ink); +} + +.score-pill span { + color: var(--muted); + font-size: 10px; + font-weight: 950; + letter-spacing: .12em; + text-transform: uppercase; +} + +/* Stage (flashcard container) */ +.stage { + position: relative; + z-index: 1; + display: grid; + place-items: center; + min-height: 390px; + perspective: 1400px; + padding: 22px 0; +} + +/* Flashcard */ +.flashcard { + position: relative; + width: min(680px, 100%); + min-height: 355px; + cursor: pointer; + transform-style: preserve-3d; + transition: transform .78s var(--ease), filter .35s var(--ease); + outline: none; +} + +.flashcard:hover .card-face { + border-color: rgba(63, 124, 172, .28); +} + +.flashcard.is-flipped { + transform: rotateY(180deg); +} + +.flashcard.is-reviewed { + animation: cardExit .58s var(--ease); +} + +.card-face { + position: absolute; + inset: 0; + min-height: 355px; + display: flex; + flex-direction: column; + justify-content: space-between; + padding: clamp(24px, 5vw, 42px); + border-radius: 30px; + backface-visibility: hidden; + background: + linear-gradient(90deg, rgba(168, 111, 36, .09) 0 1px, transparent 1px 22px), + linear-gradient(rgba(168, 111, 36, .08) 0 1px, transparent 1px 30px), + linear-gradient(145deg, #fffaf0, #f5dfaa); + border: 1px solid rgba(82, 54, 17, .18); + box-shadow: var(--card-shadow), inset 0 0 0 8px rgba(255,255,255,.24); + overflow: hidden; +} + +.card-face::before { + content: ""; + position: absolute; + inset: 18px; + border: 1px dashed rgba(82, 54, 17, .18); + border-radius: 22px; + pointer-events: none; +} + +.card-face::after { + content: ""; + position: absolute; + width: 140px; + height: 140px; + right: -46px; + bottom: -50px; + border-radius: 50%; + background: radial-gradient(circle, rgba(199,149,57,.25), transparent 66%); + pointer-events: none; +} + +.card-back { + transform: rotateY(180deg); + background: + linear-gradient(90deg, rgba(63,124,172,.08) 0 1px, transparent 1px 22px), + linear-gradient(rgba(63,124,172,.07) 0 1px, transparent 1px 30px), + linear-gradient(145deg, #fffaf1, #dfeef2); +} + +.card-meta { + position: relative; + z-index: 1; + display: flex; + justify-content: space-between; + gap: 12px; + align-items: center; + color: var(--muted); + font-size: 12px; + font-weight: 950; + letter-spacing: .12em; + text-transform: uppercase; +} + +.tag { + display: inline-flex; + align-items: center; + gap: 8px; + padding: 9px 12px; + border-radius: 999px; + background: rgba(255,255,255,.54); + border: 1px solid rgba(82, 54, 17, .12); +} + +.card-question, +.card-answer { + position: relative; + z-index: 1; + display: grid; + align-content: center; + gap: 12px; + min-height: 190px; + color: var(--ink); + font-family: "Segoe UI", Inter, Avenir, system-ui, -apple-system, BlinkMacSystemFont, sans-serif; + font-size: clamp(17px, 2.2vw, 20px); line-height: 1.55; } -.review-session-actions { - margin-top: 1rem; +.card-footer { + position: relative; + z-index: 1; display: flex; - gap: 0.65rem; - flex-wrap: wrap; + justify-content: space-between; + gap: 14px; + align-items: center; + color: var(--muted); + font-size: 13px; + font-weight: 800; } -@media (max-width: 740px) { - .review-session-actions { - display: grid; +.spacebar { + padding: 4px 9px; + border-radius: 8px; + color: #4f3a1d; + background: rgba(255,255,255,.58); + border: 1px solid rgba(82, 54, 17, .14); + box-shadow: inset 0 -2px 0 rgba(82, 54, 17, .08); + font-size: 11px; + font-weight: 950; + letter-spacing: .08em; + text-transform: uppercase; +} + +/* Controls */ +.controls { + position: relative; + z-index: 1; + display: flex; + justify-content: center; + gap: 16px; + flex-wrap: wrap; + opacity: .36; + transform: translateY(10px); + pointer-events: none; + transition: .35s var(--ease); + margin-top: 20px; +} + +.controls.ready { + opacity: 1; + transform: translateY(0); + pointer-events: auto; +} + +.review-button { + position: relative; + min-width: 170px; + min-height: 60px; + display: inline-flex; + align-items: center; + justify-content: center; + gap: 10px; + border: 0; + border-radius: 20px; + color: white; + font-family: inherit; + font-weight: 950; + font-size: 16px; + cursor: pointer; + box-shadow: 0 16px 34px rgba(63, 44, 20, .18), inset 0 1px 0 rgba(255,255,255,.32); + overflow: hidden; + transition: .25s var(--ease); +} + +.review-button.correct { + background: linear-gradient(135deg, var(--green), var(--green-deep)); +} + +.review-button.wrong { + background: linear-gradient(135deg, var(--red), var(--red-deep)); +} + +.review-button:hover { + transform: translateY(-4px); + box-shadow: 0 22px 42px rgba(63, 44, 20, .22), inset 0 1px 0 rgba(255,255,255,.32); +} + +.review-button::before { + content: ""; + position: absolute; + inset: -90% -40%; + background: linear-gradient(90deg, transparent, rgba(255,255,255,.32), transparent); + transform: rotate(20deg) translateX(-80%); + transition: .55s var(--ease); +} + +.review-button:hover::before { + transform: rotate(20deg) translateX(80%); +} + +.review-button:disabled { + opacity: 0.45; + cursor: not-allowed; + transform: none !important; +} + +/* Stamp */ +.stamp { + position: absolute; + right: 38px; + top: 36px; + z-index: 4; + padding: 12px 18px; + border: 4px double currentColor; + border-radius: 10px; + font-family: Georgia, serif; + font-size: 26px; + font-weight: 900; + letter-spacing: .08em; + text-transform: uppercase; + opacity: 0; + transform: rotate(-12deg) scale(1.3); + pointer-events: none; +} + +.stamp.correct { + color: var(--green-deep); +} + +.stamp.wrong { + color: var(--red-deep); +} + +.stamp.show { + animation: stampIn .7s var(--ease); +} + +/* Side Panel */ +.side-panel { + display: grid; + gap: 18px; +} + +.panel-card { + padding: 20px; + border-radius: 26px; + background: rgba(255, 250, 239, .68); + border: 1px solid rgba(104, 69, 22, .13); + box-shadow: 0 18px 48px rgba(86, 57, 17, .09); + backdrop-filter: blur(16px); +} + +.panel-card h3 { + margin: 0 0 14px; + font-family: Georgia, serif; + font-size: 22px; + letter-spacing: -.03em; + color: var(--ink); +} + +.stat-grid { + display: grid; + grid-template-columns: repeat(2, 1fr); + gap: 10px; +} + +.stat { + padding: 14px; + border-radius: 18px; + background: rgba(255,255,255,.52); + border: 1px solid rgba(82, 54, 17, .10); +} + +.stat b { + display: block; + font-size: 24px; + letter-spacing: -.04em; + color: var(--ink); +} + +.stat span { + color: var(--muted); + font-size: 11px; + font-weight: 950; + letter-spacing: .10em; + text-transform: uppercase; +} + +.track { + height: 14px; + border-radius: 999px; + overflow: hidden; + background: rgba(80, 54, 18, .12); + margin-top: 8px; +} + +.track span { + display: block; + height: 100%; + border-radius: inherit; + background: linear-gradient(90deg, var(--red), var(--gold), var(--green)); +} + +.queue { + display: grid; + gap: 10px; +} + +.queue-item { + display: grid; + grid-template-columns: 38px minmax(0, 1fr) auto; + gap: 10px; + align-items: center; + padding: 10px; + border-radius: 16px; + background: rgba(255,255,255,.48); + border: 1px solid rgba(82, 54, 17, .10); +} + +.queue-item strong { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + color: var(--ink); + font-size: 13px; +} + +.queue-item span { + color: var(--muted); + font-size: 11px; +} + +.queue-number { + width: 38px; + height: 38px; + display: grid; + place-items: center; + border-radius: 13px; + background: #fff5d8; + border: 1px solid rgba(82, 54, 17, .12); + color: #74531c; + font-family: Georgia, serif; + font-weight: 900; + font-size: 14px; +} + +/* Confetti Canvas */ +.confetti-canvas { + position: fixed; + inset: 0; + width: 100%; + height: 100%; + pointer-events: none; + z-index: 9999; +} + +/* Keyframes */ +@keyframes cardExit { + 0% { transform: translateX(0) rotateY(180deg) rotateZ(0); opacity: 1; } + 45% { transform: translateX(28px) rotateY(180deg) rotateZ(2deg); opacity: .9; } + 100% { transform: translateX(-32px) rotateY(180deg) rotateZ(-2deg); opacity: 0; } +} + +@keyframes stampIn { + 0% { opacity: 0; transform: rotate(-18deg) scale(1.8); } + 38% { opacity: 1; transform: rotate(-10deg) scale(.9); } + 58% { transform: rotate(-12deg) scale(1.04); } + 100% { opacity: 0; transform: rotate(-12deg) scale(1); } +} + +/* Session End */ +.session-end { + text-align: center; + padding: 3rem 1rem; + color: var(--muted); +} + +.session-end h3 { + font-family: Georgia, serif; + font-size: 28px; + color: var(--ink); + margin: 0 0 0.5rem; +} + +/* Navigation buttons row */ +.review-nav-row { + display: flex; + gap: 10px; + flex-wrap: wrap; + margin-top: 16px; + justify-content: center; +} + +@media (max-width: 1120px) { + .content-grid { + grid-template-columns: 1fr; + } + + .side-panel { grid-template-columns: repeat(2, minmax(0, 1fr)); } } + +@media (max-width: 760px) { + .session-header { + flex-direction: column; + } + + .review-panel { + min-height: auto; + } + + .stage { + min-height: 420px; + } + + .flashcard, + .card-face { + min-height: 380px; + } + + .side-panel { + grid-template-columns: 1fr; + } + + .review-button { + width: 100%; + } + + .controls { + flex-direction: column; + align-items: stretch; + } +} diff --git a/Mindforge.Web/src/components/FlashcardReviewComponent.tsx b/Mindforge.Web/src/components/FlashcardReviewComponent.tsx index 6bc9c60..77c5c07 100644 --- a/Mindforge.Web/src/components/FlashcardReviewComponent.tsx +++ b/Mindforge.Web/src/components/FlashcardReviewComponent.tsx @@ -1,4 +1,4 @@ -import { useEffect, useMemo, useState } from 'preact/hooks'; +import { useEffect, useMemo, useRef, useState } from 'preact/hooks'; import { MindforgeApiService, type FlashcardCard, @@ -26,15 +26,93 @@ function difficultyLabel(difficulty: string) { function shuffleCards(cards: FlashcardCard[]) { const shuffled = [...cards]; - for (let i = shuffled.length - 1; i > 0; i--) { const randomIndex = Math.floor(Math.random() * (i + 1)); [shuffled[i], shuffled[randomIndex]] = [shuffled[randomIndex], shuffled[i]]; } - return shuffled; } +interface ConfettiParticle { + x: number; + y: number; + vx: number; + vy: number; + color: string; + size: number; + life: number; + maxLife: number; + rotation: number; + rotationSpeed: number; +} + +function fireConfetti(canvas: HTMLCanvasElement) { + const ctx = canvas.getContext('2d'); + if (!ctx) return; + const c = ctx; + + const w = canvas.width = window.innerWidth; + const h = canvas.height = window.innerHeight; + + const colors = ['#4f8f5a', '#3f7cac', '#c79539', '#7e65a8', '#f2dfb3', '#b75b4d']; + const particles: ConfettiParticle[] = []; + + for (let i = 0; i < 120; i++) { + particles.push({ + x: Math.random() * w, + y: -20 - Math.random() * h * 0.5, + vx: (Math.random() - 0.5) * 6, + vy: Math.random() * 5 + 2, + color: colors[Math.floor(Math.random() * colors.length)], + size: Math.random() * 8 + 4, + life: 0, + maxLife: 80 + Math.random() * 60, + rotation: Math.random() * Math.PI * 2, + rotationSpeed: (Math.random() - 0.5) * 0.3, + }); + } + + let animating = true; + + function animate() { + if (!animating) return; + c.clearRect(0, 0, w, h); + + let alive = 0; + for (const p of particles) { + p.life++; + if (p.life >= p.maxLife) continue; + alive++; + p.x += p.vx; + p.y += p.vy; + p.vy += 0.08; + p.vx *= 0.995; + p.rotation += p.rotationSpeed; + const alpha = 1 - p.life / p.maxLife; + c.save(); + c.globalAlpha = alpha; + c.translate(p.x, p.y); + c.rotate(p.rotation); + c.fillStyle = p.color; + c.fillRect(-p.size / 2, -p.size / 4, p.size, p.size / 2); + c.restore(); + } + + if (alive > 0) { + requestAnimationFrame(animate); + } else { + c.clearRect(0, 0, w, h); + animating = false; + } + } + + requestAnimationFrame(animate); + + return () => { + animating = false; + }; +} + export function FlashcardReviewComponent() { const [libraries, setLibraries] = useState([]); const [selectedLibraryIds, setSelectedLibraryIds] = useState([]); @@ -45,6 +123,13 @@ export function FlashcardReviewComponent() { const [currentIndex, setCurrentIndex] = useState(0); const [showAnswer, setShowAnswer] = useState(false); const [submittingAnswer, setSubmittingAnswer] = useState(false); + const [cardExiting, setCardExiting] = useState(false); + const [stampState, setStampState] = useState<'correct' | 'wrong' | null>(null); + const [flipped, setFlipped] = useState(false); + const [sessionAnswers, setSessionAnswers] = useState>({}); + + const confettiRef = useRef(null); + const flashcardRef = useRef(null); useEffect(() => { let cancelled = false; @@ -75,6 +160,12 @@ export function FlashcardReviewComponent() { }; }, []); + useEffect(() => { + if (showAnswer && flipped) return; + if (!showAnswer && !flipped) return; + setFlipped(showAnswer); + }, [showAnswer, flipped]); + const groupedLibraries = useMemo(() => groupLibrariesBySubject(libraries), [libraries]); const libraryById = useMemo(() => { @@ -86,12 +177,34 @@ export function FlashcardReviewComponent() { ? ((currentIndex + 1) / sessionCards.length) * 100 : 0; + useEffect(() => { + function handleKeyDown(e: KeyboardEvent) { + if (sessionCards.length === 0 || !currentCard) return; + if (e.target instanceof HTMLInputElement || e.target instanceof HTMLSelectElement || e.target instanceof HTMLTextAreaElement) return; + + if (e.code === 'Space' || e.code === 'Enter') { + e.preventDefault(); + if (!showAnswer) { + setShowAnswer(true); + } + } else if (e.code === 'KeyC' && showAnswer && !submittingAnswer) { + e.preventDefault(); + registerReviewAnswer(true); + } else if (e.code === 'KeyW' && showAnswer && !submittingAnswer) { + e.preventDefault(); + registerReviewAnswer(false); + } + } + + window.addEventListener('keydown', handleKeyDown); + return () => window.removeEventListener('keydown', handleKeyDown); + }, [sessionCards.length, showAnswer, submittingAnswer, currentCard]); + const toggleLibrary = (libraryId: number) => { if (selectedLibraryIds.includes(libraryId)) { setSelectedLibraryIds(selectedLibraryIds.filter((id) => id !== libraryId)); return; } - setSelectedLibraryIds([...selectedLibraryIds, libraryId]); }; @@ -112,6 +225,10 @@ export function FlashcardReviewComponent() { setSessionCards(shuffleCards(response.cards)); setCurrentIndex(0); setShowAnswer(false); + setFlipped(false); + setStampState(null); + setCardExiting(false); + setSessionAnswers({}); } catch (err: any) { setError(err?.message || 'Falha ao iniciar sessao de revisao.'); } finally { @@ -124,48 +241,84 @@ export function FlashcardReviewComponent() { setCurrentIndex(0); setShowAnswer(false); setSubmittingAnswer(false); + setFlipped(false); + setStampState(null); + setCardExiting(false); + setSessionAnswers({}); }; const goToPrevious = () => { - if (currentIndex === 0) { - return; - } + if (currentIndex === 0) return; setCurrentIndex(currentIndex - 1); setShowAnswer(false); + setFlipped(false); + setStampState(null); + }; + + const advanceCard = () => { + if (currentIndex >= sessionCards.length - 1) { + endSession(); + return; + } + setCurrentIndex(currentIndex + 1); + setShowAnswer(false); + setFlipped(false); + setStampState(null); }; const registerReviewAnswer = async (correct: boolean) => { - if (!currentCard) { - return; - } + if (!currentCard) return; setSubmittingAnswer(true); setError(null); + if (correct) { + setStampState('correct'); + if (confettiRef.current) { + fireConfetti(confettiRef.current); + } + } else { + setStampState('wrong'); + } + + setCardExiting(true); + + setTimeout(() => { + setCardExiting(false); + setStampState(null); + }, 600); + try { await MindforgeApiService.recordFlashcardReviewAnswer({ cardId: currentCard.id, correct, }); - if (currentIndex >= sessionCards.length - 1) { - endSession(); - return; - } + setSessionAnswers((currentAnswers) => ({ + ...currentAnswers, + [currentCard.id]: correct, + })); - setCurrentIndex(currentIndex + 1); - setShowAnswer(false); + setTimeout(() => { + advanceCard(); + setSubmittingAnswer(false); + }, 580); } catch (err: any) { setError(err?.message || 'Falha ao registrar resposta da revisao.'); - } finally { setSubmittingAnswer(false); + setCardExiting(false); + setStampState(null); } }; + const correctCount = Object.values(sessionAnswers).filter(Boolean).length; + const remainingCount = sessionCards.length - currentIndex; + return (
-

Revisao Flashcards

-

Escolha as bibliotecas para estudar e inicie uma sessao de revisao.

+ +

Revisao Flashcards

+

Escolha as bibliotecas para estudar e inicie uma sessao de revisao.

{error &&
{error}
} @@ -212,57 +365,135 @@ export function FlashcardReviewComponent() { )} {sessionCards.length > 0 && currentCard && ( -
-
- {currentIndex + 1} / {sessionCards.length} -
-
+
+
+
+
+

Sessao de Revisao

+

{currentIndex + 1} de {sessionCards.length} cards

+
+
+ {correctCount} + Corretos +
+
+ +
+
{ if (!showAnswer && !submittingAnswer) setShowAnswer(true); }} + tabIndex={0} + role="button" + aria-label={showAnswer ? 'Flashcard revelado' : 'Clique ou pressione Espaco para revelar'} + > +
+ {stampState && ( +
+ {stampState === 'correct' ? 'Correto!' : 'Errado'} +
+ )} +
+ {libraryById.get(currentCard.libraryId)?.subject || 'Geral'} + {difficultyLabel(libraryById.get(currentCard.libraryId)?.difficulty || 'Easy')} +
+
+ {currentCard.front} +
+ +
+ +
+
+ Resposta + {difficultyLabel(libraryById.get(currentCard.libraryId)?.difficulty || 'Easy')} +
+
+ {currentCard.back} +
+ +
+
+
+ +
+ + +
+ +
+ +
-
-
- - {libraryById.get(currentCard.libraryId)?.fileName || 'Arquivo'} - - {' '} - {libraryById.get(currentCard.libraryId)?.subject || 'Geral'} - -
-

Frente

-

{currentCard.front}

- {showAnswer && ( - <> -

Verso

-

{currentCard.back}

- - )} -
+
+
+

Progresso

+
+
+ {currentIndex + 1}/{sessionCards.length} + Atual +
+
+ {correctCount} + Corretos +
+
+
+ +
+
-
- +
+

Fila

+
+ {sessionCards.slice(currentIndex, currentIndex + 5).map((card, idx) => ( +
+ {currentIndex + idx + 1} + {card.front.substring(0, 40)}{card.front.length > 40 ? '...' : ''} + {libraryById.get(card.libraryId)?.subject || ''} +
+ ))} + {remainingCount > 5 && ( +
+ +{remainingCount - 5} restantes +
+ )} +
+
+
+
+ )} - {!showAnswer && ( - - )} - - {showAnswer && ( - <> - - - - )} - - + {sessionCards.length > 0 && !currentCard && ( +
+

Sessao concluida!

+

Todos os cards foram revisados.

+
+
)} diff --git a/Mindforge.Web/src/components/Header.css b/Mindforge.Web/src/components/Header.css index f4a3d40..1236fc9 100644 --- a/Mindforge.Web/src/components/Header.css +++ b/Mindforge.Web/src/components/Header.css @@ -1,61 +1,72 @@ -.header { - position: fixed; - top: 0; - left: 0; - right: 0; - height: 70px; - background-color: var(--color-header); - /* Imposing black with glassy effect */ - background: rgba(15, 15, 15, 0.85); - backdrop-filter: blur(12px); - -webkit-backdrop-filter: blur(12px); +.topbar { + min-height: 72px; display: flex; align-items: center; - justify-content: center; - z-index: 1000; - box-shadow: 0 4px 20px rgba(0, 0, 0, 0.5); - border-bottom: 1px solid rgba(255, 255, 255, 0.05); + justify-content: space-between; + gap: 18px; + padding: 14px 18px; + margin-bottom: 24px; + background: rgba(255, 250, 239, .72); + border: 1px solid rgba(104, 69, 22, .13); + border-radius: 26px; + backdrop-filter: blur(18px); + box-shadow: 0 18px 50px rgba(86, 57, 17, .08); } -.header-content { - display: flex; - justify-content: center; - align-items: center; - width: 100%; - position: relative; -} - -.header-repo { - position: absolute; - right: 24px; +.topbar-left { display: flex; align-items: center; - gap: 6px; - background: rgba(255, 255, 255, 0.06); - border: 1px solid rgba(255, 255, 255, 0.1); - border-radius: 6px; - padding: 4px 10px; + gap: 12px; + cursor: pointer; + min-width: 0; } -.header-repo-icon { - font-size: 0.9rem; - color: rgba(255, 255, 255, 0.5); +.topbar-logo { + width: 42px; + height: 42px; + border-radius: 12px; + flex-shrink: 0; } -.header-repo-name { - font-size: 0.8rem; - color: rgba(255, 255, 255, 0.65); - font-family: var(--font-main); - letter-spacing: 0.5px; -} - -.header-title { - color: var(--color-text-creamy); - font-family: var(--font-main); - font-size: 1.8rem; - font-weight: 700; - letter-spacing: 2px; - text-transform: uppercase; +.topbar-title { margin: 0; - text-shadow: 0 2px 4px rgba(0, 0, 0, 0.5); + font-family: Georgia, "Times New Roman", serif; + font-size: clamp(24px, 3vw, 34px); + font-weight: 800; + letter-spacing: -.04em; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + color: var(--ink); +} + +.topbar-right { + flex-shrink: 0; +} + +.chip { + min-height: 42px; + display: inline-flex; + align-items: center; + justify-content: center; + gap: 9px; + padding: 0 14px; + border-radius: 999px; + border: 1px solid rgba(96, 67, 28, .15); + background: rgba(255,255,255,.62); + color: #4b3b27; + font-weight: 850; + font-size: 13px; + box-shadow: inset 0 1px 0 rgba(255,255,255,.72); +} + +@media (max-width: 760px) { + .topbar { + align-items: flex-start; + flex-direction: column; + } + + .topbar-title { + white-space: normal; + } } diff --git a/Mindforge.Web/src/components/Header.tsx b/Mindforge.Web/src/components/Header.tsx index f36bb47..624dcee 100644 --- a/Mindforge.Web/src/components/Header.tsx +++ b/Mindforge.Web/src/components/Header.tsx @@ -16,19 +16,19 @@ export function Header({ onGoHome }: HeaderProps) { }, []); return ( -
-
-
- Mindforge -

Mindforge

-
- {repoName && ( -
- - {repoName} -
- )} +
+
+ +

Mindforge

+ {repoName && ( +
+ + Repo + {repoName} + +
+ )}
); } diff --git a/Mindforge.Web/src/components/Sidebar.css b/Mindforge.Web/src/components/Sidebar.css index b4de4ca..2765870 100644 --- a/Mindforge.Web/src/components/Sidebar.css +++ b/Mindforge.Web/src/components/Sidebar.css @@ -1,41 +1,174 @@ .sidebar { - width: 240px; - background-color: var(--color-sidebar); - border-right: 1px solid rgba(255, 255, 255, 0.05); - display: flex; - flex-direction: column; - padding: 2rem 1.5rem; - box-shadow: 4px 0 15px rgba(0, 0, 0, 0.2); - /* Ensure it fits cleanly below the header or is independent */ - height: calc(100vh - 70px); position: sticky; - top: 70px; -} - -.sidebar-header { - margin-bottom: 2rem; -} - -.sidebar-title { - color: rgba(244, 245, 245, 0.6); - font-size: 0.9rem; - text-transform: uppercase; - letter-spacing: 1.5px; - font-weight: 700; - margin: 0; -} - -.sidebar-nav { + top: 0; + height: 100vh; + padding: 24px 18px; + background: + linear-gradient(180deg, rgba(255, 249, 234, .9), rgba(245, 226, 183, .84)), + repeating-linear-gradient(45deg, rgba(113, 74, 18, .04) 0 1px, transparent 1px 8px); + border-right: 1px solid var(--line); + box-shadow: 16px 0 40px rgba(80, 54, 18, .08); + z-index: 2; display: flex; flex-direction: column; - gap: 1rem; } -.sidebar-btn { - width: 100%; - text-align: left; +.brand { display: flex; - justify-content: flex-start; - padding: 1rem 1.2rem; - font-size: 1.05rem; -} \ No newline at end of file + align-items: center; + gap: 14px; + padding: 12px 12px 24px; +} + +.brand-mark { + width: 46px; + height: 46px; + display: grid; + place-items: center; + border-radius: 15px; + background: linear-gradient(135deg, #f2cf82, #fff2c8); + border: 1px solid rgba(105, 73, 21, .18); + box-shadow: inset 0 1px 0 rgba(255,255,255,.7), 0 10px 22px rgba(129, 86, 27, .17); + font-family: Georgia, serif; + font-size: 22px; + font-weight: 800; + color: #51340d; + flex-shrink: 0; +} + +.brand-mark img { + width: 28px; + height: 28px; + border-radius: 8px; +} + +.brand h1 { + margin: 0; + font-family: Georgia, "Times New Roman", serif; + font-size: 22px; + font-weight: 800; + letter-spacing: -.03em; + color: var(--ink); +} + +.nav-section-title { + margin: 20px 12px 10px; + color: var(--muted); + font-size: 11px; + font-weight: 900; + letter-spacing: .14em; + text-transform: uppercase; +} + +.nav-list { + display: flex; + flex-direction: column; + gap: 4px; + flex: 1; + overflow-y: auto; +} + +.nav-item { + position: relative; + width: 100%; + display: flex; + align-items: center; + gap: 12px; + padding: 13px 13px; + border: 1px solid transparent; + border-radius: 16px; + color: #4b3b27; + text-decoration: none; + font-weight: 800; + font-size: 14px; + transition: .25s var(--ease); + cursor: pointer; + background: transparent; + font-family: inherit; + text-align: left; +} + +.nav-item:hover { + transform: translateX(4px); + background: rgba(255,255,255,.45); + border-color: var(--line); +} + +.nav-item.active { + background: #fff7df; + border-color: rgba(129, 86, 27, .16); + box-shadow: 0 12px 28px rgba(104, 65, 14, .10); +} + +.nav-icon { + width: 30px; + height: 30px; + display: grid; + place-items: center; + border-radius: 11px; + background: rgba(255,255,255,.64); + border: 1px solid rgba(80, 54, 18, .10); + font-size: 14px; + flex-shrink: 0; +} + +.nav-text { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +@media (max-width: 1120px) { + .sidebar { + padding: 20px 10px; + } + + .brand { + justify-content: center; + padding: 8px 0 18px; + } + + .brand h1, + .nav-section-title, + .nav-text { + display: none; + } + + .nav-item { + justify-content: center; + padding: 12px; + } +} + +@media (max-width: 760px) { + .sidebar { + position: static; + height: auto; + display: flex; + flex-direction: row; + align-items: center; + gap: 8px; + overflow-x: auto; + border-right: 0; + border-bottom: 1px solid var(--line); + padding: 12px 16px; + } + + .brand { + min-width: 64px; + padding: 0; + } + + .nav-section-title { + display: none; + } + + .nav-list { + flex-direction: row; + } + + .nav-item { + min-width: 56px; + justify-content: center; + } +} diff --git a/Mindforge.Web/src/components/Sidebar.tsx b/Mindforge.Web/src/components/Sidebar.tsx index d64ff6f..099abfe 100644 --- a/Mindforge.Web/src/components/Sidebar.tsx +++ b/Mindforge.Web/src/components/Sidebar.tsx @@ -1,4 +1,3 @@ -import { Button } from './Button'; import './Sidebar.css'; interface SidebarProps { @@ -6,42 +5,35 @@ interface SidebarProps { activeModule: 'home' | 'verificador' | 'flashcards' | 'revisao-flashcards' | 'revisao-espacada'; } +const NAV_ITEMS: { module: SidebarProps['activeModule']; icon: string; label: string }[] = [ + { module: 'verificador', icon: '\u2713', label: 'Verificador' }, + { module: 'flashcards', icon: '\u25A6', label: 'Flashcards' }, + { module: 'revisao-flashcards', icon: '\u25B3', label: 'Revisao Flashcards' }, + { module: 'revisao-espacada', icon: '\u25CB', label: 'Revisao Espacada' }, +]; + export function Sidebar({ onModuleChange, activeModule }: SidebarProps) { return ( ); } diff --git a/Mindforge.Web/src/components/SpacedReviewComponent.css b/Mindforge.Web/src/components/SpacedReviewComponent.css index d4b7e20..c098844 100644 --- a/Mindforge.Web/src/components/SpacedReviewComponent.css +++ b/Mindforge.Web/src/components/SpacedReviewComponent.css @@ -1,43 +1,52 @@ .spaced-review-container { width: 100%; - max-width: 1020px; - margin: 0 auto; display: flex; flex-direction: column; gap: 1.2rem; - animation: slideUp 0.45s ease-out; } .spaced-review-title { - font-size: 2.35rem; + font-family: Georgia, "Times New Roman", serif; + font-size: clamp(24px, 3.5vw, 40px); + font-weight: 800; + letter-spacing: -.03em; + color: var(--ink); + margin: 0; +} + +.spaced-review-subtitle { + color: var(--muted); + font-size: 15px; + line-height: 1.55; + max-width: 660px; } .spaced-review-error { - color: #ff9c96; - text-align: left; - background: rgba(255, 69, 58, 0.12); - border: 1px solid rgba(255, 69, 58, 0.4); - border-radius: 10px; + color: var(--red); + font-size: 0.9rem; + background: rgba(183, 91, 77, 0.08); + border: 1px solid rgba(183, 91, 77, 0.2); + border-radius: var(--radius-md); padding: 0.8rem 1rem; + margin-bottom: 1rem; } -.spaced-review-panel, -.spaced-review-session-panel { - background: rgba(255, 255, 255, 0.06); - border: 1px solid rgba(255, 255, 255, 0.14); - border-radius: 12px; - padding: 1.25rem; +.spaced-review-panel { + background: rgba(255, 250, 239, .68); + border: 1px solid rgba(104, 69, 22, .13); + border-radius: var(--radius-lg); + padding: 1.5rem; + box-shadow: 0 18px 48px rgba(86, 57, 17, .09); + backdrop-filter: blur(16px); text-align: left; - backdrop-filter: blur(10px); - -webkit-backdrop-filter: blur(10px); - box-shadow: 0 14px 36px rgba(0, 0, 0, 0.18); } .spaced-review-state { - color: rgba(255, 255, 255, 0.72); + color: var(--muted); font-size: 0.95rem; } +/* Filters */ .spaced-review-filters { display: flex; flex-wrap: wrap; @@ -49,35 +58,116 @@ display: flex; align-items: center; gap: 0.45rem; - border: 1px solid rgba(255, 255, 255, 0.14); + border: 1px solid rgba(82, 54, 17, .14); border-radius: 999px; padding: 0.35rem 0.8rem; - background: rgba(255, 255, 255, 0.05); + background: rgba(255,255,255,.48); font-size: 0.88rem; + cursor: pointer; + color: var(--ink); } -.spaced-review-filter input[type="checkbox"], -.spaced-review-library-item input[type="checkbox"] { +.spaced-review-filter input[type="checkbox"] { width: 15px; height: 15px; - accent-color: var(--color-accent); + accent-color: var(--blue); } +/* RAG Badges */ +.rag-badge, +.rag-badge-inline { + display: inline-flex; + align-items: center; + gap: 0.35rem; + border-radius: 999px; + border: 1px solid rgba(82, 54, 17, .14); + padding: 0.22rem 0.55rem; + font-size: 0.76rem; + font-weight: 800; + line-height: 1; +} + +.rag-icon { + width: 16px; + height: 16px; + border-radius: 999px; + display: inline-flex; + align-items: center; + justify-content: center; + font-size: 0.74rem; + font-weight: 700; +} + +.rag-red .rag-badge, +.rag-badge.rag-red, +.spaced-review-filter.rag-red { + background: rgba(183, 91, 77, 0.12); + border-color: rgba(183, 91, 77, 0.22); +} + +.rag-red .rag-icon, +.rag-badge.rag-red .rag-icon, +.rag-badge-inline.rag-red .rag-icon { + background: rgba(183, 91, 77, 0.28); +} + +.rag-amber .rag-badge, +.rag-badge.rag-amber, +.spaced-review-filter.rag-amber { + background: rgba(199, 149, 57, 0.14); + border-color: rgba(199, 149, 57, 0.24); +} + +.rag-amber .rag-icon, +.rag-badge.rag-amber .rag-icon, +.rag-badge-inline.rag-amber .rag-icon { + background: rgba(199, 149, 57, 0.3); +} + +.rag-green .rag-badge, +.rag-badge.rag-green, +.spaced-review-filter.rag-green { + background: rgba(79, 143, 90, 0.12); + border-color: rgba(79, 143, 90, 0.22); +} + +.rag-green .rag-icon, +.rag-badge.rag-green .rag-icon, +.rag-badge-inline.rag-green .rag-icon { + background: rgba(79, 143, 90, 0.28); +} + +.rag-grey .rag-badge, +.rag-badge.rag-grey, +.spaced-review-filter.rag-grey { + background: rgba(123, 106, 80, 0.10); + border-color: rgba(123, 106, 80, 0.18); +} + +.rag-grey .rag-icon, +.rag-badge.rag-grey .rag-icon, +.rag-badge-inline.rag-grey .rag-icon { + background: rgba(123, 106, 80, 0.22); +} + +/* Subjects */ .spaced-review-subjects { display: grid; gap: 1rem; } .spaced-review-subject { - border: 1px solid rgba(255, 255, 255, 0.12); - border-radius: 10px; - background: rgba(0, 0, 0, 0.18); + border: 1px solid rgba(82, 54, 17, .10); + border-radius: var(--radius-md); + background: rgba(255,255,255,.45); padding: 0.9rem; } .spaced-review-subject-header h3 { margin: 0; font-size: 1.05rem; + font-family: Georgia, serif; + color: var(--ink); } .spaced-review-subject-toggle, @@ -92,19 +182,19 @@ .spaced-review-subsubject-toggle input[type="checkbox"] { width: 16px; height: 16px; - accent-color: var(--color-accent); + accent-color: var(--blue); } .spaced-review-subject-header p { margin: 0.35rem 0 0; font-size: 0.9rem; - color: rgba(255, 255, 255, 0.9); + color: var(--ink); } .spaced-review-subject-header small { display: block; margin-top: 0.35rem; - color: rgba(255, 255, 255, 0.67); + color: var(--muted); font-size: 0.79rem; } @@ -115,10 +205,10 @@ } .spaced-review-subsubject-block { - border: 1px solid rgba(255, 255, 255, 0.1); - border-radius: 10px; + border: 1px solid rgba(82, 54, 17, .10); + border-radius: var(--radius-md); padding: 0.7rem; - background: rgba(255, 255, 255, 0.03); + background: rgba(255,255,255,.48); } .spaced-review-subsubject-header { @@ -130,16 +220,17 @@ .spaced-review-subsubject-header strong { font-size: 0.93rem; + color: var(--ink); } .spaced-review-subsubject-header span { font-size: 0.82rem; - color: rgba(255, 255, 255, 0.78); + color: var(--muted); } .spaced-review-subsubject-header small { font-size: 0.78rem; - color: rgba(255, 255, 255, 0.6); + color: var(--muted); } .spaced-review-library-list { @@ -151,16 +242,39 @@ display: flex; align-items: center; gap: 0.7rem; - border: 1px solid rgba(255, 255, 255, 0.12); + border: 1px solid rgba(82, 54, 17, .12); border-left-width: 4px; - border-radius: 10px; + border-radius: var(--radius-md); padding: 0.6rem 0.7rem; - background: rgba(255, 255, 255, 0.03); + background: rgba(255,255,255,.48); cursor: pointer; } +.spaced-review-library-item input[type="checkbox"] { + width: 15px; + height: 15px; + accent-color: var(--blue); +} + .spaced-review-library-item.selected { - box-shadow: 0 0 0 1px rgba(var(--color-accent-rgb), 0.45); + box-shadow: 0 0 0 2px rgba(63, 124, 172, .25); + border-color: rgba(63, 124, 172, .3); +} + +.spaced-review-library-item.rag-red { + border-left-color: var(--red); +} + +.spaced-review-library-item.rag-amber { + border-left-color: var(--gold); +} + +.spaced-review-library-item.rag-green { + border-left-color: var(--green); +} + +.spaced-review-library-item.rag-grey { + border-left-color: var(--muted); } .spaced-review-library-texts { @@ -173,6 +287,7 @@ .spaced-review-library-texts strong { font-size: 0.91rem; + color: var(--ink); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; @@ -180,103 +295,15 @@ .spaced-review-library-texts span { font-size: 0.8rem; - color: rgba(255, 255, 255, 0.78); + color: var(--muted); } .spaced-review-library-texts small { font-size: 0.77rem; - color: rgba(255, 255, 255, 0.62); -} - -.rag-badge, -.rag-badge-inline { - display: inline-flex; - align-items: center; - gap: 0.35rem; - border-radius: 999px; - border: 1px solid rgba(255, 255, 255, 0.18); - padding: 0.22rem 0.55rem; - font-size: 0.76rem; - font-weight: 700; - line-height: 1; -} - -.rag-icon { - width: 16px; - height: 16px; - border-radius: 999px; - display: inline-flex; - align-items: center; - justify-content: center; - font-size: 0.74rem; - font-weight: 700; - background: rgba(255, 255, 255, 0.18); -} - -.rag-red { - border-left-color: #ff5d5d; -} - -.rag-red .rag-badge, -.rag-badge.rag-red, -.spaced-review-filter.rag-red { - background: rgba(255, 93, 93, 0.15); -} - -.rag-red .rag-icon, -.rag-badge.rag-red .rag-icon, -.rag-badge-inline.rag-red .rag-icon { - background: rgba(255, 93, 93, 0.35); -} - -.rag-amber { - border-left-color: #ffbe55; -} - -.rag-amber .rag-badge, -.rag-badge.rag-amber, -.spaced-review-filter.rag-amber { - background: rgba(255, 190, 85, 0.16); -} - -.rag-amber .rag-icon, -.rag-badge.rag-amber .rag-icon, -.rag-badge-inline.rag-amber .rag-icon { - background: rgba(255, 190, 85, 0.36); -} - -.rag-green { - border-left-color: #46d18a; -} - -.rag-green .rag-badge, -.rag-badge.rag-green, -.spaced-review-filter.rag-green { - background: rgba(70, 209, 138, 0.16); -} - -.rag-green .rag-icon, -.rag-badge.rag-green .rag-icon, -.rag-badge-inline.rag-green .rag-icon { - background: rgba(70, 209, 138, 0.35); -} - -.rag-grey { - border-left-color: #9aa6b5; -} - -.rag-grey .rag-badge, -.rag-badge.rag-grey, -.spaced-review-filter.rag-grey { - background: rgba(154, 166, 181, 0.16); -} - -.rag-grey .rag-icon, -.rag-badge.rag-grey .rag-icon, -.rag-badge-inline.rag-grey .rag-icon { - background: rgba(154, 166, 181, 0.35); + color: var(--muted); } +/* Footer */ .spaced-review-footer { margin-top: 1rem; display: flex; @@ -288,42 +315,53 @@ .spaced-review-footer p { font-size: 0.93rem; - color: rgba(255, 255, 255, 0.78); + color: var(--ink); +} + +/* Session Panel */ +.spaced-review-session-panel { + background: rgba(255, 250, 239, .68); + border: 1px solid rgba(104, 69, 22, .13); + border-radius: var(--radius-xl); + padding: clamp(18px, 3vw, 34px); + box-shadow: var(--shadow); + backdrop-filter: blur(16px); + text-align: left; } .spaced-review-progress { display: flex; align-items: center; gap: 0.9rem; - margin-bottom: 0.8rem; + margin-bottom: 1.2rem; } .spaced-review-progress span { font-size: 0.9rem; - color: rgba(255, 255, 255, 0.8); + color: var(--ink); min-width: 60px; + font-weight: 800; } .spaced-review-progress-bar { width: 100%; - height: 8px; + height: 10px; border-radius: 999px; - background: rgba(0, 0, 0, 0.25); overflow: hidden; + background: rgba(80, 54, 18, .12); } .spaced-review-progress-fill { height: 100%; - background: linear-gradient(90deg, rgba(var(--color-accent-rgb), 0.8), rgba(var(--color-accent-rgb), 1)); + background: linear-gradient(90deg, var(--blue), #79a9c8, var(--gold)); border-radius: 999px; - transition: width 0.25s ease; } .spaced-review-card { - border: 1px solid rgba(255, 255, 255, 0.16); - border-radius: 10px; - padding: 1rem; - background: rgba(0, 0, 0, 0.2); + border: 1px solid rgba(82, 54, 17, .14); + border-radius: var(--radius-lg); + padding: 1.25rem; + background: rgba(255,255,255,.52); min-height: 260px; } @@ -337,26 +375,32 @@ } .spaced-review-card small { - color: rgba(255, 255, 255, 0.62); + color: var(--muted); + font-size: 0.85rem; } .spaced-review-card h3 { margin: 0 0 0.4rem; - font-size: 0.98rem; - color: rgba(255, 255, 255, 0.9); + font-size: 0.85rem; + font-weight: 950; + letter-spacing: .12em; + text-transform: uppercase; + color: var(--blue-deep); } .spaced-review-card p { margin: 0 0 1rem; - font-size: 1rem; - line-height: 1.55; + font-size: 1.05rem; + line-height: 1.6; + color: var(--ink); + font-family: "Segoe UI", Inter, system-ui, sans-serif; } .spaced-review-card-meta { display: flex; gap: 1rem; flex-wrap: wrap; - color: rgba(255, 255, 255, 0.75); + color: var(--muted); font-size: 0.84rem; } @@ -367,6 +411,89 @@ flex-wrap: wrap; } +/* Review content grid for session mode */ +.spaced-review-content-grid { + display: grid; + grid-template-columns: minmax(0, 1fr) 330px; + gap: 24px; + align-items: start; +} + +.spaced-review-side-panel { + display: grid; + gap: 18px; +} + +.spaced-review-panel-card { + padding: 20px; + border-radius: 26px; + background: rgba(255, 250, 239, .68); + border: 1px solid rgba(104, 69, 22, .13); + box-shadow: 0 18px 48px rgba(86, 57, 17, .09); + backdrop-filter: blur(16px); +} + +.spaced-review-panel-card h3 { + margin: 0 0 14px; + font-family: Georgia, serif; + font-size: 22px; + letter-spacing: -.03em; + color: var(--ink); +} + +.spaced-review-stat-grid { + display: grid; + grid-template-columns: repeat(2, 1fr); + gap: 10px; +} + +.spaced-review-stat { + padding: 14px; + border-radius: 18px; + background: rgba(255,255,255,.52); + border: 1px solid rgba(82, 54, 17, .10); +} + +.spaced-review-stat b { + display: block; + font-size: 24px; + letter-spacing: -.04em; + color: var(--ink); +} + +.spaced-review-stat span { + color: var(--muted); + font-size: 11px; + font-weight: 950; + letter-spacing: .10em; + text-transform: uppercase; +} + +.spaced-review-track { + height: 14px; + border-radius: 999px; + overflow: hidden; + background: rgba(80, 54, 18, .12); + margin-top: 8px; +} + +.spaced-review-track span { + display: block; + height: 100%; + border-radius: inherit; + background: linear-gradient(90deg, var(--red), var(--gold), var(--green)); +} + +@media (max-width: 1120px) { + .spaced-review-content-grid { + grid-template-columns: 1fr; + } + + .spaced-review-side-panel { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } +} + @media (max-width: 760px) { .spaced-review-library-item { flex-wrap: wrap; @@ -376,4 +503,8 @@ display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); } + + .spaced-review-side-panel { + grid-template-columns: 1fr; + } } diff --git a/Mindforge.Web/src/components/SpacedReviewComponent.tsx b/Mindforge.Web/src/components/SpacedReviewComponent.tsx index 1921407..ba7c6a8 100644 --- a/Mindforge.Web/src/components/SpacedReviewComponent.tsx +++ b/Mindforge.Web/src/components/SpacedReviewComponent.tsx @@ -361,8 +361,8 @@ export function SpacedReviewComponent() { return (
-

Revisao espacada

-

Acompanhe o status RAG por arquivo de flashcards.

+

Revisao espacada

+

Acompanhe o status RAG por arquivo de flashcards.

{error &&
{error}
} @@ -492,68 +492,99 @@ export function SpacedReviewComponent() { )} {sessionCards.length > 0 && currentCard && ( -
-
- {currentIndex + 1} / {sessionCards.length} -
-
+
+
+
+ {currentIndex + 1} / {sessionCards.length} +
+
+
+
+ +
+
+ + {currentLibrary?.fileName || 'Arquivo'} - {currentLibrary?.subject || 'Geral'} - {currentLibrary?.subSubject || 'Geral'} + + + {currentStatusMeta.icon} + {currentStatusMeta.label} + +
+ +

Frente

+

{currentCard.front}

+ + {showAnswer && ( + <> +

Verso

+

{currentCard.back}

+
+ Desempenho do arquivo: {currentLibrary ? formatPerformance(currentLibrary.performanceRate) : '-'} + Ultima revisao: {formatLastReviewed(currentLibrary?.lastReviewedAt)} +
+ + )} +
+ +
+ + + {!showAnswer && ( + + )} + + {showAnswer && ( + <> + + + + )} + +
-
-
- - {currentLibrary?.fileName || 'Arquivo'} - {currentLibrary?.subject || 'Geral'} - {currentLibrary?.subSubject || 'Geral'} - - - {currentStatusMeta.icon} - {currentStatusMeta.label} - -
- -

Frente

-

{currentCard.front}

- - {showAnswer && ( - <> -

Verso

-

{currentCard.back}

-
- Desempenho do arquivo: {currentLibrary ? formatPerformance(currentLibrary.performanceRate) : '-'} - Ultima revisao: {formatLastReviewed(currentLibrary?.lastReviewedAt)} +
+
+

Progresso

+
+
+ {currentIndex + 1}/{sessionCards.length} + Atual
- - )} -
- -
- - - {!showAnswer && ( - - )} - - {showAnswer && ( - <> - - - - )} - - +
+ + {currentStatusMeta.icon} + {currentStatusMeta.label} + + Status RAG +
+
+
+ +
+
)} + + {sessionCards.length > 0 && !currentCard && ( +
+

Sessao concluida!

+

Todos os cards foram revisados.

+
+ )}
); } diff --git a/Mindforge.Web/src/components/VerificadorComponent.css b/Mindforge.Web/src/components/VerificadorComponent.css index 5d9184d..2bb961d 100644 --- a/Mindforge.Web/src/components/VerificadorComponent.css +++ b/Mindforge.Web/src/components/VerificadorComponent.css @@ -2,38 +2,37 @@ width: 100%; max-width: 1000px; margin: 0 auto; - animation: slideUp 0.5s ease-out; display: flex; flex-direction: column; gap: 1.5rem; } -.file-result-block { - background: rgba(255, 255, 255, 0.03); - border: 1px solid rgba(255, 255, 255, 0.08); - border-radius: 10px; - padding: 1rem; - margin-bottom: 1rem; +.verificador-title { + font-family: Georgia, "Times New Roman", serif; + font-size: clamp(24px, 3.5vw, 40px); + font-weight: 800; + letter-spacing: -.03em; + color: var(--ink); + margin: 0; } -.file-result-title { - font-size: 0.9rem; - font-weight: 600; - color: rgba(255, 255, 255, 0.6); - margin-bottom: 0.75rem; - padding-bottom: 0.5rem; - border-bottom: 1px solid rgba(255, 255, 255, 0.07); - font-family: monospace; +.verificador-subtitle { + color: var(--muted); + font-size: 15px; + line-height: 1.55; + max-width: 660px; } .verificador-form { display: flex; flex-direction: column; gap: 1.2rem; - background: rgba(255, 255, 255, 0.05); + background: rgba(255, 250, 239, .68); padding: 2rem; - border-radius: 12px; - border: 1px solid rgba(255, 255, 255, 0.1); + border-radius: var(--radius-lg); + border: 1px solid rgba(104, 69, 22, .13); + box-shadow: 0 18px 48px rgba(86, 57, 17, .09); + backdrop-filter: blur(16px); } .input-group { @@ -45,78 +44,38 @@ .input-group label { font-weight: 700; - color: var(--color-text-creamy); -} - -.text-area { - width: 100%; - min-height: 200px; - background: rgba(0, 0, 0, 0.2); - border: 1px solid rgba(255, 255, 255, 0.2); - border-radius: 8px; - padding: 1rem; - color: var(--color-text-creamy); - font-family: inherit; - font-size: 1rem; - resize: vertical; -} - -.text-area:focus { - outline: none; - border-color: var(--color-accent); -} - -.file-input-wrapper { - display: flex; - align-items: center; - gap: 1rem; -} - -.file-input { - display: none; -} - -.file-input-label { - background: var(--color-sidebar); - color: var(--color-text-creamy); - padding: 0.6rem 1.2rem; - border-radius: 6px; - cursor: pointer; - border: 1px solid rgba(255, 255, 255, 0.2); - transition: all 0.2s ease; -} - -.file-input-label:hover { - background: rgba(255, 255, 255, 0.1); - border-color: var(--color-accent); + color: var(--ink); + font-size: 0.9rem; } .select-input { - background: rgba(0, 0, 0, 0.2); - border: 1px solid rgba(255, 255, 255, 0.2); - color: var(--color-text-creamy); + background: rgba(255, 250, 239, .72); + border: 1px solid rgba(104, 69, 22, .15); + color: var(--ink); padding: 0.8rem; - border-radius: 8px; + border-radius: var(--radius-md); font-size: 1rem; cursor: pointer; + font-family: inherit; } .select-input:focus { outline: none; - border-color: var(--color-accent); + border-color: var(--blue); } .select-input option { - background: var(--color-bg); - color: var(--color-text-creamy); + background: var(--paper); + color: var(--ink); } -/* Response Section */ .response-section { - background: rgba(255, 255, 255, 0.05); + background: rgba(255, 250, 239, .68); padding: 2rem; - border-radius: 12px; - border: 1px solid rgba(255, 255, 255, 0.1); + border-radius: var(--radius-lg); + border: 1px solid rgba(104, 69, 22, .13); + box-shadow: 0 18px 48px rgba(86, 57, 17, .09); + backdrop-filter: blur(16px); display: flex; flex-direction: column; gap: 1.5rem; @@ -125,30 +84,35 @@ .response-content { text-align: left; white-space: pre-wrap; - background: rgba(0, 0, 0, 0.2); + background: rgba(255,255,255,.52); padding: 1.5rem; - border-radius: 8px; - border: 1px solid rgba(255, 255, 255, 0.1); - font-family: monospace; + border-radius: var(--radius-md); + border: 1px solid rgba(82, 54, 17, .10); font-size: 0.95rem; line-height: 1.5; overflow-x: auto; + color: var(--ink); +} + +.response-content.markdown-body { + font-family: Inter, ui-sans-serif, system-ui, -apple-system, sans-serif; } .diff-view { display: flex; flex-wrap: wrap; + font-family: "Segoe UI", Inter, system-ui, sans-serif; } .diff-added { - background-color: rgba(46, 160, 67, 0.3); - color: #7ee787; + background-color: rgba(79, 143, 90, 0.18); + color: var(--green-deep); border-radius: 3px; } .diff-removed { - background-color: rgba(248, 81, 73, 0.3); - color: #ff7b72; + background-color: rgba(183, 91, 77, 0.18); + color: var(--red-deep); text-decoration: line-through; border-radius: 3px; } @@ -168,32 +132,74 @@ } .pane-title { - font-size: 1.1rem; - font-weight: 700; + font-size: 0.85rem; + font-weight: 950; + letter-spacing: .12em; + text-transform: uppercase; text-align: center; - color: var(--color-accent); + color: var(--blue-deep); } -/* Spinner */ -.spinner-container { +.file-result-block { + background: rgba(255,255,255,.45); + border: 1px solid rgba(82, 54, 17, .12); + border-radius: var(--radius-md); + padding: 1rem; + margin-bottom: 1rem; +} + +.file-result-title { + font-size: 0.9rem; + font-weight: 700; + color: var(--muted); + margin-bottom: 0.75rem; + padding-bottom: 0.5rem; + border-bottom: 1px solid var(--line); + font-family: "Segoe UI", Inter, monospace; +} + +.select-status { display: flex; - flex-direction: column; align-items: center; + gap: 0.5rem; + color: var(--muted); + font-size: 0.85rem; +} + +.verificador-error { + color: var(--red); + font-size: 0.9rem; + background: rgba(183, 91, 77, 0.08); + border: 1px solid rgba(183, 91, 77, 0.2); + border-radius: var(--radius-md); + padding: 0.8rem 1rem; +} + +.loading-indicator { + display: flex; + align-items: center; + gap: 10px; justify-content: center; - padding: 3rem 0; - gap: 1rem; + padding: 2rem; + color: var(--muted); + font-size: 0.95rem; } -.spinner { - width: 50px; - height: 50px; - border: 4px solid rgba(255, 255, 255, 0.1); - border-left-color: var(--color-accent); +.loading-dot { + width: 10px; + height: 10px; border-radius: 50%; - animation: spin 1s linear infinite; + background: var(--blue); + box-shadow: 0 0 0 4px rgba(63, 124, 172, .16); } -@keyframes spin { - 0% { transform: rotate(0deg); } - 100% { transform: rotate(360deg); } +@media (max-width: 760px) { + .verificador-form, + .response-section { + padding: 1.2rem; + } + + .side-by-side { + flex-direction: column; + } } diff --git a/Mindforge.Web/src/components/VerificadorComponent.tsx b/Mindforge.Web/src/components/VerificadorComponent.tsx index 37357bc..854d31a 100644 --- a/Mindforge.Web/src/components/VerificadorComponent.tsx +++ b/Mindforge.Web/src/components/VerificadorComponent.tsx @@ -32,7 +32,7 @@ export function VerificadorComponent() { const handleSubmit = async () => { if (selectedPaths.length === 0) { - setError('Selecione pelo menos um arquivo do repositório.'); + setError('Selecione pelo menos um arquivo do repositorio.'); return; } @@ -97,44 +97,44 @@ export function VerificadorComponent() { return (
-

Verificador de Arquivos

-

Selecione os arquivos do repositório para validação de linguagem ou conteúdo.

+

Verificador de Arquivos

+

Selecione os arquivos do repositorio para validação de linguagem ou conteudo.

- + {selectedPaths.length > 0 && ( -
+
{selectedPaths.length} arquivo{selectedPaths.length !== 1 ? 's' : ''} selecionado{selectedPaths.length !== 1 ? 's' : ''}
)}
- +
- - {error &&
{error}
} + {error &&
{error}
}
{loading && ( -
-
-

Analisando sua forja mental...

+
+ + Analisando sua forja mental...
)} @@ -145,7 +145,7 @@ export function VerificadorComponent() {
{fileResult.fileName}
{fileResult.error && ( -
{fileResult.error}
+
{fileResult.error}
)} {!fileResult.error && checkType === 'language' && fileResult.languageResult && ( @@ -159,7 +159,7 @@ export function VerificadorComponent() { {!fileResult.error && checkType === 'content' && fileResult.contentResult && (
-
Conteúdo
+
Conteudo
-
Conteúdo
+
Conteudo
Nota: `marked` v17+ inclui tipos TypeScript nativos. `@types/marked` foi removido. `@types/diff` v7 permanece em devDependencies como fallback de tipos. -| **Google Fonts (Lato)** | 300/400/700 | Tipografia da interface | +| **Google Fonts (Inter)** | 400..950 (variável) | Tipografia da interface | ### Backend API (Mindforge.API) | Tecnologia | Versão | Finalidade | @@ -125,9 +125,10 @@ Formas de requisição principais: - **CSS**: Um arquivo `.css` por componente, importado diretamente. **Sem CSS Modules.** - **Estado**: Apenas estado local (`useState`). Sem store global, sem Context API. - **Roteamento**: Alternância de componentes via `display: block/none` com o estado `activeModule`. Não usa `react-router`. -- **API**: Chamadas via `MindforgeApiService` (objeto singleton com métodos estáticos usando `fetch`). +- **API**: Chamadas via `MindforgeApiService` (objeto singleton com métodos assíncronos usando `fetch`). - **TypeScript**: Modo estrito, `erasableSyntaxOnly`, `verbatimModuleSyntax`, `noUnusedLocals`, `noUnusedParameters`. - **Alias**: `react` e `react-dom` mapeados para `preact/compat/` no tsconfig. +- **Layout**: `.app` usa CSS Grid (`grid-template-columns: 288px 1fr`) com Sidebar sticky + `.main` contendo Topbar e área de conteúdo. ### Backend (C#/.NET 9) - **Namespaces**: `Mindforge.API.Controllers`, `Mindforge.API.Services`, etc. @@ -138,21 +139,38 @@ Formas de requisição principais: ### UI/UX - **Idioma**: Todo texto em **português brasileiro**. -- **Tema**: Escuro com efeito vidro (glassy). `backdrop-filter: blur()`, fundos `rgba` semitransparentes. -- **Botões**: Estilo iOS-like, modernos. Variantes `primary` (com blur) e `secondary` (transparente). -- **Tipografia**: Lato (Google Fonts), pesos 300/400/700. -- **Background**: `#005873` (azul petróleo escuro). Não muito escuro. +- **Tema**: Claro quente vintage ("mesa de estudo pessoal"). Fundos creme/pergaminho/dourado com textura de papel (grid via `body::before`). Vidro translúcido com `backdrop-filter` em tons quentes. +- **Botões**: Estilo pill quente com sombras marrons. Variantes `primary` (gradiente azul, com brilho hover) e `secondary` (translúcido pergaminho). +- **Tipografia**: Inter (UI global), Georgia/Times New Roman (marca e cabeçalhos), Segoe UI/Inter (conteúdo do flashcard). +- **Background**: Gradiente radial quente (dourado/azul) sobre base `#fff8e6` com overlay de grid de papel 24px. +- **Layout**: CSS Grid (`grid-template-columns: 288px minmax(0, 1fr)`). Sidebar sticky com textura diagonal. Topbar integrada ao fluxo (não fixa). +- **Animações**: Apenas sob ação do usuário (flip 3D do flashcard, carimbo, saída do cartão, confete canvas). Sem animações infinitas ou spinners. +- **Flashcard**: Cartão 3D com efeito `rotateY(180deg)`, frente papel pautado com borda tracejada, verso azulado. Carimbo de feedback (correto/incorreto). Confete canvas ao acertar. +- **Responsivo**: Breakpoints em 1120px (sidebar colapsada) e 760px (layout single-column). ### Variáveis CSS (definidas em `index.css`) ```css ---color-bg: #005873; ---color-header: #0f0f0f; ---color-sidebar: #013a4c; ---color-text-creamy: #f4f5f5; ---color-accent: #00b4d8; ---color-accent-rgb: 0, 180, 216; ---color-accent-hover: #0096c7; ---font-main: 'Lato', sans-serif; +--bg: #fbf3df; +--bg-soft: #fff9ea; +--paper: #fffaf0; +--paper-deep: #f2dfb3; +--ink: #2b241c; +--muted: #7b6a50; +--line: rgba(82, 60, 28, 0.18); +--blue: #3f7cac; +--blue-deep: #255f8d; +--green: #4f8f5a; +--green-deep: #32683b; +--red: #b75b4d; +--red-deep: #8d3c32; +--gold: #c79539; +--violet: #7e65a8; +--shadow: 0 28px 70px rgba(58, 42, 17, 0.18); +--card-shadow: 0 30px 90px rgba(76, 48, 12, 0.20); +--radius-xl: 32px; +--radius-lg: 22px; +--radius-md: 16px; +--ease: cubic-bezier(.2,.75,.2,1); ``` ### Git e Scripts de Build/Deploy