Webhooks for the AT Protocol airglow.run
atproto atprotocol automation webhook
12
fork

Configure Feed

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

feat: create automation on unpublished lexicons

Hugo 3be5eadd 8972ddd8

+85 -23
+11
app/islands/AutomationForm.css.ts
··· 243 243 borderColor: vars.color.error, 244 244 }); 245 245 246 + export const alertWarning = style({ 247 + paddingBlock: space[3], 248 + paddingInline: space[4], 249 + borderRadius: radii.md, 250 + fontSize: fontSize.sm, 251 + borderInlineStart: "3px solid", 252 + backgroundColor: vars.color.warningSubtle, 253 + color: vars.color.warning, 254 + borderColor: vars.color.warning, 255 + }); 256 + 246 257 export const resetFieldset = style({ 247 258 border: "none", 248 259 padding: 0,
+74 -23
app/islands/AutomationForm.tsx
··· 303 303 const [fields, setFields] = useState<Field[]>([]); 304 304 const [fieldsLoading, setFieldsLoading] = useState(false); 305 305 const [fieldsError, setFieldsError] = useState(""); 306 + const [schemaUnresolved, setSchemaUnresolved] = useState(false); 306 307 const [conditions, setConditions] = useState<Condition[]>( 307 308 initial ? toConditionDrafts(initial.conditions) : [], 308 309 ); ··· 358 359 if (!nsid) { 359 360 setFields([]); 360 361 setFieldsError(""); 362 + setSchemaUnresolved(false); 361 363 if (updateUrl) { 362 364 const url = new URL(window.location.href); 363 365 url.searchParams.delete("lexicon"); ··· 382 384 const res = await fetch(`/api/lexicons/${encodeURIComponent(nsid)}`); 383 385 const data = await res.json(); 384 386 if (!res.ok) { 385 - setFieldsError(data.error || "Failed to load fields"); 387 + if (res.status === 404) { 388 + setSchemaUnresolved(true); 389 + setFieldsError(""); 390 + } else { 391 + setSchemaUnresolved(false); 392 + setFieldsError(data.error || "Failed to load fields"); 393 + } 386 394 setFields([]); 387 395 } else { 396 + setSchemaUnresolved(false); 388 397 setFields(data.fields || []); 389 398 } 390 399 } catch { 400 + setSchemaUnresolved(false); 391 401 setFieldsError("Failed to fetch lexicon fields"); 392 402 setFields([]); 393 403 } finally { ··· 581 591 class={s.input} 582 592 type="text" 583 593 list={isEdit ? undefined : "nsid-suggestions"} 584 - placeholder="e.g. sh.tangled.feed.star" 594 + placeholder="e.g. site.standard.document" 585 595 value={lexicon} 586 596 onInput={ 587 597 isEdit ··· 600 610 {isEdit && <span class={s.hint}>Lexicon cannot be changed after creation</span>} 601 611 {fieldsLoading && <span class={s.hint}>Loading fields...</span>} 602 612 {fieldsError && <span class={s.errorText}>{fieldsError}</span>} 613 + {schemaUnresolved && ( 614 + <div class={s.alertWarning}> 615 + This lexicon's schema could not be resolved. You can still set up your automation — 616 + field paths for conditions will need to be entered manually. 617 + </div> 618 + )} 603 619 </div> 604 620 605 621 <div class={s.fieldGroup}> ··· 658 674 </div> 659 675 )} 660 676 677 + {fields.length === 0 && schemaUnresolved && ( 678 + <div class={s.placeholderGroup}> 679 + <div class={s.placeholderGroupTitle}>Record fields</div> 680 + <span class={s.hint}> 681 + Schema not available. Use paths like{" "} 682 + <code>event.commit.record.subject.uri</code> based on the record structure. 683 + </span> 684 + </div> 685 + )} 686 + 661 687 {fetches.some((f) => f.name) && ( 662 688 <div class={s.placeholderGroup}> 663 689 <div class={s.placeholderGroupTitle}>Data sources</div> ··· 699 725 <div key={i} class={s.conditionBlock}> 700 726 <div class={s.conditionRow}> 701 727 <div class={s.conditionField}> 702 - <select 703 - class={s.select} 704 - value={cond.field} 705 - disabled={fieldsLoading} 706 - onChange={(e: Event) => 707 - updateCondition(i, "field", (e.target as HTMLSelectElement).value) 708 - } 709 - > 710 - <option value=""> 711 - {fieldsLoading ? "Loading fields..." : "Select field..."} 712 - </option> 713 - {conditionFields.map((f) => ( 714 - <option key={f.path} value={f.path}> 715 - {f.path} 716 - </option> 717 - ))} 718 - </select> 719 - {cond.field && 720 - conditionFields.find((f) => f.path === cond.field)?.description && ( 728 + {schemaUnresolved && fields.length === 0 ? ( 729 + <> 730 + <input 731 + class={s.input} 732 + type="text" 733 + list="condition-builtin-fields" 734 + placeholder="e.g. commit.record.subject.uri" 735 + value={cond.field} 736 + onInput={(e: Event) => 737 + updateCondition(i, "field", (e.target as HTMLInputElement).value) 738 + } 739 + /> 721 740 <span class={s.hint}> 722 - {conditionFields.find((f) => f.path === cond.field)!.description} 741 + Enter a dot-separated field path from the event record 723 742 </span> 724 - )} 743 + </> 744 + ) : ( 745 + <> 746 + <select 747 + class={s.select} 748 + value={cond.field} 749 + disabled={fieldsLoading} 750 + onChange={(e: Event) => 751 + updateCondition(i, "field", (e.target as HTMLSelectElement).value) 752 + } 753 + > 754 + <option value=""> 755 + {fieldsLoading ? "Loading fields..." : "Select field..."} 756 + </option> 757 + {conditionFields.map((f) => ( 758 + <option key={f.path} value={f.path}> 759 + {f.path} 760 + </option> 761 + ))} 762 + </select> 763 + {cond.field && 764 + conditionFields.find((f) => f.path === cond.field)?.description && ( 765 + <span class={s.hint}> 766 + {conditionFields.find((f) => f.path === cond.field)!.description} 767 + </span> 768 + )} 769 + </> 770 + )} 725 771 </div> 726 772 {cond.field && 727 773 conditionFields.find((f) => f.path === cond.field)?.type !== "boolean" && ( ··· 920 966 <datalist id="nsid-suggestions"> 921 967 {nsidSuggestions.map((nsid) => ( 922 968 <option key={nsid} value={nsid} /> 969 + ))} 970 + </datalist> 971 + <datalist id="condition-builtin-fields"> 972 + {BUILTIN_CONDITION_FIELDS.map((f) => ( 973 + <option key={f.path} value={f.path} /> 923 974 ))} 924 975 </datalist> 925 976 </div>