@openrails/contentkit-ui 0.88.0 → 0.89.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/dist/client/generated/routes.d.ts +1 -1
  2. package/dist/client/generated/routes.js +1 -1
  3. package/dist/client/generated/routes.js.map +1 -1
  4. package/dist/client/generated/wire.d.ts +6 -0
  5. package/dist/client/generated/wire.js.map +1 -1
  6. package/dist/client/media/rules.js +1 -1
  7. package/dist/client/media/rules.js.map +1 -1
  8. package/dist/client/public.d.ts +2 -1
  9. package/dist/client/public.js +2 -2
  10. package/dist/client/public.js.map +1 -1
  11. package/dist/client/stats.d.ts +1 -1
  12. package/dist/client/stats.js.map +1 -1
  13. package/dist/components/billing-notification.d.ts +11 -0
  14. package/dist/components/billing-notification.js +70 -0
  15. package/dist/components/billing-notification.js.map +1 -0
  16. package/dist/components/content/filters.d.ts +1 -1
  17. package/dist/components/content/filters.js +2 -5
  18. package/dist/components/content/filters.js.map +1 -1
  19. package/dist/components/content/reader-stats.d.ts +11 -3
  20. package/dist/components/content/reader-stats.js +41 -23
  21. package/dist/components/content/reader-stats.js.map +1 -1
  22. package/dist/index.d.ts +1 -0
  23. package/dist/index.js +3 -2
  24. package/dist/locales/content/de.js +4 -0
  25. package/dist/locales/content/de.js.map +1 -1
  26. package/dist/locales/content/en.d.ts +4 -0
  27. package/dist/locales/content/en.js +4 -0
  28. package/dist/locales/content/en.js.map +1 -1
  29. package/dist/locales/content/es.js +4 -0
  30. package/dist/locales/content/es.js.map +1 -1
  31. package/dist/locales/content/ja.js +4 -0
  32. package/dist/locales/content/ja.js.map +1 -1
  33. package/dist/locales/content/ko.js +4 -0
  34. package/dist/locales/content/ko.js.map +1 -1
  35. package/dist/locales/content/zh.js +4 -0
  36. package/dist/locales/content/zh.js.map +1 -1
  37. package/dist/locales/en.d.ts +34 -0
  38. package/dist/locales/social/de.js +30 -0
  39. package/dist/locales/social/de.js.map +1 -1
  40. package/dist/locales/social/en.d.ts +30 -0
  41. package/dist/locales/social/en.js +30 -0
  42. package/dist/locales/social/en.js.map +1 -1
  43. package/dist/locales/social/es.js +30 -0
  44. package/dist/locales/social/es.js.map +1 -1
  45. package/dist/locales/social/ja.js +30 -0
  46. package/dist/locales/social/ja.js.map +1 -1
  47. package/dist/locales/social/ko.js +30 -0
  48. package/dist/locales/social/ko.js.map +1 -1
  49. package/dist/locales/social/zh.js +30 -0
  50. package/dist/locales/social/zh.js.map +1 -1
  51. package/dist/react/public.d.ts +3 -3
  52. package/dist/react/public.js +64 -25
  53. package/dist/react/public.js.map +1 -1
  54. package/dist/styles.css +1 -1
  55. package/package.json +5 -5
@@ -1 +1 @@
1
- {"version":3,"file":"wire.js","names":[],"sources":["../../../src/client/generated/wire.ts"],"sourcesContent":["// Generated by `go generate ./internal/contract` from ContentKit's route catalog. Do not edit.\n\nimport type { ErrorCode } from \"./error-codes.js\";\n\nexport type { ErrorCode };\n\n// Upload sizes: one PUT up to MAX_SINGLE_PUT bytes, else multipart parts.\nexport const MAX_SINGLE_PUT = 67108864;\nexport const MIN_PART_SIZE = 8388608;\nexport const MAX_PART_SIZE = 16777216;\n\nexport type OpName = \"put\" | \"edit\" | \"move\" | \"rename\" | \"remove\" | \"attach\" | \"copy\" | \"frame\" | \"meta\" | \"regenerate\";\nexport type Access = \"full\" | \"none\";\nexport type ItemState = \"ready\" | \"processing\" | \"failed\";\nexport type EncodePhase = \"queued\" | \"downloading\" | \"probing\" | \"encoding\" | \"muxing\" | \"uploading\" | \"publishing\" | \"images\";\nexport type PollKind = \"multiple_choice\" | \"free_text\";\nexport type ModerationState = \"approved\" | \"held\" | \"rejected\";\nexport type ContentState = \"draft\" | \"published\" | \"deleted\";\nexport type VersionState = \"draft\" | \"published\";\nexport type Listing = \"listed\" | \"members\" | \"unlisted\";\nexport type AliasStatus = \"found\" | \"not_found\" | \"gone\";\n\nexport interface Activity {\n days: ActivityDay[];\n}\n\nexport interface ActivityDay {\n date: string;\n views: number;\n viewers: number;\n users: number;\n}\n\nexport interface AdminComment {\n id: string;\n reply_to_id?: string;\n user_id?: string;\n anon_name?: string;\n language?: string;\n body: string;\n deleted: boolean;\n likes: number;\n dislikes: number;\n mine: number;\n reply_count: number;\n author?: PublicUser;\n moderation?: ModerationState;\n moderation_reason?: string;\n created_at: string;\n updated_at: string;\n tenant_id: string;\n content_kind: string;\n content_id: string;\n content_version_id?: string;\n deleted_at?: string;\n}\n\nexport interface AliasKey {\n source: string;\n kind: string;\n key: string;\n}\n\nexport interface AliasLookup {\n aliases: AliasKey[];\n}\n\nexport interface AliasRecord {\n kind: string;\n key: string;\n locator?: string;\n target: TargetRef;\n}\n\nexport interface AliasResult {\n source: string;\n kind: string;\n key: string;\n status: AliasStatus;\n resolved?: Resolved;\n locator?: string;\n}\n\nexport interface AliasResults {\n aliases: AliasResult[];\n}\n\nexport interface Anchor {\n id: string;\n page: string;\n offset_percent: number;\n duration_seconds?: number;\n}\n\nexport interface Anonymous {\n comments: boolean;\n reactions: boolean;\n votes: boolean;\n}\n\nexport interface AnswerGroup {\n id: string;\n label: string;\n count: number;\n}\n\nexport interface AnswerRecord {\n poll: Ref;\n user_id: string;\n text: string;\n created_at?: string;\n updated_at?: string;\n}\n\nexport interface Attribute {\n type: string;\n values?: string[];\n filterable?: boolean;\n sortable?: boolean;\n}\n\nexport interface Avatar {\n image?: string;\n renditions?: Rendition[];\n}\n\nexport interface BackgroundCue {\n file: string;\n from_page?: string;\n to_page?: string;\n from_anchor?: string;\n to_anchor?: string;\n loop?: boolean;\n layer?: string;\n}\n\nexport interface BanInput {\n reason?: string;\n until?: string;\n}\n\nexport interface BanNotice {\n scope: string;\n reason?: string;\n until?: string;\n}\n\nexport interface BannedWord {\n word: string;\n action: \"mask\" | \"reject\";\n created_by: string;\n created_at: string;\n}\n\nexport interface BannedWordInput {\n word: string;\n action?: \"mask\" | \"reject\";\n}\n\nexport interface Batch {\n source: string;\n preference_floor?: number;\n provision_scopes?: boolean;\n groups?: GroupRecord[];\n contents?: ContentRecord[];\n memberships?: MembershipSet[];\n offers?: OfferRecord[];\n aliases?: AliasRecord[];\n comments?: CommentRecord[];\n reactions?: ReactionRecord[];\n favorites?: FavoriteRecord[];\n polls?: PollRecord[];\n votes?: VoteRecord[];\n answers?: AnswerRecord[];\n signals?: SignalRecord[];\n deletes?: DeleteRecord[];\n}\n\nexport interface BodyImageInput {\n image?: string;\n version: string;\n}\n\nexport interface Card {\n id: string;\n kind: string;\n code: string;\n path: string;\n title: string;\n language: string;\n cover: PublicRendition[];\n version: CardVersion;\n groups: Record<string, Chip[]>;\n owner?: GroupCard;\n counts: Counts;\n access: CardAccess;\n viewer?: ViewerState;\n published_at?: string;\n attributes: Record<string, unknown>;\n}\n\nexport interface CardAccess {\n allowed: boolean;\n level: \"group\" | \"group_ppv\" | \"ppv\" | \"public\" | \"tier\";\n tiers?: string[];\n unknown?: boolean;\n sell?: string[];\n requires?: string;\n preview?: Preview;\n free_after?: string;\n offers?: Sale[];\n}\n\nexport interface CardVersion {\n id: string;\n code: string;\n languages: string[];\n traits: string[];\n label?: string;\n pages?: number;\n duration_ms?: number;\n has_audio: boolean;\n hls?: string;\n}\n\nexport interface CatalogCover {\n image?: string;\n url?: string;\n renditions?: PublicRendition[];\n}\n\nexport interface CatalogText {\n title: string;\n slug?: string;\n description?: string;\n body?: string;\n sort_key?: string;\n aliases?: string[];\n}\n\nexport interface Chip {\n id: string;\n slug: string;\n name: string;\n path?: string;\n}\n\nexport interface Comment {\n id: string;\n reply_to_id?: string;\n user_id?: string;\n anon_name?: string;\n language?: string;\n body: string;\n deleted: boolean;\n likes: number;\n dislikes: number;\n mine: number;\n reply_count: number;\n author?: PublicUser;\n moderation?: ModerationState;\n moderation_reason?: string;\n created_at: string;\n updated_at: string;\n}\n\nexport interface CommentBan {\n user_id: string;\n user?: PublicUser;\n scope: string;\n reason?: string;\n until?: string;\n banned_by: string;\n banned_at: string;\n expired: boolean;\n}\n\nexport interface CommentEdit {\n body: string;\n}\n\nexport interface CommentInput {\n body: string;\n reply_to_id?: string;\n anon_name?: string;\n language?: string;\n}\n\nexport interface CommentRecord {\n external_id: string;\n id?: string;\n target: TargetRef;\n language?: string;\n reply_to?: Ref;\n user_id?: string;\n anon_name?: string;\n body: string;\n deleted_at?: string;\n created_at?: string;\n updated_at?: string;\n}\n\nexport interface CommentStanding {\n can_comment: boolean;\n comments: string;\n locked: boolean;\n ban?: BanNotice;\n anonymous: boolean;\n max_length: number;\n user_id?: string;\n moderate: boolean;\n ban_scopes: (\"global\" | \"owner\")[];\n}\n\nexport interface CommitBody {\n ref: RefBody;\n operation_id: string;\n ops: Op[];\n}\n\nexport interface CommitReply {\n files: FileInfo[];\n}\n\nexport interface CompleteReply {\n blob: string;\n type: string;\n size: number;\n}\n\nexport interface Config {\n anonymous: Anonymous;\n comment_max_length: number;\n}\n\nexport interface Content {\n id: string;\n kind: string;\n code: string;\n path: string;\n language: string;\n title: string;\n slug?: string;\n description?: string;\n body?: string;\n decision: Decision;\n images?: Record<string, string>;\n cover?: CatalogCover;\n state: ContentState;\n listing: Listing;\n comments: string;\n publish_at?: string;\n published_at?: string;\n live: boolean;\n access: Rule;\n restrictions: string[];\n attributes: Record<string, unknown>;\n owner_group_id?: string;\n owner?: GroupCard;\n creators: Creator[];\n groups: GroupSummary[];\n versions: Version[];\n views?: number;\n viewer?: ViewerState;\n editor: boolean;\n text?: Record<string, CatalogText>;\n moderation?: ModerationState;\n moderation_reason?: string;\n ever_sold?: boolean;\n offer?: Offer;\n revision: number;\n created_at: string;\n updated_at: string;\n deleted_at?: string;\n}\n\nexport interface ContentFilter {\n group: GroupRef;\n default_visible: boolean;\n labels?: Record<string, string>;\n}\n\nexport interface ContentFilterState {\n group: GroupCard;\n label: string;\n default_visible: boolean;\n visible: boolean;\n overridden: boolean;\n}\n\nexport interface ContentFilters {\n filters: ContentFilterState[];\n include_deleted: boolean;\n}\n\nexport interface ContentFiltersUpdate {\n filters: FilterSetting[];\n include_deleted: boolean;\n}\n\nexport interface ContentGroups {\n kinds?: string[];\n memberships: Membership[];\n}\n\nexport interface ContentInput {\n kind: string;\n text?: Record<string, CatalogText>;\n listing?: Listing;\n comments?: string;\n access?: Rule;\n restrictions?: string[];\n attributes?: Record<string, unknown>;\n owner_group_id?: string;\n creators?: Creator[];\n versions?: VersionInput[];\n}\n\nexport interface ContentMemberships {\n content_id: string;\n memberships: Membership[];\n groups: GroupCard[];\n}\n\nexport interface ContentModel {\n languages: string[];\n tiers: string[];\n kinds: Record<string, Kind>;\n group_kinds: Record<string, GroupKind>;\n content_filters: ContentFilter[];\n signals: boolean;\n avatars?: PresetRule;\n}\n\nexport interface ContentPage {\n contents: Card[];\n next_cursor?: string;\n}\n\nexport interface ContentPatch {\n listing?: Listing;\n comments?: string;\n access?: Rule;\n restrictions?: string[];\n attributes?: Record<string, unknown>;\n creators?: Creator[];\n revision?: number;\n}\n\nexport interface ContentRecord {\n external_id: string;\n id?: string;\n kind: string;\n state: string;\n publish_at?: string;\n published_at?: string;\n deleted_at?: string;\n listing?: string;\n comments?: string;\n view_baseline?: number;\n access?: Rule;\n restrictions?: string[];\n attributes?: Record<string, unknown>;\n owner_group?: Ref;\n creators?: Creator[];\n text?: Record<string, CatalogText>;\n versions?: VersionRecord[];\n code?: string;\n created_at?: string;\n updated_at?: string;\n}\n\nexport interface ContentRef {\n tenant_id: string;\n content_kind: string;\n content_id: string;\n content_version_id?: string;\n}\n\nexport interface CopyBody {\n ref: RefBody;\n files: CopyRequest[];\n}\n\nexport interface CopyFrom {\n id: string;\n version?: string;\n path: string;\n}\n\nexport interface CopyReply {\n files: StagedCopy[];\n}\n\nexport interface CopyRequest {\n path: string;\n type: string;\n from: string;\n size?: number;\n}\n\nexport interface Count {\n content_kind: string;\n language: string;\n count: number;\n}\n\nexport interface Counts {\n likes?: number;\n dislikes?: number;\n favorites?: number;\n views?: number;\n}\n\nexport interface Creator {\n user_id: string;\n role?: string;\n position: number;\n}\n\nexport interface CreatorsInput {\n creators: Creator[];\n}\n\nexport interface Crop {\n x: number;\n y: number;\n w: number;\n h: number;\n}\n\nexport interface DateCount {\n date: string;\n count: number;\n free: number;\n}\n\nexport interface Decision {\n allowed: boolean;\n unknown?: boolean;\n sell?: string[];\n requires?: string;\n preview?: Preview;\n free_after?: string;\n offers?: Sale[];\n}\n\nexport interface DeleteRecord {\n entity: string;\n external_id: string;\n}\n\nexport interface Download {\n off?: boolean;\n serve?: string;\n}\n\nexport interface Edge {\n from: string;\n relation: string;\n to: string;\n}\n\nexport interface EdgeInput {\n relation: string;\n to: string;\n}\n\nexport interface EdgeRecord {\n relation: string;\n to: Ref;\n}\n\nexport interface Edit {\n crop?: Crop;\n rotate?: number;\n}\n\nexport interface EncodeProgress {\n phase: EncodePhase;\n queue_position?: number;\n segments_done?: number;\n segments_total?: number;\n percent: number;\n speed?: number;\n eta?: number;\n at: number;\n stalled?: boolean;\n stage?: number;\n stages?: number;\n}\n\nexport interface ErrorDetails {\n width?: number;\n height?: number;\n min_width?: number;\n max_pixels?: number;\n type?: string;\n allowed?: string[];\n size?: number;\n max_bytes?: number;\n frames?: number;\n max_frames?: number;\n seconds?: number;\n max_seconds?: number;\n min_aspect?: number;\n max_aspect?: number;\n max?: number;\n}\n\nexport interface ErrorReply {\n error: string;\n code: ErrorCode;\n retry_after?: number;\n action?: \"comment\" | \"comment_reaction\" | \"favorite\" | \"poll_vote\" | \"reaction\";\n ban?: BanNotice;\n blobs?: string[];\n details?: ErrorDetails;\n reason?: string;\n available_at?: string;\n}\n\nexport interface ExposureInput {\n render_id: string;\n stage: string;\n revision?: number;\n query_id?: string;\n surface?: string;\n ranker?: string;\n language?: string;\n occurred_at?: string;\n shown: Placement[];\n}\n\nexport interface Exposures {\n session?: string;\n exposures: ExposureInput[];\n}\n\nexport interface ExternalID {\n external_id: string;\n id: string;\n deleted: boolean;\n}\n\nexport interface ExternalIDList {\n entity: string;\n cursor?: string;\n limit?: number;\n}\n\nexport interface ExternalIDPage {\n entity: string;\n ids: ExternalID[];\n next_cursor?: string;\n}\n\nexport interface ExternalIDs {\n lists: ExternalIDPage[];\n}\n\nexport interface ExternalIDsQuery {\n source: string;\n lists: ExternalIDList[];\n}\n\nexport interface Failure {\n of: string;\n message: string;\n code?: ErrorCode;\n details?: ErrorDetails;\n}\n\nexport interface FavoriteRecord {\n target: TargetRef;\n user_id: string;\n created_at?: string;\n}\n\nexport interface FavoriteState {\n favorited: boolean;\n count?: number;\n}\n\nexport interface FeedItem {\n id: string;\n reply_to_id?: string;\n user_id?: string;\n anon_name?: string;\n language?: string;\n body: string;\n deleted: boolean;\n likes: number;\n dislikes: number;\n mine: number;\n reply_count: number;\n author?: PublicUser;\n moderation?: ModerationState;\n moderation_reason?: string;\n created_at: string;\n updated_at: string;\n tenant_id: string;\n content_kind: string;\n content_id: string;\n content_version_id?: string;\n}\n\nexport interface FileDownload {\n url: string;\n name: string;\n type: string;\n size?: number;\n expires_at: string;\n}\n\nexport interface FileInfo {\n path: string;\n type: string;\n size?: number;\n w?: number;\n h?: number;\n dur?: number;\n download?: string;\n url?: string;\n upload?: boolean;\n staged?: boolean;\n from?: string;\n edit?: Edit;\n frame?: Frame;\n meta?: Record<string, unknown>;\n unattached?: boolean;\n pending?: string[];\n failed?: Failure;\n editor_url?: string;\n progress?: EncodeProgress;\n}\n\nexport interface FilterSetting {\n group_id: string;\n visible: boolean;\n}\n\nexport interface ForegroundCue {\n file: string;\n page?: string;\n anchor?: string;\n}\n\nexport interface ForkBody {\n from: string;\n playback?: boolean;\n segments?: boolean;\n}\n\nexport interface Frame {\n t?: number;\n auto?: boolean;\n of?: string;\n}\n\nexport interface Group {\n id: string;\n kind: string;\n slug: string;\n scope_id?: string;\n parent_id?: string;\n state: \"active\" | \"deleted\" | \"merged\";\n merged_into?: string;\n attributes: Record<string, unknown>;\n cover?: GroupsCover;\n avatar?: Avatar;\n owner_user_id?: string;\n auth_scope?: string;\n created_at: string;\n updated_at: string;\n revision: number;\n}\n\nexport interface GroupAccess {\n key: string;\n offer?: Sale;\n member: boolean;\n editor?: boolean;\n unknown?: boolean;\n}\n\nexport interface GroupCard {\n id: string;\n kind: string;\n slug: string;\n scope_id?: string;\n parent_id?: string;\n name: string;\n name_language?: string;\n code?: string;\n path?: string;\n cover?: GroupsCover;\n avatar?: Avatar;\n count: number;\n attributes: Record<string, unknown>;\n access?: GroupAccess;\n created_at: string;\n}\n\nexport interface GroupDetail {\n id: string;\n kind: string;\n slug: string;\n scope_id?: string;\n parent_id?: string;\n state: \"active\" | \"deleted\" | \"merged\";\n merged_into?: string;\n attributes: Record<string, unknown>;\n cover?: GroupsCover;\n avatar?: Avatar;\n owner_user_id?: string;\n auth_scope?: string;\n created_at: string;\n updated_at: string;\n revision: number;\n name: string;\n name_language?: string;\n code?: string;\n path?: string;\n names: Name[];\n text: GroupsText[];\n parent?: GroupCard;\n scope?: GroupCard;\n children: GroupCard[];\n edges: Edge[];\n counts: Count[];\n access?: GroupAccess;\n offer?: Offer;\n}\n\nexport interface GroupInput {\n id?: string;\n kind: string;\n slug: string;\n scope_id?: string;\n parent_id?: string;\n attributes?: Record<string, unknown>;\n cover?: GroupsCover;\n avatar?: Avatar;\n owner_user_id?: string;\n auth_scope?: string;\n names?: Name[];\n text?: GroupsText[];\n}\n\nexport interface GroupJoin {\n group_id: string;\n key: string;\n member: boolean;\n}\n\nexport interface GroupKind {\n route?: string;\n ordered?: boolean;\n parent?: string;\n scoped_by?: string;\n owner?: string;\n max_per_owner?: number;\n uploads?: UploadLimits;\n sellable?: boolean;\n auth_scope?: boolean;\n windows?: boolean;\n version_level?: boolean;\n roles?: string[];\n attributes?: Record<string, Attribute>;\n}\n\nexport interface GroupPage {\n groups: GroupCard[];\n next_cursor?: string;\n}\n\nexport interface GroupRecord {\n external_id: string;\n id?: string;\n kind: string;\n slug: string;\n scope?: Ref;\n parent?: Ref;\n state?: string;\n names?: Name[];\n text?: GroupsText[];\n attributes?: Record<string, unknown>;\n cover_image?: string;\n cover_content?: Ref;\n avatar?: string;\n owner_user_id?: string;\n auth_scope?: string;\n edges?: EdgeRecord[];\n code?: string;\n created_at?: string;\n updated_at?: string;\n}\n\nexport interface GroupRef {\n kind: string;\n slug: string;\n}\n\nexport interface GroupSummary {\n id: string;\n kind: string;\n slug: string;\n name: string;\n code?: string;\n path?: string;\n}\n\nexport interface GroupUpdate {\n slug?: string;\n scope_id?: string;\n parent_id?: string;\n state?: \"active\" | \"deleted\" | \"merged\";\n attributes?: Record<string, unknown>;\n cover?: GroupsCover;\n avatar?: Avatar;\n owner_user_id?: string;\n auth_scope?: string;\n names?: Name[];\n text?: GroupsText[];\n}\n\nexport interface GroupsCover {\n image?: string;\n content_id?: string;\n auto?: boolean;\n renditions?: Rendition[];\n}\n\nexport interface GroupsText {\n language: string;\n description?: string;\n sort_key?: string;\n}\n\nexport interface HeldItem {\n kind: string;\n revision: number;\n id: string;\n ref: ContentRef;\n author_id?: string;\n author?: PublicUser;\n anon_name?: string;\n title?: string;\n body: string;\n reason: string;\n model?: string;\n prompt_version?: string;\n confidence?: number;\n error?: string;\n held_at: string;\n created_at: string;\n}\n\nexport interface HeldPage {\n items: HeldItem[];\n next?: string;\n}\n\nexport interface IPBan {\n network: string;\n reason?: string;\n until?: string;\n banned_by: string;\n banned_at: string;\n expired: boolean;\n}\n\nexport interface IPBanInput {\n network: string;\n reason?: string;\n until?: string;\n}\n\nexport interface ImageInput {\n image?: string;\n}\n\nexport interface InlineImage {\n ref: string;\n url: string | null;\n}\n\nexport interface Input {\n recipient: Recipient;\n origin: Origin;\n kind: string;\n data: unknown;\n created_at: string;\n status?: string;\n action_requested?: boolean;\n severity?: string;\n}\n\nexport interface Interaction {\n content?: Card;\n group?: GroupCard;\n value: number;\n at: string;\n}\n\nexport interface InteractionPage {\n items: Interaction[];\n next_cursor?: string;\n}\n\nexport interface Item {\n id: string;\n recipient: Recipient;\n origin: Origin;\n kind: string;\n data: unknown;\n created_at: string;\n status?: string;\n action_requested?: boolean;\n severity?: string;\n read_at: string | null;\n dismissed_at: string | null;\n resolved_at: string | null;\n updated_at: string;\n revision: number;\n}\n\nexport interface Kind {\n route: string;\n presentation: string;\n versions: VersionRules;\n segments?: boolean;\n groups?: string[];\n attributes?: Record<string, Attribute>;\n sort?: string;\n locked?: Locked;\n draft_ttl?: number;\n download?: Download;\n}\n\nexport interface LanguageTotal {\n public: number;\n tiers: Record<string, number>;\n}\n\nexport interface LibraryItem {\n content_id: string;\n kind: string;\n title: string;\n language?: string;\n deleted: boolean;\n whole: boolean;\n version_ids: string[];\n}\n\nexport interface LibraryPage {\n items: LibraryItem[];\n next_cursor?: string;\n complete: boolean;\n}\n\nexport interface Locked {\n show: string[];\n preview?: Preview;\n}\n\nexport interface MemberPage {\n members: Membership[];\n next_cursor?: string;\n}\n\nexport interface MemberRef {\n content_id: string;\n version_id?: string;\n}\n\nexport interface Membership {\n group_id?: string;\n content_id?: string;\n version_id?: string;\n role?: string;\n position?: number;\n active_from?: string;\n active_until?: string;\n state?: \"active\" | \"proposed\";\n}\n\nexport interface MembershipRecord {\n group: Ref;\n version?: Ref;\n role?: string;\n position?: number;\n active_from?: string;\n active_until?: string;\n state?: string;\n}\n\nexport interface MembershipSet {\n content: Ref;\n kinds: string[];\n memberships: MembershipRecord[];\n}\n\nexport interface MergeInput {\n into: string;\n}\n\nexport interface MergeReport {\n from: string;\n into: string;\n memberships_moved: number;\n names_moved: number;\n edges_moved: number;\n groups_moved: number;\n}\n\nexport interface MultipartReply {\n ticket: string;\n min_part_size: number;\n max_part_size: number;\n max_parts: number;\n}\n\nexport interface MyGroup {\n id: string;\n kind: string;\n slug: string;\n scope_id?: string;\n parent_id?: string;\n name: string;\n name_language?: string;\n code?: string;\n path?: string;\n cover?: GroupsCover;\n avatar?: Avatar;\n count: number;\n attributes: Record<string, unknown>;\n access?: GroupAccess;\n created_at: string;\n owner: boolean;\n editor: boolean;\n writer: boolean;\n moderator: boolean;\n member: boolean;\n}\n\nexport interface MyGroupPage {\n groups: MyGroup[];\n next_cursor?: string;\n}\n\nexport interface MyStats {\n completed: Record<string, number>;\n comments: number;\n comment_likes: number;\n comment_dislikes: number;\n}\n\nexport interface Name {\n language: string;\n kind: \"alias\" | \"name\";\n name: string;\n}\n\nexport interface Offer {\n id: string;\n target: Target;\n key: string;\n terms: Terms;\n state: \"active\" | \"withdrawn\";\n synced: boolean;\n product_id?: string;\n price_id?: string;\n last_error?: string;\n revision: number;\n updated_at: string;\n}\n\nexport interface OfferPage {\n offers: Offer[];\n next_cursor?: string;\n}\n\nexport interface OfferRecord {\n target: TargetRef;\n terms: Terms;\n}\n\nexport interface Op {\n op: OpName;\n path?: string;\n blob?: string;\n create_id?: string;\n index?: number;\n meta?: Record<string, unknown>;\n edit?: Edit;\n unattached?: boolean;\n to?: string;\n from?: CopyFrom;\n t?: number;\n auto?: boolean;\n preset?: string;\n force?: boolean;\n takedown?: boolean;\n}\n\nexport interface Order {\n members: MemberRef[];\n}\n\nexport interface OrderInput {\n ids: string[];\n}\n\nexport interface Origin {\n system: string;\n scope: string;\n id: string;\n subject?: string;\n}\n\nexport interface Page {\n items: Item[];\n next_cursor?: string;\n}\n\nexport interface PartBody {\n number: number;\n size: number;\n sha256: string;\n}\n\nexport interface PartReply {\n number: number;\n size?: number;\n sha256?: string;\n request?: RequestReply;\n}\n\nexport interface PartsBody {\n ticket: string;\n parts: PartBody[];\n}\n\nexport interface PartsReply {\n parts: PartReply[];\n}\n\nexport interface Pending {\n status: string;\n}\n\nexport interface Placement {\n content_id: string;\n position: number;\n}\n\nexport interface PlanPage {\n name: string;\n w: number;\n h: number;\n files: Record<string, string>;\n}\n\nexport interface PlanPlayback {\n item: RefBody;\n tracks: PlanTrack[];\n}\n\nexport interface PlanPreview {\n index: number;\n name: string;\n url: string;\n w: number;\n h: number;\n}\n\nexport interface PlanSegment {\n position?: number;\n title?: string;\n start: number;\n end: number;\n released: boolean;\n release_at?: string;\n locked: boolean;\n item?: RefBody;\n pages?: PlanPage[];\n}\n\nexport interface PlanTrack {\n name: string;\n path: string;\n duration?: number;\n}\n\nexport interface Poll {\n id: string;\n kind: PollKind;\n question: string;\n language: string;\n is_active: boolean;\n image_url?: string;\n live_at: string;\n closes_at?: string;\n closed: boolean;\n total_votes: number;\n options: PollOption[];\n voted: boolean;\n my_option?: string;\n answer_count?: number;\n groups?: AnswerGroup[];\n my_answer?: PollAnswer;\n}\n\nexport interface PollAnswer {\n id: string;\n text: string;\n classified: boolean;\n updated_at: string;\n}\n\nexport interface PollAnswerInput {\n text: string;\n}\n\nexport interface PollImage {\n image_url: string | null;\n}\n\nexport interface PollInput {\n kind?: PollKind;\n question: string;\n language: string;\n live_at?: string;\n closes_at?: string;\n options?: PollOptionInput[];\n}\n\nexport interface PollOption {\n id: string;\n label: string;\n image_url?: string;\n position: number;\n vote_count: number;\n}\n\nexport interface PollOptionInput {\n label: string;\n position: number;\n}\n\nexport interface PollOptionPatch {\n label?: string;\n position?: number;\n}\n\nexport interface PollOptionRecord {\n external_id: string;\n id?: string;\n label: string;\n position: number;\n}\n\nexport interface PollRecord {\n external_id: string;\n id?: string;\n kind?: string;\n question: string;\n language?: string;\n active?: boolean;\n live_at?: string;\n closes_at?: string;\n deleted_at?: string;\n created_at?: string;\n updated_at?: string;\n options?: PollOptionRecord[];\n}\n\nexport interface PollUpdate {\n question?: string;\n is_active?: boolean;\n live_at?: string;\n closes_at?: string | null;\n}\n\nexport interface PollVote {\n option_id: string;\n}\n\nexport interface PresetRule {\n kind: string;\n name: string;\n from: string;\n base: string;\n namespace: string;\n to: string;\n widths: number[];\n aspect?: string;\n min_width?: number;\n first?: number;\n default: boolean;\n stable?: boolean;\n}\n\nexport interface PresignBody {\n ref: RefBody;\n path: string;\n type: string;\n size: number;\n sha256: string;\n}\n\nexport interface PresignReply {\n path: string;\n blob: string;\n exists?: boolean;\n put?: RequestReply;\n multipart?: MultipartReply;\n process_on_upload?: boolean;\n}\n\nexport interface Preview {\n pages?: number;\n segments?: number;\n}\n\nexport interface PublicImage {\n from: string;\n preset: string;\n renditions: PublicRendition[];\n}\n\nexport interface PublicRendition {\n url: string;\n w: number;\n h: number;\n}\n\nexport interface PublicUser {\n id: string;\n name?: string;\n username: string;\n avatar?: string;\n avatar_srcset?: string;\n}\n\nexport interface Publication {\n notifications: Input[];\n}\n\nexport interface PublishInput {\n publish_at?: string;\n}\n\nexport interface Published {\n items: Item[];\n}\n\nexport interface RankedContent {\n id: string;\n title: string;\n path?: string;\n value: number;\n}\n\nexport interface ReactionCounts {\n likes?: number;\n dislikes?: number;\n mine: number;\n}\n\nexport interface ReactionRecord {\n target: TargetRef;\n user_id?: string;\n ip?: string;\n value: number;\n created_at?: string;\n updated_at?: string;\n}\n\nexport interface ReadResult {\n access: Access;\n expires: number;\n meta?: Record<string, unknown>;\n previews?: string[];\n public?: PublicImage[];\n locked?: boolean;\n total: number;\n offset: number;\n limit: number;\n hls?: string[];\n state?: ItemState;\n full?: boolean;\n uploads?: UploadRule[];\n files: FileInfo[];\n}\n\nexport interface ReaderDefaults {\n auto_advance_seconds?: number;\n}\n\nexport interface ReaderMeta {\n schema_version: number;\n defaults?: ReaderDefaults;\n anchors?: Anchor[];\n background?: BackgroundCue[];\n foreground?: ForegroundCue[];\n}\n\nexport interface ReaderPlan {\n content_id: string;\n version_id: string;\n publication_id: string;\n pages?: number;\n segments: PlanSegment[];\n previews: PlanPreview[];\n playback?: PlanPlayback;\n reader?: ReaderMeta;\n}\n\nexport interface RecentSearch {\n query: string;\n results: number;\n searched_at: string;\n}\n\nexport interface RecentSearches {\n searches: RecentSearch[];\n}\n\nexport interface Recipient {\n audience: string;\n id: string;\n}\n\nexport interface RecordFailure {\n section: string;\n index: number;\n external_id?: string;\n code: string;\n error: string;\n}\n\nexport interface Ref {\n id?: string;\n external_id?: string;\n}\n\nexport interface RefBody {\n kind: string;\n id: string;\n version?: string;\n}\n\nexport interface ReleaseDates {\n dates: DateCount[];\n next_cursor?: string;\n}\n\nexport interface Rendition {\n url: string;\n w: number;\n h: number;\n}\n\nexport interface RequestReply {\n method: string;\n url: string;\n headers: Record<string, string>;\n expires: string;\n}\n\nexport interface ResolveInput {\n decision: \"approve\" | \"reject\" | \"review\";\n revision: number;\n reason?: string;\n}\n\nexport interface Resolved {\n tenant_id: string;\n content_kind: string;\n content_id: string;\n content_version_id?: string;\n code: string;\n slug: string;\n slugs?: Record<string, string>;\n path: string;\n version_id?: string;\n}\n\nexport interface Restored {\n restored: boolean;\n}\n\nexport interface Result {\n written: Record<string, number>;\n skipped?: Record<string, number>;\n failed: RecordFailure[];\n ids: Record<string, Record<string, string>>;\n}\n\nexport interface ReviewOutcome {\n decision: \"approve\" | \"reject\" | \"review\";\n}\n\nexport interface Rule {\n level: \"group\" | \"group_ppv\" | \"ppv\" | \"public\" | \"tier\";\n tiers?: string[];\n group_id?: string;\n preview?: Preview;\n free_after?: string;\n}\n\nexport interface Sale {\n key: string;\n target: Target;\n terms: Terms;\n price_id?: string;\n}\n\nexport interface Segment {\n position: number;\n title?: string;\n start: number;\n end: number;\n release_at?: string;\n released: boolean;\n}\n\nexport interface SegmentsInput {\n segments: Segment[];\n}\n\nexport interface SignalInput {\n content_id: string;\n version_id?: string;\n type: string;\n event_id: string;\n revision?: number;\n occurred_at: string;\n duration_s?: number;\n progress?: number;\n resume?: string;\n render_id?: string;\n surface?: string;\n position?: number;\n}\n\nexport interface SignalRecord {\n target: TargetRef;\n user_id?: string;\n anon_key?: string;\n type: string;\n event_id: string;\n revision?: number;\n occurred_at: string;\n duration_s?: number;\n progress?: number;\n resume?: string;\n}\n\nexport interface Signals {\n session?: string;\n signals: SignalInput[];\n}\n\nexport interface SitemapAlias {\n language: string;\n path: string;\n image?: string;\n}\n\nexport interface SitemapEntry {\n id: string;\n code: string;\n path: string;\n lastmod: string;\n languages: SitemapAlias[];\n}\n\nexport interface SitemapPage {\n entries: SitemapEntry[];\n next_cursor?: string;\n}\n\nexport interface StagedCopy {\n path: string;\n blob: string;\n size: number;\n}\n\nexport interface Target {\n kind: \"content\" | \"group\" | \"version\";\n id: string;\n content_id?: string;\n}\n\nexport interface TargetRef {\n content?: Ref;\n version?: Ref;\n group?: Ref;\n comment?: Ref;\n}\n\nexport interface Terms {\n amount: string;\n currency: string;\n access_hours?: number;\n renew_hours?: number;\n}\n\nexport interface TicketBody {\n ticket: string;\n}\n\nexport interface TopContents {\n contents: RankedContent[];\n}\n\nexport interface Total {\n public: number;\n tiers: Record<string, number>;\n languages: Record<string, LanguageTotal>;\n}\n\nexport interface Totals {\n kinds: Record<string, Total>;\n}\n\nexport interface Typeahead {\n contents: Card[];\n groups: GroupCard[];\n}\n\nexport interface UploadLimits {\n files_per_hour?: number;\n bytes_per_day?: number;\n quota_bytes?: number;\n}\n\nexport interface UploadRule {\n path: string;\n types: string[];\n max_bytes: number;\n max?: number;\n named?: boolean;\n body?: boolean;\n frames?: string;\n aspect?: string;\n min_width?: number;\n video?: VideoLimits;\n min_aspect?: number;\n max_aspect?: number;\n}\n\nexport interface Version {\n id: string;\n code: string;\n position: number;\n languages: string[];\n traits: string[];\n label?: string;\n title?: string;\n description?: string;\n state?: ContentState;\n publish_at?: string;\n published_at?: string;\n live: boolean;\n access?: Rule;\n attributes: Record<string, unknown>;\n pages?: number;\n duration_ms?: number;\n width?: number;\n height?: number;\n has_audio: boolean;\n ordinal?: number;\n cover?: CatalogCover;\n segments: Segment[];\n decision: Decision;\n text?: Record<string, VersionText>;\n offer?: Offer;\n viewer?: ViewerState;\n}\n\nexport interface VersionFork {\n from: string;\n playback?: boolean;\n segments?: boolean;\n}\n\nexport interface VersionInput {\n languages?: string[];\n traits?: string[];\n label?: string;\n state?: VersionState;\n publish_at?: string;\n access?: Rule;\n clear_access?: boolean;\n attributes?: Record<string, unknown>;\n text?: Record<string, VersionText>;\n segments?: Segment[];\n ordinal?: number;\n cover?: string;\n fork?: VersionFork;\n}\n\nexport interface VersionRecord {\n external_id: string;\n id?: string;\n code?: string;\n languages: string[];\n traits?: string[];\n label?: string;\n state?: string;\n publish_at?: string;\n published_at?: string;\n deleted_at?: string;\n access?: Rule;\n attributes?: Record<string, unknown>;\n ordinal?: number;\n cover?: string;\n text?: Record<string, VersionText>;\n segments?: Segment[];\n created_at?: string;\n updated_at?: string;\n}\n\nexport interface VersionRules {\n languages: string;\n traits?: string[];\n hidden?: boolean;\n}\n\nexport interface VersionText {\n title?: string;\n description?: string;\n}\n\nexport interface VideoLimits {\n max_seconds?: number;\n max_fps?: number;\n max_pixels?: number;\n max_work?: number;\n}\n\nexport interface ViewerState {\n favorited: boolean;\n seen: boolean;\n completed: boolean;\n progress: number;\n progress_max: number;\n resume?: string;\n last_view_at?: string;\n}\n\nexport interface VoteRecord {\n poll: Ref;\n option: Ref;\n user_id?: string;\n ip?: string;\n created_at?: string;\n}\n\nexport interface Written {\n count: number;\n}\n"],"mappings":";AAOA,IAAa,IAAiB,UACjB,IAAgB,SAChB,IAAgB"}
1
+ {"version":3,"file":"wire.js","names":[],"sources":["../../../src/client/generated/wire.ts"],"sourcesContent":["// Generated by `go generate ./internal/contract` from ContentKit's route catalog. Do not edit.\n\nimport type { ErrorCode } from \"./error-codes.js\";\n\nexport type { ErrorCode };\n\n// Upload sizes: one PUT up to MAX_SINGLE_PUT bytes, else multipart parts.\nexport const MAX_SINGLE_PUT = 67108864;\nexport const MIN_PART_SIZE = 8388608;\nexport const MAX_PART_SIZE = 16777216;\n\nexport type OpName = \"put\" | \"edit\" | \"move\" | \"rename\" | \"remove\" | \"attach\" | \"copy\" | \"frame\" | \"meta\" | \"regenerate\";\nexport type Access = \"full\" | \"none\";\nexport type ItemState = \"ready\" | \"processing\" | \"failed\";\nexport type EncodePhase = \"queued\" | \"downloading\" | \"probing\" | \"encoding\" | \"muxing\" | \"uploading\" | \"publishing\" | \"images\";\nexport type PollKind = \"multiple_choice\" | \"free_text\";\nexport type ModerationState = \"approved\" | \"held\" | \"rejected\";\nexport type ContentState = \"draft\" | \"published\" | \"deleted\";\nexport type VersionState = \"draft\" | \"published\";\nexport type Listing = \"listed\" | \"members\" | \"unlisted\";\nexport type AliasStatus = \"found\" | \"not_found\" | \"gone\";\n\nexport interface Activity {\n days: ActivityDay[];\n}\n\nexport interface ActivityDay {\n date: string;\n views: number;\n viewers: number;\n users: number;\n}\n\nexport interface AdminComment {\n id: string;\n reply_to_id?: string;\n user_id?: string;\n anon_name?: string;\n language?: string;\n body: string;\n deleted: boolean;\n likes: number;\n dislikes: number;\n mine: number;\n reply_count: number;\n author?: PublicUser;\n moderation?: ModerationState;\n moderation_reason?: string;\n created_at: string;\n updated_at: string;\n tenant_id: string;\n content_kind: string;\n content_id: string;\n content_version_id?: string;\n deleted_at?: string;\n}\n\nexport interface AliasKey {\n source: string;\n kind: string;\n key: string;\n}\n\nexport interface AliasLookup {\n aliases: AliasKey[];\n}\n\nexport interface AliasRecord {\n kind: string;\n key: string;\n locator?: string;\n target: TargetRef;\n}\n\nexport interface AliasResult {\n source: string;\n kind: string;\n key: string;\n status: AliasStatus;\n resolved?: Resolved;\n locator?: string;\n}\n\nexport interface AliasResults {\n aliases: AliasResult[];\n}\n\nexport interface Anchor {\n id: string;\n page: string;\n offset_percent: number;\n duration_seconds?: number;\n}\n\nexport interface Anonymous {\n comments: boolean;\n reactions: boolean;\n votes: boolean;\n}\n\nexport interface AnswerGroup {\n id: string;\n label: string;\n count: number;\n}\n\nexport interface AnswerRecord {\n poll: Ref;\n user_id: string;\n text: string;\n created_at?: string;\n updated_at?: string;\n}\n\nexport interface Attribute {\n type: string;\n values?: string[];\n filterable?: boolean;\n sortable?: boolean;\n}\n\nexport interface Avatar {\n image?: string;\n renditions?: Rendition[];\n}\n\nexport interface BackgroundCue {\n file: string;\n from_page?: string;\n to_page?: string;\n from_anchor?: string;\n to_anchor?: string;\n loop?: boolean;\n layer?: string;\n}\n\nexport interface BanInput {\n reason?: string;\n until?: string;\n}\n\nexport interface BanNotice {\n scope: string;\n reason?: string;\n until?: string;\n}\n\nexport interface BannedWord {\n word: string;\n action: \"mask\" | \"reject\";\n created_by: string;\n created_at: string;\n}\n\nexport interface BannedWordInput {\n word: string;\n action?: \"mask\" | \"reject\";\n}\n\nexport interface Batch {\n source: string;\n preference_floor?: number;\n provision_scopes?: boolean;\n groups?: GroupRecord[];\n contents?: ContentRecord[];\n memberships?: MembershipSet[];\n offers?: OfferRecord[];\n aliases?: AliasRecord[];\n comments?: CommentRecord[];\n reactions?: ReactionRecord[];\n favorites?: FavoriteRecord[];\n polls?: PollRecord[];\n votes?: VoteRecord[];\n answers?: AnswerRecord[];\n signals?: SignalRecord[];\n deletes?: DeleteRecord[];\n}\n\nexport interface BodyImageInput {\n image?: string;\n version: string;\n}\n\nexport interface Card {\n id: string;\n kind: string;\n code: string;\n path: string;\n title: string;\n language: string;\n cover: PublicRendition[];\n version: CardVersion;\n groups: Record<string, Chip[]>;\n owner?: GroupCard;\n counts: Counts;\n access: CardAccess;\n viewer?: ViewerState;\n published_at?: string;\n attributes: Record<string, unknown>;\n}\n\nexport interface CardAccess {\n allowed: boolean;\n level: \"group\" | \"group_ppv\" | \"ppv\" | \"public\" | \"tier\";\n tiers?: string[];\n unknown?: boolean;\n sell?: string[];\n requires?: string;\n preview?: Preview;\n free_after?: string;\n offers?: Sale[];\n}\n\nexport interface CardVersion {\n id: string;\n code: string;\n languages: string[];\n traits: string[];\n label?: string;\n pages?: number;\n duration_ms?: number;\n has_audio: boolean;\n hls?: string;\n}\n\nexport interface CatalogCover {\n image?: string;\n url?: string;\n renditions?: PublicRendition[];\n}\n\nexport interface CatalogText {\n title: string;\n slug?: string;\n description?: string;\n body?: string;\n sort_key?: string;\n aliases?: string[];\n}\n\nexport interface Chip {\n id: string;\n slug: string;\n name: string;\n path?: string;\n}\n\nexport interface Comment {\n id: string;\n reply_to_id?: string;\n user_id?: string;\n anon_name?: string;\n language?: string;\n body: string;\n deleted: boolean;\n likes: number;\n dislikes: number;\n mine: number;\n reply_count: number;\n author?: PublicUser;\n moderation?: ModerationState;\n moderation_reason?: string;\n created_at: string;\n updated_at: string;\n}\n\nexport interface CommentBan {\n user_id: string;\n user?: PublicUser;\n scope: string;\n reason?: string;\n until?: string;\n banned_by: string;\n banned_at: string;\n expired: boolean;\n}\n\nexport interface CommentEdit {\n body: string;\n}\n\nexport interface CommentInput {\n body: string;\n reply_to_id?: string;\n anon_name?: string;\n language?: string;\n}\n\nexport interface CommentRecord {\n external_id: string;\n id?: string;\n target: TargetRef;\n language?: string;\n reply_to?: Ref;\n user_id?: string;\n anon_name?: string;\n body: string;\n deleted_at?: string;\n created_at?: string;\n updated_at?: string;\n}\n\nexport interface CommentStanding {\n can_comment: boolean;\n comments: string;\n locked: boolean;\n ban?: BanNotice;\n anonymous: boolean;\n max_length: number;\n user_id?: string;\n moderate: boolean;\n ban_scopes: (\"global\" | \"owner\")[];\n}\n\nexport interface CommitBody {\n ref: RefBody;\n operation_id: string;\n ops: Op[];\n}\n\nexport interface CommitReply {\n files: FileInfo[];\n}\n\nexport interface CompleteReply {\n blob: string;\n type: string;\n size: number;\n}\n\nexport interface Config {\n anonymous: Anonymous;\n comment_max_length: number;\n}\n\nexport interface Content {\n id: string;\n kind: string;\n code: string;\n path: string;\n language: string;\n title: string;\n slug?: string;\n description?: string;\n body?: string;\n decision: Decision;\n images?: Record<string, string>;\n cover?: CatalogCover;\n state: ContentState;\n listing: Listing;\n comments: string;\n publish_at?: string;\n published_at?: string;\n live: boolean;\n access: Rule;\n restrictions: string[];\n attributes: Record<string, unknown>;\n owner_group_id?: string;\n owner?: GroupCard;\n creators: Creator[];\n groups: GroupSummary[];\n versions: Version[];\n views?: number;\n viewer?: ViewerState;\n editor: boolean;\n text?: Record<string, CatalogText>;\n moderation?: ModerationState;\n moderation_reason?: string;\n ever_sold?: boolean;\n offer?: Offer;\n revision: number;\n created_at: string;\n updated_at: string;\n deleted_at?: string;\n}\n\nexport interface ContentFilter {\n group: GroupRef;\n default_visible: boolean;\n labels?: Record<string, string>;\n}\n\nexport interface ContentFilterState {\n group: GroupCard;\n label: string;\n default_visible: boolean;\n visible: boolean;\n overridden: boolean;\n}\n\nexport interface ContentFilters {\n filters: ContentFilterState[];\n include_deleted: boolean;\n}\n\nexport interface ContentFiltersUpdate {\n filters: FilterSetting[];\n include_deleted: boolean;\n}\n\nexport interface ContentGroups {\n kinds?: string[];\n memberships: Membership[];\n}\n\nexport interface ContentInput {\n kind: string;\n text?: Record<string, CatalogText>;\n listing?: Listing;\n comments?: string;\n access?: Rule;\n restrictions?: string[];\n attributes?: Record<string, unknown>;\n owner_group_id?: string;\n creators?: Creator[];\n versions?: VersionInput[];\n}\n\nexport interface ContentMemberships {\n content_id: string;\n memberships: Membership[];\n groups: GroupCard[];\n}\n\nexport interface ContentModel {\n languages: string[];\n tiers: string[];\n kinds: Record<string, Kind>;\n group_kinds: Record<string, GroupKind>;\n content_filters: ContentFilter[];\n signals: boolean;\n avatars?: PresetRule;\n}\n\nexport interface ContentPage {\n contents: Card[];\n next_cursor?: string;\n}\n\nexport interface ContentPatch {\n listing?: Listing;\n comments?: string;\n access?: Rule;\n restrictions?: string[];\n attributes?: Record<string, unknown>;\n creators?: Creator[];\n revision?: number;\n}\n\nexport interface ContentRecord {\n external_id: string;\n id?: string;\n kind: string;\n state: string;\n publish_at?: string;\n published_at?: string;\n deleted_at?: string;\n listing?: string;\n comments?: string;\n view_baseline?: number;\n access?: Rule;\n restrictions?: string[];\n attributes?: Record<string, unknown>;\n owner_group?: Ref;\n creators?: Creator[];\n text?: Record<string, CatalogText>;\n versions?: VersionRecord[];\n code?: string;\n created_at?: string;\n updated_at?: string;\n}\n\nexport interface ContentRef {\n tenant_id: string;\n content_kind: string;\n content_id: string;\n content_version_id?: string;\n}\n\nexport interface CopyBody {\n ref: RefBody;\n files: CopyRequest[];\n}\n\nexport interface CopyFrom {\n id: string;\n version?: string;\n path: string;\n}\n\nexport interface CopyReply {\n files: StagedCopy[];\n}\n\nexport interface CopyRequest {\n path: string;\n type: string;\n from: string;\n size?: number;\n}\n\nexport interface Count {\n content_kind: string;\n language: string;\n count: number;\n}\n\nexport interface Counts {\n likes?: number;\n dislikes?: number;\n favorites?: number;\n views?: number;\n}\n\nexport interface Creator {\n user_id: string;\n role?: string;\n position: number;\n}\n\nexport interface CreatorsInput {\n creators: Creator[];\n}\n\nexport interface Crop {\n x: number;\n y: number;\n w: number;\n h: number;\n}\n\nexport interface DateCount {\n date: string;\n count: number;\n free: number;\n}\n\nexport interface Decision {\n allowed: boolean;\n unknown?: boolean;\n sell?: string[];\n requires?: string;\n preview?: Preview;\n free_after?: string;\n offers?: Sale[];\n}\n\nexport interface DeleteRecord {\n entity: string;\n external_id: string;\n}\n\nexport interface Download {\n off?: boolean;\n serve?: string;\n}\n\nexport interface Edge {\n from: string;\n relation: string;\n to: string;\n}\n\nexport interface EdgeInput {\n relation: string;\n to: string;\n}\n\nexport interface EdgeRecord {\n relation: string;\n to: Ref;\n}\n\nexport interface Edit {\n crop?: Crop;\n rotate?: number;\n}\n\nexport interface EncodeProgress {\n phase: EncodePhase;\n queue_position?: number;\n segments_done?: number;\n segments_total?: number;\n percent: number;\n speed?: number;\n eta?: number;\n at: number;\n stalled?: boolean;\n stage?: number;\n stages?: number;\n}\n\nexport interface ErrorDetails {\n width?: number;\n height?: number;\n min_width?: number;\n max_pixels?: number;\n type?: string;\n allowed?: string[];\n size?: number;\n max_bytes?: number;\n frames?: number;\n max_frames?: number;\n seconds?: number;\n max_seconds?: number;\n min_aspect?: number;\n max_aspect?: number;\n max?: number;\n}\n\nexport interface ErrorReply {\n error: string;\n code: ErrorCode;\n retry_after?: number;\n action?: \"comment\" | \"comment_reaction\" | \"favorite\" | \"poll_vote\" | \"reaction\";\n ban?: BanNotice;\n blobs?: string[];\n details?: ErrorDetails;\n reason?: string;\n available_at?: string;\n}\n\nexport interface ExposureInput {\n render_id: string;\n stage: string;\n revision?: number;\n query_id?: string;\n surface?: string;\n ranker?: string;\n language?: string;\n occurred_at?: string;\n shown: Placement[];\n}\n\nexport interface Exposures {\n session?: string;\n exposures: ExposureInput[];\n}\n\nexport interface ExternalID {\n external_id: string;\n id: string;\n deleted: boolean;\n}\n\nexport interface ExternalIDList {\n entity: string;\n cursor?: string;\n limit?: number;\n}\n\nexport interface ExternalIDPage {\n entity: string;\n ids: ExternalID[];\n next_cursor?: string;\n}\n\nexport interface ExternalIDs {\n lists: ExternalIDPage[];\n}\n\nexport interface ExternalIDsQuery {\n source: string;\n lists: ExternalIDList[];\n}\n\nexport interface Failure {\n of: string;\n message: string;\n code?: ErrorCode;\n details?: ErrorDetails;\n}\n\nexport interface FavoriteRecord {\n target: TargetRef;\n user_id: string;\n created_at?: string;\n}\n\nexport interface FavoriteState {\n favorited: boolean;\n count?: number;\n}\n\nexport interface FeedItem {\n id: string;\n reply_to_id?: string;\n user_id?: string;\n anon_name?: string;\n language?: string;\n body: string;\n deleted: boolean;\n likes: number;\n dislikes: number;\n mine: number;\n reply_count: number;\n author?: PublicUser;\n moderation?: ModerationState;\n moderation_reason?: string;\n created_at: string;\n updated_at: string;\n tenant_id: string;\n content_kind: string;\n content_id: string;\n content_version_id?: string;\n}\n\nexport interface FileDownload {\n url: string;\n name: string;\n type: string;\n size?: number;\n expires_at: string;\n}\n\nexport interface FileInfo {\n path: string;\n type: string;\n size?: number;\n w?: number;\n h?: number;\n dur?: number;\n download?: string;\n url?: string;\n upload?: boolean;\n staged?: boolean;\n from?: string;\n edit?: Edit;\n frame?: Frame;\n meta?: Record<string, unknown>;\n unattached?: boolean;\n pending?: string[];\n failed?: Failure;\n editor_url?: string;\n progress?: EncodeProgress;\n}\n\nexport interface FilterSetting {\n group_id: string;\n visible: boolean;\n}\n\nexport interface ForegroundCue {\n file: string;\n page?: string;\n anchor?: string;\n}\n\nexport interface ForkBody {\n from: string;\n playback?: boolean;\n segments?: boolean;\n}\n\nexport interface Frame {\n t?: number;\n auto?: boolean;\n of?: string;\n}\n\nexport interface Group {\n id: string;\n kind: string;\n slug: string;\n scope_id?: string;\n parent_id?: string;\n state: \"active\" | \"deleted\" | \"merged\";\n merged_into?: string;\n attributes: Record<string, unknown>;\n cover?: GroupsCover;\n avatar?: Avatar;\n owner_user_id?: string;\n auth_scope?: string;\n created_at: string;\n updated_at: string;\n revision: number;\n}\n\nexport interface GroupAccess {\n key: string;\n offer?: Sale;\n member: boolean;\n editor?: boolean;\n unknown?: boolean;\n}\n\nexport interface GroupCard {\n id: string;\n kind: string;\n slug: string;\n scope_id?: string;\n parent_id?: string;\n name: string;\n name_language?: string;\n code?: string;\n path?: string;\n cover?: GroupsCover;\n avatar?: Avatar;\n count: number;\n attributes: Record<string, unknown>;\n access?: GroupAccess;\n created_at: string;\n}\n\nexport interface GroupDetail {\n id: string;\n kind: string;\n slug: string;\n scope_id?: string;\n parent_id?: string;\n state: \"active\" | \"deleted\" | \"merged\";\n merged_into?: string;\n attributes: Record<string, unknown>;\n cover?: GroupsCover;\n avatar?: Avatar;\n owner_user_id?: string;\n auth_scope?: string;\n created_at: string;\n updated_at: string;\n revision: number;\n name: string;\n name_language?: string;\n code?: string;\n path?: string;\n names: Name[];\n text: GroupsText[];\n parent?: GroupCard;\n scope?: GroupCard;\n children: GroupCard[];\n edges: Edge[];\n counts: Count[];\n access?: GroupAccess;\n offer?: Offer;\n}\n\nexport interface GroupInput {\n id?: string;\n kind: string;\n slug: string;\n scope_id?: string;\n parent_id?: string;\n attributes?: Record<string, unknown>;\n cover?: GroupsCover;\n avatar?: Avatar;\n owner_user_id?: string;\n auth_scope?: string;\n names?: Name[];\n text?: GroupsText[];\n}\n\nexport interface GroupJoin {\n group_id: string;\n key: string;\n member: boolean;\n}\n\nexport interface GroupKind {\n route?: string;\n ordered?: boolean;\n parent?: string;\n scoped_by?: string;\n owner?: string;\n max_per_owner?: number;\n uploads?: UploadLimits;\n sellable?: boolean;\n auth_scope?: boolean;\n windows?: boolean;\n version_level?: boolean;\n roles?: string[];\n attributes?: Record<string, Attribute>;\n}\n\nexport interface GroupPage {\n groups: GroupCard[];\n next_cursor?: string;\n}\n\nexport interface GroupRecord {\n external_id: string;\n id?: string;\n kind: string;\n slug: string;\n scope?: Ref;\n parent?: Ref;\n state?: string;\n names?: Name[];\n text?: GroupsText[];\n attributes?: Record<string, unknown>;\n cover_image?: string;\n cover_content?: Ref;\n avatar?: string;\n owner_user_id?: string;\n auth_scope?: string;\n edges?: EdgeRecord[];\n code?: string;\n created_at?: string;\n updated_at?: string;\n}\n\nexport interface GroupRef {\n kind: string;\n slug: string;\n}\n\nexport interface GroupSummary {\n id: string;\n kind: string;\n slug: string;\n name: string;\n code?: string;\n path?: string;\n}\n\nexport interface GroupUpdate {\n slug?: string;\n scope_id?: string;\n parent_id?: string;\n state?: \"active\" | \"deleted\" | \"merged\";\n attributes?: Record<string, unknown>;\n cover?: GroupsCover;\n avatar?: Avatar;\n owner_user_id?: string;\n auth_scope?: string;\n names?: Name[];\n text?: GroupsText[];\n}\n\nexport interface GroupsCover {\n image?: string;\n content_id?: string;\n auto?: boolean;\n renditions?: Rendition[];\n}\n\nexport interface GroupsText {\n language: string;\n description?: string;\n sort_key?: string;\n}\n\nexport interface HeldItem {\n kind: string;\n revision: number;\n id: string;\n ref: ContentRef;\n author_id?: string;\n author?: PublicUser;\n anon_name?: string;\n title?: string;\n body: string;\n reason: string;\n model?: string;\n prompt_version?: string;\n confidence?: number;\n error?: string;\n held_at: string;\n created_at: string;\n}\n\nexport interface HeldPage {\n items: HeldItem[];\n next?: string;\n}\n\nexport interface IPBan {\n network: string;\n reason?: string;\n until?: string;\n banned_by: string;\n banned_at: string;\n expired: boolean;\n}\n\nexport interface IPBanInput {\n network: string;\n reason?: string;\n until?: string;\n}\n\nexport interface ImageInput {\n image?: string;\n}\n\nexport interface InlineImage {\n ref: string;\n url: string | null;\n}\n\nexport interface Input {\n recipient: Recipient;\n origin: Origin;\n kind: string;\n data: unknown;\n created_at: string;\n visibility?: string;\n app_id?: string;\n status?: string;\n action_requested?: boolean;\n severity?: string;\n}\n\nexport interface Interaction {\n content?: Card;\n group?: GroupCard;\n value: number;\n at: string;\n}\n\nexport interface InteractionPage {\n items: Interaction[];\n next_cursor?: string;\n}\n\nexport interface Item {\n id: string;\n recipient: Recipient;\n origin: Origin;\n kind: string;\n data: unknown;\n created_at: string;\n visibility?: string;\n app_id?: string;\n status?: string;\n action_requested?: boolean;\n severity?: string;\n read_at: string | null;\n dismissed_at: string | null;\n resolved_at: string | null;\n updated_at: string;\n revision: number;\n}\n\nexport interface Kind {\n route: string;\n presentation: string;\n versions: VersionRules;\n segments?: boolean;\n groups?: string[];\n attributes?: Record<string, Attribute>;\n sort?: string;\n locked?: Locked;\n draft_ttl?: number;\n download?: Download;\n}\n\nexport interface LanguageTotal {\n public: number;\n tiers: Record<string, number>;\n}\n\nexport interface LibraryItem {\n content_id: string;\n kind: string;\n title: string;\n language?: string;\n deleted: boolean;\n whole: boolean;\n version_ids: string[];\n}\n\nexport interface LibraryPage {\n items: LibraryItem[];\n next_cursor?: string;\n complete: boolean;\n}\n\nexport interface Locked {\n show: string[];\n preview?: Preview;\n}\n\nexport interface MemberPage {\n members: Membership[];\n next_cursor?: string;\n}\n\nexport interface MemberRef {\n content_id: string;\n version_id?: string;\n}\n\nexport interface Membership {\n group_id?: string;\n content_id?: string;\n version_id?: string;\n role?: string;\n position?: number;\n active_from?: string;\n active_until?: string;\n state?: \"active\" | \"proposed\";\n}\n\nexport interface MembershipRecord {\n group: Ref;\n version?: Ref;\n role?: string;\n position?: number;\n active_from?: string;\n active_until?: string;\n state?: string;\n}\n\nexport interface MembershipSet {\n content: Ref;\n kinds: string[];\n memberships: MembershipRecord[];\n}\n\nexport interface MergeInput {\n into: string;\n}\n\nexport interface MergeReport {\n from: string;\n into: string;\n memberships_moved: number;\n names_moved: number;\n edges_moved: number;\n groups_moved: number;\n}\n\nexport interface MultipartReply {\n ticket: string;\n min_part_size: number;\n max_part_size: number;\n max_parts: number;\n}\n\nexport interface MyGroup {\n id: string;\n kind: string;\n slug: string;\n scope_id?: string;\n parent_id?: string;\n name: string;\n name_language?: string;\n code?: string;\n path?: string;\n cover?: GroupsCover;\n avatar?: Avatar;\n count: number;\n attributes: Record<string, unknown>;\n access?: GroupAccess;\n created_at: string;\n owner: boolean;\n editor: boolean;\n writer: boolean;\n moderator: boolean;\n member: boolean;\n}\n\nexport interface MyGroupPage {\n groups: MyGroup[];\n next_cursor?: string;\n}\n\nexport interface MyStats {\n completed: Record<string, number>;\n access_seconds?: Record<string, number>;\n comments: number;\n comment_likes: number;\n comment_dislikes: number;\n}\n\nexport interface Name {\n language: string;\n kind: \"alias\" | \"name\";\n name: string;\n}\n\nexport interface Offer {\n id: string;\n target: Target;\n key: string;\n terms: Terms;\n state: \"active\" | \"withdrawn\";\n synced: boolean;\n product_id?: string;\n price_id?: string;\n last_error?: string;\n revision: number;\n updated_at: string;\n}\n\nexport interface OfferPage {\n offers: Offer[];\n next_cursor?: string;\n}\n\nexport interface OfferRecord {\n target: TargetRef;\n terms: Terms;\n}\n\nexport interface Op {\n op: OpName;\n path?: string;\n blob?: string;\n create_id?: string;\n index?: number;\n meta?: Record<string, unknown>;\n edit?: Edit;\n unattached?: boolean;\n to?: string;\n from?: CopyFrom;\n t?: number;\n auto?: boolean;\n preset?: string;\n force?: boolean;\n takedown?: boolean;\n}\n\nexport interface Order {\n members: MemberRef[];\n}\n\nexport interface OrderInput {\n ids: string[];\n}\n\nexport interface Origin {\n system: string;\n scope: string;\n id: string;\n subject?: string;\n}\n\nexport interface Page {\n items: Item[];\n next_cursor?: string;\n}\n\nexport interface PartBody {\n number: number;\n size: number;\n sha256: string;\n}\n\nexport interface PartReply {\n number: number;\n size?: number;\n sha256?: string;\n request?: RequestReply;\n}\n\nexport interface PartsBody {\n ticket: string;\n parts: PartBody[];\n}\n\nexport interface PartsReply {\n parts: PartReply[];\n}\n\nexport interface Pending {\n status: string;\n}\n\nexport interface Placement {\n content_id: string;\n position: number;\n}\n\nexport interface PlanPage {\n name: string;\n w: number;\n h: number;\n files: Record<string, string>;\n}\n\nexport interface PlanPlayback {\n item: RefBody;\n tracks: PlanTrack[];\n}\n\nexport interface PlanPreview {\n index: number;\n name: string;\n url: string;\n w: number;\n h: number;\n}\n\nexport interface PlanSegment {\n position?: number;\n title?: string;\n start: number;\n end: number;\n released: boolean;\n release_at?: string;\n locked: boolean;\n item?: RefBody;\n pages?: PlanPage[];\n}\n\nexport interface PlanTrack {\n name: string;\n path: string;\n duration?: number;\n}\n\nexport interface Poll {\n id: string;\n kind: PollKind;\n question: string;\n language: string;\n is_active: boolean;\n image_url?: string;\n live_at: string;\n closes_at?: string;\n closed: boolean;\n total_votes: number;\n options: PollOption[];\n voted: boolean;\n my_option?: string;\n answer_count?: number;\n groups?: AnswerGroup[];\n my_answer?: PollAnswer;\n}\n\nexport interface PollAnswer {\n id: string;\n text: string;\n classified: boolean;\n updated_at: string;\n}\n\nexport interface PollAnswerInput {\n text: string;\n}\n\nexport interface PollImage {\n image_url: string | null;\n}\n\nexport interface PollInput {\n kind?: PollKind;\n question: string;\n language: string;\n live_at?: string;\n closes_at?: string;\n options?: PollOptionInput[];\n}\n\nexport interface PollOption {\n id: string;\n label: string;\n image_url?: string;\n position: number;\n vote_count: number;\n}\n\nexport interface PollOptionInput {\n label: string;\n position: number;\n}\n\nexport interface PollOptionPatch {\n label?: string;\n position?: number;\n}\n\nexport interface PollOptionRecord {\n external_id: string;\n id?: string;\n label: string;\n position: number;\n}\n\nexport interface PollRecord {\n external_id: string;\n id?: string;\n kind?: string;\n question: string;\n language?: string;\n active?: boolean;\n live_at?: string;\n closes_at?: string;\n deleted_at?: string;\n created_at?: string;\n updated_at?: string;\n options?: PollOptionRecord[];\n}\n\nexport interface PollUpdate {\n question?: string;\n is_active?: boolean;\n live_at?: string;\n closes_at?: string | null;\n}\n\nexport interface PollVote {\n option_id: string;\n}\n\nexport interface PresetRule {\n kind: string;\n name: string;\n from: string;\n base: string;\n namespace: string;\n root?: string;\n to: string;\n widths: number[];\n aspect?: string;\n min_width?: number;\n first?: number;\n default: boolean;\n stable?: boolean;\n}\n\nexport interface PresignBody {\n ref: RefBody;\n path: string;\n type: string;\n size: number;\n sha256: string;\n}\n\nexport interface PresignReply {\n path: string;\n blob: string;\n exists?: boolean;\n put?: RequestReply;\n multipart?: MultipartReply;\n process_on_upload?: boolean;\n}\n\nexport interface Preview {\n pages?: number;\n segments?: number;\n}\n\nexport interface PublicImage {\n from: string;\n preset: string;\n renditions: PublicRendition[];\n}\n\nexport interface PublicRendition {\n url: string;\n w: number;\n h: number;\n}\n\nexport interface PublicUser {\n id: string;\n name?: string;\n username: string;\n avatar?: string;\n avatar_srcset?: string;\n}\n\nexport interface Publication {\n notifications: Input[];\n}\n\nexport interface PublishInput {\n publish_at?: string;\n}\n\nexport interface Published {\n items: Item[];\n}\n\nexport interface RankedContent {\n id: string;\n title: string;\n path?: string;\n value: number;\n}\n\nexport interface ReactionCounts {\n likes?: number;\n dislikes?: number;\n mine: number;\n}\n\nexport interface ReactionRecord {\n target: TargetRef;\n user_id?: string;\n ip?: string;\n value: number;\n created_at?: string;\n updated_at?: string;\n}\n\nexport interface ReadResult {\n access: Access;\n expires: number;\n meta?: Record<string, unknown>;\n previews?: string[];\n public?: PublicImage[];\n locked?: boolean;\n total: number;\n offset: number;\n limit: number;\n hls?: string[];\n state?: ItemState;\n full?: boolean;\n uploads?: UploadRule[];\n files: FileInfo[];\n}\n\nexport interface ReaderDefaults {\n auto_advance_seconds?: number;\n}\n\nexport interface ReaderMeta {\n schema_version: number;\n defaults?: ReaderDefaults;\n anchors?: Anchor[];\n background?: BackgroundCue[];\n foreground?: ForegroundCue[];\n}\n\nexport interface ReaderPlan {\n content_id: string;\n version_id: string;\n publication_id: string;\n pages?: number;\n segments: PlanSegment[];\n previews: PlanPreview[];\n playback?: PlanPlayback;\n reader?: ReaderMeta;\n}\n\nexport interface RecentSearch {\n query: string;\n results: number;\n searched_at: string;\n}\n\nexport interface RecentSearches {\n searches: RecentSearch[];\n}\n\nexport interface Recipient {\n audience: string;\n id: string;\n}\n\nexport interface RecordFailure {\n section: string;\n index: number;\n external_id?: string;\n code: string;\n error: string;\n}\n\nexport interface Ref {\n id?: string;\n external_id?: string;\n}\n\nexport interface RefBody {\n kind: string;\n id: string;\n version?: string;\n}\n\nexport interface ReleaseDates {\n dates: DateCount[];\n next_cursor?: string;\n}\n\nexport interface Rendition {\n url: string;\n w: number;\n h: number;\n}\n\nexport interface RequestReply {\n method: string;\n url: string;\n headers: Record<string, string>;\n expires: string;\n}\n\nexport interface ResolveInput {\n decision: \"approve\" | \"reject\" | \"review\";\n revision: number;\n reason?: string;\n}\n\nexport interface Resolved {\n tenant_id: string;\n content_kind: string;\n content_id: string;\n content_version_id?: string;\n code: string;\n slug: string;\n slugs?: Record<string, string>;\n path: string;\n version_id?: string;\n}\n\nexport interface Restored {\n restored: boolean;\n}\n\nexport interface Result {\n written: Record<string, number>;\n skipped?: Record<string, number>;\n failed: RecordFailure[];\n ids: Record<string, Record<string, string>>;\n}\n\nexport interface ReviewOutcome {\n decision: \"approve\" | \"reject\" | \"review\";\n}\n\nexport interface Rule {\n level: \"group\" | \"group_ppv\" | \"ppv\" | \"public\" | \"tier\";\n tiers?: string[];\n group_id?: string;\n preview?: Preview;\n free_after?: string;\n}\n\nexport interface Sale {\n key: string;\n target: Target;\n terms: Terms;\n price_id?: string;\n}\n\nexport interface Segment {\n position: number;\n title?: string;\n start: number;\n end: number;\n release_at?: string;\n released: boolean;\n}\n\nexport interface SegmentsInput {\n segments: Segment[];\n}\n\nexport interface SignalInput {\n content_id: string;\n version_id?: string;\n type: string;\n event_id: string;\n revision?: number;\n occurred_at: string;\n duration_s?: number;\n progress?: number;\n resume?: string;\n render_id?: string;\n surface?: string;\n position?: number;\n}\n\nexport interface SignalRecord {\n target: TargetRef;\n user_id?: string;\n anon_key?: string;\n type: string;\n event_id: string;\n revision?: number;\n occurred_at: string;\n duration_s?: number;\n progress?: number;\n resume?: string;\n}\n\nexport interface Signals {\n session?: string;\n signals: SignalInput[];\n}\n\nexport interface SitemapAlias {\n language: string;\n path: string;\n image?: string;\n}\n\nexport interface SitemapEntry {\n id: string;\n code: string;\n path: string;\n lastmod: string;\n languages: SitemapAlias[];\n}\n\nexport interface SitemapPage {\n entries: SitemapEntry[];\n next_cursor?: string;\n}\n\nexport interface StagedCopy {\n path: string;\n blob: string;\n size: number;\n}\n\nexport interface Target {\n kind: \"content\" | \"group\" | \"version\";\n id: string;\n content_id?: string;\n}\n\nexport interface TargetRef {\n content?: Ref;\n version?: Ref;\n group?: Ref;\n comment?: Ref;\n}\n\nexport interface Terms {\n amount: string;\n currency: string;\n access_hours?: number;\n renew_hours?: number;\n}\n\nexport interface TicketBody {\n ticket: string;\n}\n\nexport interface TopContents {\n contents: RankedContent[];\n}\n\nexport interface Total {\n public: number;\n tiers: Record<string, number>;\n languages: Record<string, LanguageTotal>;\n}\n\nexport interface Totals {\n kinds: Record<string, Total>;\n}\n\nexport interface Typeahead {\n contents: Card[];\n groups: GroupCard[];\n}\n\nexport interface UploadLimits {\n files_per_hour?: number;\n bytes_per_day?: number;\n quota_bytes?: number;\n}\n\nexport interface UploadRule {\n path: string;\n types: string[];\n max_bytes: number;\n max?: number;\n named?: boolean;\n body?: boolean;\n frames?: string;\n aspect?: string;\n min_width?: number;\n video?: VideoLimits;\n min_aspect?: number;\n max_aspect?: number;\n}\n\nexport interface Version {\n id: string;\n code: string;\n position: number;\n languages: string[];\n traits: string[];\n label?: string;\n title?: string;\n description?: string;\n state?: ContentState;\n publish_at?: string;\n published_at?: string;\n live: boolean;\n access?: Rule;\n attributes: Record<string, unknown>;\n pages?: number;\n duration_ms?: number;\n width?: number;\n height?: number;\n has_audio: boolean;\n ordinal?: number;\n cover?: CatalogCover;\n segments: Segment[];\n decision: Decision;\n text?: Record<string, VersionText>;\n offer?: Offer;\n viewer?: ViewerState;\n}\n\nexport interface VersionFork {\n from: string;\n playback?: boolean;\n segments?: boolean;\n}\n\nexport interface VersionInput {\n languages?: string[];\n traits?: string[];\n label?: string;\n state?: VersionState;\n publish_at?: string;\n access?: Rule;\n clear_access?: boolean;\n attributes?: Record<string, unknown>;\n text?: Record<string, VersionText>;\n segments?: Segment[];\n ordinal?: number;\n cover?: string;\n fork?: VersionFork;\n}\n\nexport interface VersionRecord {\n external_id: string;\n id?: string;\n code?: string;\n languages: string[];\n traits?: string[];\n label?: string;\n state?: string;\n publish_at?: string;\n published_at?: string;\n deleted_at?: string;\n access?: Rule;\n attributes?: Record<string, unknown>;\n ordinal?: number;\n cover?: string;\n text?: Record<string, VersionText>;\n segments?: Segment[];\n created_at?: string;\n updated_at?: string;\n}\n\nexport interface VersionRules {\n languages: string;\n traits?: string[];\n hidden?: boolean;\n}\n\nexport interface VersionText {\n title?: string;\n description?: string;\n}\n\nexport interface VideoLimits {\n max_seconds?: number;\n max_fps?: number;\n max_pixels?: number;\n max_work?: number;\n}\n\nexport interface ViewerState {\n favorited: boolean;\n seen: boolean;\n completed: boolean;\n progress: number;\n progress_max: number;\n resume?: string;\n last_view_at?: string;\n}\n\nexport interface VoteRecord {\n poll: Ref;\n option: Ref;\n user_id?: string;\n ip?: string;\n created_at?: string;\n}\n\nexport interface Written {\n count: number;\n}\n"],"mappings":";AAOA,IAAa,IAAiB,UACjB,IAAgB,SAChB,IAAgB"}
@@ -133,7 +133,7 @@ function x(e, i, a) {
133
133
  preset: e.name,
134
134
  aspect: e.aspect,
135
135
  renditions: e.widths.map((r) => ({
136
- url: n(e.base, e.namespace, e.kind, i, t(e.to, { w: r }), a),
136
+ url: n(e.base, e.namespace, e.kind, i, t(e.to, { w: r }), a, e.root),
137
137
  w: r,
138
138
  ...o ? { h: Math.round(r / o) } : {}
139
139
  }))
@@ -1 +1 @@
1
- {"version":3,"file":"rules.js","names":[],"sources":["../../../src/client/media/rules.ts"],"sourcesContent":["import { ContentKitError } from \"../errors.js\";\nimport type { ErrorDetails, PresetRule, ReadResult, UploadRule } from \"../generated/wire.js\";\nimport { fill, publicURL, type PublicPreset } from \"../public.js\";\nimport { ratio } from \"../aspect.js\";\nimport { stem } from \"./client.js\";\n\nconst NO_RULES: UploadRule[] = [];\n\n/** An editor read's upload rules (a viewer read has none): one empty list, so memos keyed on them hold. */\nexport const uploadRules = (read: ReadResult | null | undefined): UploadRule[] => read?.uploads ?? NO_RULES;\n\nconst NAME = \"{name}\";\n\n/** A pattern path (\"originals/{name}\") holds many files; a literal (\"cover\") one. */\nexport const isPattern = (path: string) => path.endsWith(NAME);\n\n/** The directory of a pattern (\"originals/\"), or the literal itself. */\nexport const ruleDir = (path: string) => (isPattern(path) ? path.slice(0, -NAME.length) : path);\n\n/** The rule an upload path (with or without its extension) falls under: a literal wins over a pattern. */\nexport function ruleFor<R extends { path: string }>(rules: readonly R[], path: string): R | undefined {\n const s = stem(path);\n return (\n rules.find((r) => !isPattern(r.path) && r.path === s) ??\n rules.find((r) => {\n if (!isPattern(r.path)) return false;\n const dir = ruleDir(r.path);\n return s.startsWith(dir) && s.length > dir.length && !s.slice(dir.length).includes(\"/\");\n })\n );\n}\n\n/** Uploads of the read under rule (attached, in manifest order). */\nexport function filesFor(read: ReadResult | null | undefined, rule: { path: string }, rules: readonly { path: string }[] = [rule]) {\n return (read?.files ?? []).filter((f) => f.upload && !f.unattached && ruleFor(rules, f.path)?.path === rule.path);\n}\n\nconst BY_EXTENSION: Record<string, string> = {\n jpg: \"image/jpeg\",\n jpeg: \"image/jpeg\",\n png: \"image/png\",\n webp: \"image/webp\",\n gif: \"image/gif\",\n avif: \"image/avif\",\n mp4: \"video/mp4\",\n m4v: \"video/mp4\",\n mov: \"video/quicktime\",\n mkv: \"video/x-matroska\",\n webm: \"video/webm\",\n m4a: \"audio/mp4\",\n mp3: \"audio/mpeg\",\n vtt: \"text/vtt\",\n srt: \"application/x-subrip\",\n ass: \"text/x-ass\",\n ssa: \"text/x-ssa\",\n zip: \"application/zip\",\n};\n\n/** The file with a content type from its extension when the browser left it empty or nonstandard (.mkv, .mov, .srt). */\nexport function withMediaType(file: File, allowed?: readonly string[]): File {\n if (file.type && (!allowed || allowed.includes(file.type))) return file;\n const type = BY_EXTENSION[file.name.split(\".\").pop()?.toLowerCase() ?? \"\"];\n return type && type !== file.type ? new File([file], file.name, { type, lastModified: file.lastModified }) : file;\n}\n\n/** `accept` for a file input from the rules' types (plus the extensions browsers leave untyped). */\nexport function acceptOf(rules: readonly UploadRule[]): string | undefined {\n const types = [...new Set(rules.flatMap((r) => r.types))];\n if (!types.length) return undefined;\n const exts = Object.entries(BY_EXTENSION).filter(([, t]) => types.includes(t)).map(([e]) => `.${e}`);\n return [...types, ...exts].join(\",\");\n}\n\nexport interface Screened {\n /** Files to upload, each with its rule (typed by extension when needed). */\n accepted: { file: File; rule: UploadRule }[];\n /** Files the server would refuse, with the refusal it would answer. */\n refused: { file: File; error: ContentKitError }[];\n}\n\n/**\n * Checks files against the rules before any upload: a type some rule takes\n * (the first that does), its size, and the rule's file cap counting\n * have(rule) files already there. A rule without types, size or cap lets\n * that check through; the server decides.\n */\nexport function screenFiles(files: Iterable<File>, rules: readonly UploadRule[], have: (rule: UploadRule) => number = () => 0): Screened {\n const out: Screened = { accepted: [], refused: [] };\n const counts = new Map<UploadRule, number>();\n const allowed = [...new Set(rules.flatMap((r) => r.types))];\n for (const picked of files) {\n const file = withMediaType(picked, allowed);\n const rule = rules.find((r) => !r.types.length || r.types.includes(file.type));\n if (!rule) {\n const details: ErrorDetails = { type: file.type || undefined, allowed };\n out.refused.push({ file, error: new ContentKitError(\"type_not_allowed\", `${file.name}: type not allowed`, { status: 415, details }) });\n continue;\n }\n if (rule.max_bytes > 0 && file.size > rule.max_bytes) {\n const details: ErrorDetails = { type: file.type, size: file.size, max_bytes: rule.max_bytes };\n out.refused.push({ file, error: new ContentKitError(\"too_large\", `${file.name}: too large`, { status: 413, details }) });\n continue;\n }\n const n = counts.get(rule) ?? have(rule);\n if (rule.max && n >= rule.max) {\n const details: ErrorDetails = { max: rule.max };\n out.refused.push({ file, error: new ContentKitError(\"too_many_files\", `${file.name}: at most ${rule.max} files`, { status: 409, details }) });\n continue;\n }\n counts.set(rule, n + 1);\n out.accepted.push({ file, rule });\n }\n return out;\n}\n\n/** name, or name-2, name-3… so its stem is not among taken (names or paths without the directory). */\nexport function uniqueName(name: string, taken: Iterable<string>): string {\n const stems = new Set([...taken].map(stem));\n if (!stems.has(stem(name))) return name;\n const dot = name.lastIndexOf(\".\");\n const base = dot > 0 ? name.slice(0, dot) : name;\n const ext = dot > 0 ? name.slice(dot) : \"\";\n for (let n = 2; ; n++) {\n const candidate = `${base}-${n}${ext}`;\n if (!stems.has(stem(candidate))) return candidate;\n }\n}\n\n/** \"16:9\" style label of a width/height ratio: 2.4 → \"2.4:1\", 0.4167 → \"1:2.4\". */\nexport function ratioLabel(r: number): string {\n const round = (v: number) => String(Math.round(v * 100) / 100);\n return r >= 1 ? `${round(r)}:1` : `1:${round(1 / r)}`;\n}\n\n/** The kind's public preset for an upload path: the one named name, else the first with an aspect, else the first. */\nexport function presetFor(presets: readonly PresetRule[], kind: string, path: string, name?: string): PresetRule | undefined {\n const at = presets.filter((p) => p.kind === kind && ruleFor([{ path: p.from }], path));\n return (name ? at.find((p) => p.name === name) : undefined) ?? at.find((p) => p.aspect) ?? at[0];\n}\n\n/**\n * The kind's default image for a preset, at every width: what the gateway\n * serves for an item without its own (a URL from the template never names a\n * current file). null when the preset has no default (`rule.default`).\n */\nexport function defaultImage(rule: PresetRule, id: string, version?: string): PublicPreset | null {\n if (!rule.default || rule.first || rule.to.includes(NAME)) return null;\n return logicalImage(rule, id, version);\n}\n\n/**\n * A Stable preset's image of an item (`rule.stable`, e.g. the content\n * model's `avatars`): URLs built from the id that show its current image,\n * else the default. null for any other preset.\n */\nexport function stableImage(rule: PresetRule, id: string): PublicPreset | null {\n return rule.stable ? logicalImage(rule, id) : null;\n}\n\nfunction logicalImage(rule: PresetRule, id: string, version?: string): PublicPreset {\n const r = ratio(rule.aspect);\n return {\n preset: rule.name,\n aspect: rule.aspect,\n renditions: rule.widths.map((w) => ({ url: publicURL(rule.base, rule.namespace, rule.kind, id, fill(rule.to, { w }), version), w, ...(r ? { h: Math.round(w / r) } : {}) })),\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"rules.js","names":[],"sources":["../../../src/client/media/rules.ts"],"sourcesContent":["import { ContentKitError } from \"../errors.js\";\nimport type { ErrorDetails, PresetRule, ReadResult, UploadRule } from \"../generated/wire.js\";\nimport { fill, publicURL, type PublicPreset } from \"../public.js\";\nimport { ratio } from \"../aspect.js\";\nimport { stem } from \"./client.js\";\n\nconst NO_RULES: UploadRule[] = [];\n\n/** An editor read's upload rules (a viewer read has none): one empty list, so memos keyed on them hold. */\nexport const uploadRules = (read: ReadResult | null | undefined): UploadRule[] => read?.uploads ?? NO_RULES;\n\nconst NAME = \"{name}\";\n\n/** A pattern path (\"originals/{name}\") holds many files; a literal (\"cover\") one. */\nexport const isPattern = (path: string) => path.endsWith(NAME);\n\n/** The directory of a pattern (\"originals/\"), or the literal itself. */\nexport const ruleDir = (path: string) => (isPattern(path) ? path.slice(0, -NAME.length) : path);\n\n/** The rule an upload path (with or without its extension) falls under: a literal wins over a pattern. */\nexport function ruleFor<R extends { path: string }>(rules: readonly R[], path: string): R | undefined {\n const s = stem(path);\n return (\n rules.find((r) => !isPattern(r.path) && r.path === s) ??\n rules.find((r) => {\n if (!isPattern(r.path)) return false;\n const dir = ruleDir(r.path);\n return s.startsWith(dir) && s.length > dir.length && !s.slice(dir.length).includes(\"/\");\n })\n );\n}\n\n/** Uploads of the read under rule (attached, in manifest order). */\nexport function filesFor(read: ReadResult | null | undefined, rule: { path: string }, rules: readonly { path: string }[] = [rule]) {\n return (read?.files ?? []).filter((f) => f.upload && !f.unattached && ruleFor(rules, f.path)?.path === rule.path);\n}\n\nconst BY_EXTENSION: Record<string, string> = {\n jpg: \"image/jpeg\",\n jpeg: \"image/jpeg\",\n png: \"image/png\",\n webp: \"image/webp\",\n gif: \"image/gif\",\n avif: \"image/avif\",\n mp4: \"video/mp4\",\n m4v: \"video/mp4\",\n mov: \"video/quicktime\",\n mkv: \"video/x-matroska\",\n webm: \"video/webm\",\n m4a: \"audio/mp4\",\n mp3: \"audio/mpeg\",\n vtt: \"text/vtt\",\n srt: \"application/x-subrip\",\n ass: \"text/x-ass\",\n ssa: \"text/x-ssa\",\n zip: \"application/zip\",\n};\n\n/** The file with a content type from its extension when the browser left it empty or nonstandard (.mkv, .mov, .srt). */\nexport function withMediaType(file: File, allowed?: readonly string[]): File {\n if (file.type && (!allowed || allowed.includes(file.type))) return file;\n const type = BY_EXTENSION[file.name.split(\".\").pop()?.toLowerCase() ?? \"\"];\n return type && type !== file.type ? new File([file], file.name, { type, lastModified: file.lastModified }) : file;\n}\n\n/** `accept` for a file input from the rules' types (plus the extensions browsers leave untyped). */\nexport function acceptOf(rules: readonly UploadRule[]): string | undefined {\n const types = [...new Set(rules.flatMap((r) => r.types))];\n if (!types.length) return undefined;\n const exts = Object.entries(BY_EXTENSION).filter(([, t]) => types.includes(t)).map(([e]) => `.${e}`);\n return [...types, ...exts].join(\",\");\n}\n\nexport interface Screened {\n /** Files to upload, each with its rule (typed by extension when needed). */\n accepted: { file: File; rule: UploadRule }[];\n /** Files the server would refuse, with the refusal it would answer. */\n refused: { file: File; error: ContentKitError }[];\n}\n\n/**\n * Checks files against the rules before any upload: a type some rule takes\n * (the first that does), its size, and the rule's file cap counting\n * have(rule) files already there. A rule without types, size or cap lets\n * that check through; the server decides.\n */\nexport function screenFiles(files: Iterable<File>, rules: readonly UploadRule[], have: (rule: UploadRule) => number = () => 0): Screened {\n const out: Screened = { accepted: [], refused: [] };\n const counts = new Map<UploadRule, number>();\n const allowed = [...new Set(rules.flatMap((r) => r.types))];\n for (const picked of files) {\n const file = withMediaType(picked, allowed);\n const rule = rules.find((r) => !r.types.length || r.types.includes(file.type));\n if (!rule) {\n const details: ErrorDetails = { type: file.type || undefined, allowed };\n out.refused.push({ file, error: new ContentKitError(\"type_not_allowed\", `${file.name}: type not allowed`, { status: 415, details }) });\n continue;\n }\n if (rule.max_bytes > 0 && file.size > rule.max_bytes) {\n const details: ErrorDetails = { type: file.type, size: file.size, max_bytes: rule.max_bytes };\n out.refused.push({ file, error: new ContentKitError(\"too_large\", `${file.name}: too large`, { status: 413, details }) });\n continue;\n }\n const n = counts.get(rule) ?? have(rule);\n if (rule.max && n >= rule.max) {\n const details: ErrorDetails = { max: rule.max };\n out.refused.push({ file, error: new ContentKitError(\"too_many_files\", `${file.name}: at most ${rule.max} files`, { status: 409, details }) });\n continue;\n }\n counts.set(rule, n + 1);\n out.accepted.push({ file, rule });\n }\n return out;\n}\n\n/** name, or name-2, name-3… so its stem is not among taken (names or paths without the directory). */\nexport function uniqueName(name: string, taken: Iterable<string>): string {\n const stems = new Set([...taken].map(stem));\n if (!stems.has(stem(name))) return name;\n const dot = name.lastIndexOf(\".\");\n const base = dot > 0 ? name.slice(0, dot) : name;\n const ext = dot > 0 ? name.slice(dot) : \"\";\n for (let n = 2; ; n++) {\n const candidate = `${base}-${n}${ext}`;\n if (!stems.has(stem(candidate))) return candidate;\n }\n}\n\n/** \"16:9\" style label of a width/height ratio: 2.4 → \"2.4:1\", 0.4167 → \"1:2.4\". */\nexport function ratioLabel(r: number): string {\n const round = (v: number) => String(Math.round(v * 100) / 100);\n return r >= 1 ? `${round(r)}:1` : `1:${round(1 / r)}`;\n}\n\n/** The kind's public preset for an upload path: the one named name, else the first with an aspect, else the first. */\nexport function presetFor(presets: readonly PresetRule[], kind: string, path: string, name?: string): PresetRule | undefined {\n const at = presets.filter((p) => p.kind === kind && ruleFor([{ path: p.from }], path));\n return (name ? at.find((p) => p.name === name) : undefined) ?? at.find((p) => p.aspect) ?? at[0];\n}\n\n/**\n * The kind's default image for a preset, at every width: what the gateway\n * serves for an item without its own (a URL from the template never names a\n * current file). null when the preset has no default (`rule.default`).\n */\nexport function defaultImage(rule: PresetRule, id: string, version?: string): PublicPreset | null {\n if (!rule.default || rule.first || rule.to.includes(NAME)) return null;\n return logicalImage(rule, id, version);\n}\n\n/**\n * A Stable preset's image of an item (`rule.stable`, e.g. the content\n * model's `avatars`): URLs built from the id that show its current image,\n * else the default. null for any other preset.\n */\nexport function stableImage(rule: PresetRule, id: string): PublicPreset | null {\n return rule.stable ? logicalImage(rule, id) : null;\n}\n\nfunction logicalImage(rule: PresetRule, id: string, version?: string): PublicPreset {\n const r = ratio(rule.aspect);\n return {\n preset: rule.name,\n aspect: rule.aspect,\n renditions: rule.widths.map((w) => ({ url: publicURL(rule.base, rule.namespace, rule.kind, id, fill(rule.to, { w }), version, rule.root), w, ...(r ? { h: Math.round(w / r) } : {}) })),\n };\n}\n"],"mappings":"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"}
@@ -2,9 +2,10 @@ import { AspectRatio } from './aspect.js';
2
2
  import { Rendition } from './rendition.js';
3
3
  /**
4
4
  * A public file's URL, as media.PublicURL: `{base}/v1/{namespace}/{kind}/{id}[/{version}]/public/{name}`.
5
+ * A server-provided root selects a scoped tenant/app path; otherwise namespace is used.
5
6
  * Pass an actual published name, not a preset template for a current image.
6
7
  */
7
- export declare function publicURL(base: string, namespace: string, kind: string, id: string, name: string, version?: string): string;
8
+ export declare function publicURL(base: string, namespace: string, kind: string, id: string, name: string, version?: string, root?: string): string;
8
9
  /** Each `{key}` of template replaced by vars[key] ("" when missing), as the registry's templates fill. */
9
10
  export declare function fill(template: string, vars: Record<string, string | number>): string;
10
11
  /** A srcset from published URLs and their actual encoded widths. */
@@ -1,7 +1,7 @@
1
1
  import { sortRenditions as e } from "./rendition.js";
2
2
  //#region src/client/public.ts
3
- function t(e, t, n, r, i, a) {
4
- return `${e.replace(/\/+$/, "")}/v1/${t}/${n}/${r}${a ? `/${a}` : ""}/public/${i}`;
3
+ function t(e, t, n, r, i, a, o) {
4
+ return `${e.replace(/\/+$/, "")}/v1/${o ?? `${t}/`}${n}/${r}${a ? `/${a}` : ""}/public/${i}`;
5
5
  }
6
6
  function n(e, t) {
7
7
  return e.replace(/\{([^}]*)\}/g, (e, n) => n in t ? String(t[n]) : "");
@@ -1 +1 @@
1
- {"version":3,"file":"public.js","names":[],"sources":["../../src/client/public.ts"],"sourcesContent":["import type { AspectRatio } from \"./aspect.js\";\nimport { sortRenditions, type Rendition } from \"./rendition.js\";\n\n/**\n * A public file's URL, as media.PublicURL: `{base}/v1/{namespace}/{kind}/{id}[/{version}]/public/{name}`.\n * Pass an actual published name, not a preset template for a current image.\n */\nexport function publicURL(base: string, namespace: string, kind: string, id: string, name: string, version?: string): string {\n return `${base.replace(/\\/+$/, \"\")}/v1/${namespace}/${kind}/${id}${version ? `/${version}` : \"\"}/public/${name}`;\n}\n\n/** Each `{key}` of template replaced by vars[key] (\"\" when missing), as the registry's templates fill. */\nexport function fill(template: string, vars: Record<string, string | number>): string {\n return template.replace(/\\{([^}]*)\\}/g, (_, k: string) => (k in vars ? String(vars[k]) : \"\"));\n}\n\n/** A srcset from published URLs and their actual encoded widths. */\nexport function srcSet(renditions: readonly Rendition[]): string {\n return sortRenditions(renditions)\n .filter((r, i, all) => i === 0 || r.w !== all[i - 1]!.w)\n .map((r) => `${r.url} ${r.w}w`).join(\", \");\n}\n\n/**\n * A public preset at an item: what a slot or poster shows, with its current\n * renditions (from a read's `public`, or a host's authorized listing).\n */\nexport interface PublicPreset {\n preset: string;\n renditions: readonly Rendition[];\n /** The preset's \"W:H\"; \"\" or omitted is the source's own. */\n aspect?: AspectRatio;\n}\n\n/** Published files, narrowest first; sizes and filenames are never inferred. */\nexport function publicRenditions(p: PublicPreset | { renditions: readonly Rendition[] } | null | undefined): Rendition[] {\n if (!p) return [];\n return sortRenditions(p.renditions);\n}\n"],"mappings":";;AAOA,SAAgB,EAAU,GAAc,GAAmB,GAAc,GAAY,GAAc,GAA0B;CAC3H,OAAO,GAAG,EAAK,QAAQ,QAAQ,EAAE,EAAE,MAAM,EAAU,GAAG,EAAK,GAAG,IAAK,IAAU,IAAI,MAAY,GAAG,UAAU;AAC5G;AAGA,SAAgB,EAAK,GAAkB,GAA+C;CACpF,OAAO,EAAS,QAAQ,iBAAiB,GAAG,MAAe,KAAK,IAAO,OAAO,EAAK,EAAE,IAAI,EAAG;AAC9F;AAGA,SAAgB,EAAO,GAA0C;CAC/D,OAAO,EAAe,CAAU,CAAC,CAC9B,QAAQ,GAAG,GAAG,MAAQ,MAAM,KAAK,EAAE,MAAM,EAAI,IAAI,EAAE,CAAE,CAAC,CAAC,CACvD,KAAK,MAAM,GAAG,EAAE,IAAI,GAAG,EAAE,EAAE,EAAE,CAAC,CAAC,KAAK,IAAI;AAC7C;AAcA,SAAgB,EAAiB,GAAwF;CAEvH,OADK,IACE,EAAe,EAAE,UAAU,IADnB,CAAC;AAElB"}
1
+ {"version":3,"file":"public.js","names":[],"sources":["../../src/client/public.ts"],"sourcesContent":["import type { AspectRatio } from \"./aspect.js\";\nimport { sortRenditions, type Rendition } from \"./rendition.js\";\n\n/**\n * A public file's URL, as media.PublicURL: `{base}/v1/{namespace}/{kind}/{id}[/{version}]/public/{name}`.\n * A server-provided root selects a scoped tenant/app path; otherwise namespace is used.\n * Pass an actual published name, not a preset template for a current image.\n */\nexport function publicURL(base: string, namespace: string, kind: string, id: string, name: string, version?: string, root?: string): string {\n return `${base.replace(/\\/+$/, \"\")}/v1/${root ?? `${namespace}/`}${kind}/${id}${version ? `/${version}` : \"\"}/public/${name}`;\n}\n\n/** Each `{key}` of template replaced by vars[key] (\"\" when missing), as the registry's templates fill. */\nexport function fill(template: string, vars: Record<string, string | number>): string {\n return template.replace(/\\{([^}]*)\\}/g, (_, k: string) => (k in vars ? String(vars[k]) : \"\"));\n}\n\n/** A srcset from published URLs and their actual encoded widths. */\nexport function srcSet(renditions: readonly Rendition[]): string {\n return sortRenditions(renditions)\n .filter((r, i, all) => i === 0 || r.w !== all[i - 1]!.w)\n .map((r) => `${r.url} ${r.w}w`).join(\", \");\n}\n\n/**\n * A public preset at an item: what a slot or poster shows, with its current\n * renditions (from a read's `public`, or a host's authorized listing).\n */\nexport interface PublicPreset {\n preset: string;\n renditions: readonly Rendition[];\n /** The preset's \"W:H\"; \"\" or omitted is the source's own. */\n aspect?: AspectRatio;\n}\n\n/** Published files, narrowest first; sizes and filenames are never inferred. */\nexport function publicRenditions(p: PublicPreset | { renditions: readonly Rendition[] } | null | undefined): Rendition[] {\n if (!p) return [];\n return sortRenditions(p.renditions);\n}\n"],"mappings":";;AAQA,SAAgB,EAAU,GAAc,GAAmB,GAAc,GAAY,GAAc,GAAkB,GAAuB;CAC1I,OAAO,GAAG,EAAK,QAAQ,QAAQ,EAAE,EAAE,MAAM,KAAQ,GAAG,EAAU,KAAK,EAAK,GAAG,IAAK,IAAU,IAAI,MAAY,GAAG,UAAU;AACzH;AAGA,SAAgB,EAAK,GAAkB,GAA+C;CACpF,OAAO,EAAS,QAAQ,iBAAiB,GAAG,MAAe,KAAK,IAAO,OAAO,EAAK,EAAE,IAAI,EAAG;AAC9F;AAGA,SAAgB,EAAO,GAA0C;CAC/D,OAAO,EAAe,CAAU,CAAC,CAC9B,QAAQ,GAAG,GAAG,MAAQ,MAAM,KAAK,EAAE,MAAM,EAAI,IAAI,EAAE,CAAE,CAAC,CAAC,CACvD,KAAK,MAAM,GAAG,EAAE,IAAI,GAAG,EAAE,EAAE,EAAE,CAAC,CAAC,KAAK,IAAI;AAC7C;AAcA,SAAgB,EAAiB,GAAwF;CAEvH,OADK,IACE,EAAe,EAAE,UAAU,IADnB,CAAC;AAElB"}
@@ -27,6 +27,6 @@ export declare class StatsClient {
27
27
  activity(q: ActivityQuery, signal?: AbortSignal): Promise<Activity>;
28
28
  /** A kind's live, listed content ranked by views or published comments over a period. */
29
29
  top(q: TopQuery, signal?: AbortSignal): Promise<TopContents>;
30
- /** The signed-in reader's statistics: contents finished per kind, comments and the reactions they received. */
30
+ /** The signed-in reader's statistics: contents finished per kind, comments, reactions received and elapsed access per tier when billing supplies its history. */
31
31
  mine(signal?: AbortSignal): Promise<MyStats>;
32
32
  }
@@ -1 +1 @@
1
- {"version":3,"file":"stats.js","names":[],"sources":["../../src/client/stats.ts"],"sourcesContent":["import type { Activity, MyStats, TopContents } from \"./generated/wire.js\";\nimport type { Period } from \"./discovery.js\";\nimport type { Http } from \"./http.js\";\nimport { call } from \"./route.js\";\n\nexport interface ActivityQuery {\n kind: string;\n /** The last days, today included: 7 by default, at most 90. */\n days?: number;\n}\n\nexport interface TopQuery {\n kind: string;\n /** views (the default) or comments. */\n metric?: \"views\" | \"comments\";\n /** 30d by default. */\n period?: Period;\n access?: readonly (\"public\" | \"tier\" | \"group\" | \"ppv\" | \"group_ppv\")[];\n tiers?: readonly string[];\n /** Titles and paths in this language; the site's first by default. */\n language?: string;\n /** 10 by default, at most 100. */\n limit?: number;\n}\n\n/** Content statistics: staff activity and rankings (root:content:view-restricted), and the signed-in reader's own. */\nexport class StatsClient {\n constructor(private readonly http: Http) {}\n\n /** A kind's views per UTC day, newest first: view sessions, distinct viewers and signed-in viewers. */\n activity(q: ActivityQuery, signal?: AbortSignal): Promise<Activity> {\n return call(this.http, \"GET\", \"/stats/activity\", { query: { kind: q.kind, days: q.days }, signal });\n }\n /** A kind's live, listed content ranked by views or published comments over a period. */\n top(q: TopQuery, signal?: AbortSignal): Promise<TopContents> {\n const { access, tiers, ...rest } = q;\n return call(this.http, \"GET\", \"/stats/top\", { query: { ...rest, access: access?.join(\",\"), tiers: tiers?.join(\",\") }, signal });\n }\n /** The signed-in reader's statistics: contents finished per kind, comments and the reactions they received. */\n mine(signal?: AbortSignal): Promise<MyStats> {\n return call(this.http, \"GET\", \"/me/stats\", { signal });\n }\n}\n"],"mappings":";;AA0BA,IAAa,IAAb,MAAyB;CACM;CAA7B,YAAY,GAA6B;EAAZ,KAAA,OAAA;CAAa;CAG1C,SAAS,GAAkB,GAAyC;EAClE,OAAO,EAAK,KAAK,MAAM,OAAO,mBAAmB;GAAE,OAAO;IAAE,MAAM,EAAE;IAAM,MAAM,EAAE;GAAK;GAAG;EAAO,CAAC;CACpG;CAEA,IAAI,GAAa,GAA4C;EAC3D,IAAM,EAAE,WAAQ,UAAO,GAAG,MAAS;EACnC,OAAO,EAAK,KAAK,MAAM,OAAO,cAAc;GAAE,OAAO;IAAE,GAAG;IAAM,QAAQ,GAAQ,KAAK,GAAG;IAAG,OAAO,GAAO,KAAK,GAAG;GAAE;GAAG;EAAO,CAAC;CAChI;CAEA,KAAK,GAAwC;EAC3C,OAAO,EAAK,KAAK,MAAM,OAAO,aAAa,EAAE,UAAO,CAAC;CACvD;AACF"}
1
+ {"version":3,"file":"stats.js","names":[],"sources":["../../src/client/stats.ts"],"sourcesContent":["import type { Activity, MyStats, TopContents } from \"./generated/wire.js\";\nimport type { Period } from \"./discovery.js\";\nimport type { Http } from \"./http.js\";\nimport { call } from \"./route.js\";\n\nexport interface ActivityQuery {\n kind: string;\n /** The last days, today included: 7 by default, at most 90. */\n days?: number;\n}\n\nexport interface TopQuery {\n kind: string;\n /** views (the default) or comments. */\n metric?: \"views\" | \"comments\";\n /** 30d by default. */\n period?: Period;\n access?: readonly (\"public\" | \"tier\" | \"group\" | \"ppv\" | \"group_ppv\")[];\n tiers?: readonly string[];\n /** Titles and paths in this language; the site's first by default. */\n language?: string;\n /** 10 by default, at most 100. */\n limit?: number;\n}\n\n/** Content statistics: staff activity and rankings (root:content:view-restricted), and the signed-in reader's own. */\nexport class StatsClient {\n constructor(private readonly http: Http) {}\n\n /** A kind's views per UTC day, newest first: view sessions, distinct viewers and signed-in viewers. */\n activity(q: ActivityQuery, signal?: AbortSignal): Promise<Activity> {\n return call(this.http, \"GET\", \"/stats/activity\", { query: { kind: q.kind, days: q.days }, signal });\n }\n /** A kind's live, listed content ranked by views or published comments over a period. */\n top(q: TopQuery, signal?: AbortSignal): Promise<TopContents> {\n const { access, tiers, ...rest } = q;\n return call(this.http, \"GET\", \"/stats/top\", { query: { ...rest, access: access?.join(\",\"), tiers: tiers?.join(\",\") }, signal });\n }\n /** The signed-in reader's statistics: contents finished per kind, comments, reactions received and elapsed access per tier when billing supplies its history. */\n mine(signal?: AbortSignal): Promise<MyStats> {\n return call(this.http, \"GET\", \"/me/stats\", { signal });\n }\n}\n"],"mappings":";;AA0BA,IAAa,IAAb,MAAyB;CACM;CAA7B,YAAY,GAA6B;EAAZ,KAAA,OAAA;CAAa;CAG1C,SAAS,GAAkB,GAAyC;EAClE,OAAO,EAAK,KAAK,MAAM,OAAO,mBAAmB;GAAE,OAAO;IAAE,MAAM,EAAE;IAAM,MAAM,EAAE;GAAK;GAAG;EAAO,CAAC;CACpG;CAEA,IAAI,GAAa,GAA4C;EAC3D,IAAM,EAAE,WAAQ,UAAO,GAAG,MAAS;EACnC,OAAO,EAAK,KAAK,MAAM,OAAO,cAAc;GAAE,OAAO;IAAE,GAAG;IAAM,QAAQ,GAAQ,KAAK,GAAG;IAAG,OAAO,GAAO,KAAK,GAAG;GAAE;GAAG;EAAO,CAAC;CAChI;CAEA,KAAK,GAAwC;EAC3C,OAAO,EAAK,KAAK,MAAM,OAAO,aAAa,EAAE,UAAO,CAAC;CACvD;AACF"}
@@ -0,0 +1,11 @@
1
+ import { Item } from '../client/generated/wire.js';
2
+ export type BillingNotificationAction = "update_payment_method" | "resubscribe" | "authenticate_payment" | "contact_support" | "manage_billing";
3
+ export type BillingNotificationActions = Partial<Record<BillingNotificationAction, string>>;
4
+ export declare function isBillingNotification(item: Item): boolean;
5
+ /** The immutable event's guidance; it is not a claim that a debt remains open. */
6
+ export declare function billingNotificationAction(item: Item): BillingNotificationAction;
7
+ /** Shared billing wording. Hosts supply only their authorized destination URLs. */
8
+ export declare function BillingNotification({ item, actions }: {
9
+ item: Item;
10
+ actions?: BillingNotificationActions;
11
+ }): import('react').JSX.Element | null;
@@ -0,0 +1,70 @@
1
+ import { useMessages as e } from "../i18n/context.js";
2
+ import { jsx as t, jsxs as n } from "react/jsx-runtime";
3
+ //#region src/components/billing-notification.tsx
4
+ function r(e) {
5
+ return e.data && typeof e.data == "object" && !Array.isArray(e.data) ? e.data : {};
6
+ }
7
+ function i(e) {
8
+ return [
9
+ "payment_method_failed",
10
+ "payment_method_update_required",
11
+ "payment_authentication_required",
12
+ "order_collection_stopped"
13
+ ].includes(e);
14
+ }
15
+ function a(e) {
16
+ return i(e.kind) || e.kind === "premium_ended" || e.kind === "premium_renewed" && r(e).downgrade_applied === !0;
17
+ }
18
+ function o(e) {
19
+ let t = r(e).recovery_action ?? (e.kind === "payment_method_update_required" ? "update_payment_method" : e.kind === "payment_authentication_required" ? "authenticate_payment" : void 0);
20
+ return t === "update_payment_method" || t === "resubscribe" || t === "authenticate_payment" || t === "contact_support" ? t : "manage_billing";
21
+ }
22
+ function s({ item: s, actions: l = {} }) {
23
+ let u = e(), { t: d } = u;
24
+ if (!a(s)) return null;
25
+ let f = r(s), p = s.kind === "payment_method_update_required" ? d("billingNotifications.paymentMethodRequiredTitle") : s.kind === "payment_authentication_required" ? d("billingNotifications.authenticationRequiredTitle") : s.kind === "order_collection_stopped" ? d("billingNotifications.collectionStoppedTitle") : i(s.kind) ? d("billingNotifications.paymentFailed") : s.kind === "premium_ended" ? d("billingNotifications.membershipEnded") : d("billingNotifications.membershipDowngraded"), m;
26
+ if (i(s.kind)) {
27
+ let e = c(f.next_attempt_at, u);
28
+ m = f.retry_status === "scheduled" && e ? d("billingNotifications.retryScheduled", { date: e }) : f.retry_status === "payment_method_required" ? d("billingNotifications.paymentMethodRequired") : f.retry_status === "stopped" ? d("billingNotifications.retryStopped") : d("billingNotifications.retryUnknown");
29
+ } else if (s.kind === "premium_ended") {
30
+ let e = c(f.ended_at, u);
31
+ m = e ? d("billingNotifications.endedAt", { date: e }) : d("billingNotifications.ended");
32
+ } else m = typeof f.from_product_name == "string" && typeof f.to_product_name == "string" && f.from_product_name && f.to_product_name ? d("billingNotifications.downgradedTo", {
33
+ from: f.from_product_name,
34
+ to: f.to_product_name
35
+ }) : d("billingNotifications.downgraded");
36
+ let h = o(s), g = l[h] ?? l.manage_billing, _ = l[h] ? h : "manage_billing";
37
+ return /* @__PURE__ */ n("div", {
38
+ className: "grid gap-2",
39
+ "data-ckui": "billing-notification",
40
+ children: [
41
+ /* @__PURE__ */ t("p", {
42
+ className: "font-medium",
43
+ children: p
44
+ }),
45
+ /* @__PURE__ */ t("p", {
46
+ className: "text-muted-foreground",
47
+ children: m
48
+ }),
49
+ i(s.kind) && /* @__PURE__ */ t("p", {
50
+ className: "text-muted-foreground",
51
+ children: d(`billingNotifications.instructions.${h}`)
52
+ }),
53
+ g && /* @__PURE__ */ t("a", {
54
+ href: g,
55
+ className: "font-medium underline underline-offset-4",
56
+ children: d(`billingNotifications.actions.${_}`)
57
+ })
58
+ ]
59
+ });
60
+ }
61
+ function c(e, t) {
62
+ if (typeof e == "string" && Number.isFinite(Date.parse(e))) return new Intl.DateTimeFormat(t.language, {
63
+ dateStyle: "medium",
64
+ timeStyle: "short"
65
+ }).format(new Date(e));
66
+ }
67
+ //#endregion
68
+ export { s as BillingNotification, o as billingNotificationAction, a as isBillingNotification };
69
+
70
+ //# sourceMappingURL=billing-notification.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"billing-notification.js","names":[],"sources":["../../src/components/billing-notification.tsx"],"sourcesContent":["import type { Item } from \"../client/generated/wire.js\";\nimport { useMessages } from \"../i18n/context.js\";\nimport type { Translator } from \"../i18n/messages.js\";\n\nexport type BillingNotificationAction = \"update_payment_method\" | \"resubscribe\" | \"authenticate_payment\" | \"contact_support\" | \"manage_billing\";\nexport type BillingNotificationActions = Partial<Record<BillingNotificationAction, string>>;\n\nfunction dataOf(item: Item): Record<string, unknown> {\n return item.data && typeof item.data === \"object\" && !Array.isArray(item.data) ? item.data as Record<string, unknown> : {};\n}\n\n/** The selected billing lifecycle outcomes, rather than signup/cancel confirmations. */\nfunction isPaymentFailure(kind: string): boolean {\n return [\"payment_method_failed\", \"payment_method_update_required\", \"payment_authentication_required\", \"order_collection_stopped\"].includes(kind);\n}\n\nexport function isBillingNotification(item: Item): boolean {\n return isPaymentFailure(item.kind) || item.kind === \"premium_ended\" || (item.kind === \"premium_renewed\" && dataOf(item).downgrade_applied === true);\n}\n\n/** The immutable event's guidance; it is not a claim that a debt remains open. */\nexport function billingNotificationAction(item: Item): BillingNotificationAction {\n const action = dataOf(item).recovery_action ?? (item.kind === \"payment_method_update_required\" ? \"update_payment_method\" : item.kind === \"payment_authentication_required\" ? \"authenticate_payment\" : undefined);\n return action === \"update_payment_method\" || action === \"resubscribe\" || action === \"authenticate_payment\" || action === \"contact_support\" ? action : \"manage_billing\";\n}\n\n/** Shared billing wording. Hosts supply only their authorized destination URLs. */\nexport function BillingNotification({ item, actions = {} }: { item: Item; actions?: BillingNotificationActions }) {\n const translator = useMessages();\n const { t } = translator;\n if (!isBillingNotification(item)) return null;\n const data = dataOf(item);\n const title = item.kind === \"payment_method_update_required\" ? t(\"billingNotifications.paymentMethodRequiredTitle\")\n : item.kind === \"payment_authentication_required\" ? t(\"billingNotifications.authenticationRequiredTitle\")\n : item.kind === \"order_collection_stopped\" ? t(\"billingNotifications.collectionStoppedTitle\")\n : isPaymentFailure(item.kind) ? t(\"billingNotifications.paymentFailed\")\n : item.kind === \"premium_ended\" ? t(\"billingNotifications.membershipEnded\") : t(\"billingNotifications.membershipDowngraded\");\n let detail: string;\n if (isPaymentFailure(item.kind)) {\n const attempt = date(data.next_attempt_at, translator);\n detail = data.retry_status === \"scheduled\" && attempt ? t(\"billingNotifications.retryScheduled\", { date: attempt })\n : data.retry_status === \"payment_method_required\" ? t(\"billingNotifications.paymentMethodRequired\")\n : data.retry_status === \"stopped\" ? t(\"billingNotifications.retryStopped\")\n : t(\"billingNotifications.retryUnknown\");\n } else if (item.kind === \"premium_ended\") {\n const ended = date(data.ended_at, translator);\n detail = ended ? t(\"billingNotifications.endedAt\", { date: ended }) : t(\"billingNotifications.ended\");\n } else {\n detail = typeof data.from_product_name === \"string\" && typeof data.to_product_name === \"string\" && data.from_product_name && data.to_product_name\n ? t(\"billingNotifications.downgradedTo\", { from: data.from_product_name, to: data.to_product_name })\n : t(\"billingNotifications.downgraded\");\n }\n const action = billingNotificationAction(item);\n const href = actions[action] ?? actions.manage_billing;\n const label = actions[action] ? action : \"manage_billing\";\n return <div className=\"grid gap-2\" data-ckui=\"billing-notification\">\n <p className=\"font-medium\">{title}</p>\n <p className=\"text-muted-foreground\">{detail}</p>\n {isPaymentFailure(item.kind) && <p className=\"text-muted-foreground\">{t(`billingNotifications.instructions.${action}`)}</p>}\n {href && <a href={href} className=\"font-medium underline underline-offset-4\">{t(`billingNotifications.actions.${label}`)}</a>}\n </div>;\n}\n\nfunction date(value: unknown, translator: Translator): string | undefined {\n if (typeof value !== \"string\" || !Number.isFinite(Date.parse(value))) return undefined;\n return new Intl.DateTimeFormat(translator.language, { dateStyle: \"medium\", timeStyle: \"short\" }).format(new Date(value));\n}\n"],"mappings":";;;AAOA,SAAS,EAAO,GAAqC;CACnD,OAAO,EAAK,QAAQ,OAAO,EAAK,QAAS,YAAY,CAAC,MAAM,QAAQ,EAAK,IAAI,IAAI,EAAK,OAAkC,CAAC;AAC3H;AAGA,SAAS,EAAiB,GAAuB;CAC/C,OAAO;EAAC;EAAyB;EAAkC;EAAmC;CAA0B,CAAC,CAAC,SAAS,CAAI;AACjJ;AAEA,SAAgB,EAAsB,GAAqB;CACzD,OAAO,EAAiB,EAAK,IAAI,KAAK,EAAK,SAAS,mBAAoB,EAAK,SAAS,qBAAqB,EAAO,CAAI,CAAC,CAAC,sBAAsB;AAChJ;AAGA,SAAgB,EAA0B,GAAuC;CAC/E,IAAM,IAAS,EAAO,CAAI,CAAC,CAAC,oBAAoB,EAAK,SAAS,mCAAmC,0BAA0B,EAAK,SAAS,oCAAoC,yBAAyB,KAAA;CACtM,OAAO,MAAW,2BAA2B,MAAW,iBAAiB,MAAW,0BAA0B,MAAW,oBAAoB,IAAS;AACxJ;AAGA,SAAgB,EAAoB,EAAE,SAAM,aAAU,CAAC,KAA2D;CAChH,IAAM,IAAa,EAAY,GACzB,EAAE,SAAM;CACd,IAAI,CAAC,EAAsB,CAAI,GAAG,OAAO;CACzC,IAAM,IAAO,EAAO,CAAI,GAClB,IAAQ,EAAK,SAAS,mCAAmC,EAAE,iDAAiD,IAC9G,EAAK,SAAS,oCAAoC,EAAE,kDAAkD,IACtG,EAAK,SAAS,6BAA6B,EAAE,6CAA6C,IAC1F,EAAiB,EAAK,IAAI,IAAI,EAAE,oCAAoC,IACpE,EAAK,SAAS,kBAAkB,EAAE,sCAAsC,IAAI,EAAE,2CAA2C,GACzH;CACJ,IAAI,EAAiB,EAAK,IAAI,GAAG;EAC/B,IAAM,IAAU,EAAK,EAAK,iBAAiB,CAAU;EACrD,IAAS,EAAK,iBAAiB,eAAe,IAAU,EAAE,uCAAuC,EAAE,MAAM,EAAQ,CAAC,IAC9G,EAAK,iBAAiB,4BAA4B,EAAE,4CAA4C,IAChG,EAAK,iBAAiB,YAAY,EAAE,mCAAmC,IACvE,EAAE,mCAAmC;CAC3C,OAAO,IAAI,EAAK,SAAS,iBAAiB;EACxC,IAAM,IAAQ,EAAK,EAAK,UAAU,CAAU;EAC5C,IAAS,IAAQ,EAAE,gCAAgC,EAAE,MAAM,EAAM,CAAC,IAAI,EAAE,4BAA4B;CACtG,OACE,IAAS,OAAO,EAAK,qBAAsB,YAAY,OAAO,EAAK,mBAAoB,YAAY,EAAK,qBAAqB,EAAK,kBAC9H,EAAE,qCAAqC;EAAE,MAAM,EAAK;EAAmB,IAAI,EAAK;CAAgB,CAAC,IACjG,EAAE,iCAAiC;CAEzC,IAAM,IAAS,EAA0B,CAAI,GACvC,IAAO,EAAQ,MAAW,EAAQ,gBAClC,IAAQ,EAAQ,KAAU,IAAS;CACzC,OAAO,kBAAC,OAAD;EAAK,WAAU;EAAa,aAAU;EAAtC,UAAA;GACL,kBAAC,KAAD;IAAG,WAAU;IAAe,UAAA;GAAS,CAAA;GACrC,kBAAC,KAAD;IAAG,WAAU;IAAyB,UAAA;GAAU,CAAA;GAC/C,EAAiB,EAAK,IAAI,KAAK,kBAAC,KAAD;IAAG,WAAU;IAAyB,UAAA,EAAE,qCAAqC,GAAQ;GAAK,CAAA;GACzH,KAAQ,kBAAC,KAAD;IAAS;IAAM,WAAU;IAA4C,UAAA,EAAE,gCAAgC,GAAO;GAAK,CAAA;EACzH;;AACP;AAEA,SAAS,EAAK,GAAgB,GAA4C;CACpE,WAAO,KAAU,YAAa,OAAO,SAAS,KAAK,MAAM,CAAK,CAAC,GACnE,OAAO,IAAI,KAAK,eAAe,EAAW,UAAU;EAAE,WAAW;EAAU,WAAW;CAAQ,CAAC,CAAC,CAAC,OAAO,IAAI,KAAK,CAAK,CAAC;AACzH"}
@@ -13,7 +13,7 @@ export interface ContentFilterSettingsProps {
13
13
  appearance?: ContentKitUiAppearance;
14
14
  }
15
15
  /** The signed-in reader's content filters: show or hide each configured group, and go back to its default. */
16
- export declare function ContentFilterSettings(p: ContentFilterSettingsProps): import('react').JSX.Element;
16
+ export declare function ContentFilterSettings(p: ContentFilterSettingsProps): import('react').JSX.Element | null;
17
17
  export interface ReleaseDateIndexProps {
18
18
  /** Which contents count: the listing's filters. */
19
19
  query?: ReleaseDatesQuery;
@@ -27,7 +27,7 @@ function v(d) {
27
27
  }));
28
28
  if (!S.filters) return S.error ? w(/* @__PURE__ */ h(o, { children: s(v, S.error) })) : w(/* @__PURE__ */ h(a, { className: "h-24 w-full" }));
29
29
  let T = S.filters;
30
- return w(/* @__PURE__ */ g(m, { children: [
30
+ return !T.filters.length && d.includeDeleted === !1 ? null : w(/* @__PURE__ */ g(m, { children: [
31
31
  T.filters.length ? /* @__PURE__ */ h("ul", {
32
32
  className: "grid divide-y divide-border rounded-lg border border-border",
33
33
  children: T.filters.map((e) => /* @__PURE__ */ g("li", {
@@ -66,10 +66,7 @@ function v(d) {
66
66
  })
67
67
  ]
68
68
  }, e.group.id))
69
- }) : /* @__PURE__ */ h("p", {
70
- className: "text-muted-foreground",
71
- children: y("contents.noFilters")
72
- }),
69
+ }) : null,
73
70
  d.includeDeleted !== !1 && /* @__PURE__ */ g("div", {
74
71
  className: "flex items-center gap-3",
75
72
  children: [/* @__PURE__ */ h(l, {
@@ -1 +1 @@
1
- {"version":3,"file":"filters.js","names":[],"sources":["../../../src/components/content/filters.tsx"],"sourcesContent":["import { cn } from \"cn\";\nimport { useContext, useId } from \"react\";\nimport type { ContentKitUiAppearance } from \"../../appearance.js\";\nimport type { ContentKitClient } from \"../../client/client.js\";\nimport type { ReleaseDatesQuery } from \"../../client/discovery.js\";\nimport { useMessages } from \"../../i18n/context.js\";\nimport { ContentKitContext, type ContentKitErrorHandler } from \"../../react/context.js\";\nimport { useContentFilters, useReleaseDates } from \"../../react/discovery.js\";\nimport { ContentKitUiRoot } from \"../../scope.js\";\nimport { Button } from \"#ckui/ui/button\";\nimport { Label } from \"#ckui/ui/label\";\nimport { Skeleton } from \"#ckui/ui/skeleton\";\nimport { Switch } from \"#ckui/ui/switch\";\nimport { contentError, ErrorLine, useAction } from \"../social/parts.js\";\n\nexport interface ContentFilterSettingsProps {\n /** Labels and group names in this language; default the provider's. */\n language?: string;\n /** Offer listing deleted content the reader holds. Default true. */\n includeDeleted?: boolean;\n client?: ContentKitClient;\n onError?: ContentKitErrorHandler;\n className?: string;\n appearance?: ContentKitUiAppearance;\n}\n\n/** The signed-in reader's content filters: show or hide each configured group, and go back to its default. */\nexport function ContentFilterSettings(p: ContentFilterSettingsProps) {\n const m = useMessages();\n const { t } = m;\n const id = useId();\n const ctx = useContext(ContentKitContext);\n const f = useContentFilters({ language: p.language, client: p.client });\n const save = useAction(\"filters.save\", p.onError);\n const root = (children: React.ReactNode) => (\n <ContentKitUiRoot appearance={p.appearance} className={cn(\"grid gap-3 text-sm\", p.className)} data-ckui=\"content-filters\">\n {children}\n </ContentKitUiRoot>\n );\n if (ctx?.viewer === null) return root(<p className=\"text-muted-foreground\">{t(\"contents.filtersSignedOut\")}</p>);\n if (!f.filters) {\n if (f.error) return root(<ErrorLine>{contentError(m, f.error)}</ErrorLine>);\n return root(<Skeleton className=\"h-24 w-full\" />);\n }\n const filters = f.filters;\n return root(\n <>\n {filters.filters.length ? (\n <ul className=\"grid divide-y divide-border rounded-lg border border-border\">\n {filters.filters.map((x) => (\n <li key={x.group.id} className=\"flex flex-wrap items-center gap-3 p-3\" data-ckui=\"content-filter\" data-slug={x.group.slug} data-visible={x.visible}>\n <Switch id={`${id}-${x.group.id}`} checked={x.visible} disabled={save.pending} onCheckedChange={(v) => void save.run(() => f.setVisible(x.group.id, v))} />\n <Label htmlFor={`${id}-${x.group.id}`} className=\"min-w-0 flex-1\">\n {x.label || x.group.name}\n </Label>\n <span className=\"text-xs text-muted-foreground\">{t(x.visible ? \"contents.shown\" : \"contents.hidden\")}</span>\n {x.overridden && (\n <Button\n size=\"xs\"\n variant=\"ghost\"\n disabled={save.pending}\n onClick={() =>\n void save.run(() =>\n f.save({\n filters: filters.filters.filter((y) => y.overridden && y.group.id !== x.group.id).map((y) => ({ group_id: y.group.id, visible: y.visible })),\n include_deleted: filters.include_deleted,\n }),\n )\n }\n >\n {t(\"contents.useDefault\")}\n </Button>\n )}\n </li>\n ))}\n </ul>\n ) : (\n <p className=\"text-muted-foreground\">{t(\"contents.noFilters\")}</p>\n )}\n {p.includeDeleted !== false && (\n <div className=\"flex items-center gap-3\">\n <Switch id={`${id}-deleted`} checked={filters.include_deleted} disabled={save.pending} onCheckedChange={(v) => void save.run(() => f.setIncludeDeleted(v))} />\n <Label htmlFor={`${id}-deleted`}>{t(\"contents.includeDeleted\")}</Label>\n </div>\n )}\n <ErrorLine error={save.error} />\n </>,\n );\n}\n\nexport interface ReleaseDateIndexProps {\n /** Which contents count: the listing's filters. */\n query?: ReleaseDatesQuery;\n /** The bucket chosen (a day YYYY-MM-DD or month YYYY-MM): list with released_on set to it. */\n selected?: string;\n onSelect?: (date: string) => void;\n client?: ContentKitClient;\n className?: string;\n appearance?: ContentKitUiAppearance;\n}\n\n/** The release date index: per month or day, newest first, how many contents and how many free. */\nexport function ReleaseDateIndex({ query = {}, selected, onSelect, client, className, appearance }: ReleaseDateIndexProps) {\n const m = useMessages();\n const { t } = m;\n const dates = useReleaseDates(query, { client });\n const label = (date: string) => {\n const [y, mo, d] = date.split(\"-\").map(Number);\n const at = new Date(Date.UTC(y!, (mo ?? 1) - 1, d ?? 1));\n return new Intl.DateTimeFormat(m.language, d ? { dateStyle: \"medium\", timeZone: \"UTC\" } : { year: \"numeric\", month: \"long\", timeZone: \"UTC\" }).format(at);\n };\n return (\n <ContentKitUiRoot appearance={appearance} className={cn(\"grid gap-2 text-sm\", className)} data-ckui=\"release-dates\">\n {dates.loading && !dates.items.length ? (\n <Skeleton className=\"h-24 w-full\" />\n ) : dates.error && !dates.items.length ? (\n <ErrorLine>{contentError(m, dates.error)}</ErrorLine>\n ) : !dates.items.length ? (\n <p className=\"text-muted-foreground\">{t(\"contents.noDates\")}</p>\n ) : (\n <ul className=\"grid gap-1\">\n {dates.items.map((d) => (\n <li key={d.date}>\n <button\n type=\"button\"\n aria-pressed={selected === d.date}\n className={cn(\"flex w-full items-center justify-between gap-3 rounded-md px-2 py-1.5 text-start hover:bg-muted\", selected === d.date && \"bg-muted font-medium\")}\n onClick={() => onSelect?.(d.date)}\n data-ckui=\"release-date\"\n data-date={d.date}\n >\n <span>{label(d.date)}</span>\n <span className=\"text-xs text-muted-foreground tabular-nums\">\n {m.plural(\"contents.items\", d.count)}\n {d.free < d.count && ` · ${m.plural(\"contents.free\", d.free)}`}\n </span>\n </button>\n </li>\n ))}\n </ul>\n )}\n {dates.hasMore && (\n <Button variant=\"outline\" size=\"sm\" className=\"justify-self-center\" disabled={dates.loadingMore} onClick={() => void dates.loadMore()}>\n {t(\"contents.loadMore\")}\n </Button>\n )}\n </ContentKitUiRoot>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"filters.js","names":[],"sources":["../../../src/components/content/filters.tsx"],"sourcesContent":["import { cn } from \"cn\";\nimport { useContext, useId } from \"react\";\nimport type { ContentKitUiAppearance } from \"../../appearance.js\";\nimport type { ContentKitClient } from \"../../client/client.js\";\nimport type { ReleaseDatesQuery } from \"../../client/discovery.js\";\nimport { useMessages } from \"../../i18n/context.js\";\nimport { ContentKitContext, type ContentKitErrorHandler } from \"../../react/context.js\";\nimport { useContentFilters, useReleaseDates } from \"../../react/discovery.js\";\nimport { ContentKitUiRoot } from \"../../scope.js\";\nimport { Button } from \"#ckui/ui/button\";\nimport { Label } from \"#ckui/ui/label\";\nimport { Skeleton } from \"#ckui/ui/skeleton\";\nimport { Switch } from \"#ckui/ui/switch\";\nimport { contentError, ErrorLine, useAction } from \"../social/parts.js\";\n\nexport interface ContentFilterSettingsProps {\n /** Labels and group names in this language; default the provider's. */\n language?: string;\n /** Offer listing deleted content the reader holds. Default true. */\n includeDeleted?: boolean;\n client?: ContentKitClient;\n onError?: ContentKitErrorHandler;\n className?: string;\n appearance?: ContentKitUiAppearance;\n}\n\n/** The signed-in reader's content filters: show or hide each configured group, and go back to its default. */\nexport function ContentFilterSettings(p: ContentFilterSettingsProps) {\n const m = useMessages();\n const { t } = m;\n const id = useId();\n const ctx = useContext(ContentKitContext);\n const f = useContentFilters({ language: p.language, client: p.client });\n const save = useAction(\"filters.save\", p.onError);\n const root = (children: React.ReactNode) => (\n <ContentKitUiRoot appearance={p.appearance} className={cn(\"grid gap-3 text-sm\", p.className)} data-ckui=\"content-filters\">\n {children}\n </ContentKitUiRoot>\n );\n if (ctx?.viewer === null) return root(<p className=\"text-muted-foreground\">{t(\"contents.filtersSignedOut\")}</p>);\n if (!f.filters) {\n if (f.error) return root(<ErrorLine>{contentError(m, f.error)}</ErrorLine>);\n return root(<Skeleton className=\"h-24 w-full\" />);\n }\n const filters = f.filters;\n if (!filters.filters.length && p.includeDeleted === false) return null;\n return root(\n <>\n {filters.filters.length ? (\n <ul className=\"grid divide-y divide-border rounded-lg border border-border\">\n {filters.filters.map((x) => (\n <li key={x.group.id} className=\"flex flex-wrap items-center gap-3 p-3\" data-ckui=\"content-filter\" data-slug={x.group.slug} data-visible={x.visible}>\n <Switch id={`${id}-${x.group.id}`} checked={x.visible} disabled={save.pending} onCheckedChange={(v) => void save.run(() => f.setVisible(x.group.id, v))} />\n <Label htmlFor={`${id}-${x.group.id}`} className=\"min-w-0 flex-1\">\n {x.label || x.group.name}\n </Label>\n <span className=\"text-xs text-muted-foreground\">{t(x.visible ? \"contents.shown\" : \"contents.hidden\")}</span>\n {x.overridden && (\n <Button\n size=\"xs\"\n variant=\"ghost\"\n disabled={save.pending}\n onClick={() =>\n void save.run(() =>\n f.save({\n filters: filters.filters.filter((y) => y.overridden && y.group.id !== x.group.id).map((y) => ({ group_id: y.group.id, visible: y.visible })),\n include_deleted: filters.include_deleted,\n }),\n )\n }\n >\n {t(\"contents.useDefault\")}\n </Button>\n )}\n </li>\n ))}\n </ul>\n ) : null}\n {p.includeDeleted !== false && (\n <div className=\"flex items-center gap-3\">\n <Switch id={`${id}-deleted`} checked={filters.include_deleted} disabled={save.pending} onCheckedChange={(v) => void save.run(() => f.setIncludeDeleted(v))} />\n <Label htmlFor={`${id}-deleted`}>{t(\"contents.includeDeleted\")}</Label>\n </div>\n )}\n <ErrorLine error={save.error} />\n </>,\n );\n}\n\nexport interface ReleaseDateIndexProps {\n /** Which contents count: the listing's filters. */\n query?: ReleaseDatesQuery;\n /** The bucket chosen (a day YYYY-MM-DD or month YYYY-MM): list with released_on set to it. */\n selected?: string;\n onSelect?: (date: string) => void;\n client?: ContentKitClient;\n className?: string;\n appearance?: ContentKitUiAppearance;\n}\n\n/** The release date index: per month or day, newest first, how many contents and how many free. */\nexport function ReleaseDateIndex({ query = {}, selected, onSelect, client, className, appearance }: ReleaseDateIndexProps) {\n const m = useMessages();\n const { t } = m;\n const dates = useReleaseDates(query, { client });\n const label = (date: string) => {\n const [y, mo, d] = date.split(\"-\").map(Number);\n const at = new Date(Date.UTC(y!, (mo ?? 1) - 1, d ?? 1));\n return new Intl.DateTimeFormat(m.language, d ? { dateStyle: \"medium\", timeZone: \"UTC\" } : { year: \"numeric\", month: \"long\", timeZone: \"UTC\" }).format(at);\n };\n return (\n <ContentKitUiRoot appearance={appearance} className={cn(\"grid gap-2 text-sm\", className)} data-ckui=\"release-dates\">\n {dates.loading && !dates.items.length ? (\n <Skeleton className=\"h-24 w-full\" />\n ) : dates.error && !dates.items.length ? (\n <ErrorLine>{contentError(m, dates.error)}</ErrorLine>\n ) : !dates.items.length ? (\n <p className=\"text-muted-foreground\">{t(\"contents.noDates\")}</p>\n ) : (\n <ul className=\"grid gap-1\">\n {dates.items.map((d) => (\n <li key={d.date}>\n <button\n type=\"button\"\n aria-pressed={selected === d.date}\n className={cn(\"flex w-full items-center justify-between gap-3 rounded-md px-2 py-1.5 text-start hover:bg-muted\", selected === d.date && \"bg-muted font-medium\")}\n onClick={() => onSelect?.(d.date)}\n data-ckui=\"release-date\"\n data-date={d.date}\n >\n <span>{label(d.date)}</span>\n <span className=\"text-xs text-muted-foreground tabular-nums\">\n {m.plural(\"contents.items\", d.count)}\n {d.free < d.count && ` · ${m.plural(\"contents.free\", d.free)}`}\n </span>\n </button>\n </li>\n ))}\n </ul>\n )}\n {dates.hasMore && (\n <Button variant=\"outline\" size=\"sm\" className=\"justify-self-center\" disabled={dates.loadingMore} onClick={() => void dates.loadMore()}>\n {t(\"contents.loadMore\")}\n </Button>\n )}\n </ContentKitUiRoot>\n );\n}\n"],"mappings":"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"}
@@ -2,11 +2,19 @@ import { ContentKitUiAppearance } from '../../appearance.js';
2
2
  import { ContentKitClient } from '../../client/client.js';
3
3
  import { Labels } from '../groups/parts.js';
4
4
  export interface ReaderStatsProps {
5
- /** Display names of content kinds by identifier. */
5
+ /** Display names of content kinds and access tiers by identifier. */
6
6
  labels?: Labels;
7
+ /** Complete labels for finished-content counts, e.g. {gallery: "Galleries read"}. */
8
+ completedLabels?: Labels;
9
+ /** Content kinds to show, including an unavailable placeholder. Default: returned kinds. */
10
+ kinds?: readonly string[];
11
+ /** Access tiers to show, including an unavailable placeholder. Default: returned tiers. */
12
+ tiers?: readonly string[];
13
+ /** Include received dislikes. Defaults to true. */
14
+ showDislikes?: boolean;
7
15
  client?: ContentKitClient;
8
16
  appearance?: ContentKitUiAppearance;
9
17
  className?: string;
10
18
  }
11
- /** The signed-in reader's activity: contents finished per kind, their comments and the reactions those received. Nothing signed out. */
12
- export declare function ReaderStats({ labels, client, appearance, className }: ReaderStatsProps): import('react').JSX.Element | null;
19
+ /** The signed-in reader's activity and elapsed tier access. Nothing signed out. */
20
+ export declare function ReaderStats({ labels, completedLabels, kinds, tiers, showDislikes, client, appearance, className }: ReaderStatsProps): import('react').JSX.Element | null;