Give attachment preview tabs file actions from the shared action list

Open
#29 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Assessment

Difficulty
4/5
Estimated time
3-5 days
Newbie friendliness
48/100
Issue type
Feature
Clarity
Clearly specified
Activity status
Active
Tech stack
tauri, typescript

Research direction

Start with PreviewWorkspace.tsx, especially TabContextMenu, HeaderFileActions, PreviewAttachmentView, and the file and attachment tab components; read previewResource.ts to understand the shared actions. Then inspect DocumentTabView.tsx, its tests, and the English and Chinese i18n files. Done means the attachment actions, copy labels, document downloads, Markdown relative paths, failed-read behavior, remote host checks, translations, and docs match the listed acceptance criteria; the issue also depends on #27 and #28 and requires desktop checks and reviewed screenshots.

Written by the indexing model from the issue text.

Description

enhancement

What feature would you like to see?

A file the agent sent with SendMessage opens in a preview tab whose context menu offers only "close tab / close others / close all", and whose header offers only "rendered / source" and a download arrow. PDF, Word, Excel, PowerPoint and CSV attachments do not even get a download: their header shows page controls only. Markdown attachments resolve relative images and links against the session directory instead of the file's own directory, so their images show "无法读取该文件" ("Could not load this file"). Opening the same notes.md from the receipt and from a link gives two tabs with the same name and no word saying which is the sent copy.

Decided
  • Attachment tabs get the actions the shared action list (preview resource model issue) allows: with a trusted original they offer copy path, show on this computer, open original file, and "@" add to chat; without one they offer none of these.
  • A live file and its sent copy are two tabs. The copy is labelled "副本" and offers "打开现在的文件" (open the current file).
  • Every place in this file that decides "is this connection remote" uses the one isKikiComputer function, which fails closed.
  • When a read fails, header actions that need the bytes (download, add to chat) are not rendered.
  • Merging file and attachment tabs into one kind is a later phase; this issue keeps the two tab kinds.
Design
  1. Tab context menu (TabContextMenu): keep close tab, close others, close all. For file tabs and attachment tabs, append the items of resourceActions(...) from apps/kiki-gui/src/state/previewResource.ts for that tab's resource instead of the hand-written tab.kind === 'file' branch. The file-tab items that exist today (copy relative path, copy absolute path, show in folder, open with default app) stay available where the action list allows them.
  2. Attachment header: render the same action list. The most used action stays inline (download where the bytes are readable); the rest go into a header "more" menu. For a copy with a trusted original, the open-with-default-app action reads "打开原文件" / "Open original file", because it opens what is on disk now, not the sent bytes.
  3. Copy marking: an attachment tab whose resource is a copy shows the short mark "副本" / "Sent copy" on the tab and the line "发送时的副本" / "Copy as it was sent" in its header. When the trusted original is readable, the header offers "打开现在的文件" / "Open current file", which opens (or activates) the live file tab for the original path.
  4. Document attachments: DocumentTabView gets a download action in its own header, so PDF, Office and CSV attachments can be downloaded in every state, not only in the "unsupported" fallback.
  5. Markdown attachments: pass the original's directory as documentDirectory (and the original path as documentPath) when a trusted origin exists, so relative images and links resolve where the file came from. Without an origin, keep today's behaviour.
  6. One remote check for host actions: replace the two SSH-only checks that gate reveal and open in this file (scopeId.startsWith('ssh:') in TabContextMenu and useFileOpeners) with isKikiComputer. Today the tab menu and header offer "show in folder" and "open with default app" in a remote space that is not SSH; afterwards they do not. The connectionSource === 'remote' check in the text tab answers a different question (whether the HTML preview goes through the remote-space broker and may ask for a resource root); leave its behaviour unchanged, but give the variable a name that says so.
  7. Docs: in the preview paragraph of docs/en/guides/interface.md, say that a sent attachment whose original is known offers copy path, show and open original, and is marked as the copy as it was sent; mirror it in docs/zh/guides/interface.md.
  8. Failed reads: when the tab's read failed, the header does not render download or add-to-chat, and the tab's "@" is not rendered.
  9. Hard-coded English: the agent tab fallback label and tooltip use a hard-coded Agent:; replace both with an i18n key in en.ts and zh.ts.

Repository rules: every new string gets a key in both packages/session-core/src/i18n/en.ts and zh.ts. The i18n files are shared with other issues; add keys, do not reorder.

Where
  • apps/kiki-gui/src/components/PreviewWorkspace.tsx:1061-1125 (TabContextMenu; the file-only branch at 1088-1100; SSH-only check at 1080; both host openers required at 1082).
  • :1162-1176 (useFileOpeners, SSH-only check at 1166), :1198-1242 (HeaderFileActions).
  • :1959 (remoteScope = connection?.connectionSource === 'remote' in the text tab, used at 2003 and 2166 for the remote HTML resource root).
  • Docs: docs/en/guides/interface.md:87-97 and docs/zh/guides/interface.md:87-97.
  • :2429-2774 (PreviewAttachmentView: path stripped before the byte read at 2472-2475; document attachments hand the whole header to DocumentTabView at 2584-2596; header download at 2630-2639; markdown rendered without documentDirectory at 2738, compare the file tab at 2119-2120 and 2375).
  • :456 and :1003 (hard-coded Agent:).
  • :625-735 (PreviewFileTab with the "@" at 700-712), :811-893 (PreviewAttachmentTab).
  • apps/kiki-gui/src/components/preview/DocumentTabView.tsx:147-157 (header without download), :309-313 (download only in the fallback body).
  • Existing strings to reuse: file.copyRelativePath, file.copyAbsolutePath, file.showInFolder, file.openDefaultApp, media.download, preview.addToChat in packages/session-core/src/i18n/en.ts:883-887, 1901, 1929 and zh.ts:872-876, 1881, 1909.
Done when
  • With a SendMessage attachment that has source_id, on a local desktop host: the tab menu and header offer copy path, show on this computer, open original file and "@"; in a remote space: only copy path; without source_id: none of the three. Covered by tests in PreviewWorkspace.test.tsx using a mocked host.
  • PDF, Office and CSV attachment tabs have a download action in the header in the ready state.
  • A relative image in a Markdown attachment with a trusted origin resolves against the original's directory.
  • Opening the same file from a receipt and from a link gives two tabs; the copy carries "副本" and "发送时的副本", and "打开现在的文件" activates the live tab.
  • After a failed read, the header shows no download and no "@".
  • No scopeId.startsWith('ssh:') check remains in PreviewWorkspace.tsx; reveal and open are gated only by isKikiComputer and the host capabilities.
  • en and zh docs updated together.
  • Screenshots from apps/kiki-gui/scripts/visual-proof.mjs (--theme light,dark --locale en,zh --viewport wide,narrow, message-channels scenario with source_id, plus a mocked local desktop host built the way apps/kiki-gui/scripts/broker-desktop-mock.mjs stubs window.__TAURI_INTERNALS__) show the attachment tab header and menu at desktop and phone width; they are looked at, not only produced.
  • pnpm --filter @kiki/gui typecheck and pnpm --filter @kiki/gui test:fast pass.
  • Phone-width screenshots are taken in a touch context (isMobile: true, hasTouch: true, as apps/kiki-gui/scripts/visual-proof-mobile-reading.mjs:294-300 does). Phone mode is (pointer: coarse) and (max-width: 639px) (apps/kiki-gui/src/lib/layoutHooks.ts:57), so a width-only --viewport narrow run of visual-proof.mjs shows the desktop layout squeezed, not the phone layout.

Additional information

Start only after the pending GUI interaction and mobile pass has merged: it also edits PreviewWorkspace.tsx and mediaParts.tsx, and the line numbers above are from the tree that includes it. The later issue that splits PreviewWorkspace.tsx into renderer files must start after this one. The desktop shell's reveal, open and write commands only run in the desktop app, so the "show" and "open" items must be checked there by hand as well; the browser harness has no revealPath or openPath. Paired devices and remote spaces cannot read files by path yet; that is handled in its own issue.

Order and scope. Paths and line numbers refer to the code after the pending GUI interaction and mobile pull request is merged into kiki.
Depends on: #27, #28.
Main files: apps/kiki-gui/src/components/PreviewWorkspace.tsx, apps/kiki-gui/src/components/PreviewWorkspace.test.tsx, apps/kiki-gui/src/components/preview/DocumentTabView.tsx, packages/session-core/src/i18n/en.ts, packages/session-core/src/i18n/zh.ts, docs/en/guides/interface.md, docs/zh/guides/interface.md.

Dominant language
TypeScript
Stars
54
Forks
5
Avg merge
2h 54m
Merged PRs (30d)
2

Getting set up

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from X-T-E-R/kiki

All issues in X-T-E-R/kiki

Similar issues

More TypeScript issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.