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.
Refactor the chat input area in web.html to use the new ChatInputElement custom component instead of inline HTML. Move autocomplete handler from a function-based getInputField() to directly attaching to the chat-input element's autoCompletions property. Remove the legacy initInputField function and its associated keydown/input event listeners, consolidating all input behavior into the component. Add the chat-input.js module script to app.html to ensure the custom element is registered globally.
Extract the online users dialog from `online.html` into a dedicated `dialog_online.html` template, create a new `dialog_help.html` template with a custom `help-command-list` web component, and add global `<dialog>` styling in `base.css` with centered positioning, backdrop blur, and fade/scale animations. Update `web.html` to include both dialog templates, wire the `/help` autocomplete command to `showHelp()`, and rename the `/online` handler from `showOnlineUsers()` to `showOnline()`.
- Add `get_seconds_since_last_update` method to `ChannelMessageModel` for age checking
- Reject edits on messages older than 3 seconds in `updateMessageText` RPC handler
- Broadcast full message record (including html) instead of just text in socket event
- Update frontend `updateMessageText` to parse and set innerHTML from received html field
- Fix CSS selector for chat message images to use descendant combinator
- Add 3-second auto-clear timeout for liveType input field
- Prevent liveType submission on trailing newline or space
- Hide empty messages in channel-message event by setting display none
Implement MessageList custom element with real-time message text updates and typing glow animation, add UserList component with relative time formatting, introduce LoadBalancer class for backend process management, create DatasetWebSocketView for key-value sync over WebSocket, override save method in ChannelMessageService to render HTML templates, and return channel_message object from ChatService instead of boolean.
Implement a full-screen image preview overlay triggered by clicking any <img> element inside the messages container. The overlay uses a fixed dark background (rgba(0,0,0,0.9)) and centers the image at up to 90% viewport dimensions. Clicking the overlay dismisses it by removing the element from the DOM.
Implement real-time typing indicator feature across frontend and backend. Add `set_typing` RPC method in `RPCView` that broadcasts typing events with user details to a fixed channel. Introduce `typeLock`, `typeListener`, and `set_typing` method in `App` class to periodically send typing status. Update `Socket` to emit custom events from incoming data. Add CSS `glow` keyframe animation and apply it to user avatars in `web.html` when `set_typing` event is received.
The focus call for the input field after sending a message is now wrapped in a setTimeout with a 500ms delay. This prevents the focus from being applied before the DOM has fully updated after the new message is rendered, ensuring the cursor reliably lands in the input field for the next keystroke.
When a message is sent via Enter key, the input field now automatically regains focus, allowing the user to continue typing without manually clicking back into the text area. This improves the upload and messaging flow by eliminating an extra interaction step.
Implement a keydown listener that tracks Escape key state and counts subsequent 'G' presses. When Escape is held and 'G' is pressed twice within 300ms, the view scrolls to the last message in the messages container. Also handle Shift+G to trigger layout update via existing updateLayout function.
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.
The diff removes the `<chat-window>` element that was hidden with `display:none` from the web.html template, likely because it is no longer needed or has been replaced by a different component.
Add responsive CSS media query for max-width 600px making header position sticky with block display for logo, and make chat-input footer sticky. Change chat-input container from div to footer element in web.html template for semantic HTML structure.
- Split header padding into individual properties (top, left, right) in base.css
- Reduce logo font size from 1.5em to 1.2em in base.css
- Hide static "Snek" logo div with inline display:none in app.html
- Add new logo div with block header_text template block in app.html
- Move chat header h2 from web.html into the new header_text block in app.html
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.
The setInterval call for the updateTimes function was changed from 1 second to 30 seconds,
reducing the frequency of time display updates in the web interface to lower client-side
processing overhead and network requests.
- Fix ChannelMemberModel.get_name() to check channel tag instead of uid for dm detection
- Add fallback to channel member label when channel name is empty
- Handle self-dm edge case in ChannelMemberService.get_dm() with null join
- Update threads template to use channel name and color instead of last message user data
- Add channel membership verification in WebView to return 404 for non-members
- Pass resolved channel member name to web template instead of raw channel label
Refactor NotificationAudio.sounds from a positional array to a named object mapping event types ("message", "mention", "messageOtherChannel", "ping") to distinct audio files. Add three new notification WAV assets for mention, other-channel, and ping sounds. Update the channel-message event handler in web.html to use named sound keys, and introduce extractMentions() and isMentionForSomeoneElse() helpers to differentiate messages mentioning the current user from those mentioning other users, playing the appropriate notification sound for each case.
Add a new `isMention` function that checks if a chat message contains the current user's username, and play the new `mention1.wav` sound (index 1) when a mention is detected instead of the default beep. Also remove redundant dataset attributes from the message element creation in the channel-message event handler.
Changed the pagination offset parameter from 1 to 0 in the getMessages RPC call, allowing the chat to load messages that precede the current first message rather than skipping it. This enables seamless infinite scroll behavior when the user scrolls up in the message history.
The `isScrolledPastHalf()` check was previously commented out, causing the chat to always load extra messages regardless of scroll position. This change re-enables the guard and adjusts the threshold from 1/4 to 1/2 of the scrollable height, ensuring new messages are only fetched when the user has scrolled past the midpoint of the current content.
The conditional check `isScrolledPastHalf()` was commented out, removing the requirement that the user must scroll past the halfway point before extra content loads. Now `isLoadingExtra` is set to `true` immediately when the scroll handler fires, allowing infinite scroll to trigger on any scroll event rather than only after passing the 50% threshold.
The `isScrolledPastHalf` function previously always returned true, causing continuous loading. Now it returns true only when scroll position is within the top quarter of the container, and the `loadExtra` guard condition is negated to trigger loading only when scrolled near the top.
The `shouldAutoScroll` function previously returned false when the user scrolled past the top 20% of the scrollable area, preventing automatic scroll-to-bottom behavior. By commenting out this conditional block, the function now always returns true, allowing the chat view to continuously auto-scroll to new messages regardless of the user's current scroll position. This enables an infinite-scroll-like behavior where the viewport follows incoming content without interruption.
The auto-scroll behavior in the chat messages container now triggers at a much lower scroll position (20% from bottom instead of 80%), allowing users to stay near the bottom of the conversation without being forcibly scrolled down. This enables a more natural infinite scroll experience where new messages only auto-scroll when the user is very close to the bottom of the chat history.
The condition for preventing auto-scroll on new messages was changed from
checking if the user is within the top fifth of the scrollable area to
checking if they are beyond the bottom 80% (i.e., scrolled past 83.3%).
This effectively inverts the logic so that auto-scroll is suppressed only
when the user has scrolled significantly upward, rather than when they are
near the top. The change ensures the chat view remains locked to the latest
message unless the user deliberately scrolls far up.
The scroll detection logic in the chat messages container now triggers auto-scrolling
when the user is within the top 20% (1/5) of the remaining scrollable area, instead of
the previous 25% (1/4) threshold. This reduces premature scroll jumps during message
streaming, allowing users to read older content with less interruption.
The change modifies the condition in `isNearBottom()` within `web.html` to trigger auto-scroll when the user is within the top quarter of the remaining scrollable area instead of the top half, making the scroll behavior more responsive to new messages appearing at the bottom.