@@ -166,3 +166,151 @@ And include it in `main.html`:
166166``` jinja title="docs/theme/overrides/main.html"
167167{% include "partials/json_feed.html.jinja2" %}
168168```
169+
170+ ----
171+
172+ ## Display a recent updates page from the JSON feed
173+
174+ The updated JSON feed can also be used inside your documentation site to render a
175+ small "recently updated pages" index. This is useful for documentation portals or
176+ digital gardens where readers may want to see what changed recently without using
177+ a feed reader.
178+
179+ First, keep the updated JSON feed enabled and expose enough entries for the page:
180+
181+ ``` yaml title="mkdocs.yml"
182+ plugins :
183+ - rss :
184+ length : 100
185+ match_path : " ^(?!updates\\ .md$)(?!.*(?:^|/)(README|index)\\ .md$).+\\ .md$"
186+ feeds_filenames :
187+ json_updated : feed_json_updated.json
188+
189+ extra_javascript :
190+ - javascripts/recent-updates.js
191+
192+ nav :
193+ - Recently updated : updates.md
194+ ` ` `
195+
196+ Then create a page that contains the target container and optional direct feed
197+ links:
198+
199+ ` ` ` markdown title="docs/updates.md"
200+ # Recently updated
201+
202+ <p class="updates-note">
203+ Sorted by Git last-modified time from the updated feed. Index pages and this
204+ page are excluded.
205+ </p>
206+
207+ <p class="updates-feeds">
208+ <a href="../feed_json_updated.json">JSON feed</a>
209+ </p>
210+
211+ <div data-recent-updates data-page-size="20">
212+ Loading updates...
213+ </div>
214+ ```
215+
216+ Finally, add a small script that fetches the JSON feed and progressively renders
217+ entries:
218+
219+ ``` javascript title="docs/javascripts/recent-updates.js"
220+ (function () {
221+ var root = document .querySelector (" [data-recent-updates]" );
222+
223+ if (! root) {
224+ return ;
225+ }
226+
227+ function feedUrl () {
228+ return new URL (" ../feed_json_updated.json" , window .location .href ).toString ();
229+ }
230+
231+ function itemDate (item ) {
232+ return item .date_modified || item .date_published || item .date_created || " " ;
233+ }
234+
235+ function formatDate (value ) {
236+ var date = new Date (value);
237+
238+ if (Number .isNaN (date .getTime ())) {
239+ return value;
240+ }
241+
242+ return date .toLocaleString (undefined , {
243+ year: " numeric" ,
244+ month: " short" ,
245+ day: " numeric" ,
246+ hour: " 2-digit" ,
247+ minute: " 2-digit" ,
248+ });
249+ }
250+
251+ function itemSummary (item ) {
252+ var container = document .createElement (" div" );
253+ var text = " " ;
254+
255+ container .innerHTML = item .content_html || item .summary || " " ;
256+ text = (container .textContent || " " ).replace (/ \s + / g , " " ).trim ();
257+
258+ return text .length > 160 ? text .slice (0 , 160 ) + " ..." : text;
259+ }
260+
261+ function render (items ) {
262+ var pageSize = parseInt (root .getAttribute (" data-page-size" ) || " 20" , 10 );
263+ var visibleCount = Math .min (pageSize, items .length );
264+ var list = document .createElement (" ol" );
265+ var button = document .createElement (" button" );
266+
267+ function renderVisibleItems () {
268+ list .innerHTML = " " ;
269+
270+ items .slice (0 , visibleCount).forEach (function (item ) {
271+ var entry = document .createElement (" li" );
272+ var link = document .createElement (" a" );
273+ var summary = document .createElement (" p" );
274+ var time = document .createElement (" time" );
275+ var date = itemDate (item);
276+
277+ link .href = item .url || item .id || " #" ;
278+ link .textContent = item .title || link .href ;
279+ summary .textContent = itemSummary (item);
280+ time .dateTime = date;
281+ time .textContent = formatDate (date);
282+
283+ entry .appendChild (link);
284+ entry .appendChild (summary);
285+ entry .appendChild (time);
286+ list .appendChild (entry);
287+ });
288+
289+ button .hidden = visibleCount >= items .length ;
290+ }
291+
292+ button .type = " button" ;
293+ button .textContent = " Show more updates" ;
294+ button .addEventListener (" click" , function () {
295+ visibleCount = Math .min (visibleCount + pageSize, items .length );
296+ renderVisibleItems ();
297+ });
298+
299+ root .replaceChildren (list, button);
300+ renderVisibleItems ();
301+ }
302+
303+ fetch (feedUrl (), { cache: " no-store" })
304+ .then (function (response ) {
305+ return response .json ();
306+ })
307+ .then (function (feed ) {
308+ render (Array .isArray (feed .items ) ? feed .items : []);
309+ });
310+ })();
311+ ```
312+
313+ The example intentionally uses the updated feed (` feed_json_updated.json ` ) rather
314+ than the created feed, because the page is meant to answer "what changed
315+ recently?". You can adapt ` match_path ` to include only blog posts, release notes,
316+ or any other section of your documentation.
0 commit comments