# T12 — Chat Panel Template
## Generates: `components/chat-panel/chat-panel.component.html` + `.scss`

---

## Step 1 — Reference Files (fill in before running)

```
"Read CLAUDE.md.
Read @[REF:CHAT_OR_PANEL] — learn how we build floating panels
or notification areas in this project.
If no equivalent exists, read @[REF:MODAL] for panel header/footer
and body scroll patterns instead.
Then implement the template described below."
```

**What to look for in your references:**
- Floating panel structure (fixed positioned, no backdrop)
- Panel open/close animation pattern
- Message bubble pattern if it exists anywhere
- Input + send button pattern
- Whether project has a character counter component
- Avatar/initials component

---

## Step 2 — What This Component Is

A **floating chat panel** that slides in from the bottom-right corner.
Visible during active sessions. Independent of the toolbar.

---

## Step 3 — Content Specification

### Panel Visibility & Position
```
Show/hide based on: isOpen() signal
Position: fixed, bottom-right of viewport (above toolbar)
Open: animate in (slide up or scale up — match your project's panel style)
Close: reverse animation

No backdrop — panel floats above content without blocking it.
```

### Panel Header
```
Title: "Session Chat"
Participant count: onlineCount() — muted secondary text
Close button → uiState.toggleChat()
Use your project's compact panel header pattern.
```

### Messages Area
```
Scrollable container — #messagesContainer (ViewChild in TS)
Auto-scrolls to bottom on new messages (handled in TS)

For each message in messages() track message.sentAt:

  Own messages (fromUserId === myUserId()):
    Aligned: right
    Bubble background: your project's "primary" or "outgoing" style
    No avatar shown

  Other messages (fromUserId !== myUserId()):
    Aligned: left
    Avatar: initials of fromDisplayName
      background: message.cursorColor at low opacity
      text color: message.cursorColor
    Sender name above bubble: muted small text
    Bubble background: your project's "incoming" or neutral style

  Timestamp:
    Show below bubble: sentAt formatted as time only (HH:mm)
    Muted/secondary style

Empty state (messages().length === 0):
  Center of messages area
  Text: "No messages yet. Say hello!"
  Muted style
```

### Input Area
```
Text input:
  [(ngModel)]="inputText" or bind to signal via setter
  placeholder: "Type a message..."
  (keydown.enter): sendMessage() — send on Enter
  (keydown.shift.enter): allow newline — do NOT send
  maxlength binding: COLLAB_MAX_CHAT_LENGTH (1000)
  Use your project's input component

Character counter:
  Show only when inputText().length > 800
  Format: "180 / 1000"
  Color: warning when > 900, danger/error when > 990
  isOverLimit() === true: disable send button and show error color

Send button:
  click: sendMessage()
  disabled when: inputText().trim().length === 0 OR isOverLimit()
  Use your project's icon-only button with send/arrow icon
  OR a compact "Send" labeled button — match your project's style
```

---

## Step 4 — Angular Bindings Summary

```typescript
isOpen()          Signal<boolean>
messages()        Signal<ChatMessage[]>
inputText()       Signal<string>
charCount()       computed number
isOverLimit()     computed boolean

// From participantState (injected, exposed as public)
participantState.myUserId()
participantState.onlineCount()

sendMessage()
```

---

## SCSS Instruction

```
Write SCSS only for:
- Panel position: fixed, right-20px, above toolbar (bottom: 80px)
- Panel dimensions: ~300px wide, ~380px tall
- Open/close animation — match your project's panel animation
- Messages area: flex column, overflow-y auto, custom scrollbar
  if your project has a scrollbar style
- Avatar circles: ~24px, dynamic color (cannot use class — use [style])
- Own vs other message alignment (flex justify)
- Message bubble border-radius:
  Own:   12px 2px 12px 12px  (rounded except top-right)
  Other: 2px 12px 12px 12px  (rounded except top-left)

Do NOT restyle input, button, or typography — use your design system.
```

---

## Constraints
- Dynamic avatar color: `[style.background-color]` and `[style.color]`
- `@for messages() track message.sentAt` — sentAt is unique enough for tracking
- `#messagesContainer` template ref must be on the scrollable div (used in TS)
- Shift+Enter must NOT submit — only plain Enter submits

---
---

# T13 — Cursor Overlay Template
## Generates: `components/cursor-overlay/cursor-overlay.component.html` + `.scss`

---

## Step 1 — Reference Files

```
"Read CLAUDE.md.
No reference file needed for this component — it is a full-viewport
pointer-events:none overlay. There is unlikely to be an equivalent
in the existing project. Generate based purely on the spec below."
```

---

## Step 2 — What This Component Is

A **full-viewport fixed overlay** that renders floating cursor labels
for remote participants. It must NEVER block any user interaction.

This component has no equivalent in a typical ERP — it is a new pattern.
The SCSS is prescriptive here (not standard-driven) for that reason.

---

## Step 3 — Content Specification

```
Entire component host: pointer-events: none (set on host element in TS
  via host binding — already done in TS file)

Show content only when: isActive() === true

For each cursor in otherCursors() track cursor.userId:
  Show only when: cursor.isOnline === true AND cursor.cursorX > 0

  One cursor label per remote participant:
    Position: absolute, left = cursor.cursorX%, top = cursor.cursorY%
    Contains:
      - Small filled circle (5px) in cursor.cursorColor
      - Participant first name only:
          cursor.displayName.split(' ')[0]
        in cursor.cursorColor
    pointer-events: none (critical)
    white-space: nowrap
    font: small monospace
```

---

## Step 4 — Angular Bindings Summary

```typescript
isActive()        Signal<boolean>   — from sessionState.hasActiveSession
otherCursors()    Signal<ParticipantPresence[]>  — from participantState
```

---

## SCSS (prescriptive — no design system reference needed)

```scss
// The host element is already position:fixed inset:0 pointer-events:none
// via host binding in TS. Only write styles for cursor label:

:host {
  // host styles already applied via @Component host property
}

.cursor-label {
  position:        absolute;
  transform:       translate(4px, 4px);
  transition:      left 0.08s linear, top 0.08s linear;
  display:         flex;
  align-items:     center;
  gap:             4px;
  padding:         2px 7px;
  border-radius:   4px;
  pointer-events:  none;
  white-space:     nowrap;
  font-family:     monospace;
  font-size:       11px;
  // Color is dynamic — applied via [style.color] binding
  // Background uses color-mix or low-opacity background via [style]
}

.cursor-dot {
  width:           5px;
  height:          5px;
  border-radius:   50%;
  background:      currentColor;
  flex-shrink:     0;
}
```

---

## Step 5 — Template

```html
@if (isActive()) {
  @for (cursor of otherCursors(); track cursor.userId) {
    @if (cursor.isOnline && cursor.cursorX > 0) {
      <div class="cursor-label"
           [style.left.%]="cursor.cursorX"
           [style.top.%]="cursor.cursorY"
           [style.color]="cursor.cursorColor">
        <span class="cursor-dot"></span>
        <span>{{ cursor.displayName.split(' ')[0] }}</span>
      </div>
    }
  }
}
```

Note: This template is provided as-is because cursor overlay has
no equivalent in the project's design system to reference.
Claude Code should use this exact template and only adjust
if it conflicts with the project's build setup.

---
---

# T14 — Invite Modal Template
## Generates: `components/invite-modal/invite-modal.component.html`

---

## Step 1 — Reference Files (fill in before running)

```
"Read CLAUDE.md.
Read @[REF:MODAL] — learn how we structure modals/dialogs in this project:
  modal wrapper, header, body, footer, close button placement.
Read @[REF:FORM_FIELD] — learn our field and input component patterns.
Then implement the template described below."
```

**What to look for in your references:**
- Modal wrapper component (`<gb-modal>`, `<gb-dialog>`, or CDK Dialog pattern)
- Modal header structure
- Modal footer with action buttons
- Tab component if the project has one
- Select/dropdown component
- Copyable input or copy-to-clipboard pattern
- How success/confirmation states are shown inline

---

## Step 2 — What This Component Is

A **dialog/modal** for inviting participants to a session.
Has two tabs: invite a user directly, or generate a shareable link.
Opened via CDK Dialog — `dialogRef` and `data` are already injected in TS.

---

## Step 3 — Content Specification

### Modal Structure
```
Use your project's modal/dialog wrapper component.
If CDK Dialog is used directly (no wrapper), follow the pattern in REF:MODAL.

Header:
  Title: "Invite Participants"
  Subtitle: data.sessionTitle (the session being invited to)
  Close button → dialogRef.close()

Body: tab content (see below)

Footer:
  "Close" button → dialogRef.close()
  Use your project's secondary/ghost button
```

### Tab Switcher
```
Two tabs — use your project's tab component:
  Tab 1: "Invite by User"   → activeTab() === 'user'
  Tab 2: "Share Link"       → activeTab() === 'link'

click tab: activeTab.set('user') or activeTab.set('link')

@switch (activeTab()) { @case ('user') ... @case ('link') ... }
```

### Tab 1: Invite by User
```
Field: Select User
  Use your project's user-search or select component
  (If a people-picker/typeahead exists in the project — use it)
  label: "Search user"
  placeholder: "Type a name or email..."
  bind to: selectedUser signal via setter
  If no people-picker exists, use a standard text input

Field: Role
  label: "Offered Role"
  Use your project's select/dropdown component
  Options:
    Viewer    (default selected)
    CoEditor
    Presenter
  bind: selectedRole signal via setter

Action: Send Invite button
  label: "Send Invite"
  disabled when: selectedUser() === null OR isSending() === true
  loading state when: isSending() === true
  click: sendUserInvite()
  Use your project's primary button

Success state (show when invite sent successfully):
  Use your project's inline success message or alert
  Text: "Invite sent!"
  Auto-clears after 3 seconds (handled in TS)
```

### Tab 2: Share Link
```
Field: Role for link recipients
  Same role dropdown as Tab 1
  label: "Role for anyone joining via link"

Generate button (show when: generatedInvite() === null):
  label: "Generate Link"
  click: generateLink()
  loading when: isGenerating() === true
  Use your project's secondary button

Link display (show when: generatedInvite() !== null):
  Read-only input showing generatedInvite().joinUrl
  Use your project's read-only input or copyable-input component
  If no copyable-input component: show input + copy button side by side
    Copy button:
      click: copyLink()
      label: "Copy" → changes to "Copied!" for 2 seconds when linkCopied() === true
      icon: your project's "copy" or "clipboard" icon

  Expiry info below input:
    "Expires: " + generatedInvite().expiresAt formatted
    Muted/secondary text style

  "Generate New Link" button → generateLink() again
    small/compact secondary button
```

---

## Step 4 — Angular Bindings Summary

```typescript
// Injected (CDK Dialog)
data                      { sessionId: string, sessionTitle: string }
dialogRef                 DialogRef

// Signals
activeTab()               Signal<'user' | 'link'>
selectedRole()            Signal<ParticipantRole>
selectedUser()            Signal<string | null>
generatedInvite()         Signal<CollabSessionInvite | null>
isSending()               Signal<boolean>
isGenerating()            Signal<boolean>
linkCopied()              Signal<boolean>

// Actions
sendUserInvite()
generateLink()
copyLink()
```

---

## Constraints
- Use `@switch (activeTab())` for tab content switching
- Dialog close: `dialogRef.close()` — not router navigation
- Do not use `<form>` element — use `<div [formGroup]>` if form group
  is used, or standalone field bindings for simple cases
