diff --git a/app/static/css/style.css b/app/static/css/style.css index fb647f9..3393532 100644 --- a/app/static/css/style.css +++ b/app/static/css/style.css @@ -176,6 +176,16 @@ body { margin-bottom: 16px; } +.success-banner { + background-color: var(--success-dim, rgba(16, 185, 129, 0.15)); + border: 1px solid var(--success, #10b981); + color: #86efac; + padding: 10px 14px; + border-radius: var(--radius-sm); + font-size: 13px; + margin-bottom: 16px; +} + /* App Navbar */ .app-container { display: flex; @@ -679,6 +689,26 @@ body { color: var(--text-dim); } +.list-controls { + display: flex; + align-items: center; + gap: 8px; +} + +.batch-mode-btn { + white-space: nowrap; + flex-shrink: 0; + height: 35px; + display: inline-flex; + align-items: center; + justify-content: center; +} + +.search-mini-box { + display: flex; + align-items: center; +} + .search-mini-box input { background-color: var(--bg-surface); border: 1px solid var(--border-color); @@ -688,6 +718,8 @@ body { font-size: 13px; outline: none; min-width: 260px; + height: 35px; + box-sizing: border-box; transition: var(--transition); } @@ -924,6 +956,955 @@ body { border-top: 1px solid var(--border-color); } +.modal-sm { + max-width: 420px; +} + +/* Navigation Additions */ +.nav-center { + display: flex; + align-items: center; +} + +.btn-trainer { + background: linear-gradient(135deg, #0284c7, #38bdf8); + color: #0f172a; + font-weight: 700; + box-shadow: 0 2px 10px rgba(56, 189, 248, 0.25); + display: inline-flex; + align-items: center; + gap: 8px; + padding: 7px 14px; + border-radius: var(--radius-sm); + border: none; + cursor: pointer; + transition: var(--transition); +} + +.btn-trainer:hover { + box-shadow: 0 4px 14px rgba(56, 189, 248, 0.45); + transform: translateY(-1px); +} + +.srs-due-badge { + background-color: #0f172a; + color: #38bdf8; + font-size: 11px; + font-weight: 800; + padding: 1px 7px; + border-radius: var(--radius-full); + min-width: 18px; + text-align: center; +} + +.srs-due-badge.has-due { + background-color: #ef4444; + color: #ffffff; + animation: pulseDue 2s infinite; +} + +@keyframes pulseDue { + 0%, 100% { transform: scale(1); } + 50% { transform: scale(1.15); } +} + +/* Collections & Filters */ +.collections-filter-wrap, .tags-filter-wrap { + display: flex; + flex-direction: column; + gap: 6px; +} + +.filter-header-bar { + display: flex; + align-items: center; + justify-content: space-between; +} + +.filter-title { + font-size: 12px; + font-weight: 700; + color: var(--text-muted); + text-transform: uppercase; + letter-spacing: 0.5px; +} + +.filter-chips-row { + display: flex; + flex-wrap: wrap; + gap: 6px; + align-items: center; +} + +.col-chip { + background-color: var(--bg-surface); + border: 1px solid var(--border-color); + color: var(--text-muted); + font-size: 12px; + font-weight: 600; + padding: 4px 12px; + border-radius: var(--radius-full); + cursor: pointer; + transition: var(--transition); +} + +.col-chip:hover { + border-color: #a855f7; + color: #c084fc; +} + +.col-chip.active { + background-color: #a855f7; + border-color: #a855f7; + color: #ffffff; + font-weight: 700; +} + +.card-collection-badge { + font-size: 11px; + font-weight: 600; + background-color: rgba(168, 85, 247, 0.15); + color: #c084fc; + border: 1px solid rgba(168, 85, 247, 0.3); + padding: 2px 8px; + border-radius: var(--radius-full); +} + +/* Batch Selection */ +.batch-mode-btn.active { + background-color: var(--primary-dim); + border-color: var(--primary); + color: var(--primary); +} + +.card-select-cell { + display: none; + margin-right: 12px; + align-items: center; +} + +.batch-active .card-select-cell { + display: flex; +} + +.card-select-checkbox { + width: 18px; + height: 18px; + cursor: pointer; + accent-color: var(--primary); +} + +.vocab-card.is-selected { + border-color: var(--border-focus); + background-color: rgba(56, 189, 248, 0.05); +} + +.batch-action-bar { + position: fixed; + bottom: 24px; + left: 50%; + transform: translateX(-50%); + width: calc(100% - 32px); + max-width: 760px; + background: rgba(30, 41, 59, 0.96); + backdrop-filter: blur(12px); + border: 1px solid var(--border-focus); + border-radius: var(--radius-lg); + box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.6), 0 0 15px rgba(56, 189, 248, 0.2); + padding: 12px 20px; + display: flex; + justify-content: space-between; + align-items: center; + z-index: 900; + animation: slideUp 0.22s ease-out; +} + +@keyframes slideUp { + from { transform: translate(-50%, 40px); opacity: 0; } + to { transform: translate(-50%, 0); opacity: 1; } +} + +.batch-left { + display: flex; + align-items: center; + gap: 10px; +} + +.batch-check-label { + display: flex; + align-items: center; + gap: 8px; + font-size: 14px; + font-weight: 600; + color: var(--text-main); + cursor: pointer; +} + +.batch-master-checkbox { + width: 18px; + height: 18px; + accent-color: var(--primary); + cursor: pointer; +} + +.batch-actions { + display: flex; + align-items: center; + gap: 8px; +} + +.btn-secondary { + background-color: var(--bg-surface-hover); + color: var(--text-main); + border: 1px solid var(--border-color); +} + +.btn-secondary:hover { + background-color: var(--border-color); +} + +.btn-danger { + background-color: var(--danger); + color: #ffffff; +} + +.btn-danger:hover { + background-color: var(--danger-hover); +} + +.btn-ghost { + background: transparent; + border: none; + color: var(--primary); + cursor: pointer; + font-size: 13px; + padding: 4px 8px; + border-radius: var(--radius-sm); +} + +.btn-ghost:hover { + background-color: var(--primary-dim); +} + +.btn-xs { + padding: 2px 8px; + font-size: 11px; +} + +/* Export Modal */ +.export-modal-window { + max-width: 620px; +} + +.modal-lead { + font-size: 14px; + color: var(--text-muted); + margin-bottom: 8px; +} + +.export-grid { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 12px; +} + +.export-tile { + background-color: var(--bg-input); + border: 1px solid var(--border-color); + border-radius: var(--radius-md); + padding: 16px; + display: flex; + flex-direction: column; + justify-content: space-between; + gap: 12px; + transition: var(--transition); +} + +.export-tile:hover { + border-color: var(--primary); + transform: translateY(-2px); + box-shadow: var(--shadow-md); +} + +.export-tile-icon { + font-size: 28px; +} + +.export-tile-info h4 { + font-size: 15px; + font-weight: 700; + color: var(--text-main); + margin-bottom: 4px; +} + +.export-tile-info p { + font-size: 12px; + color: var(--text-dim); + line-height: 1.4; +} + +/* SRS View Structure */ +.srs-view { + min-height: calc(100vh - 65px); + display: flex; + flex-direction: column; + background: radial-gradient(circle at 50% 15%, rgba(56, 189, 248, 0.07), transparent 60%); +} + +.srs-topbar { + background-color: var(--bg-surface); + border-bottom: 1px solid var(--border-color); + padding: 12px 24px; + display: flex; + align-items: center; + justify-content: space-between; + gap: 16px; +} + +.srs-topbar-left { + display: flex; + align-items: center; + gap: 16px; +} + +.srs-mode-switch { + display: flex; + background-color: var(--bg-main); + padding: 3px; + border-radius: var(--radius-md); + border: 1px solid var(--border-color); +} + +.srs-tab { + background: transparent; + border: none; + color: var(--text-muted); + padding: 6px 14px; + border-radius: var(--radius-sm); + font-size: 13px; + font-weight: 600; + cursor: pointer; + transition: var(--transition); +} + +.srs-tab:hover { + color: var(--text-main); +} + +.srs-tab.active { + background-color: var(--primary); + color: #0f172a; + font-weight: 700; + box-shadow: var(--shadow-sm); +} + +.srs-topbar-center { + flex: 1; + max-width: 380px; + display: flex; + flex-direction: column; + align-items: center; + gap: 4px; +} + +.srs-progress-container { + width: 100%; + height: 7px; + background-color: var(--bg-main); + border-radius: var(--radius-full); + overflow: hidden; + border: 1px solid var(--border-color); +} + +.srs-progress-fill { + height: 100%; + background: linear-gradient(90deg, #0284c7, #38bdf8); + border-radius: var(--radius-full); + transition: width 0.3s ease; +} + +.srs-counter { + font-size: 11px; + font-weight: 600; + color: var(--text-dim); +} + +.srs-topbar-right { + display: flex; + align-items: center; +} + +.srs-arena { + flex: 1; + max-width: 720px; + width: 100%; + margin: 0 auto; + padding: 32px 16px; + display: flex; + flex-direction: column; + justify-content: center; +} + +.srs-state-box { + text-align: center; + padding: 64px 24px; + background-color: var(--bg-card); + border: 1px solid var(--border-color); + border-radius: var(--radius-lg); + box-shadow: var(--shadow-md); + display: flex; + flex-direction: column; + align-items: center; + gap: 16px; +} + +.srs-big-icon { + font-size: 56px; +} + +.srs-empty-buttons { + display: flex; + gap: 12px; + flex-wrap: wrap; + justify-content: center; + margin-top: 12px; +} + +/* Flashcard 3D Card Flip */ +.srs-mode-container { + display: flex; + flex-direction: column; + gap: 20px; + animation: fadeIn 0.25s ease-out; +} + +.flashcard-wrapper { + perspective: 1400px; + min-height: 380px; + cursor: pointer; + user-select: none; +} + +.flashcard-inner { + position: relative; + width: 100%; + min-height: 380px; + transform-style: preserve-3d; + transition: transform 0.48s cubic-bezier(0.4, 0, 0.2, 1); +} + +.flashcard-inner.is-flipped { + transform: rotateY(180deg); +} + +.flashcard-face { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + min-height: 380px; + background-color: var(--bg-card); + border: 1px solid var(--border-color); + border-radius: var(--radius-lg); + padding: 28px 32px; + box-shadow: var(--shadow-lg); + backface-visibility: hidden; + -webkit-backface-visibility: hidden; + display: flex; + flex-direction: column; + justify-content: space-between; +} + +.flashcard-front { + transform: rotateY(0deg); +} + +.flashcard-back { + transform: rotateY(180deg); + background: radial-gradient(circle at top right, rgba(56, 189, 248, 0.06), var(--bg-card) 70%); +} + +.fc-badge-row { + display: flex; + justify-content: space-between; + align-items: center; +} + +.fc-dir-badge { + font-size: 11px; + font-weight: 700; + letter-spacing: 0.5px; + color: var(--primary); + background-color: var(--primary-dim); + padding: 3px 10px; + border-radius: var(--radius-full); +} + +.fc-pos-badge { + font-size: 11px; + color: var(--text-dim); + background-color: var(--bg-surface-hover); + padding: 2px 8px; + border-radius: 4px; +} + +.fc-center { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + text-align: center; + gap: 16px; + padding: 20px 0; +} + +.fc-main-term { + font-size: 38px; + font-weight: 800; + letter-spacing: -0.5px; + color: var(--text-main); +} + +.fc-phonetic-row { + display: flex; + align-items: center; + gap: 10px; +} + +.audio-round-btn { + width: 36px; + height: 36px; + border-radius: 50%; + display: inline-flex; + align-items: center; + justify-content: center; + background-color: var(--primary-dim); + border: 1px solid var(--primary); + color: var(--primary); + font-size: 16px; + cursor: pointer; + transition: var(--transition); +} + +.audio-round-btn:hover { + background-color: var(--primary); + color: #0f172a; + transform: scale(1.08); +} + +.fc-hint-wrapper { + display: flex; + flex-direction: column; + align-items: center; + gap: 8px; + max-width: 500px; +} + +.fc-hint-content { + font-size: 14px; + font-style: italic; + color: var(--text-muted); + background-color: var(--bg-main); + padding: 10px 16px; + border-radius: var(--radius-sm); + border-left: 3px solid var(--primary); +} + +.fc-footer-hint { + text-align: center; + font-size: 12px; + color: var(--text-dim); +} + +.fc-footer-hint kbd { + background-color: var(--bg-surface-hover); + border: 1px solid var(--border-color); + padding: 2px 6px; + border-radius: 4px; + font-family: monospace; +} + +/* Flashcard Back */ +.fc-back-content { + display: flex; + flex-direction: column; + gap: 14px; + height: 100%; +} + +.fc-back-top { + display: flex; + justify-content: space-between; + align-items: center; + border-bottom: 1px solid var(--border-color); + padding-bottom: 8px; +} + +.fc-back-term { + font-size: 24px; + font-weight: 700; + color: var(--text-main); +} + +.fc-back-translation { + font-size: 22px; + font-weight: 700; + color: var(--primary); +} + +.fc-back-detail-box { + background-color: var(--bg-main); + border-radius: var(--radius-sm); + padding: 10px 14px; + border-left: 2px solid var(--border-color); +} + +.detail-label { + font-size: 10px; + font-weight: 700; + text-transform: uppercase; + color: var(--text-dim); + letter-spacing: 0.5px; + margin-bottom: 2px; + display: block; +} + +.detail-text { + font-size: 13px; + color: var(--text-main); + line-height: 1.4; +} + +.fc-flip-btn-wrap { + display: none; +} + +/* SRS Rating Bar */ +.srs-rating-bar { + display: grid; + grid-template-columns: repeat(4, 1fr); + gap: 12px; + margin-top: 8px; + animation: fadeIn 0.2s ease-out; +} + +.btn-rate { + padding: 12px 10px; + border-radius: var(--radius-md); + border: 1px solid var(--border-color); + background-color: var(--bg-surface); + cursor: pointer; + display: flex; + flex-direction: column; + align-items: center; + gap: 2px; + transition: var(--transition); +} + +.btn-rate .rate-key { + font-size: 11px; + font-family: monospace; + opacity: 0.7; +} + +.btn-rate .rate-name { + font-size: 15px; + font-weight: 700; +} + +.btn-rate .rate-time { + font-size: 11px; + opacity: 0.75; +} + +/* Rating variants */ +.btn-rate-1 { + border-color: rgba(239, 68, 68, 0.4); + color: #f87171; +} +.btn-rate-1:hover { + background-color: rgba(239, 68, 68, 0.15); + border-color: #ef4444; +} + +.btn-rate-2 { + border-color: rgba(245, 158, 11, 0.4); + color: #fbbf24; +} +.btn-rate-2:hover { + background-color: rgba(245, 158, 11, 0.15); + border-color: #f59e0b; +} + +.btn-rate-3 { + border-color: rgba(56, 189, 248, 0.4); + color: #38bdf8; +} +.btn-rate-3:hover { + background-color: rgba(56, 189, 248, 0.15); + border-color: #38bdf8; +} + +.btn-rate-4 { + border-color: rgba(16, 185, 129, 0.4); + color: #34d399; +} +.btn-rate-4:hover { + background-color: rgba(16, 185, 129, 0.15); + border-color: #10b981; +} + +/* Interactive Trainer Card (Cloze & Dictation) */ +.trainer-card { + background-color: var(--bg-card); + border: 1px solid var(--border-color); + border-radius: var(--radius-lg); + padding: 32px; + box-shadow: var(--shadow-lg); + display: flex; + flex-direction: column; + gap: 20px; +} + +.trainer-header { + display: flex; + justify-content: space-between; + align-items: center; +} + +.mode-tag { + font-size: 12px; + font-weight: 700; + color: var(--primary); + background-color: var(--primary-dim); + padding: 3px 12px; + border-radius: var(--radius-full); +} + +.trainer-pos { + font-size: 12px; + color: var(--text-dim); +} + +.cloze-sentence-display { + font-size: 20px; + line-height: 1.6; + color: var(--text-main); + background-color: var(--bg-main); + padding: 20px 24px; + border-radius: var(--radius-md); + border-left: 4px solid var(--primary); +} + +.gap-placeholder { + color: var(--primary); + font-weight: 800; + letter-spacing: 2px; + border-bottom: 2px dashed var(--primary); + padding: 0 4px; +} + +.cloze-translation-hint { + font-size: 14px; + color: var(--text-muted); +} + +.cloze-translation-hint strong { + color: var(--text-main); + margin-left: 6px; +} + +.trainer-form { + display: flex; + flex-direction: column; + gap: 12px; +} + +.input-action-row { + display: flex; + gap: 10px; +} + +.input-hero { + font-size: 18px; + padding: 12px 18px; +} + +.trainer-feedback-box { + padding: 14px 18px; + border-radius: var(--radius-sm); + font-size: 15px; + font-weight: 600; + animation: fadeIn 0.2s ease-out; +} + +.trainer-feedback-box.correct { + background-color: var(--success-dim); + border: 1px solid var(--success); + color: #34d399; +} + +.trainer-feedback-box.incorrect { + background-color: var(--danger-dim); + border: 1px solid var(--danger); + color: #fca5a5; +} + +.trainer-revealed-details { + background-color: var(--bg-main); + border-radius: var(--radius-md); + padding: 16px; + display: flex; + flex-direction: column; + gap: 10px; + border: 1px solid var(--border-color); + animation: fadeIn 0.2s ease-out; +} + +/* Audio Dictation Mode */ +.dictation-hero { + display: flex; + flex-direction: column; + align-items: center; + gap: 14px; + padding: 16px 0; +} + +.dictation-pulse-btn { + width: 80px; + height: 80px; + border-radius: 50%; + background: linear-gradient(135deg, #0284c7, #38bdf8); + border: none; + color: #0f172a; + font-size: 34px; + cursor: pointer; + box-shadow: 0 0 0 0 rgba(56, 189, 248, 0.4); + animation: pulseAudio 2s infinite; + display: flex; + align-items: center; + justify-content: center; + transition: var(--transition); +} + +.dictation-pulse-btn:hover { + transform: scale(1.08); +} + +@keyframes pulseAudio { + 0% { box-shadow: 0 0 0 0 rgba(56, 189, 248, 0.6); } + 70% { box-shadow: 0 0 0 22px rgba(56, 189, 248, 0); } + 100% { box-shadow: 0 0 0 0 rgba(56, 189, 248, 0); } +} + +.dictation-instructions { + font-size: 14px; + color: var(--text-muted); +} + +.audio-replay-btn .kbd-sub { + font-size: 10px; + margin-left: 4px; +} + +.dictation-hint-wrap { + display: flex; + align-items: center; + gap: 10px; +} + +.dictation-hint-text { + font-size: 13px; + color: var(--text-muted); + font-style: italic; +} + +/* Summary Card */ +.srs-summary-card { + background-color: var(--bg-card); + border: 1px solid var(--border-color); + border-radius: var(--radius-lg); + padding: 44px 32px; + text-align: center; + display: flex; + flex-direction: column; + align-items: center; + gap: 20px; + box-shadow: var(--shadow-lg); + animation: fadeIn 0.3s ease-out; +} + +.summary-trophy { + font-size: 64px; + animation: bounce 0.6s ease; +} + +@keyframes bounce { + 0%, 100% { transform: translateY(0); } + 50% { transform: translateY(-16px); } +} + +.srs-summary-card h2 { + font-size: 28px; + font-weight: 800; +} + +.summary-lead { + font-size: 15px; + color: var(--text-muted); + max-width: 480px; +} + +.summary-metrics { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 16px; + width: 100%; + margin: 12px 0; +} + +.metric-card { + background-color: var(--bg-main); + border: 1px solid var(--border-color); + border-radius: var(--radius-md); + padding: 16px 12px; + display: flex; + flex-direction: column; + align-items: center; + gap: 4px; +} + +.metric-val { + font-size: 28px; + font-weight: 800; +} + +.metric-desc { + font-size: 11px; + color: var(--text-dim); +} + +.text-success { color: var(--success); } +.text-primary { color: var(--primary); } + +.summary-rates-bar { + display: flex; + gap: 12px; + font-size: 13px; + color: var(--text-muted); + flex-wrap: wrap; + justify-content: center; +} + +.summary-buttons { + display: flex; + gap: 14px; + margin-top: 12px; + flex-wrap: wrap; + justify-content: center; +} + /* Responsive */ @media (max-width: 640px) { .navbar { @@ -932,6 +1913,9 @@ body { .brand-sub { display: none; } + .trainer-text { + display: none; + } .main-content { padding: 16px 12px; gap: 24px; @@ -942,10 +1926,49 @@ body { .res-term { font-size: 22px; } - .hotkey-hints { - display: none; + .list-controls { + width: 100%; + display: flex; + align-items: center; + gap: 8px; + } + .search-mini-box { + flex: 1; } .search-mini-box input { - min-width: 100%; + min-width: 0; + width: 100%; + } + display: block; + } + .srs-topbar { + padding: 10px 12px; + flex-direction: column; + align-items: stretch; + } + .srs-topbar-left, .srs-topbar-right { + justify-content: space-between; + } + .srs-mode-switch { + overflow-x: auto; + } + .srs-rating-bar { + grid-template-columns: 1fr 1fr; + } + .export-grid { + grid-template-columns: 1fr; + } + .summary-metrics { + grid-template-columns: 1fr; + } + .batch-action-bar { + flex-direction: column; + gap: 10px; + bottom: 12px; + } + .batch-actions { + width: 100%; + justify-content: space-between; } } +