Improve workspace path wrapping with natural break points

- Replace break-all with <wbr> hints after / and - characters so paths
  break at directory and word boundaries instead of mid-word
- Use overflow-wrap: anywhere as fallback for very long segments
- Apply natural breaking to the workspace name link as well
- Rename CopyablePath to CopyableValue with optional mono prop for
  better semantic clarity

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
Dotta
2026-03-17 08:53:19 -05:00
parent e6269e5817
commit 6c779fbd48

View File

@@ -129,8 +129,20 @@ function PropertyPicker({
); );
} }
/** Displays a path/value with a copy-to-clipboard icon and "Copied!" feedback. */ /** Splits a string at `/` and `-` boundaries, inserting <wbr> for natural line breaks. */
function CopyablePath({ value, label, className }: { value: string; label?: string; className?: string }) { function BreakablePath({ text }: { text: string }) {
const parts: React.ReactNode[] = [];
// Split on path separators and hyphens, keeping them in the output
const segments = text.split(/(?<=[\/-])/);
for (let i = 0; i < segments.length; i++) {
if (i > 0) parts.push(<wbr key={i} />);
parts.push(segments[i]);
}
return <>{parts}</>;
}
/** Displays a value with a copy-to-clipboard icon and "Copied!" feedback. */
function CopyableValue({ value, label, mono, className }: { value: string; label?: string; mono?: boolean; className?: string }) {
const [copied, setCopied] = useState(false); const [copied, setCopied] = useState(false);
const timerRef = useRef<ReturnType<typeof setTimeout>>(undefined); const timerRef = useRef<ReturnType<typeof setTimeout>>(undefined);
const handleCopy = useCallback(async () => { const handleCopy = useCallback(async () => {
@@ -144,15 +156,15 @@ function CopyablePath({ value, label, className }: { value: string; label?: stri
return ( return (
<div className={cn("flex items-start gap-1 group", className)}> <div className={cn("flex items-start gap-1 group", className)}>
<span className="break-all min-w-0"> <span className="min-w-0" style={{ overflowWrap: "anywhere" }}>
{label && <span className="text-muted-foreground">{label} </span>} {label && <span className="text-muted-foreground">{label} </span>}
<span className="font-mono">{value}</span> <span className={mono ? "font-mono" : undefined}><BreakablePath text={value} /></span>
</span> </span>
<button <button
type="button" type="button"
className="shrink-0 mt-0.5 p-0.5 rounded hover:bg-accent/50 transition-colors text-muted-foreground hover:text-foreground opacity-0 group-hover:opacity-100 focus:opacity-100" className="shrink-0 mt-0.5 p-0.5 rounded hover:bg-accent/50 transition-colors text-muted-foreground hover:text-foreground opacity-0 group-hover:opacity-100 focus:opacity-100"
onClick={handleCopy} onClick={handleCopy}
title={copied ? "Copied!" : "Copy"} title={copied ? "Copied!" : "Copy to clipboard"}
> >
{copied ? <Check className="h-3 w-3 text-green-500" /> : <Copy className="h-3 w-3" />} {copied ? <Check className="h-3 w-3 text-green-500" /> : <Copy className="h-3 w-3" />}
</button> </button>
@@ -694,30 +706,30 @@ export function IssueProperties({ issue, onUpdate, inline }: IssuePropertiesProp
{issue.currentExecutionWorkspace && ( {issue.currentExecutionWorkspace && (
<div className="text-[11px] text-muted-foreground space-y-0.5"> <div className="text-[11px] text-muted-foreground space-y-0.5">
<div> <div style={{ overflowWrap: "anywhere" }}>
Current:{" "} Current:{" "}
<Link <Link
to={`/execution-workspaces/${issue.currentExecutionWorkspace.id}`} to={`/execution-workspaces/${issue.currentExecutionWorkspace.id}`}
className="hover:text-foreground hover:underline" className="hover:text-foreground hover:underline"
> >
{issue.currentExecutionWorkspace.name} <BreakablePath text={issue.currentExecutionWorkspace.name} />
</Link> </Link>
{" · "} {" · "}
{issue.currentExecutionWorkspace.status} {issue.currentExecutionWorkspace.status}
</div> </div>
{issue.currentExecutionWorkspace.cwd && ( {issue.currentExecutionWorkspace.cwd && (
<CopyablePath value={issue.currentExecutionWorkspace.cwd} className="text-[11px]" /> <CopyableValue value={issue.currentExecutionWorkspace.cwd} mono className="text-[11px]" />
)} )}
{issue.currentExecutionWorkspace.branchName && ( {issue.currentExecutionWorkspace.branchName && (
<CopyablePath value={issue.currentExecutionWorkspace.branchName} label="Branch:" className="text-[11px]" /> <CopyableValue value={issue.currentExecutionWorkspace.branchName} label="Branch:" className="text-[11px]" />
)} )}
{issue.currentExecutionWorkspace.repoUrl && ( {issue.currentExecutionWorkspace.repoUrl && (
<CopyablePath value={issue.currentExecutionWorkspace.repoUrl} label="Repo:" className="text-[11px]" /> <CopyableValue value={issue.currentExecutionWorkspace.repoUrl} label="Repo:" mono className="text-[11px]" />
)} )}
</div> </div>
)} )}
{!issue.currentExecutionWorkspace && currentProject?.primaryWorkspace?.cwd && ( {!issue.currentExecutionWorkspace && currentProject?.primaryWorkspace?.cwd && (
<CopyablePath value={currentProject.primaryWorkspace.cwd} className="text-[11px] text-muted-foreground" /> <CopyableValue value={currentProject.primaryWorkspace.cwd} mono className="text-[11px] text-muted-foreground" />
)} )}
</div> </div>
</PropertyRow> </PropertyRow>