Skip to content

Commit 7ffd00d

Browse files
author
zhengyu.zy0
committed
docs: add recent updates JSON feed recipe
1 parent 49b30e7 commit 7ffd00d

1 file changed

Lines changed: 148 additions & 0 deletions

File tree

docs/integrations.md

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)