Skip to content

Commit 233d98a

Browse files
dabowmanclaude
andcommitted
Move collaborator colors from core-data to editor package
Replace the OKLCH color generation system in core-data with the simpler `AVATAR_BORDER_COLORS` array from `collab-sidebar/utils.js`. All editor consumers now use `getAvatarBorderColor(userId)` for a single color per collaborator (border, cursor, and block highlight), removing the need for separate stroke/background derivation and ~230 lines of color math. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent fd4b10f commit 233d98a

11 files changed

Lines changed: 31 additions & 424 deletions

File tree

‎packages/core-data/src/awareness/base-awareness.ts‎

Lines changed: 1 addition & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -23,21 +23,8 @@ export abstract class BaseAwarenessState<
2323
* Set the current collaborator info in the local state.
2424
*/
2525
private async setCurrentCollaboratorInfo(): Promise< void > {
26-
const states = this.getStates();
27-
const otherCollaboratorColors = Array.from( states.entries() )
28-
.filter(
29-
( [ clientId, state ] ) =>
30-
state.collaboratorInfo && clientId !== this.clientID
31-
)
32-
.map( ( [ , state ] ) => state.collaboratorInfo.color )
33-
.filter( Boolean );
34-
35-
// Get current user info and set it in local state.
3626
const currentUser = await resolveSelect( coreStore ).getCurrentUser();
37-
const collaboratorInfo = generateCollaboratorInfo(
38-
currentUser,
39-
otherCollaboratorColors
40-
);
27+
const collaboratorInfo = generateCollaboratorInfo( currentUser );
4128
this.setLocalStateField( 'collaboratorInfo', collaboratorInfo );
4229
}
4330
}

‎packages/core-data/src/awareness/test/base-awareness.ts‎

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -164,28 +164,24 @@ describe( 'BaseAwarenessState', () => {
164164
expect( resolveSelect ).toHaveBeenCalledWith( 'core' );
165165
} );
166166

167-
test( 'should exclude own color from available colors', async () => {
167+
test( 'should set collaboratorInfo for other users', async () => {
168168
// Set up another user state first
169169
const doc2 = new Y.Doc();
170170
const awareness = new TestBaseAwarenessState( doc );
171171

172-
// Manually add another user's state with a specific color
172+
// Manually add another user's state
173173
awareness.setLocalStateField( 'collaboratorInfo', {
174174
id: 2,
175175
name: 'Other User',
176176
slug: 'other-user',
177177
avatar_urls: mockAvatarUrls,
178178
browserType: 'Firefox',
179-
color: '#3858E9', // blueberry
180-
strokeColor: '#3858e9',
181-
backgroundColor: '#3858e9',
182179
enteredAt: 1704067200000,
183180
} );
184181

185182
awareness.setUp();
186183
await Promise.resolve();
187184

188-
// The new user should get a different color if possible
189185
const collaboratorInfo =
190186
awareness.getLocalStateField( 'collaboratorInfo' );
191187
expect( collaboratorInfo ).toBeDefined();
@@ -223,9 +219,6 @@ describe( 'BaseAwarenessState', () => {
223219
slug: 'custom-user',
224220
avatar_urls: mockAvatarUrls,
225221
browserType: 'Safari',
226-
color: '#E33184',
227-
strokeColor: '#e33184',
228-
backgroundColor: '#c3006a',
229222
enteredAt: 1704067200000,
230223
};
231224

@@ -247,9 +240,6 @@ describe( 'BaseAwarenessState', () => {
247240
slug: 'custom-user',
248241
avatar_urls: mockAvatarUrls,
249242
browserType: 'Safari',
250-
color: '#E33184',
251-
strokeColor: '#e33184',
252-
backgroundColor: '#c3006a',
253243
enteredAt: 1704067200000,
254244
};
255245

‎packages/core-data/src/awareness/test/post-editor-awareness.ts‎

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -491,9 +491,6 @@ describe( 'PostEditorAwareness', () => {
491491
slug: 'test',
492492
avatar_urls: mockAvatarUrls,
493493
browserType: 'Chrome',
494-
color: '#3858E9',
495-
strokeColor: '#3858e9',
496-
backgroundColor: '#3858e9',
497494
enteredAt: 1704067200000,
498495
};
499496

‎packages/core-data/src/awareness/test/utils.ts‎

Lines changed: 12 additions & 130 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,6 @@ describe( 'Awareness Utils', () => {
3131
slug: 'test-user',
3232
avatar_urls: sharedAvatarUrls,
3333
browserType: 'Chrome',
34-
color: '#3858E9',
35-
strokeColor: '#3858e9',
36-
backgroundColor: '#3858e9',
3734
enteredAt: 1704067200000,
3835
...overrides,
3936
} );
@@ -125,51 +122,6 @@ describe( 'Awareness Utils', () => {
125122
).toBe( false );
126123
} );
127124

128-
test( 'should return false when color differs', () => {
129-
const collaboratorInfo1 = createCollaboratorInfo( {
130-
color: '#3858E9',
131-
} );
132-
const collaboratorInfo2 = createCollaboratorInfo( {
133-
color: '#B42AED',
134-
} );
135-
expect(
136-
areCollaboratorInfosEqual(
137-
collaboratorInfo1,
138-
collaboratorInfo2
139-
)
140-
).toBe( false );
141-
} );
142-
143-
test( 'should return false when strokeColor differs', () => {
144-
const collaboratorInfo1 = createCollaboratorInfo( {
145-
strokeColor: '#3858e9',
146-
} );
147-
const collaboratorInfo2 = createCollaboratorInfo( {
148-
strokeColor: '#9500ff',
149-
} );
150-
expect(
151-
areCollaboratorInfosEqual(
152-
collaboratorInfo1,
153-
collaboratorInfo2
154-
)
155-
).toBe( false );
156-
} );
157-
158-
test( 'should return false when backgroundColor differs', () => {
159-
const collaboratorInfo1 = createCollaboratorInfo( {
160-
backgroundColor: '#3858e9',
161-
} );
162-
const collaboratorInfo2 = createCollaboratorInfo( {
163-
backgroundColor: '#9500ff',
164-
} );
165-
expect(
166-
areCollaboratorInfosEqual(
167-
collaboratorInfo1,
168-
collaboratorInfo2
169-
)
170-
).toBe( false );
171-
} );
172-
173125
test( 'should return false when enteredAt differs', () => {
174126
const collaboratorInfo1 = createCollaboratorInfo( {
175127
enteredAt: 1000,
@@ -231,7 +183,7 @@ describe( 'Awareness Utils', () => {
231183

232184
test( 'should generate collaboratorInfo with user properties', () => {
233185
const user = createMockUser( { id: 42, name: 'Jane Doe' } );
234-
const collaboratorInfo = generateCollaboratorInfo( user, [] );
186+
const collaboratorInfo = generateCollaboratorInfo( user );
235187

236188
expect( collaboratorInfo.id ).toBe( 42 );
237189
expect( collaboratorInfo.name ).toBe( 'Jane Doe' );
@@ -240,7 +192,7 @@ describe( 'Awareness Utils', () => {
240192

241193
test( 'should include browser type', () => {
242194
const user = createMockUser();
243-
const collaboratorInfo = generateCollaboratorInfo( user, [] );
195+
const collaboratorInfo = generateCollaboratorInfo( user );
244196

245197
expect( collaboratorInfo.browserType ).toBe( 'Chrome' );
246198
} );
@@ -250,7 +202,7 @@ describe( 'Awareness Utils', () => {
250202
'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:89.0) Gecko/20100101 Firefox/89.0'
251203
);
252204
const user = createMockUser();
253-
const collaboratorInfo = generateCollaboratorInfo( user, [] );
205+
const collaboratorInfo = generateCollaboratorInfo( user );
254206

255207
expect( collaboratorInfo.browserType ).toBe( 'Firefox' );
256208
} );
@@ -260,7 +212,7 @@ describe( 'Awareness Utils', () => {
260212
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36 Edg/91.0.864.59'
261213
);
262214
const user = createMockUser();
263-
const collaboratorInfo = generateCollaboratorInfo( user, [] );
215+
const collaboratorInfo = generateCollaboratorInfo( user );
264216

265217
expect( collaboratorInfo.browserType ).toBe( 'Microsoft Edge' );
266218
} );
@@ -270,7 +222,7 @@ describe( 'Awareness Utils', () => {
270222
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.1.1 Safari/605.1.15'
271223
);
272224
const user = createMockUser();
273-
const collaboratorInfo = generateCollaboratorInfo( user, [] );
225+
const collaboratorInfo = generateCollaboratorInfo( user );
274226

275227
expect( collaboratorInfo.browserType ).toBe( 'Safari' );
276228
} );
@@ -280,7 +232,7 @@ describe( 'Awareness Utils', () => {
280232
'Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; SLCC2; .NET CLR 2.0.50727; MSIE 10.0; rv:11.0) like Gecko'
281233
);
282234
const user = createMockUser();
283-
const collaboratorInfo = generateCollaboratorInfo( user, [] );
235+
const collaboratorInfo = generateCollaboratorInfo( user );
284236

285237
expect( collaboratorInfo.browserType ).toBe( 'Internet Explorer' );
286238
} );
@@ -290,7 +242,7 @@ describe( 'Awareness Utils', () => {
290242
'Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko'
291243
);
292244
const user = createMockUser();
293-
const collaboratorInfo = generateCollaboratorInfo( user, [] );
245+
const collaboratorInfo = generateCollaboratorInfo( user );
294246

295247
expect( collaboratorInfo.browserType ).toBe( 'Internet Explorer' );
296248
} );
@@ -300,7 +252,7 @@ describe( 'Awareness Utils', () => {
300252
'Opera/9.80 (Windows NT 6.1; WOW64) Presto/2.12.388 Version/12.18'
301253
);
302254
const user = createMockUser();
303-
const collaboratorInfo = generateCollaboratorInfo( user, [] );
255+
const collaboratorInfo = generateCollaboratorInfo( user );
304256

305257
expect( collaboratorInfo.browserType ).toBe( 'Opera' );
306258
} );
@@ -313,96 +265,26 @@ describe( 'Awareness Utils', () => {
313265
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) OPR/77.0.4054.203'
314266
);
315267
const user = createMockUser();
316-
const collaboratorInfo = generateCollaboratorInfo( user, [] );
268+
const collaboratorInfo = generateCollaboratorInfo( user );
317269

318270
expect( collaboratorInfo.browserType ).toBe( 'Opera' );
319271
} );
320272

321273
test( 'should return Unknown for unrecognized browser', () => {
322274
mockUserAgent( 'Some Unknown Browser/1.0' );
323275
const user = createMockUser();
324-
const collaboratorInfo = generateCollaboratorInfo( user, [] );
276+
const collaboratorInfo = generateCollaboratorInfo( user );
325277

326278
expect( collaboratorInfo.browserType ).toBe( 'Unknown' );
327279
} );
328280

329281
test( 'should include enteredAt timestamp', () => {
330282
const user = createMockUser();
331-
const collaboratorInfo = generateCollaboratorInfo( user, [] );
283+
const collaboratorInfo = generateCollaboratorInfo( user );
332284

333285
expect( collaboratorInfo.enteredAt ).toBe( 1704067200000 );
334286
} );
335287

336-
test( 'should assign a color from the palette when no existing colors', () => {
337-
const user = createMockUser();
338-
const collaboratorInfo = generateCollaboratorInfo( user, [] );
339-
340-
const colorPalette = [
341-
'#3858E9',
342-
'#9500FF',
343-
'#FF0080',
344-
'#F4800B',
345-
'#FF00EA',
346-
'#C3E619',
347-
'#4DB3A2',
348-
'#00D5FF',
349-
];
350-
expect( colorPalette ).toContain( collaboratorInfo.color );
351-
} );
352-
353-
test( 'should derive strokeColor and backgroundColor from seed', () => {
354-
const user = createMockUser();
355-
const collaboratorInfo = generateCollaboratorInfo( user, [] );
356-
357-
expect( collaboratorInfo.strokeColor ).toMatch( /^#[0-9a-f]{6}$/i );
358-
expect( collaboratorInfo.backgroundColor ).toMatch(
359-
/^#[0-9a-f]{6}$/i
360-
);
361-
} );
362-
363-
test( 'should avoid existing colors when assigning', () => {
364-
const user = createMockUser();
365-
const existingColors = [
366-
'#3858E9',
367-
'#9500FF',
368-
'#FF0080',
369-
'#F4800B',
370-
'#FF00EA',
371-
'#C3E619',
372-
'#4DB3A2',
373-
];
374-
const collaboratorInfo = generateCollaboratorInfo(
375-
user,
376-
existingColors
377-
);
378-
379-
// The only available color from the palette is #00D5FF (cyan)
380-
expect( collaboratorInfo.color ).toBe( '#00D5FF' );
381-
} );
382-
383-
test( 'should generate color variation when all palette colors are in use', () => {
384-
const user = createMockUser();
385-
const existingColors = [
386-
'#3858E9',
387-
'#9500FF',
388-
'#FF0080',
389-
'#F4800B',
390-
'#FF00EA',
391-
'#C3E619',
392-
'#4DB3A2',
393-
'#00D5FF',
394-
];
395-
const collaboratorInfo = generateCollaboratorInfo(
396-
user,
397-
existingColors
398-
);
399-
400-
// Should be a valid hex color
401-
expect( collaboratorInfo.color ).toMatch( /^#[0-9a-f]{6}$/i );
402-
// And should not be one of the original colors
403-
// (it's a hue-rotated variation)
404-
} );
405-
406288
test( 'should include avatar_urls from user', () => {
407289
const user = createMockUser( {
408290
avatar_urls: {
@@ -411,7 +293,7 @@ describe( 'Awareness Utils', () => {
411293
'96': 'https://example.com/large.png',
412294
},
413295
} );
414-
const collaboratorInfo = generateCollaboratorInfo( user, [] );
296+
const collaboratorInfo = generateCollaboratorInfo( user );
415297

416298
expect( collaboratorInfo.avatar_urls ).toEqual( {
417299
'24': 'https://example.com/small.png',

‎packages/core-data/src/awareness/types.ts‎

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,6 @@ export type CollaboratorInfo = Pick<
1414
'id' | 'name' | 'slug' | 'avatar_urls'
1515
> & {
1616
browserType: string;
17-
color: string;
18-
strokeColor: string;
19-
backgroundColor: string;
2017
enteredAt: number;
2118
};
2219

0 commit comments

Comments
 (0)