mirror of
https://github.com/alkimake/paperclip.git
synced 2026-06-20 04:20:38 +09:00
[codex] Polish issue composer and long document display (#4420)
## Thinking Path > - Paperclip orchestrates AI agents for zero-human companies > - Issue comments and documents are the main working surface where operators and agents collaborate > - File drops, markdown editing, and long issue descriptions need to feel predictable because they sit directly in the task execution loop > - The composer had edge cases around drag targets, attachment feedback, image drops, and long markdown content crowding the page > - This pull request polishes the issue composer, hardens markdown editor regressions, and adds a fold curtain for long issue descriptions/documents > - The benefit is a calmer issue detail surface that handles uploads and long work products without hiding state or breaking layout ## What Changed - Scoped issue-composer drag/drop behavior so the composer owns file drops without turning the whole thread into a competing drop target. - Added clearer attachment upload feedback for non-image files and image-drop stability coverage. - Hardened markdown editor and markdown body handling around HTML-like tag regressions. - Added `FoldCurtain` and wired it into issue descriptions and issue documents so long markdown previews can expand/collapse. - Added Storybook coverage for the fold curtain state. ## Verification - `pnpm exec vitest run ui/src/components/IssueChatThread.test.tsx ui/src/components/MarkdownEditor.test.tsx ui/src/components/MarkdownBody.test.tsx --config ui/vitest.config.ts` passed: 3 files, 75 tests. - `git diff --check public-gh/master..pap-2228-editor-composer-polish -- . ':(exclude)ui/storybook-static'` passed. - Confirmed this PR does not include `pnpm-lock.yaml`. ## Risks - Low-to-medium risk: this changes user-facing composer/drop behavior and long markdown display. - The fold curtain uses DOM measurement and `ResizeObserver`; reviewers should check browser behavior for very long descriptions and documents. - No database migrations. > 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 coding agent based on GPT-5, with shell, git, Paperclip API, and GitHub CLI tool use in the local Paperclip workspace. ## 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 run tests locally and they pass - [x] I have added or updated tests where applicable - [x] If this change affects the UI, I have included before/after screenshots - [x] I have updated relevant documentation to reflect my changes - [x] I have considered and documented any risks above - [x] I will address all Greptile and reviewer comments before requesting merge Note: screenshots were not newly captured during branch splitting; the UI states are covered by component tests and a Storybook story. --------- Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com> Co-authored-by: Paperclip <noreply@paperclip.ing>
This commit is contained in:
parent
8f1cd0474f
commit
77a72e28c2
10 changed files with 839 additions and 54 deletions
|
|
@ -4,6 +4,7 @@ import type { Agent, CompanySecret, EnvBinding, Project, RoutineVariable } from
|
|||
import { Code2, FileText, ListPlus, RotateCcw, Table2 } from "lucide-react";
|
||||
import { EnvVarEditor } from "@/components/EnvVarEditor";
|
||||
import { ExecutionParticipantPicker } from "@/components/ExecutionParticipantPicker";
|
||||
import { FoldCurtain } from "@/components/FoldCurtain";
|
||||
import { InlineEditor } from "@/components/InlineEditor";
|
||||
import { InlineEntitySelector, type InlineEntityOption } from "@/components/InlineEntitySelector";
|
||||
import { JsonSchemaForm, type JsonSchemaNode, getDefaultValues } from "@/components/JsonSchemaForm";
|
||||
|
|
@ -710,3 +711,85 @@ export const RoutineRunVariablesDialogOpen: Story = {
|
|||
name: "Routine Run Variables Dialog",
|
||||
render: () => <RoutineRunDialogStory />,
|
||||
};
|
||||
|
||||
const foldCurtainLongMarkdown = [
|
||||
"# paperclip-bench",
|
||||
"",
|
||||
"Ship criteria for the benchmark harness — these notes are intentionally lengthy so the fold-curtain clips them.",
|
||||
"",
|
||||
"## Overview",
|
||||
"",
|
||||
"We need a benchmark that compares agent performance across task types and model backends. This includes:",
|
||||
"",
|
||||
"- a **runner** that executes tasks in isolated workspaces",
|
||||
"- a **scorer** that grades outputs against ground truth",
|
||||
"- a **dashboard** that trends metrics over time",
|
||||
"",
|
||||
"## Task format",
|
||||
"",
|
||||
"Each task is a directory containing a `task.md`, an optional `setup.sh`, and an `expected/` fixture. The runner mounts the task, executes the agent, and diffs the resulting workspace against `expected/`.",
|
||||
"",
|
||||
"```ts",
|
||||
"type TaskResult = {",
|
||||
" taskId: string;",
|
||||
" agent: string;",
|
||||
" exitCode: number;",
|
||||
" scoreBreakdown: Record<string, number>;",
|
||||
"};",
|
||||
"```",
|
||||
"",
|
||||
"## Metrics",
|
||||
"",
|
||||
"| Metric | Description |",
|
||||
"| --- | --- |",
|
||||
"| Pass@1 | First-try correctness |",
|
||||
"| Tokens | Cost per task |",
|
||||
"| Wall time | End-to-end minutes |",
|
||||
"",
|
||||
"## Next steps",
|
||||
"",
|
||||
"1. Land the runner with support for 3 task types.",
|
||||
"2. Backfill 50 tasks from open-source benchmarks.",
|
||||
"3. Wire the scorer to GitHub Actions.",
|
||||
"4. Publish baseline numbers on the main branch.",
|
||||
"",
|
||||
"All of this is described in more detail in the design doc linked from the home page.",
|
||||
].join("\n");
|
||||
|
||||
const foldCurtainShortMarkdown = "This description is short. No curtain should appear.";
|
||||
|
||||
function FoldCurtainStory() {
|
||||
return (
|
||||
<StoryShell>
|
||||
<Section
|
||||
eyebrow="Presentation"
|
||||
title="FoldCurtain"
|
||||
description="Long content collapses to a preview with a bottom fade and a Show more button. Short content renders untouched."
|
||||
>
|
||||
<div className="space-y-6">
|
||||
<StatePanel
|
||||
label="Long description (collapsed)"
|
||||
detail="Default state on every fresh page load. Natural height far exceeds the collapsed height, so the curtain activates."
|
||||
>
|
||||
<FoldCurtain>
|
||||
<MarkdownBody className="text-[15px] leading-7">{foldCurtainLongMarkdown}</MarkdownBody>
|
||||
</FoldCurtain>
|
||||
</StatePanel>
|
||||
<StatePanel
|
||||
label="Short description (no curtain)"
|
||||
detail="Content below the activation threshold renders with no curtain and no button."
|
||||
>
|
||||
<FoldCurtain>
|
||||
<MarkdownBody className="text-[15px] leading-7">{foldCurtainShortMarkdown}</MarkdownBody>
|
||||
</FoldCurtain>
|
||||
</StatePanel>
|
||||
</div>
|
||||
</Section>
|
||||
</StoryShell>
|
||||
);
|
||||
}
|
||||
|
||||
export const FoldCurtainShowcase: Story = {
|
||||
name: "Fold Curtain",
|
||||
render: () => <FoldCurtainStory />,
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue