- Removed trailing semicolons from arrow function bodies in hiddenCompletions object
- Fixed inconsistent indentation for _leetSpeak toggle and textarea classList assignment
- Replaced spread operator with Object.assign for allAutoCompletions getter
- Truncated extractMentions regex pattern at diff boundary
Introduce a new `NewView` class in `src/snek/view/new.py` that renders the `new.html` template, requiring authentication. The template includes a custom `ChatWindow` web component that establishes a WebSocket connection via the `Socket` class, handles channel-based messaging with real-time updates, and supports sending messages on Enter key press while allowing Shift+Enter for multi-line input.
- Register NewView at /new.html endpoint in Application router
- Fix textToLeetAdvanced to require s.length > 1 for valid leet match
- Comment out event.waitUntil(reg) in service-worker push handler to prevent potential hang
- Added console.info call to log event name and data payload in the data handler
- Changed reconnect timeout from 0ms to 4000ms to introduce deliberate delay before reconnection attempt
The change reduces the default `max_workers` for `ProcessPoolExecutor` from 5 to 1 in the `get_or_executor` method of `ChannelMessageService`, and adds a debug print statement to log the number of available executors.
The change modifies the conditional logic in `MessageList.upsertMessage()` to only remove and nullify an existing message when `data.message` is falsy, instead of doing so when `data.is_final` is true or `data.message` is falsy. This ensures that non-final messages with content are not incorrectly removed from the DOM before being updated or re-inserted.
The change limits concurrent thread execution per user to a single worker,
preventing resource exhaustion from unbounded thread creation in the
get_or_create_executor method.
Remove the module-level `executor` variable and introduce instance-level `_executor_pools` dict with `get_or_create_executor` method that creates a `ThreadPoolExecutor(max_workers=5)` per user UID. Update all `run_in_executor` calls in `render_message` and `render_message_edit` to use the per-user executor instead of the shared global one, improving isolation and resource control across concurrent user requests.
The loop iterating over avatar elements now only assigns the first matching anchor to `lastElement`, ensuring the scroll target remains the earliest occurrence instead of being overwritten by subsequent matches.
Add a debug print statement in the `send` method's exception handler to log the exception message before deleting the socket, and update the existing debug print in the message processing loop to include a distinctive prefix for easier log filtering.
The method definition was incorrectly placed at module level instead of being properly indented as a member of the SocketService class. This fix moves the async def user_availability_service declaration one level deeper to align with the class body, restoring correct Python scoping and preventing AttributeError or runtime import failures when the service attempts to register or invoke this method.
Add detailed debug-level log statements throughout the
user_availability_service method to trace execution flow, including
entry into the main loop, socket iteration, connection status checks,
user update decisions, database save operations, and sleep intervals.
Replace the basic contenteditable editor with a full modal editor supporting insert, normal, and visual modes. Add a command-line interface at the bottom for executing commands like :w, :q, and :wq. Introduce mode-specific cursor styling, selection highlighting, and keyboard-driven navigation. The editor now tracks mode state, handles mode transitions via Escape and i/v keys, and displays the current mode in a status bar.
- Added sentry-sdk to project dependencies in pyproject.toml
- Imported and initialized sentry_sdk with DSN in main() function
- Added graceful fallback with print message if sentry_sdk import fails
- Imported `random` module to support randomized delay
- Inserted `await asyncio.sleep(random.uniform(0.1, 0.4))` before `await rpc(msg.json())` in the websocket message loop
- This introduces a jitter of 100-400ms to desynchronize concurrent RPC processing from multiple clients
The `endOfMessages.after(message)` call was placed after the `isScrolledToBottom()` check, causing the scroll position to be evaluated before the new message was inserted into the DOM. This resulted in incorrect scroll behavior when messages were added, as the scroll check would not account for the newly inserted element. Moving the insertion before the scroll check ensures the scroll position is evaluated after the DOM has been updated with the new message.
The diff shows a fix in the `upsertMessage` method of the `MessageList` class in `src/snek/static/message-list.js`. The original condition `(message && data.is_final) || !data.message` was incorrectly grouping the logical OR, causing messages to be removed only when `data.is_final` was true alongside a message, or when `data.message` was falsy regardless of message existence. The fix wraps the entire condition in parentheses: `(message && (data.is_final || !data.message))`, ensuring that a message is removed only if it exists AND either `data.is_final` is true or `data.message` is falsy. A comment `// TO force insert` was added to clarify the intent of nullifying the message reference after removal.
Previously, the upsertMessage method removed any existing message element from the DOM whenever a message with the same uid was received, regardless of whether the update represented a final version. This caused premature removal of messages that were still being streamed or updated incrementally. Now the removal is gated on the `data.is_final` property, ensuring that only completed messages are replaced in the DOM while in-progress messages remain visible until their final state arrives.
Add sanitize_html calls in ChannelMessageService for both render and save paths to strip script, iframe, object, embed tags and event handler attributes from rendered HTML templates. Refactor sanitize_html in template.py to use BeautifulSoup for tag removal and attribute cleaning, replacing the previous bleach-only implementation with a new sanitize_html2 function retained for compatibility.
The whitelist_attributes filter was applied to the rendered HTML output in both
the get_by_uid and save methods of ChannelMessageService. This filter was
stripping allowed HTML attributes from the message content, which could break
formatting or functionality in rendered messages. The fix removes the filter
call, allowing the full rendered template output to be stored and returned
without attribute sanitization.
Refactor the maintenance method to iterate over channel messages using synchronous `self.mapper.db["channel_message"].find()` instead of the async `self.find()` generator, wrapping each message processing in a try-except block that catches exceptions, prints them, and sleeps for 0.1 seconds before continuing.
- Import and register stats_middleware in the application middleware stack
- Add prepare_stats startup hook to initialize stats data structure
- Register /stats.html GET route with stats_handler for viewing statistics
- Create new snek/system/stats.py module with time-series data collection and SVG chart generation
- Integrate websocket statistics tracking in RPC view by calling update_websocket_stats before and after method execution
The uploadResponses array is initialized in the component constructor to collect file upload results. The reset() call is moved inside the conditional block that fires when all uploads are complete, ensuring the component state is cleared only after the 'file-uploads-done' event has been published with the full response data. A console.info log is added for debugging the collected responses.
- Convert ForumModel.get_threads and ThreadModel.get_posts from returning lists to async generators yielding individual records
- Replace deprecated `_order_by` and `_offset` parameters with `order_by` string syntax in service queries
- Add `get_timestamp` helper and `save` method to BaseModel for direct persistence
- Refactor ForumView endpoints to use `services.forum.get` and `services.thread.get` instead of `find_one`
- Inject request reference into app context via `setattr(self, "request", request)` in view handlers
- Restructure forum HTML template to extend app layout with breadcrumb navigation and dark theme styling
- Add `generate_uid` method using uuid4 to BaseForumService
- Expose public `notify` method for external event dispatching
- Rename `websocket_handler` to `get` in ForumWebSocketView for proper routing
- Fix WebSocket ID generation to use forum service instance instead of generic services
Add complete forum feature including ForumApplication sub-app mounted on parent, new ForumModel/ThreadModel/PostModel/PostLikeModel entities, corresponding mappers and services, event notification dispatch system, and WebSocket endpoint for real-time updates. Refactor service registry to use dynamic registration pattern.
Implement three new WebDAV property routes (PROPGET, PROPSET, PROPDEL) alongside property file lifecycle management: delete properties on file removal and move properties on file rename via shutil.
Add `Cross-Origin-Opener-Policy: same-origin` and `Cross-Origin-Embedder-Policy: credentialless` headers to the `cors_middleware` response in `src/snek/system/middleware.py`, with a commented-out alternative for `require-corp` to support cross-origin isolation configuration.
- Import time_cache alongside existing time_cache_async from app.cache
- Apply @time_cache with 60-minute timeout to block_code method in MarkdownRenderer
- Fix minor whitespace formatting issues in markdown.py
The synchronous _execute call replaces the previous async pattern that used a semaphore
and run_in_executor, simplifying the execution path and removing unnecessary async overhead
for database operations that are already synchronous in nature.
The semaphore is now a class-level attribute shared across all instances, and the retry loop with exponential backoff is removed in favor of a single execution path that always acquires the semaphore. The commit call is conditionally performed only when use_semaphore is True, and the template's live-type attribute is flipped from "false" to "true" to enable live chat functionality.
- Renamed shadowed `clients` variable to `matchedClients` in `isClientOpen` function to avoid conflict with global `clients` API
- Changed push event listener to async and added `await` before `isClientOpen` call to properly handle the returned promise
The change modifies the simulateTypingWithEvents call in the STTButton class to pass a single space character instead of an empty string, ensuring the typing simulation triggers proper input event handling for interim speech-to-text results.
The diff modifies the speech-to-text button handler in `src/snek/static/stt.js` to change behavior when interim speech recognition results are received. Previously, the code replaced the input element's value with an empty string and then simulated typing events to insert the interim text. The new implementation sets the input element's value directly to the interim text and calls `document.querySelector('chat-input').sendMessage(interim)` to immediately send the recognized speech as a chat message, bypassing the typing simulation entirely. The old typing simulation logic is commented out.
The diff modifies two key behaviors in the speech-to-text button component. First, the typing simulation delay parameter is changed from 1 to 0 in the `simulateTypingWithEvents` call within the finalize path, eliminating artificial typing delay when inserting punctuated text. Second, the interim speech recognition handling is refactored: instead of displaying interim text in the input field, both contenteditable and regular input elements now clear the previous interim text by setting their value to an empty string, while the commented-out `simulateTypingWithEvents` call for interim text is uncommented and activated to handle interim updates through the typing simulation mechanism instead of direct value assignment.
Add a check for the `snek-speaker` element's enabled state before calling `speak()`. When the speaker is disabled, fall back to playing the message sound instead of attempting speech synthesis. This prevents silent failures and ensures consistent audio feedback based on user preference.
Introduce a new `snek-speaker` web component that wraps the Web Speech API. The element provides `speak()`, `toggle()`, `stop()`, and `enable()`/`disable()` methods, along with an `enabled` property. Voice selection defaults to the first male English voice found, with asynchronous voice loading handled via the `onvoiceschanged` event.
Add snek-speaker element to chat-input.js, attach click handler on ttsButton to enable speaker, and include tts.js module in app.html. In web.html, trigger snek-speaker.speak() on final channel messages to enable text-to-speech output for incoming messages.
Refactor simulateTypingWithEvents to return a Promise that resolves when typing completes, enabling callers to chain actions after the typing simulation finishes. Update the speech recognition result handler to await this promise and call finalizeMessage() on the chat-input element, replacing the previous triggerEvent('keyup', "Enter") approach. Also add line breaks after punctuation in the committed text before typing simulation.
Add `login_required = True` to ChannelDriveApiView, ChannelAttachmentView, ChannelAttachmentUploadView, ChannelView, ContainerView, DriveView, BareRepoNavigator, StatsView, StatusView, ThreadsView, UploadView, and UserView to enforce authentication on these endpoints. Set `login_required = False` on RegisterFormView to allow unauthenticated registration. Refactor `BaseService.get` to accept positional args for uid and only filter deleted_at when not explicitly provided. Remove the deprecated `DriveView222` class entirely.
The previous implementation incorrectly queried all images within the entire message div, causing duplicate or out-of-context image source concatenation. This change restricts the querySelectorAll to only images nested inside the `.text` element, ensuring each message's image sources are appended exactly once and only from the relevant content area.
Remove the '![Replied image]()' wrapper around image sources when constructing reply text in the web chat template, leaving only the raw URL to prevent broken markdown rendering in non-markdown contexts.
The speech-to-text component previously used `document.activeElement` to determine the target element for transcription output, which failed when no input was focused. Changed the `targetEl` getter to query for a `textarea` element instead, ensuring consistent target availability. Also updated the DOM update logic to call `.focus()` on the target and assign directly to `.value` instead of using `setAttribute("value", ...)`, enabling proper text insertion into the textarea.
- Create new STTButton web component in src/snek/static/stt.js with shadow DOM, speech recognition API integration, and pulsing visual feedback during listening state
- Add stt.js script import to app.html template to register the custom element globally
- Instantiate and append stt-button element inside ChatInputComponent constructor in chat-input.js
- Add CSS class "chat-input-textarea" to the textarea element for styling hooks
- Attach change event listener on textarea to sync value and trigger updateFromInput on manual edits
The update_message_text method in RPCView was refactored to remove the no_save context wrapper and streamline validation logic. The commented-out time-based restriction check was also removed, allowing message updates without the previous 8-second window limitation.
Add a unique index on the `uid` field in the `channel_message` collection to enforce document uniqueness at the database level. In the RPC message update handler, remove the 8-second time-limit check and instead validate that the message is not marked as final or deleted before allowing edits.
Add lazy index creation for `['is_final','user_uid','channel_uid']` and `['deleted_at']` fields in `ChannelMessageService.save()` method, guarded by a `_configured_indexes` flag to ensure indexes are created only once per service instance.
- Added `deleted_at=None` filter to `check_message` query in `send_message` to exclude soft-deleted messages from finalization queue
- Added `is_final=False` and `deleted_at=None` filters to `get_message` in `update_message` to prevent editing finalized or deleted messages
- Added early return with error response when message is not found after applying new filters
- Added conditional finalization logic: only queue finalization for non-final messages, cancel existing finalization task for already final messages
The previous implementation used a single `await asyncio.sleep(7)` which could not be interrupted
mid-wait when the finalize task was cancelled. The new approach loops 7 times with 1-second sleeps,
checking `self._finalize_task` after each second and returning early if the task reference is cleared,
plus catching `CancelledError` to handle explicit cancellation gracefully.
When message is empty after processing check messages, the handler now returns early
instead of proceeding to send an empty message through the chat service. This prevents
unnecessary API calls and potential errors from sending blank content.
- Comment out direct scrollTop assignment in scrollToBottom method and use scrollIntoView on the bottom anchor element instead
- Change display toggle from messageDiv to textElement in updateMessage to correctly hide empty text content while preserving message container layout
- Move display toggle from textElement to messageDiv in message-list.js to properly hide entire message container when text is empty
- Remove is_final=False filter in _finalize_message_task to allow retrieval of already finalized messages
- Add early return when message is already finalized to prevent duplicate finalization attempts
- Strip whitespace from incoming message in send_message to handle empty/whitespace-only inputs
- Remove unused variable assignment for is_final in send_message non-final branch
Remove the unused `expiryTimer` property and its related clearTimeout calls from ChatInputComponent. Fix the `liveType` boolean assignment by changing the comparison operator from `!==` to `==` so that the attribute value `"true"` correctly enables live typing behavior. Make `finalizeMessage` synchronous by removing the `await` keyword from the RPC call and the method declaration. Remove the early return guard for empty message values in `sendMessage` to allow empty strings to be sent. Add server-side logic for finalizing messages after a 7-second delay via a new `_finalize_message_task` coroutine, with cancellation support through `_queue_finalize_message`. Re-enable the previously commented-out logic in `send_message` that checks for an existing non-final message from the same user and updates it instead of creating a new one.
The href attribute of the "Sign Up" anchor element in the index.html template was changed from "/register" to "/register.html" to align with the static HTML routing convention used elsewhere on the site, ensuring consistent URL resolution for the registration page.
- Invert the live-type attribute check from `=== "true"` to `!== "true"` in ChatInputComponent
- Comment out the non-final message lookup logic in RPCView.send_message and force is_final to always be True
Previously, the `send_message` method always queried for an existing non-final
message to update, even when `is_final=True`. This caused unnecessary database
lookups and could incorrectly attempt to update a stale message when sending
a final version. The fix wraps the continuation check inside `if not is_final:`,
so the lookup and update path is only taken when the message is explicitly
marked as non-final.
The `send_message` method in `RPCView` had a local variable `message` that shadowed the `message` parameter, causing the parameter to be overwritten before being passed to `update_message_text`. Renamed the local variable to `check_message` to preserve the original parameter value for downstream use.
- Introduce EventBus class in njet.js with subscribe/publish methods for decoupled component communication
- Replace direct NjetComponent subscription with eventBus.subscribe in chat-input.js for file-uploading events
- Add file-uploads-done event handler that reconstructs message from uploaded files and sends via RPC
- Reset uploadResponses array in FileUploadGrid.reset() to track completed uploads
- Publish file-uploads-done event when all uploads finish, passing array of file metadata
- Send relative_url in WebSocket done message from ChannelAttachmentUploadView for client-side link construction
- Modify RPC send_message to check for existing draft message and update it instead of creating new one
Removed four debug print calls that were logging function arguments, table name, and
result during the database operation retry loop in the BaseMapper class. These prints
were left over from development and were cluttering the output during normal operation.
Add `is_final` field to channel messages with a maintenance loop that toggles messages between final and non-final states. Introduce `send_message` RPC endpoint for creating messages with configurable finality. Remove `_finalize_message_task` and `updated_at` override in chat service, simplifying message finalization logic. Add debug prints to mapper retry loop for database operations.
- In `src/snek/service/chat.py`, wrap non-final channel message saves inside `self.app.no_save()` to prevent persistence of intermediate streaming updates, while final messages save normally.
- In `src/snek/view/rpc.py`, change `session_get` to be async and require a default argument, removing the optional default and fixing the internal lookup to use direct dict access instead of `.get()`.
- Remove unused `datetime` and `time` imports, reorder `_finalize_task` initialization after `_scheduled` list.