feat(collab): nth-in-room colors, comment toolbar + drag + global hide, luminance chip text

- presence.ts: colors claimed by ARRIVAL ORDER — each client takes the lowest
  free palette slot (no birthday-problem hash collisions; the best palette
  colors go first; N ≤ palette-size editors always distinct). Coordination-
  free: claims ride awareness; simultaneous-join collisions converge (lower
  clientID keeps, the other re-claims), same-user tabs adopt one color,
  claims are sticky across eeschema sheet rebinds (skeletons reuse them).
  colorOf(userId) resolves live colors; comment pins/popovers follow it
  (offline authors fall back to the hash). +2 unit tests (verified live:
  alice=slot0, bob=slot1, roster/pins consistent).
- CommentLayer: single comment icon expanding into a horizontal bar — new
  comment · list · show/hide all (eye empties the GAL pin set + DOM targets;
  re-shown on new-comment/panel-jump). Pins are DRAGGABLE: live LWW anchor
  writes while dragging (peers + the GAL dot follow), nearest-item re-snap on
  drop, click-vs-drag by 4px threshold (verified live: drag synced to the
  peer tab exactly). Shared setThreadAnchor + controller moveThread/
  setPinsVisible/colorFor.
- collab_presence_style.h: chip text color by background luminance (dark on
  light chips, white on dark — BitmapText draws with the stroke color).
- comments/presence-roster e2e updated for the bar + passing; collab units
  56/56; shared 122/122; shared pointer bump.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CvqUd4QsJSGHN28aunJRTq
This commit is contained in:
Gergő Törcsvári 2026-07-07 11:59:19 +02:00
commit 21e3df8fd0
No known key found for this signature in database
GPG key ID: 8E75F2CDE64E5322
9 changed files with 399 additions and 70 deletions

View file

@ -165,6 +165,15 @@ inline double textWidth( const std::string& aText, double aGlyphH )
return aText.size() * aGlyphH * 0.75;
}
// Legible text color for a chip background: near-black on light colors,
// white on dark ones (BitmapText draws with the GAL stroke color).
inline KIGFX::COLOR4D chipTextColor( const KIGFX::COLOR4D& aBg )
{
double lum = 0.299 * aBg.r + 0.587 * aBg.g + 0.114 * aBg.b;
return lum > 0.6 ? KIGFX::COLOR4D( 0.08, 0.08, 0.08, 1.0 )
: KIGFX::COLOR4D( 1.0, 1.0, 1.0, 1.0 );
}
/** Name tag next to (or inside) a box, per the label placement knobs. `px` is
* world-units-per-screen-pixel. GAL BitmapText CENTERS on its position (the
* default justify is CENTER/CENTER — GAL::ResetTextAttributes), so the block
@ -203,7 +212,7 @@ inline void drawLabel( KIGFX::VIEW_OVERLAY* aOv, const BOX2I& aBox, const std::s
VECTOR2D( x + w + padX, y + h + padY ) );
aOv->SetIsStroke( true );
aOv->SetIsFill( false );
aOv->SetStrokeColor( KIGFX::COLOR4D( 1, 1, 1, 1 ) );
aOv->SetStrokeColor( chipTextColor( aColor ) );
}
else
{
@ -376,7 +385,7 @@ inline void drawCursor( KIGFX::VIEW_OVERLAY* aOv, const VECTOR2D& aPos, const st
at + VECTOR2D( w + padX, h + padY ) );
aOv->SetIsStroke( true );
aOv->SetIsFill( false );
aOv->SetStrokeColor( KIGFX::COLOR4D( 1, 1, 1, 1 ) );
aOv->SetStrokeColor( chipTextColor( aColor ) );
}
else
{