atmosphere explorer
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

add permission sets to schema tab

Juliet 1f0f86de cdb7e811

+210
+210
src/components/lexicon-schema.tsx
··· 12 12 }; 13 13 } 14 14 15 + interface LexiconPermission { 16 + type: "permission"; 17 + // NOTE: blob, account, and identity are not supported in lexicon schema context 18 + resource: "repo" | "rpc" | "blob" | "account" | "identity"; 19 + collection?: string[]; 20 + action?: string[]; 21 + lxm?: string[]; 22 + aud?: string; 23 + inheritAud?: boolean; 24 + } 25 + 15 26 interface LexiconDef { 16 27 type: string; 17 28 description?: string; ··· 40 51 maxSize?: number; 41 52 knownValues?: string[]; 42 53 format?: string; 54 + // Permission-set fields 55 + title?: string; 56 + "title:langs"?: { [lang: string]: string }; 57 + detail?: string; 58 + "detail:langs"?: { [lang: string]: string }; 59 + permissions?: LexiconPermission[]; 43 60 } 44 61 45 62 interface LexiconObject { ··· 257 274 ); 258 275 }; 259 276 277 + const NsidLink = (props: { nsid: string }) => { 278 + const navigate = useNavigate(); 279 + 280 + const handleClick = async () => { 281 + try { 282 + const authority = await resolveLexiconAuthority(props.nsid as Nsid); 283 + navigate(`/at://${authority}/com.atproto.lexicon.schema/${props.nsid}#schema`); 284 + } catch (err) { 285 + console.error("Failed to resolve lexicon authority:", err); 286 + } 287 + }; 288 + 289 + return ( 290 + <button 291 + type="button" 292 + onClick={handleClick} 293 + class="cursor-pointer rounded bg-blue-100 px-1.5 py-0.5 font-mono text-xs text-blue-800 hover:bg-blue-200 hover:underline active:bg-blue-200 dark:bg-blue-900/30 dark:text-blue-300 dark:hover:bg-blue-900/50 dark:active:bg-blue-900/50" 294 + > 295 + {props.nsid} 296 + </button> 297 + ); 298 + }; 299 + 300 + const resourceColor = (resource: string) => { 301 + switch (resource) { 302 + case "repo": 303 + return "bg-green-100 text-green-800 dark:bg-green-900/30 dark:text-green-300"; 304 + case "rpc": 305 + return "bg-purple-100 text-purple-800 dark:bg-purple-900/30 dark:text-purple-300"; 306 + default: 307 + return "bg-neutral-200 text-neutral-800 dark:bg-neutral-700 dark:text-neutral-300"; 308 + } 309 + }; 310 + 311 + const PermissionRow = (props: { permission: LexiconPermission; index: number }) => { 312 + return ( 313 + <div class="flex flex-col gap-2 py-3"> 314 + <div class="flex flex-wrap items-center gap-2"> 315 + <span class="font-mono text-sm font-semibold">#{props.index + 1}</span> 316 + <span 317 + class={`rounded px-1.5 py-0.5 font-mono text-xs font-semibold ${resourceColor(props.permission.resource)}`} 318 + > 319 + {props.permission.resource} 320 + </span> 321 + </div> 322 + 323 + {/* Collections (for repo resource) */} 324 + <Show when={props.permission.collection && props.permission.collection.length > 0}> 325 + <div class="flex flex-col gap-1"> 326 + <span class="text-xs font-semibold text-neutral-500 dark:text-neutral-400"> 327 + Collections: 328 + </span> 329 + <div class="flex flex-wrap gap-1"> 330 + <For each={props.permission.collection}>{(col) => <NsidLink nsid={col} />}</For> 331 + </div> 332 + </div> 333 + </Show> 334 + 335 + {/* Actions */} 336 + <Show when={props.permission.action && props.permission.action.length > 0}> 337 + <div class="flex flex-col gap-1"> 338 + <span class="text-xs font-semibold text-neutral-500 dark:text-neutral-400">Actions:</span> 339 + <div class="flex flex-wrap gap-1"> 340 + <For each={props.permission.action}> 341 + {(action) => ( 342 + <span class="dark:bg-dark-200 rounded bg-neutral-200/50 px-1.5 py-0.5 font-mono text-xs"> 343 + {action} 344 + </span> 345 + )} 346 + </For> 347 + </div> 348 + </div> 349 + </Show> 350 + 351 + {/* LXM (for rpc resource) */} 352 + <Show when={props.permission.lxm && props.permission.lxm.length > 0}> 353 + <div class="flex flex-col gap-1"> 354 + <span class="text-xs font-semibold text-neutral-500 dark:text-neutral-400"> 355 + Lexicon Methods: 356 + </span> 357 + <div class="flex flex-wrap gap-1"> 358 + <For each={props.permission.lxm}>{(method) => <NsidLink nsid={method} />}</For> 359 + </div> 360 + </div> 361 + </Show> 362 + 363 + {/* Audience */} 364 + <Show when={props.permission.aud}> 365 + <div class="flex items-center gap-2 text-xs"> 366 + <span class="font-semibold text-neutral-500 dark:text-neutral-400">Audience:</span> 367 + <span class="font-mono">{props.permission.aud}</span> 368 + </div> 369 + </Show> 370 + 371 + {/* Inherit Audience */} 372 + <Show when={props.permission.inheritAud}> 373 + <div class="flex items-center gap-2 text-xs"> 374 + <span class="font-semibold text-neutral-500 dark:text-neutral-400"> 375 + Inherit Audience: 376 + </span> 377 + <span class="font-mono">true</span> 378 + </div> 379 + </Show> 380 + </div> 381 + ); 382 + }; 383 + 260 384 const DefSection = (props: { name: string; def: LexiconDef }) => { 261 385 const defTypeColor = () => { 262 386 switch (props.def.type) { ··· 272 396 return "bg-blue-100 text-blue-800 dark:bg-blue-900/30 dark:text-blue-300"; 273 397 case "token": 274 398 return "bg-yellow-100 text-yellow-800 dark:bg-yellow-900/30 dark:text-yellow-300"; 399 + case "permission-set": 400 + return "bg-cyan-100 text-cyan-800 dark:bg-cyan-900/30 dark:text-cyan-300"; 275 401 default: 276 402 return "bg-neutral-200 text-neutral-800 dark:bg-neutral-700 dark:text-neutral-300"; 277 403 } ··· 316 442 <span class="text-sm font-semibold">Record Key: </span> 317 443 <span class="font-mono text-sm">{props.def.key}</span> 318 444 </div> 445 + </Show> 446 + 447 + {/* Permission-set: Title and Detail */} 448 + <Show when={props.def.type === "permission-set" && (props.def.title || props.def.detail)}> 449 + <div class="flex flex-col gap-2 rounded-lg border border-neutral-200 bg-neutral-50/50 p-3 dark:border-neutral-700 dark:bg-neutral-800/30"> 450 + <Show when={props.def.title}> 451 + <div class="flex flex-col gap-1"> 452 + <span class="text-xs font-semibold text-neutral-500 uppercase dark:text-neutral-400"> 453 + Title 454 + </span> 455 + <span class="text-sm font-medium">{props.def.title}</span> 456 + </div> 457 + </Show> 458 + <Show when={props.def["title:langs"]}> 459 + <div class="flex flex-col gap-1"> 460 + <span class="text-xs font-semibold text-neutral-500 uppercase dark:text-neutral-400"> 461 + Localized Titles 462 + </span> 463 + <div class="flex flex-col gap-1"> 464 + <For each={Object.entries(props.def["title:langs"]!)}> 465 + {([lang, text]) => ( 466 + <div class="flex items-center gap-2 text-sm"> 467 + <span class="rounded bg-neutral-100 px-1.5 py-0.5 font-mono text-xs dark:bg-neutral-800"> 468 + {lang} 469 + </span> 470 + <span>{text}</span> 471 + </div> 472 + )} 473 + </For> 474 + </div> 475 + </div> 476 + </Show> 477 + <Show when={props.def.detail}> 478 + <div class="flex flex-col gap-1"> 479 + <span class="text-xs font-semibold text-neutral-500 uppercase dark:text-neutral-400"> 480 + Detail 481 + </span> 482 + <p class="text-sm text-neutral-700 dark:text-neutral-300">{props.def.detail}</p> 483 + </div> 484 + </Show> 485 + <Show when={props.def["detail:langs"]}> 486 + <div class="flex flex-col gap-1"> 487 + <span class="text-xs font-semibold text-neutral-500 uppercase dark:text-neutral-400"> 488 + Localized Details 489 + </span> 490 + <div class="flex flex-col gap-1"> 491 + <For each={Object.entries(props.def["detail:langs"]!)}> 492 + {([lang, text]) => ( 493 + <div class="flex flex-col gap-1 text-sm"> 494 + <span class="w-fit rounded bg-neutral-100 px-1.5 py-0.5 font-mono text-xs dark:bg-neutral-800"> 495 + {lang} 496 + </span> 497 + <p class="text-neutral-700 dark:text-neutral-300">{text}</p> 498 + </div> 499 + )} 500 + </For> 501 + </div> 502 + </div> 503 + </Show> 504 + </div> 505 + </Show> 506 + 507 + {/* Permission-set: Permissions list */} 508 + <Show when={props.def.permissions && props.def.permissions.length > 0}> 509 + {(() => { 510 + const supportedPermissions = () => 511 + props.def.permissions!.filter((p) => p.resource === "repo" || p.resource === "rpc"); 512 + return ( 513 + <Show when={supportedPermissions().length > 0}> 514 + <div class="flex flex-col gap-2"> 515 + <h4 class="text-sm font-semibold text-neutral-600 uppercase dark:text-neutral-400"> 516 + Permissions 517 + </h4> 518 + <div class="divide-y divide-neutral-200 rounded-lg border border-neutral-200 bg-neutral-50/50 px-3 dark:divide-neutral-700 dark:border-neutral-700 dark:bg-neutral-800/30"> 519 + <For each={supportedPermissions()}> 520 + {(permission, index) => ( 521 + <PermissionRow permission={permission} index={index()} /> 522 + )} 523 + </For> 524 + </div> 525 + </div> 526 + </Show> 527 + ); 528 + })()} 319 529 </Show> 320 530 321 531 {/* Properties (for record/object types) */}