This step adds file preview behavior.
Preview is a UI concern. Document understanding stays in tools such as
chat.list-attachments, chat.search-documents, chat.read-document, and
chat.extract-full-summary.
Attachment Click Contract¶
MessageList dispatches chat.open_attachment_preview when the user clicks an
attachment:
on_attachment_click:
name: chat.open_attachment_preview
context:
source: chat_message_listThe action receives the clicked attachment payload. The current chat rows use:
{
"name": "document.pdf",
"mime_type": "application/pdf",
"storage_path": "media/chat/document.pdf",
"file_id": "media-file-id",
}storage_path is a stored media reference, not a client URL.
Preview Action¶
modules/chat/actions/attachments.py resolves the stored path through the SDK
and renders an AttachmentPreview into the drawer:
@action("open_attachment_preview")
@permission_required(["chat.view"])
async def open_attachment_preview(ctx: dict, session: dict, module_sdk):
storage_path = ctx["storage_path"]
public_url = module_sdk.media.get_public_url(storage_path) if storage_path else None
preview = module_sdk.ui.AttachmentPreview(
"chat_attachment_preview",
name=ctx["name"],
mime_type=ctx["mime_type"],
storage_path=storage_path,
file_id=ctx["file_id"],
url=str(public_url or ""),
height=760,
)
...The action then builds a small Builder, calls
module_sdk.effects.build_aux_surface_messages(builder, "drawer"), and returns
those messages through module_sdk.effects.respond(...).
The current action also adjusts drawer options for the preview surface:
begin["options"] = {"position": "right", "dim": 980}That keeps large previews usable without changing the page layout.
Media Boundary¶
Do this:
public_url = module_sdk.media.get_public_url(storage_path)Do not construct runtime media URLs manually. Web, desktop, local storage, and distributed storage can require different URL/materialization behavior.
Preview Is Not Retrieval¶
Preview lets the user inspect a file. The model still uses tool contracts:
chat.list-attachmentsfor attachment and extraction status;chat.search-documentsfor targeted content search;chat.read-documentfor bounded document blocks;chat.extract-full-summaryfor cached large-document maps.