From 67bb6baeb1bebb02eb29cc52e98c9625d015d7af Mon Sep 17 00:00:00 2001
From: peterwolf-pl <84736182+peterwolf-pl@users.noreply.github.com>
Date: Thu, 3 Jul 2025 03:01:07 +0200
Subject: [PATCH 1/8] Clean up flip page container
---
LetterComposer.jsx | 7 ++++++-
PrintModule.jsx | 20 +++++++++++++++++---
index.css | 29 +++++++++++++++++++++++++----
3 files changed, 48 insertions(+), 8 deletions(-)
diff --git a/LetterComposer.jsx b/LetterComposer.jsx
index b569a9c..9f79c41 100644
--- a/LetterComposer.jsx
+++ b/LetterComposer.jsx
@@ -18,6 +18,7 @@ export default function LetterComposer({ onMoveLineToPage }) {
const [activeLetter, setActiveLetter] = useState(null);
const [ghostPos, setGhostPos] = useState({ x: 0, y: 0, visible: false });
const [isDragging, setIsDragging] = useState(false);
+ const [pickupAnim, setPickupAnim] = useState(false);
const kasztaRef = useRef();
const wierszownikRef = useRef();
const [kasztaW, setKasztaW] = useState(KASZTA_WIDTH);
@@ -58,6 +59,8 @@ export default function LetterComposer({ onMoveLineToPage }) {
e.preventDefault();
const width = await getImageWidth(field.img);
setActiveLetter({ ...field, width });
+ setPickupAnim(true);
+ setTimeout(() => setPickupAnim(false), 300);
let x = 0, y = 0;
if (e.touches && e.touches[0]) {
x = e.touches[0].clientX;
@@ -153,6 +156,7 @@ export default function LetterComposer({ onMoveLineToPage }) {
setSlots(updatedSlots);
};
+
const scale = kasztaW / KASZTA_WIDTH;
const kasztaH = kasztaW * (KASZTA_HEIGHT / KASZTA_WIDTH);
const lineW = kasztaW * 0.8; // WIERSZOWNIK 80% kaszty
@@ -209,7 +213,8 @@ export default function LetterComposer({ onMoveLineToPage }) {
pointerEvents: "none",
zIndex: 1000,
opacity: 1,
- filter: "drop-shadow(2px 2px 2px #999)"
+ filter: "drop-shadow(2px 2px 2px #999)",
+ animation: pickupAnim ? "letter-pop 0.3s ease-out forwards" : undefined
}}
/>
);
diff --git a/PrintModule.jsx b/PrintModule.jsx
index e7bfe40..10252f1 100644
--- a/PrintModule.jsx
+++ b/PrintModule.jsx
@@ -5,6 +5,7 @@ const A4_HEIGHT = 1123;
export default function PrintModule({ lines, onBack }) {
const [pageW, setPageW] = useState(A4_WIDTH);
+ const [animReady, setAnimReady] = useState(false);
// Dynamiczne skalowanie dwóch kartek w oknie
useEffect(() => {
@@ -25,6 +26,11 @@ export default function PrintModule({ lines, onBack }) {
const scale = pageW / A4_WIDTH;
const pageH = pageW * (A4_HEIGHT / A4_WIDTH);
+ useEffect(() => {
+ const t = setTimeout(() => setAnimReady(true), 500);
+ return () => clearTimeout(t);
+ }, []);
+
// Lustrzane odbicie: linie od dołu, każda linia od końca i flipped poziomo
const mirroredLines = [...lines];
@@ -123,15 +129,23 @@ export default function PrintModule({ lines, onBack }) {
display: "flex",
flexDirection: "column",
alignItems: "flex-start",
- justifyContent: "flex-start"
+ justifyContent: "flex-start",
+ transform: animReady
+ ? "translateX(0) rotateX(0deg)"
+ : `translateX(-${pageW + 48 * scale}px) rotateX(180deg)`,
+ "--dx": `${pageW + 48 * scale}px`,
+ animation: animReady
+ ? "right-page-flip 1s ease forwards"
+ : "none",
+ transformStyle: "preserve-3d",
+ backfaceVisibility: "hidden"
}}
>
Date: Mon, 1 Sep 2025 17:54:26 +0200
Subject: [PATCH 2/8] Use blacha image for page composer background
---
LetterComposer copy.jsx | 328 ----------------------------------------
LetterComposer.jsx | 75 ++++-----
PageComposer.jsx | 7 +-
PrintModule.jsx | 1 -
aApp.jsx | 35 -----
assets/.gitkeep | 0
index.css | 15 +-
7 files changed, 56 insertions(+), 405 deletions(-)
delete mode 100644 LetterComposer copy.jsx
delete mode 100644 aApp.jsx
create mode 100644 assets/.gitkeep
diff --git a/LetterComposer copy.jsx b/LetterComposer copy.jsx
deleted file mode 100644
index 71c44a5..0000000
--- a/LetterComposer copy.jsx
+++ /dev/null
@@ -1,328 +0,0 @@
-import React, { useRef, useState, useEffect } from "react";
-
-const KASZTA_WIDTH = 1618;
-const KASZTA_HEIGHT = 1080;
-const SLOTS_COUNT = 20;
-
-function getImageWidth(src) {
- return new Promise((resolve) => {
- const img = new window.Image();
- img.onload = () => resolve(img.width);
- img.src = src;
- });
-}
-
-export default function LetterComposer() {
- const [letterFields, setLetterFields] = useState([]);
- const [slots, setSlots] = useState(Array(SLOTS_COUNT).fill(null));
- const [activeLetter, setActiveLetter] = useState(null);
- const [ghostPos, setGhostPos] = useState({ x: 0, y: 0, visible: false });
- const [isDragging, setIsDragging] = useState(false);
- const kasztaRef = useRef();
- const wierszownikRef = useRef();
- const [kasztaW, setKasztaW] = useState(KASZTA_WIDTH);
-
- // BLOKUJ SCROLL strony
- useEffect(() => {
- const oldOverflow = document.body.style.overflow;
- document.body.style.overflow = "hidden";
- return () => { document.body.style.overflow = oldOverflow; };
- }, []);
-
- // Responsywna szerokość kaszty
- useEffect(() => {
- function handleResize() {
- if (kasztaRef.current) {
- const parentW = kasztaRef.current.parentElement.offsetWidth;
- setKasztaW(Math.min(parentW, KASZTA_WIDTH));
- }
- }
- handleResize();
- window.addEventListener("resize", handleResize);
- return () => window.removeEventListener("resize", handleResize);
- }, []);
-
- useEffect(() => {
- fetch('/poz.json')
- .then(res => res.json())
- .then(setLetterFields)
- .catch(() => setLetterFields([]));
- }, []);
-
- // DRAG START (mouse/touch na field)
- const handleFieldDragStart = async (field, e) => {
- e.preventDefault();
- const width = await getImageWidth(field.img);
- setActiveLetter({ ...field, width });
- let x = 0, y = 0;
- if (e.touches && e.touches[0]) {
- x = e.touches[0].clientX;
- y = e.touches[0].clientY;
- } else if (e.clientX && e.clientY) {
- x = e.clientX;
- y = e.clientY;
- }
- setGhostPos({ x, y, visible: true });
- setIsDragging(true);
- };
-
- // DRAG MOVE & DROP (document-level, działa na iOS)
- useEffect(() => {
- if (!isDragging) return;
- const moveGhost = (e) => {
- setGhostPos({
- x: e.clientX,
- y: e.clientY,
- visible: true
- });
- };
- const moveGhostTouch = (e) => {
- if (e.touches && e.touches[0]) {
- setGhostPos({
- x: e.touches[0].clientX,
- y: e.touches[0].clientY,
- visible: true
- });
- }
- };
- const handleDrop = (e) => {
- let x = 0, y = 0;
- if (e.changedTouches && e.changedTouches[0]) {
- x = e.changedTouches[0].clientX;
- y = e.changedTouches[0].clientY;
- } else if (e.clientX && e.clientY) {
- x = e.clientX;
- y = e.clientY;
- }
- if (wierszownikRef.current) {
- const rect = wierszownikRef.current.getBoundingClientRect();
- if (
- x >= rect.left &&
- x <= rect.right &&
- y >= rect.top &&
- y <= rect.bottom
- ) {
- placeLetter();
- } else {
- setActiveLetter(null);
- setGhostPos({ x: 0, y: 0, visible: false });
- }
- }
- setIsDragging(false);
- };
- document.addEventListener("mousemove", moveGhost);
- document.addEventListener("touchmove", moveGhostTouch, { passive: false });
- document.addEventListener("mouseup", handleDrop);
- document.addEventListener("touchend", handleDrop, { passive: false });
- return () => {
- document.removeEventListener("mousemove", moveGhost);
- document.removeEventListener("touchmove", moveGhostTouch);
- document.removeEventListener("mouseup", handleDrop);
- document.removeEventListener("touchend", handleDrop);
- };
- // eslint-disable-next-line
- }, [isDragging]);
-
- // Umieszcza literę na wierszowniku
- function placeLetter() {
- let idx = slots.lastIndexOf(null);
- if (idx === -1) idx = 0;
- const updatedSlots = [...slots];
- updatedSlots[idx] = { ...activeLetter, id: Math.random().toString(36) };
- setSlots(updatedSlots);
- setActiveLetter(null);
- setGhostPos({ x: 0, y: 0, visible: false });
- setIsDragging(false);
- }
-
- // Kaszta – klik/tap poza polem kaszty anuluje ghosta
- function handleKasztaBackgroundClick(e) {
- setActiveLetter(null);
- setGhostPos({ x: 0, y: 0, visible: false });
- setIsDragging(false);
- }
-
- // Usuwanie liter z wierszownika
- const removeLetterFromSlot = (i) => {
- const updatedSlots = [...slots];
- updatedSlots[i] = null;
- setSlots(updatedSlots);
- };
-
- const scale = kasztaW / KASZTA_WIDTH;
- const kasztaH = kasztaW * (KASZTA_HEIGHT / KASZTA_WIDTH);
- const lineW = kasztaW * 0.8; // WIERSZOWNIK 80% kaszty
-
- function renderLettersOnLine() {
- let right = 0;
- let visibleSlots = [];
- for (let i = slots.length - 1; i >= 0; i--) {
- const slot = slots[i];
- if (!slot) continue;
- right += slot.width * scale;
- visibleSlots.push(
-
removeLetterFromSlot(i)}
- title="Kliknij, aby usunąć literę z wierszownika"
- >
-

-
- );
- }
- return visibleSlots.reverse();
- }
-
- // Ghost litera
- const renderGhostLetter = () => {
- if (!activeLetter || !ghostPos.visible) return null;
- return (
-

- );
- };
-
- return (
-
- {/* Kaszta */}
-
- {/* Tło łapiące kliknięcie na kaszcie */}
-
-

- {/* Fieldy na kaszcie – tylko drag start */}
- {letterFields.map(field => (
-
-
- {/* Wierszownik */}
-
-
-
- {renderLettersOnLine()}
-
-
- Educational Game for the Muzeum Książki Artystycznej w Łodzi by
peterwolf.pl
-
- {renderGhostLetter()}
-
-
-
-
- );
-}
diff --git a/LetterComposer.jsx b/LetterComposer.jsx
index 9f79c41..f5c724e 100644
--- a/LetterComposer.jsx
+++ b/LetterComposer.jsx
@@ -3,6 +3,9 @@ import React, { useRef, useState, useEffect } from "react";
const KASZTA_WIDTH = 1618;
const KASZTA_HEIGHT = 1080;
const SLOTS_COUNT = 20;
+const LINE_OFFSET_RIGHT = 340;
+const LINE_OFFSET_BOTTOM = 240;
+const LETTER_HEIGHT = 96;
function getImageWidth(src) {
return new Promise((resolve) => {
@@ -22,6 +25,7 @@ export default function LetterComposer({ onMoveLineToPage }) {
const kasztaRef = useRef();
const wierszownikRef = useRef();
const [kasztaW, setKasztaW] = useState(KASZTA_WIDTH);
+ const [wierszownikSize, setWierszownikSize] = useState({ w: 0, h: 0 });
// BLOKUJ SCROLL strony
useEffect(() => {
@@ -160,23 +164,32 @@ export default function LetterComposer({ onMoveLineToPage }) {
const scale = kasztaW / KASZTA_WIDTH;
const kasztaH = kasztaW * (KASZTA_HEIGHT / KASZTA_WIDTH);
const lineW = kasztaW * 0.8; // WIERSZOWNIK 80% kaszty
+ const lineScale = wierszownikSize.w ? lineW / wierszownikSize.w : scale;
+ const letterScale = lineScale * 2;
+ const lineStartX = wierszownikSize.w
+ ? (wierszownikSize.w - LINE_OFFSET_RIGHT) * lineScale
+ : 0;
+ const lineStartY = wierszownikSize.h
+ ? (wierszownikSize.h - LINE_OFFSET_BOTTOM) * lineScale
+ : 0;
function renderLettersOnLine() {
+ if (!wierszownikSize.w) return null;
let right = 0;
let visibleSlots = [];
for (let i = slots.length - 1; i >= 0; i--) {
const slot = slots[i];
if (!slot) continue;
- right += slot.width * scale;
+ right += slot.width * letterScale;
visibleSlots.push(
@@ -206,10 +219,10 @@ export default function LetterComposer({ onMoveLineToPage }) {
alt={activeLetter.char}
style={{
position: "fixed",
- left: ghostPos.x - (activeLetter.width * scale) / 2,
- top: ghostPos.y - (96 * scale),
- width: activeLetter.width * scale,
- height: 96 * scale,
+ left: ghostPos.x - (activeLetter.width * letterScale) / 2,
+ top: ghostPos.y - (LETTER_HEIGHT * letterScale),
+ width: activeLetter.width * letterScale,
+ height: LETTER_HEIGHT * letterScale,
pointerEvents: "none",
zIndex: 1000,
opacity: 1,
@@ -229,7 +242,6 @@ export default function LetterComposer({ onMoveLineToPage }) {
minHeight: "100vh",
display: "flex",
flexDirection: "column",
- background: "#f5f6f8",
alignItems: "center",
justifyContent: "stretch",
overflow: "hidden",
@@ -324,37 +336,30 @@ export default function LetterComposer({ onMoveLineToPage }) {
style={{
position: "relative",
width: lineW,
- minHeight: 116 * scale,
+ height: wierszownikSize.h
+ ? wierszownikSize.h * lineScale
+ : 116 * scale,
margin: "1px auto 0px auto",
borderRadius: 8 * scale,
- background: "#a6a3a8",
touchAction: "none",
flexShrink: 0,
boxSizing: "border-box"
}}
>
-
+ setWierszownikSize({
+ w: e.target.naturalWidth,
+ h: e.target.naturalHeight
+ })
+ }
+ draggable={false}
style={{
- position: "absolute",
- left: -5 * scale,
- top: 96 * scale + 16 * scale,
- width: lineW + (10 * scale),
- height: 8 * scale,
- background: "#111",
- borderRadius: 8 * scale,
- zIndex: 1
- }}
- />
-
{renderLettersOnLine()}
diff --git a/PageComposer.jsx b/PageComposer.jsx
index 3c267e9..30d7a1f 100644
--- a/PageComposer.jsx
+++ b/PageComposer.jsx
@@ -205,7 +205,6 @@ export default function PageComposer({
style={{
minHeight: "100vh",
width: "100vw",
- background: "#f5f6f8",
display: "flex",
flexDirection: "column",
alignItems: "center",
@@ -230,7 +229,11 @@ export default function PageComposer({
-
-
Vite + React
-
-
-
- Edit src/App.jsx and save to test HMR
-
-
-
- Click on the Vite and React logos to learn more
-
- >
- )
-}
-
-export default App
diff --git a/assets/.gitkeep b/assets/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/index.css b/index.css
index 33389ba..64dc43e 100644
--- a/index.css
+++ b/index.css
@@ -12,9 +12,13 @@
-webkit-user-select: none;
-ms-user-select: none;
- color-scheme: light dark;
- color: rgba(255, 255, 255, 0.87);
- background-color: #242424;
+ color-scheme: light;
+ color: #213547;
+ background-color: #d4d4d4;
+ background-image: url('/assets/bg.png');
+ background-repeat: repeat;
+ background-size: var(--bg-tile-size);
+ --bg-tile-size: 64px;
font-synthesis: none;
text-rendering: optimizeLegibility;
@@ -37,6 +41,10 @@ body {
place-items: center;
min-width: 320px;
min-height: 100vh;
+ background-color: #d4d4d4;
+ background-image: url('/assets/bg.png');
+ background-repeat: repeat;
+ background-size: var(--bg-tile-size);
}
h1 {
@@ -66,7 +74,6 @@ button:focus-visible {
@media (prefers-color-scheme: light) {
:root {
color: #213547;
- background-color: #ffffff;
}
a:hover {
color: #747bff;
From 458267c4cda7928a9e39d366eb84d275bc8be132 Mon Sep 17 00:00:00 2001
From: Peter Wolf <84736182+peterwolf-pl@users.noreply.github.com>
Date: Mon, 1 Sep 2025 18:02:24 +0200
Subject: [PATCH 3/8] Align letters using top-right offsets
---
LetterComposer.jsx | 13 ++++++++-----
1 file changed, 8 insertions(+), 5 deletions(-)
diff --git a/LetterComposer.jsx b/LetterComposer.jsx
index f5c724e..4b08679 100644
--- a/LetterComposer.jsx
+++ b/LetterComposer.jsx
@@ -3,8 +3,11 @@ import React, { useRef, useState, useEffect } from "react";
const KASZTA_WIDTH = 1618;
const KASZTA_HEIGHT = 1080;
const SLOTS_COUNT = 20;
-const LINE_OFFSET_RIGHT = 340;
-const LINE_OFFSET_BOTTOM = 240;
+// Starting point for the top-right corner of the first letter
+// measured from the wierszownik photo edges.
+// Offsets are scaled with the image dimensions.
+const LINE_OFFSET_RIGHT = 120;
+const LINE_OFFSET_TOP = 170;
const LETTER_HEIGHT = 96;
function getImageWidth(src) {
@@ -170,7 +173,7 @@ export default function LetterComposer({ onMoveLineToPage }) {
? (wierszownikSize.w - LINE_OFFSET_RIGHT) * lineScale
: 0;
const lineStartY = wierszownikSize.h
- ? (wierszownikSize.h - LINE_OFFSET_BOTTOM) * lineScale
+ ? LINE_OFFSET_TOP * lineScale
: 0;
function renderLettersOnLine() {
@@ -187,7 +190,7 @@ export default function LetterComposer({ onMoveLineToPage }) {
style={{
position: "absolute",
left: lineStartX - right,
- top: lineStartY - LETTER_HEIGHT * letterScale,
+ top: lineStartY,
width: slot.width * letterScale,
height: LETTER_HEIGHT * letterScale,
zIndex: 3,
@@ -220,7 +223,7 @@ export default function LetterComposer({ onMoveLineToPage }) {
style={{
position: "fixed",
left: ghostPos.x - (activeLetter.width * letterScale) / 2,
- top: ghostPos.y - (LETTER_HEIGHT * letterScale),
+ top: ghostPos.y,
width: activeLetter.width * letterScale,
height: LETTER_HEIGHT * letterScale,
pointerEvents: "none",
From 47edcb570df8beef050ec963f672f3c855246b5e Mon Sep 17 00:00:00 2001
From: Peter Wolf <84736182+peterwolf-pl@users.noreply.github.com>
Date: Mon, 1 Sep 2025 19:21:19 +0200
Subject: [PATCH 4/8] Revert wierszownik swap and drop blacha backdrop
---
LetterComposer.jsx | 63 +++++++++++++++++++++-------------------------
PageComposer.jsx | 1 -
2 files changed, 28 insertions(+), 36 deletions(-)
diff --git a/LetterComposer.jsx b/LetterComposer.jsx
index 4b08679..c592a0b 100644
--- a/LetterComposer.jsx
+++ b/LetterComposer.jsx
@@ -3,11 +3,6 @@ import React, { useRef, useState, useEffect } from "react";
const KASZTA_WIDTH = 1618;
const KASZTA_HEIGHT = 1080;
const SLOTS_COUNT = 20;
-// Starting point for the top-right corner of the first letter
-// measured from the wierszownik photo edges.
-// Offsets are scaled with the image dimensions.
-const LINE_OFFSET_RIGHT = 120;
-const LINE_OFFSET_TOP = 170;
const LETTER_HEIGHT = 96;
function getImageWidth(src) {
@@ -28,7 +23,6 @@ export default function LetterComposer({ onMoveLineToPage }) {
const kasztaRef = useRef();
const wierszownikRef = useRef();
const [kasztaW, setKasztaW] = useState(KASZTA_WIDTH);
- const [wierszownikSize, setWierszownikSize] = useState({ w: 0, h: 0 });
// BLOKUJ SCROLL strony
useEffect(() => {
@@ -167,17 +161,9 @@ export default function LetterComposer({ onMoveLineToPage }) {
const scale = kasztaW / KASZTA_WIDTH;
const kasztaH = kasztaW * (KASZTA_HEIGHT / KASZTA_WIDTH);
const lineW = kasztaW * 0.8; // WIERSZOWNIK 80% kaszty
- const lineScale = wierszownikSize.w ? lineW / wierszownikSize.w : scale;
- const letterScale = lineScale * 2;
- const lineStartX = wierszownikSize.w
- ? (wierszownikSize.w - LINE_OFFSET_RIGHT) * lineScale
- : 0;
- const lineStartY = wierszownikSize.h
- ? LINE_OFFSET_TOP * lineScale
- : 0;
+ const letterScale = scale * 2;
function renderLettersOnLine() {
- if (!wierszownikSize.w) return null;
let right = 0;
let visibleSlots = [];
for (let i = slots.length - 1; i >= 0; i--) {
@@ -189,8 +175,8 @@ export default function LetterComposer({ onMoveLineToPage }) {
key={slot.id}
style={{
position: "absolute",
- left: lineStartX - right,
- top: lineStartY,
+ left: lineW - right,
+ top: `${16 * letterScale}px`,
width: slot.width * letterScale,
height: LETTER_HEIGHT * letterScale,
zIndex: 3,
@@ -223,7 +209,7 @@ export default function LetterComposer({ onMoveLineToPage }) {
style={{
position: "fixed",
left: ghostPos.x - (activeLetter.width * letterScale) / 2,
- top: ghostPos.y,
+ top: ghostPos.y - (LETTER_HEIGHT * letterScale) / 2,
width: activeLetter.width * letterScale,
height: LETTER_HEIGHT * letterScale,
pointerEvents: "none",
@@ -339,30 +325,37 @@ export default function LetterComposer({ onMoveLineToPage }) {
style={{
position: "relative",
width: lineW,
- height: wierszownikSize.h
- ? wierszownikSize.h * lineScale
- : 116 * scale,
+ minHeight: 116 * letterScale,
margin: "1px auto 0px auto",
- borderRadius: 8 * scale,
+ borderRadius: 8 * letterScale,
+ background: "#a6a3a8",
touchAction: "none",
flexShrink: 0,
boxSizing: "border-box"
}}
>
-

- setWierszownikSize({
- w: e.target.naturalWidth,
- h: e.target.naturalHeight
- })
- }
- draggable={false}
+
+
{renderLettersOnLine()}
diff --git a/PageComposer.jsx b/PageComposer.jsx
index 30d7a1f..7e605c1 100644
--- a/PageComposer.jsx
+++ b/PageComposer.jsx
@@ -229,7 +229,6 @@ export default function PageComposer({
Date: Mon, 1 Sep 2025 19:21:24 +0200
Subject: [PATCH 5/8] Anchor letter placement and page margins
---
LetterComposer.jsx | 20 ++++++++++++--------
PageComposer.jsx | 15 +++++++++++----
2 files changed, 23 insertions(+), 12 deletions(-)
diff --git a/LetterComposer.jsx b/LetterComposer.jsx
index c592a0b..56e65f4 100644
--- a/LetterComposer.jsx
+++ b/LetterComposer.jsx
@@ -4,6 +4,8 @@ const KASZTA_WIDTH = 1618;
const KASZTA_HEIGHT = 1080;
const SLOTS_COUNT = 20;
const LETTER_HEIGHT = 96;
+const LINE_OFFSET_RIGHT = 120;
+const LINE_OFFSET_TOP = 170;
function getImageWidth(src) {
return new Promise((resolve) => {
@@ -162,6 +164,8 @@ export default function LetterComposer({ onMoveLineToPage }) {
const kasztaH = kasztaW * (KASZTA_HEIGHT / KASZTA_WIDTH);
const lineW = kasztaW * 0.8; // WIERSZOWNIK 80% kaszty
const letterScale = scale * 2;
+ const offsetRight = LINE_OFFSET_RIGHT * scale;
+ const offsetTop = LINE_OFFSET_TOP * scale;
function renderLettersOnLine() {
let right = 0;
@@ -175,8 +179,8 @@ export default function LetterComposer({ onMoveLineToPage }) {
key={slot.id}
style={{
position: "absolute",
- left: lineW - right,
- top: `${16 * letterScale}px`,
+ left: lineW - offsetRight - right,
+ top: offsetTop,
width: slot.width * letterScale,
height: LETTER_HEIGHT * letterScale,
zIndex: 3,
@@ -337,9 +341,9 @@ export default function LetterComposer({ onMoveLineToPage }) {
{lines.map((line, i) => {
@@ -259,9 +266,9 @@ export default function PageComposer({
flexDirection: "row",
alignItems: "flex-end",
justifyContent: "flex-end",
- margin: `${30 * scale}px ${20 * scale}px ${-24 * scale}px 0`,
+ margin: `0 0 ${-24 * scale}px 0`,
minHeight: 96 / 3 * scale,
- maxWidth: `calc(100% - ${40 * scale}px)`,
+ maxWidth: `calc(100% - ${sheetOffsetRight}px)`,
cursor: dragIndex === null ? "grab" : "default",
userSelect: "none",
touchAction: "none",
@@ -289,8 +296,8 @@ export default function PageComposer({
Date: Mon, 1 Sep 2025 19:21:29 +0200
Subject: [PATCH 6/8] Use wierszownik photo with scaled baseline
---
LetterComposer.jsx | 62 +++++++++++++++++++++-------------------------
1 file changed, 28 insertions(+), 34 deletions(-)
diff --git a/LetterComposer.jsx b/LetterComposer.jsx
index 56e65f4..8c3ee89 100644
--- a/LetterComposer.jsx
+++ b/LetterComposer.jsx
@@ -4,8 +4,9 @@ const KASZTA_WIDTH = 1618;
const KASZTA_HEIGHT = 1080;
const SLOTS_COUNT = 20;
const LETTER_HEIGHT = 96;
-const LINE_OFFSET_RIGHT = 120;
-const LINE_OFFSET_TOP = 170;
+const LINE_OFFSET_RIGHT = 340;
+const LINE_OFFSET_BOTTOM = 240;
+const WIERSZOWNIK_SRC = "/assets/wierszownik.jpg";
function getImageWidth(src) {
return new Promise((resolve) => {
@@ -25,6 +26,13 @@ export default function LetterComposer({ onMoveLineToPage }) {
const kasztaRef = useRef();
const wierszownikRef = useRef();
const [kasztaW, setKasztaW] = useState(KASZTA_WIDTH);
+ const [wierszownikDims, setWierszownikDims] = useState({ width: 1, height: 1 });
+
+ useEffect(() => {
+ const img = new window.Image();
+ img.onload = () => setWierszownikDims({ width: img.width, height: img.height });
+ img.src = WIERSZOWNIK_SRC;
+ }, []);
// BLOKUJ SCROLL strony
useEffect(() => {
@@ -160,12 +168,14 @@ export default function LetterComposer({ onMoveLineToPage }) {
};
- const scale = kasztaW / KASZTA_WIDTH;
+ const kasztaScale = kasztaW / KASZTA_WIDTH;
const kasztaH = kasztaW * (KASZTA_HEIGHT / KASZTA_WIDTH);
const lineW = kasztaW * 0.8; // WIERSZOWNIK 80% kaszty
- const letterScale = scale * 2;
- const offsetRight = LINE_OFFSET_RIGHT * scale;
- const offsetTop = LINE_OFFSET_TOP * scale;
+ const wierszScale = lineW / wierszownikDims.width;
+ const lineH = wierszownikDims.height * wierszScale;
+ const letterScale = wierszScale * 2;
+ const offsetRight = LINE_OFFSET_RIGHT * wierszScale;
+ const offsetTop = lineH - LINE_OFFSET_BOTTOM * wierszScale - LETTER_HEIGHT * letterScale;
function renderLettersOnLine() {
let right = 0;
@@ -299,10 +309,10 @@ export default function LetterComposer({ onMoveLineToPage }) {
aria-label="Wybierz czcionkę"
style={{
position: "absolute",
- left: Math.min(field.x1, field.x2) * scale,
- top: Math.min(field.y1, field.y2) * scale,
- width: Math.abs(field.x2 - field.x1) * scale,
- height: Math.abs(field.y2 - field.y1) * scale,
+ left: Math.min(field.x1, field.x2) * kasztaScale,
+ top: Math.min(field.y1, field.y2) * kasztaScale,
+ width: Math.abs(field.x2 - field.x1) * kasztaScale,
+ height: Math.abs(field.y2 - field.y1) * kasztaScale,
border: "0px solid #2563eb",
background: "rgba(96,165,250,0.0)",
borderRadius: "10px",
@@ -329,37 +339,21 @@ export default function LetterComposer({ onMoveLineToPage }) {
style={{
position: "relative",
width: lineW,
- minHeight: 116 * letterScale,
+ height: lineH,
margin: "1px auto 0px auto",
- borderRadius: 8 * letterScale,
- background: "#a6a3a8",
touchAction: "none",
flexShrink: 0,
boxSizing: "border-box"
}}
>
-
-
{renderLettersOnLine()}
From e5bec0d123329878b5a0603e041657a996dd4192 Mon Sep 17 00:00:00 2001
From: Peter Wolf <84736182+peterwolf-pl@users.noreply.github.com>
Date: Mon, 1 Sep 2025 19:42:09 +0200
Subject: [PATCH 7/8] Scale sheet offsets and fix line spacing
---
PageComposer.jsx | 26 +++++++++++++++++---------
1 file changed, 17 insertions(+), 9 deletions(-)
diff --git a/PageComposer.jsx b/PageComposer.jsx
index da73726..ae6b152 100644
--- a/PageComposer.jsx
+++ b/PageComposer.jsx
@@ -19,22 +19,31 @@ export default function PageComposer({
}) {
const [pageW, setPageW] = useState(A4_WIDTH);
const wrapperRef = useRef();
+ const [sheetDims, setSheetDims] = useState({ width: A4_WIDTH, height: A4_HEIGHT });
+
+ useEffect(() => {
+ const img = new Image();
+ img.src = "/assets/blacha.png";
+ img.onload = () => {
+ setSheetDims({ width: img.width, height: img.height });
+ };
+ }, []);
useEffect(() => {
function handleResize() {
const maxW = window.innerWidth * 0.95;
const stopkaH = 40 + 18;
const maxH = window.innerHeight - stopkaH - 32;
- const byHeight = maxH * (A4_WIDTH / A4_HEIGHT);
- setPageW(Math.min(A4_WIDTH, maxW, byHeight));
+ const byHeight = maxH * (sheetDims.width / sheetDims.height);
+ setPageW(Math.min(sheetDims.width, maxW, byHeight));
}
handleResize();
window.addEventListener("resize", handleResize);
return () => window.removeEventListener("resize", handleResize);
- }, []);
+ }, [sheetDims]);
- const scale = pageW / A4_WIDTH;
- const pageH = pageW * (A4_HEIGHT / A4_WIDTH);
+ const scale = pageW / sheetDims.width;
+ const pageH = pageW * (sheetDims.height / sheetDims.width);
const sheetOffsetRight = SHEET_OFFSET_RIGHT * scale;
const sheetOffsetTop = SHEET_OFFSET_TOP * scale;
@@ -236,8 +245,7 @@ export default function PageComposer({
backgroundImage: "url(/assets/blacha.png)",
backgroundRepeat: "no-repeat",
backgroundPosition: "center",
- backgroundSize: "cover",
- border: "4px solid #222",
+ backgroundSize: "100% 100%",
borderRadius: 6 * scale,
width: pageW,
height: pageH,
@@ -266,8 +274,8 @@ export default function PageComposer({
flexDirection: "row",
alignItems: "flex-end",
justifyContent: "flex-end",
- margin: `0 0 ${-24 * scale}px 0`,
- minHeight: 96 / 3 * scale,
+ margin: `0 0 ${8 * scale}px 0`,
+ minHeight: (96 / 3) * scale,
maxWidth: `calc(100% - ${sheetOffsetRight}px)`,
cursor: dragIndex === null ? "grab" : "default",
userSelect: "none",
From d368f4db602da0d147e30b1d720f30a55472643e Mon Sep 17 00:00:00 2001
From: Peter Wolf <84736182+peterwolf-pl@users.noreply.github.com>
Date: Mon, 1 Sep 2025 19:54:06 +0200
Subject: [PATCH 8/8] Double letter size on sheet
---
PageComposer.jsx | 14 ++++++++------
1 file changed, 8 insertions(+), 6 deletions(-)
diff --git a/PageComposer.jsx b/PageComposer.jsx
index ae6b152..3bcabe1 100644
--- a/PageComposer.jsx
+++ b/PageComposer.jsx
@@ -4,6 +4,8 @@ const A4_WIDTH = 796;
const A4_HEIGHT = 1123;
const SHEET_OFFSET_RIGHT = 120;
const SHEET_OFFSET_TOP = 170;
+const LETTER_SCALE = 2;
+const LETTER_BASE_HEIGHT = 96 / 3;
@@ -186,7 +188,7 @@ export default function PageComposer({
return {
background: "#e3f2ff",
borderRadius: 4 * scale,
- minHeight: 32 * scale,
+ minHeight: LETTER_BASE_HEIGHT * LETTER_SCALE * scale,
outline: "2px dashed #28b0ef",
transition: "background 0.12s",
};
@@ -275,7 +277,7 @@ export default function PageComposer({
alignItems: "flex-end",
justifyContent: "flex-end",
margin: `0 0 ${8 * scale}px 0`,
- minHeight: (96 / 3) * scale,
+ minHeight: LETTER_BASE_HEIGHT * LETTER_SCALE * scale,
maxWidth: `calc(100% - ${sheetOffsetRight}px)`,
cursor: dragIndex === null ? "grab" : "default",
userSelect: "none",
@@ -290,8 +292,8 @@ export default function PageComposer({
key={j}
src={letter.img}
alt={letter.char}
- width={(letter.width / 3) * scale}
- height={(96 / 3) * scale}
+ width={(letter.width / 3) * scale * LETTER_SCALE}
+ height={LETTER_BASE_HEIGHT * scale * LETTER_SCALE}
style={{ marginLeft: 0, pointerEvents: "none" }}
draggable={false}
/>
@@ -324,8 +326,8 @@ export default function PageComposer({
key={j}
src={letter.img}
alt={letter.char}
- width={(letter.width / 3) * scale}
- height={(96 / 3) * scale}
+ width={(letter.width / 3) * scale * LETTER_SCALE}
+ height={LETTER_BASE_HEIGHT * scale * LETTER_SCALE}
style={{ marginLeft: 0, pointerEvents: "none" }}
draggable={false}
/>