Reorder the fallback chain for Open Graph image and video extraction in
`src/snek/system/template.py` to check `secure_url` before `url` and `image`/`video`,
ensuring HTTPS resources are preferred when available for embedded player support.
Extend the allowed path prefixes in embed_youtube to include "/shorts",
enabling embedding of YouTube Shorts videos alongside regular watch and
embed URLs.
The title attribute was removed from the SAFE_ATTRIBUTES dictionary
in src/snek/system/template.py, likely to tighten security by
preventing arbitrary title attributes in sanitized HTML output.
Also fix string interpolation syntax in embed_url function where double quotes were incorrectly nested inside f-string, changing outer quotes from double to single to resolve syntax error.
Extract repeated meta-tag lookup logic into reusable get_element_options function that accepts fallback priority chain (twitter, og, meta, elem). Enhance embed_url to skip already-processed URLs via attachment key check, and extend embed payload with optional height/width attributes from og:image:width/height or twitter:image:width/height. Refine site name selection to prefer og:site_name over twitter:site, falling back to title tag.
Extend the embed_youtube function's hostname whitelist to include "music.youtube.com" for YouTube Music URL support. Also update embed_url to skip anchor elements that have a "data-noembed" attribute, preventing unwanted embedding of explicitly excluded links.
Add CSS rules for `.message-content .spoiler` to hide child content via opacity, pointer-events, and visibility, and reveal it on hover/focus/active with a 0.3s opacity transition. Include a `delay-pointer-events` keyframe animation to prevent premature interaction during the reveal. Set spoiler container to a fixed 2.5rem height with overflow hidden, expanding on interaction.
Add a guard condition in the notification creation flow to compare
channel_member["user_uid"] against user["uid"] before calling
notify_user, preventing the sender from receiving a push notification
for their own message.
Remove unused `memberships` and `user` variables, eliminate debug print statements for headers, and migrate the HTTP POST call from the blocking `requests` library to an `aiohttp.ClientSession` with async context managers. Adjust response attribute access from `.status_code` to `.status` to match aiohttp's API.
Add a PEM-encoded X.509 certificate (cert.pem) and its corresponding RSA private key (key.pem) to enable HTTPS for debug and testing environments. The certificate is self-signed with a 1-year validity period, issued to "Internet Widgets Pty Ltd" in the Netherlands, and includes basic constraints for CA usage.
- Introduce PushMapper and PushRegistrationModel for storing user push subscription data (endpoint, key_auth, key_p256dh)
- Implement PushService replacing old Notifications class, with async notify_user method sending WebPush payloads via aiohttp
- Register PushService in service registry and PushMapper in mapper registry
- Update NotificationService to trigger push notifications on new channel messages
- Enable SSL context in main app runner loading cert.pem and key.pem
- Remove deprecated get_notifications calls and old notification module
- Refactor PushView to use new PushService for key retrieval and subscription registration
- Add await navigator.serviceWorker.ready in push.js to ensure service worker readiness before fetching public key
Clean up the import block in src/snek/system/template.py by removing
the unused `asyncio` and `aiohttp` modules, and reordering remaining
imports to follow standard grouping conventions (stdlib, third-party,
local).
Replace flexbox body layout with a CSS grid using named template areas (header, sidebar, chat-area) to improve mobile responsiveness and sidebar positioning. Update viewport meta tag to include `interactive-widget=resizes-content` for better mobile keyboard handling. Add `html { height: 100% }` and `.chat-messages { display: flex; flex-direction: column }` to support proper vertical sizing. Move `<main>` element after sidebar block in app.html template to match new grid structure. Change umami analytics script from `defer` to `async defer` in base.html.
The fix reorders operations in the submit handler to clear the input field value before calling `sendMessage`, and moves the `messageUid` assignment into the live-typing path only, eliminating the double-send that occurred when both the submit handler and the live-typing timer triggered message transmission.
Add logic to detect time gaps between consecutive messages from the same user. When the difference between two messages' timestamps exceeds 20 minutes, the later message receives a `long-time` CSS class, causing its timestamp to be displayed persistently alongside the existing `switch-user` rule. The CSS selector `.message:has(+ .message.long-time)` ensures the preceding message's time remains visible when followed by a long-delayed reply.
Add max-height:0 with overflow:hidden to .avatar in collapsed state, and set
max-height:unset on hover to prevent layout shift. Replace .time display:none
with conditional opacity:0 when message is not hovered, focused, or active,
keeping .author display:none unchanged.
- Append `&format=webp` to the image source URL in `enrich_image_rendering` function
- Restore webp format query parameter that was previously removed, enabling browser fallback to webp when supported
- Replace fragile string splitting with urlparse/parse_qs for robust YouTube URL parsing
- Support youtu.be, youtube-nocookie.com, and /embed paths alongside /watch
- Properly extract start time (t=) parameter and append to embed src
- Add early 404 return in ChannelAttachmentView when channel_attachment is None
- Set Content-Type header from original format in attachment download response
Implement clipboard paste event listener in initInputField that reads image files from clipboard and triggers upload via the upload-button component. Add drag-and-drop file drop support to the textarea input field for improved user experience.
Refactor the subscription body validation in `src/snek/view/push.py` to use `all()` with a list comprehension instead of a multi-line `and` chain, improving code clarity. Also normalize string quotes from single to double for `body["endpoint"]` and reformat the `requests.post` call to span multiple lines for consistency with project style.
Refactor `create_encrypted_payload` to accept endpoint parameter and return a dictionary containing pre-built HTTP headers and encrypted payload data, eliminating manual header construction in the PushView caller. Remove unused `public_key_base64` certificate retrieval and inline header generation logic.
Replace commented-out push handler with a full async `registerServiceWorker` function that fetches the VAPID public key, subscribes the push manager, and POSTs the subscription object to `/push.json`. Remove unused helper functions (`arrayBufferToBase64`, `requestNotificationPermission`, `subscribeUser`) and top-level fetch calls. Convert notification key file paths from string constants to `pathlib.Path` objects, replacing `os.path.isfile` checks with `.exists()` and `open`/`write` calls with `.read_bytes()`/`.write_bytes()`. Update the notification test payload to use relative icon and URL paths. Fix the app.html onclick handler to call the renamed `registerNotificationsServiceWorker()` function.
- Extend service-worker push event to parse and display notification body, icon, and title from push data payload
- Add AESGCM and HKDF imports to notification module for future encryption support
- Refactor notification key file path constants to use double quotes for consistency
- Update push view to include notification body in subscription response and validate request fields
- Export registerServiceWorker function in push.js and add debug logging for key data
- Add notificationclose event listener and fetch cache-first strategy in service-worker.js
- Add notification button to app.html template calling registerServiceWorker
- Fix public key encoding from base64 to urlsafe_b64encode in push.py for chrome compatibility
The manifest.json previously referenced a single icon file (snek1.png) for both 192x192 and 512x512 sizes, causing browser instability warnings due to mismatched dimensions. This commit adds properly sized snek192.png and snek512.png images and updates the manifest to point each icon entry to its corresponding size-specific asset, resolving the Firefox Android manifest validation issue.
The polyfill provides a static method on Promise that returns an object with resolve and reject functions exposed, enabling external resolution of a promise. It is loaded as a module script in both app.html and base.html to ensure compatibility before app.js runs.
Introduce initial push notification support by adding a new `notification.py` module for VAPID key pair generation (EC P-256), a `PushView` endpoint serving the public key, and a `push.js` client that fetches the key and subscribes the service worker with `userVisibleOnly` flag. The service worker push listener is refactored to check notification permission and handle click events. The `app.html` template now includes the push script as a module, and the application startup calls `get_notifications()` to ensure keys exist. Note: FCM-based push on Chrome/Opera remains non-functional due to unresolved compatibility issues.
Convert all JavaScript classes in app.js and schedule.js from implicit globals to ES module exports, add import statements in template scripts for the app instance, and mark all script tags with type="module" to enable module scoping. Also fix a potential null reference in web.html by using optional chaining on lastMessage.scrollIntoView and hoist the lastMessage variable to outer scope for proper closure behavior.
Create a new shared.css file containing consistent focus ring and placeholder opacity transition rules for input and textarea elements. Import shared.css into both base.css and style.css to apply the updated input styling across all pages, replacing previously scattered or missing focus indicators.
Add `preloaded-structure` attribute to `<generic-form>` elements in both login and register templates, passing the serialized form JSON from the server. This enables instant form rendering without an additional fetch. Also implement autofocus logic in `generic-form.js` to focus the first input field on page load, improving UX for authentication flows.
Add a new CSS file `back-form.css` defining a two-column grid layout for the back button and generic form, with the button positioned at (1,1) with 30px margins and z-index 1. Update both `login.html` and `register.html` to include the stylesheet in the head block and wrap the existing `fancy-button` and `generic-form` elements in a `div.back-form` container, replacing the previous standalone button placement.
Insert an empty Jinja2 block named 'head' inside the <head> element of base.html, enabling child templates to inject custom meta tags, styles, or scripts before the closing head tag.
Add six new meta elements to the HTML head in base.html: theme-color set to black,
application-name and description both set to "Snek chat by Molodetz", author set to
"Molodetz", keywords including "snek, chat, molodetz", and color-scheme set to dark.
Also insert a blank line after the title block for improved readability.
Set a default "Snek chat by Molodetz" title in base.html and override it with
"Login - Snek chat by Molodetz" and "Register - Snek chat by Molodetz" in their
respective templates, improving user-facing page identification.