From 9f4c3db72fe9a47121d50bc5fde0618aa8572a34 Mon Sep 17 00:00:00 2001 From: rasmi Date: Wed, 11 Mar 2026 14:30:05 -0400 Subject: [PATCH 1/8] Add showParticipantNumber option. --- docs/assets/api/schemas.json | 3 ++ .../components/stages/profile_stage_editor.ts | 30 +++++++++++++++---- functions/src/participant.endpoints.ts | 12 ++++++-- scripts/deliberate_lab/types.py | 1 + utils/src/participant.ts | 15 +++++++++- utils/src/stages/profile_stage.ts | 2 ++ utils/src/stages/profile_stage.validation.ts | 1 + 7 files changed, 56 insertions(+), 8 deletions(-) diff --git a/docs/assets/api/schemas.json b/docs/assets/api/schemas.json index 3746375a7..f783b8781 100644 --- a/docs/assets/api/schemas.json +++ b/docs/assets/api/schemas.json @@ -1450,6 +1450,9 @@ "type": "string" } ] + }, + "showParticipantNumber": { + "type": "boolean" } } }, diff --git a/frontend/src/components/stages/profile_stage_editor.ts b/frontend/src/components/stages/profile_stage_editor.ts index 760ade01d..b27c6a73e 100644 --- a/frontend/src/components/stages/profile_stage_editor.ts +++ b/frontend/src/components/stages/profile_stage_editor.ts @@ -1,4 +1,5 @@ import '../../pair-components/textarea'; +import '@material/web/checkbox/checkbox.js'; import '@material/web/radio/radio'; import {MobxLitElement} from '@adobe/lit-mobx'; @@ -8,11 +9,7 @@ import {customElement, property} from 'lit/decorators.js'; import {core} from '../../core/core'; import {ExperimentEditor} from '../../services/experiment.editor'; -import { - ProfileType, - ProfileStageConfig, - StageKind, -} from '@deliberation-lab/utils'; +import {ProfileType, ProfileStageConfig} from '@deliberation-lab/utils'; import {styles} from './profile_stage_editor.scss'; @@ -99,6 +96,29 @@ export class ProfileStageEditorComponent extends MobxLitElement { > + ${this.stage.profileType === ProfileType.ANONYMOUS_ANIMAL + ? html` + + ` + : nothing}
{ ) as ProfileStageConfig | undefined; const profileType = profileStage?.profileType || ProfileType.ANONYMOUS_ANIMAL; - - setProfile(numParticipants, participantConfig, true, profileType); + const showParticipantNumber = + profileStage?.showParticipantNumber ?? false; + + setProfile( + numParticipants, + participantConfig, + true, + profileType, + showParticipantNumber, + ); } else { setProfile(numParticipants, participantConfig, false); } diff --git a/scripts/deliberate_lab/types.py b/scripts/deliberate_lab/types.py index 0d717a9c6..02fdae077 100644 --- a/scripts/deliberate_lab/types.py +++ b/scripts/deliberate_lab/types.py @@ -391,6 +391,7 @@ class ProfileStageConfig(BaseModel): descriptions: StageTextConfig progress: StageProgressConfig profileType: ProfileType + showParticipantNumber: bool | None = None class Strategy(StrEnum): diff --git a/utils/src/participant.ts b/utils/src/participant.ts index 044ede71c..700c64080 100644 --- a/utils/src/participant.ts +++ b/utils/src/participant.ts @@ -191,6 +191,7 @@ export function setProfile( config: ParticipantProfileExtended, setAnonymousProfile = false, profileType: ProfileType = ProfileType.ANONYMOUS_ANIMAL, + showParticipantNumber = false, ) { const generateProfileFromSet = ( profileSet: {name: string; avatar: string}[], @@ -229,6 +230,13 @@ export function setProfile( const profileNature = generateProfileFromSet(PROFILE_SET_NATURE); const profileAnonymousParticipant = generateAnonymousParticipantProfile(); + // Append random number to profile names (e.g., "Bear 1002" instead of "Bear") + if (showParticipantNumber) { + profileAnimal1.name = `${profileAnimal1.name} ${randomNumber}`; + profileAnimal2.name = `${profileAnimal2.name} ${randomNumber}`; + profileNature.name = `${profileNature.name} ${randomNumber}`; + } + config.anonymousProfiles[PROFILE_SET_ANIMALS_1_ID] = profileAnimal1; config.anonymousProfiles[PROFILE_SET_ANIMALS_2_ID] = profileAnimal2; config.anonymousProfiles[PROFILE_SET_NATURE_ID] = profileNature; @@ -259,7 +267,12 @@ export function setProfile( config.avatar = participantProfile.avatar; } else if (profileType === ProfileType.ANONYMOUS_ANIMAL) { // Use animal profile (default) - config.name = `${mainProfile.name}${mainProfile.repeat === 0 ? '' : ` ${mainProfile.repeat + 1}`}`; + if (showParticipantNumber) { + // Name already has numeric suffix (e.g., "Bear 1002") + config.name = mainProfile.name; + } else { + config.name = `${mainProfile.name}${mainProfile.repeat === 0 ? '' : ` ${mainProfile.repeat + 1}`}`; + } config.avatar = mainProfile.avatar; } // Note: ProfileType.DEFAULT should not reach here as setAnonymousProfile would be false diff --git a/utils/src/stages/profile_stage.ts b/utils/src/stages/profile_stage.ts index f10607691..8cd4a36bb 100644 --- a/utils/src/stages/profile_stage.ts +++ b/utils/src/stages/profile_stage.ts @@ -22,6 +22,7 @@ export enum ProfileType { export interface ProfileStageConfig extends BaseStageConfig { kind: StageKind.PROFILE; profileType: ProfileType; + showParticipantNumber?: boolean; // e.g., "Bear 1002" instead of "Bear" } // ************************************************************************* // @@ -39,5 +40,6 @@ export function createProfileStage( descriptions: config.descriptions ?? createStageTextConfig(), progress: config.progress ?? createStageProgressConfig(), profileType: config.profileType ?? ProfileType.DEFAULT, + showParticipantNumber: config.showParticipantNumber ?? true, }; } diff --git a/utils/src/stages/profile_stage.validation.ts b/utils/src/stages/profile_stage.validation.ts index d1fbe4183..5f07a0f4e 100644 --- a/utils/src/stages/profile_stage.validation.ts +++ b/utils/src/stages/profile_stage.validation.ts @@ -23,6 +23,7 @@ export const ProfileStageConfigData = Type.Composite( Type.Literal(ProfileType.ANONYMOUS_ANIMAL), Type.Literal(ProfileType.ANONYMOUS_PARTICIPANT), ]), + showParticipantNumber: Type.Optional(Type.Boolean()), }, strict, ), From 31962da9ff4865d4d4ab536bd01ad9d985908bba Mon Sep 17 00:00:00 2001 From: rasmi Date: Wed, 11 Mar 2026 14:31:13 -0400 Subject: [PATCH 2/8] Have quickstart use anonymous animal profiles. --- frontend/src/shared/templates/quickstart_private_chat.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/shared/templates/quickstart_private_chat.ts b/frontend/src/shared/templates/quickstart_private_chat.ts index 06b30460d..54c25a8c9 100644 --- a/frontend/src/shared/templates/quickstart_private_chat.ts +++ b/frontend/src/shared/templates/quickstart_private_chat.ts @@ -46,7 +46,7 @@ const CHAT_STAGE_ID = 'chat'; function getStageConfigs(): StageConfig[] { const stages: StageConfig[] = []; stages.push( - createProfileStage(), + createProfileStage({profileType: ProfileType.ANONYMOUS_ANIMAL}), createPrivateChatStage({ id: CHAT_STAGE_ID, name: 'Private chat with agent', From f8dfb4c02b2f38dd52d70aa5fb89a70bf7365729 Mon Sep 17 00:00:00 2001 From: rasmi Date: Wed, 11 Mar 2026 14:37:36 -0400 Subject: [PATCH 3/8] showParticipantNumber true by default in API. --- docs/assets/api/schemas.json | 1 + scripts/deliberate_lab/types.py | 2 +- utils/src/stages/profile_stage.validation.ts | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/assets/api/schemas.json b/docs/assets/api/schemas.json index f783b8781..bb6ac33a5 100644 --- a/docs/assets/api/schemas.json +++ b/docs/assets/api/schemas.json @@ -1452,6 +1452,7 @@ ] }, "showParticipantNumber": { + "default": true, "type": "boolean" } } diff --git a/scripts/deliberate_lab/types.py b/scripts/deliberate_lab/types.py index 02fdae077..372c4adb2 100644 --- a/scripts/deliberate_lab/types.py +++ b/scripts/deliberate_lab/types.py @@ -391,7 +391,7 @@ class ProfileStageConfig(BaseModel): descriptions: StageTextConfig progress: StageProgressConfig profileType: ProfileType - showParticipantNumber: bool | None = None + showParticipantNumber: bool | None = True class Strategy(StrEnum): diff --git a/utils/src/stages/profile_stage.validation.ts b/utils/src/stages/profile_stage.validation.ts index 5f07a0f4e..744093d45 100644 --- a/utils/src/stages/profile_stage.validation.ts +++ b/utils/src/stages/profile_stage.validation.ts @@ -23,7 +23,7 @@ export const ProfileStageConfigData = Type.Composite( Type.Literal(ProfileType.ANONYMOUS_ANIMAL), Type.Literal(ProfileType.ANONYMOUS_PARTICIPANT), ]), - showParticipantNumber: Type.Optional(Type.Boolean()), + showParticipantNumber: Type.Optional(Type.Boolean({default: true})), }, strict, ), From 7cea91996f7f8cb9c43360a5bed8905f64704c0f Mon Sep 17 00:00:00 2001 From: rasmi Date: Wed, 11 Mar 2026 14:53:29 -0400 Subject: [PATCH 4/8] Always show participant number for anonymous profiles. --- docs/assets/api/schemas.json | 4 ---- .../components/stages/profile_stage_editor.ts | 24 ------------------- functions/src/participant.endpoints.ts | 12 ++-------- scripts/deliberate_lab/types.py | 1 - utils/src/participant.ts | 18 ++++---------- utils/src/stages/profile_stage.ts | 2 -- utils/src/stages/profile_stage.validation.ts | 1 - 7 files changed, 7 insertions(+), 55 deletions(-) diff --git a/docs/assets/api/schemas.json b/docs/assets/api/schemas.json index bb6ac33a5..3746375a7 100644 --- a/docs/assets/api/schemas.json +++ b/docs/assets/api/schemas.json @@ -1450,10 +1450,6 @@ "type": "string" } ] - }, - "showParticipantNumber": { - "default": true, - "type": "boolean" } } }, diff --git a/frontend/src/components/stages/profile_stage_editor.ts b/frontend/src/components/stages/profile_stage_editor.ts index b27c6a73e..27ca11c41 100644 --- a/frontend/src/components/stages/profile_stage_editor.ts +++ b/frontend/src/components/stages/profile_stage_editor.ts @@ -1,5 +1,4 @@ import '../../pair-components/textarea'; -import '@material/web/checkbox/checkbox.js'; import '@material/web/radio/radio'; import {MobxLitElement} from '@adobe/lit-mobx'; @@ -96,29 +95,6 @@ export class ProfileStageEditorComponent extends MobxLitElement { >
- ${this.stage.profileType === ProfileType.ANONYMOUS_ANIMAL - ? html` - - ` - : nothing}
{ ) as ProfileStageConfig | undefined; const profileType = profileStage?.profileType || ProfileType.ANONYMOUS_ANIMAL; - const showParticipantNumber = - profileStage?.showParticipantNumber ?? false; - - setProfile( - numParticipants, - participantConfig, - true, - profileType, - showParticipantNumber, - ); + + setProfile(numParticipants, participantConfig, true, profileType); } else { setProfile(numParticipants, participantConfig, false); } diff --git a/scripts/deliberate_lab/types.py b/scripts/deliberate_lab/types.py index 372c4adb2..0d717a9c6 100644 --- a/scripts/deliberate_lab/types.py +++ b/scripts/deliberate_lab/types.py @@ -391,7 +391,6 @@ class ProfileStageConfig(BaseModel): descriptions: StageTextConfig progress: StageProgressConfig profileType: ProfileType - showParticipantNumber: bool | None = True class Strategy(StrEnum): diff --git a/utils/src/participant.ts b/utils/src/participant.ts index 700c64080..8ddb0f12d 100644 --- a/utils/src/participant.ts +++ b/utils/src/participant.ts @@ -191,7 +191,6 @@ export function setProfile( config: ParticipantProfileExtended, setAnonymousProfile = false, profileType: ProfileType = ProfileType.ANONYMOUS_ANIMAL, - showParticipantNumber = false, ) { const generateProfileFromSet = ( profileSet: {name: string; avatar: string}[], @@ -230,12 +229,10 @@ export function setProfile( const profileNature = generateProfileFromSet(PROFILE_SET_NATURE); const profileAnonymousParticipant = generateAnonymousParticipantProfile(); - // Append random number to profile names (e.g., "Bear 1002" instead of "Bear") - if (showParticipantNumber) { - profileAnimal1.name = `${profileAnimal1.name} ${randomNumber}`; - profileAnimal2.name = `${profileAnimal2.name} ${randomNumber}`; - profileNature.name = `${profileNature.name} ${randomNumber}`; - } + // Append random number to profile names (e.g., "Bear 1002") + profileAnimal1.name = `${profileAnimal1.name} ${randomNumber}`; + profileAnimal2.name = `${profileAnimal2.name} ${randomNumber}`; + profileNature.name = `${profileNature.name} ${randomNumber}`; config.anonymousProfiles[PROFILE_SET_ANIMALS_1_ID] = profileAnimal1; config.anonymousProfiles[PROFILE_SET_ANIMALS_2_ID] = profileAnimal2; @@ -267,12 +264,7 @@ export function setProfile( config.avatar = participantProfile.avatar; } else if (profileType === ProfileType.ANONYMOUS_ANIMAL) { // Use animal profile (default) - if (showParticipantNumber) { - // Name already has numeric suffix (e.g., "Bear 1002") - config.name = mainProfile.name; - } else { - config.name = `${mainProfile.name}${mainProfile.repeat === 0 ? '' : ` ${mainProfile.repeat + 1}`}`; - } + config.name = mainProfile.name; config.avatar = mainProfile.avatar; } // Note: ProfileType.DEFAULT should not reach here as setAnonymousProfile would be false diff --git a/utils/src/stages/profile_stage.ts b/utils/src/stages/profile_stage.ts index 8cd4a36bb..f10607691 100644 --- a/utils/src/stages/profile_stage.ts +++ b/utils/src/stages/profile_stage.ts @@ -22,7 +22,6 @@ export enum ProfileType { export interface ProfileStageConfig extends BaseStageConfig { kind: StageKind.PROFILE; profileType: ProfileType; - showParticipantNumber?: boolean; // e.g., "Bear 1002" instead of "Bear" } // ************************************************************************* // @@ -40,6 +39,5 @@ export function createProfileStage( descriptions: config.descriptions ?? createStageTextConfig(), progress: config.progress ?? createStageProgressConfig(), profileType: config.profileType ?? ProfileType.DEFAULT, - showParticipantNumber: config.showParticipantNumber ?? true, }; } diff --git a/utils/src/stages/profile_stage.validation.ts b/utils/src/stages/profile_stage.validation.ts index 744093d45..d1fbe4183 100644 --- a/utils/src/stages/profile_stage.validation.ts +++ b/utils/src/stages/profile_stage.validation.ts @@ -23,7 +23,6 @@ export const ProfileStageConfigData = Type.Composite( Type.Literal(ProfileType.ANONYMOUS_ANIMAL), Type.Literal(ProfileType.ANONYMOUS_PARTICIPANT), ]), - showParticipantNumber: Type.Optional(Type.Boolean({default: true})), }, strict, ), From 2f05d65380c1969d473f90b2a7f3b766cfb93060 Mon Sep 17 00:00:00 2001 From: rasmi Date: Wed, 11 Mar 2026 15:14:07 -0400 Subject: [PATCH 5/8] Consolidate anonymous profile creation logic. --- utils/src/participant.ts | 58 +++++++++++++++++----------------------- 1 file changed, 24 insertions(+), 34 deletions(-) diff --git a/utils/src/participant.ts b/utils/src/participant.ts index 8ddb0f12d..b70b192e6 100644 --- a/utils/src/participant.ts +++ b/utils/src/participant.ts @@ -192,15 +192,19 @@ export function setProfile( setAnonymousProfile = false, profileType: ProfileType = ProfileType.ANONYMOUS_ANIMAL, ) { + // Generate random number for unique participant ID + const randomNumber = Math.floor(Math.random() * 10000); + const generateProfileFromSet = ( profileSet: {name: string; avatar: string}[], + repeat?: number, ): AnonymousProfileMetadata => { // TODO: Randomly select from set const {name, avatar} = profileSet[participantNumber % profileSet.length]; return { - name, + name: `${name} ${randomNumber}`, avatar, - repeat: Math.floor(participantNumber / profileSet.length), + repeat: repeat ?? Math.floor(participantNumber / profileSet.length), }; }; @@ -212,27 +216,14 @@ export function setProfile( }; }; - // Generate random number for unique participant ID (used in publicID and anonymous participant profile) - const randomNumber = Math.floor(Math.random() * 10000); - - const generateAnonymousParticipantProfile = (): AnonymousProfileMetadata => { - return { - name: `Participant ${randomNumber}`, - avatar: '👤', - repeat: 0, - }; - }; - // Set anonymous profiles const profileAnimal1 = generateProfileFromSet(PROFILE_SET_ANIMALS_1); const profileAnimal2 = generateProfileFromSet(PROFILE_SET_ANIMALS_2); const profileNature = generateProfileFromSet(PROFILE_SET_NATURE); - const profileAnonymousParticipant = generateAnonymousParticipantProfile(); - - // Append random number to profile names (e.g., "Bear 1002") - profileAnimal1.name = `${profileAnimal1.name} ${randomNumber}`; - profileAnimal2.name = `${profileAnimal2.name} ${randomNumber}`; - profileNature.name = `${profileNature.name} ${randomNumber}`; + const profileAnonymousParticipant = generateProfileFromSet( + [{name: 'Participant', avatar: '👤'}], + 0, + ); config.anonymousProfiles[PROFILE_SET_ANIMALS_1_ID] = profileAnimal1; config.anonymousProfiles[PROFILE_SET_ANIMALS_2_ID] = profileAnimal2; @@ -248,26 +239,25 @@ export function setProfile( config.anonymousProfiles[PROFILE_SET_RANDOM_3_ID] = generateRandomHashProfile(); - // Define public ID (using anonymous animal 1 set) - const mainProfile = profileAnimal1; + // Define public ID (using base animal name without number) + const baseName = + PROFILE_SET_ANIMALS_1[participantNumber % PROFILE_SET_ANIMALS_1.length] + .name; const color = COLORS[Math.floor(Math.random() * COLORS.length)]; - config.publicId = - `${mainProfile.name}-${color}-${randomNumber}`.toLowerCase(); + config.publicId = `${baseName}-${color}-${randomNumber}`.toLowerCase(); if (setAnonymousProfile) { - if (profileType === ProfileType.ANONYMOUS_PARTICIPANT) { - // Use participant number profile - const participantProfile = - config.anonymousProfiles[PROFILE_SET_ANONYMOUS_PARTICIPANT_ID]; - config.name = participantProfile.name; - config.avatar = participantProfile.avatar; - } else if (profileType === ProfileType.ANONYMOUS_ANIMAL) { - // Use animal profile (default) - config.name = mainProfile.name; - config.avatar = mainProfile.avatar; + const profileSetMap: Partial> = { + [ProfileType.ANONYMOUS_ANIMAL]: PROFILE_SET_ANIMALS_1_ID, + [ProfileType.ANONYMOUS_PARTICIPANT]: PROFILE_SET_ANONYMOUS_PARTICIPANT_ID, + }; + const profileSetId = profileSetMap[profileType]; + if (profileSetId) { + const profile = config.anonymousProfiles[profileSetId]; + config.name = profile.name; + config.avatar = profile.avatar; } - // Note: ProfileType.DEFAULT should not reach here as setAnonymousProfile would be false config.pronouns = ''; } } From b68f49ba56529f230825d3cac0d8c8eb321c2f97 Mon Sep 17 00:00:00 2001 From: rasmi Date: Wed, 11 Mar 2026 15:17:13 -0400 Subject: [PATCH 6/8] tidy up --- utils/src/participant.ts | 54 ++++++++++++++-------------------------- 1 file changed, 19 insertions(+), 35 deletions(-) diff --git a/utils/src/participant.ts b/utils/src/participant.ts index b70b192e6..975b9468d 100644 --- a/utils/src/participant.ts +++ b/utils/src/participant.ts @@ -192,61 +192,45 @@ export function setProfile( setAnonymousProfile = false, profileType: ProfileType = ProfileType.ANONYMOUS_ANIMAL, ) { - // Generate random number for unique participant ID const randomNumber = Math.floor(Math.random() * 10000); - const generateProfileFromSet = ( + // Create anonymous profile from a named profile set. + // Appends random number to name (e.g., "Bear 1002", "Participant 1002"). + const profileFromSet = ( profileSet: {name: string; avatar: string}[], - repeat?: number, ): AnonymousProfileMetadata => { - // TODO: Randomly select from set const {name, avatar} = profileSet[participantNumber % profileSet.length]; return { name: `${name} ${randomNumber}`, avatar, - repeat: repeat ?? Math.floor(participantNumber / profileSet.length), + repeat: Math.floor(participantNumber / profileSet.length), }; }; - const generateRandomHashProfile = (): AnonymousProfileMetadata => { - return { - name: generateId(), - avatar: '', + // Set anonymous profiles for each profile set + config.anonymousProfiles = { + [PROFILE_SET_ANIMALS_1_ID]: profileFromSet(PROFILE_SET_ANIMALS_1), + [PROFILE_SET_ANIMALS_2_ID]: profileFromSet(PROFILE_SET_ANIMALS_2), + [PROFILE_SET_NATURE_ID]: profileFromSet(PROFILE_SET_NATURE), + [PROFILE_SET_ANONYMOUS_PARTICIPANT_ID]: { + name: `Participant ${randomNumber}`, + avatar: '👤', repeat: 0, - }; + }, + // Random hashes for ordering/randomization + [PROFILE_SET_RANDOM_1_ID]: {name: generateId(), avatar: '', repeat: 0}, + [PROFILE_SET_RANDOM_2_ID]: {name: generateId(), avatar: '', repeat: 0}, + [PROFILE_SET_RANDOM_3_ID]: {name: generateId(), avatar: '', repeat: 0}, }; - // Set anonymous profiles - const profileAnimal1 = generateProfileFromSet(PROFILE_SET_ANIMALS_1); - const profileAnimal2 = generateProfileFromSet(PROFILE_SET_ANIMALS_2); - const profileNature = generateProfileFromSet(PROFILE_SET_NATURE); - const profileAnonymousParticipant = generateProfileFromSet( - [{name: 'Participant', avatar: '👤'}], - 0, - ); - - config.anonymousProfiles[PROFILE_SET_ANIMALS_1_ID] = profileAnimal1; - config.anonymousProfiles[PROFILE_SET_ANIMALS_2_ID] = profileAnimal2; - config.anonymousProfiles[PROFILE_SET_NATURE_ID] = profileNature; - config.anonymousProfiles[PROFILE_SET_ANONYMOUS_PARTICIPANT_ID] = - profileAnonymousParticipant; - - // Set random hashes (can be used for random ordering, etc.) - config.anonymousProfiles[PROFILE_SET_RANDOM_1_ID] = - generateRandomHashProfile(); - config.anonymousProfiles[PROFILE_SET_RANDOM_2_ID] = - generateRandomHashProfile(); - config.anonymousProfiles[PROFILE_SET_RANDOM_3_ID] = - generateRandomHashProfile(); - - // Define public ID (using base animal name without number) + // Define public ID using base animal name (without number suffix) const baseName = PROFILE_SET_ANIMALS_1[participantNumber % PROFILE_SET_ANIMALS_1.length] .name; const color = COLORS[Math.floor(Math.random() * COLORS.length)]; - config.publicId = `${baseName}-${color}-${randomNumber}`.toLowerCase(); + // Set display profile for anonymous participants if (setAnonymousProfile) { const profileSetMap: Partial> = { [ProfileType.ANONYMOUS_ANIMAL]: PROFILE_SET_ANIMALS_1_ID, From aeced5b8aa1a50c8e293fb352581e693e3687d32 Mon Sep 17 00:00:00 2001 From: rasmi Date: Wed, 11 Mar 2026 15:25:57 -0400 Subject: [PATCH 7/8] Add informalNameStyle flag. --- docs/assets/api/schemas.json | 4 ++++ .../components/stages/profile_stage_editor.ts | 24 +++++++++++++++++++ functions/src/participant.endpoints.ts | 11 +++++++-- scripts/deliberate_lab/types.py | 1 + utils/src/participant.ts | 14 +++++++++-- utils/src/stages/profile_stage.ts | 2 ++ utils/src/stages/profile_stage.validation.ts | 1 + 7 files changed, 53 insertions(+), 4 deletions(-) diff --git a/docs/assets/api/schemas.json b/docs/assets/api/schemas.json index 3746375a7..0a3786c36 100644 --- a/docs/assets/api/schemas.json +++ b/docs/assets/api/schemas.json @@ -1450,6 +1450,10 @@ "type": "string" } ] + }, + "informalNameStyle": { + "default": false, + "type": "boolean" } } }, diff --git a/frontend/src/components/stages/profile_stage_editor.ts b/frontend/src/components/stages/profile_stage_editor.ts index 27ca11c41..69cff260a 100644 --- a/frontend/src/components/stages/profile_stage_editor.ts +++ b/frontend/src/components/stages/profile_stage_editor.ts @@ -1,4 +1,5 @@ import '../../pair-components/textarea'; +import '@material/web/checkbox/checkbox.js'; import '@material/web/radio/radio'; import {MobxLitElement} from '@adobe/lit-mobx'; @@ -95,6 +96,29 @@ export class ProfileStageEditorComponent extends MobxLitElement { >
+ ${this.stage.profileType === ProfileType.ANONYMOUS_ANIMAL + ? html` + + ` + : nothing}
{ ) as ProfileStageConfig | undefined; const profileType = profileStage?.profileType || ProfileType.ANONYMOUS_ANIMAL; - - setProfile(numParticipants, participantConfig, true, profileType); + const informalNameStyle = profileStage?.informalNameStyle ?? false; + + setProfile( + numParticipants, + participantConfig, + true, + profileType, + informalNameStyle, + ); } else { setProfile(numParticipants, participantConfig, false); } diff --git a/scripts/deliberate_lab/types.py b/scripts/deliberate_lab/types.py index 0d717a9c6..d086be45c 100644 --- a/scripts/deliberate_lab/types.py +++ b/scripts/deliberate_lab/types.py @@ -391,6 +391,7 @@ class ProfileStageConfig(BaseModel): descriptions: StageTextConfig progress: StageProgressConfig profileType: ProfileType + informalNameStyle: bool | None = False class Strategy(StrEnum): diff --git a/utils/src/participant.ts b/utils/src/participant.ts index 975b9468d..41b0d697e 100644 --- a/utils/src/participant.ts +++ b/utils/src/participant.ts @@ -191,17 +191,27 @@ export function setProfile( config: ParticipantProfileExtended, setAnonymousProfile = false, profileType: ProfileType = ProfileType.ANONYMOUS_ANIMAL, + informalNameStyle = false, ) { const randomNumber = Math.floor(Math.random() * 10000); + // Format name with random number. + // Informal style: "bear123" (lowercase, no space) + // Default style: "Bear 1002" + const formatName = (name: string) => { + if (informalNameStyle) { + return `${name.toLowerCase()}${randomNumber}`; + } + return `${name} ${randomNumber}`; + }; + // Create anonymous profile from a named profile set. - // Appends random number to name (e.g., "Bear 1002", "Participant 1002"). const profileFromSet = ( profileSet: {name: string; avatar: string}[], ): AnonymousProfileMetadata => { const {name, avatar} = profileSet[participantNumber % profileSet.length]; return { - name: `${name} ${randomNumber}`, + name: formatName(name), avatar, repeat: Math.floor(participantNumber / profileSet.length), }; diff --git a/utils/src/stages/profile_stage.ts b/utils/src/stages/profile_stage.ts index f10607691..5326636b2 100644 --- a/utils/src/stages/profile_stage.ts +++ b/utils/src/stages/profile_stage.ts @@ -22,6 +22,7 @@ export enum ProfileType { export interface ProfileStageConfig extends BaseStageConfig { kind: StageKind.PROFILE; profileType: ProfileType; + informalNameStyle?: boolean; // e.g., "bear123" instead of "Bear 1002" } // ************************************************************************* // @@ -39,5 +40,6 @@ export function createProfileStage( descriptions: config.descriptions ?? createStageTextConfig(), progress: config.progress ?? createStageProgressConfig(), profileType: config.profileType ?? ProfileType.DEFAULT, + informalNameStyle: config.informalNameStyle ?? false, }; } diff --git a/utils/src/stages/profile_stage.validation.ts b/utils/src/stages/profile_stage.validation.ts index d1fbe4183..b98592a1d 100644 --- a/utils/src/stages/profile_stage.validation.ts +++ b/utils/src/stages/profile_stage.validation.ts @@ -23,6 +23,7 @@ export const ProfileStageConfigData = Type.Composite( Type.Literal(ProfileType.ANONYMOUS_ANIMAL), Type.Literal(ProfileType.ANONYMOUS_PARTICIPANT), ]), + informalNameStyle: Type.Optional(Type.Boolean({default: false})), }, strict, ), From e6df2fa6ff24e5a9cb612b571691181d8d87e21d Mon Sep 17 00:00:00 2001 From: rasmi Date: Wed, 11 Mar 2026 15:52:33 -0400 Subject: [PATCH 8/8] Nicer editor styling. --- .../stages/profile_stage_editor.scss | 24 +-- .../components/stages/profile_stage_editor.ts | 143 +++++++++--------- utils/src/participant.ts | 2 +- 3 files changed, 85 insertions(+), 84 deletions(-) diff --git a/frontend/src/components/stages/profile_stage_editor.scss b/frontend/src/components/stages/profile_stage_editor.scss index f3308ec9e..56448a9b2 100644 --- a/frontend/src/components/stages/profile_stage_editor.scss +++ b/frontend/src/components/stages/profile_stage_editor.scss @@ -7,25 +7,27 @@ height: 100%; } -.checkbox-wrapper { +.profile-option { @include common.flex-row-align-center; - gap: common.$spacing-medium; + cursor: pointer; + gap: common.$spacing-small; } -md-checkbox { - flex-shrink: 0; +.divider { + border-bottom: 1px solid var(--md-sys-color-outline-variant); } -.profile-options { - @include common.flex-column; - gap: common.$spacing-medium; +.title { + @include typescale.title-medium; + color: var(--md-sys-color-secondary); } -.profile-option { +.checkbox-wrapper { @include common.flex-row-align-center; + cursor: pointer; gap: common.$spacing-small; +} - label { - cursor: pointer; - } +md-checkbox { + flex-shrink: 0; } diff --git a/frontend/src/components/stages/profile_stage_editor.ts b/frontend/src/components/stages/profile_stage_editor.ts index 69cff260a..cf37a890a 100644 --- a/frontend/src/components/stages/profile_stage_editor.ts +++ b/frontend/src/components/stages/profile_stage_editor.ts @@ -62,79 +62,78 @@ export class ProfileStageEditorComponent extends MobxLitElement { }); }; + const isAnonymous = + this.stage.profileType === ProfileType.ANONYMOUS_ANIMAL || + this.stage.profileType === ProfileType.ANONYMOUS_PARTICIPANT; + return html` -
-
- handleProfileTypeChange(ProfileType.DEFAULT)} - > - -
-
- - handleProfileTypeChange(ProfileType.DEFAULT_GENDERED)} - > - -
-
- - handleProfileTypeChange(ProfileType.ANONYMOUS_ANIMAL)} - > - -
- ${this.stage.profileType === ProfileType.ANONYMOUS_ANIMAL - ? html` - - ` - : nothing} -
- - handleProfileTypeChange(ProfileType.ANONYMOUS_PARTICIPANT)} - > - -
-
+
Participant-created profiles
+ + +
+
Anonymous profiles
+ + + ${isAnonymous + ? html` + + ` + : nothing} `; } } diff --git a/utils/src/participant.ts b/utils/src/participant.ts index 41b0d697e..dc017ed63 100644 --- a/utils/src/participant.ts +++ b/utils/src/participant.ts @@ -223,7 +223,7 @@ export function setProfile( [PROFILE_SET_ANIMALS_2_ID]: profileFromSet(PROFILE_SET_ANIMALS_2), [PROFILE_SET_NATURE_ID]: profileFromSet(PROFILE_SET_NATURE), [PROFILE_SET_ANONYMOUS_PARTICIPANT_ID]: { - name: `Participant ${randomNumber}`, + name: formatName('Participant'), avatar: '👤', repeat: 0, },