I finally broke some ground on a project I've been working on for a few weeks. Instead of harassing the homie Derek with baby brain web developer questions, I'm leveraging ChatGPT 4 as a personal coding assistant. If you want to learn how to program, 2023 is a great time to do it.

I've been using Ubooquity for a while to access my home library in Aguadilla from abroad. Ubooquity has great features like letting you create individual users, but I got feedback that there were too many clicks involved in navigating my collection. For this reason, in addition to Ubooquity not being open source, I decided to find an open source epub reader to develop on top of so that I could create a better UI that allows me to choose an epub out of my existing collection from the same screen vs the page hopping I currently have to endure when selecting a book with Ubooquity.

After watching a few tutorials, I decided to implement an active search with htmx (shoutout to Will Tejeda). htmx is a steezy frontend library that enables me to create lightweight UX patterns by empowering existing HTML elements with new attributes that can trigger any kind of HTTP request to my backend to create intuitive flows on my web page.

Front end

Below is a simple input tag that will send the users search query to the back end sitting on top of a div block where the results of the active search will populate 300ms after a key on the keyboard is let up


<input type="text"
hx-get="/search"
hx-params="searchQuery:val"
hx-trigger="keyup delay:300ms"
hx-target="#resultsContainer"
placeholder="Search for EPUBs...">
<div id="resultsContainer"></div>

The browser renders the above HTML as a simple but powerful search bar (seen below).

Back End

Below is the end of my web app, created with flask and the help of Chat GPT. Each section of code beginning with @app.route are handlers that direct how resources from my server are served, and "EPUBS_DIR" is the location of my ebooks on my server.

from flask import Flask, render_template, request, send_from_directory
import os
app = Flask(__name__)

EPUBS_DIR = './reader/bookshelf'


@app.route('/')
def index():
return render_template('index.html')


@app.route('/search', methods=['GET'])
def search():
query = request.args.get('searchQuery', '').lower()
all_files = [f for f in os.listdir(EPUBS_DIR) if f.lower().endswith('.epub')]
matching_files = [f for f in all_files if query in f.lower()]

# Render the results as HTML
results_html = "".join([f'<li><a href="/reader?book=/reader/bookshelf/{file}" target="_blank">{file}</a></li>' for file in matching_files])
return results_html


@app.route('/reader/bibi/<path:subpath>')
def serve_bibi_assets(subpath):
# This will serve all of Bibi's assets, such as JavaScript, CSS, etc.
return send_from_directory('./reader/bibi', subpath)




@app.route('/reader/bookshelf/<filename>')
def serve_epub(filename):
return send_from_directory('./reader/bookshelf', filename)




@app.route('/reader')
def serve_bibi_reader():
book_path = request.args.get('book')
# You might want to do some checks on `book_path` for security reasons.
return send_from_directory('./reader/bibi', 'index.html')





if __name__ == '__main__':
app.run(debug=True)


At its core, the /search handler serves as the bridge between user queries and my repository of EPUBs, swiftly surfacing relevant results underneath the search box for the user to access via click. The handler listens for user input, processes it in real-time, and interacts with the backend to fetch matching EPUBs. Once a user selects a title from the results, a seamless transition takes them into an open source epub reader that renders the users selection in the browser. Below is a screenshot of it rendering an interactive epub. When you click on the "check" button on the left side to check your answers, a dialogue box appears to tell you if you were right or wrong. When you click on "english" on the right, it toggles the english translation of the passage.

PROGRESS!


I was really stressed out about things I couldn't control today, but this breakthrough makes up for it. Here's to more late night code sessions and my continued growth.