# Stack Context — CollabSpace in GB4.7MFE
## Read this at the start of every Claude Code session for CollabSpace work.
## This file OVERRIDES the original generic stack_context. GB4.7MFE rules apply everywhere.

---

## Project
CollabSpace is a real-time co-browse + screen-share feature inside the GB4.7MFE Angular 20
enterprise MFE monorepo. It lives at `features/gbcollabspace/`. It is NOT a standalone app.
All GB4.7MFE conventions in `CLAUDE.md` apply without exception.

---

## Tech Stack
- **Angular**: 20 with Signals. No NGXS for new code. No NgModules. Standalone components only.
- **Real-time**: `@microsoft/signalr` v8
- **Overlay/Dialog**: `@angular/cdk` (Dialog, Overlay, DragDrop) — already in project
- **HTTP**: Angular `HttpClient` → `Observable<T>` — consumed via `toSignal()` in components
- **Dates**: `date-fns` for elapsed-time **computation only**. All display formatting uses Angular `DatePipe` / `CurrencyPipe` — never manual string formatting.
- **Types**: TypeScript strict mode. Zero `any` — use typed interfaces or `unknown`.
- **Tests**: Jest (configured, use it)
- **Logging**: `GbConsoleService` (import from `@gbcommon/common`) — NEVER `console.log/warn/error`
- **Backend**: ASP.NET Core 8 — see `api_reference.md`

---

## Folder Structure

All CollabSpace code lives under `features/gbcollabspace/`. Follow this layout exactly:

```
features/gbcollabspace/
│
├── index.ts                          ← Public barrel — ONLY exports for other modules
│
├── model/
│   ├── session.model.ts
│   ├── participant.model.ts
│   ├── invite.model.ts
│   ├── hub-payloads.model.ts
│   ├── collab-config.model.ts
│   ├── collab-bridge.model.ts
│   ├── collab.enums.ts
│   ├── collab.constants.ts
│   └── index.ts
│
├── dbservice/
│   └── collab-hub.db.service.ts      ← SignalR connection owner (maps to collab-hub.service.ts in prompts)
│
├── service/
│   ├── collab-session.state.ts       ← Signal state (maps to core/state/session.state.ts)
│   ├── collab-participant.state.ts   ← Signal state (maps to core/state/participant.state.ts)
│   ├── collab-ui.state.ts            ← Signal state (maps to core/state/ui.state.ts)
│   ├── collab-state-manager.service.ts
│   ├── collab-session.service.ts
│   ├── collab-cobrowse.service.ts
│   ├── collab-invite.service.ts
│   ├── collab-config.service.ts
│   └── collab-bridge.service.ts
│
├── components/
│   ├── toolbar/
│   ├── session-card/
│   ├── participant-panel/
│   ├── chat-panel/
│   ├── cursor-overlay/
│   ├── detail-panel/
│   ├── invite-modal/
│   ├── dashboard/                    ← maps to pages/dashboard
│   ├── session-create/               ← maps to pages/session-create
│   ├── join-via-link/                ← maps to pages/join-via-link
│   └── config/                       ← maps to pages/config
│
├── pipes/
│   ├── elapsed-time.pipe.ts
│   └── session-status-label.pipe.ts
│
└── directives/
    ├── collab-view-only.directive.ts
    └── collab-context.directive.ts
```

### Prompt path mapping
When a prompt says `core/services/X` → use `service/X` or `dbservice/X` (see rule below).
When a prompt says `core/state/X` → use `service/X`.
When a prompt says `core/models/` or `core/enums/` or `core/constants/` → use `model/`.
When a prompt says `pages/X` → use `components/X`.
When a prompt says `shared/pipes/` → use `pipes/`.
When a prompt says `shared/directives/` → use `directives/`.

### DBService rule — critical
`collab-hub.service.ts` from the prompts becomes `dbservice/collab-hub.db.service.ts`.
The SignalR `HubConnection` lives exclusively in the DBService — never in `service/` files.
All other services in `core/services/` map to `service/`.

---

## Mandatory Conventions

### Dependency Injection
```typescript
// ALWAYS use inject()
private readonly svc = inject(MyService);
// NEVER constructor parameters for DI
```

### Logging — replaces Logger service from prompts
```typescript
// ALWAYS use GbConsoleService — never console.log/warn/error
private readonly console = inject(GbConsoleService);
this.console.warn('message');

// Replace every console.warn/log/error in the prompts with:
this.console.warn(...)   // was: console.warn(...)
this.console.error(...)  // was: console.error(...)
```

### Template Syntax
```html
<!-- ALWAYS Angular 20 control flow -->
@if (show()) { <div>...</div> }
@for (item of list(); track item.id) { <div>{{ item.name }}</div> }
@switch (mode()) { @case ('a') { ... } }
<!-- NEVER *ngIf *ngFor *ngSwitch -->
```

### Signals Pattern
```typescript
// Private writable — public readonly
private readonly _items = signal<Item[]>([]);
readonly items  = this._items.asReadonly();
readonly count  = computed(() => this._items().length);

// Mutations via explicit methods only
setItems(items: Item[]): void { this._items.set(items); }
addItem(item: Item): void     { this._items.update(i => [...i, item]); }
```

### Subscription Cleanup
```typescript
// In services: takeUntilDestroyed() — ALWAYS
source$.pipe(takeUntilDestroyed()).subscribe(...)

// In components: prefer toSignal() — no subscribe needed at all
messages = toSignal(this.service.message$, { initialValue: [] as CollabMessage[] });
```

### Component Declaration
```typescript
@Component({
  standalone: true,
  changeDetection: ChangeDetectionStrategy.OnPush,  // ALWAYS
  imports: [ /* only what this component needs */ ],
})
```

### No Magic Strings
```typescript
status === SessionStatus.Active  // ✅
status === 'active'              // ❌
```

---

## i18n — replaces collab.labels.ts

**Do NOT create `collab.labels.ts` or any string constants file for display text.**
All display strings go into `projects/public/i18n/en.json` under the `collabspace` key prefix.

```json
// projects/public/i18n/en.json — add under existing structure:
{
  "collabspace": {
    "roles": { "host": "Host", "presenter": "Presenter", "coEditor": "Co-Editor", "viewer": "Viewer" },
    "status": { "active": "Active", "scheduled": "Scheduled", "ended": "Ended" },
    "actions": { "joinSession": "Join Session", "endSession": "End Session", ... },
    "errors": { "notInSession": "You are not a participant in this session.", ... }
  }
}
```

Consume in components via Transloco:
```html
{{ 'collabspace.roles.host' | transloco }}
```
```typescript
private transloco = inject(TranslocoService);
this.transloco.translate('collabspace.errors.notInSession')
```

Non-display constants (URLs, intervals, regex) stay in `model/collab.constants.ts` as before.

---

## Auth Token for SignalR

`ILoginDTO` (the project's session object) does NOT contain a JWT Bearer token.
The project authenticates HTTP calls via a custom `Login` header carrying the full LoginDTO JSON.
Browser WebSocket connections do NOT support custom headers.

**Pattern for this project:**
```typescript
// In collab-hub.db.service.ts
// Use LongPolling + ServerSentEvents which support custom headers,
// OR coordinate with the backend team for a short-lived token endpoint.
// Until a token endpoint exists, force transport to avoid WebSocket:
private readonly _connection = new HubConnectionBuilder()
  .withUrl(COLLAB_HUB_URL, {
    transport: HttpTransportType.LongPolling,
    headers: { 'Login': sessionStorage.getItem('LoginDTO') ?? '' }
    // TODO: replace with GbAppStateService signal once P0 auth is resolved
  })
  .withAutomaticReconnect([0, 2000, 5000, 10000, 30000])
  .configureLogging(LogLevel.Warning)
  .build();
```

> Note: The prompts reference `authService.getToken()` — there is no such method in this codebase.
> Use the pattern above until the backend exposes a dedicated SignalR token endpoint.

---

## Dialogs and Responsive Design

```typescript
// ALWAYS — never fixed px
this.dialog.open(MyComponent, {
  width: 'min(600px, 95vw)',
  maxWidth: '95vw',
});
```

---

## Toast Notifications

The project already has `GbToastService` (from `@gbcommon/common`).
CollabSpace's internal `CollabToast` / `CollabUiState.toasts` signal is fine for in-session
transient overlays (cursor notifications, control transfer prompts).
For persistent app-level toasts, use `GbToastService` instead of creating a new toast channel.

---

## Path Alias

In `tsconfig.json` `compilerOptions.paths`, the alias is:
```json
"@gbcollabspace/*": ["features/gbcollabspace/*"]
```
*(Note: prompts use `@collab/*` — replace with `@gbcollabspace/*` throughout)*

---

## Public Exports

Only these items should be exported from `features/gbcollabspace/index.ts`:
```typescript
export { CollabBridgeService }        from './service/collab-bridge.service';
export type { CollabContext, FieldChange, ICollabBridgeService } from './model/collab-bridge.model';
export { CollabToolbarComponent }     from './components/toolbar/collab-toolbar.component';
export { CollabViewOnlyDirective }    from './directives/collab-view-only.directive';
export { COLLABORATION_ROUTES }       from './collaboration.routes';
export { provideCollaboration }       from './provide-collaboration';
```

---

## What NOT to Generate
- No placeholder `// TODO` comments
- No `console.log/warn/error` — use `GbConsoleService`
- No `any` type — use `unknown` if truly unknown, or define an interface
- No hardcoded display strings — Transloco keys only
- No `collab.labels.ts` — see i18n section
- No `sessionStorage.getItem` outside of the auth note above (P0 violation in this project)
- No barrel re-exports of internal implementation details from `index.ts`
