add new lesson mode and started code refraction

This commit is contained in:
Rene Kievits
2025-12-20 04:31:15 +01:00
parent 6438660b03
commit 4428a2b7be
101 changed files with 12255 additions and 8172 deletions

View File

@@ -6,13 +6,32 @@ $color-surface-light: hsl(221deg 17% 22%);
$color-text-white: hsl(0deg 0% 100%);
$color-text-grey: hsl(213deg 14% 70%);
$color-border: hsl(0deg 0% 100% / 8%);
$color-zinc-900: #18181b;
$color-reading-box: rgb(255 255 255 / 3%);
$color-stroke-drawn: #a1a1aa;
$color-srs-1: hsl(0deg 100% 73%);
$color-srs-2: hsl(39deg 98% 71%);
$color-srs-3: hsl(163deg 85% 64%);
$color-srs-4: hsl(206deg 92% 46%);
$color-srs-5: hsl(244deg 100% 82%);
$color-srs-6: hsl(247deg 72% 63%);
$color-srs-7: $color-primary;
$color-srs-8: hsl(339deg 97% 73%);
$color-srs-9: hsl(331deg 78% 59%);
$color-srs-10: hsl(51deg 100% 50%);
$color-danger: hsl(0deg 85% 65%);
$color-success: hsl(160deg 80% 45%);
$color-stroke-inactive: hsl(0deg 0% 33%);
$color-dot-base: hsl(0deg 0% 27%);
$srs-colors: (
1: $color-srs-1,
2: $color-srs-2,
3: $color-srs-3,
4: $color-srs-4,
5: $color-srs-5,
6: $color-srs-6,
7: $color-srs-7,
8: $color-srs-8,
9: $color-srs-9,
10: $color-srs-10
);

View File

@@ -18,6 +18,9 @@ $z-dropdown: 50;
$z-modal: 100;
$z-tooltip: 200;
$z-above: 2;
// Neue Z-Indizes aus dem Refactoring
$z-play-btn: 10;
$size-canvas: 300px;
$size-kanji-preview: 200px;
$size-icon-btn: 32px;
@@ -26,12 +29,6 @@ $stroke-width-main: 3px;
$stroke-width-arrow: 2px;
$font-size-svg-number: 5px;
$radius-xs: 2px;
$radius-sm: 4px;
$radius-md: 8px;
$radius-lg: 12px;
$radius-xl: 24px;
$radius-pill: 999px;
$radius-circle: 50%;
$size-legend-box: 12px;
$size-streak-dot: 24px;
$size-srs-track: 24px;
@@ -44,3 +41,14 @@ $padding-page-x: 24px;
$breakpoint-md: 960px;
$offset-fab: 20px;
$size-heatmap-cell-height: 10px;
$stroke-width-kanji: 6px;
$opacity-kanji-hint: 0.5;
$dash-kanji-hint: 10px 15px;
// --- NEUE VARIABLEN (Wichtig!) ---
$size-hero-wrapper: 140px;
$size-lesson-card-width: 450px;
$size-lesson-card-min-height: 500px;
$size-avatar-small: 32px;
$size-button-large-height: 70px;
$size-button-large-width: 280px;

View File

@@ -1,23 +1,53 @@
@use '../abstracts/variables' as *;
@use '../abstracts/mixins' as *;
@mixin grid-overlay {
&::before,
&::after {
content: '';
position: absolute;
pointer-events: none;
border-color: $color-border;
border-style: dashed;
border-width: 0;
z-index: $z-normal;
}
&::before {
top: 50%;
left: 10px;
right: 10px;
border-top-width: 1px;
}
&::after {
left: 50%;
top: 10px;
bottom: 10px;
border-left-width: 1px;
}
}
.canvas-container {
position: relative;
@include flex-center;
margin-bottom: $spacing-xl;
}
// The main drawing canvas wrapper
.canvas-wrapper {
position: relative;
width: $size-canvas;
height: $size-canvas;
background: $color-surface;
border-radius: $radius-lg;
background: rgba($color-surface, 0.95);
border: $border-width-md solid $color-surface-light;
position: relative;
cursor: crosshair;
border: $border-subtle;
box-shadow: $shadow-inset;
overflow: hidden;
cursor: crosshair;
display: flex;
align-items: center;
justify-content: center;
canvas {
position: absolute;
@@ -26,39 +56,66 @@
width: 100%;
height: 100%;
touch-action: none;
z-index: $z-above;
}
}
.loading-text {
position: absolute;
color: $color-text-grey;
z-index: $z-sticky;
font-size: $font-sm;
font-weight: $weight-medium;
letter-spacing: $tracking-wide;
// The SVG Viewer wrapper (Review/Lesson/Collection)
.svg-container {
width: 100%;
height: 100%;
position: relative;
display: flex;
align-items: center;
justify-content: center;
overflow: hidden;
// --- Unified Styles to match Canvas ---
background: $color-surface;
border-radius: $radius-lg; // Matches canvas-wrapper
border: $border-subtle; // Matches canvas-wrapper
box-shadow: $shadow-inset; // Matches canvas-wrapper
// -------------------------------------
@include grid-overlay;
&.hero-mode {
background: linear-gradient(145deg, $color-surface, #18181b);
border: 2px solid $color-border;
box-shadow: 0 4px 20px rgb(0 0 0 / 40%);
.stroke-path.drawn {
stroke: $color-text-white;
filter: drop-shadow(0 0 2px rgb(255 255 255 / 25%));
}
&::before,
&::after {
display: none;
}
}
}
.svg-container {
width: $size-kanji-preview;
height: $size-kanji-preview;
margin: 0 auto $spacing-lg;
background: rgba($color-bg-dark, 0.2);
border-radius: $radius-md;
border: $border-subtle;
position: relative;
@include flex-center;
overflow: hidden;
.kanji-svg {
z-index: $z-above;
display: block;
padding: 12%;
width: 100%;
height: 100%;
}
.stroke-path {
fill: none;
stroke: $color-stroke-inactive;
stroke-width: $stroke-width-main;
stroke-linecap: round;
stroke-linejoin: round;
transition: stroke $duration-normal;
stroke-width: $stroke-width-main;
transition:
stroke 0.3s ease,
opacity 0.3s ease;
&.drawn {
stroke: $color-stroke-drawn;
}
&.hidden {
opacity: 0;
@@ -70,10 +127,75 @@
stroke-dashoffset: var(--len);
animation: draw-stroke var(--duration) linear forwards;
}
}
&.drawn {
stroke: $color-text-white;
opacity: 1;
.stroke-ghost {
fill: none;
stroke: $color-stroke-inactive;
stroke-width: $stroke-width-main;
stroke-linecap: round;
stroke-linejoin: round;
opacity: 0.5;
stroke-dasharray: 10 15;
}
.stroke-badge-group {
filter: drop-shadow(0 1px 2px rgb(0 0 0 / 50%));
pointer-events: none;
}
.stroke-badge-bg {
fill: $color-primary;
opacity: 0.9;
transition: transform 0.2s ease;
}
.stroke-badge-text {
fill: $color-bg-dark;
font-size: $font-size-svg-number;
font-family: sans-serif;
font-weight: $weight-black;
user-select: none;
text-anchor: middle;
dominant-baseline: central;
}
.stroke-arrow-line {
fill: none;
stroke: $color-primary;
stroke-width: 1.5px;
vector-effect: non-scaling-stroke;
stroke-linecap: round;
stroke-linejoin: round;
pointer-events: none;
filter: drop-shadow(0 0 2px rgb(0 0 0 / 50%));
}
.play-btn {
position: absolute;
top: 10px;
right: 10px;
width: $size-icon-btn;
height: $size-icon-btn;
border-radius: $radius-circle;
background: rgb(255 255 255 / 10%);
border: 1px solid rgba($color-primary, 0.3);
color: $color-primary;
@include flex-center;
cursor: pointer;
z-index: $z-play-btn;
transition: all 0.2s ease;
&:hover {
transform: scale(1.05);
background: rgba($color-primary, 0.2);
}
svg {
width: 18px;
height: 18px;
}
}
@@ -83,65 +205,30 @@
}
}
.stroke-start-circle {
fill: $color-srs-1;
}
.stroke-number {
fill: $color-text-white;
font-size: $font-size-svg-number;
font-family: $font-family-sans;
font-weight: $weight-bold;
text-anchor: middle;
dominant-baseline: middle;
pointer-events: none;
}
.stroke-arrow-line {
fill: none;
stroke: rgba($color-secondary, 0.7);
stroke-width: $stroke-width-arrow;
stroke-linecap: round;
stroke-linejoin: round;
}
.loading-spinner {
color: $color-text-grey;
font-size: $font-sm;
}
.play-btn {
position: absolute;
top: $spacing-sm;
right: $spacing-sm;
background: rgba($color-bg-dark, 0.3);
border: $border-width-sm solid rgba($color-primary, 0.5);
color: $color-primary;
border-radius: $radius-circle;
width: $size-icon-btn;
height: $size-icon-btn;
@include flex-center;
cursor: pointer;
transition: all $duration-fast ease;
z-index: $z-sticky;
backdrop-filter: $blur-sm;
&:hover {
transform: scale(1.1);
background: $color-primary;
color: $color-bg-dark;
border-color: $color-primary;
box-shadow: $shadow-glow-base;
@keyframes shake-x {
0%,
100% {
transform: translateX(0);
}
&:active {
transform: scale(0.95);
20% {
transform: translateX(-6px);
}
svg {
width: $size-icon-small;
height: $size-icon-small;
40% {
transform: translateX(6px);
}
60% {
transform: translateX(-6px);
}
80% {
transform: translateX(6px);
}
}
.shake {
animation: shake-x 0.4s ease-in-out;
border-color: $color-danger !important;
}

View File

@@ -6,26 +6,27 @@
@include card-base;
border: $border-subtle;
background-color: $color-surface !important;
border-radius: $radius-xl !important;
}
.level-0 {
background-color: $bg-glass-subtle;
.welcome-btn {
height: $size-button-large-height !important;
width: $size-button-large-width !important;
.v-chip {
color: white !important;
background-color: $color-surface !important;
}
}
.level-1 {
background-color: color.mix($color-primary, $color-surface, 25%);
.streak-shield-avatar {
border: 1px solid currentcolor;
background: rgb(255 255 255 / 5%);
}
.level-2 {
background-color: color.mix($color-primary, $color-surface, 50%);
}
.level-3 {
background-color: $color-primary;
}
.level-4 {
background-color: color.scale($color-primary, $lightness: 40%);
.streak-day-label {
font-size: 9px !important;
}
.heatmap-container {
@@ -72,6 +73,26 @@
box-sizing: border-box;
}
.level-0 {
background-color: $bg-glass-subtle;
}
.level-1 {
background-color: color.mix($color-primary, $color-surface, 25%);
}
.level-2 {
background-color: color.mix($color-primary, $color-surface, 50%);
}
.level-3 {
background-color: $color-primary;
}
.level-4 {
background-color: color.scale($color-primary, $lightness: 40%);
}
.legend-container {
display: flex;
align-items: center;
@@ -153,22 +174,16 @@
@include scrollbar;
}
.gap-1 {
gap: $spacing-xs;
.srs-chart-container {
gap: 8px;
@media (max-width: 600px) {
gap: 4px;
}
}
.gap-2 {
gap: $spacing-sm;
}
.border-subtle {
border: $border-subtle;
}
.border-b-subtle {
border-bottom: $border-subtle;
}
.border-t-subtle {
border-top: $border-subtle;
.srs-track {
@media (max-width: 400px) {
width: 6px;
}
}

View File

@@ -8,3 +8,4 @@
@use 'pages/dashboard';
@use 'pages/review';
@use 'pages/collection';
@use 'pages/lesson';

View File

@@ -2,6 +2,11 @@
:root {
--v-theme-background: #{$color-bg-dark};
--color-surface: #{$color-surface};
@each $level, $color in $srs-colors {
--srs-#{$level}: #{$color};
}
}
body,
@@ -59,3 +64,13 @@ html,
opacity: $opacity-hover;
}
}
@each $level, $color in $srs-colors {
.text-srs-#{$level} {
color: var(--srs-#{$level}) !important;
}
.bg-srs-#{$level} {
background-color: var(--srs-#{$level}) !important;
}
}

View File

@@ -0,0 +1,78 @@
@use '../abstracts/variables' as *;
@use '../abstracts/mixins' as *;
.page-container-center {
height: 100%;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
padding: $spacing-md;
overflow-y: auto;
}
.lesson-card {
width: 100%;
max-width: $size-lesson-card-width;
border-radius: $radius-xl !important;
border: $border-subtle;
background-color: $color-surface !important;
min-height: $size-lesson-card-min-height;
display: flex;
flex-direction: column;
}
.hero-wrapper {
width: $size-hero-wrapper;
height: $size-hero-wrapper;
}
.radical-section {
width: 100%;
background: $color-zinc-900;
border-radius: $radius-md;
padding: $spacing-md;
margin-bottom: $spacing-lg;
}
.radical-chip {
border-radius: $radius-md;
border: $border-subtle;
background-color: #27272a;
img {
filter: invert(1);
width: 100%;
}
}
.readings-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: $spacing-lg;
width: 100%;
}
.reading-box {
background: $color-reading-box;
padding: $spacing-lg;
border-radius: $radius-lg;
text-align: center;
.label {
font-size: $font-xs;
color: #666;
letter-spacing: 1px;
text-transform: uppercase;
margin-bottom: $spacing-xs;
}
.val {
font-size: 1.1rem;
font-weight: $weight-bold;
}
}
.lesson-canvas-wrapper {
margin: 0 auto;
}

View File

@@ -1,21 +1,25 @@
@use '../abstracts/variables' as *;
@use '../abstracts/mixins' as *;
.review-card {
border: $border-subtle;
background-color: $color-surface !important;
width: 100%;
max-width: 420px;
}
.text-shadow {
text-shadow: $text-shadow;
}
.canvas-wrapper {
.review-canvas-area {
position: relative;
width: $size-canvas;
height: $size-canvas;
border-radius: $radius-lg;
background: $bg-glass-dark;
box-shadow: $shadow-inset;
margin-bottom: $spacing-sm;
.next-fab {
position: absolute;
bottom: -#{$offset-fab};
right: -#{$offset-fab};
bottom: 20px;
right: 20px;
z-index: $z-sticky;
}
}
@@ -45,3 +49,15 @@
opacity: 0;
transform: translateY(-#{$dist-slide-sm});
}
.opacity-80 {
opacity: 0.8;
}
.progress-bar {
opacity: 0.3;
}
.gap-2 {
gap: $spacing-sm;
}