241 lines
14 KiB
HTML
241 lines
14 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
||
<title>Toadstool Cottage Counter</title>
|
||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||
<link href="https://fonts.googleapis.com/css2?family=Playfair+Display:wght@600;700&family=Nunito:wght@400;600;700&display=swap" rel="stylesheet">
|
||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.2/css/all.min.css" crossorigin="anonymous" referrerpolicy="no-referrer">
|
||
<link rel="icon" type="image/png" sizes="32x32" href="assets/icons/appicon-32x32.png">
|
||
<link rel="icon" type="image/png" sizes="128x128" href="assets/icons/appicon-128x128.png">
|
||
<link rel="apple-touch-icon" href="assets/icons/appicon-256x256.png">
|
||
<link rel="manifest" href="assets/site.webmanifest">
|
||
<meta name="theme-color" content="#e4e8d5">
|
||
<link rel="stylesheet" href="assets/style.css">
|
||
</head>
|
||
<body>
|
||
|
||
<header>
|
||
<div class="brand">
|
||
<img class="brand-icon" src="assets/icons/appicon-128x128.png" alt="Toadstool Cottage Counter icon">
|
||
<h1 id="appTitle">Toadstool Cottage Counter</h1>
|
||
</div>
|
||
<div class="header-controls">
|
||
<button class="header-btn hidden" id="installBtn" title="Install app"><i class="fa-solid fa-download"></i></button>
|
||
<button class="header-btn" id="motionBtn" onclick="toggleAnimations()" title="Toggle Animations"><i class="fa-solid fa-wand-magic-sparkles"></i></button>
|
||
<button class="header-btn" id="themeBtn" onclick="toggleTheme()" title="Toggle Dark Mode"><i class="fa-solid fa-moon"></i></button>
|
||
<button class="header-btn" id="focusBtn" onclick="toggleFocusMode()" title="Focus Mode (Keeps Screen On)"><i class="fa-solid fa-eye"></i></button>
|
||
<button class="header-btn" id="saveLoadBtn" onclick="openSaveModal()" title="Save/Load"><i class="fa-solid fa-floppy-disk"></i></button>
|
||
<button class="header-btn" id="authBtn" onclick="openAuthModal()" title="Sign in to sync"><i class="fa-solid fa-user"></i></button>
|
||
</div>
|
||
</header>
|
||
<input type="file" id="importFile" accept="application/json" class="hidden-input" />
|
||
|
||
<div class="container" id="app"></div>
|
||
|
||
<button class="fab" onclick="openModal('addProject')">+</button>
|
||
<button class="fab fab-pattern" onclick="openPatternComposer()" title="Open Pattern Composer"><i class="fa-solid fa-swatchbook"></i></button>
|
||
|
||
<div class="modal-overlay" id="modalOverlay">
|
||
<div class="modal-content">
|
||
<h3 class="modal-title" id="modalTitle">Title</h3>
|
||
<input type="text" class="modal-input" id="modalInput" autocomplete="off">
|
||
<div class="pattern-picker" id="patternPicker">
|
||
<label for="patternSelect">Pattern (optional)</label>
|
||
<select id="patternSelect">
|
||
<option value="">No pattern</option>
|
||
</select>
|
||
</div>
|
||
<div class="modal-actions">
|
||
<button class="modal-btn btn-cancel" onclick="closeModal()">Cancel</button>
|
||
<button class="modal-btn btn-save" onclick="saveModal()">Save</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="color-overlay" id="colorOverlay">
|
||
<div class="color-modal">
|
||
<h3 class="color-title">Pick a color</h3>
|
||
<div class="color-grid" id="colorGrid"></div>
|
||
<div class="color-custom">
|
||
<label for="customColorInput">Custom:</label>
|
||
<input type="color" id="customColorInput" />
|
||
</div>
|
||
<button class="modal-btn btn-cancel" onclick="closeColorPicker()">Close</button>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="save-overlay" id="saveOverlay">
|
||
<div class="save-modal">
|
||
<h3 class="color-title">Save or Load</h3>
|
||
<p class="save-subtext">Choose projects to include:</p>
|
||
<div class="save-list" id="saveList"></div>
|
||
<div class="save-actions">
|
||
<button class="modal-btn btn-cancel" onclick="closeSaveModal()">Cancel</button>
|
||
<button class="modal-btn btn-save" onclick="exportSelected()">Save</button>
|
||
<button class="modal-btn btn-save" onclick="triggerImport()">Load</button>
|
||
</div>
|
||
<div class="import-selection hidden" id="importSelection">
|
||
<p class="save-subtext">Imported file projects:</p>
|
||
<div class="save-list" id="importList"></div>
|
||
<div class="save-actions">
|
||
<button class="modal-btn btn-cancel" onclick="cancelImportSelection()">Cancel</button>
|
||
<button class="modal-btn btn-save" onclick="applyImportSelection()">Add Selected</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="auth-overlay" id="authOverlay">
|
||
<div class="auth-modal">
|
||
<div class="auth-modal-head">
|
||
<div>
|
||
<h3 class="color-title">Sign in (optional)</h3>
|
||
<p class="auth-subtext">Stay free forever. Sign in only if you want cloud backups and device switching.</p>
|
||
</div>
|
||
<button class="pattern-close" onclick="closeAuthModal()">×</button>
|
||
</div>
|
||
<div class="auth-status">
|
||
<span id="authStatusBadge" class="status-pill">Signed out</span>
|
||
<span id="authLastSync" class="status-subtext">Last sync: never</span>
|
||
</div>
|
||
<div class="auth-tabs">
|
||
<button class="auth-tab" data-mode="login" onclick="setAuthMode('login')">Login</button>
|
||
<button class="auth-tab" data-mode="signup" onclick="setAuthMode('signup')">Sign up</button>
|
||
</div>
|
||
<form class="auth-form" onsubmit="return submitAuth(event)">
|
||
<div class="auth-when-out">
|
||
<label class="field-label" for="authEmail">Email</label>
|
||
<input id="authEmail" type="email" placeholder="you@example.com" autocomplete="email">
|
||
<label class="field-label" for="authPassword">Password</label>
|
||
<input id="authPassword" type="password" placeholder="••••••••" autocomplete="current-password">
|
||
<p class="auth-hint">Cloud sync is not required. Offline data stays on this device. When enabled, we’ll sync projects/patterns securely.</p>
|
||
<div class="auth-actions">
|
||
<button type="button" class="modal-btn btn-cancel" onclick="closeAuthModal()">Cancel</button>
|
||
<button type="submit" class="modal-btn btn-save">Continue</button>
|
||
</div>
|
||
</div>
|
||
<div class="auth-when-in">
|
||
<label class="field-label" for="authDisplayName">Display name</label>
|
||
<input id="authDisplayName" type="text" placeholder="Your name">
|
||
<label class="field-label" for="authNote">Profile note</label>
|
||
<textarea id="authNote" rows="2" placeholder="Add a note for your patterns..."></textarea>
|
||
<div class="auth-actions auth-actions-stack">
|
||
<button type="button" class="modal-btn btn-save" onclick="autoSync()">Sync now</button>
|
||
<button type="button" class="modal-btn btn-save" onclick="saveProfile()">Save profile</button>
|
||
<button type="button" class="modal-btn btn-cancel" onclick="logoutAuth()">Log out</button>
|
||
</div>
|
||
<div id="adminPanel" class="admin-panel" style="display:none;">
|
||
<h4>Admin</h4>
|
||
<div class="admin-actions">
|
||
<button type="button" class="modal-btn btn-save" onclick="fetchPendingUsers()">Refresh pending</button>
|
||
<button type="button" class="modal-btn btn-save" onclick="downloadBackup()">Download backup</button>
|
||
<label class="modal-btn btn-save">
|
||
Restore backup
|
||
<input type="file" id="restoreInput" accept="application/json" style="display:none;" onchange="uploadRestore(event)">
|
||
</label>
|
||
</div>
|
||
<div id="pendingList" class="admin-list"></div>
|
||
</div>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="pattern-overlay" id="patternOverlay">
|
||
<div class="pattern-sheet">
|
||
<div class="pattern-sheet-header">
|
||
<div class="pattern-sheet-title">
|
||
<h2>Pattern Composer</h2>
|
||
<p class="pattern-sheet-subtitle">Draft rows plus materials, gauge, and abbreviations.</p>
|
||
</div>
|
||
<div class="pattern-modes">
|
||
<button class="pattern-mode" data-mode="crochet" onclick="setPatternMode('crochet')">Crochet</button>
|
||
<button class="pattern-mode" data-mode="knit" onclick="setPatternMode('knit')">Knit</button>
|
||
</div>
|
||
<div class="pattern-save-indicator" id="patternSaveIndicator">Saved</div>
|
||
<button class="pattern-close" onclick="closePatternComposer()">×</button>
|
||
</div>
|
||
<div class="pattern-toolbar">
|
||
<div class="pattern-toolbar-left">
|
||
<button class="toolbar-btn" onclick="savePatternDraft()" title="Save draft to basket"><i class="fa-solid fa-bookmark"></i> Save</button>
|
||
<button class="toolbar-btn" onclick="exportPatternPDF()" title="Export PDF"><i class="fa-solid fa-file-pdf"></i> PDF</button>
|
||
<button class="toolbar-btn" onclick="sharePattern()" title="Share link"><i class="fa-solid fa-link"></i> Share</button>
|
||
<button class="toolbar-btn" onclick="clearPatternOutput()" title="Clear draft"><i class="fa-solid fa-eraser"></i> Clear</button>
|
||
</div>
|
||
<div class="pattern-toolbar-right">
|
||
<span class="pattern-save-indicator small" id="patternSaveIndicatorMini">Saved</span>
|
||
</div>
|
||
</div>
|
||
<div class="pattern-body">
|
||
<div class="pattern-tabs">
|
||
<button class="pattern-tab" data-tab="info" onclick="showPatternTab('info')">Pattern Info</button>
|
||
<button class="pattern-tab" data-tab="steps" onclick="showPatternTab('steps')">Steps</button>
|
||
<button class="pattern-tab" data-tab="view" onclick="showPatternTab('view')">View</button>
|
||
<button class="pattern-tab" data-tab="library" onclick="showPatternTab('library')">My Basket</button>
|
||
</div>
|
||
<div class="pattern-section" data-section="info">
|
||
<label class="field-label" for="patternTitle">Title</label>
|
||
<input id="patternTitle" type="text" placeholder="e.g., Baby Fox Plush">
|
||
<label class="field-label" for="patternDesigner">Designer / Credits</label>
|
||
<input id="patternDesigner" type="text" placeholder="Your name or shop">
|
||
<label class="field-label" for="patternMaterials">Materials (one per line)</label>
|
||
<textarea id="patternMaterials" placeholder="Yarn (Color A) – worsted Yarn (Color B) – accent Hook – 4.0 mm Safety eyes, stuffing, needle"></textarea>
|
||
<div class="field-group-inline">
|
||
<div>
|
||
<label class="field-label" for="patternGaugeSts">Stitches / 4in (10cm)</label>
|
||
<input id="patternGaugeSts" type="text" placeholder="e.g., 16 sc">
|
||
</div>
|
||
<div>
|
||
<label class="field-label" for="patternGaugeRows">Rows / 4in (10cm)</label>
|
||
<input id="patternGaugeRows" type="text" placeholder="e.g., 18 rows">
|
||
</div>
|
||
</div>
|
||
<label class="field-label" for="patternGaugeHook">Hook / Needles</label>
|
||
<input id="patternGaugeHook" type="text" placeholder="e.g., 4.0 mm hook">
|
||
<label class="field-label" for="patternSize">Finished size</label>
|
||
<input id="patternSize" type="text" placeholder="Approx. 6 in / 15 cm tall">
|
||
<label class="field-label" for="patternGauge">Gauge (extra notes)</label>
|
||
<textarea id="patternGauge" placeholder="Magic ring start; or any extra gauge notes"></textarea>
|
||
<div class="abbrev-head">
|
||
<label class="field-label" for="patternAbbrev">Abbreviations</label>
|
||
<button class="secondary" onclick="loadDefaultAbbrev()">Load defaults</button>
|
||
</div>
|
||
<div id="patternAbbrevList" class="abbrev-grid"></div>
|
||
<textarea id="patternAbbrev" placeholder="sc – single crochet dc – double crochet inc – increase k – knit p – purl"></textarea>
|
||
<label class="field-label" for="patternStitches">Stitch guide / special stitches</label>
|
||
<textarea id="patternStitches" placeholder="Magic ring: ... Invisible decrease: ... Kfb: knit front and back ..."></textarea>
|
||
<label class="field-label" for="patternNotes">Notes / finishing</label>
|
||
<textarea id="patternNotes" placeholder="Assembly, finishing, safety warnings, credits..."></textarea>
|
||
<div class="pattern-row-actions pattern-footer">
|
||
<button class="secondary" onclick="importPatternJSON()">Import JSON</button>
|
||
</div>
|
||
</div>
|
||
<div class="pattern-section" data-section="steps">
|
||
<div class="pattern-steps-head">
|
||
<h4>Steps</h4>
|
||
<button class="primary" onclick="addStep()">+ Step</button>
|
||
</div>
|
||
<div id="patternSteps"></div>
|
||
</div>
|
||
<div class="pattern-section" data-section="library">
|
||
<div class="pattern-steps-head">
|
||
<h4>Saved Patterns</h4>
|
||
<button class="primary" onclick="savePatternDraft()">Save Draft to Basket</button>
|
||
</div>
|
||
<div id="patternLibrary" class="pattern-library"></div>
|
||
</div>
|
||
<div class="pattern-section" data-section="view">
|
||
<div id="patternView" class="pattern-view"></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<script src="assets/app.js"></script>
|
||
<footer class="footer-bg" aria-hidden="true"></footer>
|
||
</body>
|
||
</html>
|