import { computed, equals, ifElse, NAME, pattern, UI, type VNode, type WishState, Writable, } from "commonfabric"; import ProfileCreate, { profileLinkListSchema, profileLinkSchema, setDefaultProfile, setMruProfile, TRUSTED_PROFILE_PICKER_SURFACE, TRUSTED_PROFILE_SET_DEFAULT_ACTION, TRUSTED_PROFILE_SET_MRU_ACTION, } from "./profile-create.tsx"; import type { ProfileHomeOutput } from "./profile-home.tsx"; // The profile picker launched by the #profile wish when the user has more than // one profile. It renders each profile natively (name + avatar + a link), // offers an inline "create another" affordance, lets the user pick a default, // and stamps most-recently-used (MRU) on selection. The chosen profile flows // back as the wish `result`: // result = default (if set) ⟶ else most-recently-used ⟶ else first. // This default-first order matches headless `#profile` resolution // (wish.ts getProfileCandidateCells) so the "current profile" is the same // whether resolved headless or through the picker. // Selection writes MRU and the "set default" control writes `defaultProfile`, // both as trusted picker-surface actions (owner-protected; see profile-create). type ProfilePickerInput = { profiles: Writable; defaultProfile: Writable; mru: Writable; }; export default pattern< ProfilePickerInput, WishState & { [UI]: VNode } >(({ profiles, defaultProfile, mru }) => { // Index (into `profiles`) of the profile the wish should resolve to. // Reads links as cell refs (asCell) and matches by link identity via `equals`, // so a cross-space profile not yet loaded here doesn't collapse the read to // `undefined` (which would mis-resolve to index 0). See profileLinkSchema. const resultIndex = computed(() => { const list = ((profiles as any).asSchema(profileLinkListSchema()).get() ?? []) as ProfileHomeOutput[]; if (list.length === 0) return -1; const def = (defaultProfile as any).asSchema(profileLinkSchema()).get() as | ProfileHomeOutput | undefined; const mruList = ((mru as any).asSchema(profileLinkListSchema()).get() ?? []) as ProfileHomeOutput[]; const matchIndex = (target: ProfileHomeOutput | undefined) => target ? list.findIndex((entry) => equals(entry, target)) : -1; // Default wins (matches headless #profile), then most-recently-used. const defIdx = matchIndex(def); if (defIdx >= 0) return defIdx; const mruIdx = mruList.length > 0 ? matchIndex(mruList[0]) : -1; if (mruIdx >= 0) return mruIdx; return 0; }); // Named computed so the CTS transformer leaves it intact in the return object // and wish.ts can read the resolved profile via `.get()`. const result = computed(() => { const idx = resultIndex; return idx >= 0 ? (profiles.key(idx) as any) : undefined; }); const profileCreate = ProfileCreate({ profiles: profiles as any, inputId: "wish-profile-picker-name-input", buttonId: "wish-profile-picker-create-button", }); return { [NAME]: "Choose a profile", result, candidates: profiles as any, [UI]: (

Your profiles

{profiles.map((p) => (
{ /* Profiles are identities — rendered via cf-cell-link (the identity idiom is cf-profile-badge), not the generic piece chip variant. */ }
{ifElse( computed(() => { const def = (defaultProfile as any).asSchema( profileLinkSchema(), ).get(); return def ? equals(def, p) : false; }), default, Set default , )} Use
))}

Add another profile

{profileCreate}
), }; });