@joewinke/jatui 0.1.24 → 0.1.26

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 (242) hide show
  1. package/dist/actions/collapse.d.ts +80 -0
  2. package/dist/actions/collapse.js +134 -0
  3. package/dist/actions/railNav.d.ts +167 -0
  4. package/dist/actions/railNav.js +351 -0
  5. package/dist/components/AnnotationLayer.svelte +108 -0
  6. package/dist/components/AnnotationLayer.svelte.d.ts +12 -0
  7. package/dist/components/AnnotationPanel.svelte +319 -0
  8. package/dist/components/AnnotationPanel.svelte.d.ts +13 -0
  9. package/dist/components/AudioWaveform.svelte +701 -0
  10. package/dist/components/AudioWaveform.svelte.d.ts +19 -0
  11. package/dist/components/AvailabilityModal.svelte +177 -0
  12. package/dist/components/AvailabilityModal.svelte.d.ts +14 -0
  13. package/dist/components/AvatarUpload.svelte +234 -0
  14. package/dist/components/AvatarUpload.svelte.d.ts +23 -0
  15. package/dist/components/Badge.svelte +38 -0
  16. package/dist/components/Badge.svelte.d.ts +14 -0
  17. package/dist/components/BookingForm.svelte +278 -0
  18. package/dist/components/BookingForm.svelte.d.ts +37 -0
  19. package/dist/components/BurndownChart.svelte +778 -0
  20. package/dist/components/BurndownChart.svelte.d.ts +17 -0
  21. package/dist/components/Button.svelte +81 -0
  22. package/dist/components/Button.svelte.d.ts +21 -0
  23. package/dist/components/CalendarPicker.svelte +284 -0
  24. package/dist/components/CalendarPicker.svelte.d.ts +22 -0
  25. package/dist/components/Card.svelte +67 -0
  26. package/dist/components/Card.svelte.d.ts +17 -0
  27. package/dist/components/CharacterCounter.svelte +82 -0
  28. package/dist/components/CharacterCounter.svelte.d.ts +10 -0
  29. package/dist/components/ChipInput.svelte +609 -0
  30. package/dist/components/ChipInput.svelte.d.ts +55 -0
  31. package/dist/components/Collapse.svelte +91 -0
  32. package/dist/components/Collapse.svelte.d.ts +18 -0
  33. package/dist/components/ColorSelector.svelte +167 -0
  34. package/dist/components/ColorSelector.svelte.d.ts +9 -0
  35. package/dist/components/CommentThread.svelte +773 -0
  36. package/dist/components/CommentThread.svelte.d.ts +32 -0
  37. package/dist/components/ConfirmDialog.svelte +348 -0
  38. package/dist/components/ConfirmDialog.svelte.d.ts +3 -0
  39. package/dist/components/ConfirmModal.svelte +142 -0
  40. package/dist/components/ConfirmModal.svelte.d.ts +15 -0
  41. package/dist/components/ContextMenu.svelte +188 -0
  42. package/dist/components/ContextMenu.svelte.d.ts +31 -0
  43. package/dist/components/CountdownTimer.svelte +94 -0
  44. package/dist/components/CountdownTimer.svelte.d.ts +10 -0
  45. package/dist/components/DateRangePicker.svelte +194 -0
  46. package/dist/components/DateRangePicker.svelte.d.ts +10 -0
  47. package/dist/components/Drawer.svelte +143 -0
  48. package/dist/components/Drawer.svelte.d.ts +33 -0
  49. package/dist/components/EmojiPicker.svelte +159 -0
  50. package/dist/components/EmojiPicker.svelte.d.ts +8 -0
  51. package/dist/components/EntityPreviewCard.svelte +104 -0
  52. package/dist/components/EntityPreviewCard.svelte.d.ts +9 -0
  53. package/dist/components/FileDropzone.svelte +493 -0
  54. package/dist/components/FileDropzone.svelte.d.ts +40 -0
  55. package/dist/components/FilePicker.svelte +488 -0
  56. package/dist/components/FilePicker.svelte.d.ts +20 -0
  57. package/dist/components/FileThumbnail.svelte +80 -0
  58. package/dist/components/FileThumbnail.svelte.d.ts +12 -0
  59. package/dist/components/FilterDropdown.svelte +203 -0
  60. package/dist/components/FilterDropdown.svelte.d.ts +48 -0
  61. package/dist/components/FloatingActionBar.svelte +354 -0
  62. package/dist/components/FloatingActionBar.svelte.d.ts +51 -0
  63. package/dist/components/GPSTracker.svelte +202 -0
  64. package/dist/components/GPSTracker.svelte.d.ts +34 -0
  65. package/dist/components/HunkDiffView.svelte +348 -0
  66. package/dist/components/HunkDiffView.svelte.d.ts +9 -0
  67. package/dist/components/ImageLightbox.svelte +274 -0
  68. package/dist/components/ImageLightbox.svelte.d.ts +25 -0
  69. package/dist/components/ImageUpload.svelte +147 -0
  70. package/dist/components/ImageUpload.svelte.d.ts +10 -0
  71. package/dist/components/InlineEdit.svelte +291 -0
  72. package/dist/components/InlineEdit.svelte.d.ts +45 -0
  73. package/dist/components/InputDialog.svelte +327 -0
  74. package/dist/components/InputDialog.svelte.d.ts +3 -0
  75. package/dist/components/KeyboardShortcutsOverlay.svelte +296 -0
  76. package/dist/components/KeyboardShortcutsOverlay.svelte.d.ts +42 -0
  77. package/dist/components/LazyImage.svelte +123 -0
  78. package/dist/components/LazyImage.svelte.d.ts +14 -0
  79. package/dist/components/LinkShortener.svelte +274 -0
  80. package/dist/components/LinkShortener.svelte.d.ts +27 -0
  81. package/dist/components/LoadingSpinner.svelte +106 -0
  82. package/dist/components/LoadingSpinner.svelte.d.ts +21 -0
  83. package/dist/components/LocationMap.svelte +186 -0
  84. package/dist/components/LocationMap.svelte.d.ts +11 -0
  85. package/dist/components/MapView.svelte +341 -0
  86. package/dist/components/MapView.svelte.d.ts +20 -0
  87. package/dist/components/MarkupEditor.svelte +485 -0
  88. package/dist/components/MarkupEditor.svelte.d.ts +10 -0
  89. package/dist/components/MarkupOverlay.svelte +55 -0
  90. package/dist/components/MarkupOverlay.svelte.d.ts +8 -0
  91. package/dist/components/MediaWorkbench.svelte +871 -0
  92. package/dist/components/MediaWorkbench.svelte.d.ts +9 -0
  93. package/dist/components/MilestoneCard.svelte +98 -0
  94. package/dist/components/MilestoneCard.svelte.d.ts +16 -0
  95. package/dist/components/MilestoneTimeline.svelte +91 -0
  96. package/dist/components/MilestoneTimeline.svelte.d.ts +15 -0
  97. package/dist/components/Modal.svelte +244 -0
  98. package/dist/components/Modal.svelte.d.ts +35 -0
  99. package/dist/components/PDFViewer.svelte +105 -0
  100. package/dist/components/PDFViewer.svelte.d.ts +9 -0
  101. package/dist/components/PdfThumbnail.svelte +63 -0
  102. package/dist/components/PdfThumbnail.svelte.d.ts +9 -0
  103. package/dist/components/PhoneInput.svelte +212 -0
  104. package/dist/components/PhoneInput.svelte.d.ts +25 -0
  105. package/dist/components/ResizableDivider.svelte +307 -0
  106. package/dist/components/ResizableDivider.svelte.d.ts +26 -0
  107. package/dist/components/ResizablePanel.svelte +305 -0
  108. package/dist/components/ResizablePanel.svelte.d.ts +18 -0
  109. package/dist/components/SearchDropdown.svelte +421 -0
  110. package/dist/components/SearchDropdown.svelte.d.ts +24 -0
  111. package/dist/components/SelectInput.svelte +237 -0
  112. package/dist/components/SelectInput.svelte.d.ts +26 -0
  113. package/dist/components/SidebarUserFooter.svelte +34 -0
  114. package/dist/components/SidebarUserFooter.svelte.d.ts +16 -0
  115. package/dist/components/SignaturePad.svelte +175 -0
  116. package/dist/components/SignaturePad.svelte.d.ts +17 -0
  117. package/dist/components/SmartImageEditor.svelte +720 -0
  118. package/dist/components/SmartImageEditor.svelte.d.ts +24 -0
  119. package/dist/components/SortDropdown.svelte +156 -0
  120. package/dist/components/SortDropdown.svelte.d.ts +40 -0
  121. package/dist/components/Sparkline.svelte +116 -0
  122. package/dist/components/Sparkline.svelte.d.ts +12 -0
  123. package/dist/components/SpeechForm.svelte +114 -0
  124. package/dist/components/SpeechForm.svelte.d.ts +17 -0
  125. package/dist/components/StatusBadge.svelte +157 -0
  126. package/dist/components/StatusBadge.svelte.d.ts +24 -0
  127. package/dist/components/TaskTypeIcon.svelte +54 -0
  128. package/dist/components/TaskTypeIcon.svelte.d.ts +17 -0
  129. package/dist/components/TextArea.svelte +162 -0
  130. package/dist/components/TextArea.svelte.d.ts +30 -0
  131. package/dist/components/TextInput.svelte +131 -0
  132. package/dist/components/TextInput.svelte.d.ts +29 -0
  133. package/dist/components/ThemeSelector.svelte +206 -0
  134. package/dist/components/ThemeSelector.svelte.d.ts +14 -0
  135. package/dist/components/TimeSlotPicker.svelte +162 -0
  136. package/dist/components/TimeSlotPicker.svelte.d.ts +18 -0
  137. package/dist/components/UserAvatar.svelte +72 -0
  138. package/dist/components/UserAvatar.svelte.d.ts +11 -0
  139. package/dist/components/VariablePicker.svelte +170 -0
  140. package/dist/components/VariablePicker.svelte.d.ts +40 -0
  141. package/dist/components/VoicePlayer.svelte +422 -0
  142. package/dist/components/VoicePlayer.svelte.d.ts +18 -0
  143. package/dist/components/linked-columns/LinkedColumns.svelte +526 -0
  144. package/dist/components/linked-columns/LinkedColumns.svelte.d.ts +130 -0
  145. package/dist/components/markup.d.ts +47 -0
  146. package/dist/components/markup.js +201 -0
  147. package/dist/components/messaging/Avatar.svelte +81 -0
  148. package/dist/components/messaging/Avatar.svelte.d.ts +11 -0
  149. package/dist/components/messaging/ChannelInfoModal.svelte +163 -0
  150. package/dist/components/messaging/ChannelInfoModal.svelte.d.ts +18 -0
  151. package/dist/components/messaging/ChannelList.svelte +107 -0
  152. package/dist/components/messaging/ChannelList.svelte.d.ts +11 -0
  153. package/dist/components/messaging/ChannelMemberAvatarStack.svelte +69 -0
  154. package/dist/components/messaging/ChannelMemberAvatarStack.svelte.d.ts +11 -0
  155. package/dist/components/messaging/ChannelMembersModal.svelte +182 -0
  156. package/dist/components/messaging/ChannelMembersModal.svelte.d.ts +16 -0
  157. package/dist/components/messaging/CreateChannelModal.svelte +190 -0
  158. package/dist/components/messaging/CreateChannelModal.svelte.d.ts +14 -0
  159. package/dist/components/messaging/DirectMessageList.svelte +145 -0
  160. package/dist/components/messaging/DirectMessageList.svelte.d.ts +12 -0
  161. package/dist/components/messaging/EmojiSelector.svelte +261 -0
  162. package/dist/components/messaging/EmojiSelector.svelte.d.ts +19 -0
  163. package/dist/components/messaging/MentionAutocomplete.svelte +193 -0
  164. package/dist/components/messaging/MentionAutocomplete.svelte.d.ts +17 -0
  165. package/dist/components/messaging/MessageAttachment.svelte +273 -0
  166. package/dist/components/messaging/MessageAttachment.svelte.d.ts +13 -0
  167. package/dist/components/messaging/MessageAttachmentUpload.svelte +243 -0
  168. package/dist/components/messaging/MessageAttachmentUpload.svelte.d.ts +19 -0
  169. package/dist/components/messaging/MessageInput.svelte +452 -0
  170. package/dist/components/messaging/MessageInput.svelte.d.ts +28 -0
  171. package/dist/components/messaging/MessageItem.svelte +341 -0
  172. package/dist/components/messaging/MessageItem.svelte.d.ts +41 -0
  173. package/dist/components/messaging/MessageThread.svelte +306 -0
  174. package/dist/components/messaging/MessageThread.svelte.d.ts +30 -0
  175. package/dist/components/messaging/NotificationSettingsModal.svelte +234 -0
  176. package/dist/components/messaging/NotificationSettingsModal.svelte.d.ts +13 -0
  177. package/dist/components/messaging/QuotedMessageDisplay.svelte +123 -0
  178. package/dist/components/messaging/QuotedMessageDisplay.svelte.d.ts +10 -0
  179. package/dist/components/messaging/StartDMModal.svelte +100 -0
  180. package/dist/components/messaging/StartDMModal.svelte.d.ts +14 -0
  181. package/dist/components/messaging/ThreadPanel.svelte +153 -0
  182. package/dist/components/messaging/ThreadPanel.svelte.d.ts +16 -0
  183. package/dist/components/pipeline/DESIGN.md +361 -0
  184. package/dist/components/pipeline/Pipeline.svelte +391 -0
  185. package/dist/components/pipeline/Pipeline.svelte.d.ts +28 -0
  186. package/dist/components/pipeline/PipelineCard.svelte +94 -0
  187. package/dist/components/pipeline/PipelineCard.svelte.d.ts +13 -0
  188. package/dist/components/pipeline/PipelineColumn.svelte +163 -0
  189. package/dist/components/pipeline/PipelineColumn.svelte.d.ts +30 -0
  190. package/dist/components/replay/ChapterTimeline.svelte +326 -0
  191. package/dist/components/replay/ChapterTimeline.svelte.d.ts +39 -0
  192. package/dist/components/session-nav/transcriptModel.d.ts +132 -0
  193. package/dist/components/session-nav/transcriptModel.js +285 -0
  194. package/dist/data/emojis.d.ts +18 -0
  195. package/dist/data/emojis.js +1196 -0
  196. package/dist/index.d.ts +142 -0
  197. package/dist/index.js +142 -0
  198. package/dist/stores/confirmDialog.svelte.d.ts +21 -0
  199. package/dist/stores/confirmDialog.svelte.js +31 -0
  200. package/dist/stores/inputDialog.svelte.d.ts +24 -0
  201. package/dist/stores/inputDialog.svelte.js +32 -0
  202. package/dist/styles/rail.css +63 -0
  203. package/dist/types/annotation.d.ts +32 -0
  204. package/dist/types/annotation.js +3 -0
  205. package/dist/types/booking.d.ts +115 -0
  206. package/dist/types/booking.js +24 -0
  207. package/dist/types/comments.d.ts +65 -0
  208. package/dist/types/comments.js +22 -0
  209. package/dist/types/entityPreview.d.ts +42 -0
  210. package/dist/types/entityPreview.js +10 -0
  211. package/dist/types/filePicker.d.ts +15 -0
  212. package/dist/types/filePicker.js +1 -0
  213. package/dist/types/googleMaps.d.ts +51 -0
  214. package/dist/types/maps.d.ts +43 -0
  215. package/dist/types/maps.js +4 -0
  216. package/dist/types/messaging.d.ts +282 -0
  217. package/dist/types/messaging.js +100 -0
  218. package/dist/types/milestone.d.ts +20 -0
  219. package/dist/types/milestone.js +7 -0
  220. package/dist/types/pipeline.d.ts +115 -0
  221. package/dist/types/pipeline.js +15 -0
  222. package/dist/types/smartImageEditor.d.ts +42 -0
  223. package/dist/types/smartImageEditor.js +1 -0
  224. package/dist/types/templateVars.d.ts +34 -0
  225. package/dist/types/templateVars.js +12 -0
  226. package/dist/utils/currency.d.ts +8 -0
  227. package/dist/utils/currency.js +17 -0
  228. package/dist/utils/daisyuiColors.d.ts +37 -0
  229. package/dist/utils/daisyuiColors.js +152 -0
  230. package/dist/utils/dateFormatters.d.ts +69 -0
  231. package/dist/utils/dateFormatters.js +199 -0
  232. package/dist/utils/googleMapsLoader.d.ts +19 -0
  233. package/dist/utils/googleMapsLoader.js +121 -0
  234. package/dist/utils/mentionParser.d.ts +67 -0
  235. package/dist/utils/mentionParser.js +132 -0
  236. package/dist/utils/phone.d.ts +13 -0
  237. package/dist/utils/phone.js +67 -0
  238. package/dist/utils/phoneFormat.d.ts +26 -0
  239. package/dist/utils/phoneFormat.js +69 -0
  240. package/dist/utils/taskUtils.d.ts +37 -0
  241. package/dist/utils/taskUtils.js +71 -0
  242. package/package.json +3 -2
@@ -0,0 +1,201 @@
1
+ // Markup drawing primitives for construction drawing redlining
2
+ // Adapted from JAT feedback annotation engine with cloud bubble tool added
3
+ export var MARKUP_COLORS = [
4
+ "#ef4444",
5
+ "#eab308",
6
+ "#22c55e",
7
+ "#3b82f6",
8
+ "#ffffff",
9
+ "#111827",
10
+ ];
11
+ export var DEFAULT_STROKE_WIDTH = 3;
12
+ var _nextId = 1;
13
+ export function nextShapeId() {
14
+ return _nextId++;
15
+ }
16
+ function drawArrow(ctx, shape) {
17
+ var start = shape.start, end = shape.end, color = shape.color, strokeWidth = shape.strokeWidth;
18
+ ctx.strokeStyle = color;
19
+ ctx.lineWidth = strokeWidth;
20
+ ctx.lineCap = "round";
21
+ ctx.lineJoin = "round";
22
+ ctx.beginPath();
23
+ ctx.moveTo(start.x, start.y);
24
+ ctx.lineTo(end.x, end.y);
25
+ ctx.stroke();
26
+ // Arrowhead
27
+ var angle = Math.atan2(end.y - start.y, end.x - start.x);
28
+ var headLen = 14;
29
+ var headAngle = Math.PI / 7;
30
+ ctx.beginPath();
31
+ ctx.moveTo(end.x, end.y);
32
+ ctx.lineTo(end.x - headLen * Math.cos(angle - headAngle), end.y - headLen * Math.sin(angle - headAngle));
33
+ ctx.moveTo(end.x, end.y);
34
+ ctx.lineTo(end.x - headLen * Math.cos(angle + headAngle), end.y - headLen * Math.sin(angle + headAngle));
35
+ ctx.stroke();
36
+ }
37
+ function drawRectangle(ctx, shape) {
38
+ var start = shape.start, end = shape.end, color = shape.color, strokeWidth = shape.strokeWidth;
39
+ ctx.strokeStyle = color;
40
+ ctx.lineWidth = strokeWidth;
41
+ ctx.lineJoin = "round";
42
+ var x = Math.min(start.x, end.x);
43
+ var y = Math.min(start.y, end.y);
44
+ var w = Math.abs(end.x - start.x);
45
+ var h = Math.abs(end.y - start.y);
46
+ ctx.strokeRect(x, y, w, h);
47
+ }
48
+ function drawEllipse(ctx, shape) {
49
+ var start = shape.start, end = shape.end, color = shape.color, strokeWidth = shape.strokeWidth;
50
+ ctx.strokeStyle = color;
51
+ ctx.lineWidth = strokeWidth;
52
+ var cx = (start.x + end.x) / 2;
53
+ var cy = (start.y + end.y) / 2;
54
+ var rx = Math.abs(end.x - start.x) / 2;
55
+ var ry = Math.abs(end.y - start.y) / 2;
56
+ if (rx < 1 || ry < 1)
57
+ return;
58
+ ctx.beginPath();
59
+ ctx.ellipse(cx, cy, rx, ry, 0, 0, Math.PI * 2);
60
+ ctx.stroke();
61
+ }
62
+ /**
63
+ * Revision cloud — a series of small arcs along the perimeter of a rectangle.
64
+ * Standard construction markup tool for highlighting areas of change.
65
+ */
66
+ function drawCloud(ctx, shape) {
67
+ var start = shape.start, end = shape.end, color = shape.color, strokeWidth = shape.strokeWidth;
68
+ ctx.strokeStyle = color;
69
+ ctx.lineWidth = strokeWidth;
70
+ ctx.lineCap = "round";
71
+ ctx.lineJoin = "round";
72
+ var x = Math.min(start.x, end.x);
73
+ var y = Math.min(start.y, end.y);
74
+ var w = Math.abs(end.x - start.x);
75
+ var h = Math.abs(end.y - start.y);
76
+ if (w < 4 || h < 4)
77
+ return;
78
+ // Generate perimeter points, then draw arcs between them
79
+ var arcSize = Math.min(w, h, 30); // arc diameter adapts to shape size
80
+ var perimeter = 2 * (w + h);
81
+ var numArcs = Math.max(8, Math.round(perimeter / arcSize));
82
+ // Collect points along the rectangle perimeter
83
+ var points = [];
84
+ var segLengths = [w, h, w, h];
85
+ var segDirs = [
86
+ { dx: 1, dy: 0 },
87
+ { dx: 0, dy: 1 },
88
+ { dx: -1, dy: 0 },
89
+ { dx: 0, dy: -1 },
90
+ ];
91
+ var segStarts = [
92
+ { x: x, y: y },
93
+ { x: x + w, y: y },
94
+ { x: x + w, y: y + h },
95
+ { x: x, y: y + h },
96
+ ];
97
+ var totalLen = 0;
98
+ for (var _i = 0, segLengths_1 = segLengths; _i < segLengths_1.length; _i++) {
99
+ var l = segLengths_1[_i];
100
+ totalLen += l;
101
+ }
102
+ var step = totalLen / numArcs;
103
+ var dist = 0;
104
+ var segIdx = 0;
105
+ var segDist = 0;
106
+ for (var i = 0; i < numArcs; i++) {
107
+ var target = i * step;
108
+ while (dist + segLengths[segIdx] - segDist < target && segIdx < 3) {
109
+ dist += segLengths[segIdx] - segDist;
110
+ segIdx++;
111
+ segDist = 0;
112
+ }
113
+ var remaining = target - dist;
114
+ points.push({
115
+ x: segStarts[segIdx].x + segDirs[segIdx].dx * (segDist + remaining),
116
+ y: segStarts[segIdx].y + segDirs[segIdx].dy * (segDist + remaining),
117
+ });
118
+ }
119
+ // Draw arcs between consecutive points
120
+ ctx.beginPath();
121
+ for (var i = 0; i < points.length; i++) {
122
+ var p1 = points[i];
123
+ var p2 = points[(i + 1) % points.length];
124
+ var mx = (p1.x + p2.x) / 2;
125
+ var my = (p1.y + p2.y) / 2;
126
+ // Bulge outward from center of rectangle
127
+ var cx = x + w / 2;
128
+ var cy = y + h / 2;
129
+ var dx = mx - cx;
130
+ var dy = my - cy;
131
+ var len = Math.sqrt(dx * dx + dy * dy) || 1;
132
+ var bulge = arcSize * 0.35;
133
+ var ctrlX = mx + (dx / len) * bulge;
134
+ var ctrlY = my + (dy / len) * bulge;
135
+ if (i === 0) {
136
+ ctx.moveTo(p1.x, p1.y);
137
+ }
138
+ ctx.quadraticCurveTo(ctrlX, ctrlY, p2.x, p2.y);
139
+ }
140
+ ctx.closePath();
141
+ ctx.stroke();
142
+ }
143
+ function drawFreehand(ctx, shape) {
144
+ var points = shape.points, color = shape.color, strokeWidth = shape.strokeWidth;
145
+ if (points.length < 2)
146
+ return;
147
+ ctx.strokeStyle = color;
148
+ ctx.lineWidth = strokeWidth;
149
+ ctx.lineCap = "round";
150
+ ctx.lineJoin = "round";
151
+ ctx.beginPath();
152
+ ctx.moveTo(points[0].x, points[0].y);
153
+ for (var i = 1; i < points.length; i++) {
154
+ ctx.lineTo(points[i].x, points[i].y);
155
+ }
156
+ ctx.stroke();
157
+ }
158
+ function drawText(ctx, shape) {
159
+ var position = shape.position, content = shape.content, color = shape.color, fontSize = shape.fontSize;
160
+ if (!content)
161
+ return;
162
+ ctx.font = "bold ".concat(fontSize, "px sans-serif");
163
+ ctx.textBaseline = "top";
164
+ // Black outline for contrast on construction drawings
165
+ ctx.strokeStyle = "#000000";
166
+ ctx.lineWidth = 2;
167
+ ctx.lineJoin = "round";
168
+ ctx.strokeText(content, position.x, position.y);
169
+ ctx.fillStyle = color;
170
+ ctx.fillText(content, position.x, position.y);
171
+ }
172
+ export function renderShape(ctx, shape) {
173
+ ctx.save();
174
+ switch (shape.type) {
175
+ case "arrow":
176
+ drawArrow(ctx, shape);
177
+ break;
178
+ case "rectangle":
179
+ drawRectangle(ctx, shape);
180
+ break;
181
+ case "ellipse":
182
+ drawEllipse(ctx, shape);
183
+ break;
184
+ case "cloud":
185
+ drawCloud(ctx, shape);
186
+ break;
187
+ case "freehand":
188
+ drawFreehand(ctx, shape);
189
+ break;
190
+ case "text":
191
+ drawText(ctx, shape);
192
+ break;
193
+ }
194
+ ctx.restore();
195
+ }
196
+ export function renderAllShapes(ctx, shapes) {
197
+ for (var _i = 0, shapes_1 = shapes; _i < shapes_1.length; _i++) {
198
+ var shape = shapes_1[_i];
199
+ renderShape(ctx, shape);
200
+ }
201
+ }
@@ -0,0 +1,81 @@
1
+ <!--
2
+ Simple Avatar Component for Messaging
3
+
4
+ Shows user avatar image with fallback to initials.
5
+ Used internally by messaging components.
6
+ -->
7
+
8
+ <script lang="ts">
9
+ interface Props {
10
+ name: string
11
+ avatarUrl?: string | null
12
+ size?: 'xs' | 'small' | 'medium' | 'large'
13
+ showStatus?: boolean
14
+ status?: 'online' | 'offline' | 'away' | 'busy'
15
+ class?: string
16
+ }
17
+
18
+ let {
19
+ name,
20
+ avatarUrl = null,
21
+ size = 'small',
22
+ showStatus = false,
23
+ status = 'offline',
24
+ class: className = '',
25
+ }: Props = $props()
26
+
27
+ const sizeClasses: Record<string, string> = {
28
+ xs: 'w-5 h-5 text-[10px]',
29
+ small: 'w-8 h-8 text-xs',
30
+ medium: 'w-10 h-10 text-sm',
31
+ large: 'w-12 h-12 text-base',
32
+ }
33
+
34
+ const statusDotSize: Record<string, string> = {
35
+ xs: 'w-1.5 h-1.5',
36
+ small: 'w-2 h-2',
37
+ medium: 'w-2.5 h-2.5',
38
+ large: 'w-3 h-3',
39
+ }
40
+
41
+ const statusColors: Record<string, string> = {
42
+ online: 'bg-success',
43
+ offline: 'bg-base-content/30',
44
+ away: 'bg-warning',
45
+ busy: 'bg-error',
46
+ }
47
+
48
+ function getInitials(fullName: string): string {
49
+ return fullName
50
+ .split(' ')
51
+ .map((part) => part[0])
52
+ .filter(Boolean)
53
+ .slice(0, 2)
54
+ .join('')
55
+ .toUpperCase()
56
+ }
57
+
58
+ const initials = $derived(getInitials(name))
59
+ </script>
60
+
61
+ <div class="relative inline-flex flex-shrink-0 {className}">
62
+ {#if avatarUrl}
63
+ <img
64
+ src={avatarUrl}
65
+ alt={name}
66
+ class="rounded-full object-cover {sizeClasses[size]}"
67
+ />
68
+ {:else}
69
+ <div
70
+ class="rounded-full bg-neutral text-neutral-content flex items-center justify-center font-medium {sizeClasses[size]}"
71
+ >
72
+ {initials}
73
+ </div>
74
+ {/if}
75
+
76
+ {#if showStatus}
77
+ <span
78
+ class="absolute bottom-0 right-0 rounded-full border-2 border-base-100 {statusDotSize[size]} {statusColors[status]}"
79
+ ></span>
80
+ {/if}
81
+ </div>
@@ -0,0 +1,11 @@
1
+ interface Props {
2
+ name: string;
3
+ avatarUrl?: string | null;
4
+ size?: 'xs' | 'small' | 'medium' | 'large';
5
+ showStatus?: boolean;
6
+ status?: 'online' | 'offline' | 'away' | 'busy';
7
+ class?: string;
8
+ }
9
+ declare const Avatar: import("svelte").Component<Props, {}, "">;
10
+ type Avatar = ReturnType<typeof Avatar>;
11
+ export default Avatar;
@@ -0,0 +1,163 @@
1
+ <!--
2
+ ChannelInfoModal Component
3
+
4
+ Displays channel information and allows admins to edit channel details.
5
+ All updates via callbacks.
6
+ -->
7
+
8
+ <script lang="ts">
9
+ import type { Channel, ChannelCallbacks } from '../../types/messaging'
10
+
11
+ interface Props {
12
+ channel: Channel & { memberCount?: number; createdBy?: string; creatorName?: string }
13
+ currentUserRole: 'admin' | 'moderator' | 'member'
14
+ open: boolean
15
+ channelCallbacks?: ChannelCallbacks
16
+ onclose?: () => void
17
+ onchannelupdate?: (updatedChannel: Channel) => void
18
+ onchanneldelete?: (channelId: string) => void
19
+ onmanagemembers?: () => void
20
+ }
21
+
22
+ let {
23
+ channel,
24
+ currentUserRole,
25
+ open = false,
26
+ channelCallbacks,
27
+ onclose,
28
+ onchannelupdate,
29
+ onchanneldelete,
30
+ onmanagemembers,
31
+ }: Props = $props()
32
+
33
+ let isEditing = $state(false)
34
+ let editName = $state('')
35
+ let editDescription = $state('')
36
+ let saving = $state(false)
37
+ let showDeleteConfirm = $state(false)
38
+ let error = $state<string | null>(null)
39
+
40
+ const isAdmin = $derived(currentUserRole === 'admin')
41
+
42
+ function startEdit() {
43
+ editName = channel.name
44
+ editDescription = channel.description || ''
45
+ isEditing = true
46
+ }
47
+
48
+ async function saveEdit() {
49
+ if (!channelCallbacks || !editName.trim()) return
50
+ saving = true
51
+ error = null
52
+ try {
53
+ const updated = await channelCallbacks.updateChannel(channel.id, {
54
+ name: editName.trim(),
55
+ description: editDescription.trim() || undefined,
56
+ })
57
+ onchannelupdate?.(updated)
58
+ isEditing = false
59
+ } catch (err) {
60
+ error = err instanceof Error ? err.message : 'Failed to update channel'
61
+ } finally {
62
+ saving = false
63
+ }
64
+ }
65
+
66
+ async function handleDelete() {
67
+ if (!channelCallbacks) return
68
+ try {
69
+ await channelCallbacks.deleteChannel(channel.id)
70
+ onchanneldelete?.(channel.id)
71
+ onclose?.()
72
+ } catch (err) {
73
+ error = err instanceof Error ? err.message : 'Failed to delete channel'
74
+ }
75
+ }
76
+
77
+ function formatDate(dateString: string): string {
78
+ return new Date(dateString).toLocaleDateString('en-US', { month: 'long', day: 'numeric', year: 'numeric' })
79
+ }
80
+ </script>
81
+
82
+ {#if open}
83
+ <dialog class="modal modal-open">
84
+ <div class="modal-box max-w-md">
85
+ <div class="flex items-center justify-between mb-4">
86
+ <h3 class="font-semibold">
87
+ {channel.emoji || '#'} {channel.name}
88
+ </h3>
89
+ <button class="btn btn-ghost btn-sm btn-circle" onclick={onclose}>✕</button>
90
+ </div>
91
+
92
+ {#if error}
93
+ <div class="alert alert-error mb-4"><span class="text-sm">{error}</span></div>
94
+ {/if}
95
+
96
+ {#if isEditing}
97
+ <div class="space-y-4">
98
+ <div>
99
+ <label for="channel-info-name" class="label"><span class="text-[0.8125rem] font-medium text-base-content/85">Name</span></label>
100
+ <input id="channel-info-name" type="text" bind:value={editName} class="input input-bordered w-full" maxlength="80" />
101
+ </div>
102
+ <div>
103
+ <label for="channel-info-description" class="label"><span class="text-[0.8125rem] font-medium text-base-content/85">Description</span></label>
104
+ <textarea id="channel-info-description" bind:value={editDescription} class="textarea textarea-bordered w-full" rows="3" maxlength="250"></textarea>
105
+ </div>
106
+ <div class="flex gap-2">
107
+ <button class="btn btn-primary btn-sm" onclick={saveEdit} disabled={saving || !editName.trim()}>
108
+ {#if saving}<span class="loading loading-spinner loading-xs"></span>{/if}
109
+ Save
110
+ </button>
111
+ <button class="btn btn-ghost btn-sm" onclick={() => (isEditing = false)}>Cancel</button>
112
+ </div>
113
+ </div>
114
+ {:else}
115
+ <div class="space-y-4">
116
+ <div>
117
+ <span class="text-[0.8125rem] text-base-content/45 tracking-[0.005em]">Type</span>
118
+ <p class="text-[0.9375rem]">{channel.type === 'private' ? '🔒 Private' : '# Public'}</p>
119
+ </div>
120
+
121
+ {#if channel.description}
122
+ <div>
123
+ <span class="text-[0.8125rem] text-base-content/45 tracking-[0.005em]">Description</span>
124
+ <p class="text-[0.9375rem]">{channel.description}</p>
125
+ </div>
126
+ {/if}
127
+
128
+ <div>
129
+ <span class="text-[0.8125rem] text-base-content/45 tracking-[0.005em]">Created</span>
130
+ <p class="text-[0.9375rem]">{formatDate(channel.createdAt)}{channel.creatorName ? ` by ${channel.creatorName}` : ''}</p>
131
+ </div>
132
+
133
+ <div class="flex flex-wrap gap-2">
134
+ {#if isAdmin}
135
+ <button class="btn btn-sm btn-ghost" onclick={startEdit}>✏ Edit</button>
136
+ {/if}
137
+ {#if onmanagemembers}
138
+ <button class="btn btn-sm btn-ghost" onclick={onmanagemembers}>👥 Members</button>
139
+ {/if}
140
+ {#if isAdmin}
141
+ {#if showDeleteConfirm}
142
+ <div class="flex items-center gap-2">
143
+ <span class="text-sm text-error">Delete this channel?</span>
144
+ <button class="btn btn-sm btn-error" onclick={handleDelete}>Confirm</button>
145
+ <button class="btn btn-sm btn-ghost" onclick={() => (showDeleteConfirm = false)}>Cancel</button>
146
+ </div>
147
+ {:else}
148
+ <button class="btn btn-sm btn-ghost text-error" onclick={() => (showDeleteConfirm = true)}>🗑 Delete</button>
149
+ {/if}
150
+ {/if}
151
+ </div>
152
+ </div>
153
+ {/if}
154
+
155
+ <div class="modal-action">
156
+ <button class="btn btn-ghost" onclick={onclose}>Close</button>
157
+ </div>
158
+ </div>
159
+ <form method="dialog" class="modal-backdrop">
160
+ <button onclick={onclose}>close</button>
161
+ </form>
162
+ </dialog>
163
+ {/if}
@@ -0,0 +1,18 @@
1
+ import type { Channel, ChannelCallbacks } from '../../types/messaging';
2
+ interface Props {
3
+ channel: Channel & {
4
+ memberCount?: number;
5
+ createdBy?: string;
6
+ creatorName?: string;
7
+ };
8
+ currentUserRole: 'admin' | 'moderator' | 'member';
9
+ open: boolean;
10
+ channelCallbacks?: ChannelCallbacks;
11
+ onclose?: () => void;
12
+ onchannelupdate?: (updatedChannel: Channel) => void;
13
+ onchanneldelete?: (channelId: string) => void;
14
+ onmanagemembers?: () => void;
15
+ }
16
+ declare const ChannelInfoModal: import("svelte").Component<Props, {}, "">;
17
+ type ChannelInfoModal = ReturnType<typeof ChannelInfoModal>;
18
+ export default ChannelInfoModal;
@@ -0,0 +1,107 @@
1
+ <!--
2
+ ChannelList Component
3
+
4
+ Displays list of channels with unread counts.
5
+ Purely presentational — receives channels via props.
6
+ -->
7
+
8
+ <script lang="ts">
9
+ import type { Channel } from '../../types/messaging'
10
+
11
+ interface Props {
12
+ channels: Channel[]
13
+ activeChannelId?: string | null
14
+ onchannelselect?: (event: { channelId: string }) => void
15
+ }
16
+
17
+ let {
18
+ channels = [],
19
+ activeChannelId = null,
20
+ onchannelselect,
21
+ }: Props = $props()
22
+
23
+ function selectChannel(channelId: string) {
24
+ onchannelselect?.({ channelId })
25
+ }
26
+
27
+ function getChannelIconColor(type: string) {
28
+ switch (type) {
29
+ case 'private': return 'text-warning'
30
+ case 'direct': return 'text-info'
31
+ default: return 'text-base-content/50'
32
+ }
33
+ }
34
+
35
+ const sortedChannels = $derived(
36
+ [...(channels || [])].sort((a, b) => {
37
+ const typeOrder = { public: 0, private: 1, direct: 2 } as Record<string, number>
38
+ const aOrder = typeOrder[a.type] ?? 3
39
+ const bOrder = typeOrder[b.type] ?? 3
40
+ if (aOrder !== bOrder) return aOrder - bOrder
41
+ return a.name.localeCompare(b.name)
42
+ }),
43
+ )
44
+ </script>
45
+
46
+ <div class="space-y-1">
47
+ {#each sortedChannels as channel}
48
+ {@const iconColor = getChannelIconColor(channel.type)}
49
+
50
+ <button
51
+ class="w-full flex items-center gap-2 px-2 py-1.5 text-left rounded hover:bg-base-300 transition-colors group"
52
+ class:bg-primary={activeChannelId === channel.id}
53
+ class:text-primary-content={activeChannelId === channel.id}
54
+ class:font-medium={activeChannelId === channel.id}
55
+ onclick={() => selectChannel(channel.id)}
56
+ >
57
+ <!-- Channel icon: emoji, lock, users, or # -->
58
+ <span
59
+ class="w-4 h-4 flex-shrink-0 text-sm flex items-center justify-center {activeChannelId === channel.id ? 'text-primary-content' : iconColor}"
60
+ >
61
+ {#if channel.type === 'private'}
62
+ 🔒
63
+ {:else if channel.type === 'direct'}
64
+ 👥
65
+ {:else}
66
+ {channel.emoji || '#'}
67
+ {/if}
68
+ </span>
69
+
70
+ <div class="flex-1 min-w-0">
71
+ <div class="flex items-center justify-between">
72
+ <span class="text-sm truncate">
73
+ {channel.name}
74
+ </span>
75
+
76
+ {#if channel.unreadCount && channel.unreadCount > 0}
77
+ <span class="badge badge-error badge-xs text-error-content">
78
+ {channel.unreadCount > 99 ? '99+' : channel.unreadCount}
79
+ </span>
80
+ {/if}
81
+ </div>
82
+
83
+ {#if channel.description && activeChannelId !== channel.id}
84
+ <div class="text-xs text-base-content/50 truncate">
85
+ {channel.description}
86
+ </div>
87
+ {/if}
88
+ </div>
89
+
90
+ <!-- Admin indicator -->
91
+ {#if channel.memberRole === 'admin'}
92
+ <div
93
+ class="w-2 h-2 rounded-full bg-warning opacity-0 group-hover:opacity-100 transition-opacity"
94
+ title="Channel Admin"
95
+ ></div>
96
+ {/if}
97
+ </button>
98
+ {/each}
99
+
100
+ {#if channels.length === 0}
101
+ <div class="text-center py-8 text-base-content/50">
102
+ <div class="text-2xl mb-2">#</div>
103
+ <div class="text-sm">No channels yet</div>
104
+ <div class="text-xs">Create one to get started</div>
105
+ </div>
106
+ {/if}
107
+ </div>
@@ -0,0 +1,11 @@
1
+ import type { Channel } from '../../types/messaging';
2
+ interface Props {
3
+ channels: Channel[];
4
+ activeChannelId?: string | null;
5
+ onchannelselect?: (event: {
6
+ channelId: string;
7
+ }) => void;
8
+ }
9
+ declare const ChannelList: import("svelte").Component<Props, {}, "">;
10
+ type ChannelList = ReturnType<typeof ChannelList>;
11
+ export default ChannelList;
@@ -0,0 +1,69 @@
1
+ <!--
2
+ ChannelMemberAvatarStack Component
3
+
4
+ Displays a stack of member avatars with overflow count.
5
+ Purely presentational — no backend dependencies.
6
+ -->
7
+
8
+ <script lang="ts">
9
+ import type { ChannelMember, PresenceState } from '../../types/messaging'
10
+ import Avatar from './Avatar.svelte'
11
+
12
+ interface Props {
13
+ members: ChannelMember[]
14
+ presenceState?: PresenceState
15
+ maxVisible?: number
16
+ size?: 'xs' | 'small' | 'medium' | 'large'
17
+ class?: string
18
+ }
19
+
20
+ let {
21
+ members = [],
22
+ presenceState = {},
23
+ maxVisible = 4,
24
+ size = 'small',
25
+ class: className = '',
26
+ }: Props = $props()
27
+
28
+ const visibleMembers = $derived(members.slice(0, maxVisible))
29
+ const remainingCount = $derived(Math.max(0, members.length - maxVisible))
30
+
31
+ function getStatus(member: ChannelMember): 'online' | 'offline' | 'away' | 'busy' {
32
+ const presence = presenceState[member.userId]
33
+ if (presence?.status === 'online' || presence?.status === 'available') {
34
+ return 'online'
35
+ }
36
+ return 'offline'
37
+ }
38
+
39
+ const overflowSize: Record<string, string> = {
40
+ xs: 'w-5 h-5 text-[8px]',
41
+ small: 'w-8 h-8 text-xs',
42
+ medium: 'w-10 h-10 text-sm',
43
+ large: 'w-12 h-12 text-base',
44
+ }
45
+ </script>
46
+
47
+ <div class="flex -space-x-2 {className}">
48
+ {#each visibleMembers as member (member.id)}
49
+ <div class="border-2 border-base-100 rounded-full">
50
+ <Avatar
51
+ name={member.fullName}
52
+ avatarUrl={member.avatarUrl}
53
+ {size}
54
+ showStatus={true}
55
+ status={getStatus(member)}
56
+ />
57
+ </div>
58
+ {/each}
59
+
60
+ {#if remainingCount > 0}
61
+ <div class="border-2 border-base-100 rounded-full">
62
+ <div
63
+ class="bg-neutral text-neutral-content rounded-full flex items-center justify-center {overflowSize[size]}"
64
+ >
65
+ <span class="font-bold">+{remainingCount}</span>
66
+ </div>
67
+ </div>
68
+ {/if}
69
+ </div>
@@ -0,0 +1,11 @@
1
+ import type { ChannelMember, PresenceState } from '../../types/messaging';
2
+ interface Props {
3
+ members: ChannelMember[];
4
+ presenceState?: PresenceState;
5
+ maxVisible?: number;
6
+ size?: 'xs' | 'small' | 'medium' | 'large';
7
+ class?: string;
8
+ }
9
+ declare const ChannelMemberAvatarStack: import("svelte").Component<Props, {}, "">;
10
+ type ChannelMemberAvatarStack = ReturnType<typeof ChannelMemberAvatarStack>;
11
+ export default ChannelMemberAvatarStack;