---
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"
    - "Edge cases handled: missing senderName shows 'Unknown', missing attachmentName handled gracefully"
  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.

NOTE: This is primarily styling/conditional rendering work (similar to Phase 8-02 visual component work). Per CLAUDE.md, TDD is optional for styling-only changes. The component behavior (conditional rendering based on attachmentId presence) is straightforward and will be verified via integration tests in Plan 02.

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. Handle edge cases:
   - Missing senderName: Display "Unknown" (already handled in existing code)
   - Missing attachmentName: Fall back to regular message rendering (handled by isAttachmentMessage check)
   - Null/undefined attachmentSize: Don't show size, just filename

6. 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
};
```

7. 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'
};
```

8. 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
6. Edge cases: missing senderName shows "Unknown", missing attachmentSize shows no size
  </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. Edge cases handled gracefully.
  </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)
- Edge cases handled: missing senderName, missing attachmentSize
</success_criteria>

<output>
After completion, create `.planning/phases/14-chat-integration-and-display/14-01-SUMMARY.md`
</output>
