Skip to content

feat(collab): shared links, chat images, and plugin fullscreen - #2274

Merged
mauriceboe merged 6 commits into
liketrek:devfrom
GodricTM:feat/collab-links-chat-images
Sep 8, 2026
Merged

feat(collab): shared links, chat images, and plugin fullscreen#2274
mauriceboe merged 6 commits into
liketrek:devfrom
GodricTM:feat/collab-links-chat-images

Conversation

@GodricTM

@GodricTM GodricTM commented Sep 6, 2026

Copy link
Copy Markdown

Summary

  • Collab Links — new Links tab in the Collab panel (add / pin / delete shared URLs), default-on like Notes/Polls, admin-toggleable.
  • i18ncollab.tabs.links and collab.links.* added in every locale so the UI shows "Links" / "Add link" instead of raw keys.
  • Chat images — attach up to 4 JPEG/PNG/GIF/WebP images in collab chat (multipart + Zod DTO).
  • Plugin fullscreen — plugin iframe gets allowFullScreen and allow="fullscreen" so Kanban and other plugins can go native fullscreen.

These are the host patches we have been running on a custom 4.2.1 image (previously trek-collab:3.5.5).

Test plan

  • node shared/scripts/i18n-parity.mjs — file + key parity OK
  • CollabPanel tests updated for the Links tab and links: false in feature-off combinations
  • Zod schemas for create/update link and image-only chat messages
  • CI on this PR
  • Manual: Collab Links tab shows Links and Add link; add/pin/delete a URL
  • Manual: Collab chat attach images
  • Manual: plugin frame native fullscreen

Notes

TREK contributing guidelines ask for a Discord #github-pr pitch first. Happy to discuss there if this needs to be split (links vs chat images vs fullscreen) or adjusted before review.

@mauriceboe
mauriceboe force-pushed the feat/collab-links-chat-images branch from 13f6697 to 0fe614f Compare September 8, 2026 20:42
GodricTM and others added 5 commits September 8, 2026 23:19
Adds a Collab Links panel (add/pin/delete) with translations in every locale so tab and button labels are not raw i18n keys, chat image attachments (up to 4), and native fullscreen on plugin iframes. Includes Zod request DTOs and CollabPanel coverage for the Links tab.
The chat image filter believed the Content-Type the client put on the part.
The stored name keeps the extension of the name the client sent, and the
download route derives what it serves from that extension and sends it inline,
so pwn.html declared as image/png came back as HTML on our own origin. The
filter now checks the extension as well, against the blocked list and against
the types it claims to accept, because a per-route filter fully replaces the
module-level one it looked like it was adding to.

The multipart message route also kept its guard decorators. Guards run before
the interceptor, so a refusal went out while the client was still streaming up
to four images and the socket died as ECONNRESET rather than carrying the
error. The checks moved into the handler, where the other upload route in this
file already does them, and a test pins both refusals.

The three collab:link events were missing from the shared registry, which the
server typecheck refuses, and the admin Links switch could never be saved
because the Zod contract had no key for it. Both are in now. Migrations moved
to the end of the list and the column add uses the pragma guard rather than a
caught duplicate-column error.

The message-required string keeps its old wording for a request without a file
part, so a client from before this route took images sees what it always saw.
…inks

The desktop layout rendered notes and links as a pair and dropped whichever of
the two was on its own, so turning the new Links feature off on an otherwise
default install made the Notes panel disappear. Both rows are now split by kind
and rendered only when they hold something, and one function decides what a
panel id renders, because the branch that lost a panel did so by not listing it.

The message and its attachment rows are written in one transaction. The caller
has already committed the image bytes by then, so a message that landed without
them left those bytes with nothing pointing at them and nothing to sweep them.
Deleting one now drops the blobs after the rows are gone rather than before.

A body the Zod pipe rejects used to leave up to four spooled images on disk:
pipes run after interceptors, so multer had already written them and the
handler's own cleanup was never reached. An interceptor removes them on any
failure before the handler, which also covers every other schema mismatch, and
the length check that could never fire is gone.

Adding, pinning and deleting a link now says something when it fails instead of
raising an unhandled rejection, the list has real types, and the rejected-image
toast is translated in all 23 locales rather than shipping English everywhere.
The picking, previewing and clearing of chat images moved into one hook both
shells use: it was copied into two, and both rebuilt every preview on each
change and leaked an object URL per image per pick.

Tests: the four link routes end to end with the validation pipe the harness was
missing, the image filter against the disguised-extension case, and the layout
combinations that drop a panel.
The URL check used `new URL(...)`, and this package compiles against lib
ES2022 alone so that it carries neither DOM nor Node globals, which is why the
shared typecheck refused it. `z.url()` plus a protocol test does the same job
with names the package actually has; z.url() on its own would take mailto: and
javascript:.

The three collab:link events also needed their fixtures and their place in the
pinned inventory count, the guard that stops the fixture map drifting from the
registry.
…columns

The three collab:link events were broadcast and landed nowhere: the links
panel held its own list and never listened, so a link somebody else added
showed up on the next reload. It listens now, the same shape the notes panel
uses, and the events are classified in the client's WS policy, which is the
ratchet that caught this.

Four e2e harnesses build their own trip_files and one asserts the collab
feature set, so the new message_id column and the new links flag had to reach
them too. Two MCP unit tests pinned the old four-key feature object.
@mauriceboe
mauriceboe force-pushed the feat/collab-links-chat-images branch from 126305a to e624c6f Compare September 8, 2026 21:25
The chat images never reached a test: committing them to storage, taking them
back out when a later one fails, and dropping them again when the reply target
turns out to be gone. The interceptor that removes the spool when a request
dies before the handler had none either, and it sits in a domain pinned at 99%
function coverage. Both filters are pinned too, including the disguised
extensions that were the point of changing them.
@mauriceboe
mauriceboe merged commit 2a38e79 into liketrek:dev Sep 8, 2026
6 of 7 checks passed
@mauriceboe mauriceboe mentioned this pull request Sep 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants