@memberjunction/ng-whiteboard 0.0.1 → 5.42.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. package/README.md +302 -28
  2. package/dist/lib/whiteboard-agent-sees-popover.component.d.ts +31 -0
  3. package/dist/lib/whiteboard-agent-sees-popover.component.d.ts.map +1 -0
  4. package/dist/lib/whiteboard-agent-sees-popover.component.js +132 -0
  5. package/dist/lib/whiteboard-agent-sees-popover.component.js.map +1 -0
  6. package/dist/lib/whiteboard-board.component.d.ts +458 -0
  7. package/dist/lib/whiteboard-board.component.d.ts.map +1 -0
  8. package/dist/lib/whiteboard-board.component.js +2357 -0
  9. package/dist/lib/whiteboard-board.component.js.map +1 -0
  10. package/dist/lib/whiteboard-context-menu.d.ts +57 -0
  11. package/dist/lib/whiteboard-context-menu.d.ts.map +1 -0
  12. package/dist/lib/whiteboard-context-menu.js +69 -0
  13. package/dist/lib/whiteboard-context-menu.js.map +1 -0
  14. package/dist/lib/whiteboard-export.d.ts +94 -0
  15. package/dist/lib/whiteboard-export.d.ts.map +1 -0
  16. package/dist/lib/whiteboard-export.js +592 -0
  17. package/dist/lib/whiteboard-export.js.map +1 -0
  18. package/dist/lib/whiteboard-host.component.d.ts +174 -0
  19. package/dist/lib/whiteboard-host.component.d.ts.map +1 -0
  20. package/dist/lib/whiteboard-host.component.js +758 -0
  21. package/dist/lib/whiteboard-host.component.js.map +1 -0
  22. package/dist/lib/whiteboard-pages.component.d.ts +83 -0
  23. package/dist/lib/whiteboard-pages.component.d.ts.map +1 -0
  24. package/dist/lib/whiteboard-pages.component.js +211 -0
  25. package/dist/lib/whiteboard-pages.component.js.map +1 -0
  26. package/dist/lib/whiteboard-snapshot.component.d.ts +30 -0
  27. package/dist/lib/whiteboard-snapshot.component.d.ts.map +1 -0
  28. package/dist/lib/whiteboard-snapshot.component.js +91 -0
  29. package/dist/lib/whiteboard-snapshot.component.js.map +1 -0
  30. package/dist/lib/whiteboard-srcdoc.pipe.d.ts +60 -0
  31. package/dist/lib/whiteboard-srcdoc.pipe.d.ts.map +1 -0
  32. package/dist/lib/whiteboard-srcdoc.pipe.js +75 -0
  33. package/dist/lib/whiteboard-srcdoc.pipe.js.map +1 -0
  34. package/dist/lib/whiteboard-state.d.ts +1113 -0
  35. package/dist/lib/whiteboard-state.d.ts.map +1 -0
  36. package/dist/lib/whiteboard-state.js +1396 -0
  37. package/dist/lib/whiteboard-state.js.map +1 -0
  38. package/dist/lib/whiteboard-toolbar.component.d.ts +95 -0
  39. package/dist/lib/whiteboard-toolbar.component.d.ts.map +1 -0
  40. package/dist/lib/whiteboard-toolbar.component.js +344 -0
  41. package/dist/lib/whiteboard-toolbar.component.js.map +1 -0
  42. package/dist/lib/whiteboard-tools.d.ts +103 -0
  43. package/dist/lib/whiteboard-tools.d.ts.map +1 -0
  44. package/dist/lib/whiteboard-tools.js +748 -0
  45. package/dist/lib/whiteboard-tools.js.map +1 -0
  46. package/dist/lib/whiteboard-widget-bridge.d.ts +217 -0
  47. package/dist/lib/whiteboard-widget-bridge.d.ts.map +1 -0
  48. package/dist/lib/whiteboard-widget-bridge.js +254 -0
  49. package/dist/lib/whiteboard-widget-bridge.js.map +1 -0
  50. package/dist/lib/whiteboard-zoom.component.d.ts +55 -0
  51. package/dist/lib/whiteboard-zoom.component.d.ts.map +1 -0
  52. package/dist/lib/whiteboard-zoom.component.js +139 -0
  53. package/dist/lib/whiteboard-zoom.component.js.map +1 -0
  54. package/dist/lib/whiteboard.module.d.ts +29 -0
  55. package/dist/lib/whiteboard.module.d.ts.map +1 -0
  56. package/dist/lib/whiteboard.module.js +78 -0
  57. package/dist/lib/whiteboard.module.js.map +1 -0
  58. package/dist/public-api.d.ts +49 -0
  59. package/dist/public-api.d.ts.map +1 -0
  60. package/dist/public-api.js +59 -0
  61. package/dist/public-api.js.map +1 -0
  62. package/package.json +43 -6
@@ -0,0 +1,758 @@
1
+ import { ChangeDetectorRef, Component, EventEmitter, HostListener, Input, Output, ViewChild, inject } from '@angular/core';
2
+ import { CommonModule } from '@angular/common';
3
+ import { WhiteboardState } from './whiteboard-state';
4
+ import { BuildWhiteboardExportHtml, BuildWhiteboardExportHtmlAllPages, BuildWhiteboardExportSvg, BuildWhiteboardExportSvgPages } from './whiteboard-export';
5
+ import { ApplyWhiteboardAgentTool, WHITEBOARD_TOOL_NAMES } from './whiteboard-tools';
6
+ import { RealtimeWhiteboardBoardComponent } from './whiteboard-board.component';
7
+ import { RealtimeWhiteboardToolbarComponent, WHITEBOARD_PEN_COLORS } from './whiteboard-toolbar.component';
8
+ import { RealtimeWhiteboardZoomComponent } from './whiteboard-zoom.component';
9
+ import { RealtimeWhiteboardAgentSeesPopoverComponent } from './whiteboard-agent-sees-popover.component';
10
+ import * as i0 from "@angular/core";
11
+ function RealtimeWhiteboardHostComponent_Conditional_20_Template(rf, ctx) { if (rf & 1) {
12
+ i0.ɵɵelement(0, "mj-realtime-whiteboard-agent-sees-popover", 14);
13
+ } if (rf & 2) {
14
+ const ctx_r1 = i0.ɵɵnextContext();
15
+ i0.ɵɵproperty("AgentName", ctx_r1.AgentName)("DeltaJson", ctx_r1.LastDeltaJson)("DebounceMs", ctx_r1.SceneDeltaDebounceMs)("ElementCount", ctx_r1.ElementCount)("LastDeltaLabel", ctx_r1.LastDeltaLabel);
16
+ } }
17
+ function RealtimeWhiteboardHostComponent_Conditional_25_Conditional_4_Template(rf, ctx) { if (rf & 1) {
18
+ const _r4 = i0.ɵɵgetCurrentView();
19
+ i0.ɵɵelementStart(0, "button", 36);
20
+ i0.ɵɵlistener("click", function RealtimeWhiteboardHostComponent_Conditional_25_Conditional_4_Template_button_click_0_listener() { i0.ɵɵrestoreView(_r4); const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.ExportDownloadHtmlAllPages()); });
21
+ i0.ɵɵelement(1, "i", 37);
22
+ i0.ɵɵtext(2, " Download HTML \u2014 all pages ");
23
+ i0.ɵɵelementEnd();
24
+ } }
25
+ function RealtimeWhiteboardHostComponent_Conditional_25_Conditional_8_Template(rf, ctx) { if (rf & 1) {
26
+ const _r5 = i0.ɵɵgetCurrentView();
27
+ i0.ɵɵelementStart(0, "button", 43);
28
+ i0.ɵɵlistener("click", function RealtimeWhiteboardHostComponent_Conditional_25_Conditional_8_Template_button_click_0_listener() { i0.ɵɵrestoreView(_r5); const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.ExportDownloadSvgAllPages()); });
29
+ i0.ɵɵelement(1, "i", 39);
30
+ i0.ɵɵtext(2, " Download SVG \u2014 all pages ");
31
+ i0.ɵɵelementEnd();
32
+ } }
33
+ function RealtimeWhiteboardHostComponent_Conditional_25_Template(rf, ctx) { if (rf & 1) {
34
+ const _r3 = i0.ɵɵgetCurrentView();
35
+ i0.ɵɵelementStart(0, "div", 18)(1, "button", 36);
36
+ i0.ɵɵlistener("click", function RealtimeWhiteboardHostComponent_Conditional_25_Template_button_click_1_listener() { i0.ɵɵrestoreView(_r3); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.ExportDownloadHtml()); });
37
+ i0.ɵɵelement(2, "i", 37);
38
+ i0.ɵɵtext(3, " Download HTML ");
39
+ i0.ɵɵelementEnd();
40
+ i0.ɵɵconditionalCreate(4, RealtimeWhiteboardHostComponent_Conditional_25_Conditional_4_Template, 3, 0, "button", 38);
41
+ i0.ɵɵelementStart(5, "button", 36);
42
+ i0.ɵɵlistener("click", function RealtimeWhiteboardHostComponent_Conditional_25_Template_button_click_5_listener() { i0.ɵɵrestoreView(_r3); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.ExportDownloadSvg()); });
43
+ i0.ɵɵelement(6, "i", 39);
44
+ i0.ɵɵtext(7, " Download SVG ");
45
+ i0.ɵɵelementEnd();
46
+ i0.ɵɵconditionalCreate(8, RealtimeWhiteboardHostComponent_Conditional_25_Conditional_8_Template, 3, 0, "button", 40);
47
+ i0.ɵɵelementStart(9, "button", 36);
48
+ i0.ɵɵlistener("click", function RealtimeWhiteboardHostComponent_Conditional_25_Template_button_click_9_listener() { i0.ɵɵrestoreView(_r3); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.ExportPrint()); });
49
+ i0.ɵɵelement(10, "i", 41);
50
+ i0.ɵɵtext(11, " Print ");
51
+ i0.ɵɵelementEnd();
52
+ i0.ɵɵelementStart(12, "button", 36);
53
+ i0.ɵɵlistener("click", function RealtimeWhiteboardHostComponent_Conditional_25_Template_button_click_12_listener() { i0.ɵɵrestoreView(_r3); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.ExportSaveToArtifacts()); });
54
+ i0.ɵɵelement(13, "i", 42);
55
+ i0.ɵɵtext(14, " Save to artifacts ");
56
+ i0.ɵɵelementEnd()();
57
+ } if (rf & 2) {
58
+ const ctx_r1 = i0.ɵɵnextContext();
59
+ i0.ɵɵadvance(4);
60
+ i0.ɵɵconditional(ctx_r1.HasMultiplePages ? 4 : -1);
61
+ i0.ɵɵadvance(4);
62
+ i0.ɵɵconditional(ctx_r1.HasMultiplePages ? 8 : -1);
63
+ } }
64
+ function RealtimeWhiteboardHostComponent_Conditional_36_Template(rf, ctx) { if (rf & 1) {
65
+ const _r7 = i0.ɵɵgetCurrentView();
66
+ i0.ɵɵelementStart(0, "div", 25);
67
+ i0.ɵɵelement(1, "i", 44);
68
+ i0.ɵɵelementStart(2, "span", 45);
69
+ i0.ɵɵtext(3);
70
+ i0.ɵɵelementEnd();
71
+ i0.ɵɵelementStart(4, "span", 46);
72
+ i0.ɵɵlistener("click", function RealtimeWhiteboardHostComponent_Conditional_36_Template_span_click_4_listener($event) { i0.ɵɵrestoreView(_r7); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.OnToastUndo($event)); });
73
+ i0.ɵɵelement(5, "i", 47);
74
+ i0.ɵɵtext(6, " Undo");
75
+ i0.ɵɵelementEnd()();
76
+ } if (rf & 2) {
77
+ const ctx_r1 = i0.ɵɵnextContext();
78
+ i0.ɵɵadvance(3);
79
+ i0.ɵɵtextInterpolate(ctx_r1.ToastText);
80
+ } }
81
+ /** Debounce window for the perception feed (the popover's "debounce 750 ms" stat). */
82
+ const SCENE_DELTA_DEBOUNCE_MS = 750;
83
+ /** How long the agent-action toast stays up (coalescing further actions while visible). */
84
+ const TOAST_MS = 6000;
85
+ /** How long the presence cursor lingers after the agent's last board action. */
86
+ const PRESENCE_MS = 4200;
87
+ /**
88
+ * LIVE WHITEBOARD host (`mj-realtime-whiteboard-host`): board header (title / saved chip /
89
+ * ownership legend / "What [Agent] sees" popover / Focus toggle), the board surface with its
90
+ * floating toolbar + zoom cluster, the agent action toast with Undo, and the status footer —
91
+ * everything EXCEPT the overlay/tab chrome, which the session shell owns.
92
+ *
93
+ * Integration contract:
94
+ * - `State` is created by the integration layer (channel state of record) and passed in;
95
+ * - `SceneDelta` emits the debounced (750 ms), coalesced scene-delta JSON on user changes —
96
+ * the perception feed the integration pipes into the live agent context;
97
+ * - `ApplyAgentTool(toolName, argsJson)` executes one agent channel tool
98
+ * (`Whiteboard_AddNote` … see `WHITEBOARD_TOOL_DEFINITIONS`) with the violet pop-in,
99
+ * toast and presence-cursor garnish, returning the result JSON for the tool round-trip;
100
+ * - `AgentUndo` fires when the user clicks Undo on the agent-action toast;
101
+ * - `FocusModeChange` asks the shell to collapse/restore the call rail ("Focus board").
102
+ *
103
+ * Extensibility (before/after events): the host mirrors the engine's cancelable
104
+ * BEFORE / AFTER mutation pairs as outputs (`ItemAdding`/`ItemAdded`,
105
+ * `ItemUpdating`/`ItemUpdated`, `ItemRemoving`/`ItemRemoved`) and forwards the board's
106
+ * editor + widget pairs (`ContentApplying`/`ContentApplied`,
107
+ * `WidgetSubmitting`/`WidgetSubmitted`). All BEFORE events honor `Cancel = true` set
108
+ * synchronously by a handler.
109
+ */
110
+ export class RealtimeWhiteboardHostComponent {
111
+ constructor() {
112
+ /** Display name of the session's agent (legend, chips, popover, toast). */
113
+ this.AgentName = 'Agent';
114
+ /** Board title shown in the header. */
115
+ this.BoardTitle = 'Whiteboard';
116
+ /** Persistence chip text (e.g. "Saved to session · v14"). */
117
+ this.SavedLabel = 'Saved to session';
118
+ /** Debounced (750 ms), coalesced scene-delta JSON — the live perception feed. */
119
+ this.SceneDelta = new EventEmitter();
120
+ /** The user clicked Undo on the agent-action toast (the undo itself already applied). */
121
+ this.AgentUndo = new EventEmitter();
122
+ /** Focus-board toggle — the session shell collapses/restores the call rail. */
123
+ this.FocusModeChange = new EventEmitter();
124
+ /** Emitted when the user picks "Save to artifacts" — the channel plugin persists via its host context. */
125
+ this.SaveToArtifactsRequested = new EventEmitter();
126
+ /**
127
+ * Cancelable BEFORE event (forwarded from the board): a sandboxed HTML widget submitted
128
+ * input via `MJWhiteboard.submit` and it passed validation. Set `Cancel = true`
129
+ * synchronously to drop it before {@link WidgetSubmitted} fires.
130
+ */
131
+ this.WidgetSubmitting = new EventEmitter();
132
+ /**
133
+ * AFTER event: a sandboxed HTML widget submitted user input via `MJWhiteboard.submit`
134
+ * (validated + size-capped by the board, not canceled). Integration layers forward it
135
+ * to their agent runtime — e.g. MJ's channel plugin sends a `[whiteboard]` context note.
136
+ */
137
+ this.WidgetSubmitted = new EventEmitter();
138
+ /**
139
+ * AMBIENT widget telemetry (forwarded from the board): the injected recorder observed
140
+ * passive form activity (clicks / changes / typing / focus) inside a sandboxed HTML
141
+ * widget, validated + pre-summarized. Integration layers forward it to their agent
142
+ * runtime as throttled, low-priority background context — e.g. MJ's channel plugin sends
143
+ * an "ambient activity" `[whiteboard]` note at most once per widget per few seconds.
144
+ */
145
+ this.WidgetInteraction = new EventEmitter();
146
+ /**
147
+ * Cancelable BEFORE event (forwarded from the board): an in-board editor commit is
148
+ * about to write to the state engine. Set `Cancel = true` synchronously to discard it.
149
+ */
150
+ this.ContentApplying = new EventEmitter();
151
+ /** AFTER event (forwarded from the board): an editor commit applied. */
152
+ this.ContentApplied = new EventEmitter();
153
+ // Engine mutation mirrors — the same before/after events {@link WhiteboardState} raises,
154
+ // re-emitted as component outputs so template-driven consumers can bind them without
155
+ // subscribing to the engine directly. Emission is synchronous, so cancelable BEFORE
156
+ // mirrors (`ItemAdding` / `ItemUpdating` / `ItemRemoving`) honor `Cancel = true` set by
157
+ // a template handler exactly like a direct engine subscription would.
158
+ /** Cancelable BEFORE mirror of {@link WhiteboardState.ItemAdding$}. */
159
+ this.ItemAdding = new EventEmitter();
160
+ /** AFTER mirror of {@link WhiteboardState.ItemAdded$}. */
161
+ this.ItemAdded = new EventEmitter();
162
+ /** Cancelable BEFORE mirror of {@link WhiteboardState.ItemUpdating$} (update / move / reorder). */
163
+ this.ItemUpdating = new EventEmitter();
164
+ /** AFTER mirror of {@link WhiteboardState.ItemUpdated$}. */
165
+ this.ItemUpdated = new EventEmitter();
166
+ /** Cancelable BEFORE mirror of {@link WhiteboardState.ItemRemoving$}. */
167
+ this.ItemRemoving = new EventEmitter();
168
+ /** AFTER mirror of {@link WhiteboardState.ItemRemoved$}. */
169
+ this.ItemRemoved = new EventEmitter();
170
+ // tool state (host-owned; toolbar + keyboard drive it, board consumes it)
171
+ this.Tool = 'select';
172
+ this.PenColor = WHITEBOARD_PEN_COLORS[0];
173
+ this.PenWidth = 4;
174
+ this.ShapeKind = 'rect';
175
+ this.TextSize = 12;
176
+ this.TextFamily = 'sans';
177
+ this.TextBold = true;
178
+ this.TextColor = null;
179
+ // header state
180
+ this.SeesOpen = false;
181
+ this.FocusMode = false;
182
+ this.ExportOpen = false;
183
+ // agent garnish state
184
+ this.AgentPresence = null;
185
+ this.ToastText = null;
186
+ this.toastPhrases = [];
187
+ this.toastTimer = null;
188
+ this.presenceTimer = null;
189
+ // perception feed state
190
+ this.LastDeltaJson = '';
191
+ this.lastDeltaToken = 0;
192
+ this.lastDeltaAt = null;
193
+ this.deltaTimer = null;
194
+ // "synced n s ago" ticker
195
+ this.NowTick = Date.now();
196
+ this.ticker = null;
197
+ this.mirrorSubs = [];
198
+ this.cdr = inject(ChangeDetectorRef);
199
+ }
200
+ ngOnInit() {
201
+ this.changedSub = this.State.Changed$.subscribe((change) => {
202
+ // perception feed: user edits (and scene replacements like undo) schedule a delta
203
+ if (change.Author === 'user' || change.Op === 'replace') {
204
+ this.scheduleSceneDelta();
205
+ }
206
+ this.cdr.markForCheck();
207
+ });
208
+ // Mirror the engine's before/after mutation events as component outputs. The re-emit
209
+ // runs synchronously inside the engine's emit, so Cancel set by an output handler
210
+ // still vetoes the mutation.
211
+ this.mirrorSubs.push(this.State.ItemAdding$.subscribe((e) => this.ItemAdding.emit(e)), this.State.ItemAdded$.subscribe((e) => this.ItemAdded.emit(e)), this.State.ItemUpdating$.subscribe((e) => this.ItemUpdating.emit(e)), this.State.ItemUpdated$.subscribe((e) => this.ItemUpdated.emit(e)), this.State.ItemRemoving$.subscribe((e) => this.ItemRemoving.emit(e)), this.State.ItemRemoved$.subscribe((e) => this.ItemRemoved.emit(e)));
212
+ this.ticker = setInterval(() => {
213
+ this.NowTick = Date.now();
214
+ this.cdr.markForCheck();
215
+ }, 1000);
216
+ }
217
+ ngOnDestroy() {
218
+ this.changedSub?.unsubscribe();
219
+ for (const sub of this.mirrorSubs) {
220
+ sub.unsubscribe();
221
+ }
222
+ this.mirrorSubs = [];
223
+ if (this.deltaTimer) {
224
+ clearTimeout(this.deltaTimer);
225
+ }
226
+ if (this.toastTimer) {
227
+ clearTimeout(this.toastTimer);
228
+ }
229
+ if (this.presenceTimer) {
230
+ clearTimeout(this.presenceTimer);
231
+ }
232
+ if (this.ticker) {
233
+ clearInterval(this.ticker);
234
+ }
235
+ }
236
+ // ────────────────────────────────────────────── AGENT tool entry point
237
+ /**
238
+ * Execute one agent whiteboard tool (author `'agent'`, one undo batch), with the UI
239
+ * garnish: presence cursor glides to the mutation point, the new item pops in violet,
240
+ * and the action toast (with Undo) appears. Returns the result JSON string that the
241
+ * integration layer feeds back to the realtime model as the `tool_response`.
242
+ */
243
+ ApplyAgentTool(toolName, argsJson) {
244
+ const resultJson = ApplyWhiteboardAgentTool(this.State, toolName, argsJson);
245
+ let result = null;
246
+ try {
247
+ result = JSON.parse(resultJson);
248
+ }
249
+ catch {
250
+ result = null;
251
+ }
252
+ if (result?.success) {
253
+ this.showAgentPresence(result.itemId);
254
+ this.pushToastPhrase(RealtimeWhiteboardHostComponent.toolPhrase(toolName));
255
+ }
256
+ this.cdr.markForCheck();
257
+ return resultJson;
258
+ }
259
+ // ────────────────────────────────────────────── header actions
260
+ ToggleSees(event) {
261
+ event.stopPropagation();
262
+ this.SeesOpen = !this.SeesOpen;
263
+ }
264
+ CloseSees() {
265
+ if (this.SeesOpen) {
266
+ this.SeesOpen = false;
267
+ }
268
+ }
269
+ ToggleFocus() {
270
+ this.FocusMode = !this.FocusMode;
271
+ this.FocusModeChange.emit(this.FocusMode);
272
+ }
273
+ // ────────────────────────────────────────────── export menu
274
+ ToggleExport(event) {
275
+ event.stopPropagation();
276
+ this.ExportOpen = !this.ExportOpen;
277
+ }
278
+ CloseExport() {
279
+ if (this.ExportOpen) {
280
+ this.ExportOpen = false;
281
+ }
282
+ }
283
+ /** Download the board as one self-contained HTML document. */
284
+ /** Request a first-class artifact snapshot of the board (handled by the channel plugin). */
285
+ ExportSaveToArtifacts() {
286
+ this.ExportOpen = false;
287
+ this.SaveToArtifactsRequested.emit();
288
+ }
289
+ ExportDownloadHtml() {
290
+ this.downloadBlob(this.buildExportHtml(), 'text/html', `whiteboard-${RealtimeWhiteboardHostComponent.exportDateStamp()}.html`);
291
+ this.ExportOpen = false;
292
+ }
293
+ /** Download ONE self-contained HTML document containing EVERY page (titled sections). */
294
+ ExportDownloadHtmlAllPages() {
295
+ const html = BuildWhiteboardExportHtmlAllPages(this.State, this.exportOptions());
296
+ this.downloadBlob(html, 'text/html', `whiteboard-all-pages-${RealtimeWhiteboardHostComponent.exportDateStamp()}.html`);
297
+ this.ExportOpen = false;
298
+ }
299
+ /** Download the ACTIVE page as a standalone SVG document. */
300
+ ExportDownloadSvg() {
301
+ this.downloadBlob(BuildWhiteboardExportSvg(this.State), 'image/svg+xml', `whiteboard-${RealtimeWhiteboardHostComponent.exportDateStamp()}.svg`);
302
+ this.ExportOpen = false;
303
+ }
304
+ /**
305
+ * Download EVERY page as its own standalone SVG document (SVG is single-image, so
306
+ * "all pages" is one file per page, downloaded sequentially with the page name
307
+ * slug-suffixed into each filename).
308
+ */
309
+ ExportDownloadSvgAllPages() {
310
+ const stamp = RealtimeWhiteboardHostComponent.exportDateStamp();
311
+ for (const page of BuildWhiteboardExportSvgPages(this.State)) {
312
+ const slug = RealtimeWhiteboardHostComponent.fileNameSlug(page.PageName) || page.PageID;
313
+ this.downloadBlob(page.Content, 'image/svg+xml', `whiteboard-${stamp}-${slug}.svg`);
314
+ }
315
+ this.ExportOpen = false;
316
+ }
317
+ /** Whether the board has more than one page (gates the "all pages" export entries). */
318
+ get HasMultiplePages() {
319
+ return this.State.Pages.length > 1;
320
+ }
321
+ /**
322
+ * Print the board: open a blank window, write the self-contained export document and
323
+ * invoke the print dialog (same precedent as the artifact viewer's print path). When the
324
+ * popup is blocked this is a logged no-op — nothing breaks, the user can retry.
325
+ */
326
+ ExportPrint() {
327
+ this.ExportOpen = false;
328
+ const printWindow = window.open('about:blank', '_blank');
329
+ if (!printWindow) {
330
+ console.warn('Whiteboard print: popup was blocked — allow popups for this site and retry.');
331
+ return;
332
+ }
333
+ printWindow.document.write(this.buildExportHtml());
334
+ printWindow.document.close();
335
+ printWindow.focus();
336
+ // let the document lay out before invoking the dialog (artifact-viewer precedent)
337
+ setTimeout(() => printWindow.print(), 250);
338
+ }
339
+ buildExportHtml() {
340
+ return BuildWhiteboardExportHtml(this.State, this.exportOptions());
341
+ }
342
+ /** Shared options for the HTML export builders (single page + all pages). */
343
+ exportOptions() {
344
+ return {
345
+ Title: this.BoardTitle,
346
+ AgentName: this.AgentName,
347
+ GeneratedAt: new Date().toLocaleString()
348
+ };
349
+ }
350
+ /** Safe filename fragment from a page name (lowercased, non-alphanumerics collapse to '-'). */
351
+ static fileNameSlug(name) {
352
+ return (name ?? '').toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '').slice(0, 40);
353
+ }
354
+ downloadBlob(content, mimeType, fileName) {
355
+ const url = URL.createObjectURL(new Blob([content], { type: mimeType }));
356
+ const anchor = document.createElement('a');
357
+ anchor.href = url;
358
+ anchor.download = fileName;
359
+ anchor.click();
360
+ URL.revokeObjectURL(url);
361
+ }
362
+ static exportDateStamp() {
363
+ return new Date().toISOString().slice(0, 10);
364
+ }
365
+ // ────────────────────────────────────────────── toast
366
+ OnToastUndo(event) {
367
+ event.stopPropagation();
368
+ // The agent's tool call ran as ONE undo batch, so a single Undo reverts it whole.
369
+ this.State.Undo();
370
+ this.hideToast();
371
+ this.AgentUndo.emit();
372
+ }
373
+ // ────────────────────────────────────────────── keyboard shortcuts
374
+ OnKeydown(event) {
375
+ const target = event.target;
376
+ if (target && (target.tagName === 'INPUT' || target.tagName === 'TEXTAREA' || target.isContentEditable)) {
377
+ return;
378
+ }
379
+ if ((event.metaKey || event.ctrlKey) && event.key.toLowerCase() === 'z') {
380
+ event.preventDefault();
381
+ if (event.shiftKey) {
382
+ this.State.Redo();
383
+ }
384
+ else {
385
+ this.State.Undo();
386
+ }
387
+ return;
388
+ }
389
+ if ((event.metaKey || event.ctrlKey) && event.key.toLowerCase() === 'y') {
390
+ event.preventDefault();
391
+ this.State.Redo();
392
+ return;
393
+ }
394
+ if (event.metaKey || event.ctrlKey || event.altKey) {
395
+ return;
396
+ }
397
+ switch (event.key) {
398
+ case 'Escape':
399
+ this.Tool = 'select';
400
+ this.Board?.CancelTransient();
401
+ this.State.Select(null);
402
+ break;
403
+ case 'Delete':
404
+ case 'Backspace':
405
+ if (this.State.SelectedIDs.length > 0) {
406
+ event.preventDefault();
407
+ // removes the WHOLE (single or multi) selection as one undo step
408
+ this.State.RemoveSelected('user');
409
+ }
410
+ break;
411
+ default: {
412
+ const tool = RealtimeWhiteboardHostComponent.toolForKey(event.key);
413
+ if (tool) {
414
+ this.Tool = tool;
415
+ }
416
+ break;
417
+ }
418
+ }
419
+ }
420
+ // ────────────────────────────────────────────── status footer / popover bindings
421
+ get ElementCount() {
422
+ return this.State.ElementCount;
423
+ }
424
+ get UserCount() {
425
+ return this.State.CountByAuthor('user');
426
+ }
427
+ get AgentCount() {
428
+ return this.State.CountByAuthor('agent');
429
+ }
430
+ get CurrentSeq() {
431
+ return this.State.CurrentSeq;
432
+ }
433
+ /** "2 s ago" style age of the last emitted delta (the ticker keeps it fresh). */
434
+ get LastDeltaLabel() {
435
+ if (this.lastDeltaAt == null) {
436
+ return '—';
437
+ }
438
+ const seconds = Math.max(0, Math.round((this.NowTick - this.lastDeltaAt) / 1000));
439
+ if (seconds < 60) {
440
+ return `${seconds} s ago`;
441
+ }
442
+ return `${Math.floor(seconds / 60)} min ago`;
443
+ }
444
+ get SceneDeltaDebounceMs() {
445
+ return SCENE_DELTA_DEBOUNCE_MS;
446
+ }
447
+ // ────────────────────────────────────────────── internals
448
+ scheduleSceneDelta() {
449
+ if (this.deltaTimer) {
450
+ clearTimeout(this.deltaTimer);
451
+ }
452
+ this.deltaTimer = setTimeout(() => {
453
+ this.deltaTimer = null;
454
+ this.emitSceneDelta();
455
+ }, SCENE_DELTA_DEBOUNCE_MS);
456
+ }
457
+ emitSceneDelta() {
458
+ const delta = this.State.BuildSceneDelta(this.lastDeltaToken);
459
+ this.lastDeltaToken = this.State.CurrentSeq;
460
+ this.lastDeltaAt = Date.now();
461
+ this.LastDeltaJson = JSON.stringify(delta, null, 2);
462
+ this.SceneDelta.emit(JSON.stringify(delta));
463
+ this.cdr.markForCheck();
464
+ }
465
+ showAgentPresence(itemId) {
466
+ let x = 480;
467
+ let y = 220;
468
+ const item = itemId ? this.State.GetItem(itemId) : undefined;
469
+ if (item) {
470
+ const b = this.State.ItemBounds(item);
471
+ x = b.X + b.W + 14;
472
+ y = b.Y + b.H / 2 - 13;
473
+ }
474
+ this.AgentPresence = { Active: true, X: x, Y: y, Label: `${this.AgentName} is drawing…` };
475
+ if (this.presenceTimer) {
476
+ clearTimeout(this.presenceTimer);
477
+ }
478
+ this.presenceTimer = setTimeout(() => {
479
+ this.AgentPresence = null;
480
+ this.cdr.markForCheck();
481
+ }, PRESENCE_MS);
482
+ }
483
+ pushToastPhrase(phrase) {
484
+ if (!this.ToastText) {
485
+ this.toastPhrases = [];
486
+ }
487
+ if (!this.toastPhrases.includes(phrase)) {
488
+ this.toastPhrases.push(phrase);
489
+ }
490
+ const joined = this.toastPhrases.length > 1
491
+ ? `${this.toastPhrases.slice(0, -1).join(', ')} & ${this.toastPhrases[this.toastPhrases.length - 1]}`
492
+ : this.toastPhrases[0];
493
+ this.ToastText = `${this.AgentName} ${joined} · just now`;
494
+ if (this.toastTimer) {
495
+ clearTimeout(this.toastTimer);
496
+ }
497
+ this.toastTimer = setTimeout(() => this.hideToast(), TOAST_MS);
498
+ }
499
+ hideToast() {
500
+ this.ToastText = null;
501
+ this.toastPhrases = [];
502
+ if (this.toastTimer) {
503
+ clearTimeout(this.toastTimer);
504
+ this.toastTimer = null;
505
+ }
506
+ this.cdr.markForCheck();
507
+ }
508
+ static toolPhrase(toolName) {
509
+ switch (toolName) {
510
+ case WHITEBOARD_TOOL_NAMES.AddNote: return 'added a note';
511
+ case WHITEBOARD_TOOL_NAMES.AddShape: return 'added a shape';
512
+ case WHITEBOARD_TOOL_NAMES.AddText: return 'added a label';
513
+ case WHITEBOARD_TOOL_NAMES.AddMarkdown: return 'added a markdown panel';
514
+ case WHITEBOARD_TOOL_NAMES.AddHtml: return 'added an HTML widget';
515
+ case WHITEBOARD_TOOL_NAMES.UpdateContent: return 'edited an item';
516
+ case WHITEBOARD_TOOL_NAMES.DrawConnector: return 'added a connector';
517
+ case WHITEBOARD_TOOL_NAMES.Highlight: return 'highlighted a region';
518
+ case WHITEBOARD_TOOL_NAMES.MoveItem: return 'moved an item';
519
+ case WHITEBOARD_TOOL_NAMES.RemoveItem: return 'removed an item';
520
+ case WHITEBOARD_TOOL_NAMES.StyleItem: return 'restyled an item';
521
+ case WHITEBOARD_TOOL_NAMES.AddPage: return 'created a page';
522
+ case WHITEBOARD_TOOL_NAMES.SwitchPage: return 'switched pages';
523
+ case WHITEBOARD_TOOL_NAMES.RenamePage: return 'renamed a page';
524
+ default: return 'updated the board';
525
+ }
526
+ }
527
+ static toolForKey(key) {
528
+ switch (key.toLowerCase()) {
529
+ case 'v': return 'select';
530
+ case 'h': return 'pan';
531
+ case 'p': return 'pen';
532
+ case 'r': return 'shape';
533
+ case 's': return 'sticky';
534
+ case 't': return 'text';
535
+ case 'm': return 'markdown';
536
+ case 'w': return 'html';
537
+ case 'i': return 'image';
538
+ case 'c': return 'connector';
539
+ case 'e': return 'eraser';
540
+ default: return null;
541
+ }
542
+ }
543
+ static { this.ɵfac = function RealtimeWhiteboardHostComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || RealtimeWhiteboardHostComponent)(); }; }
544
+ static { this.ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: RealtimeWhiteboardHostComponent, selectors: [["mj-realtime-whiteboard-host"]], viewQuery: function RealtimeWhiteboardHostComponent_Query(rf, ctx) { if (rf & 1) {
545
+ i0.ɵɵviewQuery(RealtimeWhiteboardBoardComponent, 5);
546
+ } if (rf & 2) {
547
+ let _t;
548
+ i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.Board = _t.first);
549
+ } }, hostBindings: function RealtimeWhiteboardHostComponent_HostBindings(rf, ctx) { if (rf & 1) {
550
+ i0.ɵɵlistener("click", function RealtimeWhiteboardHostComponent_click_HostBindingHandler() { return ctx.CloseExport(); }, i0.ɵɵresolveDocument)("keydown", function RealtimeWhiteboardHostComponent_keydown_HostBindingHandler($event) { return ctx.OnKeydown($event); }, i0.ɵɵresolveDocument);
551
+ } }, inputs: { State: "State", AgentName: "AgentName", BoardTitle: "BoardTitle", SavedLabel: "SavedLabel" }, outputs: { SceneDelta: "SceneDelta", AgentUndo: "AgentUndo", FocusModeChange: "FocusModeChange", SaveToArtifactsRequested: "SaveToArtifactsRequested", WidgetSubmitting: "WidgetSubmitting", WidgetSubmitted: "WidgetSubmitted", WidgetInteraction: "WidgetInteraction", ContentApplying: "ContentApplying", ContentApplied: "ContentApplied", ItemAdding: "ItemAdding", ItemAdded: "ItemAdded", ItemUpdating: "ItemUpdating", ItemUpdated: "ItemUpdated", ItemRemoving: "ItemRemoving", ItemRemoved: "ItemRemoved" }, decls: 70, vars: 48, consts: [["board", ""], [1, "board-head"], [1, "bt"], [1, "fa-solid", "fa-chalkboard"], [1, "board-save"], [1, "fa-solid", "fa-circle-check"], [1, "board-legend"], [1, "sw", "sw--you"], [1, "sw", "sw--sage"], [1, "fa-solid", "fa-feather", "legend-feather"], [1, "head-end"], ["type", "button", 1, "head-btn", "head-btn--sees", 3, "click"], [1, "eye-pulse"], [1, "fa-regular", "fa-eye"], [3, "AgentName", "DeltaJson", "DebounceMs", "ElementCount", "LastDeltaLabel"], [1, "export-wrap"], ["type", "button", 1, "head-btn", 3, "click"], [1, "fa-solid", "fa-file-export"], [1, "export-menu"], ["type", "button", "title", "More", 1, "head-btn", "head-btn--more"], [1, "fa-solid", "fa-ellipsis"], [1, "board-wrap"], [3, "ToolChangeRequest", "WidgetSubmitting", "WidgetSubmitted", "WidgetInteraction", "ContentApplying", "ContentApplied", "State", "AgentName", "Tool", "PenColor", "PenWidth", "ShapeKind", "TextSize", "TextFamily", "TextBold", "TextColor", "AgentPresence"], [3, "ToolChange", "PenColorChange", "PenWidthChange", "ShapeKindChange", "TextSizeChange", "TextFamilyChange", "TextBoldChange", "TextColorChange", "StyleSelection", "Undo", "Redo", "ActiveTool", "PenColor", "PenWidth", "ShapeKind", "TextSize", "TextFamily", "TextBold", "TextColor", "CanUndo", "CanRedo", "AgentName"], [3, "ZoomOut", "ZoomIn", "ZoomBy", "Fit", "ToggleMinimap", "ZoomPercent", "MinimapOpen"], [1, "board-toast"], [1, "board-status"], [1, "stat"], [1, "seg-r"], [1, "stat", "ctx-live", 3, "title"], [1, "d"], [1, "ctx-live__text"], ["title", "Persists as a session artifact when the call ends", 1, "stat", "persist"], ["aria-hidden", "true", 1, "fa-solid", "fa-box-archive"], [1, "persist__long"], [1, "persist__short"], ["type", "button", 1, "export-item", 3, "click"], [1, "fa-solid", "fa-file-code"], ["type", "button", 1, "export-item"], [1, "fa-solid", "fa-bezier-curve"], ["type", "button", "title", "One SVG file per page", 1, "export-item"], [1, "fa-solid", "fa-print"], [1, "fa-solid", "fa-box-archive"], ["type", "button", "title", "One SVG file per page", 1, "export-item", 3, "click"], [1, "fa-solid", "fa-feather"], [1, "board-toast__text"], [1, "undo", 3, "click"], [1, "fa-solid", "fa-rotate-left"]], template: function RealtimeWhiteboardHostComponent_Template(rf, ctx) { if (rf & 1) {
552
+ const _r1 = i0.ɵɵgetCurrentView();
553
+ i0.ɵɵelementStart(0, "div", 1)(1, "span", 2);
554
+ i0.ɵɵelement(2, "i", 3);
555
+ i0.ɵɵtext(3);
556
+ i0.ɵɵelementEnd();
557
+ i0.ɵɵelementStart(4, "span", 4);
558
+ i0.ɵɵelement(5, "i", 5);
559
+ i0.ɵɵtext(6);
560
+ i0.ɵɵelementEnd();
561
+ i0.ɵɵelementStart(7, "span", 6)(8, "span");
562
+ i0.ɵɵelement(9, "span", 7);
563
+ i0.ɵɵtext(10, "You");
564
+ i0.ɵɵelementEnd();
565
+ i0.ɵɵelementStart(11, "span");
566
+ i0.ɵɵelement(12, "span", 8);
567
+ i0.ɵɵtext(13);
568
+ i0.ɵɵelement(14, "i", 9);
569
+ i0.ɵɵelementEnd()();
570
+ i0.ɵɵelementStart(15, "div", 10)(16, "button", 11);
571
+ i0.ɵɵlistener("click", function RealtimeWhiteboardHostComponent_Template_button_click_16_listener($event) { i0.ɵɵrestoreView(_r1); return i0.ɵɵresetView(ctx.ToggleSees($event)); });
572
+ i0.ɵɵelement(17, "span", 12)(18, "i", 13);
573
+ i0.ɵɵtext(19);
574
+ i0.ɵɵconditionalCreate(20, RealtimeWhiteboardHostComponent_Conditional_20_Template, 1, 5, "mj-realtime-whiteboard-agent-sees-popover", 14);
575
+ i0.ɵɵelementEnd();
576
+ i0.ɵɵelementStart(21, "span", 15)(22, "button", 16);
577
+ i0.ɵɵlistener("click", function RealtimeWhiteboardHostComponent_Template_button_click_22_listener($event) { i0.ɵɵrestoreView(_r1); return i0.ɵɵresetView(ctx.ToggleExport($event)); });
578
+ i0.ɵɵelement(23, "i", 17);
579
+ i0.ɵɵtext(24, " Export ");
580
+ i0.ɵɵelementEnd();
581
+ i0.ɵɵconditionalCreate(25, RealtimeWhiteboardHostComponent_Conditional_25_Template, 15, 2, "div", 18);
582
+ i0.ɵɵelementEnd();
583
+ i0.ɵɵelementStart(26, "button", 16);
584
+ i0.ɵɵlistener("click", function RealtimeWhiteboardHostComponent_Template_button_click_26_listener() { i0.ɵɵrestoreView(_r1); return i0.ɵɵresetView(ctx.ToggleFocus()); });
585
+ i0.ɵɵelement(27, "i");
586
+ i0.ɵɵtext(28);
587
+ i0.ɵɵelementEnd();
588
+ i0.ɵɵelementStart(29, "button", 19);
589
+ i0.ɵɵelement(30, "i", 20);
590
+ i0.ɵɵelementEnd()()();
591
+ i0.ɵɵelementStart(31, "div", 21)(32, "mj-realtime-whiteboard", 22, 0);
592
+ i0.ɵɵlistener("ToolChangeRequest", function RealtimeWhiteboardHostComponent_Template_mj_realtime_whiteboard_ToolChangeRequest_32_listener($event) { i0.ɵɵrestoreView(_r1); return i0.ɵɵresetView(ctx.Tool = $event); })("WidgetSubmitting", function RealtimeWhiteboardHostComponent_Template_mj_realtime_whiteboard_WidgetSubmitting_32_listener($event) { i0.ɵɵrestoreView(_r1); return i0.ɵɵresetView(ctx.WidgetSubmitting.emit($event)); })("WidgetSubmitted", function RealtimeWhiteboardHostComponent_Template_mj_realtime_whiteboard_WidgetSubmitted_32_listener($event) { i0.ɵɵrestoreView(_r1); return i0.ɵɵresetView(ctx.WidgetSubmitted.emit($event)); })("WidgetInteraction", function RealtimeWhiteboardHostComponent_Template_mj_realtime_whiteboard_WidgetInteraction_32_listener($event) { i0.ɵɵrestoreView(_r1); return i0.ɵɵresetView(ctx.WidgetInteraction.emit($event)); })("ContentApplying", function RealtimeWhiteboardHostComponent_Template_mj_realtime_whiteboard_ContentApplying_32_listener($event) { i0.ɵɵrestoreView(_r1); return i0.ɵɵresetView(ctx.ContentApplying.emit($event)); })("ContentApplied", function RealtimeWhiteboardHostComponent_Template_mj_realtime_whiteboard_ContentApplied_32_listener($event) { i0.ɵɵrestoreView(_r1); return i0.ɵɵresetView(ctx.ContentApplied.emit($event)); });
593
+ i0.ɵɵelementEnd();
594
+ i0.ɵɵelementStart(34, "mj-realtime-whiteboard-toolbar", 23);
595
+ i0.ɵɵlistener("ToolChange", function RealtimeWhiteboardHostComponent_Template_mj_realtime_whiteboard_toolbar_ToolChange_34_listener($event) { i0.ɵɵrestoreView(_r1); return i0.ɵɵresetView(ctx.Tool = $event); })("PenColorChange", function RealtimeWhiteboardHostComponent_Template_mj_realtime_whiteboard_toolbar_PenColorChange_34_listener($event) { i0.ɵɵrestoreView(_r1); return i0.ɵɵresetView(ctx.PenColor = $event); })("PenWidthChange", function RealtimeWhiteboardHostComponent_Template_mj_realtime_whiteboard_toolbar_PenWidthChange_34_listener($event) { i0.ɵɵrestoreView(_r1); return i0.ɵɵresetView(ctx.PenWidth = $event); })("ShapeKindChange", function RealtimeWhiteboardHostComponent_Template_mj_realtime_whiteboard_toolbar_ShapeKindChange_34_listener($event) { i0.ɵɵrestoreView(_r1); return i0.ɵɵresetView(ctx.ShapeKind = $event); })("TextSizeChange", function RealtimeWhiteboardHostComponent_Template_mj_realtime_whiteboard_toolbar_TextSizeChange_34_listener($event) { i0.ɵɵrestoreView(_r1); return i0.ɵɵresetView(ctx.TextSize = $event); })("TextFamilyChange", function RealtimeWhiteboardHostComponent_Template_mj_realtime_whiteboard_toolbar_TextFamilyChange_34_listener($event) { i0.ɵɵrestoreView(_r1); return i0.ɵɵresetView(ctx.TextFamily = $event); })("TextBoldChange", function RealtimeWhiteboardHostComponent_Template_mj_realtime_whiteboard_toolbar_TextBoldChange_34_listener($event) { i0.ɵɵrestoreView(_r1); return i0.ɵɵresetView(ctx.TextBold = $event); })("TextColorChange", function RealtimeWhiteboardHostComponent_Template_mj_realtime_whiteboard_toolbar_TextColorChange_34_listener($event) { i0.ɵɵrestoreView(_r1); return i0.ɵɵresetView(ctx.TextColor = $event); })("StyleSelection", function RealtimeWhiteboardHostComponent_Template_mj_realtime_whiteboard_toolbar_StyleSelection_34_listener($event) { i0.ɵɵrestoreView(_r1); const board_r6 = i0.ɵɵreference(33); return i0.ɵɵresetView(board_r6.ApplyTextStyleToSelection($event)); })("Undo", function RealtimeWhiteboardHostComponent_Template_mj_realtime_whiteboard_toolbar_Undo_34_listener() { i0.ɵɵrestoreView(_r1); return i0.ɵɵresetView(ctx.State.Undo()); })("Redo", function RealtimeWhiteboardHostComponent_Template_mj_realtime_whiteboard_toolbar_Redo_34_listener() { i0.ɵɵrestoreView(_r1); return i0.ɵɵresetView(ctx.State.Redo()); });
596
+ i0.ɵɵelementEnd();
597
+ i0.ɵɵelementStart(35, "mj-realtime-whiteboard-zoom", 24);
598
+ i0.ɵɵlistener("ZoomOut", function RealtimeWhiteboardHostComponent_Template_mj_realtime_whiteboard_zoom_ZoomOut_35_listener() { i0.ɵɵrestoreView(_r1); const board_r6 = i0.ɵɵreference(33); return i0.ɵɵresetView(board_r6.ZoomOut()); })("ZoomIn", function RealtimeWhiteboardHostComponent_Template_mj_realtime_whiteboard_zoom_ZoomIn_35_listener() { i0.ɵɵrestoreView(_r1); const board_r6 = i0.ɵɵreference(33); return i0.ɵɵresetView(board_r6.ZoomIn()); })("ZoomBy", function RealtimeWhiteboardHostComponent_Template_mj_realtime_whiteboard_zoom_ZoomBy_35_listener($event) { i0.ɵɵrestoreView(_r1); const board_r6 = i0.ɵɵreference(33); return i0.ɵɵresetView(board_r6.ZoomByFactor($event)); })("Fit", function RealtimeWhiteboardHostComponent_Template_mj_realtime_whiteboard_zoom_Fit_35_listener() { i0.ɵɵrestoreView(_r1); const board_r6 = i0.ɵɵreference(33); return i0.ɵɵresetView(board_r6.FitToContent()); })("ToggleMinimap", function RealtimeWhiteboardHostComponent_Template_mj_realtime_whiteboard_zoom_ToggleMinimap_35_listener() { i0.ɵɵrestoreView(_r1); const board_r6 = i0.ɵɵreference(33); return i0.ɵɵresetView(board_r6.ToggleMinimap()); });
599
+ i0.ɵɵelementEnd();
600
+ i0.ɵɵconditionalCreate(36, RealtimeWhiteboardHostComponent_Conditional_36_Template, 7, 1, "div", 25);
601
+ i0.ɵɵelementEnd();
602
+ i0.ɵɵelementStart(37, "div", 26)(38, "span", 27)(39, "b");
603
+ i0.ɵɵtext(40);
604
+ i0.ɵɵelementEnd();
605
+ i0.ɵɵtext(41, " elements");
606
+ i0.ɵɵelementEnd();
607
+ i0.ɵɵelementStart(42, "span", 27);
608
+ i0.ɵɵtext(43, "you ");
609
+ i0.ɵɵelementStart(44, "b");
610
+ i0.ɵɵtext(45);
611
+ i0.ɵɵelementEnd();
612
+ i0.ɵɵtext(46);
613
+ i0.ɵɵelementStart(47, "b");
614
+ i0.ɵɵtext(48);
615
+ i0.ɵɵelementEnd()();
616
+ i0.ɵɵelementStart(49, "span", 27);
617
+ i0.ɵɵtext(50, "synced ");
618
+ i0.ɵɵelementStart(51, "b");
619
+ i0.ɵɵtext(52);
620
+ i0.ɵɵelementEnd();
621
+ i0.ɵɵtext(53, " \u00B7 seq ");
622
+ i0.ɵɵelementStart(54, "b");
623
+ i0.ɵɵtext(55);
624
+ i0.ɵɵelementEnd()();
625
+ i0.ɵɵelementStart(56, "span", 28)(57, "span", 29);
626
+ i0.ɵɵelement(58, "span", 30);
627
+ i0.ɵɵelementStart(59, "span", 31);
628
+ i0.ɵɵtext(60);
629
+ i0.ɵɵelementEnd()();
630
+ i0.ɵɵelementStart(61, "span", 32);
631
+ i0.ɵɵelement(62, "i", 33);
632
+ i0.ɵɵelementStart(63, "span", 34);
633
+ i0.ɵɵtext(64, "persists as ");
634
+ i0.ɵɵelementStart(65, "b");
635
+ i0.ɵɵtext(66, "session artifact");
636
+ i0.ɵɵelementEnd();
637
+ i0.ɵɵtext(67, " when the call ends");
638
+ i0.ɵɵelementEnd();
639
+ i0.ɵɵelementStart(68, "span", 35);
640
+ i0.ɵɵtext(69, "session artifact");
641
+ i0.ɵɵelementEnd()()()();
642
+ } if (rf & 2) {
643
+ const board_r6 = i0.ɵɵreference(33);
644
+ i0.ɵɵadvance(3);
645
+ i0.ɵɵtextInterpolate1(" ", ctx.BoardTitle);
646
+ i0.ɵɵadvance(3);
647
+ i0.ɵɵtextInterpolate1(" ", ctx.SavedLabel);
648
+ i0.ɵɵadvance(7);
649
+ i0.ɵɵtextInterpolate1("", ctx.AgentName, " ");
650
+ i0.ɵɵadvance(3);
651
+ i0.ɵɵclassProp("on", ctx.SeesOpen);
652
+ i0.ɵɵadvance(3);
653
+ i0.ɵɵtextInterpolate1(" What ", ctx.AgentName, " sees ");
654
+ i0.ɵɵadvance();
655
+ i0.ɵɵconditional(ctx.SeesOpen ? 20 : -1);
656
+ i0.ɵɵadvance(2);
657
+ i0.ɵɵclassProp("on", ctx.ExportOpen);
658
+ i0.ɵɵadvance(3);
659
+ i0.ɵɵconditional(ctx.ExportOpen ? 25 : -1);
660
+ i0.ɵɵadvance();
661
+ i0.ɵɵclassProp("on", ctx.FocusMode);
662
+ i0.ɵɵadvance();
663
+ i0.ɵɵclassMap(ctx.FocusMode ? "fa-solid fa-compress" : "fa-solid fa-expand");
664
+ i0.ɵɵadvance();
665
+ i0.ɵɵtextInterpolate1(" ", ctx.FocusMode ? "Show thread" : "Focus board", " ");
666
+ i0.ɵɵadvance(4);
667
+ i0.ɵɵproperty("State", ctx.State)("AgentName", ctx.AgentName)("Tool", ctx.Tool)("PenColor", ctx.PenColor)("PenWidth", ctx.PenWidth)("ShapeKind", ctx.ShapeKind)("TextSize", ctx.TextSize)("TextFamily", ctx.TextFamily)("TextBold", ctx.TextBold)("TextColor", ctx.TextColor)("AgentPresence", ctx.AgentPresence);
668
+ i0.ɵɵadvance(2);
669
+ i0.ɵɵproperty("ActiveTool", ctx.Tool)("PenColor", ctx.PenColor)("PenWidth", ctx.PenWidth)("ShapeKind", ctx.ShapeKind)("TextSize", ctx.TextSize)("TextFamily", ctx.TextFamily)("TextBold", ctx.TextBold)("TextColor", ctx.TextColor)("CanUndo", ctx.State.CanUndo)("CanRedo", ctx.State.CanRedo)("AgentName", ctx.AgentName);
670
+ i0.ɵɵadvance();
671
+ i0.ɵɵproperty("ZoomPercent", board_r6.ZoomPercent)("MinimapOpen", board_r6.MinimapOpen);
672
+ i0.ɵɵadvance();
673
+ i0.ɵɵconditional(ctx.ToastText ? 36 : -1);
674
+ i0.ɵɵadvance(4);
675
+ i0.ɵɵtextInterpolate(ctx.ElementCount);
676
+ i0.ɵɵadvance(5);
677
+ i0.ɵɵtextInterpolate(ctx.UserCount);
678
+ i0.ɵɵadvance();
679
+ i0.ɵɵtextInterpolate1(" \u00B7 ", ctx.AgentName, " ");
680
+ i0.ɵɵadvance(2);
681
+ i0.ɵɵtextInterpolate(ctx.AgentCount);
682
+ i0.ɵɵadvance(4);
683
+ i0.ɵɵtextInterpolate(ctx.LastDeltaLabel);
684
+ i0.ɵɵadvance(3);
685
+ i0.ɵɵtextInterpolate(ctx.CurrentSeq);
686
+ i0.ɵɵadvance(2);
687
+ i0.ɵɵproperty("title", ctx.AgentName + "\u2019s live context: current");
688
+ i0.ɵɵadvance(3);
689
+ i0.ɵɵtextInterpolate1(" ", ctx.AgentName, "'s live context: current");
690
+ } }, dependencies: [CommonModule,
691
+ RealtimeWhiteboardBoardComponent,
692
+ RealtimeWhiteboardToolbarComponent,
693
+ RealtimeWhiteboardZoomComponent,
694
+ RealtimeWhiteboardAgentSeesPopoverComponent], styles: ["\n\n\n\n\n[_nghost-%COMP%] {\n \n\n --sage: var(--mj-color-violet-400, #a78bfa);\n --sage-bg: color-mix(in srgb, var(--mj-color-violet-400, #a78bfa) 14%, transparent);\n --sage-border: color-mix(in srgb, var(--mj-color-violet-400, #a78bfa) 45%, transparent);\n --sage-chip-bg: color-mix(in srgb, var(--mj-color-violet-500, #8b5cf6) 32%, var(--mj-bg-surface-sunken));\n\n display: flex;\n flex-direction: column;\n flex: 1;\n min-height: 0;\n height: 100%;\n \n\n container-type: inline-size;\n container-name: wb-host;\n}\n\n\n\n.board-head[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 9px 16px;\n border-bottom: 1px solid var(--mj-border-default);\n background: var(--mj-bg-surface);\n flex-shrink: 0;\n flex-wrap: wrap;\n}\n.board-head[_ngcontent-%COMP%] .bt[_ngcontent-%COMP%] {\n font-weight: 700;\n font-size: 13.5px;\n display: flex;\n align-items: center;\n gap: 8px;\n color: var(--mj-text-primary);\n}\n.board-head[_ngcontent-%COMP%] .bt[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n color: var(--mj-brand-accent, var(--mj-brand-primary));\n font-size: 13px;\n}\n.board-save[_ngcontent-%COMP%] {\n font-size: 11px;\n color: var(--mj-text-muted);\n display: inline-flex;\n align-items: center;\n gap: 5px;\n}\n.board-save[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n color: var(--mj-status-success);\n font-size: 10px;\n}\n\n.board-legend[_ngcontent-%COMP%] {\n display: inline-flex;\n align-items: center;\n gap: 12px;\n font-size: 11px;\n color: var(--mj-text-muted);\n margin-left: 6px;\n padding: 4px 10px;\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-full, 9999px);\n background: var(--mj-bg-surface-card);\n}\n.board-legend[_ngcontent-%COMP%] .sw[_ngcontent-%COMP%] {\n width: 10px;\n height: 10px;\n border-radius: 3px;\n display: inline-block;\n vertical-align: -1px;\n margin-right: 5px;\n}\n.board-legend[_ngcontent-%COMP%] .sw--you[_ngcontent-%COMP%] {\n background: var(--mj-bg-surface-elevated);\n border: 1px solid var(--mj-border-strong);\n}\n.board-legend[_ngcontent-%COMP%] .sw--sage[_ngcontent-%COMP%] {\n background: var(--sage-bg);\n border: 1px solid var(--sage);\n}\n.legend-feather[_ngcontent-%COMP%] {\n font-size: 9px;\n color: var(--sage);\n}\n\n.head-end[_ngcontent-%COMP%] {\n margin-left: auto;\n display: flex;\n align-items: center;\n gap: 8px;\n position: relative;\n}\n\n.head-btn[_ngcontent-%COMP%] {\n display: inline-flex;\n align-items: center;\n gap: 7px;\n padding: 5px 11px;\n font-size: 11.5px;\n font-weight: 600;\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-full, 9999px);\n cursor: pointer;\n background: var(--mj-bg-surface-card);\n color: var(--mj-text-secondary);\n transition: all 140ms ease;\n font-family: inherit;\n}\n.head-btn[_ngcontent-%COMP%]:hover {\n color: var(--mj-text-primary);\n border-color: var(--mj-border-strong);\n}\n.head-btn.on[_ngcontent-%COMP%] {\n background: color-mix(in srgb, var(--mj-brand-primary) 14%, transparent);\n border-color: var(--mj-brand-primary);\n color: var(--mj-brand-accent, var(--mj-brand-primary));\n}\n.head-btn--sees[_ngcontent-%COMP%] {\n position: relative;\n}\n.head-btn--sees[_ngcontent-%COMP%] .eye-pulse[_ngcontent-%COMP%] {\n width: 6px;\n height: 6px;\n border-radius: 50%;\n background: var(--sage);\n box-shadow: 0 0 0 0 color-mix(in srgb, var(--sage) 50%, transparent);\n animation: _ngcontent-%COMP%_wb-pulse-ring 2s infinite;\n}\n.head-btn--more[_ngcontent-%COMP%] {\n padding: 5px 9px;\n}\n\n\n\n.export-wrap[_ngcontent-%COMP%] {\n position: relative;\n display: inline-flex;\n}\n.export-menu[_ngcontent-%COMP%] {\n position: absolute;\n top: calc(100% + 6px);\n right: 0;\n z-index: 30;\n min-width: 168px;\n display: flex;\n flex-direction: column;\n padding: 5px;\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-md, 8px);\n background: var(--mj-bg-surface-elevated);\n box-shadow: var(--mj-shadow-lg, 0 10px 15px -3px rgba(0, 0, 0, 0.3));\n}\n.export-item[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 9px;\n padding: 7px 10px;\n font-size: 11.5px;\n font-weight: 600;\n font-family: inherit;\n text-align: left;\n border: none;\n border-radius: var(--mj-radius-sm, 5px);\n background: transparent;\n color: var(--mj-text-secondary);\n cursor: pointer;\n white-space: nowrap;\n}\n.export-item[_ngcontent-%COMP%]:hover {\n background: var(--mj-bg-surface-hover);\n color: var(--mj-text-primary);\n}\n.export-item[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n width: 14px;\n text-align: center;\n color: var(--mj-text-muted);\n}\n.export-item[_ngcontent-%COMP%]:hover i[_ngcontent-%COMP%] {\n color: var(--mj-brand-accent, var(--mj-brand-primary));\n}\n@keyframes _ngcontent-%COMP%_wb-pulse-ring {\n 0% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--sage) 50%, transparent); }\n 70% { box-shadow: 0 0 0 6px transparent; }\n 100% { box-shadow: 0 0 0 0 transparent; }\n}\n\n\n\n.board-wrap[_ngcontent-%COMP%] {\n position: relative;\n flex: 1;\n min-height: 0;\n overflow: hidden;\n}\n\n\n\n\n\n\n.board-toast[_ngcontent-%COMP%] {\n position: absolute;\n left: 50%;\n bottom: 64px;\n transform: translateX(-50%);\n z-index: 9;\n display: flex;\n align-items: center;\n gap: 9px;\n padding: 8px 16px;\n max-width: calc(100% - 32px);\n border-radius: var(--mj-radius-full, 9999px);\n background: var(--sage-chip-bg);\n border: 1px solid var(--sage-border);\n color: var(--mj-color-violet-300, #c4b5fd);\n font-size: 12px;\n font-weight: 600;\n box-shadow: var(--mj-shadow-lg, 0 10px 15px -3px rgba(0, 0, 0, 0.3));\n animation: _ngcontent-%COMP%_wb-toast-fade 300ms ease both;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n.board-toast[_ngcontent-%COMP%] > i[_ngcontent-%COMP%] {\n flex-shrink: 0;\n}\n.board-toast__text[_ngcontent-%COMP%] {\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n.board-toast[_ngcontent-%COMP%] .undo[_ngcontent-%COMP%] {\n flex-shrink: 0;\n}\n@keyframes _ngcontent-%COMP%_wb-toast-fade {\n from { opacity: 0; transform: translate(-50%, 4px); }\n to { opacity: 1; transform: translate(-50%, 0); }\n}\n.board-toast[_ngcontent-%COMP%] .undo[_ngcontent-%COMP%] {\n color: var(--mj-text-muted);\n font-weight: 600;\n cursor: pointer;\n margin-left: 4px;\n}\n.board-toast[_ngcontent-%COMP%] .undo[_ngcontent-%COMP%]:hover {\n color: var(--mj-text-primary);\n}\n\n\n\n.board-status[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 18px;\n padding: 6px 16px;\n flex-shrink: 0;\n border-top: 1px solid var(--mj-border-default);\n background: var(--mj-bg-surface);\n font-size: 10.5px;\n color: var(--mj-text-muted);\n font-weight: 500;\n}\n.board-status[_ngcontent-%COMP%] b[_ngcontent-%COMP%] {\n color: var(--mj-text-secondary);\n font-weight: 600;\n}\n.board-status[_ngcontent-%COMP%] .seg-r[_ngcontent-%COMP%] {\n margin-left: auto;\n display: flex;\n align-items: center;\n gap: 18px;\n}\n.board-status[_ngcontent-%COMP%] .ctx-live[_ngcontent-%COMP%] {\n color: var(--mj-color-violet-300, var(--mj-text-secondary));\n display: inline-flex;\n align-items: center;\n gap: 6px;\n font-weight: 600;\n}\n.board-status[_ngcontent-%COMP%] .ctx-live[_ngcontent-%COMP%] .d[_ngcontent-%COMP%] {\n width: 6px;\n height: 6px;\n border-radius: 50%;\n background: var(--sage);\n animation: _ngcontent-%COMP%_wb-pulse-ring 2s infinite;\n}"] }); }
695
+ }
696
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(RealtimeWhiteboardHostComponent, [{
697
+ type: Component,
698
+ args: [{ standalone: true, selector: 'mj-realtime-whiteboard-host', imports: [
699
+ CommonModule,
700
+ RealtimeWhiteboardBoardComponent,
701
+ RealtimeWhiteboardToolbarComponent,
702
+ RealtimeWhiteboardZoomComponent,
703
+ RealtimeWhiteboardAgentSeesPopoverComponent
704
+ ], template: "<!-- board header -->\n<div class=\"board-head\">\n <span class=\"bt\"><i class=\"fa-solid fa-chalkboard\"></i> {{ BoardTitle }}</span>\n <span class=\"board-save\"><i class=\"fa-solid fa-circle-check\"></i> {{ SavedLabel }}</span>\n <span class=\"board-legend\">\n <span><span class=\"sw sw--you\"></span>You</span>\n <span><span class=\"sw sw--sage\"></span>{{ AgentName }} <i class=\"fa-solid fa-feather legend-feather\"></i></span>\n </span>\n <div class=\"head-end\">\n <button type=\"button\" class=\"head-btn head-btn--sees\" [class.on]=\"SeesOpen\" (click)=\"ToggleSees($event)\">\n <span class=\"eye-pulse\"></span><i class=\"fa-regular fa-eye\"></i> What {{ AgentName }} sees\n @if (SeesOpen) {\n <mj-realtime-whiteboard-agent-sees-popover\n [AgentName]=\"AgentName\"\n [DeltaJson]=\"LastDeltaJson\"\n [DebounceMs]=\"SceneDeltaDebounceMs\"\n [ElementCount]=\"ElementCount\"\n [LastDeltaLabel]=\"LastDeltaLabel\"/>\n }\n </button>\n <span class=\"export-wrap\">\n <button type=\"button\" class=\"head-btn\" [class.on]=\"ExportOpen\" (click)=\"ToggleExport($event)\">\n <i class=\"fa-solid fa-file-export\"></i> Export\n </button>\n @if (ExportOpen) {\n <div class=\"export-menu\">\n <button type=\"button\" class=\"export-item\" (click)=\"ExportDownloadHtml()\">\n <i class=\"fa-solid fa-file-code\"></i> Download HTML\n </button>\n @if (HasMultiplePages) {\n <button type=\"button\" class=\"export-item\" (click)=\"ExportDownloadHtmlAllPages()\">\n <i class=\"fa-solid fa-file-code\"></i> Download HTML \u2014 all pages\n </button>\n }\n <button type=\"button\" class=\"export-item\" (click)=\"ExportDownloadSvg()\">\n <i class=\"fa-solid fa-bezier-curve\"></i> Download SVG\n </button>\n @if (HasMultiplePages) {\n <button type=\"button\" class=\"export-item\" title=\"One SVG file per page\"\n (click)=\"ExportDownloadSvgAllPages()\">\n <i class=\"fa-solid fa-bezier-curve\"></i> Download SVG \u2014 all pages\n </button>\n }\n <button type=\"button\" class=\"export-item\" (click)=\"ExportPrint()\">\n <i class=\"fa-solid fa-print\"></i> Print\n </button>\n <button type=\"button\" class=\"export-item\" (click)=\"ExportSaveToArtifacts()\">\n <i class=\"fa-solid fa-box-archive\"></i> Save to artifacts\n </button>\n </div>\n }\n </span>\n <button type=\"button\" class=\"head-btn\" [class.on]=\"FocusMode\" (click)=\"ToggleFocus()\">\n <i [class]=\"FocusMode ? 'fa-solid fa-compress' : 'fa-solid fa-expand'\"></i>\n {{ FocusMode ? 'Show thread' : 'Focus board' }}\n </button>\n <button type=\"button\" class=\"head-btn head-btn--more\" title=\"More\"><i class=\"fa-solid fa-ellipsis\"></i></button>\n </div>\n</div>\n\n<!-- the board -->\n<div class=\"board-wrap\">\n <mj-realtime-whiteboard #board\n [State]=\"State\"\n [AgentName]=\"AgentName\"\n [Tool]=\"Tool\"\n [PenColor]=\"PenColor\"\n [PenWidth]=\"PenWidth\"\n [ShapeKind]=\"ShapeKind\"\n [TextSize]=\"TextSize\"\n [TextFamily]=\"TextFamily\"\n [TextBold]=\"TextBold\"\n [TextColor]=\"TextColor\"\n [AgentPresence]=\"AgentPresence\"\n (ToolChangeRequest)=\"Tool = $event\"\n (WidgetSubmitting)=\"WidgetSubmitting.emit($event)\"\n (WidgetSubmitted)=\"WidgetSubmitted.emit($event)\"\n (WidgetInteraction)=\"WidgetInteraction.emit($event)\"\n (ContentApplying)=\"ContentApplying.emit($event)\"\n (ContentApplied)=\"ContentApplied.emit($event)\"/>\n\n <mj-realtime-whiteboard-toolbar\n [ActiveTool]=\"Tool\"\n [PenColor]=\"PenColor\"\n [PenWidth]=\"PenWidth\"\n [ShapeKind]=\"ShapeKind\"\n [TextSize]=\"TextSize\"\n [TextFamily]=\"TextFamily\"\n [TextBold]=\"TextBold\"\n [TextColor]=\"TextColor\"\n [CanUndo]=\"State.CanUndo\"\n [CanRedo]=\"State.CanRedo\"\n [AgentName]=\"AgentName\"\n (ToolChange)=\"Tool = $event\"\n (PenColorChange)=\"PenColor = $event\"\n (PenWidthChange)=\"PenWidth = $event\"\n (ShapeKindChange)=\"ShapeKind = $event\"\n (TextSizeChange)=\"TextSize = $event\"\n (TextFamilyChange)=\"TextFamily = $event\"\n (TextBoldChange)=\"TextBold = $event\"\n (TextColorChange)=\"TextColor = $event\"\n (StyleSelection)=\"board.ApplyTextStyleToSelection($event)\"\n (Undo)=\"State.Undo()\"\n (Redo)=\"State.Redo()\"/>\n\n <mj-realtime-whiteboard-zoom\n [ZoomPercent]=\"board.ZoomPercent\"\n [MinimapOpen]=\"board.MinimapOpen\"\n (ZoomOut)=\"board.ZoomOut()\"\n (ZoomIn)=\"board.ZoomIn()\"\n (ZoomBy)=\"board.ZoomByFactor($event)\"\n (Fit)=\"board.FitToContent()\"\n (ToggleMinimap)=\"board.ToggleMinimap()\"/>\n\n <!-- agent action toast -->\n @if (ToastText) {\n <div class=\"board-toast\">\n <i class=\"fa-solid fa-feather\"></i>\n <span class=\"board-toast__text\">{{ ToastText }}</span>\n <span class=\"undo\" (click)=\"OnToastUndo($event)\"><i class=\"fa-solid fa-rotate-left\"></i> Undo</span>\n </div>\n }\n</div>\n\n<!-- board status footer: ONE row, never wraps \u2014 items ellipsize, and the long\n persistence prose collapses to an icon chip (full text on its tooltip) when\n the host is narrow (container query on the host width). -->\n<div class=\"board-status\">\n <span class=\"stat\"><b>{{ ElementCount }}</b> elements</span>\n <span class=\"stat\">you <b>{{ UserCount }}</b> \u00B7 {{ AgentName }} <b>{{ AgentCount }}</b></span>\n <span class=\"stat\">synced <b>{{ LastDeltaLabel }}</b> \u00B7 seq <b>{{ CurrentSeq }}</b></span>\n <span class=\"seg-r\">\n <span class=\"stat ctx-live\" [title]=\"AgentName + '\u2019s live context: current'\">\n <span class=\"d\"></span><span class=\"ctx-live__text\"> {{ AgentName }}'s live context: current</span>\n </span>\n <span class=\"stat persist\" title=\"Persists as a session artifact when the call ends\">\n <i class=\"fa-solid fa-box-archive\" aria-hidden=\"true\"></i>\n <span class=\"persist__long\">persists as <b>session artifact</b> when the call ends</span>\n <span class=\"persist__short\">session artifact</span>\n </span>\n </span>\n</div>\n", styles: ["/* LIVE WHITEBOARD host chrome \u2014 board header, board wrap, agent toast, status footer.\n Ported pixel-faithfully from plans/realtime/mockups/whiteboard.html\n (.board-head, .head-btn, .board-legend, .board-toast, .board-status). */\n\n:host {\n /* agent (violet) accent \u2014 same derivation as the sibling realtime components */\n --sage: var(--mj-color-violet-400, #a78bfa);\n --sage-bg: color-mix(in srgb, var(--mj-color-violet-400, #a78bfa) 14%, transparent);\n --sage-border: color-mix(in srgb, var(--mj-color-violet-400, #a78bfa) 45%, transparent);\n --sage-chip-bg: color-mix(in srgb, var(--mj-color-violet-500, #8b5cf6) 32%, var(--mj-bg-surface-sunken));\n\n display: flex;\n flex-direction: column;\n flex: 1;\n min-height: 0;\n height: 100%;\n /* width container \u2014 drives the status footer's narrow-width collapse rules */\n container-type: inline-size;\n container-name: wb-host;\n}\n\n/* ---------- board header row ---------- */\n.board-head {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 9px 16px;\n border-bottom: 1px solid var(--mj-border-default);\n background: var(--mj-bg-surface);\n flex-shrink: 0;\n flex-wrap: wrap;\n}\n.board-head .bt {\n font-weight: 700;\n font-size: 13.5px;\n display: flex;\n align-items: center;\n gap: 8px;\n color: var(--mj-text-primary);\n}\n.board-head .bt i {\n color: var(--mj-brand-accent, var(--mj-brand-primary));\n font-size: 13px;\n}\n.board-save {\n font-size: 11px;\n color: var(--mj-text-muted);\n display: inline-flex;\n align-items: center;\n gap: 5px;\n}\n.board-save i {\n color: var(--mj-status-success);\n font-size: 10px;\n}\n\n.board-legend {\n display: inline-flex;\n align-items: center;\n gap: 12px;\n font-size: 11px;\n color: var(--mj-text-muted);\n margin-left: 6px;\n padding: 4px 10px;\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-full, 9999px);\n background: var(--mj-bg-surface-card);\n}\n.board-legend .sw {\n width: 10px;\n height: 10px;\n border-radius: 3px;\n display: inline-block;\n vertical-align: -1px;\n margin-right: 5px;\n}\n.board-legend .sw--you {\n background: var(--mj-bg-surface-elevated);\n border: 1px solid var(--mj-border-strong);\n}\n.board-legend .sw--sage {\n background: var(--sage-bg);\n border: 1px solid var(--sage);\n}\n.legend-feather {\n font-size: 9px;\n color: var(--sage);\n}\n\n.head-end {\n margin-left: auto;\n display: flex;\n align-items: center;\n gap: 8px;\n position: relative;\n}\n\n.head-btn {\n display: inline-flex;\n align-items: center;\n gap: 7px;\n padding: 5px 11px;\n font-size: 11.5px;\n font-weight: 600;\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-full, 9999px);\n cursor: pointer;\n background: var(--mj-bg-surface-card);\n color: var(--mj-text-secondary);\n transition: all 140ms ease;\n font-family: inherit;\n}\n.head-btn:hover {\n color: var(--mj-text-primary);\n border-color: var(--mj-border-strong);\n}\n.head-btn.on {\n background: color-mix(in srgb, var(--mj-brand-primary) 14%, transparent);\n border-color: var(--mj-brand-primary);\n color: var(--mj-brand-accent, var(--mj-brand-primary));\n}\n.head-btn--sees {\n position: relative;\n}\n.head-btn--sees .eye-pulse {\n width: 6px;\n height: 6px;\n border-radius: 50%;\n background: var(--sage);\n box-shadow: 0 0 0 0 color-mix(in srgb, var(--sage) 50%, transparent);\n animation: wb-pulse-ring 2s infinite;\n}\n.head-btn--more {\n padding: 5px 9px;\n}\n\n/* ---------- export menu (Download HTML / Download SVG / Print) ---------- */\n.export-wrap {\n position: relative;\n display: inline-flex;\n}\n.export-menu {\n position: absolute;\n top: calc(100% + 6px);\n right: 0;\n z-index: 30;\n min-width: 168px;\n display: flex;\n flex-direction: column;\n padding: 5px;\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-md, 8px);\n background: var(--mj-bg-surface-elevated);\n box-shadow: var(--mj-shadow-lg, 0 10px 15px -3px rgba(0, 0, 0, 0.3));\n}\n.export-item {\n display: flex;\n align-items: center;\n gap: 9px;\n padding: 7px 10px;\n font-size: 11.5px;\n font-weight: 600;\n font-family: inherit;\n text-align: left;\n border: none;\n border-radius: var(--mj-radius-sm, 5px);\n background: transparent;\n color: var(--mj-text-secondary);\n cursor: pointer;\n white-space: nowrap;\n}\n.export-item:hover {\n background: var(--mj-bg-surface-hover);\n color: var(--mj-text-primary);\n}\n.export-item i {\n width: 14px;\n text-align: center;\n color: var(--mj-text-muted);\n}\n.export-item:hover i {\n color: var(--mj-brand-accent, var(--mj-brand-primary));\n}\n@keyframes wb-pulse-ring {\n 0% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--sage) 50%, transparent); }\n 70% { box-shadow: 0 0 0 6px transparent; }\n 100% { box-shadow: 0 0 0 0 transparent; }\n}\n\n/* ---------- the board ---------- */\n.board-wrap {\n position: relative;\n flex: 1;\n min-height: 0;\n overflow: hidden;\n}\n\n/* ---------- agent action toast ----------\n Bottom-center, raised ABOVE the zoom cluster's row (cluster: bottom 14px + ~38px\n tall) so the two can never collide, and width-capped clear of the cluster at the\n board's right edge. Sits just above the status footer per the mockup. */\n.board-toast {\n position: absolute;\n left: 50%;\n bottom: 64px;\n transform: translateX(-50%);\n z-index: 9;\n display: flex;\n align-items: center;\n gap: 9px;\n padding: 8px 16px;\n max-width: calc(100% - 32px);\n border-radius: var(--mj-radius-full, 9999px);\n background: var(--sage-chip-bg);\n border: 1px solid var(--sage-border);\n color: var(--mj-color-violet-300, #c4b5fd);\n font-size: 12px;\n font-weight: 600;\n box-shadow: var(--mj-shadow-lg, 0 10px 15px -3px rgba(0, 0, 0, 0.3));\n animation: wb-toast-fade 300ms ease both;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n.board-toast > i {\n flex-shrink: 0;\n}\n.board-toast__text {\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n.board-toast .undo {\n flex-shrink: 0;\n}\n@keyframes wb-toast-fade {\n from { opacity: 0; transform: translate(-50%, 4px); }\n to { opacity: 1; transform: translate(-50%, 0); }\n}\n.board-toast .undo {\n color: var(--mj-text-muted);\n font-weight: 600;\n cursor: pointer;\n margin-left: 4px;\n}\n.board-toast .undo:hover {\n color: var(--mj-text-primary);\n}\n\n/* ---------- board status footer ---------- */\n.board-status {\n display: flex;\n align-items: center;\n gap: 18px;\n padding: 6px 16px;\n flex-shrink: 0;\n border-top: 1px solid var(--mj-border-default);\n background: var(--mj-bg-surface);\n font-size: 10.5px;\n color: var(--mj-text-muted);\n font-weight: 500;\n}\n.board-status b {\n color: var(--mj-text-secondary);\n font-weight: 600;\n}\n.board-status .seg-r {\n margin-left: auto;\n display: flex;\n align-items: center;\n gap: 18px;\n}\n.board-status .ctx-live {\n color: var(--mj-color-violet-300, var(--mj-text-secondary));\n display: inline-flex;\n align-items: center;\n gap: 6px;\n font-weight: 600;\n}\n.board-status .ctx-live .d {\n width: 6px;\n height: 6px;\n border-radius: 50%;\n background: var(--sage);\n animation: wb-pulse-ring 2s infinite;\n}\n"] }]
705
+ }], null, { State: [{
706
+ type: Input,
707
+ args: [{ required: true }]
708
+ }], AgentName: [{
709
+ type: Input
710
+ }], BoardTitle: [{
711
+ type: Input
712
+ }], SavedLabel: [{
713
+ type: Input
714
+ }], SceneDelta: [{
715
+ type: Output
716
+ }], AgentUndo: [{
717
+ type: Output
718
+ }], FocusModeChange: [{
719
+ type: Output
720
+ }], SaveToArtifactsRequested: [{
721
+ type: Output
722
+ }], WidgetSubmitting: [{
723
+ type: Output
724
+ }], WidgetSubmitted: [{
725
+ type: Output
726
+ }], WidgetInteraction: [{
727
+ type: Output
728
+ }], ContentApplying: [{
729
+ type: Output
730
+ }], ContentApplied: [{
731
+ type: Output
732
+ }], ItemAdding: [{
733
+ type: Output
734
+ }], ItemAdded: [{
735
+ type: Output
736
+ }], ItemUpdating: [{
737
+ type: Output
738
+ }], ItemUpdated: [{
739
+ type: Output
740
+ }], ItemRemoving: [{
741
+ type: Output
742
+ }], ItemRemoved: [{
743
+ type: Output
744
+ }], Board: [{
745
+ type: ViewChild,
746
+ args: [RealtimeWhiteboardBoardComponent]
747
+ }], CloseSees: [{
748
+ type: HostListener,
749
+ args: ['document:click']
750
+ }], CloseExport: [{
751
+ type: HostListener,
752
+ args: ['document:click']
753
+ }], OnKeydown: [{
754
+ type: HostListener,
755
+ args: ['document:keydown', ['$event']]
756
+ }] }); })();
757
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(RealtimeWhiteboardHostComponent, { className: "RealtimeWhiteboardHostComponent", filePath: "src/lib/whiteboard-host.component.ts", lineNumber: 71 }); })();
758
+ //# sourceMappingURL=whiteboard-host.component.js.map