@jamwidgets/astro 0.5.0 → 0.6.1

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.
package/README.md CHANGED
@@ -11,6 +11,18 @@ Astro components and content loader for [Jamwidgets](https://jamwidgets.com) - w
11
11
  npm install @jamwidgets/astro
12
12
  ```
13
13
 
14
+ ## Galleries
15
+
16
+ ```astro
17
+ ---
18
+ import GalleryIndex from "@jamwidgets/astro/GalleryIndex";
19
+ import Gallery from "@jamwidgets/astro/Gallery";
20
+ ---
21
+
22
+ <GalleryIndex siteKey="your-key" deepLink />
23
+ <Gallery siteKey="your-key" slug="field-notes" tag="portraits" />
24
+ ```
25
+
14
26
  ## Setup
15
27
 
16
28
  Add your Jamwidgets site key to your `.env`:
@@ -58,6 +70,49 @@ const posts = await getCollection("posts");
58
70
  ))}
59
71
  ```
60
72
 
73
+ Render a post's Markdown body with Astro's content component:
74
+
75
+ ```astro
76
+ ---
77
+ import { getEntry, render } from "astro:content";
78
+ import { Image } from "astro:assets";
79
+ const post = await getEntry("posts", "hello-world");
80
+ if (!post) throw new Error("Post not found");
81
+ const { Content } = await render(post);
82
+ ---
83
+
84
+ {post.data.coverImage && (
85
+ <Image
86
+ src={post.data.coverImage}
87
+ inferSize
88
+ layout="full-width"
89
+ alt={post.data.title}
90
+ />
91
+ )}
92
+ <article><Content /></article>
93
+ ```
94
+
95
+ `post.data.content` is Markdown, not an HTML string. Do not render it with
96
+ `set:html`; use Astro's `render()` and `<Content />` as above.
97
+
98
+ For responsive uploaded images, authorize Jamwidgets' asset host in your Astro
99
+ config. Astro will then generate correctly sized image variants for Markdown
100
+ images. The hostname below is for Jamwidgets Cloud; self-hosted deployments use
101
+ the hostname configured by `ASSET_BASE_URL` (or fall back to
102
+ `<AWS_S3_BUCKET>.s3.<AWS_S3_REGION>.amazonaws.com`):
103
+
104
+ ```js
105
+ import { defineConfig } from "astro/config";
106
+
107
+ export default defineConfig({
108
+ image: {
109
+ domains: ["assets.jamwidgets.com"],
110
+ layout: "constrained",
111
+ responsiveStyles: true,
112
+ },
113
+ });
114
+ ```
115
+
61
116
  ### Loader Options
62
117
 
63
118
  ```ts
package/dist/index.d.ts CHANGED
@@ -5,7 +5,8 @@
5
5
  * Re-exports all types, API functions, and controllers from @jamwidgets/core.
6
6
  */
7
7
  export { DEFAULT_ENDPOINT, API_PATH, VISITOR_STORAGE_KEY, type JamwidgetsConfig, type SeriphConfig, // deprecated alias
8
- type Comment, type ReactionCounts, type FormSubmitResponse, type SubscribeResponse, type JamwidgetsPost, type SeriphPost, // deprecated alias
9
- type Announcement, type AnnouncementType, type Poll, type PollOption, type PollSettings, type PollWithResults, type ShowResultsMode, type FeedbackType, buildUrl, getSiteKey, getVisitorId, setVisitorId, type SubmitFormOptions, submitForm, type FetchCommentsOptions, fetchComments, type PostCommentOptions, postComment, type FetchReactionsOptions, type FetchReactionsResponse, fetchReactions, type AddReactionOptions, addReaction, type RemoveReactionOptions, removeReaction, type SubscribeOptions, subscribe, type JoinWaitlistOptions, type JoinWaitlistResponse, joinWaitlist, type ViewCountsOptions, type ViewCounts, type RecordViewResponse, getViewCounts, recordView, type SubmitFeedbackOptions, type SubmitFeedbackResponse, submitFeedback, type FetchPollOptions, fetchPoll, type VotePollOptions, type VotePollResponse, votePoll, type FetchAnnouncementsOptions, fetchAnnouncements, type DismissAnnouncementOptions, dismissAnnouncement, type FetchPostsOptions, fetchPosts, type FetchPostOptions, fetchPost, type SiteConfig, fetchSiteConfig, POWERED_BY_URL, POWERED_BY_LABEL, type ControllerStatus, type ControllerListener, type SubscribeState, type FormState, type ReactionsState, type CommentsState, type WaitlistState, type ViewCountsState, type FeedbackState, type PollState, type AnnouncementsState, SubscribeController, WaitlistController, FormController, ReactionsController, CommentsController, ViewCountsController, FeedbackController, PollController, AnnouncementsController, } from "@jamwidgets/core";
8
+ type Comment, type ReactionCounts, type FormSubmitResponse, type SubscribeResponse, type JamwidgetsPost, type JamwidgetsGallery, type GalleryPhoto, type GallerySummary, type SeriphPost, // deprecated alias
9
+ type Announcement, type AnnouncementType, type Poll, type PollOption, type PollSettings, type PollWithResults, type ShowResultsMode, type FeedbackType, buildUrl, getSiteKey, getVisitorId, setVisitorId, type SubmitFormOptions, submitForm, type FetchCommentsOptions, fetchComments, type PostCommentOptions, postComment, type FetchReactionsOptions, type FetchReactionsResponse, fetchReactions, type AddReactionOptions, addReaction, type RemoveReactionOptions, removeReaction, type SubscribeOptions, subscribe, type JoinWaitlistOptions, type JoinWaitlistResponse, joinWaitlist, type ViewCountsOptions, type ViewCounts, type RecordViewResponse, getViewCounts, recordView, type SubmitFeedbackOptions, type SubmitFeedbackResponse, submitFeedback, type FetchPollOptions, fetchPoll, type VotePollOptions, type VotePollResponse, votePoll, type FetchAnnouncementsOptions, fetchAnnouncements, type DismissAnnouncementOptions, dismissAnnouncement, type FetchPostsOptions, fetchPosts, type FetchPostOptions, fetchPost, type FetchGalleriesOptions, fetchGalleries, type FetchGalleryOptions, fetchGallery, type FetchRandomPhotoOptions, fetchRandomPhoto, type SiteConfig, fetchSiteConfig, POWERED_BY_URL, POWERED_BY_LABEL, type ControllerStatus, type ControllerListener, type SubscribeState, type FormState, type ReactionsState, type CommentsState, type WaitlistState, type ViewCountsState, type FeedbackState, type PollState, type AnnouncementsState, SubscribeController, WaitlistController, FormController, ReactionsController, CommentsController, ViewCountsController, FeedbackController, PollController, AnnouncementsController, } from "@jamwidgets/core";
10
10
  export { jamwidgetsPostsLoader, seriphPostsLoader, // deprecated alias
11
- type JamwidgetsPostsLoaderOptions, type SeriphPostsLoaderOptions, } from "./loader.js";
11
+ type JamwidgetsPostsLoaderOptions, type SeriphPostsLoaderOptions, // deprecated alias
12
+ jamwidgetsGalleriesLoader, type JamwidgetsGalleriesLoaderOptions, } from "./loader.js";
package/dist/index.js CHANGED
@@ -9,7 +9,7 @@ export {
9
9
  // Constants
10
10
  DEFAULT_ENDPOINT, API_PATH, VISITOR_STORAGE_KEY,
11
11
  // Helpers
12
- buildUrl, getSiteKey, getVisitorId, setVisitorId, submitForm, fetchComments, postComment, fetchReactions, addReaction, removeReaction, subscribe, joinWaitlist, getViewCounts, recordView, submitFeedback, fetchPoll, votePoll, fetchAnnouncements, dismissAnnouncement, fetchPosts, fetchPost, fetchSiteConfig, POWERED_BY_URL, POWERED_BY_LABEL, SubscribeController, WaitlistController, FormController, ReactionsController, CommentsController, ViewCountsController, FeedbackController, PollController, AnnouncementsController, } from "@jamwidgets/core";
12
+ buildUrl, getSiteKey, getVisitorId, setVisitorId, submitForm, fetchComments, postComment, fetchReactions, addReaction, removeReaction, subscribe, joinWaitlist, getViewCounts, recordView, submitFeedback, fetchPoll, votePoll, fetchAnnouncements, dismissAnnouncement, fetchPosts, fetchPost, fetchGalleries, fetchGallery, fetchRandomPhoto, fetchSiteConfig, POWERED_BY_URL, POWERED_BY_LABEL, SubscribeController, WaitlistController, FormController, ReactionsController, CommentsController, ViewCountsController, FeedbackController, PollController, AnnouncementsController, } from "@jamwidgets/core";
13
13
  // Re-export loader (Astro-specific)
14
14
  export { jamwidgetsPostsLoader, seriphPostsLoader, // deprecated alias
15
- } from "./loader.js";
15
+ jamwidgetsGalleriesLoader, } from "./loader.js";
package/dist/loader.d.ts CHANGED
@@ -45,3 +45,10 @@ export type SeriphPostsLoaderOptions = JamwidgetsPostsLoaderOptions;
45
45
  export declare function jamwidgetsPostsLoader(options: JamwidgetsPostsLoaderOptions): Loader;
46
46
  /** @deprecated Use jamwidgetsPostsLoader instead */
47
47
  export declare const seriphPostsLoader: typeof jamwidgetsPostsLoader;
48
+ export interface JamwidgetsGalleriesLoaderOptions {
49
+ siteKey: string;
50
+ endpoint?: string;
51
+ origin?: string;
52
+ onError?: "throw" | "warn" | "ignore";
53
+ }
54
+ export declare function jamwidgetsGalleriesLoader(options: JamwidgetsGalleriesLoaderOptions): Loader;
package/dist/loader.js CHANGED
@@ -16,7 +16,7 @@
16
16
  *
17
17
  * export const collections = { posts };
18
18
  */
19
- import { DEFAULT_ENDPOINT, API_PATH, getSiteKey, fetchPosts as coreFetchPosts, fetchPost as coreFetchPost, } from "@jamwidgets/core";
19
+ import { DEFAULT_ENDPOINT, API_PATH, getSiteKey, fetchPosts as coreFetchPosts, fetchPost as coreFetchPost, fetchGalleries as coreFetchGalleries, fetchGallery as coreFetchGallery, } from "@jamwidgets/core";
20
20
  export { coreFetchPosts as fetchPosts, coreFetchPost as fetchPost };
21
21
  /**
22
22
  * Creates an Astro content loader that fetches posts from Jamwidgets.
@@ -30,7 +30,7 @@ export function jamwidgetsPostsLoader(options) {
30
30
  return {
31
31
  name: "jamwidgets-posts-loader",
32
32
  async load(context) {
33
- const { store, logger } = context;
33
+ const { store, logger, renderMarkdown } = context;
34
34
  try {
35
35
  const url = new URL(`${baseUrl}/posts`);
36
36
  url.searchParams.set("limit", String(limit));
@@ -50,12 +50,15 @@ export function jamwidgetsPostsLoader(options) {
50
50
  throw new Error(`Failed to fetch posts: ${response.status} ${response.statusText}`);
51
51
  }
52
52
  const data = await response.json();
53
+ const entries = await Promise.all(data.posts.map(async (post) => ({
54
+ id: post.slug,
55
+ data: { ...post },
56
+ body: post.content,
57
+ rendered: await renderMarkdown(post.content),
58
+ })));
53
59
  store.clear();
54
- for (const post of data.posts) {
55
- store.set({
56
- id: post.slug,
57
- data: { ...post },
58
- });
60
+ for (const entry of entries) {
61
+ store.set(entry);
59
62
  }
60
63
  logger.info(`Loaded ${data.posts.length} posts from Jamwidgets`);
61
64
  }
@@ -74,3 +77,43 @@ export function jamwidgetsPostsLoader(options) {
74
77
  }
75
78
  /** @deprecated Use jamwidgetsPostsLoader instead */
76
79
  export const seriphPostsLoader = jamwidgetsPostsLoader;
80
+ export function jamwidgetsGalleriesLoader(options) {
81
+ const { endpoint = DEFAULT_ENDPOINT, origin, onError = "throw" } = options;
82
+ const siteKey = getSiteKey({ siteKey: options.siteKey });
83
+ return {
84
+ name: "jamwidgets-galleries-loader",
85
+ async load({ store, logger }) {
86
+ try {
87
+ const summaries = [];
88
+ let after;
89
+ do {
90
+ const page = await coreFetchGalleries({
91
+ siteKey,
92
+ endpoint,
93
+ origin,
94
+ limit: 100,
95
+ after,
96
+ });
97
+ summaries.push(...page.galleries);
98
+ after = page.nextCursor;
99
+ } while (after);
100
+ const galleries = [];
101
+ for (const summary of summaries) {
102
+ galleries.push(await coreFetchGallery({ siteKey, endpoint, origin, slug: summary.slug }));
103
+ }
104
+ const entries = galleries.filter((gallery) => gallery !== null);
105
+ store.clear();
106
+ for (const gallery of entries)
107
+ store.set({ id: gallery.slug, data: { ...gallery } });
108
+ logger.info(`Loaded ${entries.length} galleries from Jamwidgets at build time`);
109
+ }
110
+ catch (error) {
111
+ const message = error instanceof Error ? error.message : String(error);
112
+ if (onError === "throw")
113
+ throw error;
114
+ if (onError === "warn")
115
+ logger.warn(`Error loading galleries: ${message}`);
116
+ }
117
+ },
118
+ };
119
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@jamwidgets/astro",
3
- "version": "0.5.0",
4
- "description": "Astro components and content loader for Jamwidgets (forms, comments, reactions, posts)",
3
+ "version": "0.6.1",
4
+ "description": "Astro components and content loaders for Jamwidgets",
5
5
  "repository": {
6
6
  "type": "git",
7
7
  "url": "git+https://github.com/jamwidgets/astro.git"
@@ -33,7 +33,10 @@
33
33
  "./Announcements": "./src/Announcements.astro",
34
34
  "./Feedback": "./src/Feedback.astro",
35
35
  "./Waitlist": "./src/Waitlist.astro",
36
- "./Embed": "./src/Embed.astro"
36
+ "./Embed": "./src/Embed.astro",
37
+ "./Gallery": "./src/Gallery.astro",
38
+ "./GalleryIndex": "./src/GalleryIndex.astro",
39
+ "./RandomPhoto": "./src/RandomPhoto.astro"
37
40
  },
38
41
  "files": [
39
42
  "src",
@@ -57,10 +60,10 @@
57
60
  ],
58
61
  "license": "MIT",
59
62
  "dependencies": {
60
- "@jamwidgets/core": "^0.4.0"
63
+ "@jamwidgets/core": "^0.5.1"
61
64
  },
62
65
  "peerDependencies": {
63
- "astro": "^5.0.0 || ^6.0.0 || ^7.0.0"
66
+ "astro": "^5.9.0 || ^6.0.0 || ^7.0.0"
64
67
  },
65
68
  "devDependencies": {
66
69
  "astro": "^7.1.3",
package/src/Embed.astro CHANGED
@@ -7,7 +7,8 @@
7
7
  * any embedded polls, reactions, etc.
8
8
  *
9
9
  * @example
10
- * <article set:html={post.content} />
10
+ * const { Content } = await render(post);
11
+ * <article><Content /></article>
11
12
  * <Embed siteKey={import.meta.env.JAMWIDGETS_SITE_KEY} />
12
13
  */
13
14
 
@@ -0,0 +1,8 @@
1
+ ---
2
+ interface Props { siteKey: string; slug: string; endpoint?: string; tag?: string }
3
+ const { siteKey, slug, endpoint, tag } = Astro.props;
4
+ ---
5
+ <jamwidgets-gallery site-key={siteKey} slug={slug} endpoint={endpoint} tag={tag} />
6
+ <script>
7
+ import "@jamwidgets/core/gallery-elements";
8
+ </script>
@@ -0,0 +1,8 @@
1
+ ---
2
+ interface Props { siteKey: string; endpoint?: string; deepLink?: boolean }
3
+ const { siteKey, endpoint, deepLink = false } = Astro.props;
4
+ ---
5
+ <jamwidgets-gallery-index site-key={siteKey} endpoint={endpoint} deep-link={deepLink ? "" : undefined} />
6
+ <script>
7
+ import "@jamwidgets/core/gallery-elements";
8
+ </script>
@@ -0,0 +1,8 @@
1
+ ---
2
+ interface Props { siteKey: string; slug: string; endpoint?: string; tag?: string; seed?: string }
3
+ const { siteKey, slug, endpoint, tag, seed } = Astro.props;
4
+ ---
5
+ <jamwidgets-random-photo site-key={siteKey} slug={slug} endpoint={endpoint} tag={tag} seed={seed} />
6
+ <script>
7
+ import "@jamwidgets/core/gallery-elements";
8
+ </script>
package/src/index.ts CHANGED
@@ -20,6 +20,9 @@ export {
20
20
  type FormSubmitResponse,
21
21
  type SubscribeResponse,
22
22
  type JamwidgetsPost,
23
+ type JamwidgetsGallery,
24
+ type GalleryPhoto,
25
+ type GallerySummary,
23
26
  type SeriphPost, // deprecated alias
24
27
  type Announcement,
25
28
  type AnnouncementType,
@@ -94,6 +97,12 @@ export {
94
97
  fetchPosts,
95
98
  type FetchPostOptions,
96
99
  fetchPost,
100
+ type FetchGalleriesOptions,
101
+ fetchGalleries,
102
+ type FetchGalleryOptions,
103
+ fetchGallery,
104
+ type FetchRandomPhotoOptions,
105
+ fetchRandomPhoto,
97
106
 
98
107
  // Site config + powered-by attribution
99
108
  type SiteConfig,
@@ -130,4 +139,6 @@ export {
130
139
  seriphPostsLoader, // deprecated alias
131
140
  type JamwidgetsPostsLoaderOptions,
132
141
  type SeriphPostsLoaderOptions, // deprecated alias
142
+ jamwidgetsGalleriesLoader,
143
+ type JamwidgetsGalleriesLoaderOptions,
133
144
  } from "./loader.js";
package/src/loader.ts CHANGED
@@ -27,6 +27,10 @@ import {
27
27
  type SeriphPost, // deprecated alias
28
28
  type FetchPostsOptions,
29
29
  type FetchPostOptions,
30
+ fetchGalleries as coreFetchGalleries,
31
+ fetchGallery as coreFetchGallery,
32
+ type JamwidgetsGallery,
33
+ type GallerySummary,
30
34
  } from "@jamwidgets/core";
31
35
  import type { Loader, LoaderContext } from "astro/loaders";
32
36
 
@@ -78,7 +82,7 @@ export function jamwidgetsPostsLoader(options: JamwidgetsPostsLoaderOptions): Lo
78
82
  name: "jamwidgets-posts-loader",
79
83
 
80
84
  async load(context: LoaderContext) {
81
- const { store, logger } = context;
85
+ const { store, logger, renderMarkdown } = context;
82
86
 
83
87
  try {
84
88
  const url = new URL(`${baseUrl}/posts`);
@@ -106,13 +110,18 @@ export function jamwidgetsPostsLoader(options: JamwidgetsPostsLoaderOptions): Lo
106
110
 
107
111
  const data: ApiResponse = await response.json();
108
112
 
109
- store.clear();
110
-
111
- for (const post of data.posts) {
112
- store.set({
113
+ const entries = await Promise.all(
114
+ data.posts.map(async (post) => ({
113
115
  id: post.slug,
114
116
  data: { ...post },
115
- });
117
+ body: post.content,
118
+ rendered: await renderMarkdown(post.content),
119
+ })),
120
+ );
121
+
122
+ store.clear();
123
+ for (const entry of entries) {
124
+ store.set(entry);
116
125
  }
117
126
 
118
127
  logger.info(`Loaded ${data.posts.length} posts from Jamwidgets`);
@@ -132,3 +141,49 @@ export function jamwidgetsPostsLoader(options: JamwidgetsPostsLoaderOptions): Lo
132
141
 
133
142
  /** @deprecated Use jamwidgetsPostsLoader instead */
134
143
  export const seriphPostsLoader = jamwidgetsPostsLoader;
144
+
145
+ export interface JamwidgetsGalleriesLoaderOptions {
146
+ siteKey: string;
147
+ endpoint?: string;
148
+ origin?: string;
149
+ onError?: "throw" | "warn" | "ignore";
150
+ }
151
+
152
+ export function jamwidgetsGalleriesLoader(options: JamwidgetsGalleriesLoaderOptions): Loader {
153
+ const { endpoint = DEFAULT_ENDPOINT, origin, onError = "throw" } = options;
154
+ const siteKey = getSiteKey({ siteKey: options.siteKey });
155
+ return {
156
+ name: "jamwidgets-galleries-loader",
157
+ async load({ store, logger }: LoaderContext) {
158
+ try {
159
+ const summaries: GallerySummary[] = [];
160
+ let after: string | undefined;
161
+ do {
162
+ const page = await coreFetchGalleries({
163
+ siteKey,
164
+ endpoint,
165
+ origin,
166
+ limit: 100,
167
+ after,
168
+ });
169
+ summaries.push(...page.galleries);
170
+ after = page.nextCursor;
171
+ } while (after);
172
+ const galleries: Array<JamwidgetsGallery | null> = [];
173
+ for (const summary of summaries) {
174
+ galleries.push(
175
+ await coreFetchGallery({ siteKey, endpoint, origin, slug: summary.slug }),
176
+ );
177
+ }
178
+ const entries = galleries.filter((gallery): gallery is JamwidgetsGallery => gallery !== null);
179
+ store.clear();
180
+ for (const gallery of entries) store.set({ id: gallery.slug, data: { ...gallery } });
181
+ logger.info(`Loaded ${entries.length} galleries from Jamwidgets at build time`);
182
+ } catch (error) {
183
+ const message = error instanceof Error ? error.message : String(error);
184
+ if (onError === "throw") throw error;
185
+ if (onError === "warn") logger.warn(`Error loading galleries: ${message}`);
186
+ }
187
+ },
188
+ };
189
+ }