···1212- add your events to any ical compatible calendar
1313(go to calendar/ when signed in and click "Add to your calendar")
1414- post your events/rsvps to bluesky or anywhere else with nice open-graph images
1515+- display comments
1616+- show what events your bsky follows are going to
15171618## development
1719···3032optionally if you want all current events to be displayed run this: (will take a few minutes)
31333234```
3333-pnpm run sync
3535+pnpm backfill
3436```
35373638start dev server:
···44 "defs": {
55 "main": {
66 "type": "query",
77- "description": "List spaces the caller has access to. Default scope is 'member' (spaces the caller is a member of, including owned); 'owner' lists only spaces the caller owns.",
77+ "description": "List spaces the caller has access to. Default scope is 'member' (spaces the caller is a member of, including owned); 'owner' lists only spaces the caller owns. When scope='member', the optional 'owner' param narrows to spaces owned by that DID — useful for listing all channels in a specific community the caller can access.",
88 "parameters": {
99 "type": "params",
1010 "properties": {
···1919 "type": {
2020 "type": "string",
2121 "format": "nsid"
2222+ },
2323+ "owner": {
2424+ "type": "string",
2525+ "format": "did",
2626+ "description": "With scope=member, filter to spaces owned by this DID. Ignored when scope=owner."
2227 },
2328 "cursor": {
2429 "type": "string"
···11+// Auto-generated by @atmo-dev/contrail-lexicons. Do not edit.
22+// Pass `lexicons` to `createWorker(config, { lexicons })` to expose them
33+// at `/xrpc/<namespace>.lexicons` for consumer apps to typegen against.
44+55+import _0 from "../pulled/app/bsky/actor/profile.json";
66+import _1 from "../pulled/app/bsky/graph/follow.json";
77+import _2 from "../pulled/community/lexicon/calendar/event.json";
88+import _3 from "../pulled/community/lexicon/calendar/rsvp.json";
99+import _4 from "../pulled/community/lexicon/location/address.json";
1010+import _5 from "../pulled/community/lexicon/location/fsq.json";
1111+import _6 from "../pulled/community/lexicon/location/geo.json";
1212+import _7 from "../pulled/community/lexicon/location/hthree.json";
1313+import _8 from "./rsvp/atmo/authFull.json";
1414+import _9 from "./rsvp/atmo/event/getRecord.json";
1515+import _10 from "./rsvp/atmo/event/listRecords.json";
1616+import _11 from "./rsvp/atmo/getCursor.json";
1717+import _12 from "./rsvp/atmo/getFeed.json";
1818+import _13 from "./rsvp/atmo/getOverview.json";
1919+import _14 from "./rsvp/atmo/getProfile.json";
2020+import _15 from "./rsvp/atmo/invite/create.json";
2121+import _16 from "./rsvp/atmo/invite/defs.json";
2222+import _17 from "./rsvp/atmo/invite/list.json";
2323+import _18 from "./rsvp/atmo/invite/redeem.json";
2424+import _19 from "./rsvp/atmo/invite/revoke.json";
2525+import _20 from "./rsvp/atmo/notifyOfUpdate.json";
2626+import _21 from "./rsvp/atmo/rsvp/getRecord.json";
2727+import _22 from "./rsvp/atmo/rsvp/listRecords.json";
2828+import _23 from "./rsvp/atmo/space/addMember.json";
2929+import _24 from "./rsvp/atmo/space/createSpace.json";
3030+import _25 from "./rsvp/atmo/space/defs.json";
3131+import _26 from "./rsvp/atmo/space/deleteRecord.json";
3232+import _27 from "./rsvp/atmo/space/getBlob.json";
3333+import _28 from "./rsvp/atmo/space/getRecord.json";
3434+import _29 from "./rsvp/atmo/space/getSpace.json";
3535+import _30 from "./rsvp/atmo/space/leaveSpace.json";
3636+import _31 from "./rsvp/atmo/space/listBlobs.json";
3737+import _32 from "./rsvp/atmo/space/listMembers.json";
3838+import _33 from "./rsvp/atmo/space/listRecords.json";
3939+import _34 from "./rsvp/atmo/space/listSpaces.json";
4040+import _35 from "./rsvp/atmo/space/putRecord.json";
4141+import _36 from "./rsvp/atmo/space/removeMember.json";
4242+import _37 from "./rsvp/atmo/space/uploadBlob.json";
4343+import _38 from "./rsvp/atmo/spaceExt/whoami.json";
4444+4545+export const lexicons: object[] = [_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38];
···44import type { Client } from '@atcute/client';
55import type { Did } from '@atcute/lexicons';
6677+interface AtmoEmbedSDK {
88+ getParams(): { base: string; accent: string; dark: boolean; did: string | null };
99+ createRecord(opts: {
1010+ collection: string;
1111+ rkey?: string;
1212+ record: Record<string, unknown>;
1313+ }): Promise<{ uri: string }>;
1414+ deleteRecord(opts: { collection: string; rkey: string }): Promise<void>;
1515+}
1616+717declare global {
1818+ interface Window {
1919+ AtmoEmbed?: AtmoEmbedSDK;
2020+ }
821 namespace App {
922 // interface Error {}
1023 interface Locals {
···2134 CLIENT_ASSERTION_KEY: string;
2235 COOKIE_SECRET: string;
2336 OAUTH_PUBLIC_URL: string;
2424-DB: D1Database;
3737+ DB: D1Database;
2538 CRON_SECRET: string;
2639 };
4040+ /** Cloudflare Worker execution context. Use `ctx.waitUntil(promise)` to
4141+ * let the worker keep a fire-and-forget task alive after the response
4242+ * has been sent. Optional in dev (wrangler proxy may not provide it). */
4343+ ctx?: { waitUntil(promise: Promise<unknown>): void };
2744 }
2845 }
2946}
+11-5
src/lexicon-types/index.ts
···11export * as AppBskyActorProfile from "./types/app/bsky/actor/profile.js";
22+export * as AppBskyGraphFollow from "./types/app/bsky/graph/follow.js";
23export * as CommunityLexiconCalendarEvent from "./types/community/lexicon/calendar/event.js";
34export * as CommunityLexiconCalendarRsvp from "./types/community/lexicon/calendar/rsvp.js";
45export * as CommunityLexiconLocationAddress from "./types/community/lexicon/location/address.js";
···89export * as RsvpAtmoEventGetRecord from "./types/rsvp/atmo/event/getRecord.js";
910export * as RsvpAtmoEventListRecords from "./types/rsvp/atmo/event/listRecords.js";
1011export * as RsvpAtmoGetCursor from "./types/rsvp/atmo/getCursor.js";
1212+export * as RsvpAtmoGetFeed from "./types/rsvp/atmo/getFeed.js";
1113export * as RsvpAtmoGetOverview from "./types/rsvp/atmo/getOverview.js";
1214export * as RsvpAtmoGetProfile from "./types/rsvp/atmo/getProfile.js";
1515+export * as RsvpAtmoInviteCreate from "./types/rsvp/atmo/invite/create.js";
1616+export * as RsvpAtmoInviteDefs from "./types/rsvp/atmo/invite/defs.js";
1717+export * as RsvpAtmoInviteList from "./types/rsvp/atmo/invite/list.js";
1818+export * as RsvpAtmoInviteRedeem from "./types/rsvp/atmo/invite/redeem.js";
1919+export * as RsvpAtmoInviteRevoke from "./types/rsvp/atmo/invite/revoke.js";
1320export * as RsvpAtmoNotifyOfUpdate from "./types/rsvp/atmo/notifyOfUpdate.js";
1421export * as RsvpAtmoRsvpGetRecord from "./types/rsvp/atmo/rsvp/getRecord.js";
1522export * as RsvpAtmoRsvpListRecords from "./types/rsvp/atmo/rsvp/listRecords.js";
···1724export * as RsvpAtmoSpaceCreateSpace from "./types/rsvp/atmo/space/createSpace.js";
1825export * as RsvpAtmoSpaceDefs from "./types/rsvp/atmo/space/defs.js";
1926export * as RsvpAtmoSpaceDeleteRecord from "./types/rsvp/atmo/space/deleteRecord.js";
2727+export * as RsvpAtmoSpaceGetBlob from "./types/rsvp/atmo/space/getBlob.js";
2028export * as RsvpAtmoSpaceGetRecord from "./types/rsvp/atmo/space/getRecord.js";
2129export * as RsvpAtmoSpaceGetSpace from "./types/rsvp/atmo/space/getSpace.js";
2222-export * as RsvpAtmoSpaceInviteCreate from "./types/rsvp/atmo/space/invite/create.js";
2323-export * as RsvpAtmoSpaceInviteList from "./types/rsvp/atmo/space/invite/list.js";
2424-export * as RsvpAtmoSpaceInviteRedeem from "./types/rsvp/atmo/space/invite/redeem.js";
2525-export * as RsvpAtmoSpaceInviteRevoke from "./types/rsvp/atmo/space/invite/revoke.js";
2630export * as RsvpAtmoSpaceLeaveSpace from "./types/rsvp/atmo/space/leaveSpace.js";
3131+export * as RsvpAtmoSpaceListBlobs from "./types/rsvp/atmo/space/listBlobs.js";
2732export * as RsvpAtmoSpaceListMembers from "./types/rsvp/atmo/space/listMembers.js";
2833export * as RsvpAtmoSpaceListRecords from "./types/rsvp/atmo/space/listRecords.js";
2934export * as RsvpAtmoSpaceListSpaces from "./types/rsvp/atmo/space/listSpaces.js";
3035export * as RsvpAtmoSpacePutRecord from "./types/rsvp/atmo/space/putRecord.js";
3136export * as RsvpAtmoSpaceRemoveMember from "./types/rsvp/atmo/space/removeMember.js";
3232-export * as RsvpAtmoSpaceWhoami from "./types/rsvp/atmo/space/whoami.js";
3737+export * as RsvpAtmoSpaceUploadBlob from "./types/rsvp/atmo/space/uploadBlob.js";
3838+export * as RsvpAtmoSpaceExtWhoami from "./types/rsvp/atmo/spaceExt/whoami.js";
+30
src/lexicon-types/types/app/bsky/graph/follow.ts
···11+import type {} from "@atcute/lexicons";
22+import * as v from "@atcute/lexicons/validations";
33+import type {} from "@atcute/lexicons/ambient";
44+import * as ComAtprotoRepoStrongRef from "@atcute/atproto/types/repo/strongRef";
55+66+const _mainSchema = /*#__PURE__*/ v.record(
77+ /*#__PURE__*/ v.tidString(),
88+ /*#__PURE__*/ v.object({
99+ $type: /*#__PURE__*/ v.literal("app.bsky.graph.follow"),
1010+ createdAt: /*#__PURE__*/ v.datetimeString(),
1111+ subject: /*#__PURE__*/ v.didString(),
1212+ get via() {
1313+ return /*#__PURE__*/ v.optional(ComAtprotoRepoStrongRef.mainSchema);
1414+ },
1515+ }),
1616+);
1717+1818+type main$schematype = typeof _mainSchema;
1919+2020+export interface mainSchema extends main$schematype {}
2121+2222+export const mainSchema = _mainSchema as mainSchema;
2323+2424+export interface Main extends v.InferInput<typeof mainSchema> {}
2525+2626+declare module "@atcute/lexicons/ambient" {
2727+ interface Records {
2828+ "app.bsky.graph.follow": mainSchema;
2929+ }
3030+}
···96969797 // Add track after Plyr init and force captions on
9898 if (subtitlesUrl) {
9999- const media = plyr.media as HTMLVideoElement;
9999+ const media = videoEl;
100100 // Remove any existing tracks first
101101 media.querySelectorAll('track').forEach((t) => t.remove());
102102 // Add fresh track