Files
boocode/apps/web/src/lib/linkify-paths.tsx
indifferentketchup 8c200216eb refactor: codebase audit cleanup — dead code, dedup, module splits
Multi-agent audit + aggressive cleanup across server/web/coder/booterm,
delivered behind a DEFER discipline so none of the in-flight files were
touched. Removes dead code/deps/columns, dedups server + coder helpers,
and splits the oversized modules (tools.ts, opencode-server.ts,
sentinel-summaries, turn.ts, TerminalPane.tsx) behind stable contracts.
Adds 78 parity/unit tests (server 587, coder 323); fixes two latent bugs
(ChatPane queue keys, FileViewerOverlay blank-line parity).

Intended tag: v2.7.12-audit-cleanup.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-02 21:12:29 +00:00

35 lines
1.2 KiB
TypeScript

import type { ReactNode } from 'react';
import { sessionEvents } from '@/hooks/sessionEvents';
// Match path-shaped substrings ending in `.ext`. Requires a `/` in the match
// to reduce false positives in prose (e.g. plain `foo.ts` won't match but
// `src/foo.ts` will). False positives at the edges are accepted (2026-05-14).
const PATH_REGEX = /([a-zA-Z0-9._/-]+\.[a-zA-Z0-9]+)/g;
export function linkifyPaths(text: string, keyPrefix = 'p'): ReactNode {
const out: ReactNode[] = [];
let lastIdx = 0;
let idx = 0;
for (const match of text.matchAll(PATH_REGEX)) {
const matchedText = match[0];
const start = match.index ?? 0;
if (!matchedText.includes('/')) continue;
if (start > lastIdx) out.push(text.slice(lastIdx, start));
out.push(
<button
key={`${keyPrefix}-${idx}`}
type="button"
onClick={() => sessionEvents.emit({ type: 'open_file_in_browser', path: matchedText })}
className="text-primary underline cursor-pointer hover:text-primary/80"
>
{matchedText}
</button>
);
lastIdx = start + matchedText.length;
idx += 1;
}
if (out.length === 0) return text;
if (lastIdx < text.length) out.push(text.slice(lastIdx));
return out;
}