Skip to content

[FEATURE]:Support drag-and-drop for Microsoft Office files (.docx, .xlsx) #27689

Description

@BWbear

Feature hasn't been suggested before.

  • I have verified this feature I'm about to request hasn't been suggested before.

Describe the enhancement you want to request

Description: Currently, the opencode chat interface does not support dragging and dropping Microsoft Office files (such as .docx and .xlsx). Users are forced to manually provide file paths or convert files to text/PDF before uploading.

Proposed Solution:

Expand the allowed file types in the frontend's drag-and-drop handler to include .docx and .xlsx.
Integrate a backend parsing mechanism (e.g., using python-docx and openpyxl) to convert these documents into a text-based format (like Markdown) that the LLM can process.
Use Case: Many software engineering and administrative tasks involve reading requirements or data stored in Office documents. Direct support would significantly improve the user experience and workflow efficiency.

Activity

  1. github-actions commented on May 15, 2026

    @github-actions
    Contributor

    This issue might be a duplicate of existing issues. Please check:

    Please review those issues and consider whether your request is already covered. If it is, adding a comment there with your specific use case (.docx/.xlsx + backend parsing) would help consolidate the discussion.

  2. GP-commits commented on May 17, 2026

    @GP-commits

    Hi! I would love to work on this feature. Could you assign it to me?

    Here is my proposed implementation plan:

    1. Frontend: Update \ACCEPTED_FILE_TYPES\ in \packages/app/src/constants/file-picker.ts\ to include .docx, .xlsx, and their MIME types.
    2. Backend: Add \mammoth\ (for docx) and \xlsx\ (for Excel) to the dependencies in \packages/opencode/package.json.
    3. Modify \packages/opencode/src/tool/read.ts\ to intercept .docx\ and .xlsx\ before they hit the binary file check. We will parse their contents into text/CSV and return it within <content>\ tags for the LLM to ingest natively.

    Let me know if this approach sounds good or if you have any feedback before I submit a PR!

  3. buildgreatthings commented on Jun 9, 2026

    @buildgreatthings

    We use Opencode as a general harness for all our agent runs. I think an even simpler first change would start with:

    1. Frontnend. Same as @GP-commits
    2. Enable file passing to the LLM API endpoints. Anthropic and OpenAI already handle attachments natively.

    A separate follow on PR would be great for support opensource models.

  4. bitfinop commented on Jul 16, 2026

    @bitfinop

    Adding our case for consolidation (as the dedupe bot suggested) — we hit this in a corporate rollout of opencode Desktop on a central config.

    The angle that's slightly different from drag-and-drop: the “+” button / Cmd+U file picker filters selectable files by the model's modalities.input (text, image, audio, video, pdf) — so .docx/.xlsx and other office formats are simply greyed out and can't be attached at all.

    Meanwhile the capability already exists elsewhere: the Read tool parses those same files fine once they're inside the project folder, and plugins (e.g. opencode-markitdown) convert them to Markdown. Only the picker blocks the path.

    Two options from our users' perspective:

    1. add a document modality for office formats, or
    2. when attachments are handled tool-side, don't filter the picker — accept any file and route it through Read/conversion.

    Observed on Desktop v1.17.18, macOS. Essentially the same request as #18143.

  5. jdecker76 commented on Jul 30, 2026

    @jdecker76

    Adding our case: we run opencode against an internal gateway (LiteLLM proxy) that already handles Excel. The backend is fine with .xlsx — only the client drops it.

    One correction to @bitfinop's note above — the picker doesn't filter on the model's modalities.input; I couldn't find that in the attachment path at all. The actual blockers are three hardcoded lists:

    • ACCEPTED_FILE_TYPES in packages/app/src/constants/file-picker.ts — the picker allowlist
    • attachmentMime() in prompt-input/files.ts — returns undefined for .xlsx (it's a ZIP, so it fails the text sniff), so the file is dropped after selection even if the picker lets it through. This logic is duplicated in session-ui/src/v2/components/prompt-input/attachments.ts.
    • local-attachment.ts in the TUI — if (!mime.startsWith("image/") && mime !== "application/pdf") return

    The server already passes arbitrary mime through as a base64 file part (session/prompt.ts), so this looks client-side only.

    Would you take a config option extending the allowed types, reusing the existing attachment namespace?

    { "attachment": { "types": [".xlsx"] } }

    Same token syntax that array already mixes (MIME, text/*, or .ext), appended to the defaults. No parsing and no new dependencies — deliberately not what #32104 attempted.

    One design question before I start: when an accepted file's type is beyond what the model claims to support, should it be sent anyway and let the provider decide, or gated on capability? The former is what our proxy needs, and matches what the server does today.

  6. zukktest-cell commented on Aug 8, 2026

    @zukktest-cell

    Also affected on the desktop app (Windows). The attach button / file picker filters out .xlsx (and the attachment MIME sniffing rejects both .xls and .xlsx as binary), so spreadsheet files cannot be attached at all. Being able to drag-and-drop .xlsx and have it converted to Markdown tables (like the office-converter approach in the pending PRs) would be very useful. Looking forward to seeing this merged.

  7. revaza88 commented on Sep 2, 2026

    @revaza88

    We hit this on OpenCode Desktop 1.18.25 (Windows) and spent some time instrumenting the shipped
    bundle before building a working local patch. Sharing what we found, because there is a detail in
    this thread that will cost the next person a day.

    The change proposed above is not sufficient on its own

    Adding .docx / .xlsx to ACCEPTED_FILE_TYPES in packages/app/src/constants/file-picker.ts
    does not make Office files attachable. We tried exactly that first, and nothing changed.

    There are two independent attachment pipelines in the renderer, each with its own accept list
    and its own MIME resolver:

    list resolver add function
    legacy ACCEPTED_FILE_TYPES (packages/app/src/constants/file-picker.ts:3) attachmentMime createPromptAttachmentsCore
    v2 (the one actually rendered) accepted (packages/session-ui/src/v2/components/prompt-input/attachments.ts:5) its own attachmentMime add at attachments.ts:99

    The prompt input in the desktop app is data-component=prompt-input-v2, so only the second row is
    live. Patching the first list has no observable effect.

    Three places need to change

    1. attachments.ts:5 — add the extensions to accepted (this drives both the <input accept>
      attribute and the native picker's extensions).
    2. attachments.ts:101 — attachmentMime() samples the first 4096 bytes and rejects anything with
      NUL bytes. A .xlsx is a zip, so it is rejected as binary even once the picker lets it through.
      The file has to be converted to text before this call.
    3. file-picker.ts:3 — for consistency with the legacy path.

    A subtle bug worth flagging

    If you convert the file by replacing the File object, attachments.ts:107

    const sourcePath = input.getPathForFile?.(file) || undefined

    silently returns undefined. getPathForFile looks the file up in a WeakMap populated by the
    picker, and the converted File is a different object. The original reference has to be kept for
    the sourcePath lookup, otherwise picker-attached documents lose their path.

    What we prototyped

    Conversion in the desktop main process behind an IPC handler (convert-office), exposed on the
    preload bridge, called from add() before attachmentMime. .xlsx/.xlsm become TSV per sheet,
    .docx becomes plain text.

    Deliberately no new dependencies: the zip central directory is read directly and inflated with
    node:zlib, then xl/worksheets/sheet*.xml + xl/sharedStrings.xml (or word/document.xml) are
    parsed with regexes. No xlsx, no mammoth, no native modules, nothing added to the bundle size
    beyond ~180 lines. It has been running against real spreadsheets and Word documents on Windows.

    We are happy to open a PR with this cleaned up and tested.

    Question for the maintainers

    Per CONTRIBUTING this touches UI/product behaviour, so before writing anything we would like a
    steer on scope:

    • Desktop-only, as we prototyped it (main-process IPC)? Simple, but the CLI and web clients
      keep rejecting Office files.
    • Server-side, so every client benefits? Larger change, and it moves parsing into the server.
    • Is a dependency-free parser acceptable, or would you rather pull in a real library and accept the
      bundle cost?

    Happy to go whichever way you prefer — or to just contribute the findings above if this is already
    on someone's roadmap.

  8. supremest commented on Sep 8, 2026

    @supremest

    We needed .docx/.xlsx drag-and-drop on a self-hosted opencode web (1.18.25 → 1.18.29, Linux arm64) and ended up instrumenting the same code paths as @revaza88. A few findings not in this thread yet, in case they save someone time:

    1. attachmentMime() has two rejections, not one. After the NUL-byte check there is a control-character ratio check (> 0.3 of the first 4096 bytes). OOXML zips are ~0.13 so they only hit the NUL check, but legacy OLE2 files (.doc/.xls/.ppt, ~0.49) are rejected by the ratio check as well. Extending the accept list alone won't let those through.

    2. Once a binary passes the client gate it is labeled text/plain and sent as a data: URL. On the server, resolvePart turns text/plain data parts into synthetic Called the Read tool … + raw-content text parts before chat.message plugin hooks run. So a client-only fix leaks the raw bytes into the prompt as text — we saw the zip bytes land in context even though our hook had already replaced the file part.

    3. What worked for us end-to-end (web drop, paste, and run --file): disable the two client checks (we patched the embedded bundle; upstream this could be the attachment.types-style allowlist suggested above) plus a server-side chat.message plugin that ignores the declared mime, sniffs magic bytes (PK\x03\x04 with word/ | xl/ | ppt/ entries, the OLE2 header, %PDF-), converts documents to text (pandoc / LibreOffice / pandas in our setup), and replaces non-document binaries with a visible notice so they never reach the model as text. Verified with a headless-Chromium drop test against the real bundle for docx/xlsx/pptx/doc, mixed drops, and a random-binary guard. The remaining wart is the duplicated synthetic part from (2), which a plugin cannot prevent.

    4. Trap: over a remote plain-HTTP origin nothing attaches at all, regardless of this fix — that is Web UI: pasting images fails with Cannot read properties of undefined (reading 'digest') when accessing via HTTP on non-localhost #41706 / web: file attachments silently dropped on HTTP remote host — crypto.subtle undefined #47645 (crypto.subtle unavailable in insecure contexts). Test over HTTPS or localhost.

    If maintainers prefer the server-side direction, the smallest upstream change that makes this cleanly pluggable is running the text/plain synthetic-part injection after plugin hooks (or letting hooks see the raw file part first), plus the allowlist. Happy to share the hook if useful.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions