@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,2357 @@
1
+ import { ChangeDetectorRef, Component, ElementRef, EventEmitter, HostListener, Input, Output, ViewChild, inject } from '@angular/core';
2
+ import { CommonModule } from '@angular/common';
3
+ import { UUIDsEqual } from '@memberjunction/global';
4
+ import { MarkdownModule } from '@memberjunction/ng-markdown';
5
+ import { CodeEditorModule } from '@memberjunction/ng-code-editor';
6
+ import { BuildResizeCommitPatch, IsResizableKind, WHITEBOARD_DEFAULTS, WhiteboardState } from './whiteboard-state';
7
+ import { WHITEBOARD_PEN_COLORS } from './whiteboard-toolbar.component';
8
+ import { EvaluateWidgetInteractionMessage, EvaluateWidgetSubmitMessage, WHITEBOARD_WIDGET_INTERACTION_MAX_CHARS, WHITEBOARD_WIDGET_SUBMIT_MAX_CHARS } from './whiteboard-widget-bridge';
9
+ import { WhiteboardWidgetSrcdocPipe } from './whiteboard-srcdoc.pipe';
10
+ import { BuildWhiteboardContextMenu, BuildWhiteboardPageContextMenu } from './whiteboard-context-menu';
11
+ import { RealtimeWhiteboardPagesComponent } from './whiteboard-pages.component';
12
+ import * as i0 from "@angular/core";
13
+ import * as i1 from "@memberjunction/ng-markdown";
14
+ import * as i2 from "@memberjunction/ng-code-editor";
15
+ const _c0 = ["canvas"];
16
+ const _forTrack0 = ($index, $item) => $item.ID;
17
+ function RealtimeWhiteboardBoardComponent_For_5_Template(rf, ctx) { if (rf & 1) {
18
+ const _r2 = i0.ɵɵgetCurrentView();
19
+ i0.ɵɵnamespaceSVG();
20
+ i0.ɵɵelementStart(0, "path", 21);
21
+ i0.ɵɵlistener("pointerdown", function RealtimeWhiteboardBoardComponent_For_5_Template_path_pointerdown_0_listener($event) { const ink_r3 = i0.ɵɵrestoreView(_r2).$implicit; const ctx_r3 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r3.OnItemPointerDown($event, ink_r3)); })("contextmenu", function RealtimeWhiteboardBoardComponent_For_5_Template_path_contextmenu_0_listener($event) { const ink_r3 = i0.ɵɵrestoreView(_r2).$implicit; const ctx_r3 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r3.OnItemContextMenu($event, ink_r3)); });
22
+ i0.ɵɵelementEnd();
23
+ } if (rf & 2) {
24
+ const ink_r3 = ctx.$implicit;
25
+ const ctx_r3 = i0.ɵɵnextContext();
26
+ i0.ɵɵclassProp("ink-sage", ink_r3.Author === "agent")("wb-erasable", ctx_r3.Tool === "eraser" || ctx_r3.Tool === "select")("wb-ink-selected", ctx_r3.IsSelected(ink_r3));
27
+ i0.ɵɵattribute("d", ctx_r3.InkPath(ink_r3.Points))("stroke", ink_r3.Author === "agent" ? null : ink_r3.Color)("stroke-width", ink_r3.StrokeWidth);
28
+ } }
29
+ function RealtimeWhiteboardBoardComponent_For_7_Template(rf, ctx) { if (rf & 1) {
30
+ const _r5 = i0.ɵɵgetCurrentView();
31
+ i0.ɵɵnamespaceSVG();
32
+ i0.ɵɵelementStart(0, "g")(1, "path", 22);
33
+ i0.ɵɵlistener("pointerdown", function RealtimeWhiteboardBoardComponent_For_7_Template_path_pointerdown_1_listener($event) { const conn_r6 = i0.ɵɵrestoreView(_r5).$implicit; const ctx_r3 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r3.OnItemPointerDown($event, conn_r6)); })("contextmenu", function RealtimeWhiteboardBoardComponent_For_7_Template_path_contextmenu_1_listener($event) { const conn_r6 = i0.ɵɵrestoreView(_r5).$implicit; const ctx_r3 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r3.OnItemContextMenu($event, conn_r6)); });
34
+ i0.ɵɵelementEnd();
35
+ i0.ɵɵelement(2, "path", 23);
36
+ i0.ɵɵelementEnd();
37
+ } if (rf & 2) {
38
+ const conn_r6 = ctx.$implicit;
39
+ const ctx_r3 = i0.ɵɵnextContext();
40
+ i0.ɵɵclassProp("conn-sage", conn_r6.Author === "agent")("conn-user", conn_r6.Author === "user");
41
+ i0.ɵɵadvance();
42
+ i0.ɵɵclassProp("draw-in", ctx_r3.PopInIDs.has(conn_r6.ID))("wb-erasable", ctx_r3.Tool === "eraser" || ctx_r3.Tool === "select");
43
+ i0.ɵɵattribute("d", ctx_r3.ConnectorPath(conn_r6));
44
+ i0.ɵɵadvance();
45
+ i0.ɵɵattribute("d", ctx_r3.ConnectorArrow(conn_r6));
46
+ } }
47
+ function RealtimeWhiteboardBoardComponent_Conditional_8_Template(rf, ctx) { if (rf & 1) {
48
+ i0.ɵɵnamespaceSVG();
49
+ i0.ɵɵelement(0, "path", 10);
50
+ } if (rf & 2) {
51
+ const ctx_r3 = i0.ɵɵnextContext();
52
+ i0.ɵɵattribute("d", ctx_r3.LivePenPath)("stroke", ctx_r3.PenColor)("stroke-width", ctx_r3.PenWidth);
53
+ } }
54
+ function RealtimeWhiteboardBoardComponent_Conditional_9_Template(rf, ctx) { if (rf & 1) {
55
+ i0.ɵɵnamespaceSVG();
56
+ i0.ɵɵelement(0, "path", 11);
57
+ } if (rf & 2) {
58
+ const ctx_r3 = i0.ɵɵnextContext();
59
+ i0.ɵɵattribute("d", ctx_r3.PendingConnectorPath);
60
+ } }
61
+ function RealtimeWhiteboardBoardComponent_For_11_Template(rf, ctx) { if (rf & 1) {
62
+ const _r7 = i0.ɵɵgetCurrentView();
63
+ i0.ɵɵelementStart(0, "div", 24);
64
+ i0.ɵɵlistener("pointerdown", function RealtimeWhiteboardBoardComponent_For_11_Template_div_pointerdown_0_listener($event) { const hl_r8 = i0.ɵɵrestoreView(_r7).$implicit; const ctx_r3 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r3.OnHighlightClick($event, hl_r8)); })("contextmenu", function RealtimeWhiteboardBoardComponent_For_11_Template_div_contextmenu_0_listener($event) { const hl_r8 = i0.ɵɵrestoreView(_r7).$implicit; const ctx_r3 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r3.OnItemContextMenu($event, hl_r8)); });
65
+ i0.ɵɵelementStart(1, "span", 25);
66
+ i0.ɵɵelement(2, "i", 26);
67
+ i0.ɵɵtext(3);
68
+ i0.ɵɵelementEnd()();
69
+ } if (rf & 2) {
70
+ const hl_r8 = ctx.$implicit;
71
+ const ctx_r3 = i0.ɵɵnextContext();
72
+ i0.ɵɵstyleProp("left", ctx_r3.ItemX(hl_r8), "px")("top", ctx_r3.ItemY(hl_r8), "px")("width", ctx_r3.ItemW(hl_r8), "px")("height", ctx_r3.ItemH(hl_r8), "px");
73
+ i0.ɵɵclassProp("user-highlight", hl_r8.Author === "user");
74
+ i0.ɵɵadvance(3);
75
+ i0.ɵɵtextInterpolate1(" ", ctx_r3.HighlightTag(hl_r8));
76
+ } }
77
+ function RealtimeWhiteboardBoardComponent_Conditional_12_Template(rf, ctx) { if (rf & 1) {
78
+ i0.ɵɵelement(0, "div", 27);
79
+ } if (rf & 2) {
80
+ const ghost_r9 = ctx;
81
+ i0.ɵɵstyleProp("left", ghost_r9.X, "px")("top", ghost_r9.Y, "px")("width", ghost_r9.W, "px")("height", ghost_r9.H, "px");
82
+ } }
83
+ function RealtimeWhiteboardBoardComponent_Conditional_13_Template(rf, ctx) { if (rf & 1) {
84
+ i0.ɵɵelement(0, "div", 28);
85
+ } if (rf & 2) {
86
+ const preview_r10 = ctx;
87
+ i0.ɵɵstyleProp("left", preview_r10.X, "px")("top", preview_r10.Y, "px")("width", preview_r10.W, "px")("height", preview_r10.H, "px");
88
+ } }
89
+ function RealtimeWhiteboardBoardComponent_Conditional_14_Template(rf, ctx) { if (rf & 1) {
90
+ i0.ɵɵelement(0, "div", 29);
91
+ } if (rf & 2) {
92
+ const mq_r11 = ctx;
93
+ i0.ɵɵstyleProp("left", mq_r11.X, "px")("top", mq_r11.Y, "px")("width", mq_r11.W, "px")("height", mq_r11.H, "px");
94
+ } }
95
+ function RealtimeWhiteboardBoardComponent_For_16_Case_0_Conditional_1_Template(rf, ctx) { if (rf & 1) {
96
+ i0.ɵɵelementStart(0, "span", 37);
97
+ i0.ɵɵelement(1, "i", 40);
98
+ i0.ɵɵtext(2);
99
+ i0.ɵɵelementEnd();
100
+ } if (rf & 2) {
101
+ const ctx_r3 = i0.ɵɵnextContext(3);
102
+ i0.ɵɵadvance(2);
103
+ i0.ɵɵtextInterpolate1(" ", ctx_r3.AgentName);
104
+ } }
105
+ function RealtimeWhiteboardBoardComponent_For_16_Case_0_Conditional_2_Template(rf, ctx) { if (rf & 1) {
106
+ i0.ɵɵelementStart(0, "span", 38);
107
+ i0.ɵɵelement(1, "i", 41);
108
+ i0.ɵɵtext(2, " You");
109
+ i0.ɵɵelementEnd();
110
+ } }
111
+ function RealtimeWhiteboardBoardComponent_For_16_Case_0_Conditional_3_Template(rf, ctx) { if (rf & 1) {
112
+ const _r14 = i0.ɵɵgetCurrentView();
113
+ i0.ɵɵelementStart(0, "textarea", 42, 1);
114
+ i0.ɵɵlistener("keydown", function RealtimeWhiteboardBoardComponent_For_16_Case_0_Conditional_3_Template_textarea_keydown_0_listener($event) { i0.ɵɵrestoreView(_r14); const edit_r15 = i0.ɵɵreference(1); const ctx_r3 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r3.OnEditKeydown($event, edit_r15.value)); })("blur", function RealtimeWhiteboardBoardComponent_For_16_Case_0_Conditional_3_Template_textarea_blur_0_listener() { i0.ɵɵrestoreView(_r14); const edit_r15 = i0.ɵɵreference(1); const ctx_r3 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r3.CommitEdit(edit_r15.value)); });
115
+ i0.ɵɵelementEnd();
116
+ } if (rf & 2) {
117
+ const item_r13 = i0.ɵɵnextContext(2).$implicit;
118
+ i0.ɵɵproperty("value", item_r13.Text);
119
+ } }
120
+ function RealtimeWhiteboardBoardComponent_For_16_Case_0_Conditional_4_Template(rf, ctx) { if (rf & 1) {
121
+ i0.ɵɵtext(0);
122
+ } if (rf & 2) {
123
+ const item_r13 = i0.ɵɵnextContext(2).$implicit;
124
+ i0.ɵɵtextInterpolate1(" ", item_r13.Text, " ");
125
+ } }
126
+ function RealtimeWhiteboardBoardComponent_For_16_Case_0_Conditional_5_For_1_Template(rf, ctx) { if (rf & 1) {
127
+ const _r16 = i0.ɵɵgetCurrentView();
128
+ i0.ɵɵelementStart(0, "span", 44);
129
+ i0.ɵɵlistener("pointerdown", function RealtimeWhiteboardBoardComponent_For_16_Case_0_Conditional_5_For_1_Template_span_pointerdown_0_listener($event) { const h_r17 = i0.ɵɵrestoreView(_r16).$implicit; const item_r13 = i0.ɵɵnextContext(3).$implicit; const ctx_r3 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r3.OnHandlePointerDown($event, item_r13, h_r17)); });
130
+ i0.ɵɵelementEnd();
131
+ } if (rf & 2) {
132
+ const h_r17 = ctx.$implicit;
133
+ i0.ɵɵclassMap("sel-h " + h_r17);
134
+ } }
135
+ function RealtimeWhiteboardBoardComponent_For_16_Case_0_Conditional_5_Template(rf, ctx) { if (rf & 1) {
136
+ i0.ɵɵrepeaterCreate(0, RealtimeWhiteboardBoardComponent_For_16_Case_0_Conditional_5_For_1_Template, 1, 2, "span", 43, i0.ɵɵrepeaterTrackByIdentity);
137
+ } if (rf & 2) {
138
+ const ctx_r3 = i0.ɵɵnextContext(3);
139
+ i0.ɵɵrepeater(ctx_r3.Handles);
140
+ } }
141
+ function RealtimeWhiteboardBoardComponent_For_16_Case_0_Template(rf, ctx) { if (rf & 1) {
142
+ const _r12 = i0.ɵɵgetCurrentView();
143
+ i0.ɵɵelementStart(0, "div", 36);
144
+ i0.ɵɵlistener("pointerdown", function RealtimeWhiteboardBoardComponent_For_16_Case_0_Template_div_pointerdown_0_listener($event) { i0.ɵɵrestoreView(_r12); const item_r13 = i0.ɵɵnextContext().$implicit; const ctx_r3 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r3.OnItemPointerDown($event, item_r13)); })("dblclick", function RealtimeWhiteboardBoardComponent_For_16_Case_0_Template_div_dblclick_0_listener($event) { i0.ɵɵrestoreView(_r12); const item_r13 = i0.ɵɵnextContext().$implicit; const ctx_r3 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r3.BeginItemEdit($event, item_r13)); })("contextmenu", function RealtimeWhiteboardBoardComponent_For_16_Case_0_Template_div_contextmenu_0_listener($event) { i0.ɵɵrestoreView(_r12); const item_r13 = i0.ɵɵnextContext().$implicit; const ctx_r3 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r3.OnItemContextMenu($event, item_r13)); });
145
+ i0.ɵɵconditionalCreate(1, RealtimeWhiteboardBoardComponent_For_16_Case_0_Conditional_1_Template, 3, 1, "span", 37)(2, RealtimeWhiteboardBoardComponent_For_16_Case_0_Conditional_2_Template, 3, 0, "span", 38);
146
+ i0.ɵɵconditionalCreate(3, RealtimeWhiteboardBoardComponent_For_16_Case_0_Conditional_3_Template, 2, 1, "textarea", 39)(4, RealtimeWhiteboardBoardComponent_For_16_Case_0_Conditional_4_Template, 1, 1);
147
+ i0.ɵɵconditionalCreate(5, RealtimeWhiteboardBoardComponent_For_16_Case_0_Conditional_5_Template, 2, 0);
148
+ i0.ɵɵelementEnd();
149
+ } if (rf & 2) {
150
+ const item_r13 = i0.ɵɵnextContext().$implicit;
151
+ const ctx_r3 = i0.ɵɵnextContext();
152
+ i0.ɵɵclassMap("bi sticky " + ctx_r3.StickyTintClass(item_r13));
153
+ i0.ɵɵstyleProp("left", ctx_r3.ItemX(item_r13), "px")("top", ctx_r3.ItemY(item_r13), "px")("width", ctx_r3.ItemW(item_r13), "px")("font-size", ctx_r3.ItemFontSize(item_r13))("font-family", ctx_r3.ItemFontFamily(item_r13))("font-weight", ctx_r3.ItemFontWeight(item_r13))("transform", ctx_r3.IsDragging(item_r13) ? "" : ctx_r3.StickyRotation(item_r13));
154
+ i0.ɵɵclassProp("selected", ctx_r3.IsSelected(item_r13))("dragging", ctx_r3.IsDragging(item_r13))("pop-in", ctx_r3.PopInIDs.has(item_r13.ID));
155
+ i0.ɵɵadvance();
156
+ i0.ɵɵconditional(item_r13.Author === "agent" ? 1 : 2);
157
+ i0.ɵɵadvance(2);
158
+ i0.ɵɵconditional(ctx_r3.EditingID === item_r13.ID ? 3 : 4);
159
+ i0.ɵɵadvance(2);
160
+ i0.ɵɵconditional(ctx_r3.ShowHandles(item_r13) && !ctx_r3.IsDragging(item_r13) ? 5 : -1);
161
+ } }
162
+ function RealtimeWhiteboardBoardComponent_For_16_Case_1_Conditional_1_Template(rf, ctx) { if (rf & 1) {
163
+ i0.ɵɵelementStart(0, "span", 37);
164
+ i0.ɵɵelement(1, "i", 40);
165
+ i0.ɵɵtext(2);
166
+ i0.ɵɵelementEnd();
167
+ } if (rf & 2) {
168
+ const ctx_r3 = i0.ɵɵnextContext(3);
169
+ i0.ɵɵadvance(2);
170
+ i0.ɵɵtextInterpolate1(" ", ctx_r3.AgentName);
171
+ } }
172
+ function RealtimeWhiteboardBoardComponent_For_16_Case_1_Conditional_2_Template(rf, ctx) { if (rf & 1) {
173
+ i0.ɵɵelementStart(0, "span", 38);
174
+ i0.ɵɵelement(1, "i", 41);
175
+ i0.ɵɵtext(2, " You");
176
+ i0.ɵɵelementEnd();
177
+ } }
178
+ function RealtimeWhiteboardBoardComponent_For_16_Case_1_Conditional_3_Template(rf, ctx) { if (rf & 1) {
179
+ const _r19 = i0.ɵɵgetCurrentView();
180
+ i0.ɵɵelementStart(0, "textarea", 47, 2);
181
+ i0.ɵɵlistener("keydown", function RealtimeWhiteboardBoardComponent_For_16_Case_1_Conditional_3_Template_textarea_keydown_0_listener($event) { i0.ɵɵrestoreView(_r19); const editShape_r20 = i0.ɵɵreference(1); const ctx_r3 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r3.OnEditKeydown($event, editShape_r20.value)); })("blur", function RealtimeWhiteboardBoardComponent_For_16_Case_1_Conditional_3_Template_textarea_blur_0_listener() { i0.ɵɵrestoreView(_r19); const editShape_r20 = i0.ɵɵreference(1); const ctx_r3 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r3.CommitEdit(editShape_r20.value)); });
182
+ i0.ɵɵelementEnd();
183
+ } if (rf & 2) {
184
+ const item_r13 = i0.ɵɵnextContext(2).$implicit;
185
+ i0.ɵɵproperty("value", item_r13.Label);
186
+ } }
187
+ function RealtimeWhiteboardBoardComponent_For_16_Case_1_Conditional_4_Conditional_2_Template(rf, ctx) { if (rf & 1) {
188
+ i0.ɵɵelementStart(0, "span", 48);
189
+ i0.ɵɵtext(1);
190
+ i0.ɵɵelementEnd();
191
+ } if (rf & 2) {
192
+ const item_r13 = i0.ɵɵnextContext(3).$implicit;
193
+ i0.ɵɵadvance();
194
+ i0.ɵɵtextInterpolate(item_r13.Sub);
195
+ } }
196
+ function RealtimeWhiteboardBoardComponent_For_16_Case_1_Conditional_4_Template(rf, ctx) { if (rf & 1) {
197
+ i0.ɵɵelementStart(0, "span");
198
+ i0.ɵɵtext(1);
199
+ i0.ɵɵconditionalCreate(2, RealtimeWhiteboardBoardComponent_For_16_Case_1_Conditional_4_Conditional_2_Template, 2, 1, "span", 48);
200
+ i0.ɵɵelementEnd();
201
+ } if (rf & 2) {
202
+ const item_r13 = i0.ɵɵnextContext(2).$implicit;
203
+ i0.ɵɵadvance();
204
+ i0.ɵɵtextInterpolate(item_r13.Label);
205
+ i0.ɵɵadvance();
206
+ i0.ɵɵconditional(item_r13.Sub ? 2 : -1);
207
+ } }
208
+ function RealtimeWhiteboardBoardComponent_For_16_Case_1_Conditional_5_For_1_Template(rf, ctx) { if (rf & 1) {
209
+ const _r21 = i0.ɵɵgetCurrentView();
210
+ i0.ɵɵelementStart(0, "span", 44);
211
+ i0.ɵɵlistener("pointerdown", function RealtimeWhiteboardBoardComponent_For_16_Case_1_Conditional_5_For_1_Template_span_pointerdown_0_listener($event) { const h_r22 = i0.ɵɵrestoreView(_r21).$implicit; const item_r13 = i0.ɵɵnextContext(3).$implicit; const ctx_r3 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r3.OnHandlePointerDown($event, item_r13, h_r22)); });
212
+ i0.ɵɵelementEnd();
213
+ } if (rf & 2) {
214
+ const h_r22 = ctx.$implicit;
215
+ i0.ɵɵclassMap("sel-h " + h_r22);
216
+ } }
217
+ function RealtimeWhiteboardBoardComponent_For_16_Case_1_Conditional_5_Template(rf, ctx) { if (rf & 1) {
218
+ i0.ɵɵrepeaterCreate(0, RealtimeWhiteboardBoardComponent_For_16_Case_1_Conditional_5_For_1_Template, 1, 2, "span", 43, i0.ɵɵrepeaterTrackByIdentity);
219
+ } if (rf & 2) {
220
+ const ctx_r3 = i0.ɵɵnextContext(3);
221
+ i0.ɵɵrepeater(ctx_r3.Handles);
222
+ } }
223
+ function RealtimeWhiteboardBoardComponent_For_16_Case_1_Template(rf, ctx) { if (rf & 1) {
224
+ const _r18 = i0.ɵɵgetCurrentView();
225
+ i0.ɵɵelementStart(0, "div", 45);
226
+ i0.ɵɵlistener("pointerdown", function RealtimeWhiteboardBoardComponent_For_16_Case_1_Template_div_pointerdown_0_listener($event) { i0.ɵɵrestoreView(_r18); const item_r13 = i0.ɵɵnextContext().$implicit; const ctx_r3 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r3.OnItemPointerDown($event, item_r13)); })("dblclick", function RealtimeWhiteboardBoardComponent_For_16_Case_1_Template_div_dblclick_0_listener($event) { i0.ɵɵrestoreView(_r18); const item_r13 = i0.ɵɵnextContext().$implicit; const ctx_r3 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r3.BeginItemEdit($event, item_r13)); })("contextmenu", function RealtimeWhiteboardBoardComponent_For_16_Case_1_Template_div_contextmenu_0_listener($event) { i0.ɵɵrestoreView(_r18); const item_r13 = i0.ɵɵnextContext().$implicit; const ctx_r3 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r3.OnItemContextMenu($event, item_r13)); });
227
+ i0.ɵɵconditionalCreate(1, RealtimeWhiteboardBoardComponent_For_16_Case_1_Conditional_1_Template, 3, 1, "span", 37)(2, RealtimeWhiteboardBoardComponent_For_16_Case_1_Conditional_2_Template, 3, 0, "span", 38);
228
+ i0.ɵɵconditionalCreate(3, RealtimeWhiteboardBoardComponent_For_16_Case_1_Conditional_3_Template, 2, 1, "textarea", 46)(4, RealtimeWhiteboardBoardComponent_For_16_Case_1_Conditional_4_Template, 3, 2, "span");
229
+ i0.ɵɵconditionalCreate(5, RealtimeWhiteboardBoardComponent_For_16_Case_1_Conditional_5_Template, 2, 0);
230
+ i0.ɵɵelementEnd();
231
+ } if (rf & 2) {
232
+ const item_r13 = i0.ɵɵnextContext().$implicit;
233
+ const ctx_r3 = i0.ɵɵnextContext();
234
+ i0.ɵɵstyleProp("left", ctx_r3.ItemX(item_r13), "px")("top", ctx_r3.ItemY(item_r13), "px")("width", ctx_r3.ItemW(item_r13), "px")("height", ctx_r3.ItemH(item_r13), "px");
235
+ i0.ɵɵclassProp("shape-box--ellipse", item_r13.Shape === "ellipse")("shape-box--diamond", item_r13.Shape === "diamond")("shape-box--sage", item_r13.Author === "agent")("selected", ctx_r3.IsSelected(item_r13))("dragging", ctx_r3.IsDragging(item_r13))("pop-in", ctx_r3.PopInIDs.has(item_r13.ID));
236
+ i0.ɵɵadvance();
237
+ i0.ɵɵconditional(item_r13.Author === "agent" ? 1 : 2);
238
+ i0.ɵɵadvance(2);
239
+ i0.ɵɵconditional(ctx_r3.EditingID === item_r13.ID ? 3 : 4);
240
+ i0.ɵɵadvance(2);
241
+ i0.ɵɵconditional(ctx_r3.ShowHandles(item_r13) && !ctx_r3.IsDragging(item_r13) ? 5 : -1);
242
+ } }
243
+ function RealtimeWhiteboardBoardComponent_For_16_Case_2_Conditional_1_Template(rf, ctx) { if (rf & 1) {
244
+ i0.ɵɵelement(0, "i", 40);
245
+ } }
246
+ function RealtimeWhiteboardBoardComponent_For_16_Case_2_Conditional_2_Template(rf, ctx) { if (rf & 1) {
247
+ const _r24 = i0.ɵɵgetCurrentView();
248
+ i0.ɵɵelementStart(0, "input", 51, 3);
249
+ i0.ɵɵlistener("keydown", function RealtimeWhiteboardBoardComponent_For_16_Case_2_Conditional_2_Template_input_keydown_0_listener($event) { i0.ɵɵrestoreView(_r24); const editText_r25 = i0.ɵɵreference(1); const ctx_r3 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r3.OnEditKeydown($event, editText_r25.value)); })("blur", function RealtimeWhiteboardBoardComponent_For_16_Case_2_Conditional_2_Template_input_blur_0_listener() { i0.ɵɵrestoreView(_r24); const editText_r25 = i0.ɵɵreference(1); const ctx_r3 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r3.CommitEdit(editText_r25.value)); });
250
+ i0.ɵɵelementEnd();
251
+ } if (rf & 2) {
252
+ const item_r13 = i0.ɵɵnextContext(2).$implicit;
253
+ i0.ɵɵproperty("value", item_r13.Text);
254
+ } }
255
+ function RealtimeWhiteboardBoardComponent_For_16_Case_2_Conditional_3_Template(rf, ctx) { if (rf & 1) {
256
+ i0.ɵɵtext(0);
257
+ } if (rf & 2) {
258
+ const item_r13 = i0.ɵɵnextContext(2).$implicit;
259
+ i0.ɵɵtextInterpolate1(" ", item_r13.Text, " ");
260
+ } }
261
+ function RealtimeWhiteboardBoardComponent_For_16_Case_2_Conditional_4_For_1_Template(rf, ctx) { if (rf & 1) {
262
+ const _r26 = i0.ɵɵgetCurrentView();
263
+ i0.ɵɵelementStart(0, "span", 44);
264
+ i0.ɵɵlistener("pointerdown", function RealtimeWhiteboardBoardComponent_For_16_Case_2_Conditional_4_For_1_Template_span_pointerdown_0_listener($event) { const h_r27 = i0.ɵɵrestoreView(_r26).$implicit; const item_r13 = i0.ɵɵnextContext(3).$implicit; const ctx_r3 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r3.OnHandlePointerDown($event, item_r13, h_r27)); });
265
+ i0.ɵɵelementEnd();
266
+ } if (rf & 2) {
267
+ const h_r27 = ctx.$implicit;
268
+ i0.ɵɵclassMap("sel-h " + h_r27);
269
+ } }
270
+ function RealtimeWhiteboardBoardComponent_For_16_Case_2_Conditional_4_Template(rf, ctx) { if (rf & 1) {
271
+ i0.ɵɵrepeaterCreate(0, RealtimeWhiteboardBoardComponent_For_16_Case_2_Conditional_4_For_1_Template, 1, 2, "span", 43, i0.ɵɵrepeaterTrackByIdentity);
272
+ } if (rf & 2) {
273
+ const ctx_r3 = i0.ɵɵnextContext(3);
274
+ i0.ɵɵrepeater(ctx_r3.Handles);
275
+ } }
276
+ function RealtimeWhiteboardBoardComponent_For_16_Case_2_Template(rf, ctx) { if (rf & 1) {
277
+ const _r23 = i0.ɵɵgetCurrentView();
278
+ i0.ɵɵelementStart(0, "div", 49);
279
+ i0.ɵɵlistener("pointerdown", function RealtimeWhiteboardBoardComponent_For_16_Case_2_Template_div_pointerdown_0_listener($event) { i0.ɵɵrestoreView(_r23); const item_r13 = i0.ɵɵnextContext().$implicit; const ctx_r3 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r3.OnItemPointerDown($event, item_r13)); })("dblclick", function RealtimeWhiteboardBoardComponent_For_16_Case_2_Template_div_dblclick_0_listener($event) { i0.ɵɵrestoreView(_r23); const item_r13 = i0.ɵɵnextContext().$implicit; const ctx_r3 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r3.BeginItemEdit($event, item_r13)); })("contextmenu", function RealtimeWhiteboardBoardComponent_For_16_Case_2_Template_div_contextmenu_0_listener($event) { i0.ɵɵrestoreView(_r23); const item_r13 = i0.ɵɵnextContext().$implicit; const ctx_r3 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r3.OnItemContextMenu($event, item_r13)); });
280
+ i0.ɵɵconditionalCreate(1, RealtimeWhiteboardBoardComponent_For_16_Case_2_Conditional_1_Template, 1, 0, "i", 40);
281
+ i0.ɵɵconditionalCreate(2, RealtimeWhiteboardBoardComponent_For_16_Case_2_Conditional_2_Template, 2, 1, "input", 50)(3, RealtimeWhiteboardBoardComponent_For_16_Case_2_Conditional_3_Template, 1, 1);
282
+ i0.ɵɵconditionalCreate(4, RealtimeWhiteboardBoardComponent_For_16_Case_2_Conditional_4_Template, 2, 0);
283
+ i0.ɵɵelementEnd();
284
+ } if (rf & 2) {
285
+ const item_r13 = i0.ɵɵnextContext().$implicit;
286
+ const ctx_r3 = i0.ɵɵnextContext();
287
+ i0.ɵɵstyleProp("left", ctx_r3.ItemX(item_r13), "px")("top", ctx_r3.ItemY(item_r13), "px")("width", ctx_r3.TextWrapWidth(item_r13), "px")("max-width", ctx_r3.TextWrapWidth(item_r13), "px")("font-size", ctx_r3.ItemFontSize(item_r13))("font-family", ctx_r3.ItemFontFamily(item_r13))("font-weight", ctx_r3.ItemFontWeight(item_r13))("color", ctx_r3.ItemTextColor(item_r13));
288
+ i0.ɵɵclassProp("tlabel--user", item_r13.Author === "user")("selected", ctx_r3.IsSelected(item_r13))("dragging", ctx_r3.IsDragging(item_r13))("pop-in", ctx_r3.PopInIDs.has(item_r13.ID));
289
+ i0.ɵɵadvance();
290
+ i0.ɵɵconditional(item_r13.Author === "agent" ? 1 : -1);
291
+ i0.ɵɵadvance();
292
+ i0.ɵɵconditional(ctx_r3.EditingID === item_r13.ID ? 2 : 3);
293
+ i0.ɵɵadvance(2);
294
+ i0.ɵɵconditional(ctx_r3.ShowHandles(item_r13) && !ctx_r3.IsDragging(item_r13) ? 4 : -1);
295
+ } }
296
+ function RealtimeWhiteboardBoardComponent_For_16_Case_3_Conditional_1_Template(rf, ctx) { if (rf & 1) {
297
+ i0.ɵɵelementStart(0, "span", 37);
298
+ i0.ɵɵelement(1, "i", 40);
299
+ i0.ɵɵtext(2);
300
+ i0.ɵɵelementEnd();
301
+ } if (rf & 2) {
302
+ const ctx_r3 = i0.ɵɵnextContext(3);
303
+ i0.ɵɵadvance(2);
304
+ i0.ɵɵtextInterpolate1(" ", ctx_r3.AgentName);
305
+ } }
306
+ function RealtimeWhiteboardBoardComponent_For_16_Case_3_Conditional_2_Template(rf, ctx) { if (rf & 1) {
307
+ i0.ɵɵelementStart(0, "span", 38);
308
+ i0.ɵɵelement(1, "i", 55);
309
+ i0.ɵɵtext(2, " You");
310
+ i0.ɵɵelementEnd();
311
+ } }
312
+ function RealtimeWhiteboardBoardComponent_For_16_Case_3_Conditional_5_For_1_Template(rf, ctx) { if (rf & 1) {
313
+ const _r29 = i0.ɵɵgetCurrentView();
314
+ i0.ɵɵelementStart(0, "span", 44);
315
+ i0.ɵɵlistener("pointerdown", function RealtimeWhiteboardBoardComponent_For_16_Case_3_Conditional_5_For_1_Template_span_pointerdown_0_listener($event) { const h_r30 = i0.ɵɵrestoreView(_r29).$implicit; const item_r13 = i0.ɵɵnextContext(3).$implicit; const ctx_r3 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r3.OnHandlePointerDown($event, item_r13, h_r30)); });
316
+ i0.ɵɵelementEnd();
317
+ } if (rf & 2) {
318
+ const h_r30 = ctx.$implicit;
319
+ i0.ɵɵclassMap("sel-h " + h_r30);
320
+ } }
321
+ function RealtimeWhiteboardBoardComponent_For_16_Case_3_Conditional_5_Template(rf, ctx) { if (rf & 1) {
322
+ i0.ɵɵrepeaterCreate(0, RealtimeWhiteboardBoardComponent_For_16_Case_3_Conditional_5_For_1_Template, 1, 2, "span", 43, i0.ɵɵrepeaterTrackByIdentity);
323
+ } if (rf & 2) {
324
+ const ctx_r3 = i0.ɵɵnextContext(3);
325
+ i0.ɵɵrepeater(ctx_r3.Handles);
326
+ } }
327
+ function RealtimeWhiteboardBoardComponent_For_16_Case_3_Template(rf, ctx) { if (rf & 1) {
328
+ const _r28 = i0.ɵɵgetCurrentView();
329
+ i0.ɵɵelementStart(0, "div", 52);
330
+ i0.ɵɵlistener("pointerdown", function RealtimeWhiteboardBoardComponent_For_16_Case_3_Template_div_pointerdown_0_listener($event) { i0.ɵɵrestoreView(_r28); const item_r13 = i0.ɵɵnextContext().$implicit; const ctx_r3 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r3.OnItemPointerDown($event, item_r13)); })("dblclick", function RealtimeWhiteboardBoardComponent_For_16_Case_3_Template_div_dblclick_0_listener($event) { i0.ɵɵrestoreView(_r28); const item_r13 = i0.ɵɵnextContext().$implicit; const ctx_r3 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r3.BeginRichEdit($event, item_r13)); })("contextmenu", function RealtimeWhiteboardBoardComponent_For_16_Case_3_Template_div_contextmenu_0_listener($event) { i0.ɵɵrestoreView(_r28); const item_r13 = i0.ɵɵnextContext().$implicit; const ctx_r3 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r3.OnItemContextMenu($event, item_r13)); });
331
+ i0.ɵɵconditionalCreate(1, RealtimeWhiteboardBoardComponent_For_16_Case_3_Conditional_1_Template, 3, 1, "span", 37)(2, RealtimeWhiteboardBoardComponent_For_16_Case_3_Conditional_2_Template, 3, 0, "span", 38);
332
+ i0.ɵɵelementStart(3, "div", 53);
333
+ i0.ɵɵelement(4, "mj-markdown", 54);
334
+ i0.ɵɵelementEnd();
335
+ i0.ɵɵconditionalCreate(5, RealtimeWhiteboardBoardComponent_For_16_Case_3_Conditional_5_Template, 2, 0);
336
+ i0.ɵɵelementEnd();
337
+ } if (rf & 2) {
338
+ const item_r13 = i0.ɵɵnextContext().$implicit;
339
+ const ctx_r3 = i0.ɵɵnextContext();
340
+ i0.ɵɵstyleProp("left", ctx_r3.ItemX(item_r13), "px")("top", ctx_r3.ItemY(item_r13), "px")("width", ctx_r3.ItemW(item_r13), "px");
341
+ i0.ɵɵclassProp("md-card--sage", item_r13.Author === "agent")("selected", ctx_r3.IsSelected(item_r13))("dragging", ctx_r3.IsDragging(item_r13))("pop-in", ctx_r3.PopInIDs.has(item_r13.ID));
342
+ i0.ɵɵadvance();
343
+ i0.ɵɵconditional(item_r13.Author === "agent" ? 1 : 2);
344
+ i0.ɵɵadvance(2);
345
+ i0.ɵɵstyleProp("max-height", ctx_r3.MarkdownMaxHeight(item_r13), "px");
346
+ i0.ɵɵadvance();
347
+ i0.ɵɵproperty("data", item_r13.Markdown)("enableMermaid", false)("enableCodeCopy", false)("enableCollapsibleHeadings", false);
348
+ i0.ɵɵadvance();
349
+ i0.ɵɵconditional(ctx_r3.ShowHandles(item_r13) && !ctx_r3.IsDragging(item_r13) ? 5 : -1);
350
+ } }
351
+ function RealtimeWhiteboardBoardComponent_For_16_Case_4_Conditional_5_Template(rf, ctx) { if (rf & 1) {
352
+ i0.ɵɵelementStart(0, "span", 60);
353
+ i0.ɵɵelement(1, "i", 40);
354
+ i0.ɵɵtext(2);
355
+ i0.ɵɵelementEnd();
356
+ } if (rf & 2) {
357
+ const ctx_r3 = i0.ɵɵnextContext(3);
358
+ i0.ɵɵadvance(2);
359
+ i0.ɵɵtextInterpolate1(" ", ctx_r3.AgentName);
360
+ } }
361
+ function RealtimeWhiteboardBoardComponent_For_16_Case_4_Conditional_6_Template(rf, ctx) { if (rf & 1) {
362
+ i0.ɵɵelementStart(0, "span", 61);
363
+ i0.ɵɵelement(1, "i", 41);
364
+ i0.ɵɵtext(2, " You");
365
+ i0.ɵɵelementEnd();
366
+ } }
367
+ function RealtimeWhiteboardBoardComponent_For_16_Case_4_Conditional_7_Template(rf, ctx) { if (rf & 1) {
368
+ const _r32 = i0.ɵɵgetCurrentView();
369
+ i0.ɵɵelementStart(0, "button", 65);
370
+ i0.ɵɵlistener("pointerdown", function RealtimeWhiteboardBoardComponent_For_16_Case_4_Conditional_7_Template_button_pointerdown_0_listener($event) { i0.ɵɵrestoreView(_r32); return i0.ɵɵresetView($event.stopPropagation()); })("click", function RealtimeWhiteboardBoardComponent_For_16_Case_4_Conditional_7_Template_button_click_0_listener($event) { i0.ɵɵrestoreView(_r32); const item_r13 = i0.ɵɵnextContext(2).$implicit; const ctx_r3 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r3.BeginRichEdit($event, item_r13)); });
371
+ i0.ɵɵelement(1, "i", 66);
372
+ i0.ɵɵelementEnd();
373
+ i0.ɵɵelementStart(2, "button", 67);
374
+ i0.ɵɵlistener("pointerdown", function RealtimeWhiteboardBoardComponent_For_16_Case_4_Conditional_7_Template_button_pointerdown_2_listener($event) { i0.ɵɵrestoreView(_r32); return i0.ɵɵresetView($event.stopPropagation()); })("click", function RealtimeWhiteboardBoardComponent_For_16_Case_4_Conditional_7_Template_button_click_2_listener($event) { i0.ɵɵrestoreView(_r32); const item_r13 = i0.ɵɵnextContext(2).$implicit; const ctx_r3 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r3.RemoveItemClick($event, item_r13)); });
375
+ i0.ɵɵelement(3, "i", 68);
376
+ i0.ɵɵelementEnd();
377
+ } }
378
+ function RealtimeWhiteboardBoardComponent_For_16_Case_4_Conditional_9_Template(rf, ctx) { if (rf & 1) {
379
+ i0.ɵɵelement(0, "iframe", 63);
380
+ i0.ɵɵpipe(1, "wbWidgetSrcdoc");
381
+ } if (rf & 2) {
382
+ const item_r13 = i0.ɵɵnextContext(2).$implicit;
383
+ const ctx_r3 = i0.ɵɵnextContext();
384
+ i0.ɵɵproperty("srcdoc", i0.ɵɵpipeBind1(1, 3, item_r13.Html), i0.ɵɵsanitizeHtml);
385
+ i0.ɵɵattribute("data-item-id", item_r13.ID)("title", ctx_r3.HtmlTitle(item_r13));
386
+ } }
387
+ function RealtimeWhiteboardBoardComponent_For_16_Case_4_Conditional_10_Template(rf, ctx) { if (rf & 1) {
388
+ i0.ɵɵelementStart(0, "div", 64);
389
+ i0.ɵɵelement(1, "i", 69);
390
+ i0.ɵɵelementStart(2, "span");
391
+ i0.ɵɵtext(3);
392
+ i0.ɵɵelementEnd()();
393
+ } if (rf & 2) {
394
+ const item_r13 = i0.ɵɵnextContext(2).$implicit;
395
+ const ctx_r3 = i0.ɵɵnextContext();
396
+ i0.ɵɵadvance(3);
397
+ i0.ɵɵtextInterpolate(ctx_r3.HtmlTitle(item_r13));
398
+ } }
399
+ function RealtimeWhiteboardBoardComponent_For_16_Case_4_Conditional_11_For_1_Template(rf, ctx) { if (rf & 1) {
400
+ const _r33 = i0.ɵɵgetCurrentView();
401
+ i0.ɵɵelementStart(0, "span", 44);
402
+ i0.ɵɵlistener("pointerdown", function RealtimeWhiteboardBoardComponent_For_16_Case_4_Conditional_11_For_1_Template_span_pointerdown_0_listener($event) { const h_r34 = i0.ɵɵrestoreView(_r33).$implicit; const item_r13 = i0.ɵɵnextContext(3).$implicit; const ctx_r3 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r3.OnHandlePointerDown($event, item_r13, h_r34)); });
403
+ i0.ɵɵelementEnd();
404
+ } if (rf & 2) {
405
+ const h_r34 = ctx.$implicit;
406
+ i0.ɵɵclassMap("sel-h " + h_r34);
407
+ } }
408
+ function RealtimeWhiteboardBoardComponent_For_16_Case_4_Conditional_11_Template(rf, ctx) { if (rf & 1) {
409
+ i0.ɵɵrepeaterCreate(0, RealtimeWhiteboardBoardComponent_For_16_Case_4_Conditional_11_For_1_Template, 1, 2, "span", 43, i0.ɵɵrepeaterTrackByIdentity);
410
+ } if (rf & 2) {
411
+ const ctx_r3 = i0.ɵɵnextContext(3);
412
+ i0.ɵɵrepeater(ctx_r3.Handles);
413
+ } }
414
+ function RealtimeWhiteboardBoardComponent_For_16_Case_4_Template(rf, ctx) { if (rf & 1) {
415
+ const _r31 = i0.ɵɵgetCurrentView();
416
+ i0.ɵɵelementStart(0, "div", 56);
417
+ i0.ɵɵlistener("pointerdown", function RealtimeWhiteboardBoardComponent_For_16_Case_4_Template_div_pointerdown_0_listener($event) { i0.ɵɵrestoreView(_r31); const item_r13 = i0.ɵɵnextContext().$implicit; const ctx_r3 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r3.OnItemPointerDown($event, item_r13)); })("contextmenu", function RealtimeWhiteboardBoardComponent_For_16_Case_4_Template_div_contextmenu_0_listener($event) { i0.ɵɵrestoreView(_r31); const item_r13 = i0.ɵɵnextContext().$implicit; const ctx_r3 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r3.OnItemContextMenu($event, item_r13)); });
418
+ i0.ɵɵelementStart(1, "div", 57);
419
+ i0.ɵɵlistener("dblclick", function RealtimeWhiteboardBoardComponent_For_16_Case_4_Template_div_dblclick_1_listener($event) { i0.ɵɵrestoreView(_r31); const item_r13 = i0.ɵɵnextContext().$implicit; const ctx_r3 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r3.BeginRichEdit($event, item_r13)); });
420
+ i0.ɵɵelement(2, "i", 58);
421
+ i0.ɵɵelementStart(3, "span", 59);
422
+ i0.ɵɵtext(4);
423
+ i0.ɵɵelementEnd();
424
+ i0.ɵɵconditionalCreate(5, RealtimeWhiteboardBoardComponent_For_16_Case_4_Conditional_5_Template, 3, 1, "span", 60)(6, RealtimeWhiteboardBoardComponent_For_16_Case_4_Conditional_6_Template, 3, 0, "span", 61);
425
+ i0.ɵɵconditionalCreate(7, RealtimeWhiteboardBoardComponent_For_16_Case_4_Conditional_7_Template, 4, 0);
426
+ i0.ɵɵelementEnd();
427
+ i0.ɵɵelementStart(8, "div", 62);
428
+ i0.ɵɵconditionalCreate(9, RealtimeWhiteboardBoardComponent_For_16_Case_4_Conditional_9_Template, 2, 5, "iframe", 63)(10, RealtimeWhiteboardBoardComponent_For_16_Case_4_Conditional_10_Template, 4, 1, "div", 64);
429
+ i0.ɵɵelementEnd();
430
+ i0.ɵɵconditionalCreate(11, RealtimeWhiteboardBoardComponent_For_16_Case_4_Conditional_11_Template, 2, 0);
431
+ i0.ɵɵelementEnd();
432
+ } if (rf & 2) {
433
+ const item_r13 = i0.ɵɵnextContext().$implicit;
434
+ const ctx_r3 = i0.ɵɵnextContext();
435
+ i0.ɵɵstyleProp("left", ctx_r3.ItemX(item_r13), "px")("top", ctx_r3.ItemY(item_r13), "px")("width", ctx_r3.ItemW(item_r13), "px")("height", ctx_r3.ItemH(item_r13), "px");
436
+ i0.ɵɵclassProp("html-card--sage", item_r13.Author === "agent")("selected", ctx_r3.IsSelected(item_r13))("dragging", ctx_r3.IsDragging(item_r13))("pop-in", ctx_r3.PopInIDs.has(item_r13.ID));
437
+ i0.ɵɵadvance(4);
438
+ i0.ɵɵtextInterpolate(ctx_r3.HtmlTitle(item_r13));
439
+ i0.ɵɵadvance();
440
+ i0.ɵɵconditional(item_r13.Author === "agent" ? 5 : 6);
441
+ i0.ɵɵadvance(2);
442
+ i0.ɵɵconditional(!ctx_r3.ReadOnly ? 7 : -1);
443
+ i0.ɵɵadvance(2);
444
+ i0.ɵɵconditional(ctx_r3.IsNearViewport(item_r13) ? 9 : 10);
445
+ i0.ɵɵadvance(2);
446
+ i0.ɵɵconditional(ctx_r3.ShowHandles(item_r13) && !ctx_r3.IsDragging(item_r13) ? 11 : -1);
447
+ } }
448
+ function RealtimeWhiteboardBoardComponent_For_16_Case_5_Conditional_1_Template(rf, ctx) { if (rf & 1) {
449
+ i0.ɵɵelementStart(0, "span", 37);
450
+ i0.ɵɵelement(1, "i", 40);
451
+ i0.ɵɵtext(2);
452
+ i0.ɵɵelementEnd();
453
+ } if (rf & 2) {
454
+ const ctx_r3 = i0.ɵɵnextContext(3);
455
+ i0.ɵɵadvance(2);
456
+ i0.ɵɵtextInterpolate1(" ", ctx_r3.AgentName);
457
+ } }
458
+ function RealtimeWhiteboardBoardComponent_For_16_Case_5_Conditional_2_Template(rf, ctx) { if (rf & 1) {
459
+ i0.ɵɵelementStart(0, "span", 38);
460
+ i0.ɵɵelement(1, "i", 76);
461
+ i0.ɵɵtext(2, " You \u00B7 pasted");
462
+ i0.ɵɵelementEnd();
463
+ } }
464
+ function RealtimeWhiteboardBoardComponent_For_16_Case_5_Conditional_4_Template(rf, ctx) { if (rf & 1) {
465
+ i0.ɵɵelement(0, "img", 72);
466
+ } if (rf & 2) {
467
+ const item_r13 = i0.ɵɵnextContext(2).$implicit;
468
+ i0.ɵɵproperty("src", item_r13.Url, i0.ɵɵsanitizeUrl)("alt", item_r13.Name);
469
+ } }
470
+ function RealtimeWhiteboardBoardComponent_For_16_Case_5_Conditional_5_Template(rf, ctx) { if (rf & 1) {
471
+ i0.ɵɵelement(0, "i", 73);
472
+ } }
473
+ function RealtimeWhiteboardBoardComponent_For_16_Case_5_Conditional_9_For_1_Template(rf, ctx) { if (rf & 1) {
474
+ const _r36 = i0.ɵɵgetCurrentView();
475
+ i0.ɵɵelementStart(0, "span", 44);
476
+ i0.ɵɵlistener("pointerdown", function RealtimeWhiteboardBoardComponent_For_16_Case_5_Conditional_9_For_1_Template_span_pointerdown_0_listener($event) { const h_r37 = i0.ɵɵrestoreView(_r36).$implicit; const item_r13 = i0.ɵɵnextContext(3).$implicit; const ctx_r3 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r3.OnHandlePointerDown($event, item_r13, h_r37)); });
477
+ i0.ɵɵelementEnd();
478
+ } if (rf & 2) {
479
+ const h_r37 = ctx.$implicit;
480
+ i0.ɵɵclassMap("sel-h " + h_r37);
481
+ } }
482
+ function RealtimeWhiteboardBoardComponent_For_16_Case_5_Conditional_9_Template(rf, ctx) { if (rf & 1) {
483
+ i0.ɵɵrepeaterCreate(0, RealtimeWhiteboardBoardComponent_For_16_Case_5_Conditional_9_For_1_Template, 1, 2, "span", 43, i0.ɵɵrepeaterTrackByIdentity);
484
+ } if (rf & 2) {
485
+ const ctx_r3 = i0.ɵɵnextContext(3);
486
+ i0.ɵɵrepeater(ctx_r3.Handles);
487
+ } }
488
+ function RealtimeWhiteboardBoardComponent_For_16_Case_5_Template(rf, ctx) { if (rf & 1) {
489
+ const _r35 = i0.ɵɵgetCurrentView();
490
+ i0.ɵɵelementStart(0, "div", 70);
491
+ i0.ɵɵlistener("pointerdown", function RealtimeWhiteboardBoardComponent_For_16_Case_5_Template_div_pointerdown_0_listener($event) { i0.ɵɵrestoreView(_r35); const item_r13 = i0.ɵɵnextContext().$implicit; const ctx_r3 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r3.OnItemPointerDown($event, item_r13)); })("contextmenu", function RealtimeWhiteboardBoardComponent_For_16_Case_5_Template_div_contextmenu_0_listener($event) { i0.ɵɵrestoreView(_r35); const item_r13 = i0.ɵɵnextContext().$implicit; const ctx_r3 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r3.OnItemContextMenu($event, item_r13)); });
492
+ i0.ɵɵconditionalCreate(1, RealtimeWhiteboardBoardComponent_For_16_Case_5_Conditional_1_Template, 3, 1, "span", 37)(2, RealtimeWhiteboardBoardComponent_For_16_Case_5_Conditional_2_Template, 3, 0, "span", 38);
493
+ i0.ɵɵelementStart(3, "div", 71);
494
+ i0.ɵɵconditionalCreate(4, RealtimeWhiteboardBoardComponent_For_16_Case_5_Conditional_4_Template, 1, 2, "img", 72)(5, RealtimeWhiteboardBoardComponent_For_16_Case_5_Conditional_5_Template, 1, 0, "i", 73);
495
+ i0.ɵɵelementEnd();
496
+ i0.ɵɵelementStart(6, "div", 74);
497
+ i0.ɵɵelement(7, "i", 75);
498
+ i0.ɵɵtext(8);
499
+ i0.ɵɵelementEnd();
500
+ i0.ɵɵconditionalCreate(9, RealtimeWhiteboardBoardComponent_For_16_Case_5_Conditional_9_Template, 2, 0);
501
+ i0.ɵɵelementEnd();
502
+ } if (rf & 2) {
503
+ const item_r13 = i0.ɵɵnextContext().$implicit;
504
+ const ctx_r3 = i0.ɵɵnextContext();
505
+ i0.ɵɵstyleProp("left", ctx_r3.ItemX(item_r13), "px")("top", ctx_r3.ItemY(item_r13), "px")("width", ctx_r3.ItemW(item_r13), "px");
506
+ i0.ɵɵclassProp("selected", ctx_r3.IsSelected(item_r13))("dragging", ctx_r3.IsDragging(item_r13))("pop-in", ctx_r3.PopInIDs.has(item_r13.ID));
507
+ i0.ɵɵadvance();
508
+ i0.ɵɵconditional(item_r13.Author === "agent" ? 1 : 2);
509
+ i0.ɵɵadvance(3);
510
+ i0.ɵɵconditional(item_r13.Url ? 4 : 5);
511
+ i0.ɵɵadvance(4);
512
+ i0.ɵɵtextInterpolate1(" ", item_r13.Name);
513
+ i0.ɵɵadvance();
514
+ i0.ɵɵconditional(ctx_r3.ShowHandles(item_r13) && !ctx_r3.IsDragging(item_r13) ? 9 : -1);
515
+ } }
516
+ function RealtimeWhiteboardBoardComponent_For_16_Template(rf, ctx) { if (rf & 1) {
517
+ i0.ɵɵconditionalCreate(0, RealtimeWhiteboardBoardComponent_For_16_Case_0_Template, 6, 25, "div", 30)(1, RealtimeWhiteboardBoardComponent_For_16_Case_1_Template, 6, 23, "div", 31)(2, RealtimeWhiteboardBoardComponent_For_16_Case_2_Template, 5, 27, "div", 32)(3, RealtimeWhiteboardBoardComponent_For_16_Case_3_Template, 6, 22, "div", 33)(4, RealtimeWhiteboardBoardComponent_For_16_Case_4_Template, 12, 21, "div", 34)(5, RealtimeWhiteboardBoardComponent_For_16_Case_5_Template, 10, 16, "div", 35);
518
+ } if (rf & 2) {
519
+ let tmp_11_0;
520
+ const item_r13 = ctx.$implicit;
521
+ i0.ɵɵconditional((tmp_11_0 = item_r13.Kind) === "sticky" ? 0 : tmp_11_0 === "shape" ? 1 : tmp_11_0 === "text" ? 2 : tmp_11_0 === "markdown" ? 3 : tmp_11_0 === "html" ? 4 : tmp_11_0 === "image" ? 5 : -1);
522
+ } }
523
+ function RealtimeWhiteboardBoardComponent_Conditional_17_Template(rf, ctx) { if (rf & 1) {
524
+ i0.ɵɵelementStart(0, "div", 77)(1, "span", 78);
525
+ i0.ɵɵelement(2, "i", 40);
526
+ i0.ɵɵelementEnd();
527
+ i0.ɵɵelementStart(3, "span", 79);
528
+ i0.ɵɵelement(4, "i", 66);
529
+ i0.ɵɵtext(5);
530
+ i0.ɵɵelementEnd()();
531
+ } if (rf & 2) {
532
+ const ctx_r3 = i0.ɵɵnextContext();
533
+ i0.ɵɵstyleProp("left", ctx_r3.AgentPresence.X, "px")("top", ctx_r3.AgentPresence.Y, "px");
534
+ i0.ɵɵadvance(5);
535
+ i0.ɵɵtextInterpolate(ctx_r3.AgentPresence.Label);
536
+ } }
537
+ function RealtimeWhiteboardBoardComponent_Conditional_18_Conditional_2_Template(rf, ctx) { if (rf & 1) {
538
+ const _r39 = i0.ɵɵgetCurrentView();
539
+ i0.ɵɵelement(0, "i", 55);
540
+ i0.ɵɵelementStart(1, "span", 91);
541
+ i0.ɵɵtext(2, "Markdown panel");
542
+ i0.ɵɵelementEnd();
543
+ i0.ɵɵelementStart(3, "span", 92)(4, "button", 93);
544
+ i0.ɵɵlistener("click", function RealtimeWhiteboardBoardComponent_Conditional_18_Conditional_2_Template_button_click_4_listener() { i0.ɵɵrestoreView(_r39); const ctx_r3 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r3.RichEditorSetPreview(false)); });
545
+ i0.ɵɵtext(5, "Edit");
546
+ i0.ɵɵelementEnd();
547
+ i0.ɵɵelementStart(6, "button", 93);
548
+ i0.ɵɵlistener("click", function RealtimeWhiteboardBoardComponent_Conditional_18_Conditional_2_Template_button_click_6_listener() { i0.ɵɵrestoreView(_r39); const ctx_r3 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r3.RichEditorSetPreview(true)); });
549
+ i0.ɵɵtext(7, "Preview");
550
+ i0.ɵɵelementEnd()();
551
+ } if (rf & 2) {
552
+ const ed_r40 = i0.ɵɵnextContext();
553
+ i0.ɵɵadvance(4);
554
+ i0.ɵɵclassProp("on", !ed_r40.Preview);
555
+ i0.ɵɵadvance(2);
556
+ i0.ɵɵclassProp("on", ed_r40.Preview);
557
+ } }
558
+ function RealtimeWhiteboardBoardComponent_Conditional_18_Conditional_3_Template(rf, ctx) { if (rf & 1) {
559
+ const _r41 = i0.ɵɵgetCurrentView();
560
+ i0.ɵɵelement(0, "i", 69);
561
+ i0.ɵɵelementStart(1, "span", 91);
562
+ i0.ɵɵtext(2, "HTML widget");
563
+ i0.ɵɵelementEnd();
564
+ i0.ɵɵelementStart(3, "input", 94, 4);
565
+ i0.ɵɵlistener("input", function RealtimeWhiteboardBoardComponent_Conditional_18_Conditional_3_Template_input_input_3_listener() { i0.ɵɵrestoreView(_r41); const reTitle_r42 = i0.ɵɵreference(4); const ctx_r3 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r3.OnRichEditorTitleChange(reTitle_r42.value)); });
566
+ i0.ɵɵelementEnd();
567
+ } if (rf & 2) {
568
+ const ed_r40 = i0.ɵɵnextContext();
569
+ i0.ɵɵadvance(3);
570
+ i0.ɵɵproperty("value", ed_r40.TitleDraft);
571
+ } }
572
+ function RealtimeWhiteboardBoardComponent_Conditional_18_Conditional_5_Template(rf, ctx) { if (rf & 1) {
573
+ i0.ɵɵelementStart(0, "div", 83);
574
+ i0.ɵɵelement(1, "mj-markdown", 54);
575
+ i0.ɵɵelementEnd();
576
+ } if (rf & 2) {
577
+ const ed_r40 = i0.ɵɵnextContext();
578
+ i0.ɵɵadvance();
579
+ i0.ɵɵproperty("data", ed_r40.Draft)("enableMermaid", false)("enableCodeCopy", false)("enableCollapsibleHeadings", false);
580
+ } }
581
+ function RealtimeWhiteboardBoardComponent_Conditional_18_Conditional_6_Template(rf, ctx) { if (rf & 1) {
582
+ const _r43 = i0.ɵɵgetCurrentView();
583
+ i0.ɵɵelementStart(0, "mj-code-editor", 95);
584
+ i0.ɵɵlistener("change", function RealtimeWhiteboardBoardComponent_Conditional_18_Conditional_6_Template_mj_code_editor_change_0_listener($event) { i0.ɵɵrestoreView(_r43); const ctx_r3 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r3.OnRichEditorDraftChange($event)); });
585
+ i0.ɵɵelementEnd();
586
+ } if (rf & 2) {
587
+ const ed_r40 = i0.ɵɵnextContext();
588
+ i0.ɵɵproperty("value", ed_r40.Draft)("language", ed_r40.Kind === "markdown" ? "markdown" : "html");
589
+ } }
590
+ function RealtimeWhiteboardBoardComponent_Conditional_18_Conditional_7_Template(rf, ctx) { if (rf & 1) {
591
+ i0.ɵɵelementStart(0, "div", 85);
592
+ i0.ɵɵelement(1, "i", 96);
593
+ i0.ɵɵtext(2, " Renders in a sandboxed frame \u2014 scripts run isolated, no app or network access guaranteed. ");
594
+ i0.ɵɵelementEnd();
595
+ } }
596
+ function RealtimeWhiteboardBoardComponent_Conditional_18_Template(rf, ctx) { if (rf & 1) {
597
+ const _r38 = i0.ɵɵgetCurrentView();
598
+ i0.ɵɵelementStart(0, "div", 80);
599
+ i0.ɵɵlistener("keydown", function RealtimeWhiteboardBoardComponent_Conditional_18_Template_div_keydown_0_listener($event) { i0.ɵɵrestoreView(_r38); const ctx_r3 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r3.OnRichEditorKeydown($event)); })("pointerdown", function RealtimeWhiteboardBoardComponent_Conditional_18_Template_div_pointerdown_0_listener($event) { i0.ɵɵrestoreView(_r38); return i0.ɵɵresetView($event.stopPropagation()); })("dblclick", function RealtimeWhiteboardBoardComponent_Conditional_18_Template_div_dblclick_0_listener($event) { i0.ɵɵrestoreView(_r38); return i0.ɵɵresetView($event.stopPropagation()); })("wheel", function RealtimeWhiteboardBoardComponent_Conditional_18_Template_div_wheel_0_listener($event) { i0.ɵɵrestoreView(_r38); return i0.ɵɵresetView($event.stopPropagation()); });
600
+ i0.ɵɵelementStart(1, "div", 81);
601
+ i0.ɵɵconditionalCreate(2, RealtimeWhiteboardBoardComponent_Conditional_18_Conditional_2_Template, 8, 4)(3, RealtimeWhiteboardBoardComponent_Conditional_18_Conditional_3_Template, 5, 1);
602
+ i0.ɵɵelementEnd();
603
+ i0.ɵɵelementStart(4, "div", 82);
604
+ i0.ɵɵconditionalCreate(5, RealtimeWhiteboardBoardComponent_Conditional_18_Conditional_5_Template, 2, 4, "div", 83)(6, RealtimeWhiteboardBoardComponent_Conditional_18_Conditional_6_Template, 1, 2, "mj-code-editor", 84);
605
+ i0.ɵɵelementEnd();
606
+ i0.ɵɵconditionalCreate(7, RealtimeWhiteboardBoardComponent_Conditional_18_Conditional_7_Template, 3, 0, "div", 85);
607
+ i0.ɵɵelementStart(8, "div", 86)(9, "button", 87);
608
+ i0.ɵɵlistener("click", function RealtimeWhiteboardBoardComponent_Conditional_18_Template_button_click_9_listener() { i0.ɵɵrestoreView(_r38); const ctx_r3 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r3.RichEditorDone()); });
609
+ i0.ɵɵtext(10, "Done");
610
+ i0.ɵɵelementEnd();
611
+ i0.ɵɵelementStart(11, "button", 88);
612
+ i0.ɵɵlistener("click", function RealtimeWhiteboardBoardComponent_Conditional_18_Template_button_click_11_listener() { i0.ɵɵrestoreView(_r38); const ctx_r3 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r3.RichEditorApply()); });
613
+ i0.ɵɵtext(12, "Apply");
614
+ i0.ɵɵelementEnd();
615
+ i0.ɵɵelement(13, "span", 89);
616
+ i0.ɵɵelementStart(14, "button", 90);
617
+ i0.ɵɵlistener("click", function RealtimeWhiteboardBoardComponent_Conditional_18_Template_button_click_14_listener() { i0.ɵɵrestoreView(_r38); const ctx_r3 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r3.RichEditorCancel()); });
618
+ i0.ɵɵtext(15, "Cancel");
619
+ i0.ɵɵelementEnd()()();
620
+ } if (rf & 2) {
621
+ const ed_r40 = ctx;
622
+ i0.ɵɵadvance(2);
623
+ i0.ɵɵconditional(ed_r40.Kind === "markdown" ? 2 : 3);
624
+ i0.ɵɵadvance(3);
625
+ i0.ɵɵconditional(ed_r40.Kind === "markdown" && ed_r40.Preview ? 5 : 6);
626
+ i0.ɵɵadvance(2);
627
+ i0.ɵɵconditional(ed_r40.Kind === "html" ? 7 : -1);
628
+ } }
629
+ function RealtimeWhiteboardBoardComponent_Conditional_19_For_2_Conditional_0_Template(rf, ctx) { if (rf & 1) {
630
+ i0.ɵɵelement(0, "div", 98);
631
+ } }
632
+ function RealtimeWhiteboardBoardComponent_Conditional_19_For_2_Template(rf, ctx) { if (rf & 1) {
633
+ const _r45 = i0.ɵɵgetCurrentView();
634
+ i0.ɵɵconditionalCreate(0, RealtimeWhiteboardBoardComponent_Conditional_19_For_2_Conditional_0_Template, 1, 0, "div", 98);
635
+ i0.ɵɵelementStart(1, "button", 99);
636
+ i0.ɵɵlistener("click", function RealtimeWhiteboardBoardComponent_Conditional_19_For_2_Template_button_click_1_listener() { const action_r46 = i0.ɵɵrestoreView(_r45).$implicit; const ctx_r3 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r3.OnContextMenuAction(action_r46.ID)); });
637
+ i0.ɵɵelement(2, "i");
638
+ i0.ɵɵtext(3);
639
+ i0.ɵɵelementEnd();
640
+ } if (rf & 2) {
641
+ const action_r46 = ctx.$implicit;
642
+ i0.ɵɵconditional(action_r46.SeparatorBefore ? 0 : -1);
643
+ i0.ɵɵadvance();
644
+ i0.ɵɵclassProp("wb-context-menu__item--danger", action_r46.Danger);
645
+ i0.ɵɵadvance();
646
+ i0.ɵɵclassMap(action_r46.Icon);
647
+ i0.ɵɵadvance();
648
+ i0.ɵɵtextInterpolate1(" ", action_r46.Label, " ");
649
+ } }
650
+ function RealtimeWhiteboardBoardComponent_Conditional_19_Template(rf, ctx) { if (rf & 1) {
651
+ const _r44 = i0.ɵɵgetCurrentView();
652
+ i0.ɵɵelementStart(0, "div", 97);
653
+ i0.ɵɵlistener("pointerdown", function RealtimeWhiteboardBoardComponent_Conditional_19_Template_div_pointerdown_0_listener($event) { i0.ɵɵrestoreView(_r44); return i0.ɵɵresetView($event.stopPropagation()); })("contextmenu", function RealtimeWhiteboardBoardComponent_Conditional_19_Template_div_contextmenu_0_listener($event) { i0.ɵɵrestoreView(_r44); $event.preventDefault(); return i0.ɵɵresetView($event.stopPropagation()); })("wheel", function RealtimeWhiteboardBoardComponent_Conditional_19_Template_div_wheel_0_listener($event) { i0.ɵɵrestoreView(_r44); return i0.ɵɵresetView($event.stopPropagation()); });
654
+ i0.ɵɵrepeaterCreate(1, RealtimeWhiteboardBoardComponent_Conditional_19_For_2_Template, 4, 6, null, null, _forTrack0);
655
+ i0.ɵɵelementEnd();
656
+ } if (rf & 2) {
657
+ const menu_r47 = ctx;
658
+ i0.ɵɵstyleProp("left", menu_r47.Left, "px")("top", menu_r47.Top, "px");
659
+ i0.ɵɵadvance();
660
+ i0.ɵɵrepeater(menu_r47.Actions);
661
+ } }
662
+ function RealtimeWhiteboardBoardComponent_Conditional_21_For_3_Template(rf, ctx) { if (rf & 1) {
663
+ i0.ɵɵnamespaceSVG();
664
+ i0.ɵɵelement(0, "rect", 101);
665
+ } if (rf & 2) {
666
+ const mi_r48 = ctx.$implicit;
667
+ i0.ɵɵattribute("x", mi_r48.X)("y", mi_r48.Y)("width", mi_r48.W)("height", mi_r48.H)("class", mi_r48.Agent ? "mm-item mm-item--sage" : "mm-item");
668
+ } }
669
+ function RealtimeWhiteboardBoardComponent_Conditional_21_Template(rf, ctx) { if (rf & 1) {
670
+ i0.ɵɵelementStart(0, "div", 20);
671
+ i0.ɵɵnamespaceSVG();
672
+ i0.ɵɵelementStart(1, "svg", 100);
673
+ i0.ɵɵrepeaterCreate(2, RealtimeWhiteboardBoardComponent_Conditional_21_For_3_Template, 1, 5, ":svg:rect", 101, i0.ɵɵrepeaterTrackByIndex);
674
+ i0.ɵɵelement(4, "rect", 102);
675
+ i0.ɵɵelementEnd()();
676
+ } if (rf & 2) {
677
+ const ctx_r3 = i0.ɵɵnextContext();
678
+ i0.ɵɵadvance();
679
+ i0.ɵɵattribute("viewBox", ctx_r3.MinimapViewBox);
680
+ i0.ɵɵadvance();
681
+ i0.ɵɵrepeater(ctx_r3.MinimapItems);
682
+ i0.ɵɵadvance(2);
683
+ i0.ɵɵattribute("x", ctx_r3.MinimapViewport.X)("y", ctx_r3.MinimapViewport.Y)("width", ctx_r3.MinimapViewport.W)("height", ctx_r3.MinimapViewport.H);
684
+ } }
685
+ /**
686
+ * Starter document placed by the HTML-widget tool (user click-place) — a one-question
687
+ * quiz that demos the `MJWhiteboard.submit` input bridge: the Submit button posts the
688
+ * picked answer to the host, which surfaces it to the session agent as a context note.
689
+ * (Hardcoded colors are fine here: this is an isolated sandboxed document — the app's
690
+ * design tokens cannot reach inside the opaque-origin frame.)
691
+ */
692
+ export const WHITEBOARD_WIDGET_STARTER_HTML = `<!doctype html>
693
+ <html>
694
+ <head>
695
+ <style>
696
+ body { font-family: system-ui, sans-serif; margin: 14px; color: #1f2937; }
697
+ h3 { margin: 0 0 6px; }
698
+ label { display: block; margin: 4px 0; }
699
+ button { margin-top: 8px; padding: 5px 12px; border: 1px solid #cbd5e1; border-radius: 6px; background: #f1f5f9; cursor: pointer; }
700
+ .hint { margin-top: 8px; color: #6b7280; font-size: 12px; }
701
+ </style>
702
+ </head>
703
+ <body>
704
+ <h3>Quick quiz</h3>
705
+ <p>Which planet is closest to the sun?</p>
706
+ <label><input type="radio" name="q" value="Mercury"> Mercury</label>
707
+ <label><input type="radio" name="q" value="Venus"> Venus</label>
708
+ <label><input type="radio" name="q" value="Mars"> Mars</label>
709
+ <button id="go">Submit answer</button>
710
+ <p class="hint">Submitting sends your answer to the agent via MJWhiteboard.submit.</p>
711
+ <script>
712
+ document.getElementById('go').addEventListener('click', function () {
713
+ var picked = document.querySelector('input[name="q"]:checked');
714
+ MJWhiteboard.submit({ question: 'closest planet to the sun', answer: picked ? picked.value : null });
715
+ });
716
+ </script>
717
+ </body>
718
+ </html>`;
719
+ /** Starter markdown placed by the markdown-panel tool (user click-place). */
720
+ const STARTER_MARKDOWN = '## New note\n\n- point one';
721
+ /** Zoom presets stepped by the zoom cluster (25%–200%, matching the mockup's range). */
722
+ const ZOOM_STEPS = [0.25, 0.5, 0.75, 0.9, 1, 1.25, 1.5, 2];
723
+ const GRID_SIZE = 22;
724
+ /** How long an agent-added item keeps its pop-in/flash class. */
725
+ const POP_IN_MS = 3200;
726
+ /**
727
+ * The LIVE WHITEBOARD canvas surface (`mj-realtime-whiteboard`): dotted-grid board with
728
+ * pan/zoom, per-tool pointer interactions (select/move with ghost + handles, pan, smoothed
729
+ * freehand pen, drag-create shapes, click-place stickies/text with inline edit, image paste,
730
+ * item→item connectors with floating-point fallback, eraser, dismissible highlights),
731
+ * ownership chips, agent pop-in flash and the input-driven agent presence cursor.
732
+ *
733
+ * All mutations go through the shared {@link WhiteboardState} engine — the same API the
734
+ * agent's channel tools use — so undo/redo and the perception feed see one history.
735
+ */
736
+ export class RealtimeWhiteboardBoardComponent {
737
+ constructor() {
738
+ /** Display name of the session's agent ("Sage") — chips, highlight tags, presence label. */
739
+ this.AgentName = 'Agent';
740
+ /** Selected pen ink color. */
741
+ this.PenColor = WHITEBOARD_PEN_COLORS[0];
742
+ /** Selected pen stroke width. */
743
+ this.PenWidth = 4;
744
+ /** Selected shape kind for drag-create. */
745
+ this.ShapeKind = 'rect';
746
+ /** Selected text font size for click-placed text (curated steps; 12 = CSS default). */
747
+ this.TextSize = 12;
748
+ /** Selected text font family for click-placed text. */
749
+ this.TextFamily = 'sans';
750
+ /** Selected text bold state for click-placed text (labels render bold by default). */
751
+ this.TextBold = true;
752
+ /** Selected text color for click-placed text (null = theme default). */
753
+ this.TextColor = null;
754
+ /** Agent presence cursor (pulsing ring + "… is drawing" label), driven by the host. */
755
+ this.AgentPresence = null;
756
+ /**
757
+ * Read-only mode (artifact viewer / session review): all mutating pointer interactions,
758
+ * paste, and inline editing are disabled; pan + zoom remain available for navigation.
759
+ */
760
+ this.ReadOnly = false;
761
+ /** Requests a tool switch decided by a board gesture (e.g. Esc → select). */
762
+ this.ToolChangeRequest = new EventEmitter();
763
+ /**
764
+ * Cancelable BEFORE event: a sandboxed HTML widget called `MJWhiteboard.submit(data)`
765
+ * and the payload passed validation (marker + tracked source window + size cap).
766
+ * Handlers run synchronously — set `Cancel = true` on the args to drop the submission
767
+ * ({@link WidgetSubmitted} then never fires).
768
+ */
769
+ this.WidgetSubmitting = new EventEmitter();
770
+ /**
771
+ * AFTER event: a validated widget submission was accepted (not canceled). Integration
772
+ * layers forward this to their agent/automation runtime — e.g. MJ's realtime channel
773
+ * plugin surfaces it to the live agent as a `[whiteboard]` context note.
774
+ */
775
+ this.WidgetSubmitted = new EventEmitter();
776
+ /**
777
+ * AMBIENT widget telemetry: the injected recorder observed passive form activity
778
+ * (clicks / changes / typing / focus) inside a sandboxed HTML widget — no widget-authored
779
+ * script involved. The batch was validated (marker + tracked source + size cap + shape)
780
+ * and pre-summarized into one compact human line. Integration layers forward it to their
781
+ * agent runtime as low-priority background context (MJ's channel plugin throttles per
782
+ * widget and frames it as a do-not-respond `[whiteboard]` note).
783
+ */
784
+ this.WidgetInteraction = new EventEmitter();
785
+ /**
786
+ * Cancelable BEFORE event: an in-board editor commit (inline sticky/text/shape edit or
787
+ * the markdown/HTML rich editor's Apply/Done) is about to write to the state engine.
788
+ * Set `Cancel = true` synchronously to discard the commit; handlers may also rewrite
789
+ * the args' `Content` / `Title`.
790
+ */
791
+ this.ContentApplying = new EventEmitter();
792
+ /** AFTER event: an editor commit applied to the state engine. */
793
+ this.ContentApplied = new EventEmitter();
794
+ // viewport
795
+ this.PanX = 0;
796
+ this.PanY = 0;
797
+ this.Zoom = 0.9;
798
+ this.MinimapOpen = false;
799
+ // transient interaction state
800
+ this.Interaction = null;
801
+ this.PendingConnector = null;
802
+ this.EditingID = null;
803
+ /** The rich-content editor panel state (markdown / html items), or null when closed. */
804
+ this.RichEditor = null;
805
+ /** The open right-click context menu (canvas-relative position + model), or null. */
806
+ this.ContextMenu = null;
807
+ this.editIsNew = false;
808
+ this.pendingFocus = false;
809
+ this.userStickyCount = 0;
810
+ /** Agent items that just popped in (drive the .pop-in violet flash). */
811
+ this.PopInIDs = new Set();
812
+ this.popInTimers = [];
813
+ this._tool = 'select';
814
+ this.cdr = inject(ChangeDetectorRef);
815
+ // ── Precomputed render buckets ──────────────────────────────────────────────
816
+ // `State.Items` allocates (Array.from + sort) on every access, and the four
817
+ // template-bound getters below each used to call it and re-filter the whole
818
+ // array — so a single change-detection pass on this high-interaction drag
819
+ // surface re-sorted + re-scanned the item list 4×. We partition State.Items
820
+ // into these four buckets ONCE whenever the items/state actually change
821
+ // (initial load + every Changed$ emission, which covers adds/updates/removes
822
+ // AND page switches), and the template binds to the plain array fields.
823
+ this._boxItems = [];
824
+ this._inkItems = [];
825
+ this._connectorItems = [];
826
+ this._highlightItems = [];
827
+ /** Whether the single window 'message' listener (the widget input bridge) is attached. */
828
+ this.widgetMessageListenerAttached = false;
829
+ // ── Hidden-host layout recovery ─────────────────────────────────────────────
830
+ //
831
+ // The board is often CREATED inside a display:none tab pane (a review session's
832
+ // Whiteboard tab, a collapsed surface panel): the canvas measures 0×0, so the
833
+ // viewport-lazy culling computes everything as off-screen and the board renders
834
+ // BLANK until some interaction forces a re-measure. A ResizeObserver watches for
835
+ // the first REAL layout: it re-runs change detection (fixing the culling) and —
836
+ // when the viewport is still untouched — frames the content via FitToContent so a
837
+ // reviewed board opens centered instead of wherever pan (0,0) happens to land.
838
+ /** Observer driving {@link onCanvasResize}; created once the canvas element exists. */
839
+ this.canvasResizeObserver = null;
840
+ /** True once the canvas has had a real (non-zero) layout. */
841
+ this.hadRealLayout = false;
842
+ /** True once the user pans/zooms — the auto-fit must never fight a chosen viewport. */
843
+ this.viewportTouched = false;
844
+ this.Handles = ['nw', 'n', 'ne', 'e', 'se', 's', 'sw', 'w'];
845
+ /**
846
+ * The widget input bridge's host-side listener. The sandboxed frames have OPAQUE
847
+ * origins (their helper posts with `'*'`), so validation happens HERE: the payload
848
+ * must carry the bridge marker AND `event.source` must be the contentWindow of one of
849
+ * this board's tracked widget iframes — anything else is dropped. Accepted payloads
850
+ * are JSON-capped and surfaced through the {@link WidgetSubmitting} (cancelable) /
851
+ * {@link WidgetSubmitted} event pair.
852
+ */
853
+ this.onWindowMessage = (event) => {
854
+ const widget = this.resolveWidgetFromSource(event.source);
855
+ const outcome = EvaluateWidgetSubmitMessage(event.data, widget);
856
+ if (outcome.Kind === 'submit') {
857
+ const submitting = { Event: outcome.Event, Cancel: false };
858
+ this.WidgetSubmitting.emit(submitting);
859
+ if (!submitting.Cancel) {
860
+ this.WidgetSubmitted.emit(outcome.Event);
861
+ }
862
+ this.cdr.markForCheck();
863
+ return;
864
+ }
865
+ if (outcome.Reason === 'oversize') {
866
+ console.warn(`Whiteboard widget submit dropped: payload exceeds ${WHITEBOARD_WIDGET_SUBMIT_MAX_CHARS} chars.`);
867
+ }
868
+ else if (outcome.Reason === 'unknown-source') {
869
+ console.warn('Whiteboard widget submit dropped: message source is not a tracked widget frame.');
870
+ }
871
+ else {
872
+ // 'not-submit' — may be the injected recorder's AMBIENT interaction batch instead.
873
+ this.handleInteractionMessage(event.data, widget);
874
+ }
875
+ };
876
+ }
877
+ /** Active tool (owned by the host; toolbar + keyboard drive it). */
878
+ set Tool(value) {
879
+ if (value !== this._tool) {
880
+ this._tool = value;
881
+ this.CancelTransient();
882
+ }
883
+ }
884
+ get Tool() {
885
+ return this._tool;
886
+ }
887
+ ngOnInit() {
888
+ this.changedSub = this.State.Changed$.subscribe((change) => {
889
+ if (change.Op === 'add' && change.Author === 'agent') {
890
+ this.PopInIDs.add(change.ItemID);
891
+ this.popInTimers.push(setTimeout(() => {
892
+ this.PopInIDs.delete(change.ItemID);
893
+ this.cdr.markForCheck();
894
+ }, POP_IN_MS));
895
+ }
896
+ // NOTE: HTML-widget srcdoc payloads need no invalidation here — the
897
+ // `wbWidgetSrcdoc` pipe is view-scoped (rebuilt per mount, memoized per source),
898
+ // so journal ops can neither leave a re-mounted frame a stale document nor
899
+ // force-reload a still-mounted widget whose Html didn't change.
900
+ // close the rich editor when its target item no longer exists (erased / undone)
901
+ if (this.RichEditor && !this.State.GetItem(this.RichEditor.ItemID)) {
902
+ this.RichEditor = null;
903
+ }
904
+ // close the context menu when its target item / page no longer exists
905
+ if (this.ContextMenu?.ItemID && !this.State.GetItem(this.ContextMenu.ItemID)) {
906
+ this.ContextMenu = null;
907
+ }
908
+ if (this.ContextMenu?.PageID && !this.State.FindPage(this.ContextMenu.PageID)) {
909
+ this.ContextMenu = null;
910
+ }
911
+ this.recomputeRenderBuckets();
912
+ this.syncWidgetMessageListener();
913
+ this.cdr.markForCheck();
914
+ });
915
+ this.recomputeRenderBuckets();
916
+ this.syncWidgetMessageListener();
917
+ }
918
+ /**
919
+ * Partition `State.Items` (one Array.from + sort) into the four render buckets
920
+ * the template binds to. Called on init and on every {@link WhiteboardState.Changed$}
921
+ * emission — the engine emits there for adds/updates/removes and page switches, so
922
+ * the buckets always reflect the active page's current items.
923
+ */
924
+ recomputeRenderBuckets() {
925
+ const box = [];
926
+ const ink = [];
927
+ const connectors = [];
928
+ const highlights = [];
929
+ for (const i of this.State.Items) {
930
+ switch (i.Kind) {
931
+ case 'sticky':
932
+ case 'shape':
933
+ case 'text':
934
+ case 'image':
935
+ case 'markdown':
936
+ case 'html':
937
+ box.push(i);
938
+ break;
939
+ case 'ink':
940
+ ink.push(i);
941
+ break;
942
+ case 'connector':
943
+ connectors.push(i);
944
+ break;
945
+ case 'highlight':
946
+ highlights.push(i);
947
+ break;
948
+ }
949
+ }
950
+ this._boxItems = box;
951
+ this._inkItems = ink;
952
+ this._connectorItems = connectors;
953
+ this._highlightItems = highlights;
954
+ }
955
+ ngOnDestroy() {
956
+ this.changedSub?.unsubscribe();
957
+ for (const t of this.popInTimers) {
958
+ clearTimeout(t);
959
+ }
960
+ if (this.widgetMessageListenerAttached) {
961
+ window.removeEventListener('message', this.onWindowMessage);
962
+ this.widgetMessageListenerAttached = false;
963
+ }
964
+ }
965
+ ngAfterViewChecked() {
966
+ if (this.pendingFocus) {
967
+ this.pendingFocus = false;
968
+ const editor = this.canvasRef?.nativeElement.querySelector('.wb-edit');
969
+ editor?.focus();
970
+ }
971
+ this.observeCanvasSize();
972
+ }
973
+ /** Attaches the ResizeObserver exactly once (no-op where ResizeObserver is unavailable). */
974
+ observeCanvasSize() {
975
+ const el = this.canvasRef?.nativeElement;
976
+ if (!el || this.canvasResizeObserver || typeof ResizeObserver === 'undefined') {
977
+ return;
978
+ }
979
+ this.canvasResizeObserver = new ResizeObserver(() => this.onCanvasResize());
980
+ this.canvasResizeObserver.observe(el);
981
+ this.onCanvasResize();
982
+ }
983
+ /** First non-zero layout: recompute culling and (if untouched) frame the content. */
984
+ onCanvasResize() {
985
+ const el = this.canvasRef?.nativeElement;
986
+ if (!el || el.clientWidth === 0 || el.clientHeight === 0 || this.hadRealLayout) {
987
+ return;
988
+ }
989
+ this.hadRealLayout = true;
990
+ if (!this.viewportTouched) {
991
+ this.FitToContent();
992
+ }
993
+ this.cdr.markForCheck();
994
+ }
995
+ // ────────────────────────────────────────────── render-model getters
996
+ // These four return precomputed buckets (see recomputeRenderBuckets); they are
997
+ // refreshed only when the items/state change, not re-filtered every CD pass.
998
+ get BoxItems() {
999
+ return this._boxItems;
1000
+ }
1001
+ get InkItems() {
1002
+ return this._inkItems;
1003
+ }
1004
+ get ConnectorItems() {
1005
+ return this._connectorItems;
1006
+ }
1007
+ get HighlightItems() {
1008
+ return this._highlightItems;
1009
+ }
1010
+ get ViewportTransform() {
1011
+ return `translate(${this.PanX}px, ${this.PanY}px) scale(${this.Zoom})`;
1012
+ }
1013
+ get GridBackgroundSize() {
1014
+ const s = GRID_SIZE * this.Zoom;
1015
+ return `${s}px ${s}px`;
1016
+ }
1017
+ get GridBackgroundPosition() {
1018
+ return `${this.PanX}px ${this.PanY}px`;
1019
+ }
1020
+ get ZoomPercent() {
1021
+ return Math.round(this.Zoom * 100);
1022
+ }
1023
+ get CursorClass() {
1024
+ switch (this.Tool) {
1025
+ case 'pan': return 'cur-pan';
1026
+ case 'pen':
1027
+ case 'shape':
1028
+ case 'connector': return 'cur-cross';
1029
+ case 'sticky':
1030
+ case 'image':
1031
+ case 'markdown':
1032
+ case 'html': return 'cur-copy';
1033
+ case 'text': return 'cur-text';
1034
+ case 'eraser': return 'cur-eraser';
1035
+ default: return '';
1036
+ }
1037
+ }
1038
+ TrackItem(index, item) {
1039
+ return item.ID;
1040
+ }
1041
+ /**
1042
+ * Explicit wrap width for a text label (resize / tool w) — null lets the CSS max-width
1043
+ * wrap. Honors an in-flight RESIZE transient so the live preview tracks the handle drag
1044
+ * (a move transient is deliberately ignored: its W is the rough bounds ESTIMATE, and
1045
+ * stamping it inline would re-wrap an unsized label mid-drag).
1046
+ */
1047
+ TextWrapWidth(item) {
1048
+ if (item.Kind !== 'text') {
1049
+ return null;
1050
+ }
1051
+ if (this.Interaction?.Type === 'resize' && this.Interaction.ItemID === item.ID) {
1052
+ return this.Interaction.CurBounds.W;
1053
+ }
1054
+ return item.W ?? null;
1055
+ }
1056
+ /** Left position of an item, honoring an in-flight move/resize transient. */
1057
+ ItemX(item) {
1058
+ const t = this.transientBounds(item.ID);
1059
+ return t ? t.X : this.State.ItemBounds(item).X;
1060
+ }
1061
+ ItemY(item) {
1062
+ const t = this.transientBounds(item.ID);
1063
+ return t ? t.Y : this.State.ItemBounds(item).Y;
1064
+ }
1065
+ ItemW(item) {
1066
+ const t = this.transientBounds(item.ID);
1067
+ if (t) {
1068
+ return t.W;
1069
+ }
1070
+ if (item.Kind === 'shape' || item.Kind === 'highlight') {
1071
+ return item.W;
1072
+ }
1073
+ if (item.Kind === 'sticky') {
1074
+ return item.W ?? WHITEBOARD_DEFAULTS.StickyW;
1075
+ }
1076
+ if (item.Kind === 'image') {
1077
+ return item.W ?? WHITEBOARD_DEFAULTS.ImageW;
1078
+ }
1079
+ if (item.Kind === 'markdown' || item.Kind === 'html') {
1080
+ return item.W;
1081
+ }
1082
+ return null;
1083
+ }
1084
+ ItemH(item) {
1085
+ const t = this.transientBounds(item.ID);
1086
+ if (t && (item.Kind === 'shape' || item.Kind === 'highlight' || item.Kind === 'html')) {
1087
+ return t.H;
1088
+ }
1089
+ if (item.Kind === 'shape' || item.Kind === 'highlight' || item.Kind === 'html') {
1090
+ return item.H;
1091
+ }
1092
+ return null;
1093
+ }
1094
+ /** A markdown panel's optional max height (its rendered height stays content-driven). */
1095
+ MarkdownMaxHeight(item) {
1096
+ return item.H ?? null;
1097
+ }
1098
+ /** Whether the item is part of the current (single OR multi) selection. */
1099
+ IsSelected(item) {
1100
+ return this.State.IsItemSelected(item.ID);
1101
+ }
1102
+ /** Dragging visuals: the grabbed item, plus every other member during a GROUP drag. */
1103
+ IsDragging(item) {
1104
+ const i = this.Interaction;
1105
+ if (i?.Type !== 'move' || !i.Moved) {
1106
+ return false;
1107
+ }
1108
+ return UUIDsEqual(i.ItemID, item.ID) || (i.Group && this.State.IsItemSelected(item.ID));
1109
+ }
1110
+ /**
1111
+ * 8 handles render on the selected, resizable item (gate: {@link IsResizableKind}) —
1112
+ * but only for a SINGLE selection; a multi-selection shows the outline treatment only.
1113
+ */
1114
+ ShowHandles(item) {
1115
+ return this.IsSelected(item) && this.State.SelectedIDs.length === 1 && IsResizableKind(item.Kind);
1116
+ }
1117
+ /** The drag ghost rectangle ("from here") at the moved item's original bounds. */
1118
+ get DragGhost() {
1119
+ return this.Interaction?.Type === 'move' && this.Interaction.Moved ? this.Interaction.OrigBounds : null;
1120
+ }
1121
+ /** Live shape-create preview rectangle. */
1122
+ get ShapePreview() {
1123
+ if (this.Interaction?.Type !== 'shape') {
1124
+ return null;
1125
+ }
1126
+ const s = this.Interaction;
1127
+ return {
1128
+ X: Math.min(s.StartX, s.CurX),
1129
+ Y: Math.min(s.StartY, s.CurY),
1130
+ W: Math.abs(s.CurX - s.StartX),
1131
+ H: Math.abs(s.CurY - s.StartY)
1132
+ };
1133
+ }
1134
+ /** Live marquee (rubber-band) selection rectangle, or null when none is in flight. */
1135
+ get MarqueeRect() {
1136
+ if (this.Interaction?.Type !== 'marquee') {
1137
+ return null;
1138
+ }
1139
+ const m = this.Interaction;
1140
+ const rect = {
1141
+ X: Math.min(m.StartX, m.CurX),
1142
+ Y: Math.min(m.StartY, m.CurY),
1143
+ W: Math.abs(m.CurX - m.StartX),
1144
+ H: Math.abs(m.CurY - m.StartY)
1145
+ };
1146
+ return rect.W >= 3 || rect.H >= 3 ? rect : null;
1147
+ }
1148
+ StickyRotation(item) {
1149
+ return `rotate(${item.Rotation ?? 0}deg)`;
1150
+ }
1151
+ StickyTintClass(item) {
1152
+ if (item.Author === 'agent') {
1153
+ return 'sticky--sage';
1154
+ }
1155
+ return item.Tint === 'amber-light' ? 'sticky--amber2' : 'sticky--amber';
1156
+ }
1157
+ HighlightTag(item) {
1158
+ return item.Label || `${item.Author === 'agent' ? this.AgentName : 'You'} highlighted`;
1159
+ }
1160
+ // ────────────────────────────────────────────── text styling
1161
+ /** Family key → CSS font stack ('sans' = null so the board's default font cascades). */
1162
+ static { this.FontStacks = {
1163
+ sans: null,
1164
+ serif: 'Georgia, "Times New Roman", serif',
1165
+ mono: 'var(--mj-font-mono, ui-monospace, "SF Mono", Menlo, monospace)'
1166
+ }; }
1167
+ /** Inline font-size for a styled text/sticky item (null = the kind's CSS default). */
1168
+ ItemFontSize(item) {
1169
+ return item.FontSize != null ? `${item.FontSize}px` : null;
1170
+ }
1171
+ /** Inline font-family stack for a styled text/sticky item (null = inherit). */
1172
+ ItemFontFamily(item) {
1173
+ return item.FontFamily ? RealtimeWhiteboardBoardComponent.FontStacks[item.FontFamily] : null;
1174
+ }
1175
+ /** Inline font-weight for a styled text/sticky item (null = the kind's CSS default). */
1176
+ ItemFontWeight(item) {
1177
+ return item.FontWeight != null ? `${item.FontWeight}` : null;
1178
+ }
1179
+ /**
1180
+ * Inline color for a USER text label. Agent labels keep their violet authorship
1181
+ * treatment from CSS — `Color` never overrides it.
1182
+ */
1183
+ ItemTextColor(item) {
1184
+ return item.Author === 'user' && item.Color ? item.Color : null;
1185
+ }
1186
+ /**
1187
+ * Apply a toolbar style pick to the selected text/sticky item (the "restyle existing"
1188
+ * path — the toolbar emits `StyleSelection`, the host routes it here). No-op when
1189
+ * nothing applicable is selected; color only applies to user text labels.
1190
+ */
1191
+ ApplyTextStyleToSelection(style) {
1192
+ const id = this.State.SelectedID;
1193
+ const item = id ? this.State.GetItem(id) : undefined;
1194
+ if (!id || !item || (item.Kind !== 'sticky' && item.Kind !== 'text')) {
1195
+ return;
1196
+ }
1197
+ const patch = {};
1198
+ if (style.FontSize !== undefined) {
1199
+ patch.FontSize = style.FontSize;
1200
+ }
1201
+ if (style.FontFamily !== undefined) {
1202
+ patch.FontFamily = style.FontFamily;
1203
+ }
1204
+ if (style.Bold !== undefined) {
1205
+ patch.FontWeight = style.Bold ? 700 : 400;
1206
+ }
1207
+ if (style.Color !== undefined && item.Kind === 'text') {
1208
+ patch.Color = style.Color;
1209
+ }
1210
+ if (Object.keys(patch).length > 0) {
1211
+ this.State.UpdateItem(id, patch, 'user');
1212
+ }
1213
+ }
1214
+ // ────────────────────────────────────────────── svg path builders
1215
+ /** Smoothed freehand path (quadratic curves through segment midpoints). */
1216
+ InkPath(points) {
1217
+ if (points.length === 0) {
1218
+ return '';
1219
+ }
1220
+ if (points.length < 3) {
1221
+ const last = points[points.length - 1];
1222
+ return `M ${points[0].X} ${points[0].Y} L ${last.X} ${last.Y}`;
1223
+ }
1224
+ let d = `M ${points[0].X} ${points[0].Y}`;
1225
+ for (let i = 1; i < points.length - 1; i++) {
1226
+ const midX = (points[i].X + points[i + 1].X) / 2;
1227
+ const midY = (points[i].Y + points[i + 1].Y) / 2;
1228
+ d += ` Q ${points[i].X} ${points[i].Y} ${midX} ${midY}`;
1229
+ }
1230
+ const last = points[points.length - 1];
1231
+ d += ` L ${last.X} ${last.Y}`;
1232
+ return d;
1233
+ }
1234
+ /** Curved connector path between resolved (edge-clipped) endpoints. */
1235
+ ConnectorPath(conn) {
1236
+ const { from, to } = this.connectorEndpoints(conn);
1237
+ return this.curveBetween(from, to);
1238
+ }
1239
+ /** Two short arrowhead strokes at the connector's target end. */
1240
+ ConnectorArrow(conn) {
1241
+ const { from, to } = this.connectorEndpoints(conn);
1242
+ return this.arrowAt(from, to);
1243
+ }
1244
+ get PendingConnectorPath() {
1245
+ if (!this.PendingConnector) {
1246
+ return '';
1247
+ }
1248
+ const from = this.pendingFromPoint();
1249
+ return this.curveBetween(from, { X: this.PendingConnector.CurX, Y: this.PendingConnector.CurY });
1250
+ }
1251
+ get LivePenPath() {
1252
+ return this.Interaction?.Type === 'pen' ? this.InkPath(this.Interaction.Points) : '';
1253
+ }
1254
+ connectorEndpoints(conn) {
1255
+ let from = this.State.ResolveEndpoint(conn, 'from');
1256
+ let to = this.State.ResolveEndpoint(conn, 'to');
1257
+ // clip anchored endpoints to the referenced item's bounds edge
1258
+ const fromItem = conn.FromItemID ? this.State.GetItem(conn.FromItemID) : undefined;
1259
+ const toItem = conn.ToItemID ? this.State.GetItem(conn.ToItemID) : undefined;
1260
+ if (fromItem) {
1261
+ from = this.edgePoint(this.State.ItemBounds(fromItem), to);
1262
+ }
1263
+ if (toItem) {
1264
+ to = this.edgePoint(this.State.ItemBounds(toItem), from);
1265
+ }
1266
+ return { from, to };
1267
+ }
1268
+ /** Intersect the segment bounds-center → towards with the bounds rectangle edge. */
1269
+ edgePoint(b, towards) {
1270
+ const cx = b.X + b.W / 2;
1271
+ const cy = b.Y + b.H / 2;
1272
+ const dx = towards.X - cx;
1273
+ const dy = towards.Y - cy;
1274
+ if (dx === 0 && dy === 0) {
1275
+ return { X: cx, Y: cy };
1276
+ }
1277
+ const sx = dx !== 0 ? (b.W / 2) / Math.abs(dx) : Infinity;
1278
+ const sy = dy !== 0 ? (b.H / 2) / Math.abs(dy) : Infinity;
1279
+ const s = Math.min(sx, sy);
1280
+ return { X: cx + dx * s, Y: cy + dy * s };
1281
+ }
1282
+ curveBetween(from, to) {
1283
+ const dx = to.X - from.X;
1284
+ const dy = to.Y - from.Y;
1285
+ if (Math.abs(dy) >= Math.abs(dx)) {
1286
+ // vertical bias (matches the mockup's email→webinar curve)
1287
+ return `M ${from.X} ${from.Y} C ${from.X + dx * 0.08} ${from.Y + dy * 0.45}, ${to.X - dx * 0.08} ${to.Y - dy * 0.45}, ${to.X} ${to.Y}`;
1288
+ }
1289
+ return `M ${from.X} ${from.Y} C ${from.X + dx * 0.45} ${from.Y + dy * 0.08}, ${to.X - dx * 0.45} ${to.Y - dy * 0.08}, ${to.X} ${to.Y}`;
1290
+ }
1291
+ arrowAt(from, to) {
1292
+ const angle = Math.atan2(to.Y - from.Y, to.X - from.X);
1293
+ const len = 13;
1294
+ const spread = 0.46; // ~26°
1295
+ const a1 = angle + Math.PI - spread;
1296
+ const a2 = angle + Math.PI + spread;
1297
+ const p1 = { X: to.X + Math.cos(a1) * len, Y: to.Y + Math.sin(a1) * len };
1298
+ const p2 = { X: to.X + Math.cos(a2) * len, Y: to.Y + Math.sin(a2) * len };
1299
+ return `M ${to.X} ${to.Y} L ${p1.X.toFixed(1)} ${p1.Y.toFixed(1)} M ${to.X} ${to.Y} L ${p2.X.toFixed(1)} ${p2.Y.toFixed(1)}`;
1300
+ }
1301
+ // ────────────────────────────────────────────── pointer interactions
1302
+ OnCanvasPointerDown(event) {
1303
+ if (event.button !== 0) {
1304
+ return;
1305
+ }
1306
+ if (this.ReadOnly) {
1307
+ // Pan stays available for read navigation; every other tool is inert.
1308
+ if (this.Tool === 'pan') {
1309
+ this.Interaction = { Type: 'pan', StartClientX: event.clientX, StartClientY: event.clientY, OrigPanX: this.PanX, OrigPanY: this.PanY };
1310
+ }
1311
+ return;
1312
+ }
1313
+ this.canvasRef?.nativeElement.focus();
1314
+ const p = this.toBoard(event);
1315
+ switch (this.Tool) {
1316
+ case 'select':
1317
+ // marquee (rubber-band) selection starts on empty canvas; a tiny drag (a plain
1318
+ // click) clears the selection at pointerup unless shift is held (additive)
1319
+ this.Interaction = { Type: 'marquee', StartX: p.X, StartY: p.Y, CurX: p.X, CurY: p.Y, Additive: event.shiftKey };
1320
+ break;
1321
+ case 'pan':
1322
+ this.Interaction = { Type: 'pan', StartClientX: event.clientX, StartClientY: event.clientY, OrigPanX: this.PanX, OrigPanY: this.PanY };
1323
+ break;
1324
+ case 'pen':
1325
+ this.Interaction = { Type: 'pen', Points: [p] };
1326
+ break;
1327
+ case 'shape':
1328
+ this.Interaction = { Type: 'shape', StartX: p.X, StartY: p.Y, CurX: p.X, CurY: p.Y };
1329
+ break;
1330
+ case 'sticky':
1331
+ this.placeSticky(p);
1332
+ break;
1333
+ case 'text':
1334
+ this.placeText(p);
1335
+ break;
1336
+ case 'markdown':
1337
+ this.placeMarkdown(p);
1338
+ break;
1339
+ case 'html':
1340
+ this.placeHtml(p);
1341
+ break;
1342
+ case 'image':
1343
+ this.placeImage(p, 'pasted-image.png', null);
1344
+ break;
1345
+ case 'connector':
1346
+ this.connectorClick(null, p);
1347
+ break;
1348
+ case 'eraser':
1349
+ break;
1350
+ }
1351
+ }
1352
+ OnItemPointerDown(event, item) {
1353
+ if (this.ReadOnly) {
1354
+ return;
1355
+ }
1356
+ if (event.button !== 0) {
1357
+ return;
1358
+ }
1359
+ if (UUIDsEqual(this.EditingID, item.ID)) {
1360
+ event.stopPropagation();
1361
+ return;
1362
+ }
1363
+ switch (this.Tool) {
1364
+ case 'select': {
1365
+ event.stopPropagation();
1366
+ if (event.shiftKey) {
1367
+ // shift-click toggles membership in the multi-selection (no drag from it)
1368
+ this.State.ToggleSelect(item.ID);
1369
+ break;
1370
+ }
1371
+ // pointerdown on an already multi-selected item keeps the selection and drags
1372
+ // the whole GROUP; a click without movement collapses to that item at pointerup
1373
+ const group = this.State.IsItemSelected(item.ID) && this.State.SelectedIDs.length > 1;
1374
+ if (!group) {
1375
+ this.State.Select(item.ID);
1376
+ }
1377
+ const p = this.toBoard(event);
1378
+ const bounds = this.State.ItemBounds(item);
1379
+ this.Interaction = { Type: 'move', ItemID: item.ID, StartX: p.X, StartY: p.Y, OrigBounds: bounds, DX: 0, DY: 0, Moved: false, Group: group };
1380
+ break;
1381
+ }
1382
+ case 'eraser':
1383
+ event.stopPropagation();
1384
+ this.State.RemoveItem(item.ID, 'user');
1385
+ break;
1386
+ case 'connector': {
1387
+ event.stopPropagation();
1388
+ this.connectorClick(item.ID, this.toBoard(event));
1389
+ break;
1390
+ }
1391
+ default:
1392
+ // pen / shape / sticky / text / image gestures begin on the canvas — let it bubble
1393
+ break;
1394
+ }
1395
+ }
1396
+ OnHandlePointerDown(event, item, handle) {
1397
+ if (this.ReadOnly) {
1398
+ return;
1399
+ }
1400
+ if (event.button !== 0 || this.Tool !== 'select') {
1401
+ return;
1402
+ }
1403
+ event.stopPropagation();
1404
+ const p = this.toBoard(event);
1405
+ const bounds = this.State.ItemBounds(item);
1406
+ this.Interaction = { Type: 'resize', ItemID: item.ID, Handle: handle, StartX: p.X, StartY: p.Y, OrigBounds: bounds, CurBounds: { ...bounds } };
1407
+ }
1408
+ /** Click on a highlight region dismisses it (mockup: "click dismisses"). */
1409
+ OnHighlightClick(event, item) {
1410
+ event.stopPropagation();
1411
+ this.State.RemoveItem(item.ID, 'user');
1412
+ }
1413
+ OnWindowPointerMove(event) {
1414
+ if (!this.Interaction && !this.PendingConnector) {
1415
+ return;
1416
+ }
1417
+ const p = this.toBoard(event);
1418
+ if (this.PendingConnector) {
1419
+ this.PendingConnector.CurX = p.X;
1420
+ this.PendingConnector.CurY = p.Y;
1421
+ }
1422
+ const i = this.Interaction;
1423
+ if (!i) {
1424
+ return;
1425
+ }
1426
+ switch (i.Type) {
1427
+ case 'move': {
1428
+ i.DX = p.X - i.StartX;
1429
+ i.DY = p.Y - i.StartY;
1430
+ if (!i.Moved && Math.hypot(i.DX, i.DY) > 3) {
1431
+ i.Moved = true;
1432
+ }
1433
+ break;
1434
+ }
1435
+ case 'resize':
1436
+ i.CurBounds = this.resizeBounds(i.OrigBounds, i.Handle, p.X - i.StartX, p.Y - i.StartY);
1437
+ break;
1438
+ case 'pen': {
1439
+ const last = i.Points[i.Points.length - 1];
1440
+ if (Math.hypot(p.X - last.X, p.Y - last.Y) > 2.2) {
1441
+ i.Points.push(p);
1442
+ }
1443
+ break;
1444
+ }
1445
+ case 'shape':
1446
+ i.CurX = p.X;
1447
+ i.CurY = p.Y;
1448
+ break;
1449
+ case 'marquee':
1450
+ i.CurX = p.X;
1451
+ i.CurY = p.Y;
1452
+ break;
1453
+ case 'pan':
1454
+ this.viewportTouched = true; // a chosen viewport — the first-layout auto-fit must not fight it
1455
+ this.PanX = i.OrigPanX + (event.clientX - i.StartClientX);
1456
+ this.PanY = i.OrigPanY + (event.clientY - i.StartClientY);
1457
+ break;
1458
+ }
1459
+ }
1460
+ OnWindowPointerUp() {
1461
+ const i = this.Interaction;
1462
+ if (!i) {
1463
+ return;
1464
+ }
1465
+ this.Interaction = null;
1466
+ switch (i.Type) {
1467
+ case 'move':
1468
+ if (i.Moved) {
1469
+ if (i.Group) {
1470
+ // group drag: every selected item moves by the same delta — ONE undo step
1471
+ this.State.MoveSelectedBy(i.DX, i.DY, 'user');
1472
+ }
1473
+ else {
1474
+ this.State.MoveItem(i.ItemID, i.OrigBounds.X + i.DX, i.OrigBounds.Y + i.DY, 'user');
1475
+ }
1476
+ }
1477
+ else if (i.Group) {
1478
+ // click (no drag) on a multi-selected item collapses the selection to it
1479
+ this.State.Select(i.ItemID);
1480
+ }
1481
+ break;
1482
+ case 'marquee': {
1483
+ const rect = {
1484
+ X: Math.min(i.StartX, i.CurX),
1485
+ Y: Math.min(i.StartY, i.CurY),
1486
+ W: Math.abs(i.CurX - i.StartX),
1487
+ H: Math.abs(i.CurY - i.StartY)
1488
+ };
1489
+ if (rect.W < 3 && rect.H < 3) {
1490
+ // a plain click on empty canvas clears the selection (shift-click keeps it)
1491
+ if (!i.Additive) {
1492
+ this.State.Select(null);
1493
+ }
1494
+ break;
1495
+ }
1496
+ const hits = this.State.ItemsIntersecting(rect).map((it) => it.ID);
1497
+ this.State.SelectMany(i.Additive ? [...this.State.SelectedIDs, ...hits] : hits);
1498
+ break;
1499
+ }
1500
+ case 'resize': {
1501
+ const item = this.State.GetItem(i.ItemID);
1502
+ // per-kind commit: full-box kinds take X/Y/W/H; content-driven-height kinds
1503
+ // (sticky/text/image/markdown) take X/Y/W only — see BuildResizeCommitPatch.
1504
+ const patch = item ? BuildResizeCommitPatch(item.Kind, i.CurBounds) : null;
1505
+ if (patch) {
1506
+ this.State.UpdateItem(i.ItemID, patch, 'user');
1507
+ }
1508
+ break;
1509
+ }
1510
+ case 'pen':
1511
+ if (i.Points.length >= 2) {
1512
+ this.State.AddItem({ Kind: 'ink', Points: i.Points, Color: this.PenColor, StrokeWidth: this.PenWidth }, 'user');
1513
+ }
1514
+ break;
1515
+ case 'shape': {
1516
+ const r = {
1517
+ X: Math.min(i.StartX, i.CurX),
1518
+ Y: Math.min(i.StartY, i.CurY),
1519
+ W: Math.abs(i.CurX - i.StartX),
1520
+ H: Math.abs(i.CurY - i.StartY)
1521
+ };
1522
+ if (r.W >= 16 && r.H >= 16) {
1523
+ const item = this.State.AddItem({ Kind: 'shape', Shape: this.ShapeKind, X: r.X, Y: r.Y, W: Math.max(r.W, 60), H: Math.max(r.H, WHITEBOARD_DEFAULTS.ShapeMinH), Label: '' }, 'user');
1524
+ if (item) {
1525
+ this.State.Select(item.ID);
1526
+ this.beginEdit(item.ID, true);
1527
+ }
1528
+ }
1529
+ break;
1530
+ }
1531
+ case 'pan':
1532
+ break;
1533
+ }
1534
+ }
1535
+ OnWheel(event) {
1536
+ event.preventDefault();
1537
+ if (event.ctrlKey || event.metaKey) {
1538
+ const factor = event.deltaY < 0 ? 1.1 : 1 / 1.1;
1539
+ this.zoomAt(this.clampZoom(this.Zoom * factor), event.clientX, event.clientY);
1540
+ }
1541
+ else {
1542
+ this.PanX -= event.deltaX;
1543
+ this.PanY -= event.deltaY;
1544
+ }
1545
+ }
1546
+ /** Paste an image from the clipboard onto the board (mockup: pasted-image card). */
1547
+ OnPaste(event) {
1548
+ if (this.ReadOnly) {
1549
+ return;
1550
+ }
1551
+ const files = event.clipboardData?.files;
1552
+ if (!files || files.length === 0) {
1553
+ return;
1554
+ }
1555
+ for (let n = 0; n < files.length; n++) {
1556
+ const file = files[n];
1557
+ if (file.type.startsWith('image/')) {
1558
+ event.preventDefault();
1559
+ const center = this.viewportCenter();
1560
+ this.placeImage({ X: center.X - WHITEBOARD_DEFAULTS.ImageW / 2, Y: center.Y - WHITEBOARD_DEFAULTS.ImageH / 2 }, file.name || 'pasted-image.png', URL.createObjectURL(file));
1561
+ return;
1562
+ }
1563
+ }
1564
+ }
1565
+ // ────────────────────────────────────────────── inline editing
1566
+ BeginItemEdit(event, item) {
1567
+ if (item.Kind !== 'sticky' && item.Kind !== 'text' && item.Kind !== 'shape') {
1568
+ return;
1569
+ }
1570
+ event.stopPropagation();
1571
+ this.beginEdit(item.ID, false);
1572
+ }
1573
+ /**
1574
+ * Commit the inline editor's value to the edited sticky / text / shape item. Routes
1575
+ * through the {@link ContentApplying} (cancelable) / {@link ContentApplied} event pair;
1576
+ * an empty commit on a JUST-PLACED sticky/text abandons the placement instead (the item
1577
+ * is removed — that path raises the engine's remove events, not the content pair).
1578
+ */
1579
+ CommitEdit(value) {
1580
+ const id = this.EditingID;
1581
+ if (!id) {
1582
+ return;
1583
+ }
1584
+ this.EditingID = null;
1585
+ const item = this.State.GetItem(id);
1586
+ if (!item) {
1587
+ return;
1588
+ }
1589
+ const text = value.trim();
1590
+ if (text.length === 0 && this.editIsNew && (item.Kind === 'sticky' || item.Kind === 'text')) {
1591
+ this.State.RemoveItem(id, 'user'); // abandoned placement
1592
+ return;
1593
+ }
1594
+ if (item.Kind === 'shape') {
1595
+ this.applyContent(id, 'shape', text);
1596
+ }
1597
+ else if (item.Kind === 'sticky' || item.Kind === 'text') {
1598
+ this.applyContent(id, item.Kind, text);
1599
+ }
1600
+ this.editIsNew = false;
1601
+ }
1602
+ /**
1603
+ * Run one editor commit through the {@link ContentApplying} / {@link ContentApplied}
1604
+ * pair: raise the cancelable BEFORE event, apply the (possibly handler-edited) content
1605
+ * via the state engine when not canceled, and raise the AFTER event when the engine
1606
+ * applied it (an engine-level `ItemUpdating$` veto also suppresses the AFTER event).
1607
+ */
1608
+ applyContent(itemId, kind, content, title) {
1609
+ const applying = { ItemID: itemId, Kind: kind, Content: content, Title: title, Cancel: false };
1610
+ this.ContentApplying.emit(applying);
1611
+ if (applying.Cancel) {
1612
+ return;
1613
+ }
1614
+ let patch;
1615
+ switch (kind) {
1616
+ case 'shape':
1617
+ patch = { Label: applying.Content };
1618
+ break;
1619
+ case 'markdown':
1620
+ patch = { Markdown: applying.Content };
1621
+ break;
1622
+ case 'html':
1623
+ patch = { Html: applying.Content, Title: applying.Title };
1624
+ break;
1625
+ default:
1626
+ patch = { Text: applying.Content };
1627
+ break; // sticky / text
1628
+ }
1629
+ if (this.State.UpdateItem(itemId, patch, 'user')) {
1630
+ this.ContentApplied.emit({ ItemID: itemId, Kind: kind, Content: applying.Content, Title: applying.Title });
1631
+ }
1632
+ }
1633
+ OnEditKeydown(event, value) {
1634
+ event.stopPropagation();
1635
+ if (event.key === 'Enter' && !event.shiftKey) {
1636
+ event.preventDefault();
1637
+ this.CommitEdit(value);
1638
+ }
1639
+ else if (event.key === 'Escape') {
1640
+ event.preventDefault();
1641
+ this.CommitEdit(value);
1642
+ }
1643
+ }
1644
+ // ────────────────────────────────────────────── rich widgets (markdown / html)
1645
+ // The sandboxed iframe's `srcdoc` is produced by the view-scoped `wbWidgetSrcdoc`
1646
+ // pipe (see whiteboard-srcdoc.pipe.ts for the per-mount lifecycle contract and the
1647
+ // full security rationale for bypassing Angular's sanitizer into the sandbox).
1648
+ // ────────────────────────────────────────────── widget input bridge (MJWhiteboard.submit)
1649
+ /** Attach/detach the SINGLE window 'message' listener so it only runs while widgets exist. */
1650
+ syncWidgetMessageListener() {
1651
+ const hasWidgets = this.State.Items.some((i) => i.Kind === 'html');
1652
+ if (hasWidgets && !this.widgetMessageListenerAttached) {
1653
+ window.addEventListener('message', this.onWindowMessage);
1654
+ this.widgetMessageListenerAttached = true;
1655
+ }
1656
+ else if (!hasWidgets && this.widgetMessageListenerAttached) {
1657
+ window.removeEventListener('message', this.onWindowMessage);
1658
+ this.widgetMessageListenerAttached = false;
1659
+ }
1660
+ }
1661
+ /**
1662
+ * Second leg of the message listener: evaluate a non-submit message against the ambient
1663
+ * interaction contract and surface accepted batches through {@link WidgetInteraction}.
1664
+ * `'not-interaction'` is unrelated postMessage traffic — ignored silently, like the
1665
+ * submit path's `'not-submit'`.
1666
+ */
1667
+ handleInteractionMessage(messageData, widget) {
1668
+ const outcome = EvaluateWidgetInteractionMessage(messageData, widget);
1669
+ if (outcome.Kind === 'interaction') {
1670
+ this.WidgetInteraction.emit(outcome.Event);
1671
+ this.cdr.markForCheck();
1672
+ return;
1673
+ }
1674
+ if (outcome.Reason === 'oversize') {
1675
+ console.warn(`Whiteboard widget interaction dropped: batch exceeds ${WHITEBOARD_WIDGET_INTERACTION_MAX_CHARS} chars.`);
1676
+ }
1677
+ else if (outcome.Reason === 'unknown-source') {
1678
+ console.warn('Whiteboard widget interaction dropped: message source is not a tracked widget frame.');
1679
+ }
1680
+ else if (outcome.Reason === 'bad-shape') {
1681
+ console.warn('Whiteboard widget interaction dropped: events batch is malformed.');
1682
+ }
1683
+ }
1684
+ /** Resolve a message `event.source` to the tracked widget iframe (window → item) it belongs to. */
1685
+ resolveWidgetFromSource(source) {
1686
+ if (!source) {
1687
+ return null;
1688
+ }
1689
+ const frames = this.canvasRef?.nativeElement.querySelectorAll('iframe.html-card__frame[data-item-id]');
1690
+ if (!frames) {
1691
+ return null;
1692
+ }
1693
+ for (const frame of Array.from(frames)) {
1694
+ if (frame.contentWindow === source) {
1695
+ const id = frame.dataset['itemId'];
1696
+ const item = id ? this.State.GetItem(id) : undefined;
1697
+ if (item && item.Kind === 'html') {
1698
+ return { ItemID: item.ID, Title: item.Title };
1699
+ }
1700
+ }
1701
+ }
1702
+ return null;
1703
+ }
1704
+ /**
1705
+ * Cheap "on-screen-ish" check that gates iframe instantiation for HTML widgets: only
1706
+ * widgets within (or near) the current viewport get a live sandboxed frame; the rest
1707
+ * render a static placeholder until panned/zoomed into view. Re-entering view re-creates
1708
+ * the frame (any internal widget state resets — acceptable for board decorations).
1709
+ */
1710
+ IsNearViewport(item) {
1711
+ const el = this.canvasRef?.nativeElement;
1712
+ if (!el || el.clientWidth === 0 || el.clientHeight === 0) {
1713
+ // Before the first layout — or while the host pane is display:none (an
1714
+ // unfocused tab) — there is no real viewport to cull against: render rather
1715
+ // than blank out (culling resumes on the first real layout).
1716
+ return true;
1717
+ }
1718
+ const margin = 320;
1719
+ const b = this.State.ItemBounds(item);
1720
+ const vx = -this.PanX / this.Zoom;
1721
+ const vy = -this.PanY / this.Zoom;
1722
+ const vw = el.clientWidth / this.Zoom;
1723
+ const vh = el.clientHeight / this.Zoom;
1724
+ return b.X < vx + vw + margin && b.X + b.W > vx - margin
1725
+ && b.Y < vy + vh + margin && b.Y + b.H > vy - margin;
1726
+ }
1727
+ /** Header title for an HTML widget. */
1728
+ HtmlTitle(item) {
1729
+ return item.Title || 'HTML widget';
1730
+ }
1731
+ /** Open the rich editor for a markdown panel / HTML widget (dblclick or header edit). */
1732
+ BeginRichEdit(event, item) {
1733
+ if (this.ReadOnly || (item.Kind !== 'markdown' && item.Kind !== 'html')) {
1734
+ return;
1735
+ }
1736
+ event.stopPropagation();
1737
+ this.openRichEditor(item);
1738
+ }
1739
+ /** Delete affordance on the HTML widget header (the user removing their own widget). */
1740
+ RemoveItemClick(event, item) {
1741
+ if (this.ReadOnly) {
1742
+ return;
1743
+ }
1744
+ event.stopPropagation();
1745
+ this.State.RemoveItem(item.ID, 'user');
1746
+ }
1747
+ /** Markdown editor: flip between the source editor and the rendered preview. */
1748
+ RichEditorSetPreview(preview) {
1749
+ if (this.RichEditor) {
1750
+ this.RichEditor.Preview = preview;
1751
+ }
1752
+ }
1753
+ OnRichEditorDraftChange(value) {
1754
+ if (this.RichEditor) {
1755
+ this.RichEditor.Draft = value;
1756
+ }
1757
+ }
1758
+ OnRichEditorTitleChange(value) {
1759
+ if (this.RichEditor) {
1760
+ this.RichEditor.TitleDraft = value;
1761
+ }
1762
+ }
1763
+ /**
1764
+ * Commit the draft to the item (ONE update = one undo step) and keep the panel open.
1765
+ * Routes through the {@link ContentApplying} (cancelable) / {@link ContentApplied} pair.
1766
+ */
1767
+ RichEditorApply() {
1768
+ const editor = this.RichEditor;
1769
+ if (!editor || !this.State.GetItem(editor.ItemID)) {
1770
+ return;
1771
+ }
1772
+ if (editor.Kind === 'markdown') {
1773
+ this.applyContent(editor.ItemID, 'markdown', editor.Draft);
1774
+ }
1775
+ else {
1776
+ // empty title clears the header label (UpdateItem skips undefined, so pass '' to clear)
1777
+ this.applyContent(editor.ItemID, 'html', editor.Draft, editor.TitleDraft.trim());
1778
+ }
1779
+ }
1780
+ /** Commit the draft and close the editor panel. */
1781
+ RichEditorDone() {
1782
+ this.RichEditorApply();
1783
+ this.RichEditor = null;
1784
+ }
1785
+ /** Close the editor, discarding any uncommitted draft changes. */
1786
+ RichEditorCancel() {
1787
+ this.RichEditor = null;
1788
+ }
1789
+ /** Esc inside the editor panel closes it; keys must not leak to the board shortcuts. */
1790
+ OnRichEditorKeydown(event) {
1791
+ event.stopPropagation();
1792
+ if (event.key === 'Escape') {
1793
+ event.preventDefault();
1794
+ this.RichEditorCancel();
1795
+ }
1796
+ }
1797
+ openRichEditor(item) {
1798
+ this.RichEditor = item.Kind === 'markdown'
1799
+ ? { ItemID: item.ID, Kind: 'markdown', Draft: item.Markdown, TitleDraft: '', Preview: false }
1800
+ : { ItemID: item.ID, Kind: 'html', Draft: item.Html, TitleDraft: item.Title ?? '', Preview: false };
1801
+ }
1802
+ // ────────────────────────────────────────────── context menu (right-click)
1803
+ /** Right-click on empty canvas → the "add … here" menu (suppresses the native menu). */
1804
+ OnCanvasContextMenu(event) {
1805
+ if (this.ReadOnly) {
1806
+ return; // no board menu in read-only — the native browser menu stays available
1807
+ }
1808
+ event.preventDefault();
1809
+ this.openContextMenu(event, null);
1810
+ }
1811
+ /** Right-click on a board item → the item action menu (selects the item first). */
1812
+ OnItemContextMenu(event, item) {
1813
+ if (this.ReadOnly) {
1814
+ return;
1815
+ }
1816
+ event.preventDefault();
1817
+ event.stopPropagation();
1818
+ if (item.Kind !== 'highlight') {
1819
+ this.State.Select(item.ID);
1820
+ }
1821
+ this.openContextMenu(event, item);
1822
+ }
1823
+ /** Dispatch one picked context-menu action, then close the menu. */
1824
+ OnContextMenuAction(actionId) {
1825
+ const menu = this.ContextMenu;
1826
+ this.ContextMenu = null;
1827
+ if (!menu) {
1828
+ return;
1829
+ }
1830
+ const item = menu.ItemID ? this.State.GetItem(menu.ItemID) : undefined;
1831
+ switch (actionId) {
1832
+ case 'edit':
1833
+ // same paths as dblclick: inline edit for sticky/text/shape, rich editor for md/html
1834
+ if (item && (item.Kind === 'sticky' || item.Kind === 'text' || item.Kind === 'shape')) {
1835
+ this.State.Select(item.ID);
1836
+ this.beginEdit(item.ID, false);
1837
+ }
1838
+ else if (item && (item.Kind === 'markdown' || item.Kind === 'html')) {
1839
+ this.openRichEditor(item);
1840
+ }
1841
+ break;
1842
+ case 'restyle':
1843
+ // open the toolbar's text-style flyout with the item selected — style picks
1844
+ // restyle the selection through the existing StyleSelection path
1845
+ if (item) {
1846
+ this.State.Select(item.ID);
1847
+ this.ToolChangeRequest.emit('text');
1848
+ }
1849
+ break;
1850
+ case 'duplicate':
1851
+ if (item) {
1852
+ const copy = this.State.DuplicateItem(item.ID, 'user');
1853
+ if (copy) {
1854
+ this.State.Select(copy.ID);
1855
+ }
1856
+ }
1857
+ break;
1858
+ case 'bring-front':
1859
+ if (item) {
1860
+ this.State.BringToFront(item.ID, 'user');
1861
+ }
1862
+ break;
1863
+ case 'send-back':
1864
+ if (item) {
1865
+ this.State.SendToBack(item.ID, 'user');
1866
+ }
1867
+ break;
1868
+ case 'delete':
1869
+ if (item) {
1870
+ this.State.RemoveItem(item.ID, 'user');
1871
+ }
1872
+ break;
1873
+ case 'add-sticky':
1874
+ this.placeSticky(menu.Point);
1875
+ break;
1876
+ case 'add-text':
1877
+ this.placeText(menu.Point);
1878
+ break;
1879
+ case 'add-markdown':
1880
+ this.placeMarkdown(menu.Point);
1881
+ break;
1882
+ case 'add-html':
1883
+ this.placeHtml(menu.Point);
1884
+ break;
1885
+ case 'add-page':
1886
+ // same path as the strip's "+" — an auto-named page the engine switches to
1887
+ this.State.AddPage(undefined, 'user');
1888
+ break;
1889
+ case 'page-rename':
1890
+ if (menu.PageID) {
1891
+ this.pagesStrip?.BeginRename(menu.PageID);
1892
+ }
1893
+ break;
1894
+ case 'page-delete':
1895
+ if (menu.PageID) {
1896
+ this.State.RemovePage(menu.PageID, 'user'); // engine guards the last page
1897
+ }
1898
+ break;
1899
+ }
1900
+ }
1901
+ /** Click-away closes the menu (the menu root stops pointerdown propagation for itself). */
1902
+ CloseContextMenu() {
1903
+ if (this.ContextMenu) {
1904
+ this.ContextMenu = null;
1905
+ }
1906
+ }
1907
+ /**
1908
+ * Right-click on a PAGE CHIP in the page strip (forwarded by the strip's
1909
+ * `ChipContextMenu` output) → the page action menu: Rename (routes back into the
1910
+ * strip's inline-rename editor) and Delete (offered only when more than one page
1911
+ * exists — the engine's last-page guard). Never opens in ReadOnly.
1912
+ */
1913
+ OnPageChipContextMenu(payload) {
1914
+ if (this.ReadOnly) {
1915
+ return;
1916
+ }
1917
+ const actions = BuildWhiteboardPageContextMenu(payload.Page, {
1918
+ CanDelete: this.State.Pages.length > 1,
1919
+ ReadOnly: this.ReadOnly
1920
+ });
1921
+ if (actions.length === 0) {
1922
+ return;
1923
+ }
1924
+ this.showContextMenu(payload.Event, actions, null, payload.Page.ID);
1925
+ }
1926
+ openContextMenu(event, item) {
1927
+ this.showContextMenu(event, BuildWhiteboardContextMenu(item), item ? item.ID : null, null);
1928
+ }
1929
+ /** Position + open the shared context-menu panel for any target (item / canvas / page chip). */
1930
+ showContextMenu(event, actions, itemId, pageId) {
1931
+ const el = this.canvasRef?.nativeElement;
1932
+ const rect = el ? el.getBoundingClientRect() : { left: 0, top: 0, width: 0, height: 0 };
1933
+ // keep the menu inside the canvas (estimate: ~200px wide, ~31px per row + padding)
1934
+ const menuW = 200;
1935
+ const menuH = actions.length * 31 + 12;
1936
+ const left = Math.max(4, Math.min(event.clientX - rect.left, Math.max(menuW, rect.width) - menuW));
1937
+ const top = Math.max(4, Math.min(event.clientY - rect.top, Math.max(menuH, rect.height) - menuH));
1938
+ this.ContextMenu = {
1939
+ Left: left,
1940
+ Top: top,
1941
+ Point: this.toBoard(event),
1942
+ ItemID: itemId,
1943
+ PageID: pageId,
1944
+ Actions: actions
1945
+ };
1946
+ }
1947
+ // ────────────────────────────────────────────── zoom / fit / minimap
1948
+ ZoomIn() {
1949
+ const next = ZOOM_STEPS.find((z) => z > this.Zoom + 0.001);
1950
+ this.zoomCentered(next ?? ZOOM_STEPS[ZOOM_STEPS.length - 1]);
1951
+ }
1952
+ /**
1953
+ * Continuous zoom by a multiplicative factor at the viewport center — the
1954
+ * hold-to-zoom tick path (the zoom cluster emits ~3.5% factors every 50 ms while a
1955
+ * +/− button is held). Clamped to the same 25%–200% limits as the stepped zoom.
1956
+ */
1957
+ ZoomByFactor(factor) {
1958
+ if (!Number.isFinite(factor) || factor <= 0) {
1959
+ return;
1960
+ }
1961
+ this.zoomCentered(this.clampZoom(this.Zoom * factor));
1962
+ }
1963
+ ZoomOut() {
1964
+ const lower = [...ZOOM_STEPS].reverse().find((z) => z < this.Zoom - 0.001);
1965
+ this.zoomCentered(lower ?? ZOOM_STEPS[0]);
1966
+ }
1967
+ /** Fit all content into the viewport (25%–200%, 60px padding). */
1968
+ FitToContent() {
1969
+ const bounds = this.State.ContentBounds();
1970
+ const el = this.canvasRef?.nativeElement;
1971
+ if (!bounds || !el || bounds.W === 0 || bounds.H === 0 || el.clientWidth === 0 || el.clientHeight === 0) {
1972
+ return; // a hidden (0×0) host cannot be fit against — wait for a real layout
1973
+ }
1974
+ const pad = 60;
1975
+ const zoom = this.clampZoom(Math.min((el.clientWidth - pad * 2) / bounds.W, (el.clientHeight - pad * 2) / bounds.H));
1976
+ this.Zoom = zoom;
1977
+ this.PanX = (el.clientWidth - bounds.W * zoom) / 2 - bounds.X * zoom;
1978
+ this.PanY = (el.clientHeight - bounds.H * zoom) / 2 - bounds.Y * zoom;
1979
+ }
1980
+ ToggleMinimap() {
1981
+ this.MinimapOpen = !this.MinimapOpen;
1982
+ }
1983
+ /** Cancel any in-flight transient gesture (tool switch, Esc) — incl. the rich editor + context menu. */
1984
+ CancelTransient() {
1985
+ this.Interaction = null;
1986
+ this.PendingConnector = null;
1987
+ this.RichEditor = null;
1988
+ this.ContextMenu = null;
1989
+ }
1990
+ // minimap render model
1991
+ get MinimapViewBox() {
1992
+ const world = this.minimapWorld();
1993
+ return `${world.X} ${world.Y} ${world.W} ${world.H}`;
1994
+ }
1995
+ get MinimapItems() {
1996
+ return this.State.Items
1997
+ .filter((i) => i.Kind !== 'highlight')
1998
+ .map((i) => {
1999
+ const b = this.State.ItemBounds(i);
2000
+ return { X: b.X, Y: b.Y, W: Math.max(b.W, 8), H: Math.max(b.H, 8), Agent: i.Author === 'agent' };
2001
+ });
2002
+ }
2003
+ get MinimapViewport() {
2004
+ const el = this.canvasRef?.nativeElement;
2005
+ const w = el ? el.clientWidth : 0;
2006
+ const h = el ? el.clientHeight : 0;
2007
+ return { X: -this.PanX / this.Zoom, Y: -this.PanY / this.Zoom, W: w / this.Zoom, H: h / this.Zoom };
2008
+ }
2009
+ minimapWorld() {
2010
+ const content = this.State.ContentBounds();
2011
+ const vp = this.MinimapViewport;
2012
+ const minX = Math.min(content?.X ?? vp.X, vp.X) - 40;
2013
+ const minY = Math.min(content?.Y ?? vp.Y, vp.Y) - 40;
2014
+ const maxX = Math.max(content ? content.X + content.W : vp.X + vp.W, vp.X + vp.W) + 40;
2015
+ const maxY = Math.max(content ? content.Y + content.H : vp.Y + vp.H, vp.Y + vp.H) + 40;
2016
+ return { X: minX, Y: minY, W: Math.max(maxX - minX, 1), H: Math.max(maxY - minY, 1) };
2017
+ }
2018
+ // ────────────────────────────────────────────── internals
2019
+ toBoard(event) {
2020
+ const el = this.canvasRef?.nativeElement;
2021
+ const rect = el ? el.getBoundingClientRect() : { left: 0, top: 0 };
2022
+ return {
2023
+ X: (event.clientX - rect.left - this.PanX) / this.Zoom,
2024
+ Y: (event.clientY - rect.top - this.PanY) / this.Zoom
2025
+ };
2026
+ }
2027
+ viewportCenter() {
2028
+ const el = this.canvasRef?.nativeElement;
2029
+ const w = el ? el.clientWidth : 0;
2030
+ const h = el ? el.clientHeight : 0;
2031
+ return { X: (w / 2 - this.PanX) / this.Zoom, Y: (h / 2 - this.PanY) / this.Zoom };
2032
+ }
2033
+ clampZoom(z) {
2034
+ return Math.min(2, Math.max(0.25, z));
2035
+ }
2036
+ zoomCentered(next) {
2037
+ this.viewportTouched = true; // user-driven zoom — disable the first-layout auto-fit
2038
+ const el = this.canvasRef?.nativeElement;
2039
+ if (!el) {
2040
+ this.Zoom = next;
2041
+ return;
2042
+ }
2043
+ const rect = el.getBoundingClientRect();
2044
+ this.zoomAt(next, rect.left + rect.width / 2, rect.top + rect.height / 2);
2045
+ }
2046
+ zoomAt(next, clientX, clientY) {
2047
+ const el = this.canvasRef?.nativeElement;
2048
+ if (!el) {
2049
+ this.Zoom = next;
2050
+ return;
2051
+ }
2052
+ const rect = el.getBoundingClientRect();
2053
+ const px = clientX - rect.left;
2054
+ const py = clientY - rect.top;
2055
+ const bx = (px - this.PanX) / this.Zoom;
2056
+ const by = (py - this.PanY) / this.Zoom;
2057
+ this.Zoom = next;
2058
+ this.PanX = px - bx * next;
2059
+ this.PanY = py - by * next;
2060
+ }
2061
+ resizeBounds(orig, handle, dx, dy) {
2062
+ let { X, Y, W, H } = orig;
2063
+ if (handle.includes('w')) {
2064
+ X = orig.X + dx;
2065
+ W = orig.W - dx;
2066
+ }
2067
+ if (handle.includes('e')) {
2068
+ W = orig.W + dx;
2069
+ }
2070
+ if (handle.includes('n')) {
2071
+ Y = orig.Y + dy;
2072
+ H = orig.H - dy;
2073
+ }
2074
+ if (handle.includes('s')) {
2075
+ H = orig.H + dy;
2076
+ }
2077
+ const minSize = 24;
2078
+ if (W < minSize) {
2079
+ if (handle.includes('w')) {
2080
+ X -= minSize - W;
2081
+ }
2082
+ W = minSize;
2083
+ }
2084
+ if (H < minSize) {
2085
+ if (handle.includes('n')) {
2086
+ Y -= minSize - H;
2087
+ }
2088
+ H = minSize;
2089
+ }
2090
+ return { X, Y, W, H };
2091
+ }
2092
+ transientBounds(itemId) {
2093
+ const i = this.Interaction;
2094
+ if (i?.Type === 'move' && i.Moved) {
2095
+ if (i.ItemID === itemId) {
2096
+ return { X: i.OrigBounds.X + i.DX, Y: i.OrigBounds.Y + i.DY, W: i.OrigBounds.W, H: i.OrigBounds.H };
2097
+ }
2098
+ // GROUP drag: every other selected member previews at the same live delta
2099
+ // (state is untouched mid-drag, so ItemBounds still reports the original spot)
2100
+ if (i.Group && this.State.IsItemSelected(itemId)) {
2101
+ const item = this.State.GetItem(itemId);
2102
+ if (item) {
2103
+ const b = this.State.ItemBounds(item);
2104
+ return { X: b.X + i.DX, Y: b.Y + i.DY, W: b.W, H: b.H };
2105
+ }
2106
+ }
2107
+ }
2108
+ if (i?.Type === 'resize' && i.ItemID === itemId) {
2109
+ return i.CurBounds;
2110
+ }
2111
+ return null;
2112
+ }
2113
+ placeSticky(p) {
2114
+ const tint = this.userStickyCount % 2 === 0 ? 'amber' : 'amber-light';
2115
+ const rotation = this.userStickyCount % 2 === 0 ? -1 : 0.8;
2116
+ this.userStickyCount++;
2117
+ const item = this.State.AddItem({ Kind: 'sticky', X: p.X - WHITEBOARD_DEFAULTS.StickyW / 2, Y: p.Y - 24, Text: '', Tint: tint, Rotation: rotation }, 'user');
2118
+ if (!item) {
2119
+ return; // canceled via ItemAdding
2120
+ }
2121
+ this.State.Select(item.ID);
2122
+ this.beginEdit(item.ID, true);
2123
+ }
2124
+ placeText(p) {
2125
+ // Stamp only non-default style choices so an untouched flyout keeps the default look
2126
+ // (and the compact perception payloads stay free of noise fields).
2127
+ const item = this.State.AddItem({
2128
+ Kind: 'text',
2129
+ X: p.X,
2130
+ Y: p.Y - 9,
2131
+ Text: '',
2132
+ FontSize: this.TextSize !== 12 ? this.TextSize : undefined,
2133
+ FontFamily: this.TextFamily !== 'sans' ? this.TextFamily : undefined,
2134
+ FontWeight: this.TextBold ? undefined : 400,
2135
+ Color: this.TextColor ?? undefined
2136
+ }, 'user');
2137
+ if (!item) {
2138
+ return; // canceled via ItemAdding
2139
+ }
2140
+ this.State.Select(item.ID);
2141
+ this.beginEdit(item.ID, true);
2142
+ }
2143
+ placeImage(p, name, url) {
2144
+ const item = this.State.AddItem({ Kind: 'image', X: p.X, Y: p.Y, Name: name, Url: url }, 'user');
2145
+ if (item) {
2146
+ this.State.Select(item.ID);
2147
+ }
2148
+ }
2149
+ /** Click-place a starter markdown panel and open its editor. */
2150
+ placeMarkdown(p) {
2151
+ const item = this.State.AddItem({
2152
+ Kind: 'markdown',
2153
+ X: p.X - WHITEBOARD_DEFAULTS.MarkdownW / 2,
2154
+ Y: p.Y - 20,
2155
+ W: WHITEBOARD_DEFAULTS.MarkdownW,
2156
+ Markdown: STARTER_MARKDOWN
2157
+ }, 'user');
2158
+ if (!item) {
2159
+ return; // canceled via ItemAdding
2160
+ }
2161
+ this.State.Select(item.ID);
2162
+ this.openRichEditor(item);
2163
+ }
2164
+ /** Click-place a starter HTML widget (sandboxed) and open its editor. */
2165
+ placeHtml(p) {
2166
+ const item = this.State.AddItem({
2167
+ Kind: 'html',
2168
+ X: p.X - WHITEBOARD_DEFAULTS.HtmlW / 2,
2169
+ Y: p.Y - 20,
2170
+ W: WHITEBOARD_DEFAULTS.HtmlW,
2171
+ H: WHITEBOARD_DEFAULTS.HtmlH,
2172
+ Html: WHITEBOARD_WIDGET_STARTER_HTML,
2173
+ Title: 'New widget'
2174
+ }, 'user');
2175
+ if (!item) {
2176
+ return; // canceled via ItemAdding
2177
+ }
2178
+ this.State.Select(item.ID);
2179
+ this.openRichEditor(item);
2180
+ }
2181
+ beginEdit(id, isNew) {
2182
+ this.EditingID = id;
2183
+ this.editIsNew = isNew;
2184
+ this.pendingFocus = true;
2185
+ }
2186
+ connectorClick(itemId, p) {
2187
+ if (!this.PendingConnector) {
2188
+ this.PendingConnector = {
2189
+ FromItemID: itemId,
2190
+ FromPoint: itemId ? null : p,
2191
+ CurX: p.X,
2192
+ CurY: p.Y
2193
+ };
2194
+ return;
2195
+ }
2196
+ const pending = this.PendingConnector;
2197
+ this.PendingConnector = null;
2198
+ if (itemId && pending.FromItemID === itemId) {
2199
+ return; // self-connector — ignore
2200
+ }
2201
+ this.State.AddItem({
2202
+ Kind: 'connector',
2203
+ FromItemID: pending.FromItemID,
2204
+ ToItemID: itemId,
2205
+ FromPoint: pending.FromPoint,
2206
+ ToPoint: itemId ? null : p
2207
+ }, 'user');
2208
+ }
2209
+ pendingFromPoint() {
2210
+ const pc = this.PendingConnector;
2211
+ if (!pc) {
2212
+ return { X: 0, Y: 0 };
2213
+ }
2214
+ if (pc.FromItemID) {
2215
+ const item = this.State.GetItem(pc.FromItemID);
2216
+ if (item) {
2217
+ const b = this.State.ItemBounds(item);
2218
+ return { X: b.X + b.W / 2, Y: b.Y + b.H / 2 };
2219
+ }
2220
+ }
2221
+ return pc.FromPoint ?? { X: 0, Y: 0 };
2222
+ }
2223
+ static { this.ɵfac = function RealtimeWhiteboardBoardComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || RealtimeWhiteboardBoardComponent)(); }; }
2224
+ static { this.ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: RealtimeWhiteboardBoardComponent, selectors: [["mj-realtime-whiteboard"]], viewQuery: function RealtimeWhiteboardBoardComponent_Query(rf, ctx) { if (rf & 1) {
2225
+ i0.ɵɵviewQuery(_c0, 5)(RealtimeWhiteboardPagesComponent, 5);
2226
+ } if (rf & 2) {
2227
+ let _t;
2228
+ i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.canvasRef = _t.first);
2229
+ i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.pagesStrip = _t.first);
2230
+ } }, hostBindings: function RealtimeWhiteboardBoardComponent_HostBindings(rf, ctx) { if (rf & 1) {
2231
+ i0.ɵɵlistener("pointermove", function RealtimeWhiteboardBoardComponent_pointermove_HostBindingHandler($event) { return ctx.OnWindowPointerMove($event); }, i0.ɵɵresolveWindow)("pointerup", function RealtimeWhiteboardBoardComponent_pointerup_HostBindingHandler() { return ctx.OnWindowPointerUp(); }, i0.ɵɵresolveWindow)("pointerdown", function RealtimeWhiteboardBoardComponent_pointerdown_HostBindingHandler() { return ctx.CloseContextMenu(); }, i0.ɵɵresolveDocument);
2232
+ } }, inputs: { State: "State", AgentName: "AgentName", Tool: "Tool", PenColor: "PenColor", PenWidth: "PenWidth", ShapeKind: "ShapeKind", TextSize: "TextSize", TextFamily: "TextFamily", TextBold: "TextBold", TextColor: "TextColor", AgentPresence: "AgentPresence", ReadOnly: "ReadOnly" }, outputs: { ToolChangeRequest: "ToolChangeRequest", WidgetSubmitting: "WidgetSubmitting", WidgetSubmitted: "WidgetSubmitted", WidgetInteraction: "WidgetInteraction", ContentApplying: "ContentApplying", ContentApplied: "ContentApplied" }, decls: 22, vars: 23, consts: [["canvas", ""], ["edit", ""], ["editShape", ""], ["editText", ""], ["reTitle", ""], ["tabindex", "0", 3, "pointerdown", "contextmenu", "wheel", "paste"], [1, "board-viewport"], [1, "ink-layer"], ["fill", "none", "stroke-linecap", "round", "stroke-linejoin", "round", 3, "ink-sage", "wb-erasable", "wb-ink-selected"], [3, "conn-sage", "conn-user"], ["fill", "none", "stroke-linecap", "round", "stroke-linejoin", "round", 1, "wb-live-pen"], ["stroke-width", "2", "fill", "none", "stroke-linecap", "round", 1, "wb-pending-conn"], ["title", "Click to dismiss", 1, "sage-highlight", 3, "user-highlight", "left", "top", "width", "height"], [1, "drag-ghost", 3, "left", "top", "width", "height"], [1, "shape-preview", 3, "left", "top", "width", "height"], [1, "wb-marquee", 3, "left", "top", "width", "height"], [1, "cursor", "cursor--sage", 3, "left", "top"], [1, "rich-editor"], [1, "wb-context-menu", 3, "left", "top"], [3, "ChipContextMenu", "State", "ReadOnly"], [1, "minimap"], ["fill", "none", "stroke-linecap", "round", "stroke-linejoin", "round", 3, "pointerdown", "contextmenu"], ["stroke-width", "2.5", "fill", "none", "stroke-linecap", "round", 3, "pointerdown", "contextmenu"], ["stroke-width", "2.5", "fill", "none", "stroke-linecap", "round"], ["title", "Click to dismiss", 1, "sage-highlight", 3, "pointerdown", "contextmenu"], [1, "hl-tag"], [1, "fa-solid", "fa-highlighter"], [1, "drag-ghost"], [1, "shape-preview"], [1, "wb-marquee"], [3, "class", "selected", "dragging", "pop-in", "left", "top", "width", "fontSize", "fontFamily", "fontWeight", "transform"], [1, "bi", "shape-box", 3, "shape-box--ellipse", "shape-box--diamond", "shape-box--sage", "selected", "dragging", "pop-in", "left", "top", "width", "height"], [1, "bi", "tlabel", 3, "tlabel--user", "selected", "dragging", "pop-in", "left", "top", "width", "maxWidth", "fontSize", "fontFamily", "fontWeight", "color"], [1, "bi", "md-card", 3, "md-card--sage", "selected", "dragging", "pop-in", "left", "top", "width"], [1, "bi", "html-card", 3, "html-card--sage", "selected", "dragging", "pop-in", "left", "top", "width", "height"], [1, "bi", "img-card", 3, "selected", "dragging", "pop-in", "left", "top", "width"], [3, "pointerdown", "dblclick", "contextmenu"], [1, "author-chip", "author-chip--sage"], [1, "author-chip", "author-chip--you"], ["rows", "3", 1, "wb-edit", 3, "value"], [1, "fa-solid", "fa-feather"], [1, "fa-solid", "fa-user"], ["rows", "3", 1, "wb-edit", 3, "keydown", "blur", "value"], [3, "class"], [3, "pointerdown"], [1, "bi", "shape-box", 3, "pointerdown", "dblclick", "contextmenu"], ["rows", "1", 1, "wb-edit", "wb-edit--shape", 3, "value"], ["rows", "1", 1, "wb-edit", "wb-edit--shape", 3, "keydown", "blur", "value"], [1, "sub"], [1, "bi", "tlabel", 3, "pointerdown", "dblclick", "contextmenu"], ["type", "text", 1, "wb-edit", "wb-edit--text", 3, "value"], ["type", "text", 1, "wb-edit", "wb-edit--text", 3, "keydown", "blur", "value"], [1, "bi", "md-card", 3, "pointerdown", "dblclick", "contextmenu"], [1, "md-card__body"], [3, "data", "enableMermaid", "enableCodeCopy", "enableCollapsibleHeadings"], [1, "fa-brands", "fa-markdown"], [1, "bi", "html-card", 3, "pointerdown", "contextmenu"], [1, "html-card__head", 3, "dblclick"], [1, "fa-solid", "fa-code", "html-card__glyph"], [1, "html-card__title"], [1, "author-chip", "author-chip--sage", "author-chip--inline"], [1, "author-chip", "author-chip--you", "author-chip--inline"], [1, "html-card__body"], ["sandbox", "allow-scripts", 1, "html-card__frame", 3, "srcdoc"], [1, "html-card__lazy"], ["type", "button", "title", "Edit HTML", 1, "html-card__btn", 3, "pointerdown", "click"], [1, "fa-solid", "fa-pen"], ["type", "button", "title", "Remove widget", 1, "html-card__btn", 3, "pointerdown", "click"], [1, "fa-solid", "fa-trash-can"], [1, "fa-solid", "fa-code"], [1, "bi", "img-card", 3, "pointerdown", "contextmenu"], [1, "img-card__body"], [3, "src", "alt"], [1, "fa-regular", "fa-image"], [1, "img-card__name"], [1, "fa-solid", "fa-chart-area"], [1, "fa-solid", "fa-paste"], [1, "cursor", "cursor--sage"], [1, "tip"], [1, "lbl"], [1, "rich-editor", 3, "keydown", "pointerdown", "dblclick", "wheel"], [1, "rich-editor__head"], [1, "rich-editor__body"], [1, "rich-editor__preview"], [1, "rich-editor__code", 3, "value", "language"], [1, "rich-editor__note"], [1, "rich-editor__foot"], ["type", "button", 1, "re-btn", "re-btn--primary", 3, "click"], ["type", "button", "title", "Apply without closing \u2014 re-renders the widget", 1, "re-btn", 3, "click"], [1, "re-spacer"], ["type", "button", "title", "Close without applying draft changes", 1, "re-btn", 3, "click"], [1, "rich-editor__label"], [1, "rich-editor__modes"], ["type", "button", 1, "re-mode", 3, "click"], ["type", "text", "placeholder", "Title", 1, "rich-editor__title", 3, "input", "value"], [1, "rich-editor__code", 3, "change", "value", "language"], [1, "fa-solid", "fa-shield-halved"], [1, "wb-context-menu", 3, "pointerdown", "contextmenu", "wheel"], [1, "wb-context-menu__sep"], ["type", "button", 1, "wb-context-menu__item", 3, "click"], ["preserveAspectRatio", "xMidYMid meet"], ["rx", "6"], ["rx", "8", 1, "mm-viewport"]], template: function RealtimeWhiteboardBoardComponent_Template(rf, ctx) { if (rf & 1) {
2233
+ const _r1 = i0.ɵɵgetCurrentView();
2234
+ i0.ɵɵelementStart(0, "div", 5, 0);
2235
+ i0.ɵɵlistener("pointerdown", function RealtimeWhiteboardBoardComponent_Template_div_pointerdown_0_listener($event) { i0.ɵɵrestoreView(_r1); return i0.ɵɵresetView(ctx.OnCanvasPointerDown($event)); })("contextmenu", function RealtimeWhiteboardBoardComponent_Template_div_contextmenu_0_listener($event) { i0.ɵɵrestoreView(_r1); return i0.ɵɵresetView(ctx.OnCanvasContextMenu($event)); })("wheel", function RealtimeWhiteboardBoardComponent_Template_div_wheel_0_listener($event) { i0.ɵɵrestoreView(_r1); return i0.ɵɵresetView(ctx.OnWheel($event)); })("paste", function RealtimeWhiteboardBoardComponent_Template_div_paste_0_listener($event) { i0.ɵɵrestoreView(_r1); return i0.ɵɵresetView(ctx.OnPaste($event)); });
2236
+ i0.ɵɵelementStart(2, "div", 6);
2237
+ i0.ɵɵnamespaceSVG();
2238
+ i0.ɵɵelementStart(3, "svg", 7);
2239
+ i0.ɵɵrepeaterCreate(4, RealtimeWhiteboardBoardComponent_For_5_Template, 1, 9, ":svg:path", 8, _forTrack0);
2240
+ i0.ɵɵrepeaterCreate(6, RealtimeWhiteboardBoardComponent_For_7_Template, 3, 10, ":svg:g", 9, _forTrack0);
2241
+ i0.ɵɵconditionalCreate(8, RealtimeWhiteboardBoardComponent_Conditional_8_Template, 1, 3, ":svg:path", 10);
2242
+ i0.ɵɵconditionalCreate(9, RealtimeWhiteboardBoardComponent_Conditional_9_Template, 1, 1, ":svg:path", 11);
2243
+ i0.ɵɵelementEnd();
2244
+ i0.ɵɵrepeaterCreate(10, RealtimeWhiteboardBoardComponent_For_11_Template, 4, 11, "div", 12, _forTrack0);
2245
+ i0.ɵɵconditionalCreate(12, RealtimeWhiteboardBoardComponent_Conditional_12_Template, 1, 8, "div", 13);
2246
+ i0.ɵɵconditionalCreate(13, RealtimeWhiteboardBoardComponent_Conditional_13_Template, 1, 8, "div", 14);
2247
+ i0.ɵɵconditionalCreate(14, RealtimeWhiteboardBoardComponent_Conditional_14_Template, 1, 8, "div", 15);
2248
+ i0.ɵɵrepeaterCreate(15, RealtimeWhiteboardBoardComponent_For_16_Template, 6, 1, null, null, _forTrack0);
2249
+ i0.ɵɵconditionalCreate(17, RealtimeWhiteboardBoardComponent_Conditional_17_Template, 6, 5, "div", 16);
2250
+ i0.ɵɵelementEnd();
2251
+ i0.ɵɵconditionalCreate(18, RealtimeWhiteboardBoardComponent_Conditional_18_Template, 16, 3, "div", 17);
2252
+ i0.ɵɵconditionalCreate(19, RealtimeWhiteboardBoardComponent_Conditional_19_Template, 3, 4, "div", 18);
2253
+ i0.ɵɵnamespaceHTML();
2254
+ i0.ɵɵelementStart(20, "mj-realtime-whiteboard-pages", 19);
2255
+ i0.ɵɵlistener("ChipContextMenu", function RealtimeWhiteboardBoardComponent_Template_mj_realtime_whiteboard_pages_ChipContextMenu_20_listener($event) { i0.ɵɵrestoreView(_r1); return i0.ɵɵresetView(ctx.OnPageChipContextMenu($event)); });
2256
+ i0.ɵɵelementEnd();
2257
+ i0.ɵɵconditionalCreate(21, RealtimeWhiteboardBoardComponent_Conditional_21_Template, 5, 5, "div", 20);
2258
+ i0.ɵɵelementEnd();
2259
+ } if (rf & 2) {
2260
+ let tmp_12_0;
2261
+ let tmp_13_0;
2262
+ let tmp_14_0;
2263
+ let tmp_17_0;
2264
+ let tmp_18_0;
2265
+ i0.ɵɵclassMap("board-canvas " + ctx.CursorClass);
2266
+ i0.ɵɵstyleProp("background-size", ctx.GridBackgroundSize)("background-position", ctx.GridBackgroundPosition);
2267
+ i0.ɵɵclassProp("is-panning", (ctx.Interaction == null ? null : ctx.Interaction.Type) === "pan")("is-interacting", ctx.Interaction !== null || ctx.PendingConnector !== null);
2268
+ i0.ɵɵadvance(2);
2269
+ i0.ɵɵstyleProp("transform", ctx.ViewportTransform);
2270
+ i0.ɵɵadvance(2);
2271
+ i0.ɵɵrepeater(ctx.InkItems);
2272
+ i0.ɵɵadvance(2);
2273
+ i0.ɵɵrepeater(ctx.ConnectorItems);
2274
+ i0.ɵɵadvance(2);
2275
+ i0.ɵɵconditional(ctx.LivePenPath ? 8 : -1);
2276
+ i0.ɵɵadvance();
2277
+ i0.ɵɵconditional(ctx.PendingConnector ? 9 : -1);
2278
+ i0.ɵɵadvance();
2279
+ i0.ɵɵrepeater(ctx.HighlightItems);
2280
+ i0.ɵɵadvance(2);
2281
+ i0.ɵɵconditional((tmp_12_0 = ctx.DragGhost) ? 12 : -1, tmp_12_0);
2282
+ i0.ɵɵadvance();
2283
+ i0.ɵɵconditional((tmp_13_0 = ctx.ShapePreview) ? 13 : -1, tmp_13_0);
2284
+ i0.ɵɵadvance();
2285
+ i0.ɵɵconditional((tmp_14_0 = ctx.MarqueeRect) ? 14 : -1, tmp_14_0);
2286
+ i0.ɵɵadvance();
2287
+ i0.ɵɵrepeater(ctx.BoxItems);
2288
+ i0.ɵɵadvance(2);
2289
+ i0.ɵɵconditional((ctx.AgentPresence == null ? null : ctx.AgentPresence.Active) ? 17 : -1);
2290
+ i0.ɵɵadvance();
2291
+ i0.ɵɵconditional((tmp_17_0 = ctx.RichEditor) ? 18 : -1, tmp_17_0);
2292
+ i0.ɵɵadvance();
2293
+ i0.ɵɵconditional((tmp_18_0 = ctx.ContextMenu) ? 19 : -1, tmp_18_0);
2294
+ i0.ɵɵadvance();
2295
+ i0.ɵɵproperty("State", ctx.State)("ReadOnly", ctx.ReadOnly);
2296
+ i0.ɵɵadvance();
2297
+ i0.ɵɵconditional(ctx.MinimapOpen ? 21 : -1);
2298
+ } }, dependencies: [CommonModule, MarkdownModule, i1.MarkdownComponent, CodeEditorModule, i2.CodeEditorComponent, RealtimeWhiteboardPagesComponent, WhiteboardWidgetSrcdocPipe], styles: ["\n\n\n\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: block;\n position: absolute;\n inset: 0;\n}\n\n\n\n.board-canvas[_ngcontent-%COMP%] {\n position: absolute;\n inset: 0;\n overflow: hidden;\n outline: none;\n background-color: var(--mj-bg-surface-sunken);\n background-image: radial-gradient(color-mix(in srgb, var(--mj-color-neutral-400, #94a3b8) 15%, transparent) 1px, transparent 1px);\n background-size: 22px 22px;\n cursor: default;\n touch-action: none;\n}\n.cur-pan[_ngcontent-%COMP%] { cursor: grab; }\n.cur-pan.is-panning[_ngcontent-%COMP%] { cursor: grabbing; }\n.cur-cross[_ngcontent-%COMP%] { cursor: crosshair; }\n.cur-copy[_ngcontent-%COMP%] { cursor: copy; }\n.cur-text[_ngcontent-%COMP%] { cursor: text; }\n.cur-eraser[_ngcontent-%COMP%] { cursor: cell; }\n\n.board-viewport[_ngcontent-%COMP%] {\n position: absolute;\n left: 0;\n top: 0;\n width: 0;\n height: 0;\n transform-origin: 0 0;\n overflow: visible;\n}\n\n\n\n.ink-layer[_ngcontent-%COMP%] {\n position: absolute;\n left: 0;\n top: 0;\n width: 1px;\n height: 1px;\n pointer-events: none;\n overflow: visible;\n}\n.ink-layer[_ngcontent-%COMP%] path[_ngcontent-%COMP%] {\n pointer-events: none;\n}\n.ink-layer[_ngcontent-%COMP%] path.wb-erasable[_ngcontent-%COMP%] {\n pointer-events: stroke;\n cursor: pointer;\n}\n.ink-sage[_ngcontent-%COMP%] { stroke: var(--sage) !important; }\n.wb-ink-selected[_ngcontent-%COMP%] { filter: drop-shadow(0 0 4px color-mix(in srgb, var(--mj-brand-primary) 60%, transparent)); }\n\n.conn-user[_ngcontent-%COMP%] path[_ngcontent-%COMP%] { stroke: var(--mj-color-neutral-300, #cbd5e1); }\n.conn-sage[_ngcontent-%COMP%] path[_ngcontent-%COMP%] {\n stroke: var(--sage);\n filter: drop-shadow(0 0 4px color-mix(in srgb, var(--sage) 35%, transparent));\n}\n.draw-in[_ngcontent-%COMP%] {\n stroke-dasharray: 420;\n stroke-dashoffset: 420;\n animation: _ngcontent-%COMP%_wb-conn-draw 1.4s ease-out forwards;\n}\n@keyframes _ngcontent-%COMP%_wb-conn-draw { to { stroke-dashoffset: 0; } }\n\n.wb-live-pen[_ngcontent-%COMP%] { opacity: 0.95; }\n.wb-pending-conn[_ngcontent-%COMP%] {\n stroke: var(--mj-text-disabled);\n stroke-dasharray: 4 4;\n}\n\n\n\n.bi[_ngcontent-%COMP%] {\n position: absolute;\n box-shadow: var(--mj-shadow-lg, 0 10px 15px -3px rgba(0, 0, 0, 0.3));\n \n\n user-select: none;\n -webkit-user-select: none;\n}\n.author-chip[_ngcontent-%COMP%] {\n position: absolute;\n top: -9px;\n left: 8px;\n z-index: 3;\n display: inline-flex;\n align-items: center;\n gap: 4px;\n padding: 1px 7px;\n font-size: 9px;\n font-weight: 800;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n border-radius: var(--mj-radius-full, 9999px);\n line-height: 1.5;\n white-space: nowrap;\n pointer-events: none;\n}\n.author-chip[_ngcontent-%COMP%] i[_ngcontent-%COMP%] { font-size: 7.5px; }\n.author-chip--you[_ngcontent-%COMP%] {\n background: var(--mj-color-neutral-700, #334155);\n border: 1px solid var(--mj-border-strong);\n color: var(--mj-color-neutral-300, #cbd5e1);\n}\n.author-chip--sage[_ngcontent-%COMP%] {\n background: var(--sage-chip-bg);\n border: 1px solid var(--sage);\n color: var(--mj-color-violet-300, #c4b5fd);\n}\n\n\n\n.sticky[_ngcontent-%COMP%] {\n width: 172px;\n padding: 14px 13px 12px;\n border-radius: 4px 4px 14px 4px;\n font-size: 12.5px;\n line-height: 1.45;\n font-weight: 500;\n cursor: default;\n white-space: pre-wrap;\n word-break: break-word;\n}\n.sticky--amber[_ngcontent-%COMP%] { background: linear-gradient(180deg, #fcd34d, #f5b831); color: #3f2d04; }\n.sticky--amber2[_ngcontent-%COMP%] { background: linear-gradient(180deg, #fde68a, #f8ca4d); color: #3f2d04; }\n.sticky--sage[_ngcontent-%COMP%] {\n background: linear-gradient(180deg, #38306b, #2c2455);\n color: var(--mj-color-violet-300, #c4b5fd);\n border: 1px solid var(--sage);\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--sage) 20%, transparent), var(--mj-shadow-lg, 0 10px 15px -3px rgba(0, 0, 0, 0.3));\n}\n\n\n\n.shape-box[_ngcontent-%COMP%] {\n min-width: 60px;\n min-height: 56px;\n display: grid;\n place-items: center;\n text-align: center;\n padding: 10px 12px;\n border: 2px solid var(--mj-color-neutral-400, #94a3b8);\n border-radius: 10px;\n background: color-mix(in srgb, var(--mj-color-neutral-800, #1e293b) 88%, transparent);\n font-size: 12.5px;\n font-weight: 600;\n color: var(--mj-text-primary);\n cursor: default;\n}\n.shape-box[_ngcontent-%COMP%] .sub[_ngcontent-%COMP%] {\n display: block;\n font-size: 10.5px;\n color: var(--mj-text-muted);\n font-weight: 500;\n margin-top: 2px;\n}\n.shape-box--ellipse[_ngcontent-%COMP%] { border-radius: 50%; }\n.shape-box--diamond[_ngcontent-%COMP%] {\n border-radius: 10px;\n transform: rotate(45deg);\n}\n.shape-box--diamond[_ngcontent-%COMP%] > span[_ngcontent-%COMP%] { transform: rotate(-45deg); }\n.shape-box--sage[_ngcontent-%COMP%] {\n border-color: var(--sage);\n background: color-mix(in srgb, var(--sage) 12%, var(--mj-bg-surface-sunken));\n color: var(--mj-color-violet-300, #c4b5fd);\n}\n\n\n\n.shape-preview[_ngcontent-%COMP%] {\n position: absolute;\n border: 2px dashed var(--mj-brand-primary);\n border-radius: 10px;\n background: color-mix(in srgb, var(--mj-brand-primary) 6%, transparent);\n pointer-events: none;\n}\n\n\n\n.wb-marquee[_ngcontent-%COMP%] {\n position: absolute;\n border: 1px dashed var(--mj-brand-primary);\n border-radius: 3px;\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, transparent);\n pointer-events: none;\n z-index: 40;\n}\n\n\n\n.tlabel[_ngcontent-%COMP%] {\n font-size: 12px;\n font-weight: 700;\n color: var(--mj-color-violet-300, #c4b5fd);\n background: transparent;\n box-shadow: none;\n letter-spacing: 0.01em;\n text-shadow: 0 1px 6px rgba(2, 6, 23, 0.8);\n \n\n\n white-space: pre-wrap;\n overflow-wrap: break-word;\n max-width: 380px;\n cursor: default;\n}\n.tlabel[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 9px;\n margin-right: 4px;\n opacity: 0.8;\n}\n.tlabel--user[_ngcontent-%COMP%] {\n color: var(--mj-text-primary);\n}\n\n\n\n\n\n\n.md-card[_ngcontent-%COMP%] {\n border-radius: 10px;\n border: 1.5px solid var(--mj-border-strong);\n background: var(--mj-bg-surface-card);\n cursor: default;\n}\n.md-card__body[_ngcontent-%COMP%] {\n padding: 16px 12px 10px;\n font-size: 12px;\n line-height: 1.45;\n color: var(--mj-text-primary);\n overflow: hidden;\n border-radius: 8.5px; \n\n}\n.md-card--sage[_ngcontent-%COMP%] {\n border-color: var(--sage);\n background: color-mix(in srgb, var(--sage) 8%, var(--mj-bg-surface-card));\n}\n\n\n\n.md-card[_ngcontent-%COMP%] .mj-markdown-container h1 { font-size: 16px; margin: 4px 0 6px; }\n.md-card[_ngcontent-%COMP%] .mj-markdown-container h2 { font-size: 14px; margin: 4px 0 6px; }\n.md-card[_ngcontent-%COMP%] .mj-markdown-container h3, \n.md-card[_ngcontent-%COMP%] .mj-markdown-container h4 { font-size: 12.5px; margin: 4px 0 4px; }\n.md-card[_ngcontent-%COMP%] .mj-markdown-container p { margin: 0 0 6px; }\n.md-card[_ngcontent-%COMP%] .mj-markdown-container ul, \n.md-card[_ngcontent-%COMP%] .mj-markdown-container ol { margin: 0 0 6px; padding-left: 18px; }\n.md-card[_ngcontent-%COMP%] .mj-markdown-container pre { font-size: 10.5px; padding: 6px 8px; margin: 0 0 6px; overflow: auto; }\n.md-card[_ngcontent-%COMP%] .mj-markdown-container code { font-size: 10.5px; }\n\n\n\n\n\n\n.html-card[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n border-radius: 10px;\n border: 1.5px solid var(--mj-border-strong);\n background: var(--mj-bg-surface-card);\n cursor: default;\n min-width: 160px;\n min-height: 90px;\n}\n.html-card--sage[_ngcontent-%COMP%] {\n border-color: var(--sage);\n}\n.html-card__head[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 7px;\n padding: 5px 8px;\n background: var(--mj-bg-surface-sunken);\n border-bottom: 1px solid var(--mj-border-default);\n border-radius: 8.5px 8.5px 0 0; \n\n cursor: grab;\n user-select: none;\n}\n.html-card--sage[_ngcontent-%COMP%] .html-card__head[_ngcontent-%COMP%] {\n background: color-mix(in srgb, var(--sage) 14%, var(--mj-bg-surface-sunken));\n}\n.html-card__glyph[_ngcontent-%COMP%] {\n font-size: 10px;\n color: var(--mj-text-muted);\n}\n.html-card--sage[_ngcontent-%COMP%] .html-card__glyph[_ngcontent-%COMP%] { color: var(--sage); }\n.html-card__title[_ngcontent-%COMP%] {\n flex: 1;\n min-width: 0;\n font-size: 11px;\n font-weight: 700;\n color: var(--mj-text-primary);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n.author-chip--inline[_ngcontent-%COMP%] {\n position: static;\n flex-shrink: 0;\n}\n.html-card__btn[_ngcontent-%COMP%] {\n flex-shrink: 0;\n display: grid;\n place-items: center;\n width: 20px;\n height: 20px;\n padding: 0;\n border: 0;\n border-radius: var(--mj-radius-sm, 4px);\n background: transparent;\n color: var(--mj-text-muted);\n font-size: 10px;\n cursor: pointer;\n}\n.html-card__btn[_ngcontent-%COMP%]:hover {\n background: var(--mj-bg-surface-hover);\n color: var(--mj-text-primary);\n}\n.html-card__body[_ngcontent-%COMP%] {\n position: relative;\n flex: 1;\n min-height: 0;\n background: var(--mj-bg-surface);\n overflow: hidden;\n border-radius: 0 0 8.5px 8.5px; \n\n}\n.html-card__frame[_ngcontent-%COMP%] {\n position: absolute;\n inset: 0;\n width: 100%;\n height: 100%;\n border: 0;\n display: block;\n background: #ffffff; \n\n}\n\n\n\n.board-canvas.is-interacting[_ngcontent-%COMP%] .html-card__frame[_ngcontent-%COMP%] {\n pointer-events: none;\n}\n.html-card__lazy[_ngcontent-%COMP%] {\n position: absolute;\n inset: 0;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 6px;\n font-size: 11px;\n color: var(--mj-text-muted);\n background: var(--mj-bg-surface-sunken);\n \n\n\n cursor: grab;\n}\n.html-card__lazy[_ngcontent-%COMP%] i[_ngcontent-%COMP%] { font-size: 16px; color: var(--mj-text-disabled); }\n\n\n\n.rich-editor[_ngcontent-%COMP%] {\n position: absolute;\n top: 14px;\n right: 14px;\n bottom: 14px;\n z-index: 12;\n width: min(480px, 62%);\n display: flex;\n flex-direction: column;\n background: var(--mj-bg-surface-elevated, var(--mj-bg-surface));\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-lg, 12px);\n box-shadow: var(--mj-shadow-xl, 0 20px 25px -5px rgba(0, 0, 0, 0.3));\n overflow: hidden;\n cursor: default;\n}\n.rich-editor__head[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 10px;\n border-bottom: 1px solid var(--mj-border-default);\n background: var(--mj-bg-surface-sunken);\n font-size: 11px;\n color: var(--mj-text-muted);\n}\n.rich-editor__label[_ngcontent-%COMP%] {\n font-weight: 700;\n color: var(--mj-text-primary);\n white-space: nowrap;\n}\n.rich-editor__modes[_ngcontent-%COMP%] {\n margin-left: auto;\n display: inline-flex;\n gap: 2px;\n padding: 2px;\n border-radius: var(--mj-radius-full, 9999px);\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n}\n.re-mode[_ngcontent-%COMP%] {\n border: 0;\n background: transparent;\n color: var(--mj-text-muted);\n font-size: 10.5px;\n font-weight: 700;\n padding: 2px 10px;\n border-radius: var(--mj-radius-full, 9999px);\n cursor: pointer;\n}\n.re-mode.on[_ngcontent-%COMP%] {\n background: var(--mj-brand-primary);\n color: var(--mj-text-inverse, #fff);\n}\n.rich-editor__title[_ngcontent-%COMP%] {\n flex: 1;\n min-width: 0;\n margin-left: 4px;\n padding: 3px 8px;\n font-size: 11.5px;\n color: var(--mj-text-primary);\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-sm, 4px);\n outline: none;\n}\n.rich-editor__title[_ngcontent-%COMP%]:focus { border-color: var(--mj-border-focus, var(--mj-brand-primary)); }\n.rich-editor__body[_ngcontent-%COMP%] {\n flex: 1;\n min-height: 0;\n display: flex;\n flex-direction: column;\n}\n.rich-editor__code[_ngcontent-%COMP%] {\n flex: 1;\n min-height: 0;\n display: block;\n overflow: auto;\n font-size: 12px;\n}\n.rich-editor__preview[_ngcontent-%COMP%] {\n flex: 1;\n min-height: 0;\n overflow: auto;\n padding: 12px 14px;\n font-size: 12.5px;\n color: var(--mj-text-primary);\n}\n.rich-editor__note[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 7px;\n padding: 6px 10px;\n font-size: 10px;\n color: var(--mj-text-muted);\n border-top: 1px solid var(--mj-border-subtle, var(--mj-border-default));\n}\n.rich-editor__foot[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 10px;\n border-top: 1px solid var(--mj-border-default);\n background: var(--mj-bg-surface-sunken);\n}\n.re-spacer[_ngcontent-%COMP%] { flex: 1; }\n.re-btn[_ngcontent-%COMP%] {\n padding: 4px 14px;\n font-size: 11.5px;\n font-weight: 700;\n border-radius: var(--mj-radius-md, 6px);\n border: 1px solid var(--mj-border-default);\n background: var(--mj-bg-surface);\n color: var(--mj-text-primary);\n cursor: pointer;\n}\n.re-btn[_ngcontent-%COMP%]:hover { background: var(--mj-bg-surface-hover); }\n.re-btn--primary[_ngcontent-%COMP%] {\n background: var(--mj-brand-primary);\n border-color: var(--mj-brand-primary);\n color: var(--mj-text-inverse, #fff);\n}\n.re-btn--primary[_ngcontent-%COMP%]:hover { background: var(--mj-brand-primary-hover, var(--mj-brand-primary)); }\n\n\n\n\n\n.img-card[_ngcontent-%COMP%] {\n width: 198px;\n border-radius: 10px;\n border: 1.5px solid var(--mj-border-strong);\n background: var(--mj-bg-surface-card);\n cursor: default;\n}\n.img-card__body[_ngcontent-%COMP%] {\n height: 104px;\n display: grid;\n place-items: center;\n overflow: hidden;\n border-radius: 8.5px 8.5px 0 0; \n\n background:\n linear-gradient(160deg, color-mix(in srgb, var(--mj-brand-primary) 30%, transparent) 0%, color-mix(in srgb, var(--mj-brand-primary) 10%, transparent) 38%, transparent 39%),\n linear-gradient(200deg, color-mix(in srgb, var(--mj-brand-accent, var(--mj-brand-primary)) 22%, transparent) 0%, color-mix(in srgb, var(--mj-brand-accent, var(--mj-brand-primary)) 8%, transparent) 60%, transparent 61%),\n var(--mj-color-neutral-900, #0f172a);\n color: var(--mj-text-disabled);\n font-size: 22px;\n}\n.img-card__body[_ngcontent-%COMP%] img[_ngcontent-%COMP%] {\n width: 100%;\n height: 100%;\n object-fit: cover;\n display: block;\n \n\n\n pointer-events: none;\n -webkit-user-drag: none;\n}\n.img-card__name[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 7px;\n padding: 6px 10px;\n font-size: 10.5px;\n color: var(--mj-text-muted);\n font-family: var(--mj-font-mono, ui-monospace, monospace);\n border-top: 1px solid var(--mj-border-default);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n.img-card__name[_ngcontent-%COMP%] i[_ngcontent-%COMP%] { color: var(--mj-brand-accent, var(--mj-brand-primary)); }\n\n\n\n.pop-in[_ngcontent-%COMP%] {\n animation: _ngcontent-%COMP%_wb-bi-pop 600ms cubic-bezier(0.34, 1.4, 0.64, 1) both, _ngcontent-%COMP%_wb-sage-flash 2.4s ease-out;\n}\n@keyframes _ngcontent-%COMP%_wb-bi-pop {\n 0% { scale: 0.55; opacity: 0; }\n 70% { scale: 1.06; }\n 100% { scale: 1; opacity: 1; }\n}\n@keyframes _ngcontent-%COMP%_wb-sage-flash {\n 0% {\n box-shadow: 0 0 0 12px color-mix(in srgb, var(--sage) 45%, transparent),\n 0 0 36px color-mix(in srgb, var(--sage) 50%, transparent),\n var(--mj-shadow-lg, 0 10px 15px -3px rgba(0, 0, 0, 0.3));\n }\n 100% {\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--sage) 20%, transparent),\n var(--mj-shadow-lg, 0 10px 15px -3px rgba(0, 0, 0, 0.3));\n }\n}\n@media (prefers-reduced-motion: reduce) {\n .pop-in[_ngcontent-%COMP%] { animation: none; }\n .draw-in[_ngcontent-%COMP%] { animation: none; stroke-dashoffset: 0; }\n}\n\n\n\n.bi.selected[_ngcontent-%COMP%] {\n outline: 1.5px solid var(--mj-brand-primary);\n outline-offset: 3px;\n}\n.bi.dragging[_ngcontent-%COMP%] {\n transform: rotate(-1.6deg) scale(1.02);\n box-shadow: var(--mj-shadow-2xl, 0 25px 50px -12px rgba(0, 0, 0, 0.45));\n cursor: grabbing;\n z-index: 5;\n border-color: var(--mj-brand-primary);\n}\n.sel-h[_ngcontent-%COMP%] {\n position: absolute;\n width: 8px;\n height: 8px;\n border-radius: 2px;\n background: var(--mj-bg-surface);\n border: 1.5px solid var(--mj-brand-primary);\n z-index: 4;\n}\n.sel-h.nw[_ngcontent-%COMP%] { top: -8px; left: -8px; cursor: nwse-resize; }\n.sel-h.n[_ngcontent-%COMP%] { top: -8px; left: calc(50% - 4px); cursor: ns-resize; }\n.sel-h.ne[_ngcontent-%COMP%] { top: -8px; right: -8px; cursor: nesw-resize; }\n.sel-h.e[_ngcontent-%COMP%] { top: calc(50% - 4px); right: -8px; cursor: ew-resize; }\n.sel-h.se[_ngcontent-%COMP%] { bottom: -8px; right: -8px; cursor: nwse-resize; }\n.sel-h.s[_ngcontent-%COMP%] { bottom: -8px; left: calc(50% - 4px); cursor: ns-resize; }\n.sel-h.sw[_ngcontent-%COMP%] { bottom: -8px; left: -8px; cursor: nesw-resize; }\n.sel-h.w[_ngcontent-%COMP%] { top: calc(50% - 4px); left: -8px; cursor: ew-resize; }\n\n.drag-ghost[_ngcontent-%COMP%] {\n position: absolute;\n border: 1.5px dashed var(--mj-border-strong);\n border-radius: 10px;\n opacity: 0.55;\n pointer-events: none;\n}\n.drag-ghost[_ngcontent-%COMP%]::after {\n content: \"from here\";\n position: absolute;\n bottom: -18px;\n left: 4px;\n font-size: 9px;\n color: var(--mj-text-disabled);\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n}\n\n\n\n.sage-highlight[_ngcontent-%COMP%] {\n position: absolute;\n border: 2px dashed var(--sage);\n border-radius: 16px;\n background: color-mix(in srgb, var(--sage) 6%, transparent);\n animation: _ngcontent-%COMP%_wb-hl-pulse 2.2s ease-in-out infinite;\n cursor: pointer;\n}\n@keyframes _ngcontent-%COMP%_wb-hl-pulse {\n 0%, 100% { box-shadow: 0 0 0 0 transparent; }\n 50% { box-shadow: 0 0 22px 2px color-mix(in srgb, var(--sage) 25%, transparent); }\n}\n.sage-highlight[_ngcontent-%COMP%] .hl-tag[_ngcontent-%COMP%] {\n position: absolute;\n top: -11px;\n right: 12px;\n display: inline-flex;\n align-items: center;\n gap: 5px;\n padding: 1px 8px;\n font-size: 9.5px;\n font-weight: 800;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n background: var(--sage-chip-bg);\n border: 1px solid var(--sage);\n color: var(--mj-color-violet-300, #c4b5fd);\n border-radius: var(--mj-radius-full, 9999px);\n white-space: nowrap;\n}\n.sage-highlight.user-highlight[_ngcontent-%COMP%] {\n border-color: var(--mj-border-strong);\n background: color-mix(in srgb, var(--mj-text-secondary) 5%, transparent);\n}\n.sage-highlight.user-highlight[_ngcontent-%COMP%] .hl-tag[_ngcontent-%COMP%] {\n background: var(--mj-color-neutral-700, #334155);\n border-color: var(--mj-border-strong);\n color: var(--mj-color-neutral-300, #cbd5e1);\n}\n\n\n\n.cursor[_ngcontent-%COMP%] {\n position: absolute;\n z-index: 8;\n pointer-events: none;\n}\n.cursor--sage[_ngcontent-%COMP%] {\n display: flex;\n align-items: flex-start;\n gap: 6px;\n transition: left 700ms cubic-bezier(0.33, 1, 0.68, 1), top 700ms cubic-bezier(0.33, 1, 0.68, 1);\n animation: _ngcontent-%COMP%_wb-sage-cursor-drift 5s ease-in-out infinite alternate;\n}\n.cursor--sage[_ngcontent-%COMP%] .tip[_ngcontent-%COMP%] {\n position: relative;\n width: 26px;\n height: 26px;\n border-radius: 50% 50% 50% 4px;\n display: grid;\n place-items: center;\n background: var(--sage);\n color: #1b1438;\n font-size: 11px;\n box-shadow: 0 0 14px color-mix(in srgb, var(--sage) 60%, transparent);\n}\n.cursor--sage[_ngcontent-%COMP%] .tip[_ngcontent-%COMP%]::after {\n content: \"\";\n position: absolute;\n inset: -5px;\n border-radius: inherit;\n border: 2px solid color-mix(in srgb, var(--sage) 45%, transparent);\n animation: _ngcontent-%COMP%_wb-orb-ring 1.8s ease-out infinite;\n}\n.cursor--sage[_ngcontent-%COMP%] .lbl[_ngcontent-%COMP%] {\n margin-top: 14px;\n padding: 3px 10px;\n border-radius: var(--mj-radius-full, 9999px);\n white-space: nowrap;\n background: var(--sage-chip-bg);\n border: 1px solid var(--sage);\n color: var(--mj-color-violet-300, #c4b5fd);\n font-size: 10.5px;\n font-weight: 700;\n}\n.cursor--sage[_ngcontent-%COMP%] .lbl[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n animation: _ngcontent-%COMP%_wb-pen-wiggle 0.9s ease-in-out infinite;\n display: inline-block;\n margin-right: 3px;\n font-size: 9px;\n}\n@keyframes _ngcontent-%COMP%_wb-pen-wiggle {\n 0%, 100% { transform: rotate(0); }\n 50% { transform: rotate(-14deg); }\n}\n@keyframes _ngcontent-%COMP%_wb-sage-cursor-drift {\n from { transform: translate(0, 0); }\n to { transform: translate(-26px, 20px); }\n}\n@keyframes _ngcontent-%COMP%_wb-orb-ring {\n 0% { transform: scale(1); opacity: 0.6; }\n 100% { transform: scale(1.5); opacity: 0; }\n}\n@media (prefers-reduced-motion: reduce) {\n .cursor--sage[_ngcontent-%COMP%] { animation: none; }\n .sage-highlight[_ngcontent-%COMP%] { animation: none; }\n}\n\n\n\n.wb-edit[_ngcontent-%COMP%] {\n display: block;\n width: 100%;\n background: transparent;\n border: 0;\n outline: none;\n resize: none;\n color: inherit;\n font: inherit;\n line-height: inherit;\n padding: 0;\n \n\n user-select: text;\n -webkit-user-select: text;\n}\n.wb-edit--shape[_ngcontent-%COMP%] { text-align: center; }\n.wb-edit--text[_ngcontent-%COMP%] { min-width: 110px; }\n\n\n\n.minimap[_ngcontent-%COMP%] {\n position: absolute;\n right: 14px;\n bottom: 58px;\n z-index: 10;\n width: 176px;\n height: 118px;\n padding: 6px;\n background: var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-lg, 12px);\n box-shadow: var(--mj-shadow-xl, 0 20px 25px -5px rgba(0, 0, 0, 0.3));\n}\n.minimap[_ngcontent-%COMP%] svg[_ngcontent-%COMP%] {\n width: 100%;\n height: 100%;\n display: block;\n}\n.mm-item[_ngcontent-%COMP%] {\n fill: color-mix(in srgb, var(--mj-text-secondary) 45%, transparent);\n}\n.mm-item--sage[_ngcontent-%COMP%] {\n fill: color-mix(in srgb, var(--sage) 60%, transparent);\n}\n.mm-viewport[_ngcontent-%COMP%] {\n fill: none;\n stroke: var(--mj-brand-primary);\n stroke-width: 6;\n vector-effect: non-scaling-stroke;\n}\n\n\n\n.wb-context-menu[_ngcontent-%COMP%] {\n position: absolute;\n z-index: 40;\n min-width: 178px;\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 cursor: default;\n}\n.wb-context-menu__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.wb-context-menu__item[_ngcontent-%COMP%]:hover {\n background: var(--mj-bg-surface-hover);\n color: var(--mj-text-primary);\n}\n.wb-context-menu__item[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n width: 14px;\n text-align: center;\n color: var(--mj-text-muted);\n}\n.wb-context-menu__item[_ngcontent-%COMP%]:hover i[_ngcontent-%COMP%] {\n color: var(--mj-brand-accent, var(--mj-brand-primary));\n}\n.wb-context-menu__item--danger[_ngcontent-%COMP%] {\n color: var(--mj-status-error);\n}\n.wb-context-menu__item--danger[_ngcontent-%COMP%]:hover {\n background: color-mix(in srgb, var(--mj-status-error) 10%, var(--mj-bg-surface-elevated));\n color: var(--mj-status-error-text, var(--mj-status-error));\n}\n.wb-context-menu__item--danger[_ngcontent-%COMP%] i[_ngcontent-%COMP%], \n.wb-context-menu__item--danger[_ngcontent-%COMP%]:hover i[_ngcontent-%COMP%] {\n color: var(--mj-status-error);\n}\n.wb-context-menu__sep[_ngcontent-%COMP%] {\n height: 1px;\n margin: 4px 6px;\n background: var(--mj-border-subtle, var(--mj-border-default));\n}"] }); }
2299
+ }
2300
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(RealtimeWhiteboardBoardComponent, [{
2301
+ type: Component,
2302
+ args: [{ standalone: true, selector: 'mj-realtime-whiteboard', imports: [CommonModule, MarkdownModule, CodeEditorModule, RealtimeWhiteboardPagesComponent, WhiteboardWidgetSrcdocPipe], template: "<div #canvas\n [class]=\"'board-canvas ' + CursorClass\"\n [class.is-panning]=\"Interaction?.Type === 'pan'\"\n [class.is-interacting]=\"Interaction !== null || PendingConnector !== null\"\n [style.backgroundSize]=\"GridBackgroundSize\"\n [style.backgroundPosition]=\"GridBackgroundPosition\"\n tabindex=\"0\"\n (pointerdown)=\"OnCanvasPointerDown($event)\"\n (contextmenu)=\"OnCanvasContextMenu($event)\"\n (wheel)=\"OnWheel($event)\"\n (paste)=\"OnPaste($event)\">\n\n <div class=\"board-viewport\" [style.transform]=\"ViewportTransform\">\n\n <!-- ink + connectors -->\n <svg class=\"ink-layer\">\n @for (ink of InkItems; track ink.ID) {\n <path [attr.d]=\"InkPath(ink.Points)\"\n [class.ink-sage]=\"ink.Author === 'agent'\"\n [class.wb-erasable]=\"Tool === 'eraser' || Tool === 'select'\"\n [class.wb-ink-selected]=\"IsSelected(ink)\"\n [attr.stroke]=\"ink.Author === 'agent' ? null : ink.Color\"\n [attr.stroke-width]=\"ink.StrokeWidth\"\n fill=\"none\" stroke-linecap=\"round\" stroke-linejoin=\"round\"\n (pointerdown)=\"OnItemPointerDown($event, ink)\"\n (contextmenu)=\"OnItemContextMenu($event, ink)\"/>\n }\n @for (conn of ConnectorItems; track conn.ID) {\n <g [class.conn-sage]=\"conn.Author === 'agent'\" [class.conn-user]=\"conn.Author === 'user'\">\n <path [attr.d]=\"ConnectorPath(conn)\"\n [class.draw-in]=\"PopInIDs.has(conn.ID)\"\n [class.wb-erasable]=\"Tool === 'eraser' || Tool === 'select'\"\n stroke-width=\"2.5\" fill=\"none\" stroke-linecap=\"round\"\n (pointerdown)=\"OnItemPointerDown($event, conn)\"\n (contextmenu)=\"OnItemContextMenu($event, conn)\"/>\n <path [attr.d]=\"ConnectorArrow(conn)\" stroke-width=\"2.5\" fill=\"none\" stroke-linecap=\"round\"/>\n </g>\n }\n <!-- live transients -->\n @if (LivePenPath) {\n <path class=\"wb-live-pen\" [attr.d]=\"LivePenPath\" [attr.stroke]=\"PenColor\" [attr.stroke-width]=\"PenWidth\"\n fill=\"none\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n }\n @if (PendingConnector) {\n <path class=\"wb-pending-conn\" [attr.d]=\"PendingConnectorPath\" stroke-width=\"2\" fill=\"none\" stroke-linecap=\"round\"/>\n }\n </svg>\n\n <!-- highlight regions (agent \"pointing without touching\"; click dismisses) -->\n @for (hl of HighlightItems; track hl.ID) {\n <div class=\"sage-highlight\"\n [class.user-highlight]=\"hl.Author === 'user'\"\n [style.left.px]=\"ItemX(hl)\" [style.top.px]=\"ItemY(hl)\"\n [style.width.px]=\"ItemW(hl)\" [style.height.px]=\"ItemH(hl)\"\n title=\"Click to dismiss\"\n (pointerdown)=\"OnHighlightClick($event, hl)\"\n (contextmenu)=\"OnItemContextMenu($event, hl)\">\n <span class=\"hl-tag\"><i class=\"fa-solid fa-highlighter\"></i> {{ HighlightTag(hl) }}</span>\n </div>\n }\n\n <!-- drag ghost (\"from here\") -->\n @if (DragGhost; as ghost) {\n <div class=\"drag-ghost\"\n [style.left.px]=\"ghost.X\" [style.top.px]=\"ghost.Y\"\n [style.width.px]=\"ghost.W\" [style.height.px]=\"ghost.H\"></div>\n }\n\n <!-- shape drag-create preview -->\n @if (ShapePreview; as preview) {\n <div class=\"shape-preview\"\n [style.left.px]=\"preview.X\" [style.top.px]=\"preview.Y\"\n [style.width.px]=\"preview.W\" [style.height.px]=\"preview.H\"></div>\n }\n\n <!-- marquee (rubber-band) multi-selection rectangle -->\n @if (MarqueeRect; as mq) {\n <div class=\"wb-marquee\"\n [style.left.px]=\"mq.X\" [style.top.px]=\"mq.Y\"\n [style.width.px]=\"mq.W\" [style.height.px]=\"mq.H\"></div>\n }\n\n <!-- board items -->\n @for (item of BoxItems; track item.ID) {\n @switch (item.Kind) {\n\n @case ('sticky') {\n <div [class]=\"'bi sticky ' + StickyTintClass(item)\"\n [class.selected]=\"IsSelected(item)\"\n [class.dragging]=\"IsDragging(item)\"\n [class.pop-in]=\"PopInIDs.has(item.ID)\"\n [style.left.px]=\"ItemX(item)\" [style.top.px]=\"ItemY(item)\"\n [style.width.px]=\"ItemW(item)\"\n [style.fontSize]=\"ItemFontSize(item)\"\n [style.fontFamily]=\"ItemFontFamily(item)\"\n [style.fontWeight]=\"ItemFontWeight(item)\"\n [style.transform]=\"IsDragging(item) ? '' : StickyRotation(item)\"\n (pointerdown)=\"OnItemPointerDown($event, item)\"\n (dblclick)=\"BeginItemEdit($event, item)\"\n (contextmenu)=\"OnItemContextMenu($event, item)\">\n @if (item.Author === 'agent') {\n <span class=\"author-chip author-chip--sage\"><i class=\"fa-solid fa-feather\"></i> {{ AgentName }}</span>\n } @else {\n <span class=\"author-chip author-chip--you\"><i class=\"fa-solid fa-user\"></i> You</span>\n }\n @if (EditingID === item.ID) {\n <textarea class=\"wb-edit\" rows=\"3\"\n [value]=\"item.Text\"\n (keydown)=\"OnEditKeydown($event, edit.value)\"\n (blur)=\"CommitEdit(edit.value)\"\n #edit></textarea>\n } @else {\n {{ item.Text }}\n }\n @if (ShowHandles(item) && !IsDragging(item)) {\n @for (h of Handles; track h) {\n <span [class]=\"'sel-h ' + h\" (pointerdown)=\"OnHandlePointerDown($event, item, h)\"></span>\n }\n }\n </div>\n }\n\n @case ('shape') {\n <div class=\"bi shape-box\"\n [class.shape-box--ellipse]=\"item.Shape === 'ellipse'\"\n [class.shape-box--diamond]=\"item.Shape === 'diamond'\"\n [class.shape-box--sage]=\"item.Author === 'agent'\"\n [class.selected]=\"IsSelected(item)\"\n [class.dragging]=\"IsDragging(item)\"\n [class.pop-in]=\"PopInIDs.has(item.ID)\"\n [style.left.px]=\"ItemX(item)\" [style.top.px]=\"ItemY(item)\"\n [style.width.px]=\"ItemW(item)\" [style.height.px]=\"ItemH(item)\"\n (pointerdown)=\"OnItemPointerDown($event, item)\"\n (dblclick)=\"BeginItemEdit($event, item)\"\n (contextmenu)=\"OnItemContextMenu($event, item)\">\n @if (item.Author === 'agent') {\n <span class=\"author-chip author-chip--sage\"><i class=\"fa-solid fa-feather\"></i> {{ AgentName }}</span>\n } @else {\n <span class=\"author-chip author-chip--you\"><i class=\"fa-solid fa-user\"></i> You</span>\n }\n @if (EditingID === item.ID) {\n <textarea class=\"wb-edit wb-edit--shape\" rows=\"1\"\n [value]=\"item.Label\"\n (keydown)=\"OnEditKeydown($event, editShape.value)\"\n (blur)=\"CommitEdit(editShape.value)\"\n #editShape></textarea>\n } @else {\n <span>{{ item.Label }}@if (item.Sub) {<span class=\"sub\">{{ item.Sub }}</span>}</span>\n }\n @if (ShowHandles(item) && !IsDragging(item)) {\n @for (h of Handles; track h) {\n <span [class]=\"'sel-h ' + h\" (pointerdown)=\"OnHandlePointerDown($event, item, h)\"></span>\n }\n }\n </div>\n }\n\n @case ('text') {\n <div class=\"bi tlabel\"\n [class.tlabel--user]=\"item.Author === 'user'\"\n [class.selected]=\"IsSelected(item)\"\n [class.dragging]=\"IsDragging(item)\"\n [class.pop-in]=\"PopInIDs.has(item.ID)\"\n [style.left.px]=\"ItemX(item)\" [style.top.px]=\"ItemY(item)\"\n [style.width.px]=\"TextWrapWidth(item)\"\n [style.maxWidth.px]=\"TextWrapWidth(item)\"\n [style.fontSize]=\"ItemFontSize(item)\"\n [style.fontFamily]=\"ItemFontFamily(item)\"\n [style.fontWeight]=\"ItemFontWeight(item)\"\n [style.color]=\"ItemTextColor(item)\"\n (pointerdown)=\"OnItemPointerDown($event, item)\"\n (dblclick)=\"BeginItemEdit($event, item)\"\n (contextmenu)=\"OnItemContextMenu($event, item)\">\n @if (item.Author === 'agent') {\n <i class=\"fa-solid fa-feather\"></i>\n }\n @if (EditingID === item.ID) {\n <input class=\"wb-edit wb-edit--text\" type=\"text\"\n [value]=\"item.Text\"\n (keydown)=\"OnEditKeydown($event, editText.value)\"\n (blur)=\"CommitEdit(editText.value)\"\n #editText/>\n } @else {\n {{ item.Text }}\n }\n @if (ShowHandles(item) && !IsDragging(item)) {\n @for (h of Handles; track h) {\n <span [class]=\"'sel-h ' + h\" (pointerdown)=\"OnHandlePointerDown($event, item, h)\"></span>\n }\n }\n </div>\n }\n\n @case ('markdown') {\n <div class=\"bi md-card\"\n [class.md-card--sage]=\"item.Author === 'agent'\"\n [class.selected]=\"IsSelected(item)\"\n [class.dragging]=\"IsDragging(item)\"\n [class.pop-in]=\"PopInIDs.has(item.ID)\"\n [style.left.px]=\"ItemX(item)\" [style.top.px]=\"ItemY(item)\"\n [style.width.px]=\"ItemW(item)\"\n (pointerdown)=\"OnItemPointerDown($event, item)\"\n (dblclick)=\"BeginRichEdit($event, item)\"\n (contextmenu)=\"OnItemContextMenu($event, item)\">\n @if (item.Author === 'agent') {\n <span class=\"author-chip author-chip--sage\"><i class=\"fa-solid fa-feather\"></i> {{ AgentName }}</span>\n } @else {\n <span class=\"author-chip author-chip--you\"><i class=\"fa-brands fa-markdown\"></i> You</span>\n }\n <!-- the BODY clips (overflow + max height) so the out-jutting selection\n handles and author chip on the card root are never clipped away -->\n <div class=\"md-card__body\"\n [style.maxHeight.px]=\"MarkdownMaxHeight(item)\">\n <!-- rendered through the shared mj-markdown component (sanitize defaults ON \u2014 no raw HTML passthrough) -->\n <mj-markdown [data]=\"item.Markdown\"\n [enableMermaid]=\"false\"\n [enableCodeCopy]=\"false\"\n [enableCollapsibleHeadings]=\"false\"/>\n </div>\n @if (ShowHandles(item) && !IsDragging(item)) {\n @for (h of Handles; track h) {\n <span [class]=\"'sel-h ' + h\" (pointerdown)=\"OnHandlePointerDown($event, item, h)\"></span>\n }\n }\n </div>\n }\n\n @case ('html') {\n <div class=\"bi html-card\"\n [class.html-card--sage]=\"item.Author === 'agent'\"\n [class.selected]=\"IsSelected(item)\"\n [class.dragging]=\"IsDragging(item)\"\n [class.pop-in]=\"PopInIDs.has(item.ID)\"\n [style.left.px]=\"ItemX(item)\" [style.top.px]=\"ItemY(item)\"\n [style.width.px]=\"ItemW(item)\" [style.height.px]=\"ItemH(item)\"\n (pointerdown)=\"OnItemPointerDown($event, item)\"\n (contextmenu)=\"OnItemContextMenu($event, item)\">\n <!-- Drag/select wiring lives on the CARD ROOT: pointer events from the header,\n the border ring and the lazy placeholder all bubble here, while events\n inside the sandboxed iframe never reach the parent document \u2014 so the\n widget body stays fully interactive and everything else drags the card. -->\n <div class=\"html-card__head\"\n (dblclick)=\"BeginRichEdit($event, item)\">\n <i class=\"fa-solid fa-code html-card__glyph\"></i>\n <span class=\"html-card__title\">{{ HtmlTitle(item) }}</span>\n @if (item.Author === 'agent') {\n <span class=\"author-chip author-chip--sage author-chip--inline\"><i class=\"fa-solid fa-feather\"></i> {{ AgentName }}</span>\n } @else {\n <span class=\"author-chip author-chip--you author-chip--inline\"><i class=\"fa-solid fa-user\"></i> You</span>\n }\n @if (!ReadOnly) {\n <button type=\"button\" class=\"html-card__btn\" title=\"Edit HTML\"\n (pointerdown)=\"$event.stopPropagation()\"\n (click)=\"BeginRichEdit($event, item)\"><i class=\"fa-solid fa-pen\"></i></button>\n <button type=\"button\" class=\"html-card__btn\" title=\"Remove widget\"\n (pointerdown)=\"$event.stopPropagation()\"\n (click)=\"RemoveItemClick($event, item)\"><i class=\"fa-solid fa-trash-can\"></i></button>\n }\n </div>\n <div class=\"html-card__body\">\n @if (IsNearViewport(item)) {\n <!-- SANDBOXED: allow-scripts ONLY (no allow-same-origin) \u2014 opaque origin,\n no access to the app DOM, session, cookies or storage. The srcdoc is\n built by the view-scoped wbWidgetSrcdoc pipe: rebuilt per MOUNT (page\n switches / lazy toggle / board re-creation always render fresh) and\n identity-stable while mounted (no reload churn). See whiteboard-srcdoc.pipe.ts. -->\n <iframe class=\"html-card__frame\"\n sandbox=\"allow-scripts\"\n [srcdoc]=\"item.Html | wbWidgetSrcdoc\"\n [attr.data-item-id]=\"item.ID\"\n [attr.title]=\"HtmlTitle(item)\"></iframe>\n } @else {\n <div class=\"html-card__lazy\">\n <i class=\"fa-solid fa-code\"></i>\n <span>{{ HtmlTitle(item) }}</span>\n </div>\n }\n </div>\n @if (ShowHandles(item) && !IsDragging(item)) {\n @for (h of Handles; track h) {\n <span [class]=\"'sel-h ' + h\" (pointerdown)=\"OnHandlePointerDown($event, item, h)\"></span>\n }\n }\n </div>\n }\n\n @case ('image') {\n <div class=\"bi img-card\"\n [class.selected]=\"IsSelected(item)\"\n [class.dragging]=\"IsDragging(item)\"\n [class.pop-in]=\"PopInIDs.has(item.ID)\"\n [style.left.px]=\"ItemX(item)\" [style.top.px]=\"ItemY(item)\"\n [style.width.px]=\"ItemW(item)\"\n (pointerdown)=\"OnItemPointerDown($event, item)\"\n (contextmenu)=\"OnItemContextMenu($event, item)\">\n @if (item.Author === 'agent') {\n <span class=\"author-chip author-chip--sage\"><i class=\"fa-solid fa-feather\"></i> {{ AgentName }}</span>\n } @else {\n <span class=\"author-chip author-chip--you\"><i class=\"fa-solid fa-paste\"></i> You \u00B7 pasted</span>\n }\n <div class=\"img-card__body\">\n @if (item.Url) {\n <img [src]=\"item.Url\" [alt]=\"item.Name\"/>\n } @else {\n <i class=\"fa-regular fa-image\"></i>\n }\n </div>\n <div class=\"img-card__name\"><i class=\"fa-solid fa-chart-area\"></i> {{ item.Name }}</div>\n @if (ShowHandles(item) && !IsDragging(item)) {\n @for (h of Handles; track h) {\n <span [class]=\"'sel-h ' + h\" (pointerdown)=\"OnHandlePointerDown($event, item, h)\"></span>\n }\n }\n </div>\n }\n }\n }\n\n <!-- agent presence cursor -->\n @if (AgentPresence?.Active) {\n <div class=\"cursor cursor--sage\" [style.left.px]=\"AgentPresence!.X\" [style.top.px]=\"AgentPresence!.Y\">\n <span class=\"tip\"><i class=\"fa-solid fa-feather\"></i></span>\n <span class=\"lbl\"><i class=\"fa-solid fa-pen\"></i>{{ AgentPresence!.Label }}</span>\n </div>\n }\n </div>\n\n <!-- in-board rich-content editor panel (markdown / html items) -->\n @if (RichEditor; as ed) {\n <div class=\"rich-editor\"\n (keydown)=\"OnRichEditorKeydown($event)\"\n (pointerdown)=\"$event.stopPropagation()\"\n (dblclick)=\"$event.stopPropagation()\"\n (wheel)=\"$event.stopPropagation()\">\n <div class=\"rich-editor__head\">\n @if (ed.Kind === 'markdown') {\n <i class=\"fa-brands fa-markdown\"></i>\n <span class=\"rich-editor__label\">Markdown panel</span>\n <span class=\"rich-editor__modes\">\n <button type=\"button\" class=\"re-mode\" [class.on]=\"!ed.Preview\" (click)=\"RichEditorSetPreview(false)\">Edit</button>\n <button type=\"button\" class=\"re-mode\" [class.on]=\"ed.Preview\" (click)=\"RichEditorSetPreview(true)\">Preview</button>\n </span>\n } @else {\n <i class=\"fa-solid fa-code\"></i>\n <span class=\"rich-editor__label\">HTML widget</span>\n <input class=\"rich-editor__title\" type=\"text\" placeholder=\"Title\" #reTitle\n [value]=\"ed.TitleDraft\" (input)=\"OnRichEditorTitleChange(reTitle.value)\"/>\n }\n </div>\n <div class=\"rich-editor__body\">\n @if (ed.Kind === 'markdown' && ed.Preview) {\n <div class=\"rich-editor__preview\">\n <mj-markdown [data]=\"ed.Draft\"\n [enableMermaid]=\"false\"\n [enableCodeCopy]=\"false\"\n [enableCollapsibleHeadings]=\"false\"/>\n </div>\n } @else {\n <mj-code-editor class=\"rich-editor__code\"\n [value]=\"ed.Draft\"\n [language]=\"ed.Kind === 'markdown' ? 'markdown' : 'html'\"\n (change)=\"OnRichEditorDraftChange($event)\"/>\n }\n </div>\n @if (ed.Kind === 'html') {\n <div class=\"rich-editor__note\">\n <i class=\"fa-solid fa-shield-halved\"></i>\n Renders in a sandboxed frame \u2014 scripts run isolated, no app or network access guaranteed.\n </div>\n }\n <div class=\"rich-editor__foot\">\n <button type=\"button\" class=\"re-btn re-btn--primary\" (click)=\"RichEditorDone()\">Done</button>\n <button type=\"button\" class=\"re-btn\" title=\"Apply without closing \u2014 re-renders the widget\" (click)=\"RichEditorApply()\">Apply</button>\n <span class=\"re-spacer\"></span>\n <button type=\"button\" class=\"re-btn\" title=\"Close without applying draft changes\" (click)=\"RichEditorCancel()\">Cancel</button>\n </div>\n </div>\n }\n\n <!-- right-click context menu (item actions / \"add \u2026 here\") -->\n @if (ContextMenu; as menu) {\n <div class=\"wb-context-menu\"\n [style.left.px]=\"menu.Left\" [style.top.px]=\"menu.Top\"\n (pointerdown)=\"$event.stopPropagation()\"\n (contextmenu)=\"$event.preventDefault(); $event.stopPropagation()\"\n (wheel)=\"$event.stopPropagation()\">\n @for (action of menu.Actions; track action.ID) {\n @if (action.SeparatorBefore) {\n <div class=\"wb-context-menu__sep\"></div>\n }\n <button type=\"button\" class=\"wb-context-menu__item\"\n [class.wb-context-menu__item--danger]=\"action.Danger\"\n (click)=\"OnContextMenuAction(action.ID)\">\n <i [class]=\"action.Icon\"></i> {{ action.Label }}\n </button>\n }\n </div>\n }\n\n <!-- page strip (OneNote-style tabs; switching works in ReadOnly, mutations don't) -->\n <mj-realtime-whiteboard-pages [State]=\"State\" [ReadOnly]=\"ReadOnly\"\n (ChipContextMenu)=\"OnPageChipContextMenu($event)\"/>\n\n <!-- minimap -->\n @if (MinimapOpen) {\n <div class=\"minimap\">\n <svg [attr.viewBox]=\"MinimapViewBox\" preserveAspectRatio=\"xMidYMid meet\">\n @for (mi of MinimapItems; track $index) {\n <rect [attr.x]=\"mi.X\" [attr.y]=\"mi.Y\" [attr.width]=\"mi.W\" [attr.height]=\"mi.H\"\n [attr.class]=\"mi.Agent ? 'mm-item mm-item--sage' : 'mm-item'\" rx=\"6\"/>\n }\n <rect class=\"mm-viewport\"\n [attr.x]=\"MinimapViewport.X\" [attr.y]=\"MinimapViewport.Y\"\n [attr.width]=\"MinimapViewport.W\" [attr.height]=\"MinimapViewport.H\" rx=\"8\"/>\n </svg>\n </div>\n }\n</div>\n", styles: ["/* LIVE WHITEBOARD canvas \u2014 ported pixel-faithfully from\n plans/realtime/mockups/whiteboard.html (.board-canvas, .bi, .sticky, .shape-box,\n .tlabel, .img-card, .sage-highlight, .cursor--sage, selection chrome, animations).\n Sticky/ink hues are categorical content colors (like chart palettes) and stay literal;\n all chrome colors are design tokens. The violet agent accent is derived exactly the way\n the sibling realtime components derive it. */\n\n:host {\n /* agent (violet) accent \u2014 reserved for the agent's marks, never the user's */\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: block;\n position: absolute;\n inset: 0;\n}\n\n/* ---------- the canvas + dotted grid (scales/offsets with the viewport) ---------- */\n.board-canvas {\n position: absolute;\n inset: 0;\n overflow: hidden;\n outline: none;\n background-color: var(--mj-bg-surface-sunken);\n background-image: radial-gradient(color-mix(in srgb, var(--mj-color-neutral-400, #94a3b8) 15%, transparent) 1px, transparent 1px);\n background-size: 22px 22px;\n cursor: default;\n touch-action: none;\n}\n.cur-pan { cursor: grab; }\n.cur-pan.is-panning { cursor: grabbing; }\n.cur-cross { cursor: crosshair; }\n.cur-copy { cursor: copy; }\n.cur-text { cursor: text; }\n.cur-eraser { cursor: cell; }\n\n.board-viewport {\n position: absolute;\n left: 0;\n top: 0;\n width: 0;\n height: 0;\n transform-origin: 0 0;\n overflow: visible;\n}\n\n/* ---------- ink + connector svg layer ---------- */\n.ink-layer {\n position: absolute;\n left: 0;\n top: 0;\n width: 1px;\n height: 1px;\n pointer-events: none;\n overflow: visible;\n}\n.ink-layer path {\n pointer-events: none;\n}\n.ink-layer path.wb-erasable {\n pointer-events: stroke;\n cursor: pointer;\n}\n.ink-sage { stroke: var(--sage) !important; }\n.wb-ink-selected { filter: drop-shadow(0 0 4px color-mix(in srgb, var(--mj-brand-primary) 60%, transparent)); }\n\n.conn-user path { stroke: var(--mj-color-neutral-300, #cbd5e1); }\n.conn-sage path {\n stroke: var(--sage);\n filter: drop-shadow(0 0 4px color-mix(in srgb, var(--sage) 35%, transparent));\n}\n.draw-in {\n stroke-dasharray: 420;\n stroke-dashoffset: 420;\n animation: wb-conn-draw 1.4s ease-out forwards;\n}\n@keyframes wb-conn-draw { to { stroke-dashoffset: 0; } }\n\n.wb-live-pen { opacity: 0.95; }\n.wb-pending-conn {\n stroke: var(--mj-text-disabled);\n stroke-dasharray: 4 4;\n}\n\n/* ---------- board items: shared chrome ---------- */\n.bi {\n position: absolute;\n box-shadow: var(--mj-shadow-lg, 0 10px 15px -3px rgba(0, 0, 0, 0.3));\n /* drag-to-move must never fight native text selection (inline editors re-enable it) */\n user-select: none;\n -webkit-user-select: none;\n}\n.author-chip {\n position: absolute;\n top: -9px;\n left: 8px;\n z-index: 3;\n display: inline-flex;\n align-items: center;\n gap: 4px;\n padding: 1px 7px;\n font-size: 9px;\n font-weight: 800;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n border-radius: var(--mj-radius-full, 9999px);\n line-height: 1.5;\n white-space: nowrap;\n pointer-events: none;\n}\n.author-chip i { font-size: 7.5px; }\n.author-chip--you {\n background: var(--mj-color-neutral-700, #334155);\n border: 1px solid var(--mj-border-strong);\n color: var(--mj-color-neutral-300, #cbd5e1);\n}\n.author-chip--sage {\n background: var(--sage-chip-bg);\n border: 1px solid var(--sage);\n color: var(--mj-color-violet-300, #c4b5fd);\n}\n\n/* ---------- sticky notes (amber tints are categorical content colors) ---------- */\n.sticky {\n width: 172px;\n padding: 14px 13px 12px;\n border-radius: 4px 4px 14px 4px;\n font-size: 12.5px;\n line-height: 1.45;\n font-weight: 500;\n cursor: default;\n white-space: pre-wrap;\n word-break: break-word;\n}\n.sticky--amber { background: linear-gradient(180deg, #fcd34d, #f5b831); color: #3f2d04; }\n.sticky--amber2 { background: linear-gradient(180deg, #fde68a, #f8ca4d); color: #3f2d04; }\n.sticky--sage {\n background: linear-gradient(180deg, #38306b, #2c2455);\n color: var(--mj-color-violet-300, #c4b5fd);\n border: 1px solid var(--sage);\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--sage) 20%, transparent), var(--mj-shadow-lg, 0 10px 15px -3px rgba(0, 0, 0, 0.3));\n}\n\n/* ---------- shape boxes ---------- */\n.shape-box {\n min-width: 60px;\n min-height: 56px;\n display: grid;\n place-items: center;\n text-align: center;\n padding: 10px 12px;\n border: 2px solid var(--mj-color-neutral-400, #94a3b8);\n border-radius: 10px;\n background: color-mix(in srgb, var(--mj-color-neutral-800, #1e293b) 88%, transparent);\n font-size: 12.5px;\n font-weight: 600;\n color: var(--mj-text-primary);\n cursor: default;\n}\n.shape-box .sub {\n display: block;\n font-size: 10.5px;\n color: var(--mj-text-muted);\n font-weight: 500;\n margin-top: 2px;\n}\n.shape-box--ellipse { border-radius: 50%; }\n.shape-box--diamond {\n border-radius: 10px;\n transform: rotate(45deg);\n}\n.shape-box--diamond > span { transform: rotate(-45deg); }\n.shape-box--sage {\n border-color: var(--sage);\n background: color-mix(in srgb, var(--sage) 12%, var(--mj-bg-surface-sunken));\n color: var(--mj-color-violet-300, #c4b5fd);\n}\n\n/* shape drag-create preview */\n.shape-preview {\n position: absolute;\n border: 2px dashed var(--mj-brand-primary);\n border-radius: 10px;\n background: color-mix(in srgb, var(--mj-brand-primary) 6%, transparent);\n pointer-events: none;\n}\n\n/* marquee (rubber-band) multi-selection rectangle \u2014 select tool drag on empty canvas */\n.wb-marquee {\n position: absolute;\n border: 1px dashed var(--mj-brand-primary);\n border-radius: 3px;\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, transparent);\n pointer-events: none;\n z-index: 40;\n}\n\n/* ---------- text labels ---------- */\n.tlabel {\n font-size: 12px;\n font-weight: 700;\n color: var(--mj-color-violet-300, #c4b5fd);\n background: transparent;\n box-shadow: none;\n letter-spacing: 0.01em;\n text-shadow: 0 1px 6px rgba(2, 6, 23, 0.8);\n /* WRAP by default: long labels fold at the default max width; an explicit item\n width (resize / the tool's w param) binds inline and wins. */\n white-space: pre-wrap;\n overflow-wrap: break-word;\n max-width: 380px;\n cursor: default;\n}\n.tlabel i {\n font-size: 9px;\n margin-right: 4px;\n opacity: 0.8;\n}\n.tlabel--user {\n color: var(--mj-text-primary);\n}\n\n/* ---------- markdown panel ----------\n NOTE: the card ROOT must NOT clip (no overflow:hidden) \u2014 the selection handles and the\n author chip jut OUTSIDE its box (\u22128/\u22129px) and clipping made them invisible/ungrabbable.\n The BODY does the clipping (overflow + the template's max-height binding) instead. */\n.md-card {\n border-radius: 10px;\n border: 1.5px solid var(--mj-border-strong);\n background: var(--mj-bg-surface-card);\n cursor: default;\n}\n.md-card__body {\n padding: 16px 12px 10px;\n font-size: 12px;\n line-height: 1.45;\n color: var(--mj-text-primary);\n overflow: hidden;\n border-radius: 8.5px; /* outer 10px radius minus the 1.5px border */\n}\n.md-card--sage {\n border-color: var(--sage);\n background: color-mix(in srgb, var(--sage) 8%, var(--mj-bg-surface-card));\n}\n/* compact the shared markdown renderer's defaults inside the small board card\n (mj-markdown uses encapsulation:None, so ::ng-deep reaches its rendered content) */\n.md-card ::ng-deep .mj-markdown-container h1 { font-size: 16px; margin: 4px 0 6px; }\n.md-card ::ng-deep .mj-markdown-container h2 { font-size: 14px; margin: 4px 0 6px; }\n.md-card ::ng-deep .mj-markdown-container h3,\n.md-card ::ng-deep .mj-markdown-container h4 { font-size: 12.5px; margin: 4px 0 4px; }\n.md-card ::ng-deep .mj-markdown-container p { margin: 0 0 6px; }\n.md-card ::ng-deep .mj-markdown-container ul,\n.md-card ::ng-deep .mj-markdown-container ol { margin: 0 0 6px; padding-left: 18px; }\n.md-card ::ng-deep .mj-markdown-container pre { font-size: 10.5px; padding: 6px 8px; margin: 0 0 6px; overflow: auto; }\n.md-card ::ng-deep .mj-markdown-container code { font-size: 10.5px; }\n\n/* ---------- interactive HTML widget (sandboxed iframe in a mini window) ----------\n NOTE: the card ROOT must NOT clip (no overflow:hidden) \u2014 the selection handles jut\n OUTSIDE its box (\u22128px) and clipping made them invisible/ungrabbable. The head/body\n carry the rounded-corner clipping instead. */\n.html-card {\n display: flex;\n flex-direction: column;\n border-radius: 10px;\n border: 1.5px solid var(--mj-border-strong);\n background: var(--mj-bg-surface-card);\n cursor: default;\n min-width: 160px;\n min-height: 90px;\n}\n.html-card--sage {\n border-color: var(--sage);\n}\n.html-card__head {\n display: flex;\n align-items: center;\n gap: 7px;\n padding: 5px 8px;\n background: var(--mj-bg-surface-sunken);\n border-bottom: 1px solid var(--mj-border-default);\n border-radius: 8.5px 8.5px 0 0; /* outer 10px radius minus the 1.5px border */\n cursor: grab;\n user-select: none;\n}\n.html-card--sage .html-card__head {\n background: color-mix(in srgb, var(--sage) 14%, var(--mj-bg-surface-sunken));\n}\n.html-card__glyph {\n font-size: 10px;\n color: var(--mj-text-muted);\n}\n.html-card--sage .html-card__glyph { color: var(--sage); }\n.html-card__title {\n flex: 1;\n min-width: 0;\n font-size: 11px;\n font-weight: 700;\n color: var(--mj-text-primary);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n.author-chip--inline {\n position: static;\n flex-shrink: 0;\n}\n.html-card__btn {\n flex-shrink: 0;\n display: grid;\n place-items: center;\n width: 20px;\n height: 20px;\n padding: 0;\n border: 0;\n border-radius: var(--mj-radius-sm, 4px);\n background: transparent;\n color: var(--mj-text-muted);\n font-size: 10px;\n cursor: pointer;\n}\n.html-card__btn:hover {\n background: var(--mj-bg-surface-hover);\n color: var(--mj-text-primary);\n}\n.html-card__body {\n position: relative;\n flex: 1;\n min-height: 0;\n background: var(--mj-bg-surface);\n overflow: hidden;\n border-radius: 0 0 8.5px 8.5px; /* clips the iframe's square corners */\n}\n.html-card__frame {\n position: absolute;\n inset: 0;\n width: 100%;\n height: 100%;\n border: 0;\n display: block;\n background: #ffffff; /* widgets author against a plain document background */\n}\n/* While a board gesture is in flight, frames must not swallow pointermove \u2014\n otherwise dragging a widget by its header dies the moment the pointer crosses an iframe. */\n.board-canvas.is-interacting .html-card__frame {\n pointer-events: none;\n}\n.html-card__lazy {\n position: absolute;\n inset: 0;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 6px;\n font-size: 11px;\n color: var(--mj-text-muted);\n background: var(--mj-bg-surface-sunken);\n /* off-viewport placeholder is a plain div \u2014 pointerdown bubbles to the card root,\n so the whole placeholder is a drag surface (unlike the live iframe body) */\n cursor: grab;\n}\n.html-card__lazy i { font-size: 16px; color: var(--mj-text-disabled); }\n\n/* ---------- in-board rich-content editor panel ---------- */\n.rich-editor {\n position: absolute;\n top: 14px;\n right: 14px;\n bottom: 14px;\n z-index: 12;\n width: min(480px, 62%);\n display: flex;\n flex-direction: column;\n background: var(--mj-bg-surface-elevated, var(--mj-bg-surface));\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-lg, 12px);\n box-shadow: var(--mj-shadow-xl, 0 20px 25px -5px rgba(0, 0, 0, 0.3));\n overflow: hidden;\n cursor: default;\n}\n.rich-editor__head {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 10px;\n border-bottom: 1px solid var(--mj-border-default);\n background: var(--mj-bg-surface-sunken);\n font-size: 11px;\n color: var(--mj-text-muted);\n}\n.rich-editor__label {\n font-weight: 700;\n color: var(--mj-text-primary);\n white-space: nowrap;\n}\n.rich-editor__modes {\n margin-left: auto;\n display: inline-flex;\n gap: 2px;\n padding: 2px;\n border-radius: var(--mj-radius-full, 9999px);\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n}\n.re-mode {\n border: 0;\n background: transparent;\n color: var(--mj-text-muted);\n font-size: 10.5px;\n font-weight: 700;\n padding: 2px 10px;\n border-radius: var(--mj-radius-full, 9999px);\n cursor: pointer;\n}\n.re-mode.on {\n background: var(--mj-brand-primary);\n color: var(--mj-text-inverse, #fff);\n}\n.rich-editor__title {\n flex: 1;\n min-width: 0;\n margin-left: 4px;\n padding: 3px 8px;\n font-size: 11.5px;\n color: var(--mj-text-primary);\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-sm, 4px);\n outline: none;\n}\n.rich-editor__title:focus { border-color: var(--mj-border-focus, var(--mj-brand-primary)); }\n.rich-editor__body {\n flex: 1;\n min-height: 0;\n display: flex;\n flex-direction: column;\n}\n.rich-editor__code {\n flex: 1;\n min-height: 0;\n display: block;\n overflow: auto;\n font-size: 12px;\n}\n.rich-editor__preview {\n flex: 1;\n min-height: 0;\n overflow: auto;\n padding: 12px 14px;\n font-size: 12.5px;\n color: var(--mj-text-primary);\n}\n.rich-editor__note {\n display: flex;\n align-items: center;\n gap: 7px;\n padding: 6px 10px;\n font-size: 10px;\n color: var(--mj-text-muted);\n border-top: 1px solid var(--mj-border-subtle, var(--mj-border-default));\n}\n.rich-editor__foot {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 10px;\n border-top: 1px solid var(--mj-border-default);\n background: var(--mj-bg-surface-sunken);\n}\n.re-spacer { flex: 1; }\n.re-btn {\n padding: 4px 14px;\n font-size: 11.5px;\n font-weight: 700;\n border-radius: var(--mj-radius-md, 6px);\n border: 1px solid var(--mj-border-default);\n background: var(--mj-bg-surface);\n color: var(--mj-text-primary);\n cursor: pointer;\n}\n.re-btn:hover { background: var(--mj-bg-surface-hover); }\n.re-btn--primary {\n background: var(--mj-brand-primary);\n border-color: var(--mj-brand-primary);\n color: var(--mj-text-inverse, #fff);\n}\n.re-btn--primary:hover { background: var(--mj-brand-primary-hover, var(--mj-brand-primary)); }\n\n/* ---------- pasted image card ----------\n NOTE: the card ROOT must NOT clip (no overflow:hidden) \u2014 selection handles + author\n chip jut outside its box. The body clips the photo's top corners instead. */\n.img-card {\n width: 198px;\n border-radius: 10px;\n border: 1.5px solid var(--mj-border-strong);\n background: var(--mj-bg-surface-card);\n cursor: default;\n}\n.img-card__body {\n height: 104px;\n display: grid;\n place-items: center;\n overflow: hidden;\n border-radius: 8.5px 8.5px 0 0; /* outer 10px radius minus the 1.5px border */\n background:\n linear-gradient(160deg, color-mix(in srgb, var(--mj-brand-primary) 30%, transparent) 0%, color-mix(in srgb, var(--mj-brand-primary) 10%, transparent) 38%, transparent 39%),\n linear-gradient(200deg, color-mix(in srgb, var(--mj-brand-accent, var(--mj-brand-primary)) 22%, transparent) 0%, color-mix(in srgb, var(--mj-brand-accent, var(--mj-brand-primary)) 8%, transparent) 60%, transparent 61%),\n var(--mj-color-neutral-900, #0f172a);\n color: var(--mj-text-disabled);\n font-size: 22px;\n}\n.img-card__body img {\n width: 100%;\n height: 100%;\n object-fit: cover;\n display: block;\n /* the photo is presentational \u2014 without this, the browser's NATIVE image drag\n hijacks the pointer gesture and kills the board's move interaction */\n pointer-events: none;\n -webkit-user-drag: none;\n}\n.img-card__name {\n display: flex;\n align-items: center;\n gap: 7px;\n padding: 6px 10px;\n font-size: 10.5px;\n color: var(--mj-text-muted);\n font-family: var(--mj-font-mono, ui-monospace, monospace);\n border-top: 1px solid var(--mj-border-default);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n.img-card__name i { color: var(--mj-brand-accent, var(--mj-brand-primary)); }\n\n/* ---------- agent pop-in (just-added item) ---------- */\n.pop-in {\n animation: wb-bi-pop 600ms cubic-bezier(0.34, 1.4, 0.64, 1) both, wb-sage-flash 2.4s ease-out;\n}\n@keyframes wb-bi-pop {\n 0% { scale: 0.55; opacity: 0; }\n 70% { scale: 1.06; }\n 100% { scale: 1; opacity: 1; }\n}\n@keyframes wb-sage-flash {\n 0% {\n box-shadow: 0 0 0 12px color-mix(in srgb, var(--sage) 45%, transparent),\n 0 0 36px color-mix(in srgb, var(--sage) 50%, transparent),\n var(--mj-shadow-lg, 0 10px 15px -3px rgba(0, 0, 0, 0.3));\n }\n 100% {\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--sage) 20%, transparent),\n var(--mj-shadow-lg, 0 10px 15px -3px rgba(0, 0, 0, 0.3));\n }\n}\n@media (prefers-reduced-motion: reduce) {\n .pop-in { animation: none; }\n .draw-in { animation: none; stroke-dashoffset: 0; }\n}\n\n/* ---------- selection + drag state ---------- */\n.bi.selected {\n outline: 1.5px solid var(--mj-brand-primary);\n outline-offset: 3px;\n}\n.bi.dragging {\n transform: rotate(-1.6deg) scale(1.02);\n box-shadow: var(--mj-shadow-2xl, 0 25px 50px -12px rgba(0, 0, 0, 0.45));\n cursor: grabbing;\n z-index: 5;\n border-color: var(--mj-brand-primary);\n}\n.sel-h {\n position: absolute;\n width: 8px;\n height: 8px;\n border-radius: 2px;\n background: var(--mj-bg-surface);\n border: 1.5px solid var(--mj-brand-primary);\n z-index: 4;\n}\n.sel-h.nw { top: -8px; left: -8px; cursor: nwse-resize; }\n.sel-h.n { top: -8px; left: calc(50% - 4px); cursor: ns-resize; }\n.sel-h.ne { top: -8px; right: -8px; cursor: nesw-resize; }\n.sel-h.e { top: calc(50% - 4px); right: -8px; cursor: ew-resize; }\n.sel-h.se { bottom: -8px; right: -8px; cursor: nwse-resize; }\n.sel-h.s { bottom: -8px; left: calc(50% - 4px); cursor: ns-resize; }\n.sel-h.sw { bottom: -8px; left: -8px; cursor: nesw-resize; }\n.sel-h.w { top: calc(50% - 4px); left: -8px; cursor: ew-resize; }\n\n.drag-ghost {\n position: absolute;\n border: 1.5px dashed var(--mj-border-strong);\n border-radius: 10px;\n opacity: 0.55;\n pointer-events: none;\n}\n.drag-ghost::after {\n content: \"from here\";\n position: absolute;\n bottom: -18px;\n left: 4px;\n font-size: 9px;\n color: var(--mj-text-disabled);\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n}\n\n/* ---------- agent highlight region (click dismisses) ---------- */\n.sage-highlight {\n position: absolute;\n border: 2px dashed var(--sage);\n border-radius: 16px;\n background: color-mix(in srgb, var(--sage) 6%, transparent);\n animation: wb-hl-pulse 2.2s ease-in-out infinite;\n cursor: pointer;\n}\n@keyframes wb-hl-pulse {\n 0%, 100% { box-shadow: 0 0 0 0 transparent; }\n 50% { box-shadow: 0 0 22px 2px color-mix(in srgb, var(--sage) 25%, transparent); }\n}\n.sage-highlight .hl-tag {\n position: absolute;\n top: -11px;\n right: 12px;\n display: inline-flex;\n align-items: center;\n gap: 5px;\n padding: 1px 8px;\n font-size: 9.5px;\n font-weight: 800;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n background: var(--sage-chip-bg);\n border: 1px solid var(--sage);\n color: var(--mj-color-violet-300, #c4b5fd);\n border-radius: var(--mj-radius-full, 9999px);\n white-space: nowrap;\n}\n.sage-highlight.user-highlight {\n border-color: var(--mj-border-strong);\n background: color-mix(in srgb, var(--mj-text-secondary) 5%, transparent);\n}\n.sage-highlight.user-highlight .hl-tag {\n background: var(--mj-color-neutral-700, #334155);\n border-color: var(--mj-border-strong);\n color: var(--mj-color-neutral-300, #cbd5e1);\n}\n\n/* ---------- agent presence cursor ---------- */\n.cursor {\n position: absolute;\n z-index: 8;\n pointer-events: none;\n}\n.cursor--sage {\n display: flex;\n align-items: flex-start;\n gap: 6px;\n transition: left 700ms cubic-bezier(0.33, 1, 0.68, 1), top 700ms cubic-bezier(0.33, 1, 0.68, 1);\n animation: wb-sage-cursor-drift 5s ease-in-out infinite alternate;\n}\n.cursor--sage .tip {\n position: relative;\n width: 26px;\n height: 26px;\n border-radius: 50% 50% 50% 4px;\n display: grid;\n place-items: center;\n background: var(--sage);\n color: #1b1438;\n font-size: 11px;\n box-shadow: 0 0 14px color-mix(in srgb, var(--sage) 60%, transparent);\n}\n.cursor--sage .tip::after {\n content: \"\";\n position: absolute;\n inset: -5px;\n border-radius: inherit;\n border: 2px solid color-mix(in srgb, var(--sage) 45%, transparent);\n animation: wb-orb-ring 1.8s ease-out infinite;\n}\n.cursor--sage .lbl {\n margin-top: 14px;\n padding: 3px 10px;\n border-radius: var(--mj-radius-full, 9999px);\n white-space: nowrap;\n background: var(--sage-chip-bg);\n border: 1px solid var(--sage);\n color: var(--mj-color-violet-300, #c4b5fd);\n font-size: 10.5px;\n font-weight: 700;\n}\n.cursor--sage .lbl i {\n animation: wb-pen-wiggle 0.9s ease-in-out infinite;\n display: inline-block;\n margin-right: 3px;\n font-size: 9px;\n}\n@keyframes wb-pen-wiggle {\n 0%, 100% { transform: rotate(0); }\n 50% { transform: rotate(-14deg); }\n}\n@keyframes wb-sage-cursor-drift {\n from { transform: translate(0, 0); }\n to { transform: translate(-26px, 20px); }\n}\n@keyframes wb-orb-ring {\n 0% { transform: scale(1); opacity: 0.6; }\n 100% { transform: scale(1.5); opacity: 0; }\n}\n@media (prefers-reduced-motion: reduce) {\n .cursor--sage { animation: none; }\n .sage-highlight { animation: none; }\n}\n\n/* ---------- inline editors ---------- */\n.wb-edit {\n display: block;\n width: 100%;\n background: transparent;\n border: 0;\n outline: none;\n resize: none;\n color: inherit;\n font: inherit;\n line-height: inherit;\n padding: 0;\n /* re-enable selection inside the editors (.bi disables it for drag-to-move) */\n user-select: text;\n -webkit-user-select: text;\n}\n.wb-edit--shape { text-align: center; }\n.wb-edit--text { min-width: 110px; }\n\n/* ---------- minimap ---------- */\n.minimap {\n position: absolute;\n right: 14px;\n bottom: 58px;\n z-index: 10;\n width: 176px;\n height: 118px;\n padding: 6px;\n background: var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-lg, 12px);\n box-shadow: var(--mj-shadow-xl, 0 20px 25px -5px rgba(0, 0, 0, 0.3));\n}\n.minimap svg {\n width: 100%;\n height: 100%;\n display: block;\n}\n.mm-item {\n fill: color-mix(in srgb, var(--mj-text-secondary) 45%, transparent);\n}\n.mm-item--sage {\n fill: color-mix(in srgb, var(--sage) 60%, transparent);\n}\n.mm-viewport {\n fill: none;\n stroke: var(--mj-brand-primary);\n stroke-width: 6;\n vector-effect: non-scaling-stroke;\n}\n\n/* \u2500\u2500 right-click context menu \u2014 matches the export-menu / flyout chrome \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.wb-context-menu {\n position: absolute;\n z-index: 40;\n min-width: 178px;\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 cursor: default;\n}\n.wb-context-menu__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.wb-context-menu__item:hover {\n background: var(--mj-bg-surface-hover);\n color: var(--mj-text-primary);\n}\n.wb-context-menu__item i {\n width: 14px;\n text-align: center;\n color: var(--mj-text-muted);\n}\n.wb-context-menu__item:hover i {\n color: var(--mj-brand-accent, var(--mj-brand-primary));\n}\n.wb-context-menu__item--danger {\n color: var(--mj-status-error);\n}\n.wb-context-menu__item--danger:hover {\n background: color-mix(in srgb, var(--mj-status-error) 10%, var(--mj-bg-surface-elevated));\n color: var(--mj-status-error-text, var(--mj-status-error));\n}\n.wb-context-menu__item--danger i,\n.wb-context-menu__item--danger:hover i {\n color: var(--mj-status-error);\n}\n.wb-context-menu__sep {\n height: 1px;\n margin: 4px 6px;\n background: var(--mj-border-subtle, var(--mj-border-default));\n}\n"] }]
2303
+ }], null, { State: [{
2304
+ type: Input,
2305
+ args: [{ required: true }]
2306
+ }], AgentName: [{
2307
+ type: Input
2308
+ }], Tool: [{
2309
+ type: Input
2310
+ }], PenColor: [{
2311
+ type: Input
2312
+ }], PenWidth: [{
2313
+ type: Input
2314
+ }], ShapeKind: [{
2315
+ type: Input
2316
+ }], TextSize: [{
2317
+ type: Input
2318
+ }], TextFamily: [{
2319
+ type: Input
2320
+ }], TextBold: [{
2321
+ type: Input
2322
+ }], TextColor: [{
2323
+ type: Input
2324
+ }], AgentPresence: [{
2325
+ type: Input
2326
+ }], ReadOnly: [{
2327
+ type: Input
2328
+ }], ToolChangeRequest: [{
2329
+ type: Output
2330
+ }], WidgetSubmitting: [{
2331
+ type: Output
2332
+ }], WidgetSubmitted: [{
2333
+ type: Output
2334
+ }], WidgetInteraction: [{
2335
+ type: Output
2336
+ }], ContentApplying: [{
2337
+ type: Output
2338
+ }], ContentApplied: [{
2339
+ type: Output
2340
+ }], canvasRef: [{
2341
+ type: ViewChild,
2342
+ args: ['canvas']
2343
+ }], pagesStrip: [{
2344
+ type: ViewChild,
2345
+ args: [RealtimeWhiteboardPagesComponent]
2346
+ }], OnWindowPointerMove: [{
2347
+ type: HostListener,
2348
+ args: ['window:pointermove', ['$event']]
2349
+ }], OnWindowPointerUp: [{
2350
+ type: HostListener,
2351
+ args: ['window:pointerup']
2352
+ }], CloseContextMenu: [{
2353
+ type: HostListener,
2354
+ args: ['document:pointerdown']
2355
+ }] }); })();
2356
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(RealtimeWhiteboardBoardComponent, { className: "RealtimeWhiteboardBoardComponent", filePath: "src/lib/whiteboard-board.component.ts", lineNumber: 164 }); })();
2357
+ //# sourceMappingURL=whiteboard-board.component.js.map