Commit Graph
2 Commits
Author SHA1 Message Date
TonioandClaude Fable 5.1 3e7c3ee9a9 fix(ui): fall back to the icon face when an Artifacts V1 preview fails to load (#17260)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - Agents produce artifacts: documents, files, pictures, video and
links. The Artifacts V1 presentation (`enableArtifactsV1`, #17228) shows
them as prints, sheets and rows on the Artifacts page, in the panel's
Artifacts tab and in the task thread
> - A picture is printed from its attachment's content path. When that
content cannot be loaded (on staging, image work products whose
attachment returns 404), the browser draws its broken-picture glyph
inside the bordered print and inside the 40px row thumbnail
> - The presentation already has a face for a file with nothing to show:
the icon sheet (the faint glyph and the type label) and the row's
document icon. A picture that cannot be loaded should take that face,
not a browser glyph
> - This pull request listens for the element's own `error` event in the
V1 primitives and falls back to the icon face on all three surfaces: the
stack front and the single tile on the Artifacts page, and the panel and
thread rows
> - The benefit is that a missing or unreadable file reads as a file of
its type everywhere, with no fetch to find out, and nothing changes for
a picture that loads

## Linked Issues or Issue Description

No public issue exists. This section follows the bug report template.

**What happened?**

With Artifacts V1 on, an image work product whose attachment content
returns 404 ("Attachment not found") shows the browser's broken-image
glyph inside the bordered print on the Artifacts page (the stack front
and the single tile) and inside the 40px thumbnail in the panel and
thread rows.

**What did you expect to happen?**

The artifact shows the icon face a file without a preview gets: the icon
sheet with the faint glyph and the type label on the Artifacts page, and
the document icon with the type label in the rows.

**Steps to reproduce**

1. Turn on Settings > Experimental > Artifacts V1.
2. Register an image work product whose attachment content returns 404,
or delete the attachment behind one.
3. Open the Artifacts page, the task's Artifacts tab and the task
thread.

**Relevant subsystem**

The web UI (`ui/src/components/artifacts/v1/`).

## What Changed

- `ArtifactPrint`: a new `usePreviewFailure(source)` hook beside
`useImageAspect`, and an `onMediaError` prop wired to the `error` event
of the print's `<img>` and `<video>`. The hook remembers the source that
failed rather than a flag, so a front that moves on to another picture
(a stack's newest artifact) gets a fresh chance.
- `ArtifactFront`: owns the failure state for its subject and renders
the icon sheet (`IconSheet`, the `icon` treatment) once the print
reports an error; it passes the error on through its own `onMediaError`.
- `ArtifactStack`: on an error it stops fitting a print and stands the
icon sheet where a sheet stands in the media stack (the portrait anchor,
where audio stands), keeping the media backing.
- `ArtifactRow`: the thumbnail listens for `error` on its `<img>` /
`<video>` and gives way to `ArtifactRowIcon`, the document icon with the
type label.
- Until the picture loads the print stays the 4:3 placeholder, and a
picture that loads behaves as before. No fetch is made to detect a
failure.
- A failed video takes the plain icon sheet with no play disc, since the
stream it would promise cannot play; a video with no URL at all keeps
the existing media-fallback treatment with the disc.
- `index.ts` exports `usePreviewFailure`; the plan doc
`doc/plans/2026-10-22-artifacts-mvp-v1.md` records the fallback.
- Tests: `ArtifactTile.test.tsx` covers the single tile (4:3
placeholder, then the icon sheet after `error`), the picture stack (the
sheet stands where the audio stack's sheet stands, on the media backing)
and a video whose stream fails (icon sheet, no play disc).
`ArtifactRow.test.tsx` covers the thumbnail and the video thumbnail
giving way to the document icon, and a new source getting its own
chance.

## Verification

- `cd ui && npx vitest run src/components/artifacts/v1` — 6 files, 33
tests pass (5 new).
- `pnpm run check:token-gates` — exits 0: all four token gates clean,
palette tokens and the ClipLab character in sync. (The bare `pnpm
check:token-gates` form reports "Command not found" under pnpm 9.15.4 in
this worktree; `pnpm run` works.)
- `cd ui && pnpm typecheck` — exits 0 with no errors.
- Browser proof on a throwaway backend-free vite page (not committed)
rendering the primitives with a 404 picture, a 404 video, a picture that
loads and audio: on the Artifacts page the 404 picture's stack front is
the PNG icon sheet at the same position as the audio stack's sheet (left
37.89px, top 13.34px) on the media backing; the single tiles show the
PNG and WEBM icon sheets with no play disc while the loading picture
keeps its fitted 240×160 print; the rows show the PNG and WEBM document
icons with no `<img>`/`<video>` left, while the loading picture's row
keeps its thumbnail. Not run: the browser suites (`pnpm test:e2e`, `pnpm
test:release-smoke`) and a live staging check.

States enumerated: loading (4:3 placeholder print, unchanged), loaded
(fitted print, unchanged), failed picture (icon sheet / document icon),
failed poster-less video (icon sheet without play disc / document icon),
source changed after a failure (print again).

## Risks

- Low risk. The change is behind `enableArtifactsV1` and only acts on
the element's `error` event, which fires for a 404, a network failure,
or content that is not decodable as a picture; nothing is fetched to
find out.
- `ArtifactFront` and `ArtifactStack` each keep the failure state for
their subject (as both already keep the loaded aspect), set from the
same event in one render; a consumer that renders `ArtifactFront` with a
print size but ignores `onMediaError` still gets the icon face, only at
the print's position.
- A picture that fails once and later becomes readable at the same URL
keeps the icon face until the surface remounts or the subject moves on
to another source; the data does not change under a mounted tile in
practice.

> 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

- Claude (Anthropic), model ID `claude-fable-5-1`, extended thinking,
running in Claude Code (desktop app) with tool use: file editing, shell,
Vitest, and the built-in browser for the proof page.

## 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
- [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-26 21:08:49 -07:00
TonioandClaude Fable 5.1 341b622604 feat(ui): Artifacts V1 behind an experimental setting — Artifacts page, panel tab and task thread (#17228)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - Agents produce artifacts: documents, files, pictures, video and
links. The app shows them on the Artifacts page, in the right panel's
Artifacts tab and in the task thread
> - The artifact directions lab (#17213) settled a presentation for
those surfaces, MVP V1, drawn from the PCLP-Core mocks. It is a
reference, not product code
> - Shipping it means product components on product data, token-only,
behind a switch, one surface at a time, so the current presentation
stays the default until the new one has been used
> - This pull request adds the switch (Settings > Experimental >
Artifacts V1), the `--artifact-*` tokens, the V1 primitives as product
components, and the V1 presentation on all three surfaces behind the
switch: the Artifacts page, the panel's Artifacts tab and the task
thread
> - The benefit is the redesign in the product, reversible by one
setting, with every existing behaviour of those surfaces kept

## Linked Issues or Issue Description

No public issue exists. This section follows the feature request
template; the plan is `doc/plans/2026-10-22-artifacts-mvp-v1.md`.

**Subsystem affected**

The web UI (`ui/`): the Artifacts page, the right panel's Artifacts tab
and the task thread. The experimental settings contract in
`packages/shared` and `server` gains one boolean.

**Problem or motivation**

The three artifact surfaces grew separately and their artifact
presentation is dated and inconsistent. The lab produced a measured
direction; nothing in the product uses it yet.

**Proposed solution**

An experimental setting, `enableArtifactsV1`, default off, with the same
plumbing as the other experimental settings. Product primitives under
`ui/src/components/artifacts/v1` (kind helpers and adapters, the sheet
and print artwork, the stack, the page tiles with the Paper Mono pill,
the row the panel and thread will share), driven by `--artifact-*`
tokens in `index.css` with light and dark values. With the setting on:
the Artifacts page shows one row of views (Tasks, All files, Images,
Videos, Documents, Text, Files) and the V1 tiles, a task with several
artifacts as a stack whose newest artifact is the first page, everything
else as single tiles; the panel's Artifacts tab is one flat list of V1
rows, newest first, with the expandable rows (Markdown review surface,
document annotations, deep links) keeping their behaviour behind the row
as their face; the thread shows a message's files as rows under its
footer and registered deliverables and documents as one row each. The
hover movement respects reduced motion.

**Alternatives considered**

Replacing the current cards outright: no fallback and three surfaces to
land at once. Mounting the lab's components: they are typed on lab
fixtures and keep lab-local tokens, which the token gates reject. The
primitives were ported instead and the lab stays a reference.

**Roadmap alignment**

Supports the "Artifacts & Work Products" theme in `ROADMAP.md`.
Presentation only; no data model change.

**Additional context**

No new API fields: the tiles show title, identifier, count and "Updated
…"; rows take sizes from the work-product and attachment payloads; a
picture's aspect ratio comes from the loaded image, 4:3 until it loads.
The plan records what each surface keeps and what it drops (run sections
in the panel; the "Images · N / Files · N" labels and the four-up grid
in the thread).

## What Changed

- Add the `enableArtifactsV1` experimental setting: shared type,
validator and feature catalog, server defaults and managed keys, the
`useArtifactsV1Enabled` hook, and the Settings > Experimental toggle (in
its alphabetical place).
- Add the `--artifact-*` tokens and the Artifacts V1 component rules to
`ui/src/index.css`: light values on `:root`, dark overrides, and the
hover movement under `prefers-reduced-motion: no-preference`.
- Add `ui/src/components/artifacts/v1/`: `kinds.ts` (kind treatment,
labels, glyphs, the server's media-kind rule, adapters from
`CompanyArtifact`), `geometry.ts` (the measured constants), `when.ts`,
`ArtifactSheet`, `ArtifactPrint`, `ArtifactStack`, `ArtifactFront`,
`ArtifactTile` (stack and single tiles, caption, grid), `ArtifactRow`
(the row and its inline group), `ArtifactsV1Filters`, with the mock's
SVG assets.
- Wire the Artifacts page: with the setting on, the toolbar's filter row
becomes the V1 views and the grid renders V1 tiles; the toolbar takes an
optional `filters` row for this. The old cards and kind filters stay the
default.
- Wire the panel's Artifacts tab (`IssuePropertiesArtifactsTab`): with
the setting on, one flat list of V1 rows, newest first; `DocumentRow`
and `MarkdownWorkProductRow` take a `face` render prop so the V1 row
fronts their unchanged expansion, review and deep-link behaviour;
`PanelFileRow` opens attachments and attachment-backed work products the
way the shipped card does (gallery, text tab, new tab) with open and
download beside the name; other work products are a link row with their
status.
- Wire the task thread: `TaskChatBubble` hangs a message's files under
its footer as V1 rows inline (pictures and video to the gallery, text to
a text tab, files to a new tab) in place of the media grid and file
chips; `TaskChatProtocolCard`'s resource rows render deliverables,
documents and attachments as one V1 row each, opening where the card
did.
- `ArtifactRow` keeps trailing controls beside its own control (no
nested interactive elements) and can be a link, an in-app anchor, a
new-tab anchor or a button; adapters read work products, documents,
attachments and message references.
- Add a catalogued Storybook story, `Components/Artifact V1 primitives`,
under `documents-artifacts`, on fixtures.
- Add tests: the primitives (kinds, dates, print fitting, tiles, rows),
the hook, the Artifacts page, the panel's Artifacts tab, the task-chat
bubble and the protocol card, each with the setting on and off; the
settings page and server tests list the new key.
- Add `doc/plans/2026-10-22-artifacts-mvp-v1.md`.

## Verification

- Run `cd ui && pnpm typecheck`: the Storybook catalog accepts the new
story, and `tsc -b` is clean apart from the five pre-existing
`PluginOrganizationSwitcher` errors.
- Run `pnpm check:token-gates` and `node scripts/check-maps.mjs`: clean.
- Run `cd ui && npx vitest run src/components/artifacts
src/components/issue-properties
src/components/task-chat/TaskChatBubble.test.tsx
src/components/task-chat/TaskChatBubble.artifactsV1.test.tsx
src/components/task-chat/TaskChatProtocolCard.test.tsx
src/components/task-chat/TaskChatProtocolCard.artifactsV1.test.tsx
src/pages/Artifacts.test.tsx src/hooks/useArtifactsV1Enabled.test.ts
src/pages/InstanceExperimentalSettings.test.tsx`: the suites pass (151
tests in the last run).
- Run `cd packages/shared && npx vitest run src/feature-catalog.test.ts`
and `cd server && npx vitest run
src/__tests__/instance-settings-service.test.ts`: pass.
- Manual: open Settings > Experimental, turn on Artifacts V1. On the
Artifacts page the view row reads Tasks, All files, Images, Videos,
Documents, Text, Files; tasks with several artifacts show as stacks,
hovering fans the pages and lifts the artwork (not under reduced
motion); Images shows single tiles with bordered prints. Open a task's
Artifacts tab: one list of rows, newest first; a Markdown work product
still expands into its review surface, a document still opens in its
tab. In the thread, a message's files sit under its footer as rows. Turn
the setting off: everything returns to the current presentation.
- Storybook: `Tasks and conversations / Documents, attachments, and work
products / Components / Artifact V1 primitives`.
- Not run: the full `pnpm test:run` and `pnpm build` (CI runs them), and
a browser pass against a dev instance with real artifacts on the three
surfaces; that pass is the remaining step before this leaves draft.

## Risks

- Off by default; with the setting off, the only changes on a product
path are an optional `filters` prop on the toolbar, an optional `face`
prop on two panel rows, a read of the setting in the bubble and the
protocol card, and the new token declarations.
- With the setting on, the Artifacts page's view tabs set two URL
parameters (`groupBy`, `kind`) at once; the group-by menu and search are
unchanged.
- A picture with unknown dimensions renders at 4:3 until it loads, then
the print refits once; the frame's height is fixed so nothing below it
moves.
- About thirty new `--artifact-*` tokens, namespaced and documented in
place.

## Model Used

- Claude Fable 5.1 by Anthropic, model ID `claude-fable-5-1`, used
through Claude Code in the desktop app. Extended reasoning was on.
Capabilities used: tool use (shell, file edits, a headless Playwright
browser for the lab's measurements, the Figma MCP server for the mocks
during the lab work) and image input. The context window is the Claude
Code session's; its exact size is not surfaced to the session.

## 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 (#17213 is the lab this builds on)
- [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 (the suites listed above;
CI runs the full set)
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes (the
plan document; no command or product behaviour changes outside the
setting)
- [x] I have considered and documented any risks above
- [ ] All Paperclip CI gates are green
- [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-22 15:14:58 -07:00