docs(14): create phase plan for Chat Integration & Display
Phase 14: Chat Integration & Display - 2 plan(s) in 1 wave(s) - 2 parallel (wave 1), 0 sequential - Extends JKChatMessage for attachment display - Adds clickable links with signed URL fetching - Human verification checkpoint in plan 02 - Ready for execution
This commit is contained in:
parent
45e284096a
commit
68229dd003
|
|
@ -55,8 +55,8 @@ Decimal phases appear between their surrounding integers in numeric order.
|
|||
|
||||
**Milestone Goal:** Add file attachment capability to music sessions, allowing users to upload files from their computer and view them in the chat window with real-time synchronization across all session participants.
|
||||
|
||||
- [ ] **Phase 12: Attachment Research & Backend Validation** - Explore legacy attachment implementation, validate existing backend infrastructure
|
||||
- [ ] **Phase 13: File Upload Infrastructure** - Attach button, file dialog, validation, S3 upload with progress
|
||||
- [x] **Phase 12: Attachment Research & Backend Validation** - Explore legacy attachment implementation, validate existing backend infrastructure
|
||||
- [x] **Phase 13: File Upload Infrastructure** - Attach button, file dialog, validation, S3 upload with progress
|
||||
- [ ] **Phase 14: Chat Integration & Display** - Display attachments as messages in chat window
|
||||
- [ ] **Phase 15: Real-time Synchronization** - WebSocket broadcast and attachment history
|
||||
- [ ] **Phase 16: Attachment Finalization** - Error handling, edge cases, UAT
|
||||
|
|
@ -259,8 +259,8 @@ Plans:
|
|||
9. Attachment messages display correctly at different window sizes (responsive layout, long filename truncation)
|
||||
|
||||
Plans:
|
||||
- [ ] 14-01: Attachment message display with metadata and styling
|
||||
- [ ] 14-02: Clickable links, chat history integration, responsive layout
|
||||
- [ ] 14-01-PLAN.md — Attachment message display with metadata and styling
|
||||
- [ ] 14-02-PLAN.md — Clickable links, chat history integration, responsive layout
|
||||
|
||||
#### Phase 15: Real-time Synchronization
|
||||
**Goal**: New attachments broadcast to all session participants in real-time via WebSocket
|
||||
|
|
@ -320,6 +320,6 @@ Phases execute in numeric order: 1 → 2 → 3 → 4 → 5 → 6 → 7 → 8 →
|
|||
| 11. Chat Finalization | v1.1 | 2/2 | Complete | 2026-01-31 |
|
||||
| 12. Attachment Research & Backend Validation | v1.2 | 2/2 | Complete | 2026-02-02 |
|
||||
| 13. File Upload Infrastructure | v1.2 | 3/3 | Complete | 2026-02-05 |
|
||||
| 14. Chat Integration & Display | v1.2 | 0/2 | Not started | - |
|
||||
| 14. Chat Integration & Display | v1.2 | 0/2 | Planned | - |
|
||||
| 15. Real-time Synchronization | v1.2 | 0/1 | Not started | - |
|
||||
| 16. Attachment Finalization | v1.2 | 0/2 | Not started | - |
|
||||
|
|
|
|||
|
|
@ -0,0 +1,237 @@
|
|||
---
|
||||
phase: 14-chat-integration-and-display
|
||||
plan: 01
|
||||
type: execute
|
||||
wave: 1
|
||||
depends_on: []
|
||||
files_modified:
|
||||
- jam-ui/src/components/client/chat/JKChatMessage.js
|
||||
- jam-ui/src/components/client/JKSessionScreen.js
|
||||
autonomous: true
|
||||
|
||||
must_haves:
|
||||
truths:
|
||||
- "Attachment messages display with '[UserName] attached [FileName]' format"
|
||||
- "Attachment messages include file size (KB/MB), uploader name, and timestamp"
|
||||
- "Attachment messages have visual distinction from regular text messages (paperclip icon, different styling)"
|
||||
- "Attachment messages sort chronologically with regular chat messages"
|
||||
artifacts:
|
||||
- path: "jam-ui/src/components/client/chat/JKChatMessage.js"
|
||||
provides: "Attachment message rendering with metadata display"
|
||||
contains: "attachmentId"
|
||||
- path: "jam-ui/src/components/client/JKSessionScreen.js"
|
||||
provides: "WebSocket message transformation including attachment fields"
|
||||
contains: "attachment_id"
|
||||
key_links:
|
||||
- from: "JKSessionScreen.js"
|
||||
to: "sessionChatSlice"
|
||||
via: "addMessageFromWebSocket with attachment fields"
|
||||
pattern: "attachmentId.*attachmentName.*attachmentSize"
|
||||
- from: "JKChatMessage.js"
|
||||
to: "formatFileSize"
|
||||
via: "import from attachmentValidation"
|
||||
pattern: "formatFileSize.*attachmentSize"
|
||||
---
|
||||
|
||||
<objective>
|
||||
Extend JKChatMessage component to detect and display attachment messages with metadata and visual distinction from regular text messages.
|
||||
|
||||
Purpose: Users need to distinguish attachment messages from regular chat messages and see file metadata (name, size, uploader, timestamp) at a glance.
|
||||
|
||||
Output: JKChatMessage renders attachment messages with paperclip icon, styled differently from text messages, showing all required metadata.
|
||||
</objective>
|
||||
|
||||
<execution_context>
|
||||
@/Users/nuwan/.claude/get-shit-done/workflows/execute-plan.md
|
||||
@/Users/nuwan/.claude/get-shit-done/templates/summary.md
|
||||
</execution_context>
|
||||
|
||||
<context>
|
||||
@.planning/PROJECT.md
|
||||
@.planning/ROADMAP.md
|
||||
@.planning/STATE.md
|
||||
@.planning/phases/13-file-upload-infrastructure/13-03-SUMMARY.md
|
||||
@jam-ui/src/components/client/chat/JKChatMessage.js
|
||||
@jam-ui/src/components/client/JKSessionScreen.js
|
||||
@jam-ui/src/services/attachmentValidation.js
|
||||
</context>
|
||||
|
||||
<tasks>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 1: Extend WebSocket message transformation to include attachment fields</name>
|
||||
<files>jam-ui/src/components/client/JKSessionScreen.js</files>
|
||||
<action>
|
||||
Find the CHAT_MESSAGE WebSocket handler in handleWebSocketMessage function. The handler transforms Protocol Buffer payload to Redux format. Extend the message transformation to include attachment fields from the WebSocket payload:
|
||||
|
||||
```javascript
|
||||
// In CHAT_MESSAGE handler, extend the message object construction:
|
||||
const message = {
|
||||
id: payload.msg_id,
|
||||
senderId: payload.user_id,
|
||||
senderName: payload.user_name,
|
||||
message: payload.message,
|
||||
createdAt: payload.created_at,
|
||||
channel: payload.channel,
|
||||
sessionId: payload.session_id,
|
||||
// NEW: Attachment fields (null/undefined if not an attachment)
|
||||
purpose: payload.purpose, // 'Notation File', 'Audio File', or undefined
|
||||
attachmentId: payload.attachment_id, // MusicNotation UUID
|
||||
attachmentType: payload.attachment_type, // 'notation' or 'audio'
|
||||
attachmentName: payload.attachment_name, // filename
|
||||
attachmentSize: payload.attachment_size // file size in bytes (may be null)
|
||||
};
|
||||
```
|
||||
|
||||
The backend ChatMessage.send_chat_msg includes these fields. The payload structure matches Protocol Buffer definitions in pb/src/client_container.proto.
|
||||
|
||||
Note: attachmentSize may not be present in current WebSocket payload. If backend doesn't send it, we'll handle gracefully (show filename without size). Check if size is available; if not, we can fetch it later or display without size.
|
||||
</action>
|
||||
<verify>
|
||||
Review the code to confirm attachment fields are mapped correctly. The message object passed to addMessageFromWebSocket should include attachmentId, attachmentName, attachmentType, purpose, and attachmentSize (if available).
|
||||
</verify>
|
||||
<done>
|
||||
WebSocket CHAT_MESSAGE handler transforms attachment fields into Redux message format. Messages with attachments have attachmentId, attachmentName, attachmentType, and purpose set; regular messages have these as undefined.
|
||||
</done>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 2: Extend JKChatMessage to render attachment messages with metadata</name>
|
||||
<files>jam-ui/src/components/client/chat/JKChatMessage.js</files>
|
||||
<action>
|
||||
Modify JKChatMessage to detect and render attachment messages differently from regular text messages.
|
||||
|
||||
1. Import formatFileSize from attachmentValidation service:
|
||||
```javascript
|
||||
import { formatFileSize } from '../../../services/attachmentValidation';
|
||||
```
|
||||
|
||||
2. Add helper function to determine if message is an attachment:
|
||||
```javascript
|
||||
const isAttachmentMessage = (message) => {
|
||||
return message.attachmentId && message.attachmentName;
|
||||
};
|
||||
```
|
||||
|
||||
3. Create attachment-specific render section inside the component. When message has attachmentId, render:
|
||||
- Paperclip icon (use Unicode character or simple text indicator: "[File]" or "📎")
|
||||
- Format: "[senderName] attached [attachmentName]"
|
||||
- File size in parentheses if available: "(2.5 MB)"
|
||||
- Timestamp (using existing formatTimestamp)
|
||||
- Visual styling: Different background color (light blue #e3f2fd) to distinguish from regular messages
|
||||
|
||||
4. Keep existing rendering for regular text messages unchanged.
|
||||
|
||||
5. Update PropTypes to include new optional fields:
|
||||
```javascript
|
||||
JKChatMessage.propTypes = {
|
||||
message: PropTypes.shape({
|
||||
id: PropTypes.string.isRequired,
|
||||
senderId: PropTypes.string.isRequired,
|
||||
senderName: PropTypes.string,
|
||||
message: PropTypes.string, // Optional for attachment-only messages
|
||||
createdAt: PropTypes.string.isRequired,
|
||||
// Attachment fields (optional)
|
||||
attachmentId: PropTypes.string,
|
||||
attachmentName: PropTypes.string,
|
||||
attachmentType: PropTypes.oneOf(['notation', 'audio']),
|
||||
purpose: PropTypes.string,
|
||||
attachmentSize: PropTypes.number
|
||||
}).isRequired
|
||||
};
|
||||
```
|
||||
|
||||
6. Styling for attachment message (inline styles, consistent with existing pattern):
|
||||
```javascript
|
||||
const attachmentStyle = {
|
||||
display: 'flex',
|
||||
gap: '12px',
|
||||
marginBottom: '16px',
|
||||
padding: '8px',
|
||||
borderRadius: '4px',
|
||||
backgroundColor: '#e3f2fd', // Light blue to distinguish from regular messages (#f8f9fa)
|
||||
border: '1px solid #bbdefb'
|
||||
};
|
||||
|
||||
const fileIconStyle = {
|
||||
fontSize: '16px',
|
||||
marginRight: '4px'
|
||||
};
|
||||
```
|
||||
|
||||
7. Conditional rendering structure:
|
||||
```javascript
|
||||
if (isAttachmentMessage(message)) {
|
||||
return (
|
||||
<div style={attachmentStyle}>
|
||||
{/* Avatar */}
|
||||
<div style={avatarStyle}>
|
||||
{getInitials(senderName)}
|
||||
</div>
|
||||
|
||||
{/* Attachment content */}
|
||||
<div style={{ flex: 1, minWidth: 0 }}>
|
||||
<div style={{ display: 'flex', alignItems: 'baseline', gap: '8px', marginBottom: '4px' }}>
|
||||
<span style={{ fontWeight: 600, fontSize: '14px', color: '#212529' }}>
|
||||
{senderName || 'Unknown'}
|
||||
</span>
|
||||
<span style={{ fontSize: '12px', color: '#6c757d' }}>
|
||||
{formatTimestamp(createdAt)}
|
||||
</span>
|
||||
</div>
|
||||
<div style={{ fontSize: '14px', color: '#212529', display: 'flex', alignItems: 'center' }}>
|
||||
<span style={fileIconStyle}>📎</span>
|
||||
<span>
|
||||
attached <strong>{attachmentName}</strong>
|
||||
{attachmentSize && <span style={{ color: '#6c757d', marginLeft: '4px' }}>({formatFileSize(attachmentSize)})</span>}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
// ... existing regular message rendering
|
||||
```
|
||||
|
||||
React.memo should remain in place for performance optimization.
|
||||
</action>
|
||||
<verify>
|
||||
1. Code compiles without errors
|
||||
2. Regular text messages render unchanged
|
||||
3. Attachment messages render with paperclip icon, "[name] attached [filename]" format
|
||||
4. File size displays when available (formatted as KB/MB)
|
||||
5. Visual distinction: attachment messages have light blue background
|
||||
</verify>
|
||||
<done>
|
||||
JKChatMessage detects attachment messages and renders them with distinct styling, paperclip icon, sender name, filename, size (if available), and timestamp. Regular messages render unchanged.
|
||||
</done>
|
||||
</task>
|
||||
|
||||
</tasks>
|
||||
|
||||
<verification>
|
||||
1. Start dev server: `cd jam-ui && npm start`
|
||||
2. Join a music session
|
||||
3. Upload a file using Attach button (from Phase 13)
|
||||
4. Verify attachment message appears in chat with:
|
||||
- Light blue background (distinct from regular messages)
|
||||
- Paperclip icon
|
||||
- "[YourName] attached [filename]" format
|
||||
- Timestamp
|
||||
5. Send regular text message
|
||||
6. Verify text message renders normally (gray background, no icon)
|
||||
7. Verify both messages appear in chronological order
|
||||
</verification>
|
||||
|
||||
<success_criteria>
|
||||
- Attachment messages display with "[UserName] attached [FileName]" format
|
||||
- Attachment messages have visual distinction (paperclip icon, light blue background)
|
||||
- File size displays if available (formatted as KB/MB)
|
||||
- Timestamp displays using existing formatTimestamp utility
|
||||
- Regular text messages render unchanged
|
||||
- Messages sort chronologically (existing behavior preserved)
|
||||
</success_criteria>
|
||||
|
||||
<output>
|
||||
After completion, create `.planning/phases/14-chat-integration-and-display/14-01-SUMMARY.md`
|
||||
</output>
|
||||
|
|
@ -0,0 +1,321 @@
|
|||
---
|
||||
phase: 14-chat-integration-and-display
|
||||
plan: 02
|
||||
type: execute
|
||||
wave: 1
|
||||
depends_on: []
|
||||
files_modified:
|
||||
- jam-ui/src/components/client/chat/JKChatMessage.js
|
||||
- jam-ui/src/helpers/rest.js
|
||||
autonomous: false
|
||||
|
||||
must_haves:
|
||||
truths:
|
||||
- "Filename is clickable link that opens file in new browser tab"
|
||||
- "Browser handles view/download based on file type (PDF viewer, image display, audio player)"
|
||||
- "Chat history includes attachments (visible when joining session, persists across refresh)"
|
||||
- "Long filenames truncate with ellipsis at appropriate window sizes"
|
||||
- "Attachment messages display correctly at different window sizes (responsive)"
|
||||
artifacts:
|
||||
- path: "jam-ui/src/components/client/chat/JKChatMessage.js"
|
||||
provides: "Clickable attachment links with signed URL fetching"
|
||||
contains: "handleAttachmentClick"
|
||||
- path: "jam-ui/src/helpers/rest.js"
|
||||
provides: "getMusicNotationUrl helper for signed S3 URLs"
|
||||
contains: "getMusicNotationUrl"
|
||||
key_links:
|
||||
- from: "JKChatMessage.js"
|
||||
to: "getMusicNotationUrl"
|
||||
via: "import and fetch on click"
|
||||
pattern: "getMusicNotationUrl.*attachmentId"
|
||||
- from: "JKChatMessage.js"
|
||||
to: "window.open"
|
||||
via: "open signed URL in new tab"
|
||||
pattern: "window\\.open.*_blank"
|
||||
---
|
||||
|
||||
<objective>
|
||||
Add clickable attachment links with signed URL fetching and responsive layout for attachment messages.
|
||||
|
||||
Purpose: Users need to click attachment filenames to view/download files, and the UI must work well at different window sizes with proper filename truncation.
|
||||
|
||||
Output: Attachment filenames are clickable links that fetch signed S3 URLs and open files in new browser tabs. Long filenames truncate gracefully, and layout is responsive.
|
||||
</objective>
|
||||
|
||||
<execution_context>
|
||||
@/Users/nuwan/.claude/get-shit-done/workflows/execute-plan.md
|
||||
@/Users/nuwan/.claude/get-shit-done/templates/summary.md
|
||||
</execution_context>
|
||||
|
||||
<context>
|
||||
@.planning/PROJECT.md
|
||||
@.planning/ROADMAP.md
|
||||
@.planning/STATE.md
|
||||
@.planning/phases/14-chat-integration-and-display/14-01-PLAN.md
|
||||
@jam-ui/src/components/client/chat/JKChatMessage.js
|
||||
@jam-ui/src/helpers/rest.js
|
||||
@.planning/phases/12-attachment-research-&-backend-validation/12-RESEARCH.md
|
||||
</context>
|
||||
|
||||
<tasks>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 1: Add click handler to fetch signed URL and open file</name>
|
||||
<files>jam-ui/src/components/client/chat/JKChatMessage.js</files>
|
||||
<action>
|
||||
Add click handler for attachment filename that:
|
||||
1. Fetches signed S3 URL via getMusicNotationUrl REST helper
|
||||
2. Opens URL in new browser tab (target="_blank")
|
||||
|
||||
Import the REST helper:
|
||||
```javascript
|
||||
import { getMusicNotationUrl } from '../../../helpers/rest';
|
||||
```
|
||||
|
||||
Add click handler inside the component (wrap in useCallback for memoization):
|
||||
```javascript
|
||||
import React, { useCallback, useState } from 'react';
|
||||
|
||||
// Inside component:
|
||||
const [isLoadingUrl, setIsLoadingUrl] = useState(false);
|
||||
|
||||
const handleAttachmentClick = useCallback(async (attachmentId) => {
|
||||
if (isLoadingUrl) return; // Prevent rapid clicks
|
||||
|
||||
try {
|
||||
setIsLoadingUrl(true);
|
||||
const response = await getMusicNotationUrl(attachmentId);
|
||||
// Response is { url: "https://s3.amazonaws.com/...?signature=..." }
|
||||
if (response && response.url) {
|
||||
window.open(response.url, '_blank');
|
||||
} else {
|
||||
console.error('No URL in response:', response);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch attachment URL:', error);
|
||||
// Could show toast error here, but keep it simple for now
|
||||
} finally {
|
||||
setIsLoadingUrl(false);
|
||||
}
|
||||
}, [isLoadingUrl]);
|
||||
```
|
||||
|
||||
Update the attachment name rendering to be a clickable link:
|
||||
```javascript
|
||||
<span
|
||||
onClick={() => handleAttachmentClick(message.attachmentId)}
|
||||
style={{
|
||||
cursor: 'pointer',
|
||||
color: '#1976d2',
|
||||
textDecoration: 'underline',
|
||||
// Truncation for long filenames
|
||||
maxWidth: '200px',
|
||||
overflow: 'hidden',
|
||||
textOverflow: 'ellipsis',
|
||||
whiteSpace: 'nowrap',
|
||||
display: 'inline-block',
|
||||
verticalAlign: 'bottom'
|
||||
}}
|
||||
title={message.attachmentName} // Full filename on hover
|
||||
>
|
||||
{message.attachmentName}
|
||||
</span>
|
||||
```
|
||||
|
||||
Note: Browser will handle file display based on Content-Type returned by S3:
|
||||
- PDF: Opens in browser's PDF viewer
|
||||
- Images (PNG, JPG, GIF): Displays in browser
|
||||
- Audio (MP3, WAV): Opens browser's audio player
|
||||
- Other types (XML, TXT): Typically downloads
|
||||
|
||||
The backend sets Content-Disposition based on file type.
|
||||
</action>
|
||||
<verify>
|
||||
1. Code compiles without errors
|
||||
2. Clicking attachment filename triggers handleAttachmentClick
|
||||
3. isLoadingUrl state prevents rapid multiple clicks
|
||||
4. Error handling logs to console (no crash on failure)
|
||||
</verify>
|
||||
<done>
|
||||
Attachment filenames are clickable links that fetch signed S3 URLs and open in new browser tabs. Loading state prevents rapid clicks. Errors logged to console without crashing.
|
||||
</done>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 2: Add responsive styling with filename truncation</name>
|
||||
<files>jam-ui/src/components/client/chat/JKChatMessage.js</files>
|
||||
<action>
|
||||
Enhance attachment message styling for responsive behavior:
|
||||
|
||||
1. Update attachment content container for better responsiveness:
|
||||
```javascript
|
||||
const attachmentContentStyle = {
|
||||
flex: 1,
|
||||
minWidth: 0, // Required for text-overflow to work in flex containers
|
||||
overflow: 'hidden'
|
||||
};
|
||||
|
||||
const attachmentTextStyle = {
|
||||
fontSize: '14px',
|
||||
color: '#212529',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
flexWrap: 'wrap', // Allow wrapping on very narrow windows
|
||||
gap: '4px'
|
||||
};
|
||||
|
||||
const filenameLinkStyle = {
|
||||
cursor: isLoadingUrl ? 'wait' : 'pointer',
|
||||
color: '#1976d2',
|
||||
textDecoration: 'underline',
|
||||
fontWeight: 600,
|
||||
// Truncation
|
||||
maxWidth: '100%', // Take available width
|
||||
overflow: 'hidden',
|
||||
textOverflow: 'ellipsis',
|
||||
whiteSpace: 'nowrap',
|
||||
display: 'inline-block',
|
||||
verticalAlign: 'bottom'
|
||||
};
|
||||
|
||||
const fileSizeStyle = {
|
||||
color: '#6c757d',
|
||||
fontSize: '13px',
|
||||
flexShrink: 0 // Prevent size from shrinking
|
||||
};
|
||||
```
|
||||
|
||||
2. Update the attachment render:
|
||||
```javascript
|
||||
if (isAttachmentMessage(message)) {
|
||||
const { senderName, createdAt, attachmentId, attachmentName, attachmentSize } = message;
|
||||
|
||||
return (
|
||||
<div style={{
|
||||
display: 'flex',
|
||||
gap: '12px',
|
||||
marginBottom: '16px',
|
||||
padding: '8px',
|
||||
borderRadius: '4px',
|
||||
backgroundColor: '#e3f2fd',
|
||||
border: '1px solid #bbdefb'
|
||||
}}>
|
||||
{/* Avatar */}
|
||||
<div style={{
|
||||
width: '36px',
|
||||
height: '36px',
|
||||
borderRadius: '50%',
|
||||
backgroundColor: '#007bff',
|
||||
color: 'white',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
fontWeight: 'bold',
|
||||
fontSize: '14px',
|
||||
flexShrink: 0
|
||||
}}>
|
||||
{getInitials(senderName)}
|
||||
</div>
|
||||
|
||||
{/* Content */}
|
||||
<div style={attachmentContentStyle}>
|
||||
{/* Header: Name + Timestamp */}
|
||||
<div style={{ display: 'flex', alignItems: 'baseline', gap: '8px', marginBottom: '4px', flexWrap: 'wrap' }}>
|
||||
<span style={{ fontWeight: 600, fontSize: '14px', color: '#212529' }}>
|
||||
{senderName || 'Unknown'}
|
||||
</span>
|
||||
<span style={{ fontSize: '12px', color: '#6c757d' }}>
|
||||
{formatTimestamp(createdAt)}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{/* Attachment info */}
|
||||
<div style={attachmentTextStyle}>
|
||||
<span style={{ fontSize: '16px', flexShrink: 0 }}>📎</span>
|
||||
<span>attached</span>
|
||||
<span
|
||||
onClick={() => handleAttachmentClick(attachmentId)}
|
||||
style={filenameLinkStyle}
|
||||
title={attachmentName}
|
||||
>
|
||||
{attachmentName}
|
||||
</span>
|
||||
{attachmentSize && (
|
||||
<span style={fileSizeStyle}>({formatFileSize(attachmentSize)})</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
3. Ensure minWidth: 0 is set on all flex children that need truncation (this is critical for text-overflow: ellipsis to work).
|
||||
</action>
|
||||
<verify>
|
||||
1. Long filenames truncate with ellipsis ("verylongfilename..." instead of overflow)
|
||||
2. Full filename visible on hover (title attribute)
|
||||
3. File size stays visible and doesn't truncate
|
||||
4. Layout works at different window widths (resize chat window)
|
||||
5. Content doesn't overflow container bounds
|
||||
</verify>
|
||||
<done>
|
||||
Attachment messages display correctly at different window sizes. Long filenames truncate with ellipsis, full name shown on hover. File size and timestamp always visible.
|
||||
</done>
|
||||
</task>
|
||||
|
||||
<task type="checkpoint:human-verify" gate="blocking">
|
||||
<what-built>
|
||||
Complete attachment message display with clickable links, signed URL fetching, and responsive layout
|
||||
</what-built>
|
||||
<how-to-verify>
|
||||
1. Start the app: `cd jam-ui && npm start`
|
||||
2. Start backend: `./runweb` (or appropriate Rails command)
|
||||
3. Login and join a music session
|
||||
4. Upload a file using the Attach button (from Phase 13)
|
||||
5. Verify attachment message appears with:
|
||||
- Light blue background (distinct from text messages)
|
||||
- Paperclip icon
|
||||
- "[YourName] attached [filename]" format
|
||||
- Clickable filename (underlined, blue color)
|
||||
6. Click the filename:
|
||||
- Should open new browser tab
|
||||
- PDF/images should display in browser
|
||||
- Other files should download
|
||||
7. Test long filename: Upload a file with a very long name
|
||||
- Should truncate with ellipsis
|
||||
- Hover should show full name
|
||||
8. Resize chat window:
|
||||
- Content should remain readable
|
||||
- No overflow issues
|
||||
9. Refresh page:
|
||||
- Attachment should still be visible in chat history
|
||||
10. Test regular text message still works normally
|
||||
</how-to-verify>
|
||||
<resume-signal>Type "approved" if all checks pass, or describe any issues found</resume-signal>
|
||||
</task>
|
||||
|
||||
</tasks>
|
||||
|
||||
<verification>
|
||||
1. Attachment filename is clickable link
|
||||
2. Clicking fetches signed S3 URL and opens in new tab
|
||||
3. Browser displays/downloads file based on type
|
||||
4. Long filenames truncate with ellipsis, full name on hover
|
||||
5. Layout responsive at different window sizes
|
||||
6. Chat history includes attachments after refresh
|
||||
7. Regular text messages still render correctly
|
||||
</verification>
|
||||
|
||||
<success_criteria>
|
||||
- Filename is clickable link opening file in new browser tab
|
||||
- Browser handles view/download based on file type (PDF views, images display, others download)
|
||||
- Long filenames truncate with ellipsis, full name shown on hover
|
||||
- Responsive layout at different window sizes
|
||||
- Chat history includes attachments (persists across refresh)
|
||||
- Auto-scroll works when new attachment appears (existing behavior)
|
||||
</success_criteria>
|
||||
|
||||
<output>
|
||||
After completion, create `.planning/phases/14-chat-integration-and-display/14-02-SUMMARY.md`
|
||||
</output>
|
||||
Loading…
Reference in New Issue