fix(ui): wrap company skill source paths (#9405)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work > - Company skills expose their source metadata in a narrow details sidebar > - Long filesystem paths and repository locators were truncated, hiding the part operators often need to distinguish sources > - The sidebar can preserve the complete value by wrapping at arbitrary path boundaries instead of ellipsizing it > - This pull request renders full source paths and repository labels without widening the layout > - The benefit is that operators can inspect and copy the actual skill source from the UI ## Linked Issues or Issue Description ### Pre-submission checklist - [x] I have searched existing open and closed issues and this is not a duplicate. - [x] I can reproduce this on `master`. - [x] I have confirmed the behavior originates in Paperclip itself, not an agent adapter, API provider, or local configuration. ### What happened? Long company-skill source paths and repository locators were truncated in the skill details sidebar. ### Expected behavior The complete source value remains visible and wraps within the available sidebar width. ### Steps to reproduce 1. Open a company skill whose source path is longer than the details sidebar. 2. View the Source field. 3. Observe that the old UI replaces the middle or end of the value with an ellipsis. ### Paperclip version or commit `origin/master` at `02e2dd271`. ### Deployment mode Local dev (`pnpm dev`). ### Installation method Built from source (`pnpm dev` / `pnpm build`). ### Agent adapter(s) involved None; this is a company-skills UI layout issue. ### Logs, configuration, or screenshots Not applicable; the behavior is directly visible in the Source field. ### Additional context The narrow sidebar should remain width-constrained. Wrapping intentionally trades vertical space for full source inspectability. ## What Changed - Replace source-path truncation with width-constrained arbitrary wrapping. - Apply the same wrapping behavior to linked repository/source labels. - Add a regression test proving the full long path is rendered without ellipsis. ## Verification - `vitest --project @paperclipai/ui src/pages/CompanySkills.test.tsx` — 11 tests passed. - `node scripts/check-token-gates.mjs` — all token gates clean. ## Risks - Low risk: the change is limited to text layout in the company skill details view. - Very long unbroken values may make the Source section taller, intentionally trading vertical space for inspectability. > For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and discuss it in `#dev` before opening the PR. Feature PRs that overlap with planned core work may need to be redirected — check the roadmap first. See `CONTRIBUTING.md`. ## Model Used - OpenAI Codex, GPT-5.3 Codex, reasoning with repository tool use and code execution; context-window size was not exposed by the runtime. ## Checklist - [x] I have included a thinking path that traces from project context to this change - [x] I have specified the model used (with version and capability details) - [x] I have checked ROADMAP.md and confirmed this PR does not duplicate planned core work - [x] I have searched GitHub for duplicate or related PRs and linked them above - [x] I have either (a) linked existing issues with `Fixes: #` / `Closes #` / `Refs #` OR (b) described the issue in-PR following the relevant issue template - [x] I have not referenced internal/instance-local Paperclip issues or links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip` URLs) - [x] My branch name describes the change (e.g. `docs/...`, `fix/...`) and contains no internal Paperclip ticket id or instance-derived details - [x] I have run tests locally and they pass - [x] I have added or updated tests where applicable - [x] I have updated relevant documentation to reflect my changes - [x] I have considered and documented any risks above - [ ] All Paperclip CI gates are green - [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups - [x] I will address all Greptile and reviewer comments before requesting merge --------- Co-authored-by: Paperclip <noreply@paperclip.ing>
This commit is contained in:
parent
02e2dd271b
commit
5618ea91f6
|
|
@ -390,6 +390,26 @@ describe("SkillDetailPage settings", () => {
|
|||
expect(onFork).toHaveBeenCalledOnce();
|
||||
});
|
||||
|
||||
it("renders long source paths in full so they can wrap inside the sidebar", async () => {
|
||||
const v1 = makeVersion(1, "# Demo Skill");
|
||||
const longSourcePath = "/srv/paperclip/home/paperclipai/paperclip/.agents/skills/prepare-pr/SKILL.md";
|
||||
const node = await renderSkillDetail([v1], {
|
||||
activeTab: "agents",
|
||||
detail: makeDetail(v1, {
|
||||
sourcePath: longSourcePath,
|
||||
sourceLocator: null,
|
||||
}),
|
||||
});
|
||||
|
||||
const sourceValue = Array.from(node.querySelectorAll("div")).find((element) =>
|
||||
element.textContent === longSourcePath,
|
||||
);
|
||||
|
||||
expect(sourceValue).toBeTruthy();
|
||||
expect(sourceValue?.className).toContain("[overflow-wrap:anywhere]");
|
||||
expect(node.textContent).not.toContain("...");
|
||||
});
|
||||
|
||||
it("saves normalized category edits from the settings dialog", async () => {
|
||||
const v1 = makeVersion(1, "# Demo Skill");
|
||||
const onUpdateSettings = vi.fn();
|
||||
|
|
|
|||
|
|
@ -2588,10 +2588,9 @@ export function SkillDetailPage({
|
|||
? githubSource.url
|
||||
?? `https://${githubSource.hostname}/${githubSource.owner}/${githubSource.repo}/tree/${githubSource.ref}/${githubSource.path}`.replace(/\/$/, "")
|
||||
: null;
|
||||
// Fallback for non-catalog skills: the recorded locator/path, middle-truncated
|
||||
// so long file paths stay readable in the narrow sidebar.
|
||||
// Fallback for non-catalog skills: the recorded locator/path wraps inside
|
||||
// the narrow sidebar instead of widening the page.
|
||||
const sourceLocatorText = skill.sourcePath || skill.sourceLocator || null;
|
||||
const sourceLocatorDisplay = sourceLocatorText ? middleTruncate(sourceLocatorText, 44) : null;
|
||||
const sourceHref =
|
||||
skill.homepageUrl
|
||||
?? (sourceLocatorText && /^(https?:\/\/|[\w.-]+\.[a-z]{2,}\/)/i.test(sourceLocatorText)
|
||||
|
|
@ -2709,7 +2708,7 @@ export function SkillDetailPage({
|
|||
</div>
|
||||
<div className="min-w-0 border-b border-border py-2">
|
||||
<div className="text-xs text-muted-foreground">Source</div>
|
||||
<div className="mt-1 truncate">{skill.sourcePath ?? source.label}</div>
|
||||
<div className="mt-1 min-w-0 [overflow-wrap:anywhere]">{sourceLocatorText ?? source.label}</div>
|
||||
</div>
|
||||
<div className="min-w-0 border-b border-border py-2">
|
||||
<div className="text-xs text-muted-foreground">Version</div>
|
||||
|
|
@ -3054,9 +3053,9 @@ export function SkillDetailPage({
|
|||
target="_blank"
|
||||
rel="noreferrer"
|
||||
title={githubRepoText ?? undefined}
|
||||
className="mt-0.5 flex max-w-full items-center gap-1 text-xs text-muted-foreground no-underline transition-colors hover:text-foreground"
|
||||
className="mt-0.5 flex max-w-full items-start gap-1 text-xs text-muted-foreground no-underline transition-colors [overflow-wrap:anywhere] hover:text-foreground"
|
||||
>
|
||||
<span className="truncate">{githubRepoText}</span>
|
||||
<span className="min-w-0 [overflow-wrap:anywhere]">{githubRepoText}</span>
|
||||
<ExternalLink className="h-3 w-3 shrink-0" aria-hidden="true" />
|
||||
</a>
|
||||
<div className="mt-0.5 truncate font-mono text-(length:--text-micro) text-muted-foreground" title={githubSource.commit}>
|
||||
|
|
@ -3070,21 +3069,21 @@ export function SkillDetailPage({
|
|||
<SourceIcon className="mt-0.5 h-4 w-4 shrink-0 text-muted-foreground" aria-hidden="true" />
|
||||
<div className="min-w-0">
|
||||
<div className="text-foreground">{source.label}</div>
|
||||
{sourceLocatorDisplay ? (
|
||||
{sourceLocatorText ? (
|
||||
sourceHref ? (
|
||||
<a
|
||||
href={sourceHref}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
title={sourceLocatorText ?? undefined}
|
||||
className="mt-0.5 flex max-w-full items-center gap-1 text-xs text-muted-foreground no-underline transition-colors hover:text-foreground"
|
||||
className="mt-0.5 flex max-w-full items-start gap-1 text-xs text-muted-foreground no-underline transition-colors [overflow-wrap:anywhere] hover:text-foreground"
|
||||
>
|
||||
<span className="truncate">{sourceLocatorDisplay}</span>
|
||||
<span className="min-w-0 [overflow-wrap:anywhere]">{sourceLocatorText}</span>
|
||||
<ExternalLink className="h-3 w-3 shrink-0" aria-hidden="true" />
|
||||
</a>
|
||||
) : (
|
||||
<div className="mt-0.5 truncate text-xs text-muted-foreground" title={sourceLocatorText ?? undefined}>
|
||||
{sourceLocatorDisplay}
|
||||
<div className="mt-0.5 min-w-0 text-xs text-muted-foreground [overflow-wrap:anywhere]" title={sourceLocatorText ?? undefined}>
|
||||
{sourceLocatorText}
|
||||
</div>
|
||||
)
|
||||
) : (
|
||||
|
|
|
|||
Loading…
Reference in New Issue