@tldraw/commenting 5.3.0-next.d7d8ced023d5 → 5.3.0-next.df310c6196c0
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.
- package/commenting.css +85 -87
- package/dist-cjs/canvas/anchor-lifecycle.js +2 -2
- package/dist-cjs/canvas/anchor-lifecycle.js.map +2 -2
- package/dist-cjs/{ui/tooltip-button.js → canvas/canvas-events.js} +16 -13
- package/dist-cjs/canvas/canvas-events.js.map +7 -0
- package/dist-cjs/canvas/cluster-badge.js +121 -0
- package/dist-cjs/canvas/cluster-badge.js.map +7 -0
- package/dist-cjs/canvas/cluster-fade.js +85 -0
- package/dist-cjs/canvas/cluster-fade.js.map +7 -0
- package/dist-cjs/canvas/cluster-input.js +51 -1
- package/dist-cjs/canvas/cluster-input.js.map +2 -2
- package/dist-cjs/canvas/cluster-model.js +276 -0
- package/dist-cjs/canvas/cluster-model.js.map +7 -0
- package/dist-cjs/canvas/comment-mutations.js +57 -28
- package/dist-cjs/canvas/comment-mutations.js.map +2 -2
- package/dist-cjs/canvas/comment-reactions.js +4 -7
- package/dist-cjs/canvas/comment-reactions.js.map +2 -2
- package/dist-cjs/canvas/comment-render.js +4 -3
- package/dist-cjs/canvas/comment-render.js.map +2 -2
- package/dist-cjs/canvas/comment-store.js.map +2 -2
- package/dist-cjs/canvas/comment-tool.js +12 -7
- package/dist-cjs/canvas/comment-tool.js.map +2 -2
- package/dist-cjs/canvas/comments-filter-menu.js +11 -13
- package/dist-cjs/canvas/comments-filter-menu.js.map +2 -2
- package/dist-cjs/canvas/comments-overflow-menu.js +10 -2
- package/dist-cjs/canvas/comments-overflow-menu.js.map +2 -2
- package/dist-cjs/canvas/comments-overlay.js +81 -871
- package/dist-cjs/canvas/comments-overlay.js.map +3 -3
- package/dist-cjs/canvas/comments-sidebar.js +16 -11
- package/dist-cjs/canvas/comments-sidebar.js.map +2 -2
- package/dist-cjs/canvas/comments-visibility-toggle.js +5 -25
- package/dist-cjs/canvas/comments-visibility-toggle.js.map +2 -2
- package/dist-cjs/canvas/context.js.map +1 -1
- package/dist-cjs/canvas/hooks.js +8 -2
- package/dist-cjs/canvas/hooks.js.map +2 -2
- package/dist-cjs/canvas/license.js.map +1 -1
- package/dist-cjs/canvas/mobile-placement.js +91 -0
- package/dist-cjs/canvas/mobile-placement.js.map +7 -0
- package/dist-cjs/canvas/options.js +39 -1
- package/dist-cjs/canvas/options.js.map +2 -2
- package/dist-cjs/canvas/pending-composer.js +135 -0
- package/dist-cjs/canvas/pending-composer.js.map +7 -0
- package/dist-cjs/canvas/pin-stacking.js +26 -1
- package/dist-cjs/canvas/pin-stacking.js.map +2 -2
- package/dist-cjs/canvas/region-box.js +113 -0
- package/dist-cjs/canvas/region-box.js.map +7 -0
- package/dist-cjs/canvas/state.js.map +2 -2
- package/dist-cjs/canvas/thread-pin.js +304 -0
- package/dist-cjs/canvas/thread-pin.js.map +7 -0
- package/dist-cjs/canvas/thread-preview.js +41 -49
- package/dist-cjs/canvas/thread-preview.js.map +2 -2
- package/dist-cjs/canvas/thread-stack.js +7 -7
- package/dist-cjs/canvas/thread-stack.js.map +2 -2
- package/dist-cjs/canvas/thread-state.js +15 -0
- package/dist-cjs/canvas/thread-state.js.map +2 -2
- package/dist-cjs/canvas/thread-view.js +120 -67
- package/dist-cjs/canvas/thread-view.js.map +3 -3
- package/dist-cjs/clustering/computeClusterTable.js +2 -2
- package/dist-cjs/clustering/computeClusterTable.js.map +2 -2
- package/dist-cjs/clustering/replay.js +89 -22
- package/dist-cjs/clustering/replay.js.map +3 -3
- package/dist-cjs/clustering/runtime.js +19 -12
- package/dist-cjs/clustering/runtime.js.map +2 -2
- package/dist-cjs/clustering/types.js.map +1 -1
- package/dist-cjs/index.d.ts +326 -203
- package/dist-cjs/index.js +7 -1
- package/dist-cjs/index.js.map +2 -2
- package/dist-cjs/ui/byline.js +5 -3
- package/dist-cjs/ui/byline.js.map +2 -2
- package/dist-cjs/ui/comment-composer.js +2 -18
- package/dist-cjs/ui/comment-composer.js.map +2 -2
- package/dist-cjs/ui/comment-pin.js +2 -15
- package/dist-cjs/ui/comment-pin.js.map +2 -2
- package/dist-cjs/ui/comments-list.js +11 -24
- package/dist-cjs/ui/comments-list.js.map +2 -2
- package/dist-cjs/ui/format-time.js +13 -1
- package/dist-cjs/ui/format-time.js.map +2 -2
- package/dist-cjs/ui/icons.js +138 -0
- package/dist-cjs/ui/icons.js.map +7 -0
- package/dist-cjs/ui/reaction-picker.js +11 -13
- package/dist-cjs/ui/reaction-picker.js.map +2 -2
- package/dist-cjs/ui/reaction.js +3 -4
- package/dist-cjs/ui/reaction.js.map +2 -2
- package/dist-cjs/ui/send-button.js +2 -9
- package/dist-cjs/ui/send-button.js.map +2 -2
- package/dist-cjs/ui/visual-viewport.js +32 -0
- package/dist-cjs/ui/visual-viewport.js.map +7 -0
- package/dist-esm/canvas/anchor-lifecycle.mjs +3 -3
- package/dist-esm/canvas/anchor-lifecycle.mjs.map +2 -2
- package/dist-esm/canvas/canvas-events.mjs +15 -0
- package/dist-esm/canvas/canvas-events.mjs.map +7 -0
- package/dist-esm/canvas/cluster-badge.mjs +106 -0
- package/dist-esm/canvas/cluster-badge.mjs.map +7 -0
- package/dist-esm/canvas/cluster-fade.mjs +65 -0
- package/dist-esm/canvas/cluster-fade.mjs.map +7 -0
- package/dist-esm/canvas/cluster-input.mjs +52 -2
- package/dist-esm/canvas/cluster-input.mjs.map +2 -2
- package/dist-esm/canvas/cluster-model.mjs +260 -0
- package/dist-esm/canvas/cluster-model.mjs.map +7 -0
- package/dist-esm/canvas/comment-mutations.mjs +57 -28
- package/dist-esm/canvas/comment-mutations.mjs.map +2 -2
- package/dist-esm/canvas/comment-reactions.mjs +5 -12
- package/dist-esm/canvas/comment-reactions.mjs.map +2 -2
- package/dist-esm/canvas/comment-render.mjs +5 -4
- package/dist-esm/canvas/comment-render.mjs.map +2 -2
- package/dist-esm/canvas/comment-store.mjs.map +2 -2
- package/dist-esm/canvas/comment-tool.mjs +12 -7
- package/dist-esm/canvas/comment-tool.mjs.map +2 -2
- package/dist-esm/canvas/comments-filter-menu.mjs +12 -13
- package/dist-esm/canvas/comments-filter-menu.mjs.map +2 -2
- package/dist-esm/canvas/comments-overflow-menu.mjs +11 -2
- package/dist-esm/canvas/comments-overflow-menu.mjs.map +2 -2
- package/dist-esm/canvas/comments-overlay.mjs +58 -879
- package/dist-esm/canvas/comments-overlay.mjs.map +3 -3
- package/dist-esm/canvas/comments-sidebar.mjs +18 -14
- package/dist-esm/canvas/comments-sidebar.mjs.map +2 -2
- package/dist-esm/canvas/comments-visibility-toggle.mjs +5 -25
- package/dist-esm/canvas/comments-visibility-toggle.mjs.map +2 -2
- package/dist-esm/canvas/hooks.mjs +9 -2
- package/dist-esm/canvas/hooks.mjs.map +2 -2
- package/dist-esm/canvas/license.mjs.map +1 -1
- package/dist-esm/canvas/mobile-placement.mjs +71 -0
- package/dist-esm/canvas/mobile-placement.mjs.map +7 -0
- package/dist-esm/canvas/options.mjs +39 -1
- package/dist-esm/canvas/options.mjs.map +2 -2
- package/dist-esm/canvas/pending-composer.mjs +127 -0
- package/dist-esm/canvas/pending-composer.mjs.map +7 -0
- package/dist-esm/canvas/pin-stacking.mjs +26 -1
- package/dist-esm/canvas/pin-stacking.mjs.map +2 -2
- package/dist-esm/canvas/region-box.mjs +93 -0
- package/dist-esm/canvas/region-box.mjs.map +7 -0
- package/dist-esm/canvas/state.mjs.map +2 -2
- package/dist-esm/canvas/thread-pin.mjs +310 -0
- package/dist-esm/canvas/thread-pin.mjs.map +7 -0
- package/dist-esm/canvas/thread-preview.mjs +42 -49
- package/dist-esm/canvas/thread-preview.mjs.map +2 -2
- package/dist-esm/canvas/thread-stack.mjs +8 -9
- package/dist-esm/canvas/thread-stack.mjs.map +2 -2
- package/dist-esm/canvas/thread-state.mjs +15 -0
- package/dist-esm/canvas/thread-state.mjs.map +2 -2
- package/dist-esm/canvas/thread-view.mjs +131 -72
- package/dist-esm/canvas/thread-view.mjs.map +3 -3
- package/dist-esm/clustering/computeClusterTable.mjs +2 -2
- package/dist-esm/clustering/computeClusterTable.mjs.map +2 -2
- package/dist-esm/clustering/replay.mjs +89 -22
- package/dist-esm/clustering/replay.mjs.map +3 -3
- package/dist-esm/clustering/runtime.mjs +19 -12
- package/dist-esm/clustering/runtime.mjs.map +2 -2
- package/dist-esm/index.d.mts +326 -203
- package/dist-esm/index.mjs +16 -3
- package/dist-esm/index.mjs.map +2 -2
- package/dist-esm/ui/byline.mjs +6 -4
- package/dist-esm/ui/byline.mjs.map +2 -2
- package/dist-esm/ui/comment-composer.mjs +2 -18
- package/dist-esm/ui/comment-composer.mjs.map +2 -2
- package/dist-esm/ui/comment-pin.mjs +2 -15
- package/dist-esm/ui/comment-pin.mjs.map +2 -2
- package/dist-esm/ui/comments-list.mjs +10 -23
- package/dist-esm/ui/comments-list.mjs.map +2 -2
- package/dist-esm/ui/format-time.mjs +13 -1
- package/dist-esm/ui/format-time.mjs.map +2 -2
- package/dist-esm/ui/icons.mjs +118 -0
- package/dist-esm/ui/icons.mjs.map +7 -0
- package/dist-esm/ui/reaction-picker.mjs +12 -13
- package/dist-esm/ui/reaction-picker.mjs.map +2 -2
- package/dist-esm/ui/reaction.mjs +3 -4
- package/dist-esm/ui/reaction.mjs.map +2 -2
- package/dist-esm/ui/send-button.mjs +2 -9
- package/dist-esm/ui/send-button.mjs.map +2 -2
- package/dist-esm/ui/visual-viewport.mjs +12 -0
- package/dist-esm/ui/visual-viewport.mjs.map +7 -0
- package/package.json +7 -6
- package/src/canvas/anchor-lifecycle.test.ts +2 -2
- package/src/canvas/anchor-lifecycle.ts +25 -39
- package/src/canvas/canvas-events.ts +18 -0
- package/src/canvas/canvas.css +5 -11
- package/src/canvas/cluster-badge.tsx +133 -0
- package/src/canvas/cluster-fade.ts +102 -0
- package/src/canvas/cluster-input.test.ts +148 -14
- package/src/canvas/cluster-input.ts +92 -5
- package/src/canvas/cluster-model.test.ts +88 -0
- package/src/canvas/cluster-model.ts +421 -0
- package/src/canvas/comment-mutations.test.ts +46 -4
- package/src/canvas/comment-mutations.ts +115 -104
- package/src/canvas/comment-reactions.test.ts +22 -0
- package/src/canvas/comment-reactions.tsx +33 -32
- package/src/canvas/comment-render.ts +8 -8
- package/src/canvas/comment-store.ts +12 -17
- package/src/canvas/comment-tool.test.ts +109 -4
- package/src/canvas/comment-tool.tsx +31 -19
- package/src/canvas/comments-filter-menu.tsx +10 -17
- package/src/canvas/comments-overflow-menu.tsx +8 -3
- package/src/canvas/comments-overlay.tsx +160 -1345
- package/src/canvas/comments-sidebar.tsx +40 -19
- package/src/canvas/comments-visibility-toggle.tsx +6 -30
- package/src/canvas/context.ts +4 -3
- package/src/canvas/hooks.test.ts +94 -0
- package/src/canvas/hooks.ts +17 -3
- package/src/canvas/license.ts +1 -1
- package/src/canvas/mobile-placement.ts +115 -0
- package/src/canvas/options.test.ts +124 -2
- package/src/canvas/options.ts +192 -58
- package/src/canvas/pending-composer.tsx +161 -0
- package/src/canvas/pin-stacking.test.ts +111 -1
- package/src/canvas/pin-stacking.ts +46 -0
- package/src/canvas/region-box.tsx +124 -0
- package/src/canvas/state.ts +20 -28
- package/src/canvas/thread-pin.tsx +419 -0
- package/src/canvas/thread-preview.tsx +77 -87
- package/src/canvas/thread-stack.tsx +19 -29
- package/src/canvas/thread-state.test.ts +51 -0
- package/src/canvas/thread-state.ts +56 -23
- package/src/canvas/thread-view.test.ts +72 -0
- package/src/canvas/thread-view.tsx +231 -133
- package/src/clustering/computeClusterTable.ts +12 -3
- package/src/clustering/replay.test.ts +0 -7
- package/src/clustering/replay.ts +131 -32
- package/src/clustering/runtime.test.ts +50 -6
- package/src/clustering/runtime.ts +42 -39
- package/src/clustering/schedule.test.ts +0 -6
- package/src/clustering/screen-offsets.test.ts +171 -0
- package/src/clustering/types.ts +10 -0
- package/src/index.ts +15 -2
- package/src/ui/byline.tsx +16 -6
- package/src/ui/comment-composer.tsx +25 -69
- package/src/ui/comment-pin.tsx +3 -16
- package/src/ui/comments-list.tsx +40 -29
- package/src/ui/comments.css +80 -76
- package/src/ui/format-time.test.ts +69 -0
- package/src/ui/format-time.ts +22 -2
- package/src/ui/icons.tsx +138 -0
- package/src/ui/reaction-picker.tsx +9 -16
- package/src/ui/reaction.tsx +10 -6
- package/src/ui/send-button.tsx +3 -8
- package/src/ui/visual-viewport.test.ts +36 -0
- package/src/ui/visual-viewport.ts +27 -0
- package/dist-cjs/ui/tooltip-button.js.map +0 -7
- package/dist-esm/ui/tooltip-button.mjs +0 -12
- package/dist-esm/ui/tooltip-button.mjs.map +0 -7
- package/src/ui/tooltip-button.tsx +0 -20
package/commenting.css
CHANGED
|
@@ -198,6 +198,15 @@
|
|
|
198
198
|
overflow-wrap: anywhere;
|
|
199
199
|
}
|
|
200
200
|
|
|
201
|
+
/* A comment body is read-only but its text must stay selectable so it can be copied. tldraw's
|
|
202
|
+
container sets `user-select: none` on every descendant (`.tl-container *`) to keep canvas gestures
|
|
203
|
+
from selecting UI text, so the body and its rendered children have to opt back in. */
|
|
204
|
+
.tlui-cmt-text,
|
|
205
|
+
.tlui-cmt-text * {
|
|
206
|
+
-webkit-user-select: text;
|
|
207
|
+
user-select: text;
|
|
208
|
+
}
|
|
209
|
+
|
|
201
210
|
.tlui-cmt-edited {
|
|
202
211
|
font-style: italic;
|
|
203
212
|
}
|
|
@@ -311,8 +320,6 @@
|
|
|
311
320
|
pointer-events: none;
|
|
312
321
|
white-space: nowrap;
|
|
313
322
|
width: max-content;
|
|
314
|
-
max-height: none;
|
|
315
|
-
overflow: visible;
|
|
316
323
|
}
|
|
317
324
|
|
|
318
325
|
.tlui-cmt-input {
|
|
@@ -352,6 +359,17 @@
|
|
|
352
359
|
pointer-events: none;
|
|
353
360
|
}
|
|
354
361
|
|
|
362
|
+
/* iOS Safari zooms the whole page in when a text input with a sub-16px font takes focus, and doesn't
|
|
363
|
+
zoom back out. Raising the font to 16px on touch devices suppresses that without disabling
|
|
364
|
+
pinch-zoom of the canvas (which a `maximum-scale` viewport lock would). Must come after both base
|
|
365
|
+
rules above: a media query adds no specificity, so it only wins the placeholder on source order. */
|
|
366
|
+
@media (pointer: coarse) {
|
|
367
|
+
.tlui-cmt-input,
|
|
368
|
+
.tlui-cmt-input__placeholder {
|
|
369
|
+
font-size: 16px;
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
|
|
355
373
|
/* A bare up-arrow icon button (matching the placement composer): no fill by default, a grey square
|
|
356
374
|
on hover, grey while there's nothing to send and dark once there is. */
|
|
357
375
|
.tlui-cmt-send {
|
|
@@ -528,12 +546,17 @@
|
|
|
528
546
|
font-size: 10px;
|
|
529
547
|
font-weight: 400;
|
|
530
548
|
color: var(--tl-color-text-1);
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
/* An inert pill renders as a span (e.g. in a notification row); only the button variant is
|
|
552
|
+
clickable, so only it gets the pointer cursor and hover shade. */
|
|
553
|
+
button.tlui-cmt-reaction {
|
|
531
554
|
cursor: pointer;
|
|
532
555
|
}
|
|
533
556
|
|
|
534
557
|
/* Hover deepens the grey — no border to tint now. Mixing toward the text colour is theme-aware:
|
|
535
558
|
it darkens the fill in light mode and lightens it in dark, tracking the pill's own contrast. */
|
|
536
|
-
.tlui-cmt-reaction:hover {
|
|
559
|
+
button.tlui-cmt-reaction:hover {
|
|
537
560
|
background: var(--tl-color-muted-1);
|
|
538
561
|
}
|
|
539
562
|
|
|
@@ -543,14 +566,15 @@
|
|
|
543
566
|
background: color-mix(in srgb, var(--tl-color-selected) 28%, var(--tl-color-panel));
|
|
544
567
|
}
|
|
545
568
|
|
|
546
|
-
.tlui-cmt-reaction--active:hover {
|
|
569
|
+
button.tlui-cmt-reaction--active:hover {
|
|
547
570
|
background: color-mix(in srgb, var(--tl-color-selected) 38%, var(--tl-color-panel));
|
|
548
571
|
}
|
|
549
572
|
|
|
550
|
-
/*
|
|
573
|
+
/* The emoji renders larger than the 10px count, but its pinned line-height keeps the pill the
|
|
574
|
+
count's height — the glyph overflows the line box rather than growing it. */
|
|
551
575
|
.tlui-cmt-reaction__emoji {
|
|
552
|
-
font-size:
|
|
553
|
-
line-height:
|
|
576
|
+
font-size: 13px;
|
|
577
|
+
line-height: 10px;
|
|
554
578
|
}
|
|
555
579
|
|
|
556
580
|
.tlui-cmt-reaction__count {
|
|
@@ -653,7 +677,8 @@
|
|
|
653
677
|
--tlui-cmt-marker-transition: 0.08s ease;
|
|
654
678
|
--tlui-cmt-thread-padding: 12px;
|
|
655
679
|
--tlui-cmt-thread-gap: 0px;
|
|
656
|
-
|
|
680
|
+
/* Hit-area width of one .tlui-cmt-thread__action button (drawn as a 32px square inset 4px). */
|
|
681
|
+
--tlui-cmt-thread-action-size: 40px;
|
|
657
682
|
/* A floating pill's edge — its hairline ring is the only thing separating it from the panel it
|
|
658
683
|
overlaps, so the ring is a theme token rather than a literal black: a near-black hairline
|
|
659
684
|
vanishes on a dark panel, where the divider token lightens instead. The drop below it stays
|
|
@@ -716,47 +741,28 @@
|
|
|
716
741
|
space-between alone would leave them at the left). */
|
|
717
742
|
margin-left: auto;
|
|
718
743
|
}
|
|
719
|
-
/*
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
.
|
|
724
|
-
|
|
725
|
-
display: flex;
|
|
726
|
-
align-items: center;
|
|
727
|
-
justify-content: center;
|
|
744
|
+
/* These are TldrawUiButtons (type="icon"), so the 40px box, the 32px ::after square carrying the
|
|
745
|
+
hover wash, and the hover z-index flip all come from .tlui-button — the hit area (not the
|
|
746
|
+
::after) is what's clickable, and it never reaches past its row. Only the bits that differ from
|
|
747
|
+
a toolbar button are set here, at .tlui-button's specificity + 1 so stylesheet order can't
|
|
748
|
+
decide the winner. */
|
|
749
|
+
.tlui-button.tlui-cmt-thread__action {
|
|
728
750
|
width: 40px;
|
|
729
|
-
height: 40px;
|
|
730
751
|
padding: 0;
|
|
731
|
-
border: none;
|
|
732
752
|
border-radius: 6px;
|
|
733
|
-
background: transparent;
|
|
734
753
|
color: var(--tl-color-text-3);
|
|
735
754
|
font-size: 14px;
|
|
736
755
|
line-height: 1;
|
|
737
|
-
cursor: pointer;
|
|
738
756
|
}
|
|
739
757
|
/* Overlap each button 4px into the previous so the 32px visual squares sit 4px apart, not 8px.
|
|
740
758
|
Left-only, so the last button's hit area still stops at its row's right edge. */
|
|
741
759
|
.tlui-cmt-thread__action + .tlui-cmt-thread__action {
|
|
742
760
|
margin-left: -4px;
|
|
743
761
|
}
|
|
744
|
-
.tlui-cmt-thread__action
|
|
745
|
-
content: '';
|
|
746
|
-
position: absolute;
|
|
747
|
-
inset: 4px;
|
|
748
|
-
border-radius: var(--tl-radius-2);
|
|
749
|
-
}
|
|
750
|
-
.tlui-cmt-thread__action:hover {
|
|
762
|
+
.tlui-button.tlui-cmt-thread__action:hover {
|
|
751
763
|
color: var(--tl-color-text-1);
|
|
752
|
-
/* Raise the hovered button above its overlapping neighbours so its 40px hit area wins the
|
|
753
|
-
overlap zone — the same z-index flip tldraw's own toolbar buttons use. */
|
|
754
|
-
z-index: 1;
|
|
755
|
-
}
|
|
756
|
-
.tlui-cmt-thread__action:hover::after {
|
|
757
|
-
background: var(--tl-color-muted-2);
|
|
758
764
|
}
|
|
759
|
-
.tlui-cmt-thread__action--danger:hover {
|
|
765
|
+
.tlui-button.tlui-cmt-thread__action--danger:hover {
|
|
760
766
|
color: var(--tl-color-danger);
|
|
761
767
|
}
|
|
762
768
|
.tlui-cmt-thread__resolved {
|
|
@@ -839,7 +845,7 @@
|
|
|
839
845
|
.tlui-cmt-canvas-preview--thread .tlui-cmt-card {
|
|
840
846
|
width: 100%;
|
|
841
847
|
gap: 0;
|
|
842
|
-
padding: 0
|
|
848
|
+
padding: 0 var(--tlui-cmt-thread-padding);
|
|
843
849
|
background: transparent;
|
|
844
850
|
border: none;
|
|
845
851
|
}
|
|
@@ -868,14 +874,17 @@
|
|
|
868
874
|
.tlui-cmt-stack-list .tlui-cmt-card .tlui-cmt-head {
|
|
869
875
|
min-height: 40px;
|
|
870
876
|
}
|
|
871
|
-
/*
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
877
|
+
/* Reserve head room only for pills that actually rendered (one child / two children) — pill count
|
|
878
|
+
is a permissions question (canModifyComment), not ownership, and empty slots (read-only viewers)
|
|
879
|
+
keep the full row. Sized from the 40px hit areas (4px overlap), minus the card padding the
|
|
880
|
+
right:0 pills sit over; text stops 4px shy of the visible square. */
|
|
881
|
+
.tlui-cmt-thread .tlui-cmt-card:has(.tlui-cmt-card__actions > *) .tlui-cmt-head {
|
|
882
|
+
padding-right: calc(var(--tlui-cmt-thread-action-size) - var(--tlui-cmt-thread-padding));
|
|
876
883
|
}
|
|
877
|
-
.tlui-cmt-thread .tlui-cmt-card
|
|
878
|
-
padding-right: calc(
|
|
884
|
+
.tlui-cmt-thread .tlui-cmt-card:has(.tlui-cmt-card__actions > * + *) .tlui-cmt-head {
|
|
885
|
+
padding-right: calc(
|
|
886
|
+
2 * var(--tlui-cmt-thread-action-size) - 4px - var(--tlui-cmt-thread-padding)
|
|
887
|
+
);
|
|
879
888
|
}
|
|
880
889
|
|
|
881
890
|
/* comments list — thread summaries, one per row (used by the canvas sidebar) */
|
|
@@ -891,7 +900,7 @@
|
|
|
891
900
|
align-items: center;
|
|
892
901
|
justify-content: space-between;
|
|
893
902
|
gap: 8px;
|
|
894
|
-
padding:
|
|
903
|
+
padding: 2px 2px 2px 14px;
|
|
895
904
|
border-bottom: 1px solid var(--tl-color-divider);
|
|
896
905
|
}
|
|
897
906
|
.tlui-cmt-list__header-title {
|
|
@@ -900,43 +909,28 @@
|
|
|
900
909
|
color: var(--tl-color-text-1);
|
|
901
910
|
}
|
|
902
911
|
|
|
903
|
-
/*
|
|
904
|
-
.tlui-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
width: 26px;
|
|
910
|
-
height: 26px;
|
|
912
|
+
/* An icon button in the sidebar header (filter funnel, overflow …). Sized like the thread actions:
|
|
913
|
+
a TldrawUiButton (type="icon") left at its native 40px box, with .tlui-button's 32px ::after
|
|
914
|
+
square carrying the hover wash. Only the bits that differ are set here, at .tlui-button's
|
|
915
|
+
specificity + 1 so stylesheet order can't decide the winner. */
|
|
916
|
+
.tlui-button.tlui-cmt-header-btn {
|
|
917
|
+
width: 40px;
|
|
911
918
|
padding: 0;
|
|
912
|
-
border: none;
|
|
913
919
|
border-radius: 6px;
|
|
914
|
-
background: transparent;
|
|
915
920
|
color: var(--tl-color-text-2);
|
|
916
|
-
cursor: pointer;
|
|
917
|
-
}
|
|
918
|
-
/* Same hit-target expansion as the thread actions, tiled the same way — this row's gap is 2px too
|
|
919
|
-
(see `.tlui-cmt-list__header-actions`). */
|
|
920
|
-
.tlui-cmt-header-btn::after {
|
|
921
|
-
content: '';
|
|
922
|
-
position: absolute;
|
|
923
|
-
inset: -6px -1px;
|
|
924
|
-
}
|
|
925
|
-
.tlui-cmt-header-btn:first-child::after {
|
|
926
|
-
left: -6px;
|
|
927
921
|
}
|
|
928
|
-
|
|
929
|
-
|
|
922
|
+
/* Overlap each button 4px into the previous so the 32px visual squares sit 4px apart, not 8px —
|
|
923
|
+
the same tiling the thread actions use. */
|
|
924
|
+
.tlui-cmt-header-btn + .tlui-cmt-header-btn {
|
|
925
|
+
margin-left: -4px;
|
|
930
926
|
}
|
|
931
|
-
.tlui-cmt-header-btn:hover,
|
|
932
|
-
.tlui-cmt-header-btn[data-state='open'] {
|
|
933
|
-
background: var(--tl-color-muted-2);
|
|
927
|
+
.tlui-button.tlui-cmt-header-btn:hover,
|
|
928
|
+
.tlui-button.tlui-cmt-header-btn[data-state='open'] {
|
|
934
929
|
color: var(--tl-color-text-1);
|
|
935
930
|
}
|
|
936
931
|
.tlui-cmt-list__header-actions {
|
|
937
932
|
display: flex;
|
|
938
933
|
align-items: center;
|
|
939
|
-
gap: 2px;
|
|
940
934
|
}
|
|
941
935
|
|
|
942
936
|
/* The commenting dropdowns' surface — a little air between the items and the menu edge. */
|
|
@@ -992,6 +986,7 @@
|
|
|
992
986
|
font-size: 12px;
|
|
993
987
|
}
|
|
994
988
|
.tlui-cmt-list__item {
|
|
989
|
+
position: relative;
|
|
995
990
|
display: flex;
|
|
996
991
|
gap: 10px;
|
|
997
992
|
width: 100%;
|
|
@@ -1006,18 +1001,27 @@
|
|
|
1006
1001
|
color: inherit;
|
|
1007
1002
|
text-decoration: none;
|
|
1008
1003
|
}
|
|
1009
|
-
|
|
1004
|
+
/* The highlight is an inset, rounded wash like tldraw's buttons and menu items — not an
|
|
1005
|
+
edge-to-edge band — so a comment row lights up the same way every other interactive row does. */
|
|
1006
|
+
.tlui-cmt-list__item::after {
|
|
1007
|
+
content: '';
|
|
1008
|
+
position: absolute;
|
|
1009
|
+
inset: 4px;
|
|
1010
|
+
border-radius: var(--tl-radius-2);
|
|
1011
|
+
pointer-events: none;
|
|
1012
|
+
}
|
|
1013
|
+
.tlui-cmt-list__item:hover::after {
|
|
1010
1014
|
background: var(--tl-color-muted-2);
|
|
1011
1015
|
}
|
|
1012
1016
|
/* Selected sits a step above hover so the open thread's row stays distinguishable while
|
|
1013
1017
|
pointing at other rows — but only a step: the muted-1 token (10%) reads too heavy here.
|
|
1014
1018
|
These rows sit on the opaque sidebar panel, so a translucent tint composes safely. */
|
|
1015
|
-
.tlui-cmt-list__item--selected,
|
|
1016
|
-
.tlui-cmt-list__item--selected:hover {
|
|
1019
|
+
.tlui-cmt-list__item--selected::after,
|
|
1020
|
+
.tlui-cmt-list__item--selected:hover::after {
|
|
1017
1021
|
background: hsl(0, 0%, 0%, 5%);
|
|
1018
1022
|
}
|
|
1019
|
-
.tl-theme__dark .tlui-cmt-list__item--selected,
|
|
1020
|
-
.tl-theme__dark .tlui-cmt-list__item--selected:hover {
|
|
1023
|
+
.tl-theme__dark .tlui-cmt-list__item--selected::after,
|
|
1024
|
+
.tl-theme__dark .tlui-cmt-list__item--selected:hover::after {
|
|
1021
1025
|
background: hsl(0, 0%, 100%, 5%);
|
|
1022
1026
|
}
|
|
1023
1027
|
/* resolved rows mute their content but keep the "Resolved" marker legible */
|
|
@@ -1076,10 +1080,11 @@
|
|
|
1076
1080
|
color: var(--tl-color-text-3);
|
|
1077
1081
|
}
|
|
1078
1082
|
|
|
1079
|
-
/* The comments layer
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
+
/* The comments layer renders through `EditorPortal`, so its pieces can pick their own layer rather
|
|
1084
|
+
than the one the component is mounted in. Pins live in the canvas-in-front layer (beneath the UI
|
|
1085
|
+
panels at z-index 300), so the toolbar and style panel draw over them; the open thread's popover
|
|
1086
|
+
goes to the menus layer instead (`.tlui-cmt-canvas-popover`) so it isn't clipped. Click-through
|
|
1087
|
+
except on its own children. */
|
|
1083
1088
|
.tlui-cmt-canvas-layer {
|
|
1084
1089
|
position: absolute;
|
|
1085
1090
|
inset: 0;
|
|
@@ -1423,13 +1428,6 @@
|
|
|
1423
1428
|
background: color-mix(in srgb, var(--tl-color-selected) 10%, transparent);
|
|
1424
1429
|
}
|
|
1425
1430
|
|
|
1426
|
-
/* A movable region body (the 'body'/'both' move modes): draggable to translate the whole region.
|
|
1427
|
-
Interactive, so it captures the interior while shown — the tradeoff of dragging the body. */
|
|
1428
|
-
.tlui-cmt-canvas-region--movable {
|
|
1429
|
-
pointer-events: auto;
|
|
1430
|
-
cursor: move;
|
|
1431
|
-
}
|
|
1432
|
-
|
|
1433
1431
|
/* Corner resize handles on a region box (three corners; the pin corner is the move handle). */
|
|
1434
1432
|
.tlui-cmt-canvas-region-handle {
|
|
1435
1433
|
position: absolute;
|
|
@@ -107,7 +107,7 @@ function registerCommentAnchorLifecycle(editor) {
|
|
|
107
107
|
}
|
|
108
108
|
}
|
|
109
109
|
if (updates.length > 0) {
|
|
110
|
-
(0, import_comment_mutations.commitCommentMutation)(editor, () => (
|
|
110
|
+
(0, import_comment_mutations.commitCommentMutation)(editor, ({ put }) => put(updates));
|
|
111
111
|
}
|
|
112
112
|
});
|
|
113
113
|
const disposeAfterCreate = editor.sideEffects.registerAfterCreateHandler(
|
|
@@ -134,7 +134,7 @@ function registerCommentAnchorLifecycle(editor) {
|
|
|
134
134
|
rehomeThread(thread, pageId, updates);
|
|
135
135
|
}
|
|
136
136
|
if (updates.length > 0) {
|
|
137
|
-
(0, import_comment_mutations.commitCommentMutation)(editor, () => (
|
|
137
|
+
(0, import_comment_mutations.commitCommentMutation)(editor, ({ put }) => put(updates));
|
|
138
138
|
}
|
|
139
139
|
}
|
|
140
140
|
);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/canvas/anchor-lifecycle.ts"],
|
|
4
|
-
"sourcesContent": ["import { WeakCache } from '@tldraw/utils'\nimport { Editor, TLCommentAnchor, TLCommentThread, TLPageId, TLShapeId, VecLike } from 'tldraw'\nimport { commitCommentMutation
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,mBAA0B;AAE1B,+
|
|
4
|
+
"sourcesContent": ["import { WeakCache } from '@tldraw/utils'\nimport { Editor, TLCommentAnchor, TLCommentThread, TLPageId, TLShapeId, VecLike } from 'tldraw'\nimport { commitCommentMutation } from './comment-mutations'\nimport { getCommentRecord, getComments, getCommentThreads, TLCommentRecord } from './comment-store'\nimport { anchorPagePoint, impreciseShapePinInset } from './thread-state'\n\ntype ShapeAnchor = Extract<TLCommentAnchor, { type: 'shape' }>\n\n/**\n * Threads converted to point anchors because their shape was deleted, kept so the anchor can be\n * restored if the shape comes back. Owned by the editor, not the registration closure: the\n * registering effect can re-run, and an undo can arrive long after the delete.\n */\nconst convertedByShapeCache = new WeakCache<\n\tEditor,\n\tMap<TLShapeId, { threadId: string; anchor: ShapeAnchor; point: VecLike }[]>\n>()\n\nfunction isAnchoredToShape(\n\tthread: TLCommentThread,\n\tshapeId: TLShapeId\n): thread is TLCommentThread & { anchor: ShapeAnchor } {\n\tconst anchor = thread.anchor\n\treturn anchor.type === 'shape' && anchor.shapeId === shapeId\n}\n\n/**\n * Keep shape-anchored threads alive across their shape's lifecycle:\n *\n * - When the shape is deleted, the thread converts to a `point` anchor where its pin last sat, so\n * the conversation outlives the shape instead of becoming invisible.\n * - When the shape moves to another page, the thread follows: its `pageId` and each comment's\n * denormalized `pageId` update, and the anchor keeps riding the shape.\n * - When a deleted shape comes back, the thread re-attaches \u2014 unless its pin was manually moved in\n * the meantime, in which case the manual placement wins.\n *\n * A page move is `deleteShapes` + re-create with preserved ids inside one `editor.run`, but each\n * store write is its own operation, so \"this shape is being moved\" is never observable as a single\n * event. The handlers therefore cooperate across operations: `beforeDelete` snapshots where each\n * affected pin sits, the operation-complete pass converts threads whose shape is really gone, and\n * `afterCreate` plus that same pass restore the anchor once the store has settled. Undo/redo of a\n * move replays as a `parentId` update, so `afterChange` re-homes threads on cross-page reparents \u2014\n * including threads anchored to descendants, which move without change events of their own.\n *\n * Remote changes are ignored: the client that performed the operation runs this same maintenance\n * and syncs the result. Writes honour the {@link CommentingOptions.history} option.\n *\n * Registered by `CanvasComments` on mount; parts-built consumers can call this directly. Returns a\n * cleanup function that unregisters all handlers.\n *\n * @public\n */\nexport function registerCommentAnchorLifecycle(editor: Editor): () => void {\n\t// Pin positions snapshotted during the current operation: shape id -> (thread id -> pin page\n\t// point).\n\tconst pendingByShape = new Map<TLShapeId, Map<string, VecLike | null>>()\n\tconst convertedByShape = convertedByShapeCache.get(editor, () => new Map())\n\n\tfunction rehomeThread(thread: TLCommentThread, pageId: TLPageId, updates: TLCommentRecord[]) {\n\t\tupdates.push({ ...thread, pageId })\n\t\tfor (const comment of getComments(editor)) {\n\t\t\tif (comment.threadId === thread.id) updates.push({ ...comment, pageId })\n\t\t}\n\t}\n\n\tconst disposeBeforeDelete = editor.sideEffects.registerBeforeDeleteHandler(\n\t\t'shape',\n\t\t(shape, source) => {\n\t\t\tif (source === 'remote') return\n\t\t\tfor (const thread of getCommentThreads(editor)) {\n\t\t\t\tif (!isAnchoredToShape(thread, shape.id)) continue\n\t\t\t\tlet snapshot = pendingByShape.get(shape.id)\n\t\t\t\tif (!snapshot) {\n\t\t\t\t\tsnapshot = new Map()\n\t\t\t\t\tpendingByShape.set(shape.id, snapshot)\n\t\t\t\t}\n\t\t\t\t// Snapshot where the pin is drawn, not just the anchor point: imprecise shape pins render inset\n\t\t\t\t// toward the shape's centre, so without baking the inset in at the current zoom the pin would jump.\n\t\t\t\tlet point = anchorPagePoint(editor, thread.anchor)\n\t\t\t\tconst inset = impreciseShapePinInset(editor, thread.anchor)\n\t\t\t\tif (point && inset) {\n\t\t\t\t\tconst zoom = editor.getZoomLevel()\n\t\t\t\t\tpoint = { x: point.x + inset.x / zoom, y: point.y + inset.y / zoom }\n\t\t\t\t}\n\t\t\t\tsnapshot.set(thread.id, point)\n\t\t\t}\n\t\t}\n\t)\n\n\t// Shape ids from `convertedByShape` re-created during the current operation. Settled at operation\n\t// complete rather than in `afterCreate`: creation order within an operation is arbitrary, so a shape\n\t// can appear before its parent and not resolve an ancestor page yet.\n\tconst returnedShapeIds = new Set<TLShapeId>()\n\n\tconst disposeOperationComplete = editor.sideEffects.registerOperationCompleteHandler((source) => {\n\t\t// Only user-sourced handlers populate the maps and only local operations should settle\n\t\t// them; a remote operation completing leaves any (impossible-in-practice) leftovers for\n\t\t// the next local settle rather than judging them against remote state.\n\t\tif (source === 'remote') return\n\t\tif (pendingByShape.size === 0 && returnedShapeIds.size === 0) return\n\t\tconst settled = [...pendingByShape.entries()]\n\t\tpendingByShape.clear()\n\t\tconst returned = [...returnedShapeIds]\n\t\treturnedShapeIds.clear()\n\n\t\tconst updates: TLCommentRecord[] = []\n\t\tfor (const [shapeId, threadPoints] of settled) {\n\t\t\tif (editor.getShape(shapeId)) continue\n\t\t\tfor (const [threadId, point] of threadPoints) {\n\t\t\t\tif (!point) continue\n\t\t\t\tconst thread = getCommentRecord(editor, threadId)\n\t\t\t\tif (!thread || thread.typeName !== 'comment-thread') continue\n\t\t\t\tif (!isAnchoredToShape(thread, shapeId)) continue\n\t\t\t\tlet converted = convertedByShape.get(shapeId)\n\t\t\t\tif (!converted) {\n\t\t\t\t\tconverted = []\n\t\t\t\t\tconvertedByShape.set(shapeId, converted)\n\t\t\t\t}\n\t\t\t\tconverted.push({ threadId, anchor: thread.anchor, point })\n\t\t\t\tupdates.push({ ...thread, anchor: { type: 'point', x: point.x, y: point.y } })\n\t\t\t}\n\t\t}\n\n\t\tfor (const shapeId of returned) {\n\t\t\tconst shape = editor.getShape(shapeId)\n\t\t\tif (!shape) continue\n\t\t\tconst converted = convertedByShape.get(shapeId)\n\t\t\tif (!converted) continue\n\t\t\tconvertedByShape.delete(shapeId)\n\n\t\t\tconst pageId = editor.getAncestorPageId(shape)\n\t\t\tfor (const { threadId, anchor, point } of converted) {\n\t\t\t\tconst thread = getCommentRecord(editor, threadId)\n\t\t\t\tif (!thread || thread.typeName !== 'comment-thread') continue\n\t\t\t\t// Re-attach only threads still sitting exactly where the conversion left them \u2014 a\n\t\t\t\t// pin moved since then was placed deliberately, and that placement wins.\n\t\t\t\tconst current = thread.anchor\n\t\t\t\tif (current.type !== 'point' || current.x !== point.x || current.y !== point.y) {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tif (!pageId || thread.pageId === pageId) {\n\t\t\t\t\tupdates.push({ ...thread, anchor })\n\t\t\t\t} else {\n\t\t\t\t\trehomeThread({ ...thread, anchor }, pageId, updates)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (updates.length > 0) {\n\t\t\tcommitCommentMutation(editor, ({ put }) => put(updates))\n\t\t}\n\t})\n\n\tconst disposeAfterCreate = editor.sideEffects.registerAfterCreateHandler(\n\t\t'shape',\n\t\t(shape, source) => {\n\t\t\tif (source === 'remote') return\n\t\t\tif (convertedByShape.has(shape.id)) returnedShapeIds.add(shape.id)\n\t\t}\n\t)\n\n\tconst disposeAfterChange = editor.sideEffects.registerAfterChangeHandler(\n\t\t'shape',\n\t\t(prev, next, source) => {\n\t\t\tif (source === 'remote') return\n\t\t\tif (prev.parentId === next.parentId) return\n\t\t\tconst pageId = editor.getAncestorPageId(next)\n\t\t\tif (!pageId) return\n\t\t\t// Descendants move with their parent without change events of their own.\n\t\t\tconst movedIds = editor.getShapeAndDescendantIds([next.id])\n\t\t\tconst updates: TLCommentRecord[] = []\n\t\t\tfor (const thread of getCommentThreads(editor)) {\n\t\t\t\tconst anchor = thread.anchor\n\t\t\t\tif (anchor.type !== 'shape') continue\n\t\t\t\tif (!movedIds.has(anchor.shapeId)) continue\n\t\t\t\tif (thread.pageId === pageId) continue\n\t\t\t\trehomeThread(thread, pageId, updates)\n\t\t\t}\n\t\t\tif (updates.length > 0) {\n\t\t\t\tcommitCommentMutation(editor, ({ put }) => put(updates))\n\t\t\t}\n\t\t}\n\t)\n\n\treturn () => {\n\t\tdisposeBeforeDelete()\n\t\tdisposeOperationComplete()\n\t\tdisposeAfterCreate()\n\t\tdisposeAfterChange()\n\t}\n}\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,mBAA0B;AAE1B,+BAAsC;AACtC,2BAAkF;AAClF,0BAAwD;AASxD,MAAM,wBAAwB,IAAI,uBAGhC;AAEF,SAAS,kBACR,QACA,SACsD;AACtD,QAAM,SAAS,OAAO;AACtB,SAAO,OAAO,SAAS,WAAW,OAAO,YAAY;AACtD;AA4BO,SAAS,+BAA+B,QAA4B;AAG1E,QAAM,iBAAiB,oBAAI,IAA4C;AACvE,QAAM,mBAAmB,sBAAsB,IAAI,QAAQ,MAAM,oBAAI,IAAI,CAAC;AAE1E,WAAS,aAAa,QAAyB,QAAkB,SAA4B;AAC5F,YAAQ,KAAK,EAAE,GAAG,QAAQ,OAAO,CAAC;AAClC,eAAW,eAAW,kCAAY,MAAM,GAAG;AAC1C,UAAI,QAAQ,aAAa,OAAO,GAAI,SAAQ,KAAK,EAAE,GAAG,SAAS,OAAO,CAAC;AAAA,IACxE;AAAA,EACD;AAEA,QAAM,sBAAsB,OAAO,YAAY;AAAA,IAC9C;AAAA,IACA,CAAC,OAAO,WAAW;AAClB,UAAI,WAAW,SAAU;AACzB,iBAAW,cAAU,wCAAkB,MAAM,GAAG;AAC/C,YAAI,CAAC,kBAAkB,QAAQ,MAAM,EAAE,EAAG;AAC1C,YAAI,WAAW,eAAe,IAAI,MAAM,EAAE;AAC1C,YAAI,CAAC,UAAU;AACd,qBAAW,oBAAI,IAAI;AACnB,yBAAe,IAAI,MAAM,IAAI,QAAQ;AAAA,QACtC;AAGA,YAAI,YAAQ,qCAAgB,QAAQ,OAAO,MAAM;AACjD,cAAM,YAAQ,4CAAuB,QAAQ,OAAO,MAAM;AAC1D,YAAI,SAAS,OAAO;AACnB,gBAAM,OAAO,OAAO,aAAa;AACjC,kBAAQ,EAAE,GAAG,MAAM,IAAI,MAAM,IAAI,MAAM,GAAG,MAAM,IAAI,MAAM,IAAI,KAAK;AAAA,QACpE;AACA,iBAAS,IAAI,OAAO,IAAI,KAAK;AAAA,MAC9B;AAAA,IACD;AAAA,EACD;AAKA,QAAM,mBAAmB,oBAAI,IAAe;AAE5C,QAAM,2BAA2B,OAAO,YAAY,iCAAiC,CAAC,WAAW;AAIhG,QAAI,WAAW,SAAU;AACzB,QAAI,eAAe,SAAS,KAAK,iBAAiB,SAAS,EAAG;AAC9D,UAAM,UAAU,CAAC,GAAG,eAAe,QAAQ,CAAC;AAC5C,mBAAe,MAAM;AACrB,UAAM,WAAW,CAAC,GAAG,gBAAgB;AACrC,qBAAiB,MAAM;AAEvB,UAAM,UAA6B,CAAC;AACpC,eAAW,CAAC,SAAS,YAAY,KAAK,SAAS;AAC9C,UAAI,OAAO,SAAS,OAAO,EAAG;AAC9B,iBAAW,CAAC,UAAU,KAAK,KAAK,cAAc;AAC7C,YAAI,CAAC,MAAO;AACZ,cAAM,aAAS,uCAAiB,QAAQ,QAAQ;AAChD,YAAI,CAAC,UAAU,OAAO,aAAa,iBAAkB;AACrD,YAAI,CAAC,kBAAkB,QAAQ,OAAO,EAAG;AACzC,YAAI,YAAY,iBAAiB,IAAI,OAAO;AAC5C,YAAI,CAAC,WAAW;AACf,sBAAY,CAAC;AACb,2BAAiB,IAAI,SAAS,SAAS;AAAA,QACxC;AACA,kBAAU,KAAK,EAAE,UAAU,QAAQ,OAAO,QAAQ,MAAM,CAAC;AACzD,gBAAQ,KAAK,EAAE,GAAG,QAAQ,QAAQ,EAAE,MAAM,SAAS,GAAG,MAAM,GAAG,GAAG,MAAM,EAAE,EAAE,CAAC;AAAA,MAC9E;AAAA,IACD;AAEA,eAAW,WAAW,UAAU;AAC/B,YAAM,QAAQ,OAAO,SAAS,OAAO;AACrC,UAAI,CAAC,MAAO;AACZ,YAAM,YAAY,iBAAiB,IAAI,OAAO;AAC9C,UAAI,CAAC,UAAW;AAChB,uBAAiB,OAAO,OAAO;AAE/B,YAAM,SAAS,OAAO,kBAAkB,KAAK;AAC7C,iBAAW,EAAE,UAAU,QAAQ,MAAM,KAAK,WAAW;AACpD,cAAM,aAAS,uCAAiB,QAAQ,QAAQ;AAChD,YAAI,CAAC,UAAU,OAAO,aAAa,iBAAkB;AAGrD,cAAM,UAAU,OAAO;AACvB,YAAI,QAAQ,SAAS,WAAW,QAAQ,MAAM,MAAM,KAAK,QAAQ,MAAM,MAAM,GAAG;AAC/E;AAAA,QACD;AACA,YAAI,CAAC,UAAU,OAAO,WAAW,QAAQ;AACxC,kBAAQ,KAAK,EAAE,GAAG,QAAQ,OAAO,CAAC;AAAA,QACnC,OAAO;AACN,uBAAa,EAAE,GAAG,QAAQ,OAAO,GAAG,QAAQ,OAAO;AAAA,QACpD;AAAA,MACD;AAAA,IACD;AAEA,QAAI,QAAQ,SAAS,GAAG;AACvB,0DAAsB,QAAQ,CAAC,EAAE,IAAI,MAAM,IAAI,OAAO,CAAC;AAAA,IACxD;AAAA,EACD,CAAC;AAED,QAAM,qBAAqB,OAAO,YAAY;AAAA,IAC7C;AAAA,IACA,CAAC,OAAO,WAAW;AAClB,UAAI,WAAW,SAAU;AACzB,UAAI,iBAAiB,IAAI,MAAM,EAAE,EAAG,kBAAiB,IAAI,MAAM,EAAE;AAAA,IAClE;AAAA,EACD;AAEA,QAAM,qBAAqB,OAAO,YAAY;AAAA,IAC7C;AAAA,IACA,CAAC,MAAM,MAAM,WAAW;AACvB,UAAI,WAAW,SAAU;AACzB,UAAI,KAAK,aAAa,KAAK,SAAU;AACrC,YAAM,SAAS,OAAO,kBAAkB,IAAI;AAC5C,UAAI,CAAC,OAAQ;AAEb,YAAM,WAAW,OAAO,yBAAyB,CAAC,KAAK,EAAE,CAAC;AAC1D,YAAM,UAA6B,CAAC;AACpC,iBAAW,cAAU,wCAAkB,MAAM,GAAG;AAC/C,cAAM,SAAS,OAAO;AACtB,YAAI,OAAO,SAAS,QAAS;AAC7B,YAAI,CAAC,SAAS,IAAI,OAAO,OAAO,EAAG;AACnC,YAAI,OAAO,WAAW,OAAQ;AAC9B,qBAAa,QAAQ,QAAQ,OAAO;AAAA,MACrC;AACA,UAAI,QAAQ,SAAS,GAAG;AACvB,4DAAsB,QAAQ,CAAC,EAAE,IAAI,MAAM,IAAI,OAAO,CAAC;AAAA,MACxD;AAAA,IACD;AAAA,EACD;AAEA,SAAO,MAAM;AACZ,wBAAoB;AACpB,6BAAyB;AACzB,uBAAmB;AACnB,uBAAmB;AAAA,EACpB;AACD;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -16,17 +16,20 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
16
16
|
return to;
|
|
17
17
|
};
|
|
18
18
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
-
var
|
|
20
|
-
__export(
|
|
21
|
-
|
|
19
|
+
var canvas_events_exports = {};
|
|
20
|
+
__export(canvas_events_exports, {
|
|
21
|
+
forwardPointerEventToCanvas: () => forwardPointerEventToCanvas,
|
|
22
|
+
isCanvasPanGesture: () => isCanvasPanGesture
|
|
22
23
|
});
|
|
23
|
-
module.exports = __toCommonJS(
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
24
|
+
module.exports = __toCommonJS(canvas_events_exports);
|
|
25
|
+
function isCanvasPanGesture(editor, e) {
|
|
26
|
+
return e.button !== 0 || editor.inputs.keys.has("Space");
|
|
27
|
+
}
|
|
28
|
+
function forwardPointerEventToCanvas(container, e) {
|
|
29
|
+
const cvs = container.querySelector(".tl-canvas");
|
|
30
|
+
if (!cvs) return;
|
|
31
|
+
const newEvent = new PointerEvent(e.type, e.nativeEvent);
|
|
32
|
+
newEvent.isSpecialRedispatchedEvent = true;
|
|
33
|
+
cvs.dispatchEvent(newEvent);
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=canvas-events.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/canvas/canvas-events.ts"],
|
|
4
|
+
"sourcesContent": ["import { type PointerEvent as ReactPointerEvent } from 'react'\nimport { Editor } from 'tldraw'\n\n/** A pointer-down that belongs to the camera, not the comment UI: any non-primary button\n * (middle/right-button pans), or a primary press with the spacebar pan key held. */\nexport function isCanvasPanGesture(editor: Editor, e: ReactPointerEvent) {\n\treturn e.button !== 0 || editor.inputs.keys.has('Space')\n}\n\n/** Hand a pointer event to the canvas beneath the comments layer, marked the same way the\n * pass-through wheel/hover hooks mark their re-dispatched events. */\nexport function forwardPointerEventToCanvas(container: HTMLElement, e: ReactPointerEvent) {\n\tconst cvs = container.querySelector('.tl-canvas')\n\tif (!cvs) return\n\tconst newEvent = new PointerEvent(e.type, e.nativeEvent as any)\n\t;(newEvent as any).isSpecialRedispatchedEvent = true\n\tcvs.dispatchEvent(newEvent)\n}\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAKO,SAAS,mBAAmB,QAAgB,GAAsB;AACxE,SAAO,EAAE,WAAW,KAAK,OAAO,OAAO,KAAK,IAAI,OAAO;AACxD;AAIO,SAAS,4BAA4B,WAAwB,GAAsB;AACzF,QAAM,MAAM,UAAU,cAAc,YAAY;AAChD,MAAI,CAAC,IAAK;AACV,QAAM,WAAW,IAAI,aAAa,EAAE,MAAM,EAAE,WAAkB;AAC7D,EAAC,SAAiB,6BAA6B;AAChD,MAAI,cAAc,QAAQ;AAC3B;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
var cluster_badge_exports = {};
|
|
20
|
+
__export(cluster_badge_exports, {
|
|
21
|
+
ClusterBadge: () => ClusterBadge
|
|
22
|
+
});
|
|
23
|
+
module.exports = __toCommonJS(cluster_badge_exports);
|
|
24
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
25
|
+
var import_react = require("react");
|
|
26
|
+
var import_tldraw = require("tldraw");
|
|
27
|
+
var import_count_badge = require("../ui/count-badge");
|
|
28
|
+
var import_canvas_events = require("./canvas-events");
|
|
29
|
+
var import_thread_preview = require("./thread-preview");
|
|
30
|
+
var import_thread_state = require("./thread-state");
|
|
31
|
+
const ClusterBadge = (0, import_react.memo)(function ClusterBadge2({
|
|
32
|
+
editor,
|
|
33
|
+
node,
|
|
34
|
+
onExpand,
|
|
35
|
+
onSelectThread,
|
|
36
|
+
threadsById,
|
|
37
|
+
...props
|
|
38
|
+
}) {
|
|
39
|
+
const container = (0, import_tldraw.useContainer)();
|
|
40
|
+
const msg = (0, import_tldraw.useTranslation)();
|
|
41
|
+
const badgeRef = (0, import_react.useRef)(null);
|
|
42
|
+
const { previewShown, previewHandlers } = (0, import_thread_preview.useMarkerPreview)(editor, `cluster:${node.id}`);
|
|
43
|
+
(0, import_tldraw.usePassThroughWheelEvents)(badgeRef);
|
|
44
|
+
const meanInset = (0, import_tldraw.useValue)(
|
|
45
|
+
"cluster badge inset",
|
|
46
|
+
() => {
|
|
47
|
+
let x = 0;
|
|
48
|
+
let y = 0;
|
|
49
|
+
for (const id of node.members) {
|
|
50
|
+
const thread = threadsById.get(id);
|
|
51
|
+
if (!thread) continue;
|
|
52
|
+
const inset = (0, import_thread_state.impreciseShapePinInset)(editor, thread.anchor);
|
|
53
|
+
if (inset) {
|
|
54
|
+
x += inset.x;
|
|
55
|
+
y += inset.y;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return { x: x / node.count, y: y / node.count };
|
|
59
|
+
},
|
|
60
|
+
[editor, node, threadsById]
|
|
61
|
+
);
|
|
62
|
+
const point = (0, import_tldraw.useValue)(
|
|
63
|
+
"cluster badge point",
|
|
64
|
+
() => {
|
|
65
|
+
const pagePoint = editor.pageToViewport(node.centroid);
|
|
66
|
+
if (!(0, import_thread_state.isInInflatedViewport)(editor, pagePoint)) return null;
|
|
67
|
+
return { x: pagePoint.x + meanInset.x, y: pagePoint.y + meanInset.y };
|
|
68
|
+
},
|
|
69
|
+
[editor, node, meanInset]
|
|
70
|
+
);
|
|
71
|
+
const previewThreads = (0, import_react.useMemo)(() => {
|
|
72
|
+
if (!previewShown) return [];
|
|
73
|
+
const threads = [];
|
|
74
|
+
for (const id of node.members) {
|
|
75
|
+
const thread = threadsById.get(id);
|
|
76
|
+
if (thread) threads.push(thread);
|
|
77
|
+
}
|
|
78
|
+
return (0, import_thread_preview.sortThreadsForPreview)(threads);
|
|
79
|
+
}, [previewShown, node.members, threadsById]);
|
|
80
|
+
if (!point) return null;
|
|
81
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
82
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
83
|
+
"button",
|
|
84
|
+
{
|
|
85
|
+
ref: badgeRef,
|
|
86
|
+
type: "button",
|
|
87
|
+
className: "tlui-cmt-button tlui-cmt-canvas-cluster",
|
|
88
|
+
style: { left: point.x, top: point.y },
|
|
89
|
+
"aria-label": msg("comments.cluster-label").replace("{count}", String(node.count)),
|
|
90
|
+
onPointerDown: (e) => {
|
|
91
|
+
if ((0, import_canvas_events.isCanvasPanGesture)(editor, e)) {
|
|
92
|
+
(0, import_canvas_events.forwardPointerEventToCanvas)(container, e);
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
e.stopPropagation();
|
|
96
|
+
},
|
|
97
|
+
onClick: (e) => {
|
|
98
|
+
e.stopPropagation();
|
|
99
|
+
onExpand(node);
|
|
100
|
+
},
|
|
101
|
+
...previewHandlers,
|
|
102
|
+
onFocus: previewHandlers.onPointerEnter,
|
|
103
|
+
onBlur: previewHandlers.onPointerLeave,
|
|
104
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_count_badge.CountBadge, { count: node.count })
|
|
105
|
+
}
|
|
106
|
+
),
|
|
107
|
+
previewShown && previewThreads.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
108
|
+
import_thread_preview.ThreadPreview,
|
|
109
|
+
{
|
|
110
|
+
editor,
|
|
111
|
+
threads: previewThreads,
|
|
112
|
+
variant: "list",
|
|
113
|
+
point,
|
|
114
|
+
onSelectThread,
|
|
115
|
+
...previewHandlers,
|
|
116
|
+
...props
|
|
117
|
+
}
|
|
118
|
+
)
|
|
119
|
+
] });
|
|
120
|
+
});
|
|
121
|
+
//# sourceMappingURL=cluster-badge.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/canvas/cluster-badge.tsx"],
|
|
4
|
+
"sourcesContent": ["import { memo, useMemo, useRef } from 'react'\nimport {\n\tEditor,\n\tTLCommentThread,\n\tuseContainer,\n\tusePassThroughWheelEvents,\n\tuseTranslation,\n\tuseValue,\n} from 'tldraw'\nimport type { ClusterNode } from '../clustering/types'\nimport { CountBadge } from '../ui/count-badge'\nimport { forwardPointerEventToCanvas, isCanvasPanGesture } from './canvas-events'\nimport { type CommentingContext } from './context'\nimport { sortThreadsForPreview, ThreadPreview, useMarkerPreview } from './thread-preview'\nimport { impreciseShapePinInset, isInInflatedViewport } from './thread-state'\n\n/**\n * The count badge standing in for several threads folded together at the current zoom. Hovering it\n * previews its threads as a list; clicking zooms to the zoom level at which the cluster splits.\n *\n * Memoized: cluster nodes and thread records are identity-stable while unchanged, so pins and\n * badges skip re-rendering when the parent re-renders for reasons that don't concern them\n * (leaf recomputes during shape drags, partition changes elsewhere). Camera tracking still\n * works \u2014 each component subscribes to its own viewport position via signals, not via props.\n */\nexport const ClusterBadge = memo(function ClusterBadge({\n\teditor,\n\tnode,\n\tonExpand,\n\tonSelectThread,\n\tthreadsById,\n\t...props\n}: Pick<CommentingContext, 'currentUserId' | 'resolveAuthor'> & {\n\teditor: Editor\n\tnode: ClusterNode\n\tonExpand(node: ClusterNode): void\n\tonSelectThread(thread: TLCommentThread): void\n\tthreadsById: ReadonlyMap<string, TLCommentThread>\n}) {\n\tconst container = useContainer()\n\tconst msg = useTranslation()\n\tconst badgeRef = useRef<HTMLButtonElement>(null)\n\tconst { previewShown, previewHandlers } = useMarkerPreview(editor, `cluster:${node.id}`)\n\t// Wheel pass-through sits on the badge (never scrollable), not the layer root \u2014 see the\n\t// note on the layer.\n\tusePassThroughWheelEvents(badgeRef)\n\t// The mean of the members' render offsets, in screen px. Imprecise members' pins draw tucked\n\t// into their shape (see impreciseShapePinInset), and the merge thresholds are priced at those\n\t// visual positions \u2014 so the badge draws at the centroid of the pins as drawn, which (screen\n\t// mapping being affine) is the anchor centroid plus this mean. Camera-independent: its own\n\t// computed so the per-frame point below only adds two numbers, and a cluster of precise\n\t// members tracks nothing and adds zero.\n\tconst meanInset = useValue(\n\t\t'cluster badge inset',\n\t\t() => {\n\t\t\tlet x = 0\n\t\t\tlet y = 0\n\t\t\tfor (const id of node.members) {\n\t\t\t\tconst thread = threadsById.get(id)\n\t\t\t\tif (!thread) continue\n\t\t\t\tconst inset = impreciseShapePinInset(editor, thread.anchor)\n\t\t\t\tif (inset) {\n\t\t\t\t\tx += inset.x\n\t\t\t\t\ty += inset.y\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn { x: x / node.count, y: y / node.count }\n\t\t},\n\t\t[editor, node, threadsById]\n\t)\n\tconst point = useValue(\n\t\t'cluster badge point',\n\t\t() => {\n\t\t\tconst pagePoint = editor.pageToViewport(node.centroid)\n\t\t\tif (!isInInflatedViewport(editor, pagePoint)) return null\n\t\t\treturn { x: pagePoint.x + meanInset.x, y: pagePoint.y + meanInset.y }\n\t\t},\n\t\t[editor, node, meanInset]\n\t)\n\n\t// `node.members` is sorted by id (the clustering table's ordering); the preview wants them in\n\t// the order a reader would expect. Only computed while the preview is up.\n\tconst previewThreads = useMemo(() => {\n\t\tif (!previewShown) return []\n\t\tconst threads: TLCommentThread[] = []\n\t\tfor (const id of node.members) {\n\t\t\tconst thread = threadsById.get(id)\n\t\t\tif (thread) threads.push(thread)\n\t\t}\n\t\treturn sortThreadsForPreview(threads)\n\t}, [previewShown, node.members, threadsById])\n\n\tif (!point) return null\n\n\treturn (\n\t\t<>\n\t\t\t<button\n\t\t\t\tref={badgeRef}\n\t\t\t\ttype=\"button\"\n\t\t\t\tclassName=\"tlui-cmt-button tlui-cmt-canvas-cluster\"\n\t\t\t\tstyle={{ left: point.x, top: point.y }}\n\t\t\t\taria-label={msg('comments.cluster-label').replace('{count}', String(node.count))}\n\t\t\t\tonPointerDown={(e) => {\n\t\t\t\t\tif (isCanvasPanGesture(editor, e)) {\n\t\t\t\t\t\tforwardPointerEventToCanvas(container, e)\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\t\t\t\t\te.stopPropagation()\n\t\t\t\t}}\n\t\t\t\tonClick={(e) => {\n\t\t\t\t\te.stopPropagation()\n\t\t\t\t\tonExpand(node)\n\t\t\t\t}}\n\t\t\t\t{...previewHandlers}\n\t\t\t\tonFocus={previewHandlers.onPointerEnter}\n\t\t\t\tonBlur={previewHandlers.onPointerLeave}\n\t\t\t>\n\t\t\t\t<CountBadge count={node.count} />\n\t\t\t</button>\n\t\t\t{previewShown && previewThreads.length > 0 && (\n\t\t\t\t<ThreadPreview\n\t\t\t\t\teditor={editor}\n\t\t\t\t\tthreads={previewThreads}\n\t\t\t\t\tvariant=\"list\"\n\t\t\t\t\tpoint={point}\n\t\t\t\t\tonSelectThread={onSelectThread}\n\t\t\t\t\t{...previewHandlers}\n\t\t\t\t\t{...props}\n\t\t\t\t/>\n\t\t\t)}\n\t\t</>\n\t)\n})\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AA+FE;AA/FF,mBAAsC;AACtC,oBAOO;AAEP,yBAA2B;AAC3B,2BAAgE;AAEhE,4BAAuE;AACvE,0BAA6D;AAWtD,MAAM,mBAAe,mBAAK,SAASA,cAAa;AAAA,EACtD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,GAAG;AACJ,GAMG;AACF,QAAM,gBAAY,4BAAa;AAC/B,QAAM,UAAM,8BAAe;AAC3B,QAAM,eAAW,qBAA0B,IAAI;AAC/C,QAAM,EAAE,cAAc,gBAAgB,QAAI,wCAAiB,QAAQ,WAAW,KAAK,EAAE,EAAE;AAGvF,+CAA0B,QAAQ;AAOlC,QAAM,gBAAY;AAAA,IACjB;AAAA,IACA,MAAM;AACL,UAAI,IAAI;AACR,UAAI,IAAI;AACR,iBAAW,MAAM,KAAK,SAAS;AAC9B,cAAM,SAAS,YAAY,IAAI,EAAE;AACjC,YAAI,CAAC,OAAQ;AACb,cAAM,YAAQ,4CAAuB,QAAQ,OAAO,MAAM;AAC1D,YAAI,OAAO;AACV,eAAK,MAAM;AACX,eAAK,MAAM;AAAA,QACZ;AAAA,MACD;AACA,aAAO,EAAE,GAAG,IAAI,KAAK,OAAO,GAAG,IAAI,KAAK,MAAM;AAAA,IAC/C;AAAA,IACA,CAAC,QAAQ,MAAM,WAAW;AAAA,EAC3B;AACA,QAAM,YAAQ;AAAA,IACb;AAAA,IACA,MAAM;AACL,YAAM,YAAY,OAAO,eAAe,KAAK,QAAQ;AACrD,UAAI,KAAC,0CAAqB,QAAQ,SAAS,EAAG,QAAO;AACrD,aAAO,EAAE,GAAG,UAAU,IAAI,UAAU,GAAG,GAAG,UAAU,IAAI,UAAU,EAAE;AAAA,IACrE;AAAA,IACA,CAAC,QAAQ,MAAM,SAAS;AAAA,EACzB;AAIA,QAAM,qBAAiB,sBAAQ,MAAM;AACpC,QAAI,CAAC,aAAc,QAAO,CAAC;AAC3B,UAAM,UAA6B,CAAC;AACpC,eAAW,MAAM,KAAK,SAAS;AAC9B,YAAM,SAAS,YAAY,IAAI,EAAE;AACjC,UAAI,OAAQ,SAAQ,KAAK,MAAM;AAAA,IAChC;AACA,eAAO,6CAAsB,OAAO;AAAA,EACrC,GAAG,CAAC,cAAc,KAAK,SAAS,WAAW,CAAC;AAE5C,MAAI,CAAC,MAAO,QAAO;AAEnB,SACC,4EACC;AAAA;AAAA,MAAC;AAAA;AAAA,QACA,KAAK;AAAA,QACL,MAAK;AAAA,QACL,WAAU;AAAA,QACV,OAAO,EAAE,MAAM,MAAM,GAAG,KAAK,MAAM,EAAE;AAAA,QACrC,cAAY,IAAI,wBAAwB,EAAE,QAAQ,WAAW,OAAO,KAAK,KAAK,CAAC;AAAA,QAC/E,eAAe,CAAC,MAAM;AACrB,kBAAI,yCAAmB,QAAQ,CAAC,GAAG;AAClC,kEAA4B,WAAW,CAAC;AACxC;AAAA,UACD;AACA,YAAE,gBAAgB;AAAA,QACnB;AAAA,QACA,SAAS,CAAC,MAAM;AACf,YAAE,gBAAgB;AAClB,mBAAS,IAAI;AAAA,QACd;AAAA,QACC,GAAG;AAAA,QACJ,SAAS,gBAAgB;AAAA,QACzB,QAAQ,gBAAgB;AAAA,QAExB,sDAAC,iCAAW,OAAO,KAAK,OAAO;AAAA;AAAA,IAChC;AAAA,IACC,gBAAgB,eAAe,SAAS,KACxC;AAAA,MAAC;AAAA;AAAA,QACA;AAAA,QACA,SAAS;AAAA,QACT,SAAQ;AAAA,QACR;AAAA,QACA;AAAA,QACC,GAAG;AAAA,QACH,GAAG;AAAA;AAAA,IACL;AAAA,KAEF;AAEF,CAAC;",
|
|
6
|
+
"names": ["ClusterBadge"]
|
|
7
|
+
}
|