A website for the ATmosphereConf

Compare changes

Choose any two refs to compare.

Changed files
+80 -81
lexicons
src
components
lexicon
types
org
atmosphereconf
pages
+9 -3
lexicons/profile.json
··· 14 "maxGraphemes": 64, 15 "maxLength": 640 16 }, 17 - "description": { 18 "type": "string", 19 - "description": "Free-form profile description text.", 20 "maxGraphemes": 256, 21 "maxLength": 2560 22 }, 23 "avatar": { 24 "type": "blob", 25 - "description": "Profile picture for conference attendee", 26 "accept": ["image/png", "image/jpeg"], 27 "maxSize": 1000000 28 },
··· 14 "maxGraphemes": 64, 15 "maxLength": 640 16 }, 17 + "about": { 18 "type": "string", 19 + "description": "Conference attendee extended about text", 20 + "maxGraphemes": 1024, 21 + "maxLength": 10240 22 + }, 23 + "shortbio": { 24 + "type": "string", 25 + "description": "If you are a speaker, this is displayed as your bio. Use about for extended text", 26 "maxGraphemes": 256, 27 "maxLength": 2560 28 }, 29 "avatar": { 30 "type": "blob", 31 + "description": "Profile picture for conference attendee. Defaults to Bluesky profile pic on first login. If you are speaking, this is used as your speaker headshot", 32 "accept": ["image/png", "image/jpeg"], 33 "maxSize": 1000000 34 },
+33 -19
src/components/ProfileForm.astro
··· 1 --- 2 interface Props { 3 displayName?: string 4 - description?: string 5 avatar?: string 6 banner?: string 7 submitLabel?: string ··· 10 11 const { 12 displayName = '', 13 - description = '', 14 avatar = '', 15 banner = '', 16 submitLabel = 'Create Profile', ··· 26 > 27 <div class="form-control"> 28 <label class="label"> 29 - <span class="label-text">Display Name</span> 30 <span class="label-text-alt">Max 64 characters</span> 31 </label> 32 <input 33 type="text" 34 name="displayName" 35 - placeholder="Enter your display name" 36 class="input input-bordered w-full" 37 value={displayName} 38 maxlength="64" ··· 42 43 <div class="form-control"> 44 <label class="label"> 45 - <span class="label-text">Description</span> 46 - <span class="label-text-alt">Max 256 characters</span> 47 - </label> 48 - <textarea 49 - name="description" 50 - placeholder="Tell us about yourself" 51 - class="textarea textarea-bordered h-24" 52 - maxlength="256" 53 - >{description}</textarea> 54 - </div> 55 - 56 - <div class="form-control"> 57 - <label class="label"> 58 - <span class="label-text">Avatar</span> 59 - <span class="label-text-alt">PNG or JPEG, max 1MB</span> 60 </label> 61 {avatar && ( 62 <div class="avatar mb-2"> ··· 72 class="file-input file-input-bordered w-full" 73 /> 74 </div> 75 76 <div class="form-control"> 77 <label class="label">
··· 1 --- 2 interface Props { 3 displayName?: string 4 + about?: string 5 avatar?: string 6 banner?: string 7 submitLabel?: string ··· 10 11 const { 12 displayName = '', 13 + about = '', 14 avatar = '', 15 banner = '', 16 submitLabel = 'Create Profile', ··· 26 > 27 <div class="form-control"> 28 <label class="label"> 29 + <span class="label-text">Attendee Name</span> 30 <span class="label-text-alt">Max 64 characters</span> 31 </label> 32 <input 33 type="text" 34 name="displayName" 35 + placeholder="This is the display name that will be shown as your attendee name." 36 class="input input-bordered w-full" 37 value={displayName} 38 maxlength="64" ··· 42 43 <div class="form-control"> 44 <label class="label"> 45 + <span class="label-text">Attendee Image</span> 46 + <span class="label-text-alt">Your public attendee image. PNG or JPEG, max 1MB</span> 47 </label> 48 {avatar && ( 49 <div class="avatar mb-2"> ··· 59 class="file-input file-input-bordered w-full" 60 /> 61 </div> 62 + 63 + <div class="form-control"> 64 + <label class="label"> 65 + <span class="label-text">Bio</span> 66 + <span class="label-text-alt">A short biography, displayed as speaker bios. Max 256 characters</span> 67 + </label><br /> 68 + <textarea 69 + name="shortbio" 70 + placeholder="If you are a speaker, this is displayed as your bio. Use About for extended background" 71 + class="textarea textarea-bordered h-24 w-full" 72 + maxlength="256" 73 + >{about}</textarea> 74 + </div> 75 + 76 + <div class="form-control"> 77 + <label class="label"> 78 + <span class="label-text">About</span> 79 + <span class="label-text-alt">Max 1024 characters</span> 80 + </label><br /> 81 + <textarea 82 + name="about" 83 + placeholder="Tell us about yourself" 84 + class="textarea textarea-bordered h-24 w-full" 85 + maxlength="1024" 86 + >{about}</textarea> 87 + </div> 88 + 89 90 <div class="form-control"> 91 <label class="label">
+27 -46
src/lexicon/lexicons.ts
··· 1 /** 2 * GENERATED CODE - DO NOT MODIFY 3 */ 4 - import { 5 - type LexiconDoc, 6 - Lexicons, 7 - ValidationError, 8 - type ValidationResult, 9 - } from '@atproto/lexicon' 10 import { type $Typed, is$typed, maybe$typed } from './util.js' 11 12 export const schemaDict = { ··· 16 defs: { 17 label: { 18 type: 'object', 19 - description: 20 - 'Metadata tag on an atproto resource (eg, repo or record).', 21 required: ['src', 'uri', 'val', 'cts'], 22 properties: { 23 ver: { ··· 32 uri: { 33 type: 'string', 34 format: 'uri', 35 - description: 36 - 'AT URI of the record, repository (account), or other resource that this label applies to.', 37 }, 38 cid: { 39 type: 'string', 40 format: 'cid', 41 - description: 42 - "Optionally, CID specifying the specific version of 'uri' resource this label applies to.", 43 }, 44 val: { 45 type: 'string', 46 maxLength: 128, 47 - description: 48 - 'The short string name of the value or type of this label.', 49 }, 50 neg: { 51 type: 'boolean', 52 - description: 53 - 'If true, this is a negation label, overwriting a previous label.', 54 }, 55 cts: { 56 type: 'string', ··· 60 exp: { 61 type: 'string', 62 format: 'datetime', 63 - description: 64 - 'Timestamp at which this label expires (no longer applies).', 65 }, 66 sig: { 67 type: 'bytes', ··· 71 }, 72 selfLabels: { 73 type: 'object', 74 - description: 75 - 'Metadata tags on an atproto record, published by the author within the record.', 76 required: ['values'], 77 properties: { 78 values: { ··· 94 val: { 95 type: 'string', 96 maxLength: 128, 97 - description: 98 - 'The short string name of the value or type of this label.', 99 }, 100 }, 101 }, 102 labelValueDefinition: { 103 type: 'object', 104 - description: 105 - 'Declares a label value and its expected interpretations and behaviors.', 106 required: ['identifier', 'severity', 'blurs', 'locales'], 107 properties: { 108 identifier: { ··· 132 }, 133 adultOnly: { 134 type: 'boolean', 135 - description: 136 - 'Does the user need to have adult content enabled in order to configure this label?', 137 }, 138 locales: { 139 type: 'array', ··· 146 }, 147 labelValueDefinitionStrings: { 148 type: 'object', 149 - description: 150 - 'Strings which describe the label in the UI, localized into a specific language.', 151 required: ['lang', 'name', 'description'], 152 properties: { 153 lang: { 154 type: 'string', 155 - description: 156 - 'The code of the language these strings are written in.', 157 format: 'language', 158 }, 159 name: { ··· 164 }, 165 description: { 166 type: 'string', 167 - description: 168 - 'A longer description of what the label means and why it might be applied.', 169 maxGraphemes: 10000, 170 maxLength: 100000, 171 }, ··· 205 maxGraphemes: 64, 206 maxLength: 640, 207 }, 208 - description: { 209 type: 'string', 210 - description: 'Free-form profile description text.', 211 maxGraphemes: 256, 212 maxLength: 2560, 213 }, 214 avatar: { 215 type: 'blob', 216 - description: 'Profile picture for conference attendee', 217 accept: ['image/png', 'image/jpeg'], 218 maxSize: 1000000, 219 }, 220 banner: { 221 type: 'blob', 222 - description: 223 - 'Larger horizontal image to display behind profile view.', 224 accept: ['image/png', 'image/jpeg'], 225 maxSize: 1000000, 226 }, ··· 275 hash: string, 276 requiredType?: false, 277 ): ValidationResult<T> 278 - export function validate( 279 - v: unknown, 280 - id: string, 281 - hash: string, 282 - requiredType?: boolean, 283 - ): ValidationResult { 284 return (requiredType ? is$typed : maybe$typed)(v, id, hash) 285 ? lexicons.validate(`${id}#${hash}`, v) 286 : { 287 success: false, 288 - error: new ValidationError( 289 - `Must be an object with "${hash === 'main' ? id : `${id}#${hash}`}" $type property`, 290 - ), 291 } 292 } 293
··· 1 /** 2 * GENERATED CODE - DO NOT MODIFY 3 */ 4 + import { type LexiconDoc, Lexicons, ValidationError, type ValidationResult } from '@atproto/lexicon' 5 import { type $Typed, is$typed, maybe$typed } from './util.js' 6 7 export const schemaDict = { ··· 11 defs: { 12 label: { 13 type: 'object', 14 + description: 'Metadata tag on an atproto resource (eg, repo or record).', 15 required: ['src', 'uri', 'val', 'cts'], 16 properties: { 17 ver: { ··· 26 uri: { 27 type: 'string', 28 format: 'uri', 29 + description: 'AT URI of the record, repository (account), or other resource that this label applies to.', 30 }, 31 cid: { 32 type: 'string', 33 format: 'cid', 34 + description: "Optionally, CID specifying the specific version of 'uri' resource this label applies to.", 35 }, 36 val: { 37 type: 'string', 38 maxLength: 128, 39 + description: 'The short string name of the value or type of this label.', 40 }, 41 neg: { 42 type: 'boolean', 43 + description: 'If true, this is a negation label, overwriting a previous label.', 44 }, 45 cts: { 46 type: 'string', ··· 50 exp: { 51 type: 'string', 52 format: 'datetime', 53 + description: 'Timestamp at which this label expires (no longer applies).', 54 }, 55 sig: { 56 type: 'bytes', ··· 60 }, 61 selfLabels: { 62 type: 'object', 63 + description: 'Metadata tags on an atproto record, published by the author within the record.', 64 required: ['values'], 65 properties: { 66 values: { ··· 82 val: { 83 type: 'string', 84 maxLength: 128, 85 + description: 'The short string name of the value or type of this label.', 86 }, 87 }, 88 }, 89 labelValueDefinition: { 90 type: 'object', 91 + description: 'Declares a label value and its expected interpretations and behaviors.', 92 required: ['identifier', 'severity', 'blurs', 'locales'], 93 properties: { 94 identifier: { ··· 118 }, 119 adultOnly: { 120 type: 'boolean', 121 + description: 'Does the user need to have adult content enabled in order to configure this label?', 122 }, 123 locales: { 124 type: 'array', ··· 131 }, 132 labelValueDefinitionStrings: { 133 type: 'object', 134 + description: 'Strings which describe the label in the UI, localized into a specific language.', 135 required: ['lang', 'name', 'description'], 136 properties: { 137 lang: { 138 type: 'string', 139 + description: 'The code of the language these strings are written in.', 140 format: 'language', 141 }, 142 name: { ··· 147 }, 148 description: { 149 type: 'string', 150 + description: 'A longer description of what the label means and why it might be applied.', 151 maxGraphemes: 10000, 152 maxLength: 100000, 153 }, ··· 187 maxGraphemes: 64, 188 maxLength: 640, 189 }, 190 + about: { 191 + type: 'string', 192 + description: 'Conference attendee extended about text', 193 + maxGraphemes: 512, 194 + maxLength: 5120, 195 + }, 196 + shortbio: { 197 type: 'string', 198 + description: 'If you are a speaker, this is displayed as your bio. Use about for extended text', 199 maxGraphemes: 256, 200 maxLength: 2560, 201 }, 202 avatar: { 203 type: 'blob', 204 + description: 205 + 'Profile picture for conference attendee. Defaults to Bluesky profile pic on first login. If you are speaking, this is used as your speaker headshot', 206 accept: ['image/png', 'image/jpeg'], 207 maxSize: 1000000, 208 }, 209 banner: { 210 type: 'blob', 211 + description: 'Larger horizontal image to display behind profile view.', 212 accept: ['image/png', 'image/jpeg'], 213 maxSize: 1000000, 214 }, ··· 263 hash: string, 264 requiredType?: false, 265 ): ValidationResult<T> 266 + export function validate(v: unknown, id: string, hash: string, requiredType?: boolean): ValidationResult { 267 return (requiredType ? is$typed : maybe$typed)(v, id, hash) 268 ? lexicons.validate(`${id}#${hash}`, v) 269 : { 270 success: false, 271 + error: new ValidationError(`Must be an object with "${hash === 'main' ? id : `${id}#${hash}`}" $type property`), 272 } 273 } 274
+4 -6
src/lexicon/types/org/atmosphereconf/profile.ts
··· 14 export interface Main { 15 $type: 'org.atmosphereconf.profile' 16 displayName?: string 17 /** Free-form profile description text. */ 18 - description?: string 19 /** Profile picture for conference attendee */ 20 avatar?: BlobRef 21 /** Larger horizontal image to display behind profile view. */ ··· 35 return validate<Main & V>(v, id, hashMain, true) 36 } 37 38 - export { 39 - type Main as Record, 40 - isMain as isRecord, 41 - validateMain as validateRecord, 42 - }
··· 14 export interface Main { 15 $type: 'org.atmosphereconf.profile' 16 displayName?: string 17 + /** Bio for speakers */ 18 + shortbio?: string 19 /** Free-form profile description text. */ 20 + about?: string 21 /** Profile picture for conference attendee */ 22 avatar?: BlobRef 23 /** Larger horizontal image to display behind profile view. */ ··· 37 return validate<Main & V>(v, id, hashMain, true) 38 } 39 40 + export { type Main as Record, isMain as isRecord, validateMain as validateRecord }
+2 -2
src/pages/index.astro
··· 34 const displayName = profile?.displayName || agent?.assertDid || 'User' 35 const handle = profile?.handle || agent?.assertDid || '' 36 const avatar = profile?.avatar 37 - const description = profile?.description 38 --- 39 40 <html lang="en" data-theme="dracula"> ··· 64 )} 65 <p class="text-lg font-semibold">{displayName}</p> 66 <p class="text-sm opacity-70">{handle}</p> 67 - {description && <p class="text-sm mt-2 opacity-80">{description}</p>} 68 </div> 69 <div class="space-y-2 w-full"> 70 <a href={`/profile/${handle}`} class="btn btn-primary w-full">
··· 34 const displayName = profile?.displayName || agent?.assertDid || 'User' 35 const handle = profile?.handle || agent?.assertDid || '' 36 const avatar = profile?.avatar 37 + const shortbio = profile?.shortbio 38 --- 39 40 <html lang="en" data-theme="dracula"> ··· 64 )} 65 <p class="text-lg font-semibold">{displayName}</p> 66 <p class="text-sm opacity-70">{handle}</p> 67 + {shortbio && <p class="text-sm mt-2 opacity-80">{shortbio}</p>} 68 </div> 69 <div class="space-y-2 w-full"> 70 <a href={`/profile/${handle}`} class="btn btn-primary w-full">
+3 -3
src/pages/profile/[handle].astro
··· 80 } 81 82 const displayName = conferenceProfile?.displayName || profile?.displayName || handle 83 - const description = conferenceProfile?.description || profile?.description || '' 84 85 // Handle both blob refs and direct URLs 86 let avatar = '' ··· 159 )} 160 </div> 161 162 - {description && ( 163 - <p class="mt-4 text-base whitespace-pre-wrap">{description}</p> 164 )} 165 166 <div class="mt-4">
··· 80 } 81 82 const displayName = conferenceProfile?.displayName || profile?.displayName || handle 83 + const shortbio = conferenceProfile?.shortbio || profile?.description || '' 84 85 // Handle both blob refs and direct URLs 86 let avatar = '' ··· 159 )} 160 </div> 161 162 + {shortbio && ( 163 + <p class="mt-4 text-base whitespace-pre-wrap">{shortbio}</p> 164 )} 165 166 <div class="mt-4">
+2 -2
src/pages/profile/create.astro
··· 43 } 44 45 const displayName = existingProfile?.displayName || '' 46 - const description = existingProfile?.description || '' 47 --- 48 49 <html lang="en" data-theme="dracula"> ··· 71 72 <ProfileForm 73 displayName={displayName} 74 - description={description} 75 submitLabel={existingProfile ? 'Update Profile' : 'Create Profile'} 76 /> 77
··· 43 } 44 45 const displayName = existingProfile?.displayName || '' 46 + const about = existingProfile?.about || '' 47 --- 48 49 <html lang="en" data-theme="dracula"> ··· 71 72 <ProfileForm 73 displayName={displayName} 74 + about={about} 75 submitLabel={existingProfile ? 'Update Profile' : 'Create Profile'} 76 /> 77