@geekity/cms 0.12.0 → 0.14.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.
- package/README.md +188 -9
- package/admin/pages/account/login.njk +3 -0
- package/admin/pages/documents/editor.njk +20 -0
- package/admin/pages/indieauth/consent.njk +51 -0
- package/admin/pages/indieauth/refused.njk +11 -0
- package/admin/pages/settings/general.njk +12 -2
- package/admin/pages/settings/reading.njk +93 -0
- package/admin/pages/users/apps.njk +74 -0
- package/admin/pages/users/edit.njk +12 -0
- package/admin/static/admin.css +27 -0
- package/dist/admin/accounts.d.ts +3 -0
- package/dist/admin/accounts.d.ts.map +1 -1
- package/dist/admin/accounts.js +5 -0
- package/dist/admin/accounts.js.map +1 -1
- package/dist/admin/documents.d.ts +4 -0
- package/dist/admin/documents.d.ts.map +1 -1
- package/dist/admin/documents.js +45 -5
- package/dist/admin/documents.js.map +1 -1
- package/dist/admin/federation.js +2 -2
- package/dist/admin/federation.js.map +1 -1
- package/dist/admin/headers.d.ts +9 -2
- package/dist/admin/headers.d.ts.map +1 -1
- package/dist/admin/headers.js +16 -7
- package/dist/admin/headers.js.map +1 -1
- package/dist/admin/menu.d.ts.map +1 -1
- package/dist/admin/menu.js +2 -1
- package/dist/admin/menu.js.map +1 -1
- package/dist/admin/routes.d.ts.map +1 -1
- package/dist/admin/routes.js +48 -5
- package/dist/admin/routes.js.map +1 -1
- package/dist/admin/settings-general.d.ts.map +1 -1
- package/dist/admin/settings-general.js +28 -3
- package/dist/admin/settings-general.js.map +1 -1
- package/dist/admin/settings-reading.d.ts.map +1 -1
- package/dist/admin/settings-reading.js +15 -1
- package/dist/admin/settings-reading.js.map +1 -1
- package/dist/admin/settings.d.ts +83 -4
- package/dist/admin/settings.d.ts.map +1 -1
- package/dist/admin/settings.js +166 -4
- package/dist/admin/settings.js.map +1 -1
- package/dist/admin/templates.d.ts +3 -0
- package/dist/admin/templates.d.ts.map +1 -1
- package/dist/admin/templates.js +3 -0
- package/dist/admin/templates.js.map +1 -1
- package/dist/admin/users.d.ts +8 -0
- package/dist/admin/users.d.ts.map +1 -1
- package/dist/admin/users.js +9 -0
- package/dist/admin/users.js.map +1 -1
- package/dist/config.d.ts +34 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +1 -0
- package/dist/config.js.map +1 -1
- package/dist/content/pinned.d.ts +26 -0
- package/dist/content/pinned.d.ts.map +1 -0
- package/dist/content/pinned.js +42 -0
- package/dist/content/pinned.js.map +1 -0
- package/dist/content/store.d.ts +7 -0
- package/dist/content/store.d.ts.map +1 -1
- package/dist/content/store.js +20 -0
- package/dist/content/store.js.map +1 -1
- package/dist/env.d.ts +21 -0
- package/dist/env.d.ts.map +1 -1
- package/dist/federation/actor.d.ts +15 -0
- package/dist/federation/actor.d.ts.map +1 -1
- package/dist/federation/actor.js +94 -3
- package/dist/federation/actor.js.map +1 -1
- package/dist/federation/article.d.ts +13 -1
- package/dist/federation/article.d.ts.map +1 -1
- package/dist/federation/article.js +45 -6
- package/dist/federation/article.js.map +1 -1
- package/dist/federation/delivery.d.ts.map +1 -1
- package/dist/federation/delivery.js +36 -4
- package/dist/federation/delivery.js.map +1 -1
- package/dist/federation/federation.d.ts.map +1 -1
- package/dist/federation/federation.js +24 -2
- package/dist/federation/federation.js.map +1 -1
- package/dist/federation/mount.js +2 -2
- package/dist/federation/mount.js.map +1 -1
- package/dist/federation/paths.d.ts +30 -0
- package/dist/federation/paths.d.ts.map +1 -1
- package/dist/federation/paths.js +36 -0
- package/dist/federation/paths.js.map +1 -1
- package/dist/images/icons.d.ts +44 -25
- package/dist/images/icons.d.ts.map +1 -1
- package/dist/images/icons.js +205 -60
- package/dist/images/icons.js.map +1 -1
- package/dist/images/index.d.ts +2 -2
- package/dist/images/index.d.ts.map +1 -1
- package/dist/images/index.js +1 -1
- package/dist/images/index.js.map +1 -1
- package/dist/images/markup.d.ts +14 -0
- package/dist/images/markup.d.ts.map +1 -1
- package/dist/images/markup.js +16 -11
- package/dist/images/markup.js.map +1 -1
- package/dist/images/variants.d.ts +11 -0
- package/dist/images/variants.d.ts.map +1 -1
- package/dist/images/variants.js +24 -4
- package/dist/images/variants.js.map +1 -1
- package/dist/index.d.ts +15 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +33 -2
- package/dist/index.js.map +1 -1
- package/dist/indexnow.d.ts +34 -0
- package/dist/indexnow.d.ts.map +1 -0
- package/dist/indexnow.js +141 -0
- package/dist/indexnow.js.map +1 -0
- package/dist/indieauth/bearer.d.ts +39 -0
- package/dist/indieauth/bearer.d.ts.map +1 -0
- package/dist/indieauth/bearer.js +63 -0
- package/dist/indieauth/bearer.js.map +1 -0
- package/dist/indieauth/client.d.ts +61 -0
- package/dist/indieauth/client.d.ts.map +1 -0
- package/dist/indieauth/client.js +139 -0
- package/dist/indieauth/client.js.map +1 -0
- package/dist/indieauth/connected-apps.d.ts +23 -0
- package/dist/indieauth/connected-apps.d.ts.map +1 -0
- package/dist/indieauth/connected-apps.js +73 -0
- package/dist/indieauth/connected-apps.js.map +1 -0
- package/dist/indieauth/consent.d.ts +28 -0
- package/dist/indieauth/consent.d.ts.map +1 -0
- package/dist/indieauth/consent.js +185 -0
- package/dist/indieauth/consent.js.map +1 -0
- package/dist/indieauth/discovery.d.ts +87 -0
- package/dist/indieauth/discovery.d.ts.map +1 -0
- package/dist/indieauth/discovery.js +127 -0
- package/dist/indieauth/discovery.js.map +1 -0
- package/dist/indieauth/grants.d.ts +57 -0
- package/dist/indieauth/grants.d.ts.map +1 -0
- package/dist/indieauth/grants.js +35 -0
- package/dist/indieauth/grants.js.map +1 -0
- package/dist/indieauth/identity.d.ts +36 -0
- package/dist/indieauth/identity.d.ts.map +1 -0
- package/dist/indieauth/identity.js +77 -0
- package/dist/indieauth/identity.js.map +1 -0
- package/dist/indieauth/redeem.d.ts +47 -0
- package/dist/indieauth/redeem.d.ts.map +1 -0
- package/dist/indieauth/redeem.js +69 -0
- package/dist/indieauth/redeem.js.map +1 -0
- package/dist/indieauth/request.d.ts +47 -0
- package/dist/indieauth/request.d.ts.map +1 -0
- package/dist/indieauth/request.js +104 -0
- package/dist/indieauth/request.js.map +1 -0
- package/dist/indieauth/token.d.ts +19 -0
- package/dist/indieauth/token.d.ts.map +1 -0
- package/dist/indieauth/token.js +110 -0
- package/dist/indieauth/token.js.map +1 -0
- package/dist/indieauth/tokens.d.ts +131 -0
- package/dist/indieauth/tokens.d.ts.map +1 -0
- package/dist/indieauth/tokens.js +205 -0
- package/dist/indieauth/tokens.js.map +1 -0
- package/dist/web/admin-bar.d.ts +3 -1
- package/dist/web/admin-bar.d.ts.map +1 -1
- package/dist/web/admin-bar.js +5 -1
- package/dist/web/admin-bar.js.map +1 -1
- package/dist/web/archive.d.ts +1 -1
- package/dist/web/archive.d.ts.map +1 -1
- package/dist/web/archive.js +3 -2
- package/dist/web/archive.js.map +1 -1
- package/dist/web/authors.d.ts +19 -0
- package/dist/web/authors.d.ts.map +1 -1
- package/dist/web/authors.js +25 -0
- package/dist/web/authors.js.map +1 -1
- package/dist/web/context.d.ts +25 -0
- package/dist/web/context.d.ts.map +1 -1
- package/dist/web/context.js +18 -0
- package/dist/web/context.js.map +1 -1
- package/dist/web/feed-atom.d.ts.map +1 -1
- package/dist/web/feed-atom.js +5 -2
- package/dist/web/feed-atom.js.map +1 -1
- package/dist/web/feed-item.d.ts +24 -6
- package/dist/web/feed-item.d.ts.map +1 -1
- package/dist/web/feed-item.js +16 -8
- package/dist/web/feed-item.js.map +1 -1
- package/dist/web/feed-json.d.ts +6 -2
- package/dist/web/feed-json.d.ts.map +1 -1
- package/dist/web/feed-json.js +6 -2
- package/dist/web/feed-json.js.map +1 -1
- package/dist/web/feed-rss.d.ts.map +1 -1
- package/dist/web/feed-rss.js +17 -4
- package/dist/web/feed-rss.js.map +1 -1
- package/dist/web/feed-source.d.ts +31 -0
- package/dist/web/feed-source.d.ts.map +1 -1
- package/dist/web/feed-source.js +31 -0
- package/dist/web/feed-source.js.map +1 -1
- package/dist/web/feed-xml.d.ts +2 -0
- package/dist/web/feed-xml.d.ts.map +1 -1
- package/dist/web/feed-xml.js +2 -0
- package/dist/web/feed-xml.js.map +1 -1
- package/dist/web/feeds.d.ts.map +1 -1
- package/dist/web/feeds.js +15 -3
- package/dist/web/feeds.js.map +1 -1
- package/dist/web/index.d.ts +4 -2
- package/dist/web/index.d.ts.map +1 -1
- package/dist/web/index.js +3 -2
- package/dist/web/index.js.map +1 -1
- package/dist/web/indexnow.d.ts +9 -0
- package/dist/web/indexnow.d.ts.map +1 -0
- package/dist/web/indexnow.js +27 -0
- package/dist/web/indexnow.js.map +1 -0
- package/dist/web/llms.d.ts +58 -0
- package/dist/web/llms.d.ts.map +1 -0
- package/dist/web/llms.js +91 -0
- package/dist/web/llms.js.map +1 -0
- package/dist/web/locale.d.ts +41 -0
- package/dist/web/locale.d.ts.map +1 -0
- package/dist/web/locale.js +56 -0
- package/dist/web/locale.js.map +1 -0
- package/dist/web/manifest.d.ts +50 -0
- package/dist/web/manifest.d.ts.map +1 -0
- package/dist/web/manifest.js +56 -0
- package/dist/web/manifest.js.map +1 -0
- package/dist/web/negotiate.d.ts.map +1 -1
- package/dist/web/negotiate.js +6 -0
- package/dist/web/negotiate.js.map +1 -1
- package/dist/web/oembed.d.ts +104 -0
- package/dist/web/oembed.d.ts.map +1 -0
- package/dist/web/oembed.js +290 -0
- package/dist/web/oembed.js.map +1 -0
- package/dist/web/opensearch.d.ts +35 -0
- package/dist/web/opensearch.d.ts.map +1 -0
- package/dist/web/opensearch.js +65 -0
- package/dist/web/opensearch.js.map +1 -0
- package/dist/web/render.d.ts +6 -1
- package/dist/web/render.d.ts.map +1 -1
- package/dist/web/render.js +40 -10
- package/dist/web/render.js.map +1 -1
- package/dist/web/robots.d.ts +89 -0
- package/dist/web/robots.d.ts.map +1 -0
- package/dist/web/robots.js +259 -0
- package/dist/web/robots.js.map +1 -0
- package/dist/web/routes.d.ts.map +1 -1
- package/dist/web/routes.js +179 -8
- package/dist/web/routes.js.map +1 -1
- package/dist/web/share-image.d.ts +51 -0
- package/dist/web/share-image.d.ts.map +1 -0
- package/dist/web/share-image.js +43 -0
- package/dist/web/share-image.js.map +1 -0
- package/dist/web/sitemap.d.ts +0 -26
- package/dist/web/sitemap.d.ts.map +1 -1
- package/dist/web/sitemap.js +0 -44
- package/dist/web/sitemap.js.map +1 -1
- package/dist/web/templates.d.ts +16 -9
- package/dist/web/templates.d.ts.map +1 -1
- package/dist/web/templates.js +88 -55
- package/dist/web/templates.js.map +1 -1
- package/dist/web/themes.d.ts +33 -0
- package/dist/web/themes.d.ts.map +1 -1
- package/dist/web/themes.js +57 -0
- package/dist/web/themes.js.map +1 -1
- package/dist/webmention/discovery.d.ts +4 -2
- package/dist/webmention/discovery.d.ts.map +1 -1
- package/dist/webmention/discovery.js +14 -8
- package/dist/webmention/discovery.js.map +1 -1
- package/dist/webmention/fetch-public.d.ts +2 -0
- package/dist/webmention/fetch-public.d.ts.map +1 -1
- package/dist/webmention/fetch-public.js +1 -1
- package/dist/webmention/fetch-public.js.map +1 -1
- package/package.json +2 -1
- package/templates/site/content/_data/site.json +0 -1
- package/themes/default/README.md +174 -43
- package/themes/default/layouts/base.njk +116 -17
- package/themes/default/layouts/front-page.njk +7 -1
- package/themes/default/layouts/home.njk +11 -0
- package/themes/default/layouts/page.njk +4 -3
- package/themes/default/layouts/post.njk +5 -4
- package/themes/default/layouts/search.njk +4 -5
- package/themes/default/partials/archive.njk +3 -1
- package/themes/default/partials/bio.njk +16 -2
- package/themes/default/partials/breadcrumbs.njk +20 -0
- package/themes/default/partials/conversation.njk +4 -1
- package/themes/default/partials/jsonld.njk +49 -9
- package/themes/default/partials/kicker.njk +6 -4
- package/themes/default/partials/lang.njk +7 -0
- package/themes/default/partials/post-list.njk +5 -3
- package/themes/default/partials/reply-context.njk +4 -2
- package/themes/default/src/style.css +14 -0
- package/themes/default/static/style.css +57 -0
- package/themes/default/theme.json +2 -0
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import type { Document } from '../content/document.ts';
|
|
2
|
+
import type { ImageConfig } from '../images/paths.ts';
|
|
3
|
+
import type { AuthorContext } from './authors.ts';
|
|
4
|
+
import type { SiteData } from './context.ts';
|
|
5
|
+
import type { ConditionalHeaders } from './negotiate.ts';
|
|
6
|
+
/**
|
|
7
|
+
* The oEmbed provider (TASK-205): what WordPress, Discourse and the rest ask
|
|
8
|
+
* to turn a pasted post URL into a card.
|
|
9
|
+
*
|
|
10
|
+
* Under `/_geekity/`, the CMS's own prefix, so no permalink can take it. Each
|
|
11
|
+
* post and page links it from its head, with its own URL in the query.
|
|
12
|
+
*/
|
|
13
|
+
/** The endpoint's URL. */
|
|
14
|
+
export declare const OEMBED_PATH = "/_geekity/oembed";
|
|
15
|
+
/**
|
|
16
|
+
* The embed view's URL (TASK-208): the card alone as a page, with the
|
|
17
|
+
* document's absolute URL in `?url=`, and the one response on the site another
|
|
18
|
+
* origin may frame.
|
|
19
|
+
*
|
|
20
|
+
* WordPress keeps a rich embed from a provider it does not know only when its
|
|
21
|
+
* html holds an `<iframe>`, so the oEmbed html frames this page after the
|
|
22
|
+
* blockquote, the way WordPress's own provider does.
|
|
23
|
+
*/
|
|
24
|
+
export declare const EMBED_PATH = "/_geekity/embed";
|
|
25
|
+
/** The formats the spec names. Anything else is a 501. */
|
|
26
|
+
export type OEmbedFormat = 'json' | 'xml';
|
|
27
|
+
/** One request, from its query. */
|
|
28
|
+
export interface OEmbedRequest {
|
|
29
|
+
/** The URL to embed, as given. */
|
|
30
|
+
url: string;
|
|
31
|
+
/** `undefined` for a format this provider does not serve. */
|
|
32
|
+
format: OEmbedFormat | undefined;
|
|
33
|
+
maxwidth?: number | undefined;
|
|
34
|
+
maxheight?: number | undefined;
|
|
35
|
+
}
|
|
36
|
+
/** A `type: rich` response. The thumbnail's three fields come together or not at all. */
|
|
37
|
+
export interface OEmbedRich {
|
|
38
|
+
version: '1.0';
|
|
39
|
+
type: 'rich';
|
|
40
|
+
title: string;
|
|
41
|
+
author_name?: string;
|
|
42
|
+
author_url?: string;
|
|
43
|
+
provider_name: string;
|
|
44
|
+
provider_url: string;
|
|
45
|
+
html: string;
|
|
46
|
+
width: number;
|
|
47
|
+
height: number;
|
|
48
|
+
thumbnail_url?: string;
|
|
49
|
+
thumbnail_width?: number;
|
|
50
|
+
thumbnail_height?: number;
|
|
51
|
+
}
|
|
52
|
+
/** A query as a request. A missing `format` is JSON, which every consumer reads. */
|
|
53
|
+
export declare function oEmbedRequest(query: (name: string) => string | undefined): OEmbedRequest;
|
|
54
|
+
/** One published document, served at `href`, and what its card names beside it. */
|
|
55
|
+
export interface EmbedSubject {
|
|
56
|
+
document: Document;
|
|
57
|
+
href: string;
|
|
58
|
+
author: AuthorContext | undefined;
|
|
59
|
+
config: {
|
|
60
|
+
baseUrl: string;
|
|
61
|
+
};
|
|
62
|
+
site: SiteData;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* The embed of one published document, served at `href`.
|
|
66
|
+
*
|
|
67
|
+
* The title is the document's, else a note's first words. The html is the
|
|
68
|
+
* card, then a hidden sandboxed frame of {@link EMBED_PATH}: a consumer that
|
|
69
|
+
* keeps the html as given shows the blockquote, one that strips iframes still
|
|
70
|
+
* has it, and WordPress, which keeps only the frame, shows that once the page
|
|
71
|
+
* inside has told it how tall it is. The thumbnail is the
|
|
72
|
+
* document's own `image`, never the site's avatar, and only when its size is
|
|
73
|
+
* recorded: the spec requires the size with the URL, and a render never opens
|
|
74
|
+
* an image (decision-10). One wider or taller than the consumer allows is left
|
|
75
|
+
* out, because the spec says it must respect `maxwidth` and `maxheight` too.
|
|
76
|
+
*/
|
|
77
|
+
export declare function oEmbedFor(input: EmbedSubject & {
|
|
78
|
+
config: ImageConfig & {
|
|
79
|
+
baseUrl: string;
|
|
80
|
+
};
|
|
81
|
+
request: OEmbedRequest;
|
|
82
|
+
}): OEmbedRich;
|
|
83
|
+
/** The embed as a response in `format`, or a 304 to a client that already holds it. */
|
|
84
|
+
export declare function oEmbedResponse(embed: OEmbedRich, format: OEmbedFormat, conditional: ConditionalHeaders | undefined): Response;
|
|
85
|
+
/**
|
|
86
|
+
* The embed view's page: the card, a style to read it by, and the script that
|
|
87
|
+
* speaks WordPress's side of the embed.
|
|
88
|
+
*
|
|
89
|
+
* Nothing in it depends on who asked. There is no theme, no admin bar and no
|
|
90
|
+
* session, so a reader of a framing site sees the same card a signed-in admin
|
|
91
|
+
* would, and nothing about either. `noindex` because the post is the page to
|
|
92
|
+
* find, not its card.
|
|
93
|
+
*/
|
|
94
|
+
export declare function embedPage(subject: EmbedSubject): string;
|
|
95
|
+
/**
|
|
96
|
+
* The embed page as a response, or a 304 to a client that already holds it.
|
|
97
|
+
*
|
|
98
|
+
* Its policy is its own and complete: the one inline style and the one inline
|
|
99
|
+
* script by hash, nothing else loaded, and any origin may frame it. The
|
|
100
|
+
* caller marks the context frameable as well, so the baseline leaves off
|
|
101
|
+
* `X-Frame-Options`, which has no way to say "anyone".
|
|
102
|
+
*/
|
|
103
|
+
export declare function embedResponse(page: string, conditional: ConditionalHeaders | undefined): Response;
|
|
104
|
+
//# sourceMappingURL=oembed.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"oembed.d.ts","sourceRoot":"","sources":["../../src/web/oembed.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAEvD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAElD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAI7C,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAIzD;;;;;;GAMG;AAEH,0BAA0B;AAC1B,eAAO,MAAM,WAAW,qBAAqB,CAAC;AAE9C;;;;;;;;GAQG;AACH,eAAO,MAAM,UAAU,oBAAoB,CAAC;AAE5C,0DAA0D;AAC1D,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,KAAK,CAAC;AAe1C,mCAAmC;AACnC,MAAM,WAAW,aAAa;IAC5B,kCAAkC;IAClC,GAAG,EAAE,MAAM,CAAC;IACZ,6DAA6D;IAC7D,MAAM,EAAE,YAAY,GAAG,SAAS,CAAC;IACjC,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAChC;AAED,yFAAyF;AACzF,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,KAAK,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,MAAM,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,oFAAoF;AACpF,wBAAgB,aAAa,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,GAAG,aAAa,CAQxF;AAED,mFAAmF;AACnF,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,QAAQ,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,aAAa,GAAG,SAAS,CAAC;IAClC,MAAM,EAAE;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IAC5B,IAAI,EAAE,QAAQ,CAAC;CAChB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,SAAS,CACvB,KAAK,EAAE,YAAY,GAAG;IAAE,MAAM,EAAE,WAAW,GAAG;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IAAC,OAAO,EAAE,aAAa,CAAA;CAAE,GAC1F,UAAU,CA+CZ;AAED,uFAAuF;AACvF,wBAAgB,cAAc,CAC5B,KAAK,EAAE,UAAU,EACjB,MAAM,EAAE,YAAY,EACpB,WAAW,EAAE,kBAAkB,GAAG,SAAS,GAC1C,QAAQ,CAWV;AAED;;;;;;;;GAQG;AACH,wBAAgB,SAAS,CAAC,OAAO,EAAE,YAAY,GAAG,MAAM,CAkBvD;AAED;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,kBAAkB,GAAG,SAAS,GAAG,QAAQ,CAcjG"}
|
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { postLabel } from '../content/post-type.js';
|
|
3
|
+
import { siteLocale, siteTimezone } from './context.js';
|
|
4
|
+
import { feedExcerpt } from './feed-item.js';
|
|
5
|
+
import { escapeXml } from './feed-xml.js';
|
|
6
|
+
import { absoluteUrl, contentEtag, isNotModified } from './negotiate.js';
|
|
7
|
+
import { shareImage } from './share-image.js';
|
|
8
|
+
import { formatDate } from './templates.js';
|
|
9
|
+
/**
|
|
10
|
+
* The oEmbed provider (TASK-205): what WordPress, Discourse and the rest ask
|
|
11
|
+
* to turn a pasted post URL into a card.
|
|
12
|
+
*
|
|
13
|
+
* Under `/_geekity/`, the CMS's own prefix, so no permalink can take it. Each
|
|
14
|
+
* post and page links it from its head, with its own URL in the query.
|
|
15
|
+
*/
|
|
16
|
+
/** The endpoint's URL. */
|
|
17
|
+
export const OEMBED_PATH = '/_geekity/oembed';
|
|
18
|
+
/**
|
|
19
|
+
* The embed view's URL (TASK-208): the card alone as a page, with the
|
|
20
|
+
* document's absolute URL in `?url=`, and the one response on the site another
|
|
21
|
+
* origin may frame.
|
|
22
|
+
*
|
|
23
|
+
* WordPress keeps a rich embed from a provider it does not know only when its
|
|
24
|
+
* html holds an `<iframe>`, so the oEmbed html frames this page after the
|
|
25
|
+
* blockquote, the way WordPress's own provider does.
|
|
26
|
+
*/
|
|
27
|
+
export const EMBED_PATH = '/_geekity/embed';
|
|
28
|
+
const CONTENT_TYPES = {
|
|
29
|
+
json: 'application/json; charset=utf-8',
|
|
30
|
+
xml: 'text/xml; charset=utf-8',
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
33
|
+
* The size the card asks for. A rich embed must give both, and a blockquote
|
|
34
|
+
* of words reflows to whatever it is given, so these are what WordPress asks
|
|
35
|
+
* for its own cards rather than a measurement.
|
|
36
|
+
*/
|
|
37
|
+
const DEFAULT_WIDTH = 600;
|
|
38
|
+
const DEFAULT_HEIGHT = 338;
|
|
39
|
+
/** A query as a request. A missing `format` is JSON, which every consumer reads. */
|
|
40
|
+
export function oEmbedRequest(query) {
|
|
41
|
+
const format = query('format') ?? 'json';
|
|
42
|
+
return {
|
|
43
|
+
url: query('url') ?? '',
|
|
44
|
+
format: format === 'json' || format === 'xml' ? format : undefined,
|
|
45
|
+
maxwidth: positiveInteger(query('maxwidth')),
|
|
46
|
+
maxheight: positiveInteger(query('maxheight')),
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* The embed of one published document, served at `href`.
|
|
51
|
+
*
|
|
52
|
+
* The title is the document's, else a note's first words. The html is the
|
|
53
|
+
* card, then a hidden sandboxed frame of {@link EMBED_PATH}: a consumer that
|
|
54
|
+
* keeps the html as given shows the blockquote, one that strips iframes still
|
|
55
|
+
* has it, and WordPress, which keeps only the frame, shows that once the page
|
|
56
|
+
* inside has told it how tall it is. The thumbnail is the
|
|
57
|
+
* document's own `image`, never the site's avatar, and only when its size is
|
|
58
|
+
* recorded: the spec requires the size with the URL, and a render never opens
|
|
59
|
+
* an image (decision-10). One wider or taller than the consumer allows is left
|
|
60
|
+
* out, because the spec says it must respect `maxwidth` and `maxheight` too.
|
|
61
|
+
*/
|
|
62
|
+
export function oEmbedFor(input) {
|
|
63
|
+
const { document, author, config, site, request } = input;
|
|
64
|
+
const url = (pathname) => absoluteUrl(pathname, config.baseUrl);
|
|
65
|
+
const title = postLabel(document);
|
|
66
|
+
const authorUrl = author?.url === undefined ? undefined : url(author.url);
|
|
67
|
+
const width = Math.min(DEFAULT_WIDTH, request.maxwidth ?? DEFAULT_WIDTH);
|
|
68
|
+
const height = Math.min(DEFAULT_HEIGHT, request.maxheight ?? DEFAULT_HEIGHT);
|
|
69
|
+
const image = shareImage({
|
|
70
|
+
config,
|
|
71
|
+
image: document.extra['image'],
|
|
72
|
+
imageAlt: document.extra['imageAlt'],
|
|
73
|
+
title,
|
|
74
|
+
avatar: undefined,
|
|
75
|
+
owner: site.title,
|
|
76
|
+
});
|
|
77
|
+
const thumbnail = image?.size !== undefined &&
|
|
78
|
+
image.size.width <= (request.maxwidth ?? Infinity) &&
|
|
79
|
+
image.size.height <= (request.maxheight ?? Infinity)
|
|
80
|
+
? { url: url(image.url), ...image.size }
|
|
81
|
+
: undefined;
|
|
82
|
+
return {
|
|
83
|
+
version: '1.0',
|
|
84
|
+
type: 'rich',
|
|
85
|
+
title,
|
|
86
|
+
...(author === undefined ? {} : { author_name: author.name }),
|
|
87
|
+
...(authorUrl === undefined ? {} : { author_url: authorUrl }),
|
|
88
|
+
provider_name: site.title,
|
|
89
|
+
provider_url: url('/'),
|
|
90
|
+
html: `${embedCard(input)}${embedFrame({
|
|
91
|
+
src: url(`${EMBED_PATH}?${new URLSearchParams({ url: url(input.href) }).toString()}`),
|
|
92
|
+
title,
|
|
93
|
+
width,
|
|
94
|
+
height,
|
|
95
|
+
})}`,
|
|
96
|
+
width,
|
|
97
|
+
height,
|
|
98
|
+
...(thumbnail === undefined
|
|
99
|
+
? {}
|
|
100
|
+
: {
|
|
101
|
+
thumbnail_url: thumbnail.url,
|
|
102
|
+
thumbnail_width: thumbnail.width,
|
|
103
|
+
thumbnail_height: thumbnail.height,
|
|
104
|
+
}),
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
/** The embed as a response in `format`, or a 304 to a client that already holds it. */
|
|
108
|
+
export function oEmbedResponse(embed, format, conditional) {
|
|
109
|
+
const body = format === 'json' ? JSON.stringify(embed) : oEmbedXml(embed);
|
|
110
|
+
const etag = contentEtag(`oembed-${format}`, body);
|
|
111
|
+
const headers = new Headers({ etag, 'cache-control': 'no-cache' });
|
|
112
|
+
if (isNotModified(conditional, etag, undefined)) {
|
|
113
|
+
return new Response(null, { status: 304, headers });
|
|
114
|
+
}
|
|
115
|
+
headers.set('content-type', CONTENT_TYPES[format]);
|
|
116
|
+
return new Response(body, { headers });
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* The embed view's page: the card, a style to read it by, and the script that
|
|
120
|
+
* speaks WordPress's side of the embed.
|
|
121
|
+
*
|
|
122
|
+
* Nothing in it depends on who asked. There is no theme, no admin bar and no
|
|
123
|
+
* session, so a reader of a framing site sees the same card a signed-in admin
|
|
124
|
+
* would, and nothing about either. `noindex` because the post is the page to
|
|
125
|
+
* find, not its card.
|
|
126
|
+
*/
|
|
127
|
+
export function embedPage(subject) {
|
|
128
|
+
return [
|
|
129
|
+
'<!doctype html>',
|
|
130
|
+
`<html lang="${escapeXml(siteLocale(subject.site))}">`,
|
|
131
|
+
'<head>',
|
|
132
|
+
'<meta charset="utf-8">',
|
|
133
|
+
'<meta name="viewport" content="width=device-width, initial-scale=1">',
|
|
134
|
+
'<meta name="robots" content="noindex">',
|
|
135
|
+
`<title>${escapeXml(postLabel(subject.document))}</title>`,
|
|
136
|
+
`<style>${EMBED_STYLE}</style>`,
|
|
137
|
+
'</head>',
|
|
138
|
+
'<body>',
|
|
139
|
+
embedCard(subject),
|
|
140
|
+
`<script>${EMBED_SCRIPT}</script>`,
|
|
141
|
+
'</body>',
|
|
142
|
+
'</html>',
|
|
143
|
+
'',
|
|
144
|
+
].join('\n');
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* The embed page as a response, or a 304 to a client that already holds it.
|
|
148
|
+
*
|
|
149
|
+
* Its policy is its own and complete: the one inline style and the one inline
|
|
150
|
+
* script by hash, nothing else loaded, and any origin may frame it. The
|
|
151
|
+
* caller marks the context frameable as well, so the baseline leaves off
|
|
152
|
+
* `X-Frame-Options`, which has no way to say "anyone".
|
|
153
|
+
*/
|
|
154
|
+
export function embedResponse(page, conditional) {
|
|
155
|
+
const etag = contentEtag('embed', page);
|
|
156
|
+
const headers = new Headers({
|
|
157
|
+
etag,
|
|
158
|
+
'cache-control': 'no-cache',
|
|
159
|
+
'content-security-policy': EMBED_CONTENT_SECURITY_POLICY,
|
|
160
|
+
});
|
|
161
|
+
if (isNotModified(conditional, etag, undefined)) {
|
|
162
|
+
return new Response(null, { status: 304, headers });
|
|
163
|
+
}
|
|
164
|
+
headers.set('content-type', 'text/html; charset=utf-8');
|
|
165
|
+
return new Response(page, { headers });
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* The card's look inside the frame. Inline, like the script, because the page
|
|
169
|
+
* loads nothing: one request per embed is all a framing page should cost.
|
|
170
|
+
*/
|
|
171
|
+
const EMBED_STYLE = [
|
|
172
|
+
'html{color-scheme:light}',
|
|
173
|
+
'body{margin:0;background:#fff;color:#1f1f1f;font:16px/1.5 system-ui,-apple-system,"Segoe UI",sans-serif}',
|
|
174
|
+
'.geekity-embed{margin:0;padding:16px 20px;border:1px solid #d6d6d6;border-radius:8px;overflow-wrap:anywhere}',
|
|
175
|
+
'.geekity-embed p{margin:0 0 8px}',
|
|
176
|
+
'.geekity-embed p:last-child{margin:0;font-size:14px;color:#595959}',
|
|
177
|
+
'.geekity-embed a{color:#0b57d0}',
|
|
178
|
+
].join('');
|
|
179
|
+
/**
|
|
180
|
+
* WordPress's embed protocol, the framed side (wp-includes/js/wp-embed.js is
|
|
181
|
+
* the other).
|
|
182
|
+
*
|
|
183
|
+
* WordPress puts a secret in the frame's URL as `#?secret=` and keeps the
|
|
184
|
+
* frame hidden until a message with that secret says how tall the page is.
|
|
185
|
+
* The page says so when it loads, when it is resized, and whenever the host
|
|
186
|
+
* says it is ready. Its frame is sandboxed with scripts only, so a link cannot
|
|
187
|
+
* navigate by itself: a click is sent to the host as a `link` message, which
|
|
188
|
+
* WordPress follows when the link is on this site's host. Opened directly
|
|
189
|
+
* there is no secret, and the links are ordinary links.
|
|
190
|
+
*/
|
|
191
|
+
const EMBED_SCRIPT = [
|
|
192
|
+
'(function(){',
|
|
193
|
+
'var found=/[?&]secret=([A-Za-z0-9]+)/.exec(location.hash);',
|
|
194
|
+
'if(!found||window.parent===window)return;',
|
|
195
|
+
'var secret=found[1];',
|
|
196
|
+
"function send(message,value){window.parent.postMessage({message:message,value:value,secret:secret},'*');}",
|
|
197
|
+
"function height(){send('height',Math.ceil(document.documentElement.getBoundingClientRect().height));}",
|
|
198
|
+
"addEventListener('load',height);",
|
|
199
|
+
"addEventListener('resize',height);",
|
|
200
|
+
"addEventListener('message',function(event){var data=event.data;if(data&&data.message==='ready'&&data.secret===secret)height();});",
|
|
201
|
+
"document.addEventListener('click',function(event){var link=event.target instanceof Element?event.target.closest('a[href]'):null;if(!link)return;event.preventDefault();send('link',link.href);});",
|
|
202
|
+
'})();',
|
|
203
|
+
].join('');
|
|
204
|
+
/** A CSP source naming one inline element's exact contents. */
|
|
205
|
+
function inlineHash(contents) {
|
|
206
|
+
return `'sha256-${createHash('sha256').update(contents, 'utf8').digest('base64')}'`;
|
|
207
|
+
}
|
|
208
|
+
const EMBED_CONTENT_SECURITY_POLICY = [
|
|
209
|
+
"default-src 'none'",
|
|
210
|
+
`style-src ${inlineHash(EMBED_STYLE)}`,
|
|
211
|
+
`script-src ${inlineHash(EMBED_SCRIPT)}`,
|
|
212
|
+
"base-uri 'none'",
|
|
213
|
+
"form-action 'none'",
|
|
214
|
+
'frame-ancestors *',
|
|
215
|
+
].join('; ');
|
|
216
|
+
/**
|
|
217
|
+
* The frame of the embed view, in the shape WordPress's own provider sends:
|
|
218
|
+
* sandboxed, borderless, and hidden until the page inside reports its height.
|
|
219
|
+
*/
|
|
220
|
+
function embedFrame(input) {
|
|
221
|
+
return [
|
|
222
|
+
'<iframe sandbox="allow-scripts" security="restricted"',
|
|
223
|
+
` src="${escapeXml(input.src)}"`,
|
|
224
|
+
` width="${String(input.width)}" height="${String(input.height)}"`,
|
|
225
|
+
` title="${escapeXml(input.title)}"`,
|
|
226
|
+
' frameborder="0" marginwidth="0" marginheight="0" scrolling="no"',
|
|
227
|
+
' style="position: absolute; visibility: hidden;"></iframe>',
|
|
228
|
+
].join('');
|
|
229
|
+
}
|
|
230
|
+
/** The XML spelling: one element per field, under `<oembed>`. */
|
|
231
|
+
function oEmbedXml(embed) {
|
|
232
|
+
const fields = Object.entries(embed).map(([name, value]) => ` <${name}>${escapeXml(String(value))}</${name}>`);
|
|
233
|
+
return [
|
|
234
|
+
'<?xml version="1.0" encoding="utf-8" standalone="yes"?>',
|
|
235
|
+
'<oembed>',
|
|
236
|
+
...fields,
|
|
237
|
+
'</oembed>',
|
|
238
|
+
'',
|
|
239
|
+
].join('\n');
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* The card of one document, as both the oEmbed html and the embed page carry
|
|
243
|
+
* it. A blockquote of escaped text and links, so it needs nothing from this
|
|
244
|
+
* site to draw and can carry nothing a consumer would run.
|
|
245
|
+
*/
|
|
246
|
+
function embedCard(subject) {
|
|
247
|
+
const { document, author, site, config } = subject;
|
|
248
|
+
const url = (pathname) => absoluteUrl(pathname, config.baseUrl);
|
|
249
|
+
return card({
|
|
250
|
+
href: url(subject.href),
|
|
251
|
+
title: postLabel(document),
|
|
252
|
+
excerpt: document.title === '' ? '' : feedExcerpt(document),
|
|
253
|
+
author: author === undefined
|
|
254
|
+
? undefined
|
|
255
|
+
: { name: author.name, url: author.url === undefined ? undefined : url(author.url) },
|
|
256
|
+
date: {
|
|
257
|
+
iso: formatDate(document.date, 'iso'),
|
|
258
|
+
readable: formatDate(document.date, 'readable', siteTimezone(site), siteLocale(site)),
|
|
259
|
+
},
|
|
260
|
+
site: { name: site.title, url: url('/') },
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
/** The card: a linked title, the excerpt, who and when, and the site it is from. */
|
|
264
|
+
function card(input) {
|
|
265
|
+
const link = (href, text) => `<a href="${escapeXml(href)}">${escapeXml(text)}</a>`;
|
|
266
|
+
const { author, date } = input;
|
|
267
|
+
const byline = [
|
|
268
|
+
...(author === undefined
|
|
269
|
+
? []
|
|
270
|
+
: [author.url === undefined ? escapeXml(author.name) : link(author.url, author.name)]),
|
|
271
|
+
...(date.iso === ''
|
|
272
|
+
? []
|
|
273
|
+
: [`<time datetime="${escapeXml(date.iso)}">${escapeXml(date.readable)}</time>`]),
|
|
274
|
+
];
|
|
275
|
+
return [
|
|
276
|
+
`<blockquote class="geekity-embed" cite="${escapeXml(input.href)}">`,
|
|
277
|
+
`<p><strong>${link(input.href, input.title)}</strong></p>`,
|
|
278
|
+
...(input.excerpt === '' ? [] : [`<p>${escapeXml(input.excerpt)}</p>`]),
|
|
279
|
+
...(byline.length === 0 ? [] : [`<p>${byline.join(', ')}</p>`]),
|
|
280
|
+
`<p>${link(input.site.url, input.site.name)}</p>`,
|
|
281
|
+
'</blockquote>',
|
|
282
|
+
].join('');
|
|
283
|
+
}
|
|
284
|
+
function positiveInteger(value) {
|
|
285
|
+
if (value === undefined || !/^[0-9]+$/.test(value))
|
|
286
|
+
return undefined;
|
|
287
|
+
const parsed = Number(value);
|
|
288
|
+
return parsed > 0 ? parsed : undefined;
|
|
289
|
+
}
|
|
290
|
+
//# sourceMappingURL=oembed.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"oembed.js","sourceRoot":"","sources":["../../src/web/oembed.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { ImageConfig } from '../images/paths.ts';
|
|
2
|
+
import type { ConditionalHeaders } from './negotiate.ts';
|
|
3
|
+
/**
|
|
4
|
+
* The OpenSearch description (TASK-204): what a browser reads to offer the
|
|
5
|
+
* site's own search from its address bar.
|
|
6
|
+
*
|
|
7
|
+
* A fixed route at the root for the reason the manifest is one, so no
|
|
8
|
+
* permalink can take it, and linked from the head of every page.
|
|
9
|
+
*/
|
|
10
|
+
/** The description's URL. */
|
|
11
|
+
export declare const OPENSEARCH_PATH = "/opensearch.xml";
|
|
12
|
+
/**
|
|
13
|
+
* The description of one site, as XML.
|
|
14
|
+
*
|
|
15
|
+
* The short name is the title cut to 16 characters, and the description is
|
|
16
|
+
* the tagline, else the title, since the spec requires one. The image is
|
|
17
|
+
* `/favicon.ico`, whose 16 pixel frame is the size the spec suggests, and is
|
|
18
|
+
* left out on a site with no icon, where that URL answers 404. Every URL is
|
|
19
|
+
* absolute, because a browser keeps the template and runs it from its address
|
|
20
|
+
* bar, with no page to resolve a relative one against.
|
|
21
|
+
*/
|
|
22
|
+
export declare function openSearchDescription(input: {
|
|
23
|
+
config: ImageConfig & {
|
|
24
|
+
baseUrl: string;
|
|
25
|
+
};
|
|
26
|
+
site: {
|
|
27
|
+
title: string;
|
|
28
|
+
tagline?: string | undefined;
|
|
29
|
+
icon?: unknown;
|
|
30
|
+
avatar?: unknown;
|
|
31
|
+
};
|
|
32
|
+
}): string;
|
|
33
|
+
/** The description as a response, or a 304 to a client that already holds it. */
|
|
34
|
+
export declare function openSearchResponse(body: string, conditional: ConditionalHeaders | undefined): Response;
|
|
35
|
+
//# sourceMappingURL=opensearch.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"opensearch.d.ts","sourceRoot":"","sources":["../../src/web/opensearch.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAItD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAGzD;;;;;;GAMG;AAEH,6BAA6B;AAC7B,eAAO,MAAM,eAAe,oBAAoB,CAAC;AAQjD;;;;;;;;;GASG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE;IAC3C,MAAM,EAAE,WAAW,GAAG;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IAC1C,IAAI,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QAAC,IAAI,CAAC,EAAE,OAAO,CAAC;QAAC,MAAM,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC;CACzF,GAAG,MAAM,CAsBT;AAED,iFAAiF;AACjF,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,MAAM,EACZ,WAAW,EAAE,kBAAkB,GAAG,SAAS,GAC1C,QAAQ,CAUV"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { iconSetting, siteIconSource } from '../images/icons.js';
|
|
2
|
+
import { FAVICON_PATH } from './manifest.js';
|
|
3
|
+
import { escapeXml } from './feed-xml.js';
|
|
4
|
+
import { absoluteUrl, contentEtag, isNotModified } from './negotiate.js';
|
|
5
|
+
import { SEARCH_PATH } from './search.js';
|
|
6
|
+
/**
|
|
7
|
+
* The OpenSearch description (TASK-204): what a browser reads to offer the
|
|
8
|
+
* site's own search from its address bar.
|
|
9
|
+
*
|
|
10
|
+
* A fixed route at the root for the reason the manifest is one, so no
|
|
11
|
+
* permalink can take it, and linked from the head of every page.
|
|
12
|
+
*/
|
|
13
|
+
/** The description's URL. */
|
|
14
|
+
export const OPENSEARCH_PATH = '/opensearch.xml';
|
|
15
|
+
const OPENSEARCH_CONTENT_TYPE = 'application/opensearchdescription+xml; charset=utf-8';
|
|
16
|
+
/** OpenSearch 1.1 caps these two, and a browser may refuse a longer one. */
|
|
17
|
+
const SHORT_NAME_MAX = 16;
|
|
18
|
+
const DESCRIPTION_MAX = 1024;
|
|
19
|
+
/**
|
|
20
|
+
* The description of one site, as XML.
|
|
21
|
+
*
|
|
22
|
+
* The short name is the title cut to 16 characters, and the description is
|
|
23
|
+
* the tagline, else the title, since the spec requires one. The image is
|
|
24
|
+
* `/favicon.ico`, whose 16 pixel frame is the size the spec suggests, and is
|
|
25
|
+
* left out on a site with no icon, where that URL answers 404. Every URL is
|
|
26
|
+
* absolute, because a browser keeps the template and runs it from its address
|
|
27
|
+
* bar, with no page to resolve a relative one against.
|
|
28
|
+
*/
|
|
29
|
+
export function openSearchDescription(input) {
|
|
30
|
+
const { config, site } = input;
|
|
31
|
+
const url = (pathname) => absoluteUrl(pathname, config.baseUrl);
|
|
32
|
+
const tagline = site.tagline?.trim() ?? '';
|
|
33
|
+
const hasIcon = siteIconSource(config, iconSetting(site)) !== undefined;
|
|
34
|
+
const lines = [
|
|
35
|
+
'<?xml version="1.0" encoding="UTF-8"?>',
|
|
36
|
+
'<OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/">',
|
|
37
|
+
` <ShortName>${escapeXml(cut(site.title, SHORT_NAME_MAX))}</ShortName>`,
|
|
38
|
+
` <Description>${escapeXml(cut(tagline || site.title, DESCRIPTION_MAX))}</Description>`,
|
|
39
|
+
' <InputEncoding>UTF-8</InputEncoding>',
|
|
40
|
+
...(hasIcon
|
|
41
|
+
? [
|
|
42
|
+
` <Image width="16" height="16" type="image/x-icon">${escapeXml(url(FAVICON_PATH))}</Image>`,
|
|
43
|
+
]
|
|
44
|
+
: []),
|
|
45
|
+
` <Url type="text/html" method="get" template="${escapeXml(url(SEARCH_PATH))}?q={searchTerms}"/>`,
|
|
46
|
+
` <Url type="application/opensearchdescription+xml" rel="self" template="${escapeXml(url(OPENSEARCH_PATH))}"/>`,
|
|
47
|
+
'</OpenSearchDescription>',
|
|
48
|
+
];
|
|
49
|
+
return `${lines.join('\n')}\n`;
|
|
50
|
+
}
|
|
51
|
+
/** The description as a response, or a 304 to a client that already holds it. */
|
|
52
|
+
export function openSearchResponse(body, conditional) {
|
|
53
|
+
const etag = contentEtag('opensearch', body);
|
|
54
|
+
const headers = new Headers({ etag, 'cache-control': 'no-cache' });
|
|
55
|
+
if (isNotModified(conditional, etag, undefined)) {
|
|
56
|
+
return new Response(null, { status: 304, headers });
|
|
57
|
+
}
|
|
58
|
+
headers.set('content-type', OPENSEARCH_CONTENT_TYPE);
|
|
59
|
+
return new Response(body, { headers });
|
|
60
|
+
}
|
|
61
|
+
/** `text` cut to at most `max` characters, never inside a surrogate pair. */
|
|
62
|
+
function cut(text, max) {
|
|
63
|
+
return [...text].slice(0, max).join('').trimEnd();
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=opensearch.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"opensearch.js","sourceRoot":"","sources":["../../src/web/opensearch.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAEjE,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAC7C,OAAO,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAC1C,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAEzE,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE1C;;;;;;GAMG;AAEH,6BAA6B;AAC7B,MAAM,CAAC,MAAM,eAAe,GAAG,iBAAiB,CAAC;AAEjD,MAAM,uBAAuB,GAAG,sDAAsD,CAAC;AAEvF,4EAA4E;AAC5E,MAAM,cAAc,GAAG,EAAE,CAAC;AAC1B,MAAM,eAAe,GAAG,IAAI,CAAC;AAE7B;;;;;;;;;GASG;AACH,MAAM,UAAU,qBAAqB,CAAC,KAGrC;IACC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,KAAK,CAAC;IAC/B,MAAM,GAAG,GAAG,CAAC,QAAgB,EAAU,EAAE,CAAC,WAAW,CAAC,QAAQ,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC;IAChF,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;IAC3C,MAAM,OAAO,GAAG,cAAc,CAAC,MAAM,EAAE,WAAW,CAAC,IAAI,CAAC,CAAC,KAAK,SAAS,CAAC;IAExE,MAAM,KAAK,GAAG;QACZ,wCAAwC;QACxC,sEAAsE;QACtE,gBAAgB,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,EAAE,cAAc,CAAC,CAAC,cAAc;QACxE,kBAAkB,SAAS,CAAC,GAAG,CAAC,OAAO,IAAI,IAAI,CAAC,KAAK,EAAE,eAAe,CAAC,CAAC,gBAAgB;QACxF,wCAAwC;QACxC,GAAG,CAAC,OAAO;YACT,CAAC,CAAC;gBACE,uDAAuD,SAAS,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC,UAAU;aAC9F;YACH,CAAC,CAAC,EAAE,CAAC;QACP,kDAAkD,SAAS,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC,qBAAqB;QAClG,4EAA4E,SAAS,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC,KAAK;QAChH,0BAA0B;KAC3B,CAAC;IACF,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC;AACjC,CAAC;AAED,iFAAiF;AACjF,MAAM,UAAU,kBAAkB,CAChC,IAAY,EACZ,WAA2C;IAE3C,MAAM,IAAI,GAAG,WAAW,CAAC,YAAY,EAAE,IAAI,CAAC,CAAC;IAC7C,MAAM,OAAO,GAAG,IAAI,OAAO,CAAC,EAAE,IAAI,EAAE,eAAe,EAAE,UAAU,EAAE,CAAC,CAAC;IAEnE,IAAI,aAAa,CAAC,WAAW,EAAE,IAAI,EAAE,SAAS,CAAC,EAAE,CAAC;QAChD,OAAO,IAAI,QAAQ,CAAC,IAAI,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,CAAC,CAAC;IACtD,CAAC;IAED,OAAO,CAAC,GAAG,CAAC,cAAc,EAAE,uBAAuB,CAAC,CAAC;IACrD,OAAO,IAAI,QAAQ,CAAC,IAAI,EAAE,EAAE,OAAO,EAAE,CAAC,CAAC;AACzC,CAAC;AAED,6EAA6E;AAC7E,SAAS,GAAG,CAAC,IAAY,EAAE,GAAW;IACpC,OAAO,CAAC,GAAG,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC;AACpD,CAAC"}
|
package/dist/web/render.d.ts
CHANGED
|
@@ -10,7 +10,7 @@ import type { Conversation } from './conversation.ts';
|
|
|
10
10
|
import type { FrontPageSlugs, SiteData } from './context.ts';
|
|
11
11
|
import type { Pagination } from './pagination.ts';
|
|
12
12
|
import type { TaxonomyBases, TaxonomyRedirect } from './taxonomy.ts';
|
|
13
|
-
import type { ThemeSource } from './themes.ts';
|
|
13
|
+
import type { ThemeColors, ThemeSource } from './themes.ts';
|
|
14
14
|
import type { ReplyContext } from '../webmention/reply-context.ts';
|
|
15
15
|
/** Templates the default theme ships and the public routes ask for by name. */
|
|
16
16
|
export declare const TEMPLATES: {
|
|
@@ -168,6 +168,11 @@ export interface Renderer {
|
|
|
168
168
|
* the page that links to it can never come from two different themes.
|
|
169
169
|
*/
|
|
170
170
|
themeDirs(): readonly string[];
|
|
171
|
+
/**
|
|
172
|
+
* The colours of the theme a render wears right now, gap-filled from the
|
|
173
|
+
* packaged theme: what the head declares and the web manifest repeats.
|
|
174
|
+
*/
|
|
175
|
+
themeColors(): ThemeColors;
|
|
171
176
|
/** The Nunjucks environment, for a site that wants to add its own filters. */
|
|
172
177
|
readonly environment: Environment;
|
|
173
178
|
}
|
package/dist/web/render.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"render.d.ts","sourceRoot":"","sources":["../../src/web/render.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAE5C,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,sBAAsB,CAAC;AACjD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AACnD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAGvD,OAAO,KAAK,EAAE,kBAAkB,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"render.d.ts","sourceRoot":"","sources":["../../src/web/render.ts"],"names":[],"mappings":"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"}
|