@joewinke/jatui 0.1.28 → 0.1.30

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 (42) hide show
  1. package/LICENSE +21 -0
  2. package/dist/actions/collapse.js +15 -29
  3. package/dist/actions/listNav.js +108 -137
  4. package/dist/actions/railNav.js +72 -89
  5. package/dist/components/markup.js +59 -62
  6. package/dist/components/session-nav/transcriptModel.js +49 -66
  7. package/dist/data/emojis.js +4 -4
  8. package/dist/dnd/DropIndicator.svelte +119 -0
  9. package/dist/dnd/DropIndicator.svelte.d.ts +16 -0
  10. package/dist/dnd/announce.d.ts +1 -0
  11. package/dist/dnd/announce.js +1 -0
  12. package/dist/dnd/autoScroll.d.ts +7 -0
  13. package/dist/dnd/autoScroll.js +25 -0
  14. package/dist/dnd/createDndBoard.svelte.d.ts +43 -0
  15. package/dist/dnd/createDndBoard.svelte.js +183 -0
  16. package/dist/dnd/createDndBoard.test.d.ts +1 -0
  17. package/dist/dnd/createDndBoard.test.js +185 -0
  18. package/dist/dnd/draggable.d.ts +13 -0
  19. package/dist/dnd/draggable.js +42 -0
  20. package/dist/dnd/dropTarget.d.ts +14 -0
  21. package/dist/dnd/dropTarget.js +31 -0
  22. package/dist/dnd/index.d.ts +13 -0
  23. package/dist/dnd/index.js +9 -0
  24. package/dist/dnd/types.d.ts +41 -0
  25. package/dist/dnd/types.js +14 -0
  26. package/dist/index.d.ts +5 -0
  27. package/dist/index.js +5 -0
  28. package/dist/stores/confirmDialog.svelte.js +5 -5
  29. package/dist/stores/inputDialog.svelte.js +5 -5
  30. package/dist/types/booking.js +4 -4
  31. package/dist/types/comments.js +2 -2
  32. package/dist/types/messaging.js +3 -3
  33. package/dist/types/pipeline.js +2 -2
  34. package/dist/utils/currency.js +2 -4
  35. package/dist/utils/daisyuiColors.js +25 -27
  36. package/dist/utils/dateFormatters.js +44 -44
  37. package/dist/utils/googleMapsLoader.js +46 -100
  38. package/dist/utils/mentionParser.js +30 -30
  39. package/dist/utils/phone.js +21 -27
  40. package/dist/utils/phoneFormat.js +9 -9
  41. package/dist/utils/taskUtils.js +1 -1
  42. package/package.json +25 -3
@@ -60,17 +60,6 @@
60
60
  * 3. Handle Left/Right in `onAction` and call `rail.bump(host)` on change:
61
61
  * onAction: (id, dir, host) => { formData[id] = cycle(opts[id], …, dir); rail.bump(host); }
62
62
  */
63
- var __assign = (this && this.__assign) || function () {
64
- __assign = Object.assign || function(t) {
65
- for (var s, i = 1, n = arguments.length; i < n; i++) {
66
- s = arguments[i];
67
- for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
68
- t[p] = s[p];
69
- }
70
- return t;
71
- };
72
- return __assign.apply(this, arguments);
73
- };
74
63
  // ─── Exported helpers ──────────────────────────────────────────────────────────
75
64
  /**
76
65
  * Cycle through `list`, returning the item `dir` steps from `current`.
@@ -84,40 +73,39 @@ var __assign = (this && this.__assign) || function () {
84
73
  export function cycle(list, current, dir) {
85
74
  if (list.length === 0)
86
75
  return current;
87
- var i = list.indexOf(current);
76
+ const i = list.indexOf(current);
88
77
  // When `current` is not in the list, treat it as if we're at position -1
89
78
  // so that `dir=1` lands at index 0 (the first item) and `dir=-1` lands at
90
79
  // the last item — matching the "not found → start from the beginning" idiom.
91
- var base = i === -1 ? -1 : i;
80
+ const base = i === -1 ? -1 : i;
92
81
  return list[(((base + dir) % list.length) + list.length) % list.length];
93
82
  }
94
83
  // ─── Internals ─────────────────────────────────────────────────────────────────
95
- var BUMP_DURATION_MS = 280;
96
- var RAIL_FOCUS_CLASS = 'rail-focus';
97
- var RAIL_CYCLED_CLASS = 'rail-cycled';
84
+ const BUMP_DURATION_MS = 280;
85
+ const RAIL_FOCUS_CLASS = 'rail-focus';
86
+ const RAIL_CYCLED_CLASS = 'rail-cycled';
98
87
  function resolveOrder(opts) {
99
88
  return typeof opts.order === 'function' ? opts.order() : opts.order;
100
89
  }
101
90
  function resolveAttr(opts) {
102
- var _a;
103
- return (_a = opts.attr) !== null && _a !== void 0 ? _a : 'data-rail-id';
91
+ return opts.attr ?? 'data-rail-id';
104
92
  }
105
93
  /** Return all currently-rendered rung hosts in the rail order. */
106
94
  function resolveHosts(optsRef) {
107
- var container = optsRef.current.container;
95
+ const { container } = optsRef.current;
108
96
  if (!container)
109
97
  return [];
110
- var attr = resolveAttr(optsRef.current);
111
- var order = resolveOrder(optsRef.current);
98
+ const attr = resolveAttr(optsRef.current);
99
+ const order = resolveOrder(optsRef.current);
112
100
  return order
113
- .map(function (id) { return container.querySelector("[".concat(attr, "=\"").concat(id, "\"]")); })
101
+ .map((id) => container.querySelector(`[${attr}="${id}"]`))
114
102
  // checkVisibility() correctly returns false for elements inside a closed <details>
115
103
  // (browsers keep offsetParent non-null for those, so that check alone is insufficient).
116
104
  // Where checkVisibility is unavailable (jsdom, older engines) treat the rung as
117
105
  // visible — a rendered element in `order` is navigable by default; only the
118
106
  // closed-<details> case needs the extra exclusion, and that only exists in a
119
107
  // real layout engine.
120
- .filter(function (el) { var _a, _b; return !!el && ((_b = (_a = el.checkVisibility) === null || _a === void 0 ? void 0 : _a.call(el)) !== null && _b !== void 0 ? _b : true); });
108
+ .filter((el) => !!el && (el.checkVisibility?.() ?? true));
121
109
  }
122
110
  /**
123
111
  * Focus the most appropriate control inside a rung host:
@@ -132,17 +120,17 @@ function resolveHosts(optsRef) {
132
120
  * 4. Last resort: focus the host itself (requires tabindex on the host).
133
121
  */
134
122
  function focusRailHost(host) {
135
- var _a;
136
123
  if (host.matches('input,select,textarea,button,[contenteditable="true"],[tabindex]')) {
137
124
  host.focus();
138
125
  return;
139
126
  }
140
- var inner = (_a = host.querySelector('[contenteditable="true"],input,select,textarea')) !== null && _a !== void 0 ? _a : host.querySelector('.sd-trigger,button');
141
- (inner !== null && inner !== void 0 ? inner : host).focus();
127
+ const inner = host.querySelector('[contenteditable="true"],input,select,textarea') ??
128
+ host.querySelector('.sd-trigger,button');
129
+ (inner ?? host).focus();
142
130
  }
143
131
  function makeController(optsRef) {
144
- var _currentRailId = '';
145
- var _lastFocusedHost = null;
132
+ let _currentRailId = '';
133
+ let _lastFocusedHost = null;
146
134
  function applyFocusClass(host) {
147
135
  if (_lastFocusedHost && _lastFocusedHost !== host) {
148
136
  _lastFocusedHost.classList.remove(RAIL_FOCUS_CLASS);
@@ -157,14 +145,14 @@ function makeController(optsRef) {
157
145
  host.classList.remove(RAIL_CYCLED_CLASS);
158
146
  void host.offsetWidth; // reflow so the animation restarts on rapid presses
159
147
  host.classList.add(RAIL_CYCLED_CLASS);
160
- setTimeout(function () { return host.classList.remove(RAIL_CYCLED_CLASS); }, BUMP_DURATION_MS);
148
+ setTimeout(() => host.classList.remove(RAIL_CYCLED_CLASS), BUMP_DURATION_MS);
161
149
  }
162
150
  function focus(id) {
163
- var container = optsRef.current.container;
151
+ const { container } = optsRef.current;
164
152
  if (!container)
165
153
  return;
166
- var attr = resolveAttr(optsRef.current);
167
- var host = container.querySelector("[".concat(attr, "=\"").concat(id, "\"]"));
154
+ const attr = resolveAttr(optsRef.current);
155
+ const host = container.querySelector(`[${attr}="${id}"]`);
168
156
  if (host) {
169
157
  focusRailHost(host);
170
158
  applyFocusClass(host);
@@ -175,21 +163,20 @@ function makeController(optsRef) {
175
163
  }
176
164
  }
177
165
  function moveRung(dir) {
178
- var _a;
179
- var hosts = resolveHosts(optsRef);
166
+ const hosts = resolveHosts(optsRef);
180
167
  if (hosts.length === 0)
181
168
  return;
182
- var attr = resolveAttr(optsRef.current);
183
- var ids = hosts.map(function (h) { return h.getAttribute(attr); });
169
+ const attr = resolveAttr(optsRef.current);
170
+ const ids = hosts.map((h) => h.getAttribute(attr));
184
171
  // Anchor on the actually-focused rung first. Fall back to the last tracked
185
172
  // rung (covers focus parked on the container after an Escape pop-out).
186
- var active = document.activeElement;
187
- var activeFocused = (_a = (active === null || active === void 0 ? void 0 : active.closest("[".concat(attr, "]")))) === null || _a === void 0 ? void 0 : _a.getAttribute(attr);
188
- var fromId = activeFocused && ids.includes(activeFocused) ? activeFocused : _currentRailId;
189
- var pos = fromId ? ids.indexOf(fromId) : -1;
173
+ const active = document.activeElement;
174
+ const activeFocused = (active?.closest(`[${attr}]`))?.getAttribute(attr);
175
+ const fromId = activeFocused && ids.includes(activeFocused) ? activeFocused : _currentRailId;
176
+ let pos = fromId ? ids.indexOf(fromId) : -1;
190
177
  if (pos === -1)
191
178
  pos = dir === 1 ? -1 : hosts.length;
192
- var next = (((pos + dir) % hosts.length) + hosts.length) % hosts.length;
179
+ const next = (((pos + dir) % hosts.length) + hosts.length) % hosts.length;
193
180
  focusRailHost(hosts[next]);
194
181
  applyFocusClass(hosts[next]);
195
182
  _currentRailId = ids[next];
@@ -197,10 +184,9 @@ function makeController(optsRef) {
197
184
  function refresh() {
198
185
  // Re-derive hosts; re-apply focus class if the current rung is still rendered
199
186
  // (DOM may have been reconstructed by Svelte's reactive updates).
200
- var hosts = resolveHosts(optsRef);
201
- var attr = resolveAttr(optsRef.current);
202
- for (var _i = 0, hosts_1 = hosts; _i < hosts_1.length; _i++) {
203
- var h = hosts_1[_i];
187
+ const hosts = resolveHosts(optsRef);
188
+ const attr = resolveAttr(optsRef.current);
189
+ for (const h of hosts) {
204
190
  if (h.getAttribute(attr) === _currentRailId) {
205
191
  applyFocusClass(h);
206
192
  return;
@@ -208,11 +194,10 @@ function makeController(optsRef) {
208
194
  }
209
195
  }
210
196
  function handleFocusin(event) {
211
- var _a;
212
- var attr = resolveAttr(optsRef.current);
213
- var order = resolveOrder(optsRef.current);
214
- var host = (_a = event.target) === null || _a === void 0 ? void 0 : _a.closest("[".concat(attr, "]"));
215
- var id = host === null || host === void 0 ? void 0 : host.getAttribute(attr);
197
+ const attr = resolveAttr(optsRef.current);
198
+ const order = resolveOrder(optsRef.current);
199
+ const host = event.target?.closest(`[${attr}]`);
200
+ const id = host?.getAttribute(attr);
216
201
  // Only track rungs that are in the rail order. Tabbing into an off-rail
217
202
  // control (e.g. a description textarea) leaves the last real rung intact
218
203
  // so Up / Down can resume from it after the user tabs back out.
@@ -222,18 +207,17 @@ function makeController(optsRef) {
222
207
  }
223
208
  }
224
209
  function handleKeydown(event) {
225
- var _a, _b, _c, _d, _e, _f, _g, _h;
226
- var opts = optsRef.current;
227
- var el = document.activeElement;
228
- var tag = el === null || el === void 0 ? void 0 : el.tagName;
229
- var isTextField = !!el &&
210
+ const opts = optsRef.current;
211
+ const el = document.activeElement;
212
+ const tag = el?.tagName;
213
+ const isTextField = !!el &&
230
214
  (tag === 'INPUT' || tag === 'TEXTAREA' || el.isContentEditable === true);
231
- var isMultiline = !!el && (tag === 'TEXTAREA' || el.isContentEditable === true);
232
- var hasMod = event.metaKey || event.ctrlKey || event.altKey;
215
+ const isMultiline = !!el && (tag === 'TEXTAREA' || el.isContentEditable === true);
216
+ const hasMod = event.metaKey || event.ctrlKey || event.altKey;
233
217
  // Modal trap — stop non-text keystrokes from bubbling to window-level page
234
218
  // listeners (e.g., a listNav attached to `window`). Text-field keystrokes
235
219
  // propagate normally so typing in the drawer doesn't interfere.
236
- var trap = opts.trap !== false;
220
+ const trap = opts.trap !== false;
237
221
  if (trap && !isTextField)
238
222
  event.stopPropagation();
239
223
  // Escape cascade:
@@ -241,33 +225,33 @@ function makeController(optsRef) {
241
225
  // 2. focus inside a descendant control → pop back to container
242
226
  // 3. focus already on the container (or just popped) → call onEscape
243
227
  if (event.key === 'Escape' && !hasMod) {
244
- var popoverOpen_1 = (_b = (_a = opts.isPopoverOpen) === null || _a === void 0 ? void 0 : _a.call(opts)) !== null && _b !== void 0 ? _b : false;
245
- if (popoverOpen_1) {
228
+ const popoverOpen = opts.isPopoverOpen?.() ?? false;
229
+ if (popoverOpen) {
246
230
  event.preventDefault();
247
- (_c = opts.onPopoverEscape) === null || _c === void 0 ? void 0 : _c.call(opts);
231
+ opts.onPopoverEscape?.();
248
232
  return true;
249
233
  }
250
- var container = opts.container;
234
+ const { container } = opts;
251
235
  if (container && el && el !== container && container.contains(el)) {
252
236
  event.preventDefault();
253
237
  container.focus();
254
238
  return true;
255
239
  }
256
240
  event.preventDefault();
257
- (_d = opts.onEscape) === null || _d === void 0 ? void 0 : _d.call(opts);
241
+ opts.onEscape?.();
258
242
  return true;
259
243
  }
260
244
  // Rail motion — suppressed when a popover is open or a modifier is held.
261
- var popoverOpen = (_f = (_e = opts.isPopoverOpen) === null || _e === void 0 ? void 0 : _e.call(opts)) !== null && _f !== void 0 ? _f : false;
245
+ const popoverOpen = opts.isPopoverOpen?.() ?? false;
262
246
  if (!hasMod && !popoverOpen) {
263
247
  // Vertical: ArrowDown / j step down; ArrowUp / k step up.
264
248
  // j / k are blocked in ANY text field; arrows are blocked only inside
265
249
  // multiline fields so the caret can still move line-by-line.
266
- var goDown = event.key === 'ArrowDown' || event.key === 'j';
267
- var goUp = event.key === 'ArrowUp' || event.key === 'k';
250
+ const goDown = event.key === 'ArrowDown' || event.key === 'j';
251
+ const goUp = event.key === 'ArrowUp' || event.key === 'k';
268
252
  if (goDown || goUp) {
269
- var isVimKey = event.key === 'j' || event.key === 'k';
270
- var blocked = isVimKey ? isTextField : isMultiline;
253
+ const isVimKey = event.key === 'j' || event.key === 'k';
254
+ const blocked = isVimKey ? isTextField : isMultiline;
271
255
  if (!blocked) {
272
256
  event.preventDefault();
273
257
  moveRung(goDown ? 1 : -1);
@@ -279,14 +263,15 @@ function makeController(optsRef) {
279
263
  if ((event.key === 'ArrowLeft' || event.key === 'ArrowRight') &&
280
264
  !isTextField) {
281
265
  event.preventDefault();
282
- var attr = resolveAttr(opts);
266
+ const attr = resolveAttr(opts);
283
267
  // Prefer the element that actually has DOM focus, then fall back to
284
268
  // the last tracked rung (focus parked on the container after pop-out).
285
- var focusedHost = (_g = (el === null || el === void 0 ? void 0 : el.closest("[".concat(attr, "]")))) !== null && _g !== void 0 ? _g : (opts.container && _currentRailId
286
- ? opts.container.querySelector("[".concat(attr, "=\"").concat(_currentRailId, "\"]"))
287
- : null);
269
+ const focusedHost = (el?.closest(`[${attr}]`)) ??
270
+ (opts.container && _currentRailId
271
+ ? opts.container.querySelector(`[${attr}="${_currentRailId}"]`)
272
+ : null);
288
273
  if (focusedHost && opts.onAction) {
289
- var rungId = (_h = focusedHost.getAttribute(attr)) !== null && _h !== void 0 ? _h : '';
274
+ const rungId = focusedHost.getAttribute(attr) ?? '';
290
275
  opts.onAction(rungId, event.key === 'ArrowRight' ? 1 : -1, focusedHost);
291
276
  }
292
277
  return true;
@@ -295,13 +280,13 @@ function makeController(optsRef) {
295
280
  return false;
296
281
  }
297
282
  return {
298
- handleKeydown: handleKeydown,
299
- handleFocusin: handleFocusin,
300
- moveRung: moveRung,
301
- focus: focus,
302
- refresh: refresh,
303
- currentId: function () { return _currentRailId; },
304
- bump: bump,
283
+ handleKeydown,
284
+ handleFocusin,
285
+ moveRung,
286
+ focus,
287
+ refresh,
288
+ currentId: () => _currentRailId,
289
+ bump,
305
290
  };
306
291
  }
307
292
  // ─── Composable ────────────────────────────────────────────────────────────────
@@ -316,7 +301,7 @@ function makeController(optsRef) {
316
301
  * panel.addEventListener('focusin', (e) => rail.handleFocusin(e));
317
302
  */
318
303
  export function createRailNav(options) {
319
- var ref = { current: options };
304
+ const ref = { current: options };
320
305
  return makeController(ref);
321
306
  }
322
307
  // ─── Action ────────────────────────────────────────────────────────────────────
@@ -331,10 +316,9 @@ export function createRailNav(options) {
331
316
  * <div data-rail-id="priority" tabindex="-1">…</div>
332
317
  * </div>
333
318
  */
334
- export var railNav = function (node, initial) {
335
- if (initial === void 0) { initial = { order: [] }; }
336
- var ref = { current: __assign(__assign({}, initial), { container: node }) };
337
- var controller = makeController(ref);
319
+ export const railNav = (node, initial = { order: [] }) => {
320
+ const ref = { current: { ...initial, container: node } };
321
+ const controller = makeController(ref);
338
322
  function onKeydown(e) {
339
323
  controller.handleKeydown(e);
340
324
  }
@@ -344,12 +328,11 @@ export var railNav = function (node, initial) {
344
328
  node.addEventListener('keydown', onKeydown);
345
329
  node.addEventListener('focusin', onFocusin);
346
330
  return {
347
- update: function (next) {
348
- if (next === void 0) { next = { order: [] }; }
349
- ref.current = __assign(__assign({}, next), { container: node });
331
+ update(next = { order: [] }) {
332
+ ref.current = { ...next, container: node };
350
333
  controller.refresh();
351
334
  },
352
- destroy: function () {
335
+ destroy() {
353
336
  node.removeEventListener('keydown', onKeydown);
354
337
  node.removeEventListener('focusin', onFocusin);
355
338
  },
@@ -1,6 +1,6 @@
1
1
  // Markup drawing primitives for construction drawing redlining
2
2
  // Adapted from JAT feedback annotation engine with cloud bubble tool added
3
- export var MARKUP_COLORS = [
3
+ export const MARKUP_COLORS = [
4
4
  "#ef4444",
5
5
  "#eab308",
6
6
  "#22c55e",
@@ -8,13 +8,13 @@ export var MARKUP_COLORS = [
8
8
  "#ffffff",
9
9
  "#111827",
10
10
  ];
11
- export var DEFAULT_STROKE_WIDTH = 3;
12
- var _nextId = 1;
11
+ export const DEFAULT_STROKE_WIDTH = 3;
12
+ let _nextId = 1;
13
13
  export function nextShapeId() {
14
14
  return _nextId++;
15
15
  }
16
16
  function drawArrow(ctx, shape) {
17
- var start = shape.start, end = shape.end, color = shape.color, strokeWidth = shape.strokeWidth;
17
+ const { start, end, color, strokeWidth } = shape;
18
18
  ctx.strokeStyle = color;
19
19
  ctx.lineWidth = strokeWidth;
20
20
  ctx.lineCap = "round";
@@ -24,9 +24,9 @@ function drawArrow(ctx, shape) {
24
24
  ctx.lineTo(end.x, end.y);
25
25
  ctx.stroke();
26
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;
27
+ const angle = Math.atan2(end.y - start.y, end.x - start.x);
28
+ const headLen = 14;
29
+ const headAngle = Math.PI / 7;
30
30
  ctx.beginPath();
31
31
  ctx.moveTo(end.x, end.y);
32
32
  ctx.lineTo(end.x - headLen * Math.cos(angle - headAngle), end.y - headLen * Math.sin(angle - headAngle));
@@ -35,24 +35,24 @@ function drawArrow(ctx, shape) {
35
35
  ctx.stroke();
36
36
  }
37
37
  function drawRectangle(ctx, shape) {
38
- var start = shape.start, end = shape.end, color = shape.color, strokeWidth = shape.strokeWidth;
38
+ const { start, end, color, strokeWidth } = shape;
39
39
  ctx.strokeStyle = color;
40
40
  ctx.lineWidth = strokeWidth;
41
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);
42
+ const x = Math.min(start.x, end.x);
43
+ const y = Math.min(start.y, end.y);
44
+ const w = Math.abs(end.x - start.x);
45
+ const h = Math.abs(end.y - start.y);
46
46
  ctx.strokeRect(x, y, w, h);
47
47
  }
48
48
  function drawEllipse(ctx, shape) {
49
- var start = shape.start, end = shape.end, color = shape.color, strokeWidth = shape.strokeWidth;
49
+ const { start, end, color, strokeWidth } = shape;
50
50
  ctx.strokeStyle = color;
51
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;
52
+ const cx = (start.x + end.x) / 2;
53
+ const cy = (start.y + end.y) / 2;
54
+ const rx = Math.abs(end.x - start.x) / 2;
55
+ const ry = Math.abs(end.y - start.y) / 2;
56
56
  if (rx < 1 || ry < 1)
57
57
  return;
58
58
  ctx.beginPath();
@@ -64,53 +64,51 @@ function drawEllipse(ctx, shape) {
64
64
  * Standard construction markup tool for highlighting areas of change.
65
65
  */
66
66
  function drawCloud(ctx, shape) {
67
- var start = shape.start, end = shape.end, color = shape.color, strokeWidth = shape.strokeWidth;
67
+ const { start, end, color, strokeWidth } = shape;
68
68
  ctx.strokeStyle = color;
69
69
  ctx.lineWidth = strokeWidth;
70
70
  ctx.lineCap = "round";
71
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);
72
+ const x = Math.min(start.x, end.x);
73
+ const y = Math.min(start.y, end.y);
74
+ const w = Math.abs(end.x - start.x);
75
+ const h = Math.abs(end.y - start.y);
76
76
  if (w < 4 || h < 4)
77
77
  return;
78
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));
79
+ const arcSize = Math.min(w, h, 30); // arc diameter adapts to shape size
80
+ const perimeter = 2 * (w + h);
81
+ const numArcs = Math.max(8, Math.round(perimeter / arcSize));
82
82
  // Collect points along the rectangle perimeter
83
- var points = [];
84
- var segLengths = [w, h, w, h];
85
- var segDirs = [
83
+ const points = [];
84
+ const segLengths = [w, h, w, h];
85
+ const segDirs = [
86
86
  { dx: 1, dy: 0 },
87
87
  { dx: 0, dy: 1 },
88
88
  { dx: -1, dy: 0 },
89
89
  { dx: 0, dy: -1 },
90
90
  ];
91
- var segStarts = [
92
- { x: x, y: y },
93
- { x: x + w, y: y },
91
+ const segStarts = [
92
+ { x, y },
93
+ { x: x + w, y },
94
94
  { x: x + w, y: y + h },
95
- { x: x, y: y + h },
95
+ { x, y: y + h },
96
96
  ];
97
- var totalLen = 0;
98
- for (var _i = 0, segLengths_1 = segLengths; _i < segLengths_1.length; _i++) {
99
- var l = segLengths_1[_i];
97
+ let totalLen = 0;
98
+ for (const l of segLengths)
100
99
  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;
100
+ const step = totalLen / numArcs;
101
+ let dist = 0;
102
+ let segIdx = 0;
103
+ let segDist = 0;
104
+ for (let i = 0; i < numArcs; i++) {
105
+ const target = i * step;
108
106
  while (dist + segLengths[segIdx] - segDist < target && segIdx < 3) {
109
107
  dist += segLengths[segIdx] - segDist;
110
108
  segIdx++;
111
109
  segDist = 0;
112
110
  }
113
- var remaining = target - dist;
111
+ const remaining = target - dist;
114
112
  points.push({
115
113
  x: segStarts[segIdx].x + segDirs[segIdx].dx * (segDist + remaining),
116
114
  y: segStarts[segIdx].y + segDirs[segIdx].dy * (segDist + remaining),
@@ -118,20 +116,20 @@ function drawCloud(ctx, shape) {
118
116
  }
119
117
  // Draw arcs between consecutive points
120
118
  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;
119
+ for (let i = 0; i < points.length; i++) {
120
+ const p1 = points[i];
121
+ const p2 = points[(i + 1) % points.length];
122
+ const mx = (p1.x + p2.x) / 2;
123
+ const my = (p1.y + p2.y) / 2;
126
124
  // 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;
125
+ const cx = x + w / 2;
126
+ const cy = y + h / 2;
127
+ const dx = mx - cx;
128
+ const dy = my - cy;
129
+ const len = Math.sqrt(dx * dx + dy * dy) || 1;
130
+ const bulge = arcSize * 0.35;
131
+ const ctrlX = mx + (dx / len) * bulge;
132
+ const ctrlY = my + (dy / len) * bulge;
135
133
  if (i === 0) {
136
134
  ctx.moveTo(p1.x, p1.y);
137
135
  }
@@ -141,7 +139,7 @@ function drawCloud(ctx, shape) {
141
139
  ctx.stroke();
142
140
  }
143
141
  function drawFreehand(ctx, shape) {
144
- var points = shape.points, color = shape.color, strokeWidth = shape.strokeWidth;
142
+ const { points, color, strokeWidth } = shape;
145
143
  if (points.length < 2)
146
144
  return;
147
145
  ctx.strokeStyle = color;
@@ -150,16 +148,16 @@ function drawFreehand(ctx, shape) {
150
148
  ctx.lineJoin = "round";
151
149
  ctx.beginPath();
152
150
  ctx.moveTo(points[0].x, points[0].y);
153
- for (var i = 1; i < points.length; i++) {
151
+ for (let i = 1; i < points.length; i++) {
154
152
  ctx.lineTo(points[i].x, points[i].y);
155
153
  }
156
154
  ctx.stroke();
157
155
  }
158
156
  function drawText(ctx, shape) {
159
- var position = shape.position, content = shape.content, color = shape.color, fontSize = shape.fontSize;
157
+ const { position, content, color, fontSize } = shape;
160
158
  if (!content)
161
159
  return;
162
- ctx.font = "bold ".concat(fontSize, "px sans-serif");
160
+ ctx.font = `bold ${fontSize}px sans-serif`;
163
161
  ctx.textBaseline = "top";
164
162
  // Black outline for contrast on construction drawings
165
163
  ctx.strokeStyle = "#000000";
@@ -194,8 +192,7 @@ export function renderShape(ctx, shape) {
194
192
  ctx.restore();
195
193
  }
196
194
  export function renderAllShapes(ctx, shapes) {
197
- for (var _i = 0, shapes_1 = shapes; _i < shapes_1.length; _i++) {
198
- var shape = shapes_1[_i];
195
+ for (const shape of shapes) {
199
196
  renderShape(ctx, shape);
200
197
  }
201
198
  }