ashish-soni08 Codex commited on
Commit
ac53235
·
0 Parent(s):

chore: establish StudyFlow project structure

Browse files

Organize the standalone frontend for synchronized GitHub and Hugging Face Spaces publication, preserving the selected design reference and QA history.

Co-authored-by: Codex <codex@openai.com>

.gitattributes ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ *.png filter=lfs diff=lfs merge=lfs -text
2
+ *.png !text !filter !merge !diff
3
+ assets/hero/josephine-mainz-morning.png filter=lfs diff=lfs merge=lfs -text
4
+ assets/materials/mq3zhq9n-clipboard.png filter=lfs diff=lfs merge=lfs -text
5
+ assets/materials/mq3zhqat-clipboard.png filter=lfs diff=lfs merge=lfs -text
6
+ assets/materials/mq3zhqbu-clipboard.png filter=lfs diff=lfs merge=lfs -text
7
+ docs/design/reference/josephines-tagebuch.png filter=lfs diff=lfs merge=lfs -text
8
+ docs/design/screenshots/josephine-design-comparison.png filter=lfs diff=lfs merge=lfs -text
9
+ docs/design/screenshots/josephine-home-desktop.png filter=lfs diff=lfs merge=lfs -text
.gitignore ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .env
2
+ .env.*
3
+ !.env.example
4
+
5
+ __pycache__/
6
+ *.py[cod]
7
+ .pytest_cache/
8
+ .venv/
9
+ venv/
10
+
11
+ .uv-cache/
12
+ .uv-python/
13
+ .uv-tools/
14
+
15
+ node_modules/
16
+ dist/
17
+ coverage/
18
+
19
+ .DS_Store
20
+ Thumbs.db
21
+ *.log
22
+
23
+ # Local design exploration archive and duplicated source renders.
24
+ docs/design/local-archive/
25
+ docs/design/open-design/references/
LICENSE ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Ashish Soni
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
README.md ADDED
@@ -0,0 +1,78 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ title: StudyFlow AI
3
+ emoji: 📚
4
+ colorFrom: green
5
+ colorTo: purple
6
+ sdk: static
7
+ app_file: index.html
8
+ pinned: false
9
+ license: mit
10
+ short_description: A private, personalized AI study copilot.
11
+ ---
12
+
13
+ # StudyFlow AI
14
+
15
+ StudyFlow AI is a private study copilot built around the materials students
16
+ already use: handwritten notes, lecture slides, PDFs, calendars, flashcards,
17
+ and reminders.
18
+
19
+ The current release is an interactive frontend prototype centered on
20
+ Josephine Wendland, a multilingual teaching student from Mainz. Backend,
21
+ OCR, local model, and retrieval integrations will be added next.
22
+
23
+ ## Current Experience
24
+
25
+ - Personal daily briefing and schedule
26
+ - Material upload and OCR review interface
27
+ - Connected slide, note, and AI study canvas
28
+ - Calendar and personalized learning plan
29
+ - Knowledge-gap review
30
+ - Flashcard editing and export flows
31
+ - Quiz simulation and reminders
32
+ - Local-first privacy messaging
33
+
34
+ ## Run Locally
35
+
36
+ No build step is required.
37
+
38
+ ```powershell
39
+ node server.js
40
+ ```
41
+
42
+ Then open [http://localhost:4173](http://localhost:4173).
43
+
44
+ Python can also serve the static prototype:
45
+
46
+ ```powershell
47
+ python -m http.server 4173
48
+ ```
49
+
50
+ ## Repository Layout
51
+
52
+ ```text
53
+ assets/
54
+ hero/ Generated Josephine illustration
55
+ materials/ Sample note, slide, and calendar imagery
56
+ docs/design/ Reference concepts, QA, reviews, and design history
57
+ tools/ Local design and review utilities
58
+ app.js Application views and interactions
59
+ index.html Static Space entry point
60
+ styles.css Responsive visual system
61
+ server.js Tiny local development server
62
+ ```
63
+
64
+ ## Publication
65
+
66
+ The same Git history is pushed to:
67
+
68
+ - GitHub: source repository and development history
69
+ - Hugging Face Spaces: live static prototype
70
+
71
+ Commits created with Codex include an explicit Codex co-author trailer.
72
+
73
+ ## Next Phase
74
+
75
+ The frontend will later be migrated to a Gradio/FastAPI-backed application
76
+ with OCR, document linking, local retrieval, study-plan generation, and
77
+ notification workflows.
78
+
docs/design/finalized.json ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "source_mockup": null,
3
+ "source_plan": "Pasted PRD attachment and current mockup iterations",
4
+ "mode": "evolve",
5
+ "html_file": "C:\\Users\\Lenovo\\Documents\\build-small-hackathon\\projects\\studyflow-ai\\index.html",
6
+ "pretext_tier": "card-grid-dashboard",
7
+ "framework": "vanilla",
8
+ "iterations": 1,
9
+ "date": "2026-06-06T00:00:00+02:00",
10
+ "screen": "studyflow-lehramt-arbeitszentrale",
11
+ "branch": "main"
12
+ }
docs/design/legacy/script.js ADDED
@@ -0,0 +1,119 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ const toast = document.querySelector("#toast");
2
+ const statusStrip = document.querySelector("#statusStrip");
3
+ const flowCopy = document.querySelectorAll("[data-flow-copy]");
4
+
5
+ function syncFlowCopyState() {
6
+ flowCopy.forEach((element) => {
7
+ element.classList.toggle(
8
+ "is-overflowing",
9
+ element.scrollWidth > element.clientWidth + 1
10
+ );
11
+ });
12
+ }
13
+
14
+ if (flowCopy.length) {
15
+ const resizeObserver = new ResizeObserver(syncFlowCopyState);
16
+ resizeObserver.observe(document.querySelector(".workspace"));
17
+ flowCopy.forEach((element) => {
18
+ new MutationObserver(syncFlowCopyState).observe(element, {
19
+ characterData: true,
20
+ childList: true,
21
+ subtree: true,
22
+ });
23
+ });
24
+ document.fonts?.ready.then(syncFlowCopyState);
25
+ syncFlowCopyState();
26
+ }
27
+
28
+ function showToast(message) {
29
+ toast.textContent = message;
30
+ toast.classList.add("show");
31
+ window.clearTimeout(showToast.timer);
32
+ showToast.timer = window.setTimeout(() => toast.classList.remove("show"), 2800);
33
+ }
34
+
35
+ document.querySelectorAll(".nav-item").forEach((item) => {
36
+ item.addEventListener("click", () => {
37
+ document.querySelectorAll(".nav-item").forEach((nav) => nav.classList.remove("active"));
38
+ item.classList.add("active");
39
+ const label = item.textContent.replace(/\s+/g, " ").trim();
40
+ showToast(`${label} geöffnet. Für den Demo-Mockup bleibt die Übersicht im Fokus.`);
41
+ });
42
+ });
43
+
44
+ document.querySelectorAll(".language-toggle button").forEach((button) => {
45
+ button.addEventListener("click", () => {
46
+ document.querySelectorAll(".language-toggle button").forEach((btn) => btn.classList.remove("selected"));
47
+ button.classList.add("selected");
48
+ const language = button.textContent.trim();
49
+ if (language === "DE") {
50
+ showToast("Deutsch bleibt die Hauptsprache der App.");
51
+ } else {
52
+ showToast(`${language} wird als Materialsprache erkannt. Die Oberfläche bleibt deutsch.`);
53
+ }
54
+ });
55
+ });
56
+
57
+ document.querySelectorAll(".action-row").forEach((button) => {
58
+ button.addEventListener("click", () => {
59
+ showToast(button.dataset.message);
60
+ });
61
+ });
62
+
63
+ document.querySelectorAll(".confirm-button").forEach((button) => {
64
+ button.addEventListener("click", () => {
65
+ const row = button.closest("tr");
66
+ row.classList.remove("pending-row");
67
+ row.querySelector("td:nth-child(7)").innerHTML = '<span class="chip green">Bestätigt</span>';
68
+ row.querySelector("td:nth-child(8)").innerHTML = '<button class="icon-button" type="button" aria-label="Termin bestätigt"><span class="material-symbols-rounded">check</span></button>';
69
+ showToast("Termin bestätigt und in Mayas Lernplan übernommen.");
70
+ });
71
+ });
72
+
73
+ document.querySelector("#linkCheckBtn").addEventListener("click", () => {
74
+ showToast("Verknüpfungen geprüft: Folien 10-15 passen zu Notizen 4-6.");
75
+ });
76
+
77
+ document.querySelector("#addEventBtn").addEventListener("click", () => {
78
+ const tbody = document.querySelector("#eventRows");
79
+ const row = document.createElement("tr");
80
+ row.innerHTML = `
81
+ <td>Fr, 30. Mai</td>
82
+ <td>11:30</td>
83
+ <td>Unterrichtsentwurf Abgabe</td>
84
+ <td><span class="chip red">Abgabe</span></td>
85
+ <td>Notizen_Seminar.pdf</td>
86
+ <td>84%</td>
87
+ <td><span class="chip amber">Ausstehend</span></td>
88
+ <td><button class="confirm-button" type="button" aria-label="Termin bestätigen">OK</button></td>
89
+ `;
90
+ row.querySelector(".confirm-button").addEventListener("click", () => {
91
+ row.querySelector("td:nth-child(7)").innerHTML = '<span class="chip green">Bestätigt</span>';
92
+ row.querySelector("td:nth-child(8)").innerHTML = '<button class="icon-button" type="button" aria-label="Termin bestätigt"><span class="material-symbols-rounded">check</span></button>';
93
+ showToast("Neuer Termin bestätigt.");
94
+ });
95
+ tbody.appendChild(row);
96
+ showToast("Neuer erkannter Termin wurde ergänzt.");
97
+ });
98
+
99
+ document.querySelector("#processBtn").addEventListener("click", () => {
100
+ const button = document.querySelector("#processBtn");
101
+ button.disabled = true;
102
+ button.innerHTML = '<span class="material-symbols-rounded">progress_activity</span> Prüfe...';
103
+ window.setTimeout(() => {
104
+ statusStrip.classList.remove("show");
105
+ button.disabled = false;
106
+ button.innerHTML = '<span class="material-symbols-rounded">task_alt</span> Jetzt prüfen';
107
+ showToast("Uploads geprüft: 2 Termine, 1 neue Wissenslücke und 12 Karteikarten gefunden.");
108
+ }, 650);
109
+ });
110
+
111
+ document.querySelectorAll(".focus-list input").forEach((checkbox) => {
112
+ checkbox.addEventListener("change", () => {
113
+ showToast(checkbox.checked ? "Lernblock als erledigt markiert." : "Lernblock wieder geöffnet.");
114
+ });
115
+ });
116
+
117
+ document.querySelector('[data-view="uploads"]').addEventListener("click", () => {
118
+ statusStrip.classList.add("show");
119
+ });
docs/design/open-design/asset-inventory.md ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # StudyFlow AI Visual Asset Inventory
2
+
3
+ The six reference screens are the canonical visual targets. Only source-material
4
+ imagery should remain bitmap content; all interface elements must be rebuilt in
5
+ HTML and CSS.
6
+
7
+ ## Extracted bitmap assets
8
+
9
+ | Asset | Source | Used for |
10
+ | --- | --- | --- |
11
+ | `handwritten-calendar.png` | `02-uploads-ocr.png` | OCR review document canvas |
12
+ | `lecture-slide-scaffolding.png` | `03-study-canvas.png` | Main linked lecture slide |
13
+ | `handwritten-scaffolding-notes.png` | `03-study-canvas.png` | Main linked handwritten notes |
14
+ | `flashcard-source-slide.png` | `05-flashcards.png` | Flashcard source evidence |
15
+ | `flashcard-source-notes.png` | `05-flashcards.png` | Flashcard handwritten evidence |
16
+ | `note-code-switching.png` | `01-overview.png` | Recent-note thumbnail |
17
+ | `note-motivation.png` | `01-overview.png` | Recent-note thumbnail |
18
+ | `note-past-progressive.png` | `01-overview.png` | Recent-note thumbnail |
19
+ | `note-authentic-texts.png` | `01-overview.png` | Recent-note thumbnail |
20
+
21
+ ## Rebuild in code
22
+
23
+ - StudyFlow logo and all sidebar/navigation icons
24
+ - Maya avatar treatment
25
+ - Buttons, inputs, tabs, badges, menus, checkboxes, and toolbars
26
+ - Calendar grid and event blocks
27
+ - Roadmap timeline and progress indicators
28
+ - Charts, confidence bars, storage bars, and status dots
29
+ - PDF/PPTX/JPG file badges and document containers
30
+ - OCR bounding boxes and extraction overlays
31
+ - Quiz answers, navigator, timer, feedback, and source cards
32
+ - All typography, borders, shadows, spacing, and responsive layout
33
+
34
+ ## Replication constraint
35
+
36
+ Use only the six canonical references and the extracted assets above. Do not add
37
+ stock photography, illustrations, decorative gradients, or generated imagery.
docs/design/open-design/compare_josephine.py ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from pathlib import Path
2
+
3
+ from PIL import Image, ImageDraw
4
+
5
+
6
+ root = Path(__file__).resolve().parents[1]
7
+ reference = Image.open(
8
+ root / "docs" / "design" / "reference" / "josephines-tagebuch.png"
9
+ ).convert("RGB")
10
+ prototype = Image.open(
11
+ root / "docs" / "design" / "screenshots" / "josephine-home-desktop.png"
12
+ ).convert("RGB")
13
+
14
+ height = 1024
15
+ reference.thumbnail((720, height))
16
+ prototype.thumbnail((720, height))
17
+
18
+ canvas = Image.new("RGB", (1440, height + 42), "#f4f1eb")
19
+ canvas.paste(reference, (0, 42))
20
+ canvas.paste(prototype, (720, 42))
21
+
22
+ draw = ImageDraw.Draw(canvas)
23
+ draw.text((18, 13), "REFERENCE", fill="#4b2140")
24
+ draw.text((738, 13), "IMPLEMENTATION", fill="#0f5848")
25
+
26
+ canvas.save(
27
+ root / "docs" / "design" / "screenshots" / "josephine-design-comparison.png",
28
+ optimize=True,
29
+ )
docs/design/open-design/extract_assets.py ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from pathlib import Path
2
+
3
+ from PIL import Image
4
+
5
+
6
+ ROOT = Path(__file__).resolve().parent
7
+ REFS = ROOT / "references"
8
+ OUT = ROOT / "assets"
9
+ OUT.mkdir(exist_ok=True)
10
+
11
+
12
+ def crop(source: str, output: str, box: tuple[int, int, int, int]) -> None:
13
+ with Image.open(REFS / source) as image:
14
+ image.crop(box).save(OUT / output, optimize=True)
15
+
16
+
17
+ crop("02-uploads-ocr.png", "handwritten-calendar.png", (652, 220, 1136, 742))
18
+ crop("03-study-canvas.png", "lecture-slide-scaffolding.png", (355, 228, 653, 810))
19
+ crop("03-study-canvas.png", "handwritten-scaffolding-notes.png", (677, 242, 1103, 811))
20
+ crop("05-flashcards.png", "flashcard-source-slide.png", (1135, 216, 1299, 338))
21
+ crop("05-flashcards.png", "flashcard-source-notes.png", (1135, 443, 1304, 566))
22
+
23
+ crop("01-overview.png", "note-code-switching.png", (263, 883, 347, 974))
24
+ crop("01-overview.png", "note-motivation.png", (512, 883, 600, 974))
25
+ crop("01-overview.png", "note-past-progressive.png", (758, 883, 846, 974))
26
+ crop("01-overview.png", "note-authentic-texts.png", (1042, 883, 1130, 974))
docs/design/reference/josephines-tagebuch.png ADDED

Git LFS Details

  • SHA256: 34c89d386264b330030172a4ed3004856d18621fad066d945fbcfa77607dbaaa
  • Pointer size: 132 Bytes
  • Size of remote file: 1.93 MB
docs/design/reviews/design-html-review.md ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Design HTML Finalization
2
+
3
+ Date: 2026-06-06
4
+ Mode: evolve existing standalone mockup
5
+ Screen: StudyFlow AI Lehramt Arbeitszentrale
6
+ Framework: vanilla HTML, CSS, JavaScript
7
+
8
+ ## Implementation Spec
9
+
10
+ - Layout: responsive dashboard with fixed desktop sidebar, collapsed icon rail at tablet width, stacked mobile workspace.
11
+ - Typography: Fraunces display headings, IBM Plex Sans interface text, Material Symbols Rounded icons.
12
+ - Palette: warm paper background, green primary action/state, blue informational accents, amber/red priority states, dark-mode token overrides.
13
+ - Components: navigation rail, local-processing trust panel, upload status strip, material-linking workspace, AI action list, extraction review table, daily focus, knowledge gaps, roadmap, subject progress.
14
+
15
+ ## Design HTML Pass
16
+
17
+ - Added explicit theme metadata and dark-mode CSS token overrides.
18
+ - Added named breakpoint refinements for 1440px, 1024px, 768px, and 375px.
19
+ - Made the primary headline and subheading editable for demo iteration.
20
+ - Added a small ResizeObserver and MutationObserver pass for editable copy overflow.
21
+ - Kept the existing live app as the source of truth instead of generating a separate detached artifact.
22
+
23
+ ## Verification
24
+
25
+ - UI checker: passed via `uv run python ui_ux_check.py`.
26
+ - Desktop screenshot: `studyflow-design-html-desktop.png`
27
+ - Mobile screenshot: `studyflow-design-html-mobile.png`
28
+ - Narrow screenshot: `studyflow-design-html-375.png`
docs/design/reviews/design-qa.md ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Product Design QA: Josephines Tagebuch
2
+
3
+ Reference: `open-design/josephine-concepts/02-josephines-tagebuch.png`
4
+
5
+ Implementation: `josephine-home-desktop.png`
6
+
7
+ Comparison: `josephine-design-comparison.png`
8
+
9
+ ## Visual Match
10
+
11
+ - Desktop shell, navigation width, hero proportion, editorial typography, and
12
+ two-column daily agenda closely match the selected 1440 x 1024 reference.
13
+ - Josephine's generated Mainz morning scene preserves the intended story:
14
+ family, friends, horse, bread, cheese, aubergine accents, and teacher studies.
15
+ - The hierarchy stays personal and spacious. Josephine's name is the dominant
16
+ first-viewport signal and the agenda reads as one continuous surface.
17
+ - Colors, fine rules, restrained radii, and icon treatment are consistent with
18
+ the reference.
19
+ - The mobile layout was separately checked and reflows the hero copy below the
20
+ illustration without overlap.
21
+
22
+ ## Functional Check
23
+
24
+ - All nine navigation destinations load successfully.
25
+ - Timeline and "Heute wichtig" rows navigate to their related tools.
26
+ - Materials retains upload and OCR functionality.
27
+ - Existing canvas, calendar, cards, and quiz interactions remain available.
28
+ - Browser console: no warnings or errors.
29
+
30
+ ## Remaining P3 Polish
31
+
32
+ - The generated hero is a fresh image in the selected art direction rather
33
+ than a pixel-identical crop of the concept image.
34
+ - The small decorative horse sketch from the concept is intentionally omitted;
35
+ the horse is already represented prominently in the hero.
36
+
37
+ final result: passed
docs/design/reviews/design-review.md ADDED
@@ -0,0 +1,73 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # StudyFlow AI Design Review
2
+
3
+ Review lenses: ui-ux-pro-max and gstack design-review.
4
+ URL: http://localhost:4173
5
+ Screenshots:
6
+ - Desktop: studyflow-review-desktop.png
7
+ - Mobile: studyflow-review-mobile.png
8
+
9
+ ## Result
10
+
11
+ Design Score: B+ -> A-
12
+ AI Slop Score: B -> A-
13
+ Goodwill: 78/100 -> 86/100
14
+
15
+ The dashboard now reads as a credible app UI rather than a generic generated dashboard. The strongest parts are the German-first Lehramt framing, clear privacy promise, tight study workflow, and visible links between documents, deadlines, gaps, and actions.
16
+
17
+ ## Fixes Applied
18
+
19
+ 0. Added a `uv`-run UI/UX checker.
20
+ - Why: gives the mockup a repeatable quality gate for accessibility, interaction, motion, mobile behavior, and token hygiene.
21
+
22
+ 1. Replaced the CSS-drawn brand mark with an icon-library mark.
23
+ - Why: avoids fake visual assets and improves consistency with the icon system.
24
+
25
+ 2. Tightened navigation labels and mobile navigation behavior.
26
+ - Why: German labels were wrapping/clipping awkwardly, especially on mobile.
27
+
28
+ 3. Improved accessibility and interaction states.
29
+ - Added visible focus styles, `aria-label`s for icon-only table actions, visited-link styling, and reduced-motion handling.
30
+
31
+ 4. Increased critical touch targets.
32
+ - Language buttons, profile, table action buttons, and primary action rows now have more reliable hit areas.
33
+
34
+ 5. Added mobile-specific table behavior.
35
+ - The linked-materials area now becomes compact row cards on small screens instead of forcing a desktop spreadsheet layout.
36
+
37
+ 6. Added a skip link and better action feedback.
38
+ - The upload review CTA now has a short loading state, and keyboard users can jump straight to the main dashboard.
39
+
40
+ 7. Reduced ad-hoc color usage.
41
+ - Repeated colors now route through semantic CSS tokens; the final `uv` checker reports only 26 raw hex values outside `:root`.
42
+
43
+ ## Remaining Tradeoffs
44
+
45
+ - The desktop dashboard intentionally stays dense because it is meant to impress in a hackathon demo.
46
+ - Mobile is now readable, but it is still a secondary presentation mode. A production app would likely need a dedicated mobile information architecture.
47
+ - The mockup still uses realistic sample data rather than real uploaded notes/slides.
48
+
49
+ ## uv Check
50
+
51
+ Command:
52
+
53
+ ```powershell
54
+ $env:UV_CACHE_DIR='C:\Users\Lenovo\Documents\build-small-hackathon\.uv-cache'; $env:UV_PYTHON_INSTALL_DIR='C:\Users\Lenovo\Documents\build-small-hackathon\.uv-python'; uv run python ui_ux_check.py
55
+ ```
56
+
57
+ Result: all checks passed.
58
+
59
+ ## Emil Design Engineering Pass
60
+
61
+ Ran the Emil-style animation and interaction review after the UI/UX pass. See `emil-design-review.md` for the before/after table. The main changes were custom easing tokens, scale-based press feedback, pointer-gated hover states, and cleaner toast/status transitions.
62
+
63
+ ## Browser Annotation Fix
64
+
65
+ Fixed the `Lernroadmap` section after review in the browser. The prior timeline placed detail text into the wrong implicit grid cells, which made short phrases wrap vertically and feel broken. The updated roadmap wraps each entry into a structured row with a timeline marker, date label, main task, and supporting detail, plus clearer active and exam states.
66
+
67
+ ## Frontend Design Pass
68
+
69
+ Ran the `frontend-design` skill as a final aesthetic pass. See `frontend-design-review.md`. The main update was moving from generic dashboard polish toward a refined academic study-ledger identity with more distinctive typography, subtle paper-grid atmosphere, and clearer first-screen narrative labels.
70
+
71
+ ## Final Notes
72
+
73
+ Quickest next polish would be adding one real-looking handwritten note thumbnail and one real-looking lecture slide thumbnail. That would make the demo feel less synthetic without adding much complexity.
docs/design/reviews/emil-design-review.md ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Emil Design Engineering Review
2
+
3
+ Skill lens: Emil Kowalski-style design engineering polish.
4
+ URL: http://localhost:4173
5
+
6
+ | Before | After | Why |
7
+ | --- | --- | --- |
8
+ | Press feedback used `translateY(1px)` | Press feedback uses `transform: scale(0.97)` | Scaling the whole control gives a more tactile "the UI heard me" response without nudging layout rhythm. |
9
+ | Transitions used built-in `ease` / `.2s` shorthand | Transitions use explicit properties with `--ease-out: cubic-bezier(0.23, 1, 0.32, 1)` | Custom ease-out starts fast and feels more responsive for dashboard UI. |
10
+ | Hover states applied globally | Hover states are gated behind `@media (hover: hover) and (pointer: fine)` | Touch devices should not get sticky hover behavior after tapping. |
11
+ | Toast entered from a hardcoded `translateY(22px)` | Toast enters from `translateY(100%)` with transform/opacity only | Percentage translation adapts to the toast's own height and stays GPU-friendly. |
12
+ | Status strip transition used generic `ease` | Status strip uses exact max-height, padding, and opacity timings | The reveal feels intentional without animating expensive layout properties broadly. |
13
+ | Reduced-motion disabled all motion but left press transforms available | Reduced-motion now removes active transforms too | Users requesting reduced motion should not get unnecessary movement feedback. |
14
+
15
+ ## Verification
16
+
17
+ - `uv run python ui_ux_check.py`: passed.
18
+ - Desktop screenshot: `studyflow-emil-desktop.png`.
19
+ - Mobile screenshot: `studyflow-emil-mobile.png`.
20
+ - Local server health: `http://localhost:4173` returned `200`.
docs/design/reviews/frontend-design-review.md ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Frontend Design Pass
2
+
3
+ Skill lens: `frontend-design`.
4
+ URL: http://localhost:4173
5
+
6
+ ## Direction
7
+
8
+ Refined academic study ledger: a private, German-first learning workspace with a quiet paper-grid atmosphere, editorial title typography, and a stronger sense that StudyFlow connects real study artifacts rather than simply listing dashboard cards.
9
+
10
+ ## Changes
11
+
12
+ - Replaced the generic Inter-only type system with `Fraunces` for display type and `IBM Plex Sans` for interface text.
13
+ - Added a subtle paper-grid workspace texture so the app feels closer to study material and notebooks.
14
+ - Gave the primary material-linking panel a stronger visual identity with a soft gradient surface and accent spine.
15
+ - Added section kickers such as `01 / Materialabgleich` and `02 / Nächster Schritt` to make the first-screen story clearer.
16
+ - Lifted document tables into contained study-document surfaces so the linked slide/note area feels more intentional.
17
+ - Preserved the existing accessibility and interaction polish from the UI/UX and Emil passes.
18
+
19
+ ## Verification
20
+
21
+ - `uv run python ui_ux_check.py`: passed.
22
+ - Desktop screenshot: `studyflow-frontend-design-desktop.png`.
23
+ - Mobile screenshot: `studyflow-frontend-design-mobile.png`.
24
+ - Local server health: `http://localhost:4173` returned `200`.
docs/design/screenshots/josephine-design-comparison.png ADDED

Git LFS Details

  • SHA256: 0a3f99d64e8543aa215b4ecf760002ca7ec519426fc9686bb7bfd5f594c20097
  • Pointer size: 131 Bytes
  • Size of remote file: 797 kB
docs/design/screenshots/josephine-home-desktop.png ADDED

Git LFS Details

  • SHA256: 4876c8cc55326db01d73d36e1f65e55d239f37d7ed60da32fd12fc07133e9cee
  • Pointer size: 131 Bytes
  • Size of remote file: 167 kB
server.js ADDED
@@ -0,0 +1,42 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ const http = require("http");
2
+ const fs = require("fs");
3
+ const path = require("path");
4
+
5
+ const root = __dirname;
6
+ const port = process.env.PORT || 4173;
7
+
8
+ const types = {
9
+ ".html": "text/html; charset=utf-8",
10
+ ".css": "text/css; charset=utf-8",
11
+ ".js": "application/javascript; charset=utf-8",
12
+ ".png": "image/png",
13
+ ".jpg": "image/jpeg",
14
+ ".jpeg": "image/jpeg",
15
+ ".svg": "image/svg+xml",
16
+ };
17
+
18
+ const server = http.createServer((req, res) => {
19
+ const urlPath = decodeURIComponent(req.url.split("?")[0]);
20
+ const filePath = path.join(root, urlPath === "/" ? "index.html" : urlPath);
21
+
22
+ if (!filePath.startsWith(root)) {
23
+ res.writeHead(403);
24
+ res.end("Forbidden");
25
+ return;
26
+ }
27
+
28
+ fs.readFile(filePath, (err, data) => {
29
+ if (err) {
30
+ res.writeHead(404);
31
+ res.end("Not found");
32
+ return;
33
+ }
34
+
35
+ res.writeHead(200, { "Content-Type": types[path.extname(filePath)] || "application/octet-stream" });
36
+ res.end(data);
37
+ });
38
+ });
39
+
40
+ server.listen(port, () => {
41
+ console.log(`StudyFlow mockup running at http://localhost:${port}`);
42
+ });
tools/open-design-proxy.js ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ const http = require("node:http");
2
+
3
+ const targetPort = Number(process.env.OD_TARGET_PORT || 49540);
4
+ const listenPort = Number(process.env.OD_PROXY_PORT || 7456);
5
+
6
+ const server = http.createServer((request, response) => {
7
+ const proxy = http.request(
8
+ {
9
+ hostname: "127.0.0.1",
10
+ port: targetPort,
11
+ path: request.url,
12
+ method: request.method,
13
+ headers: request.headers,
14
+ },
15
+ (upstream) => {
16
+ response.writeHead(upstream.statusCode || 502, upstream.headers);
17
+ upstream.pipe(response);
18
+ }
19
+ );
20
+
21
+ proxy.on("error", (error) => {
22
+ response.writeHead(502, { "content-type": "application/json" });
23
+ response.end(JSON.stringify({ error: error.message }));
24
+ });
25
+
26
+ request.pipe(proxy);
27
+ });
28
+
29
+ server.listen(listenPort, "127.0.0.1");
tools/ui_ux_check.py ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from pathlib import Path
2
+ import re
3
+
4
+ root = Path(__file__).parent
5
+ html = (root / "index.html").read_text(encoding="utf-8")
6
+ css = (root / "styles.css").read_text(encoding="utf-8")
7
+
8
+ checks = []
9
+
10
+ def add(name, passed, detail):
11
+ checks.append((name, passed, detail))
12
+
13
+ add("viewport", 'name="viewport"' in html and "initial-scale=1" in html, "viewport meta should allow responsive scaling")
14
+ add("skip_link", "skip-link" in html, "keyboard users should be able to skip navigation")
15
+ add("focus_visible", ":focus-visible" in css, "visible focus rings should be present")
16
+ add("reduced_motion", "prefers-reduced-motion" in css, "motion should respect reduced motion")
17
+ add("visited_links", "a:visited" in css, "visited links should remain distinguishable")
18
+ add("no_horizontal_body", "overflow-x: hidden" in css, "body should avoid horizontal drift")
19
+ add("touch_action", "touch-action: manipulation" in css, "tap delay should be minimized")
20
+ add("aria_labels", html.count("aria-label") >= 6, "icon-only/profile controls need labels")
21
+ add("mobile_breakpoint", "@media (max-width: 820px)" in css, "small-screen layout rules should exist")
22
+ add("button_feedback", ":active" in css, "tap/press states should feel responsive")
23
+
24
+ small_fonts = [float(x) for x in re.findall(r"font-size:\s*([0-9.]+)px", css) if float(x) < 11]
25
+ add("tiny_text", not small_fonts, f"avoid unreadable text smaller than 11px; found {small_fonts}")
26
+
27
+ css_without_root = re.sub(r":root\s*\{.*?\}\s*", "", css, flags=re.S)
28
+ raw_hex_count = len(re.findall(r"#[0-9a-fA-F]{3,8}", css_without_root))
29
+ add("semantic_color_bias", raw_hex_count <= 35, f"prefer semantic CSS tokens outside :root; raw hex count is {raw_hex_count}")
30
+
31
+ failed = [item for item in checks if not item[1]]
32
+ for name, passed, detail in checks:
33
+ print(f"{'PASS' if passed else 'FAIL'} {name}: {detail}")
34
+
35
+ raise SystemExit(1 if failed else 0)