claude go reimplement
This commit is contained in:
1 parent
4bcb924158
commit
f523c79618
83 files changed
+3714
-3454
No files matched your search
@@ -0,0 +1,152 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Saved - KJV API</title>
|
||||
<link rel="stylesheet" href="/styles/page.css?v=__V__">
|
||||
<style>
|
||||
.item { background-color: var(--color-bg-secondary); border: 1px solid var(--color-border);
|
||||
border-left: 3px solid var(--color-accent-primary); border-radius: var(--border-radius-sm);
|
||||
padding: var(--space-sm) var(--space-md); margin-bottom: var(--space-sm); }
|
||||
.item .ref { font-weight: 700; }
|
||||
.item .text { font-family: var(--font-scripture); margin: 0.25rem 0 0.5rem; color: var(--color-text-primary); }
|
||||
.item p { margin: 0.25rem 0; }
|
||||
.item button { min-height: 36px; padding: 0.2rem 0.9rem; font-size: 0.85rem;
|
||||
background-color: var(--color-bg-tertiary); color: var(--color-text-primary); border: 1px solid var(--color-border); }
|
||||
.item .mynote { white-space: pre-wrap; border-left: 3px solid #d9a93b; padding-left: 0.6rem; color: var(--color-text-secondary); }
|
||||
.file-btn { display: inline-block; padding: 0.6rem 1.2rem; border: 1px solid var(--color-border); border-radius: var(--border-radius-sm);
|
||||
background-color: var(--color-bg-tertiary); font-weight: 600; }
|
||||
.empty { color: var(--color-text-muted); }
|
||||
.note { font-size: 0.9rem; color: var(--color-text-muted); }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<a href="/">Home</a> <a href="/find">Quick Search</a> <a href="/plan">Reading Plan</a>
|
||||
<a href="/random?view=rich">Random Verse</a> <a href="/votd?view=rich">Verse of the Day</a>
|
||||
</header>
|
||||
<h2>Saved</h2>
|
||||
<p class="note">Saved in this browser only. Clearing site data or using another device starts fresh.</p>
|
||||
|
||||
<h3>Notes</h3>
|
||||
<div id="notes"></div>
|
||||
<h3>Favorite verses</h3>
|
||||
<div id="fav"></div>
|
||||
<h3>Definitions</h3>
|
||||
<div id="defs"></div>
|
||||
<h3>Cross references</h3>
|
||||
<div id="xrefs"></div>
|
||||
|
||||
<h3>Backup</h3>
|
||||
<p class="note">Browser storage can be cleared without warning. Download a copy of your notes and saved items, and restore it here or on another device.</p>
|
||||
<p>
|
||||
<button type="button" id="backup">Download backup</button>
|
||||
<label class="choice"><input type="file" id="restore" accept="application/json,.json" hidden>
|
||||
<span class="file-btn">Restore from backup</span></label>
|
||||
</p>
|
||||
<p class="note" id="backup-status" role="status"></p>
|
||||
|
||||
<script>
|
||||
var KEYS = { notes: 'kjv.notes', fav: 'kjv.favorites', defs: 'kjv.definitions', xrefs: 'kjv.crossrefs' };
|
||||
|
||||
function load(key) {
|
||||
try { var v = JSON.parse(localStorage.getItem(key)); return Array.isArray(v) ? v : []; }
|
||||
catch (e) { return []; }
|
||||
}
|
||||
function el(tag, cls, text) {
|
||||
var n = document.createElement(tag);
|
||||
if (cls) n.className = cls;
|
||||
if (text !== undefined) n.textContent = text;
|
||||
return n;
|
||||
}
|
||||
function findUrl(ref) { return '/find?kw=' + encodeURIComponent(ref) + '&view=rich'; }
|
||||
|
||||
function render(key, targetId, emptyText, build) {
|
||||
var target = document.getElementById(targetId);
|
||||
target.textContent = '';
|
||||
var list = load(key);
|
||||
if (!list.length) { target.appendChild(el('p', 'empty', emptyText)); return; }
|
||||
list.forEach(function (item, i) {
|
||||
var box = el('div', 'item');
|
||||
build(box, item);
|
||||
var remove = el('button', '', 'Remove');
|
||||
remove.type = 'button';
|
||||
remove.addEventListener('click', function () {
|
||||
var current = load(key);
|
||||
current.splice(i, 1);
|
||||
try { localStorage.setItem(key, JSON.stringify(current)); } catch (e) {}
|
||||
render(key, targetId, emptyText, build);
|
||||
});
|
||||
box.appendChild(remove);
|
||||
target.appendChild(box);
|
||||
});
|
||||
}
|
||||
|
||||
function refLink(ref) {
|
||||
var a = el('a', 'ref', ref);
|
||||
a.href = findUrl(ref);
|
||||
return a;
|
||||
}
|
||||
|
||||
function draw() {
|
||||
render(KEYS.notes, 'notes', 'No notes yet. Tap a verse (or its \u22ef button) and choose Note.', function (box, n) {
|
||||
box.appendChild(refLink(n.ref));
|
||||
if (n.text) { box.appendChild(el('div', 'text', n.text)); }
|
||||
box.appendChild(el('p', 'mynote', n.note));
|
||||
});
|
||||
render(KEYS.fav, 'fav', 'No favorites yet. Tap a verse and choose Favorite.', function (box, f) {
|
||||
box.appendChild(refLink(f.ref));
|
||||
box.appendChild(el('div', 'text', f.text));
|
||||
});
|
||||
render(KEYS.defs, 'defs', 'No saved definitions. Tap a verse, choose Define words, and save one.', function (box, d) {
|
||||
box.appendChild(el('strong', '', d.word));
|
||||
if (d.ref) { box.appendChild(document.createTextNode(' from ')); box.appendChild(refLink(d.ref)); }
|
||||
(d.defs || []).forEach(function (t) { box.appendChild(el('p', '', t)); });
|
||||
});
|
||||
render(KEYS.xrefs, 'xrefs', 'No saved cross references. Tap a verse, choose Cross references, and save one.', function (box, x) {
|
||||
box.appendChild(refLink(x.ref));
|
||||
box.appendChild(el('span', 'note', ' (cross reference of ' + x.from + ')'));
|
||||
box.appendChild(el('div', 'text', x.text));
|
||||
});
|
||||
}
|
||||
draw();
|
||||
|
||||
document.getElementById('backup').addEventListener('click', function () {
|
||||
var data = { kjv_backup: 1, saved: new Date().toISOString() };
|
||||
Object.keys(KEYS).forEach(function (name) { data[name] = load(KEYS[name]); });
|
||||
var blob = new Blob([JSON.stringify(data, null, 1)], { type: 'application/json' });
|
||||
var link = document.createElement('a');
|
||||
link.href = URL.createObjectURL(blob);
|
||||
link.download = 'kjv-saved-' + new Date().toISOString().slice(0, 10) + '.json';
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
link.remove();
|
||||
setTimeout(function () { URL.revokeObjectURL(link.href); }, 1000);
|
||||
});
|
||||
|
||||
document.getElementById('restore').addEventListener('change', function (event) {
|
||||
var file = event.target.files[0];
|
||||
var status = document.getElementById('backup-status');
|
||||
if (!file) { return; }
|
||||
var reader = new FileReader();
|
||||
reader.onload = function () {
|
||||
try {
|
||||
var data = JSON.parse(reader.result);
|
||||
if (!data || data.kjv_backup !== 1) { throw new Error('not a backup'); }
|
||||
if (!confirm('Replace the notes and saved items in this browser with this backup?')) { return; }
|
||||
Object.keys(KEYS).forEach(function (name) {
|
||||
if (Array.isArray(data[name])) { localStorage.setItem(KEYS[name], JSON.stringify(data[name])); }
|
||||
});
|
||||
draw();
|
||||
status.textContent = 'Backup restored.';
|
||||
} catch (e) {
|
||||
status.textContent = 'That file is not a valid backup.';
|
||||
}
|
||||
};
|
||||
reader.readAsText(file);
|
||||
event.target.value = '';
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in new issue
Block a user