diff --git a/.claude/settings.json b/.claude/settings.json
index 480c385..ee58420 100644
--- a/.claude/settings.json
+++ b/.claude/settings.json
@@ -5,7 +5,6 @@
"Read(./secrets/**)",
"Read(./config/credentials.json)",
"Read(./build)",
- "Read(./**.xml)",
"Read(./embeddings**)"
]
}
diff --git a/.env.example b/.env.example
index 7468339..a81adb0 100644
--- a/.env.example
+++ b/.env.example
@@ -1,5 +1,19 @@
DATABASE_URL=example.db
+PUBLIC_SITE_URL=https://bibdle.com
+
+# nodemailer
+SMTP_USERNAME=email@example.com
+SMTP_TOKEN=TOKEN
+SMTP_SERVER=smtp.example.com
+SMTP_PORT=port
+# note from mail provider: Enable TLS or SSL on the external service if it is supported.
+
+# sign in with Discord
+
+# sign in with google
+
+# sign in with apple
AUTH_SECRET=your-random-secret-here
APPLE_ID=com.yourcompany.yourapp.client
APPLE_TEAM_ID=your-team-id
diff --git a/.gitignore b/.gitignore
index c6b584a..6415f92 100644
--- a/.gitignore
+++ b/.gitignore
@@ -28,4 +28,5 @@ vite.config.ts.timestamp-*
llms-*
embeddings*
-*.xml
\ No newline at end of file
+*bible.xml
+engwebu_usfx.xml
diff --git a/EnglishNKJBible.xml b/EnglishNKJBible.xml
index 472ce20..90b784b 100644
--- a/EnglishNKJBible.xml
+++ b/EnglishNKJBible.xml
@@ -24143,7 +24143,7 @@
Gather the people, Sanctify the congregation, Assemble the elders, Gather the children and nursing babes; Let the bridegroom go out from his chamber, And the bride from her dressing room.Let the priests, who minister to the Lord, Weep between the porch and the altar; Let them say, “Spare Your people, O Lord, And do not give Your heritage to reproach, That the nations should rule over them. Why should they say among the peoples, ‘Where is their God?’ ”Then the Lord will be zealous for His land, And pity His people.
- The Lord will answer and say to His people, “Behold, I will send you grain and new wine and oil, And you will be satisfied by them; I will no longer make you a reproach among the nations.
+ The Lord will answer and say to His people, “Behold, I will send you grain and new wine and oil, And you will be satisfied by them; I will no longer make you a reproach among the nations.”“But I will remove far from you the northern army, And will drive him away into a barren and desolate land, With his face toward the eastern sea And his back toward the western sea; His stench will come up, And his foul odor will rise, Because he has done monstrous things.”Fear not, O land; Be glad and rejoice, For the Lord has done marvelous things!Do not be afraid, you beasts of the field; For the open pastures are springing up, And the tree bears its fruit; The fig tree and the vine yield their strength.
@@ -33616,4 +33616,4 @@
-
\ No newline at end of file
+
diff --git a/analyze_top_users.sh b/analyze_top_users.sh
new file mode 100755
index 0000000..1f33b81
--- /dev/null
+++ b/analyze_top_users.sh
@@ -0,0 +1,31 @@
+#!/bin/bash
+
+# analyze_top_users.sh
+# Analyzes the daily_completions table to find the top 10 anonymous IDs by completion count
+
+# Set database path from argument or default to dev.db
+DB_PATH="${1:-dev.db}"
+
+# Check if database file exists
+if [ ! -f "$DB_PATH" ]; then
+ echo "Error: Database file not found: $DB_PATH"
+ echo "Usage: $0 [database_path]"
+ exit 1
+fi
+
+# Run the analysis query
+sqlite3 "$DB_PATH" <
- import { onMount } from "svelte";
+ import { onMount } from "svelte";
- interface ImposterData {
- verses: string[];
- refs: string[];
- imposterIndex: number;
- }
+ interface ImposterData {
+ verses: string[];
+ refs: string[];
+ imposterIndex: number;
+ }
- let data: ImposterData | null = null;
- let clicked: boolean[] = [];
- let gameOver = false;
- let loading = true;
- let error: string | null = null;
+ let data: ImposterData | null = null;
+ let clicked: boolean[] = [];
+ let gameOver = false;
+ let loading = true;
+ let error: string | null = null;
- async function loadGame() {
- try {
- const res = await fetch("/api/imposter");
- if (!res.ok) {
- throw new Error(`HTTP ${res.status}: ${res.statusText}`);
- }
- data = (await res.json()) as ImposterData;
- clicked = new Array(data.verses.length).fill(false);
- gameOver = false;
- } catch (e) {
- error = e instanceof Error ? e.message : "Unknown error";
- } finally {
- loading = false;
- }
- }
+ async function loadGame() {
+ try {
+ const res = await fetch("/api/imposter");
+ if (!res.ok) {
+ throw new Error(`HTTP ${res.status}: ${res.statusText}`);
+ }
+ data = (await res.json()) as ImposterData;
+ clicked = new Array(data.verses.length).fill(false);
+ gameOver = false;
+ } catch (e) {
+ error = e instanceof Error ? e.message : "Unknown error";
+ } finally {
+ loading = false;
+ }
+ }
- function handleClick(index: number) {
- if (gameOver || !data || clicked[index]) return;
- clicked[index] = true;
- if (index !== data.imposterIndex) {
- clicked[data.imposterIndex] = true;
- }
- gameOver = true;
- }
+ function handleClick(index: number) {
+ if (gameOver || !data || clicked[index]) return;
+ clicked[index] = true;
+ if (index !== data.imposterIndex) {
+ clicked[data.imposterIndex] = true;
+ }
+ gameOver = true;
+ }
- function newGame() {
- loading = true;
- error = null;
- data = null;
- loadGame();
- }
+ function newGame() {
+ loading = true;
+ error = null;
+ data = null;
+ loadGame();
+ }
- onMount(loadGame);
+ onMount(loadGame);
- function formatVerse(verse: string): string {
- let formatted = verse;
+ function formatVerse(verse: string): string {
+ let formatted = verse;
- // Handle unbalanced opening/closing punctuation
- const pairs: [string, string][] = [
- ["(", ")"],
- ["[", "]"],
- ["{", "}"],
- ['"', '"'],
- ["'", "'"],
- ["\u201C", "\u201D"], // \u201C
- ["\u2018", "\u2019"], // \u2018
- ];
- for (const [open, close] of pairs) {
- if (formatted.startsWith(open) && !formatted.includes(close)) {
- formatted += "..." + close;
- break;
- }
- }
- for (const [open, close] of pairs) {
- if (formatted.endsWith(close) && !formatted.includes(open)) {
- formatted = open + "..." + formatted;
- break;
- }
- }
+ // Handle unbalanced opening/closing punctuation
+ const pairs: [string, string][] = [
+ ["(", ")"],
+ ["[", "]"],
+ ["{", "}"],
+ ['"', '"'],
+ ["'", "'"],
+ ["\u201C", "\u201D"], // \u201C
+ ["\u2018", "\u2019"], // \u2018
+ ];
+ for (const [open, close] of pairs) {
+ if (formatted.startsWith(open) && !formatted.includes(close)) {
+ formatted += "..." + close;
+ break;
+ }
+ }
+ for (const [open, close] of pairs) {
+ if (formatted.endsWith(close) && !formatted.includes(open)) {
+ formatted = open + "..." + formatted;
+ break;
+ }
+ }
- if (/^[a-z]/.test(formatted)) {
- formatted = "..." + formatted;
- }
- formatted = formatted.replace(/[,:;-—]$/, "...");
- return formatted;
- }
+ if (/^[a-z]/.test(formatted)) {
+ formatted = "..." + formatted;
+ }
+ // Replace trailing punctuation with ellipsis
+ // Preserve closing quotes/brackets that may have been added
+ formatted = formatted.replace(
+ /[,:;-—]([)\]}\"\'\u201D\u2019]*)$/,
+ "...$1",
+ );
+ return formatted;
+ }