# Service Account Article Modal Implementation Plan

> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.

**Goal:** Make page modals dismissible from the backdrop without leaving pointer-focus outlines, and redesign the service-account article reader around the approved compact header and editorial footer.

**Architecture:** Keep dismissal and focus restoration policy in the shared `PageModal` shell. Keep article-specific chrome, publisher metadata, reading measure, and layout in `ServiceAccountArticlePreview`; pass real author and timestamp data from `MessageRow`, while the service-account editor preview supplies the account name only.

**Tech Stack:** React 19, TypeScript, Base UI Dialog, Tailwind CSS, Node test runner with Happy DOM.

---

### Task 1: PageModal dismissal and focus policy

**Files:**
- Modify: `web/src/agentteam/components/PageModal.tsx`
- Modify: `web/src/agentteam/components/PageModal.dom.test.tsx`

- [ ] Change the backdrop test to require an outside pointer press to close the modal.
- [ ] Add a pointer-close assertion that the opener is not programmatically refocused.
- [ ] Preserve the existing Escape assertion that keyboard close restores opener focus.
- [ ] Set `disablePointerDismissal` to `false` by default and configure `DialogContent.finalFocus` to use default focus restoration only for keyboard close.
- [ ] Run `npx tsx --test src/agentteam/components/PageModal.dom.test.tsx`.

### Task 2: Article reader presentation

**Files:**
- Modify: `web/src/agentteam/workspace/conversation/components/message-row/ServiceAccountArticlePreview.tsx`
- Modify: `web/src/agentteam/workspace/conversation/components/message-row/ServiceAccountArticlePreview.test.ts`
- Modify: `web/src/agentteam/workspace/shared/serviceAccountArticles.ts`
- Modify: `web/src/agentteam/workspace/shared/serviceAccountArticles.test.ts`

- [ ] Add a deterministic reading-time presenter based on plain CJK characters and Latin words.
- [ ] Replace the floating close button and large body title with a fixed 62px `DialogHeader`.
- [ ] Render the title with the shared `DialogTitle` typography (`text-base`, `leading-6`, semibold), one-line truncation, and a native full-title tooltip.
- [ ] Keep `DialogBody` as the only scroll region and constrain article copy to `68ch`.
- [ ] Add the publisher footer after article content with service avatar, account name, service-account badge, published timestamp when available, and estimated reading time.
- [ ] Keep the modal reader at the approved article width while retaining the shared PageModal height and responsive full-page fallback.
- [ ] Extend existing tests for header height, title typography, bounded scrolling, prose width, footer identity, and reading-time behavior.

### Task 3: Supply real publisher metadata

**Files:**
- Modify: `web/src/agentteam/workspace/conversation/components/MessageRow.tsx`
- Modify: `web/src/agentteam/workspace/service-accounts/ServiceAccountArticleEditorDialog.tsx`
- Modify: `web/src/agentteam/i18n/locales/en/workspace/messaging.json`
- Modify: `web/src/agentteam/i18n/locales/zh-CN/workspace/messaging.json`
- Modify: `web/src/agentteam/i18n/locales/ja/workspace/messaging.json`

- [ ] Pass `originalDisplayName`, `authorAvatarUrl`, and `message.createdAt` from a delivered service-account message.
- [ ] Pass `accountName` from the editor's live preview and omit a fake publication timestamp.
- [ ] Add localized publication and reading-time labels in all workspace locales.
- [ ] Run the service-account article, shared presenter, route, and MessageRow source tests.

### Task 4: Verification and review

**Files:**
- Verify all modified files.

- [ ] Run focused DOM and unit tests.
- [ ] Run `npm run typecheck`.
- [ ] Run ESLint on modified TypeScript/TSX files.
- [ ] Run the repository test-quality guardrail without increasing its ratchets.
- [ ] Commit and push the feature branch.
- [ ] Add AgentTeam review context, ask `agentteam-glm4` for review, address findings, create the PR, subscribe with `pr-watch`, then send `/merge`.
