Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/apursley2012/PunctuOwlity/llms.txt

Use this file to discover all available pages before exploring further.

PunctuOwlity revolves around its event grid, where each upcoming item appears as a compact card showing the weekday abbreviation, day number, event title, scheduled time, and an alarm indicator. You can add as many events as you need, jump back to edit any of them, or remove them with a single tap on the delete icon. The add and edit flow shares one form — the distinction is determined by whether an event ID is present in the URL (web) or the Activity Intent (Android).

Event Form Fields

Open the Add/Edit An Event screen via the floating action button (+) on the events grid, or tap the edit icon on any existing card. The same page handles both creating and updating.
eventTitle
string
required
A short label for the event, e.g. "Dentist" or "Jessica's Wedding". The title is also used for auto-category detection (see below) and for case-insensitive search matching on the events screen.
eventDate
date
required
The calendar date of the event, entered via a native date picker. Stored internally as YYYY-MM-DD (e.g. 2025-04-14).
eventTime
time
required
The time of day, entered via a native time picker in HH:MM 24-hour format. Displayed on the card in 12-hour format with AM/PM (e.g. 01:30PM).
eventAlert
boolean
A checkbox labelled “Would you like to be reminded of this event?” When enabled, the web app fires a browser notification on the event day (provided Notification.permission is 'granted'). On Android, SMS permission is requested on app launch regardless of this field. Defaults to false.
If any of the three required fields — title, date, or time — are left blank when the form is submitted, the app shows the toast “All fields are required” and does not save.

How Dates and Times Are Stored and Displayed

PunctuOwlity normalises every event through the normalizeEvent function before saving or rendering, ensuring a consistent display format regardless of how the raw value was entered.
ValueStorage formatDisplay format
DateYYYY-MM-DD (e.g. 2025-04-14)Three-letter weekday (MON) + zero-padded day number (14)
TimeHH:MM 24-hour (e.g. 13:30)12-hour with AM/PM, zero-padded hour (e.g. 01:30PM)
All-day event (no time)Empty rawTimeALL DAY
// Time normalisation in normalizeEvent (app.js)
if (/^\d{1,2}:\d{2}$/.test(rawTime)) {
  const [hours, minutes] = rawTime.split(':').map(Number);
  const suffix = hours >= 12 ? 'PM' : 'AM';
  const hour   = hours % 12 || 12;
  displayTime  = `${String(hour).padStart(2, '0')}:${String(minutes).padStart(2, '0')}${suffix}`;
}
The weekday abbreviation is derived from Date.toLocaleDateString('en-US', { weekday: 'short' }) called on the parsed YYYY-MM-DD date, then uppercased (e.g. 'Sat''SAT').

Auto-Category Detection

When a new event is saved without an explicitly chosen category, PunctuOwlity inspects the title and assigns one of the built-in categories automatically. This keeps the category tabs useful without requiring manual selection for common event types.
Title pattern (case-insensitive)Assigned category
Contains birthdaybirthday
Matches /(appointment|dentist|dental|doctor)/appointment
Matches /(trip|travel|vacation)/trip
No matchgeneral
// Auto-category logic in normalizeEvent (app.js)
let category = event.category || 'general';
if (!event.category && lowerTitle.includes('birthday'))
  category = 'birthday';
else if (!event.category && /(appointment|dentist|dental|doctor)/.test(lowerTitle))
  category = 'appointment';
else if (!event.category && /(trip|travel|vacation)/.test(lowerTitle))
  category = 'trip';

Event Card Layout

Each card in the grid displays the following elements:

Header row

Three-letter weekday abbreviation on the left (e.g. MON) and an alarm icon on the right — teal when the alert is active, red when it is off.

Action row

Edit button (pencil icon) and Delete button (trash icon) side by side.

Date

Zero-padded day number in large text (e.g. 14).

Title & time

Event title on one line and the formatted time (or ALL DAY) below it.

Editing an Event

Tapping the edit button on a card navigates to add-event.html with the event’s ID appended as a query parameter:
add-event.html?id=<eventId>
On load, app.js reads ?id from URLSearchParams and, if a matching event is found in localStorage, pre-fills all four form fields with the existing values. Submitting the form overwrites the matching entry in the event array (matched by id) and shows the toast “Event Updated” before redirecting back to the events grid. On Android, the same flow passes the integer event_id through an Intent extra:
Intent intent = new Intent(MainActivity.this, AddEventActivity.class);
intent.putExtra("event_id", event.getId());
startActivity(intent);
AddEventActivity then calls db.getEventById(eventId) and populates the fields via setText.

Deleting an Event

Click or tap the delete icon on any event card. The app filters the card’s data-id out of the stored events array, saves the updated list back to localStorage, re-renders the grid, and shows the toast “Event Deleted”.
// Delete handler (app.js)
if (e.target.closest('.delete')) {
  saveEvents(getEvents().filter(x => x.id !== card.dataset.id));
  render();
  toast('Event Deleted');
}
On Android, the equivalent call is db.deleteEvent(event.getId()) followed by a full reload of the grid via loadEvents().

Web vs Android Comparison

Events are persisted as a JSON array in localStorage under the key punctuowlity-events. The full event object stored for each item is:
{
  id:       "1713820800000", // Date.now() string
  day:      "MON",
  date:     "14",
  fullDate: "2025-04-14",
  rawTime:  "13:30",
  time:     "01:30PM",
  title:    "Dentist",
  category: "appointment",
  alert:    false
}
The normalizeEvent helper re-derives day, date, and time from fullDate and rawTime on every read, so display values stay consistent even if the underlying data was entered in an older format.
On first load, if no events are found in localStorage, the web app automatically seeds the grid with four sample events — a project deadline, a birthday, a dentist appointment, and a wedding — so you can explore the interface straight away without entering any data.

Build docs developers (and LLMs) love