commit 8cc347fa00d1521e1edafb5379663d4ea48635c3
parent a1fc6caeebb144e572b786c73c4f2b0bbc42e3aa
Author: Hunter
Date: Mon, 17 Nov 2025 11:00:12 -0500
support arbitrary grid sizes; add banana theme
Diffstat:
| M | readme.md | | | 13 | ++++++++----- |
| M | script.js | | | 108 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--- |
| M | style.css | | | 74 | +++++++++++++++++++++++++++++++++++++++++++++----------------------------- |
3 files changed, 157 insertions(+), 38 deletions(-)
diff --git a/readme.md b/readme.md
@@ -22,16 +22,19 @@ So here's a web app that does the same thing (and then some) for free.
## usage
<img src="readme_images/usage.gif">
-Different Techo types use differently-spaced grid paper. Use the links below to select the appropriate spacing for your Techo.
+Different Techo types use differently-sized grid paper. Use the links below to select the appropriate grid size for your Techo.
| <b>Techo</b> | <b>Link</b> |
|-------------------|-------------|
-| Planner A6 | [Use 4mm grid ↗](link)<br>(default) |
-| Original<br>Cousin<br>HON A6<br>HON A5<br>Original Avec<br>Cousin Avec<br>5-Year Techo A6<br>5-Year Techo A5<br>Day-Free A6<br>Day-Free A5 | [Use 3.7mm grid ↗](link) |
-| Weeks<br>Weeks Mega | [Use 3.55mm grid ↗](link) |
+| Planner A6 | [Use 4mm grid ↗](https://hunterirving.github.io/memori?grid-size=4mm)<br>(default) |
+| Original<br>Cousin<br>HON A6<br>HON A5<br>Original Avec<br>Cousin Avec<br>5-Year Techo A6<br>5-Year Techo A5<br>Day-Free A6<br>Day-Free A5 | [Use 3.7mm grid ↗](https://hunterirving.github.io/memori?grid-size=3.7mm) |
+| Weeks<br>Weeks Mega | [Use 3.55mm grid ↗](https://hunterirving.github.io/memori?grid-size=3.55mm) |
-After opening memori in your web browser, drag one or more images from your desktop onto the grid.
+
+### controls
+
+- After opening memori in your web browser, drag one or more images from your desktop onto the grid.<br>
<img src="readme_images/drag_n_drop.gif">
- click and drag images to move them<br>
diff --git a/script.js b/script.js
@@ -1,6 +1,40 @@
-const GRID_COLS = 49;
-const GRID_ROWS = 66;
-const CELL_SIZE_MM = 4; // Physical size of each cell when printed
+// Paper dimensions in mm
+const LETTER_WIDTH_MM = 215.9; // 8.5 inches
+const LETTER_HEIGHT_MM = 279.4; // 11 inches
+const A4_WIDTH_MM = 210;
+const A4_HEIGHT_MM = 297;
+const MARGIN_MM = 6.35; // 0.25 inches
+
+// Calculate maximum grid dimensions that fit both Letter and A4 with margins
+function calculateGridDimensions(cellSize) {
+ // Available printable area (limiting factor is the smaller of Letter/A4 for each dimension)
+ const availableWidth = Math.min(LETTER_WIDTH_MM, A4_WIDTH_MM) - (2 * MARGIN_MM);
+ const availableHeight = Math.min(LETTER_HEIGHT_MM, A4_HEIGHT_MM) - (2 * MARGIN_MM);
+
+ // Calculate how many cells fit
+ const cols = Math.floor(availableWidth / cellSize);
+ const rows = Math.floor(availableHeight / cellSize);
+
+ return { cols, rows };
+}
+
+// Calculate grid dimensions as percentage of Letter paper (used for screen layout)
+function calculateGridPercentages(cellSize, cols, rows) {
+ const gridWidthMM = cols * cellSize;
+ const gridHeightMM = rows * cellSize;
+ return {
+ widthPercent: (gridWidthMM / LETTER_WIDTH_MM) * 100,
+ heightPercent: (gridHeightMM / LETTER_HEIGHT_MM) * 100
+ };
+}
+
+// Cell size bounds (in mm)
+const MIN_CELL_SIZE_MM = 2;
+const MAX_CELL_SIZE_MM = 10;
+
+let GRID_COLS = 49;
+let GRID_ROWS = 66;
+let CELL_SIZE_MM = 4; // Physical size of each cell when printed (can be overridden by URL parameter)
const grid = document.getElementById('grid');
const page = document.querySelector('.page');
@@ -8,6 +42,54 @@ const page = document.querySelector('.page');
let popupElement = null;
let popupTimeout = null;
+// Parse URL parameters for custom cell size
+function parseCellSizeFromURL() {
+ const urlParams = new URLSearchParams(window.location.search);
+ const gridSizeParam = urlParams.get('grid-size');
+
+ if (gridSizeParam) {
+ // Remove 'mm' suffix if present
+ const sizeStr = gridSizeParam.toLowerCase().replace('mm', '').trim();
+ const size = parseFloat(sizeStr);
+
+ // Check if size is valid
+ if (isNaN(size)) {
+ showPopup('Invalid grid size. Using default 4mm.', 3000);
+ return 4;
+ }
+
+ // Check if size is within reasonable bounds
+ if (size < MIN_CELL_SIZE_MM || size > MAX_CELL_SIZE_MM) {
+ showPopup(`Grid size must be between ${MIN_CELL_SIZE_MM}mm and ${MAX_CELL_SIZE_MM}mm. Using default 4mm.`, 3500);
+ return 4;
+ }
+
+ // Valid size
+ showPopup(`Grid set to ${size}mm`);
+ return size;
+ }
+
+ return 4; // Default
+}
+
+// Initialize cell size from URL
+CELL_SIZE_MM = parseCellSizeFromURL();
+
+// Calculate grid dimensions based on cell size
+const gridDimensions = calculateGridDimensions(CELL_SIZE_MM);
+GRID_COLS = gridDimensions.cols;
+GRID_ROWS = gridDimensions.rows;
+
+// Calculate grid percentages for screen layout
+const gridPercentages = calculateGridPercentages(CELL_SIZE_MM, GRID_COLS, GRID_ROWS);
+
+// Update CSS variables for both screen and print
+document.documentElement.style.setProperty('--cell-size-mm', `${CELL_SIZE_MM}mm`);
+document.documentElement.style.setProperty('--grid-cols', GRID_COLS);
+document.documentElement.style.setProperty('--grid-rows', GRID_ROWS);
+document.documentElement.style.setProperty('--grid-width-percent', `${gridPercentages.widthPercent}%`);
+document.documentElement.style.setProperty('--grid-height-percent', `${gridPercentages.heightPercent}%`);
+
// Calculate cell size dynamically based on actual grid dimensions
function getCellSize() {
const gridRect = grid.getBoundingClientRect();
@@ -78,9 +160,27 @@ function getPixelPerfectBounds(cellX, cellY, cellWidth, cellHeight) {
for (let i = 0; i < GRID_COLS * GRID_ROWS; i++) {
const cell = document.createElement('div');
cell.className = 'grid-cell';
+
+ // Add right border to rightmost column
+ const col = i % GRID_COLS;
+ if (col === GRID_COLS - 1) {
+ cell.classList.add('right-edge');
+ }
+
+ // Add bottom border to bottom row
+ const row = Math.floor(i / GRID_COLS);
+ if (row === GRID_ROWS - 1) {
+ cell.classList.add('bottom-edge');
+ }
+
grid.appendChild(cell);
}
+// Apply solid border style for grids 3.56mm or smaller
+if (CELL_SIZE_MM <= 3.56) {
+ document.documentElement.classList.add('solid-grid');
+}
+
let images = [];
let dragState = null;
let resizeState = null;
@@ -997,7 +1097,7 @@ window.addEventListener('afterprint', () => {
// Theme system
let currentThemeIndex = 0;
let isF2Pressed = false;
-const themes = ['sea-breeze', 'grape-soda', 'grapefruit', 'guac', 'mojito', 'toast'];
+const themes = ['sea-breeze', 'grape-soda', 'grapefruit', 'guac', 'mojito', 'banana'];
function setTheme(theme) {
document.documentElement.setAttribute('data-theme', theme);
diff --git a/style.css b/style.css
@@ -12,11 +12,11 @@
--lime: #a9cf51;
--avocado: #7a8520;
- /* toast theme colors */
- --butter: #ffe598;
- --white-wheat: #fffef9;
- --crumb: #decca3;
- --peanut-butter: #be852a;
+ /* banana theme colors */
+ --peel: #ffcc12;
+ --flesh: #f8f5e3;
+ --unripe: #a5d269;
+ --bruise: #4d4235;
/* mojito theme colors */
--mint: #9febaa;
@@ -56,6 +56,7 @@
--desk: var(--cerulean);
--page: var(--mist);
--grid-line: var(--horizon);
+ --grid-line-light: color-mix(in srgb, var(--grid-line) 65%, transparent);
--accent: var(--lagoon);
/* Set text color for all themes */
@@ -73,11 +74,11 @@
--accent: var(--onion);
}
-:root[data-theme="toast"] {
- --desk: var(--butter);
- --page: var(--white-wheat);
- --grid-line: var(--crumb);
- --accent: var(--peanut-butter);
+:root[data-theme="banana"] {
+ --desk: var(--peel);
+ --page: var(--flesh);
+ --grid-line: var(--unripe);
+ --accent: var(--bruise);
}
:root[data-theme="mojito"] {
@@ -165,18 +166,36 @@ body {
display: block;
}
-/* Add right border to rightmost column (every 49th cell starting from cell 49) */
-.grid-cell:nth-child(49n) {
+/* Add right border to rightmost column */
+.grid-cell.right-edge {
border-right: 1px dashed var(--grid-line);
width: calc(100% + 1px);
}
-/* Add bottom border to bottom row (last 49 cells: 3185-3234) */
-.grid-cell:nth-child(n + 3185) {
+/* Add bottom border to bottom row */
+.grid-cell.bottom-edge {
border-bottom: 1px dashed var(--grid-line);
height: calc(100% + 1px);
}
+/* Solid grid lines for cell sizes <= 3.56mm (screen preview only) */
+.solid-grid .grid-cell {
+ border-top-style: solid;
+ border-left-style: solid;
+ border-top-color: var(--grid-line-light);
+ border-left-color: var(--grid-line-light);
+}
+
+.solid-grid .grid-cell.right-edge {
+ border-right-style: solid;
+ border-right-color: var(--grid-line-light);
+}
+
+.solid-grid .grid-cell.bottom-edge {
+ border-bottom-style: solid;
+ border-bottom-color: var(--grid-line-light);
+}
+
@media (max-width: 680px) {
body {
padding: 0;
@@ -190,19 +209,19 @@ body {
.grid-cell {
border-top-style: solid;
border-left-style: solid;
- border-top-color: color-mix(in srgb, var(--grid-line) 65%, transparent);
- border-left-color: color-mix(in srgb, var(--grid-line) 65%, transparent);
+ border-top-color: var(--grid-line-light);
+ border-left-color: var(--grid-line-light);
}
- .grid-cell:nth-child(49n) {
+ .grid-cell.right-edge {
border-right-style: solid;
- border-right-color: color-mix(in srgb, var(--grid-line) 65%, transparent);
+ border-right-color: var(--grid-line-light);
width: calc(100% + 1px);
}
- .grid-cell:nth-child(n + 3185) {
+ .grid-cell.bottom-edge {
border-bottom-style: solid;
- border-bottom-color: color-mix(in srgb, var(--grid-line) 65%, transparent);
+ border-bottom-color: var(--grid-line-light);
height: calc(100% + 1px);
}
}
@@ -483,22 +502,16 @@ body.dragging * {
padding: 0 !important;
}
- .grid-cell:nth-child(49n) {
+ .grid-cell.right-edge {
border-right: 1px dashed var(--default-grid) !important;
width: calc(100% + 1px) !important;
}
- .grid-cell:nth-child(n + 3185) {
+ .grid-cell.bottom-edge {
border-bottom: 1px dashed var(--default-grid) !important;
height: calc(100% + 1px) !important;
}
- /* Remove bottom border from cell above bottom-right corner to prevent artifact */
- .grid-cell:nth-child(3185) {
- border-bottom: none !important;
- height: 100% !important;
- }
-
.image-container {
outline: none !important;
position: absolute !important;
@@ -543,6 +556,10 @@ body.dragging * {
.add-images-btn {
display: none !important;
}
+
+ .popup-notification {
+ display: none !important;
+ }
}
@page {
@@ -596,7 +613,6 @@ body.dragging * {
transform: translateX(-50%);
background-color: var(--accent);
color: var(--page);
- border: 2px solid var(--page);
padding: 0.7rem 0.9rem;
border-radius: 50rem;
font-size: 1.2rem;