Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 22 additions & 9 deletions console/web/src/components/chat/coder/OpenInEditorButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,14 @@ import { copyTextToClipboard } from '@/lib/clipboard'
import { EDITORS, type EditorId, editorById } from '@/lib/editor-links'

/**
* "open in editor" affordance for coder file-change rows: one button that
* always opens a menu of editors (cursor / vs code / zed) — each entry
* launches that editor via its URL scheme — plus "copy path", the fallback
* when the browser isn't on the machine that has the files. No editor is
* privileged; the menu is shown every time so the choice stays explicit.
* Renders nothing for non-absolute paths (result resolution failed —
* `coder` results are jail-resolved absolute on success).
* "open in editor" affordance for coder file-change rows: the console's
* own shell explorer first (`#/ext/shell/open/<encoded-path>[:line]` —
* the shell worker serves both the `coder::*` surface that produced this
* row and that page, so the route always resolves), then external
* editors (cursor / vs code / zed) via their URL schemes, plus "copy
* path" — the fallback when the browser isn't on the machine that has
* the files. Renders nothing for non-absolute paths (result resolution
* failed — `coder` results are jail-resolved absolute on success).
*/
export function OpenInEditorButton({
path,
Expand All @@ -29,6 +30,14 @@ export function OpenInEditorButton({
const [copied, setCopied] = useState(false)
if (!path.startsWith('/')) return null

const openInShell = () => {
const anchor =
typeof line === 'number' && Number.isInteger(line) && line > 0
? `:${line}`
: ''
window.location.hash = `#/ext/shell/open/${encodeURIComponent(path)}${anchor}`
}

const openWith = (id: EditorId) => {
window.location.href = editorById(id).buildUrl(path, line)
}
Expand All @@ -47,13 +56,17 @@ export function OpenInEditorButton({
<button
type="button"
className="inline-flex items-center shrink-0 cursor-pointer text-ink-ghost hover:text-ink transition-colors"
aria-label="open in editor"
title="open in editor"
aria-label="open file"
title="open file"
>
<SquareArrowOutUpRight size={12} aria-hidden />
</button>
</DropdownMenuTrigger>
<DropdownMenuContent align="start">
<DropdownMenuItem onSelect={openInShell}>
open in shell
</DropdownMenuItem>
<DropdownMenuSeparator />
{EDITORS.map((e) => (
<DropdownMenuItem key={e.id} onSelect={() => openWith(e.id)}>
open in {e.label}
Expand Down
Loading