@geekity/cms 0.5.0 → 0.6.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 +24 -14
- package/admin/pages/navigation/menus.njk +8 -5
- package/admin/pages/users/edit.njk +25 -2
- package/admin/static/admin.css +18 -3
- package/dist/admin/accounts.d.ts +10 -0
- package/dist/admin/accounts.d.ts.map +1 -1
- package/dist/admin/accounts.js +15 -3
- package/dist/admin/accounts.js.map +1 -1
- package/dist/admin/users.d.ts +24 -0
- package/dist/admin/users.d.ts.map +1 -1
- package/dist/admin/users.js +102 -30
- package/dist/admin/users.js.map +1 -1
- package/dist/federation/mount.d.ts.map +1 -1
- package/dist/federation/mount.js +16 -3
- package/dist/federation/mount.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/web/authors.d.ts +15 -1
- package/dist/web/authors.d.ts.map +1 -1
- package/dist/web/authors.js +9 -1
- package/dist/web/authors.js.map +1 -1
- package/dist/web/index.d.ts +3 -3
- package/dist/web/index.d.ts.map +1 -1
- package/dist/web/index.js +1 -1
- package/dist/web/index.js.map +1 -1
- package/dist/web/navigation.d.ts +106 -28
- package/dist/web/navigation.d.ts.map +1 -1
- package/dist/web/navigation.js +135 -49
- package/dist/web/navigation.js.map +1 -1
- package/package.json +1 -1
- package/themes/default/README.md +25 -10
- package/themes/default/partials/bio.njk +5 -1
- package/themes/default/partials/menu.njk +6 -4
- package/themes/default/static/style.css +28 -10
package/dist/web/navigation.d.ts
CHANGED
|
@@ -5,17 +5,64 @@ import type { SiteData } from './context.ts';
|
|
|
5
5
|
*/
|
|
6
6
|
export declare const DEFAULT_MENU_NAME = "primary";
|
|
7
7
|
/**
|
|
8
|
-
*
|
|
9
|
-
*
|
|
8
|
+
* What one `rel` value may be spelled with: letters and dashes, which is what
|
|
9
|
+
* every HTML link type is spelled with.
|
|
10
10
|
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
11
|
+
* Deliberately not a list of the values this CMS knows about (TASK-114). The
|
|
12
|
+
* trailing part of a link line is whatever `rel` values the person typed —
|
|
13
|
+
* `me`, `nofollow`, `author`, `noopener`, one somebody's own reader looks for
|
|
14
|
+
* — and all of them reach the rendered attribute. A closed list would mean
|
|
15
|
+
* every value anybody wanted had to be added here first, and `me` was never a
|
|
16
|
+
* flag in the first place: it is a `rel` value that this CMS happened to be
|
|
17
|
+
* the only one to know.
|
|
15
18
|
*/
|
|
16
|
-
export declare const
|
|
17
|
-
/**
|
|
18
|
-
|
|
19
|
+
export declare const REL_VALUE_PATTERN: RegExp;
|
|
20
|
+
/**
|
|
21
|
+
* What a link line's trailing part may be, in the words both boxes explain it
|
|
22
|
+
* in, beside {@link LINK_URL_RULE}.
|
|
23
|
+
*/
|
|
24
|
+
export declare const LINK_REL_RULE = "the rel values the link carries, a word each \u2014 \"| me\", \"| nofollow noopener\"";
|
|
25
|
+
/**
|
|
26
|
+
* One bar-separated part as the `rel` values it names, or `undefined` when it
|
|
27
|
+
* is not a list of them.
|
|
28
|
+
*
|
|
29
|
+
* Every word has to be one: `nofollow noopener` is a list, `a=1|2`'s `2` is
|
|
30
|
+
* not, and that is what keeps a bar inside a URL a bar. An empty part names no
|
|
31
|
+
* values rather than an empty list, because `About | /about/ |` is a line with
|
|
32
|
+
* a trailing bar, not a line with a rel.
|
|
33
|
+
*/
|
|
34
|
+
export declare function relValuesOf(text: string): string[] | undefined;
|
|
35
|
+
/**
|
|
36
|
+
* Some `rel` values as one attribute holds them: lower case, each said once,
|
|
37
|
+
* in the order they were first typed.
|
|
38
|
+
*
|
|
39
|
+
* A theme prints this string and nothing else, so it never joins a list itself
|
|
40
|
+
* and never emits two `rel` attributes on one link. Lower case is what makes
|
|
41
|
+
* saying a value twice — `me` typed on a profile link that already carries it
|
|
42
|
+
* — a no-op rather than `rel="me ME"`.
|
|
43
|
+
*/
|
|
44
|
+
export declare function relText(values: Iterable<string>): string;
|
|
45
|
+
/** A link line split at the point where its `rel` values begin. */
|
|
46
|
+
export interface LinkLine {
|
|
47
|
+
/** The line with its trailing `rel` values taken off: the label and the URL. */
|
|
48
|
+
text: string;
|
|
49
|
+
/** The values that were typed, as one attribute holds them. Empty for none. */
|
|
50
|
+
rel: string;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* One typed link line, with the `rel` values taken off the end of it.
|
|
54
|
+
*
|
|
55
|
+
* The values come off the right, one bar-separated part at a time, and only
|
|
56
|
+
* while the last part is a list of rel values — so `Mastodon |
|
|
57
|
+
* https://example.social/@me | me | nofollow` and `... | me nofollow` are the
|
|
58
|
+
* same line, and `Odd | /odd/?a=1|2` keeps its bar because `2` is not one.
|
|
59
|
+
*
|
|
60
|
+
* The one place a link line is split, for the reason the URL rule is worded
|
|
61
|
+
* once (TASK-114): the menu box on the Navigation screen and the Links box on
|
|
62
|
+
* a profile take the same line, so somebody who learns one box has learned the
|
|
63
|
+
* other.
|
|
64
|
+
*/
|
|
65
|
+
export declare function splitLinkRel(line: string): LinkLine;
|
|
19
66
|
/**
|
|
20
67
|
* What a menu name may be spelled with.
|
|
21
68
|
*
|
|
@@ -51,16 +98,17 @@ export interface NavigationItem {
|
|
|
51
98
|
/** Where it goes: a site-root path, or an absolute URL for somewhere else. */
|
|
52
99
|
url: string;
|
|
53
100
|
/**
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
*
|
|
101
|
+
* What the link's `rel` attribute says, as one string holding every value:
|
|
102
|
+
* `me` for a profile that is you, `nofollow noopener` for somewhere you are
|
|
103
|
+
* only pointing at, whatever else a reader of this site looks for.
|
|
57
104
|
*
|
|
58
|
-
* Present only when
|
|
59
|
-
*
|
|
60
|
-
*
|
|
61
|
-
*
|
|
105
|
+
* Present only when there is something to say, so a `site.json` holding a
|
|
106
|
+
* plain `{ label, url }` is a complete item rather than one missing a key.
|
|
107
|
+
* Already lower case and deduplicated ({@link relText}), because a theme
|
|
108
|
+
* prints it as it stands and no theme should be joining a list or emitting
|
|
109
|
+
* two `rel` attributes on one link.
|
|
62
110
|
*/
|
|
63
|
-
|
|
111
|
+
rel?: string;
|
|
64
112
|
}
|
|
65
113
|
/** Every menu one site stores, by name. */
|
|
66
114
|
export type NavigationMenus = Record<string, NavigationItem[]>;
|
|
@@ -148,10 +196,15 @@ export declare function menusOf(value: unknown): NavigationMenus;
|
|
|
148
196
|
* One menu's stored value as the items it names, ignoring anything that is not
|
|
149
197
|
* one.
|
|
150
198
|
*
|
|
151
|
-
*
|
|
152
|
-
*
|
|
153
|
-
*
|
|
154
|
-
*
|
|
199
|
+
* `rel` is read through {@link relText}, so a file that wrote it in capitals
|
|
200
|
+
* or said a value twice renders one attribute either way, and a `rel` that is
|
|
201
|
+
* not a string is an item with none rather than an item dropped.
|
|
202
|
+
*
|
|
203
|
+
* `me: true` is still read, as `rel: "me"`. It is how every marked item was
|
|
204
|
+
* spelled before the trailing part of a line was a list of rel values
|
|
205
|
+
* (TASK-107, TASK-114), and a site that had set the only flag there was must
|
|
206
|
+
* not lose it on upgrade; the Navigation screen writes `rel` the next time
|
|
207
|
+
* that menu is saved. Only the literal `true` counts, the way it always did.
|
|
155
208
|
*/
|
|
156
209
|
export declare function navigationItemsOf(value: unknown): NavigationItem[];
|
|
157
210
|
/**
|
|
@@ -161,13 +214,9 @@ export declare function navigationItemsOf(value: unknown): NavigationItem[];
|
|
|
161
214
|
* `about/` would be resolved against whatever page it was printed on, which is
|
|
162
215
|
* never what a menu means.
|
|
163
216
|
*
|
|
164
|
-
* A line may end in the
|
|
165
|
-
* https://example.social/@me | me`
|
|
166
|
-
*
|
|
167
|
-
* actually has ({@link MENU_ITEM_FLAGS}). That is what lets a URL still hold a
|
|
168
|
-
* bar, a query string say: a trailing `| elsewhere` is not a flag, so it stays
|
|
169
|
-
* part of the URL and the line is refused for not naming one, rather than
|
|
170
|
-
* being quietly read as a flag nobody asked for.
|
|
217
|
+
* A line may end in the `rel` values the link carries — `Mastodon |
|
|
218
|
+
* https://example.social/@me | me`, `A source | https://example.com/thing |
|
|
219
|
+
* nofollow noopener` — which {@link splitLinkRel} takes off the end first.
|
|
171
220
|
*
|
|
172
221
|
* What is left splits at its first bar, so a label still cannot hold one.
|
|
173
222
|
*/
|
|
@@ -192,4 +241,33 @@ export declare function menuItemsText(items: readonly NavigationItem[]): string;
|
|
|
192
241
|
* half of the sentence.
|
|
193
242
|
*/
|
|
194
243
|
export declare function menuItemLineProblem(value: string): string | undefined;
|
|
244
|
+
/**
|
|
245
|
+
* What a URL typed into a link box may be, in the words both boxes explain it
|
|
246
|
+
* in: the menu on the Navigation screen and the Links on a user's profile.
|
|
247
|
+
*
|
|
248
|
+
* One sentence fragment rather than two, so the rule {@link isLinkUrl} applies
|
|
249
|
+
* is worded the same wherever somebody is told about it (TASK-112). Somebody
|
|
250
|
+
* who learns one box should not be taught the wrong thing about the other.
|
|
251
|
+
*/
|
|
252
|
+
export declare const LINK_URL_RULE = "a path like /about/ or an absolute http:// or https:// URL, with no spaces in it";
|
|
253
|
+
/**
|
|
254
|
+
* Whether a typed URL is one a link box accepts: a site-root path, or an
|
|
255
|
+
* absolute http(s) URL, and no whitespace either way.
|
|
256
|
+
*
|
|
257
|
+
* A bare `about/` would be resolved against whatever page it was printed on,
|
|
258
|
+
* which is never what a link box means, and a `javascript:` or `data:` URL is
|
|
259
|
+
* not somewhere a reader goes.
|
|
260
|
+
*
|
|
261
|
+
* The whitespace half is not decoration. `new URL` does not throw on
|
|
262
|
+
* `https://shll.me/@a | me`: it reads the space and the bar as path
|
|
263
|
+
* characters and percent-encodes them, so a check that only asked the parser
|
|
264
|
+
* took a label, a URL and a trailing word as one URL and stored it — which is
|
|
265
|
+
* how a reader of shll.me ended up at `/author/a%20%7C%20me/` (TASK-112). A
|
|
266
|
+
* URL somebody types has no spaces in it; a space means they typed two things.
|
|
267
|
+
*
|
|
268
|
+
* Deliberately a test rather than a normaliser: a link keeps the URL as it was
|
|
269
|
+
* typed, so `https://example.social/@me` is what the link says and what the
|
|
270
|
+
* box shows it back as.
|
|
271
|
+
*/
|
|
272
|
+
export declare function isLinkUrl(url: string): boolean;
|
|
195
273
|
//# sourceMappingURL=navigation.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"navigation.d.ts","sourceRoot":"","sources":["../../src/web/navigation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAE7C;;;GAGG;AACH,eAAO,MAAM,iBAAiB,YAAY,CAAC;AAE3C
|
|
1
|
+
{"version":3,"file":"navigation.d.ts","sourceRoot":"","sources":["../../src/web/navigation.ts"],"names":[],"mappings":"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"}
|
package/dist/web/navigation.js
CHANGED
|
@@ -4,15 +4,88 @@
|
|
|
4
4
|
*/
|
|
5
5
|
export const DEFAULT_MENU_NAME = 'primary';
|
|
6
6
|
/**
|
|
7
|
-
*
|
|
8
|
-
*
|
|
7
|
+
* What one `rel` value may be spelled with: letters and dashes, which is what
|
|
8
|
+
* every HTML link type is spelled with.
|
|
9
9
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
10
|
+
* Deliberately not a list of the values this CMS knows about (TASK-114). The
|
|
11
|
+
* trailing part of a link line is whatever `rel` values the person typed —
|
|
12
|
+
* `me`, `nofollow`, `author`, `noopener`, one somebody's own reader looks for
|
|
13
|
+
* — and all of them reach the rendered attribute. A closed list would mean
|
|
14
|
+
* every value anybody wanted had to be added here first, and `me` was never a
|
|
15
|
+
* flag in the first place: it is a `rel` value that this CMS happened to be
|
|
16
|
+
* the only one to know.
|
|
14
17
|
*/
|
|
15
|
-
export const
|
|
18
|
+
export const REL_VALUE_PATTERN = /^[a-z-]+$/i;
|
|
19
|
+
/**
|
|
20
|
+
* What a link line's trailing part may be, in the words both boxes explain it
|
|
21
|
+
* in, beside {@link LINK_URL_RULE}.
|
|
22
|
+
*/
|
|
23
|
+
export const LINK_REL_RULE = 'the rel values the link carries, a word each — "| me", "| nofollow noopener"';
|
|
24
|
+
/**
|
|
25
|
+
* One bar-separated part as the `rel` values it names, or `undefined` when it
|
|
26
|
+
* is not a list of them.
|
|
27
|
+
*
|
|
28
|
+
* Every word has to be one: `nofollow noopener` is a list, `a=1|2`'s `2` is
|
|
29
|
+
* not, and that is what keeps a bar inside a URL a bar. An empty part names no
|
|
30
|
+
* values rather than an empty list, because `About | /about/ |` is a line with
|
|
31
|
+
* a trailing bar, not a line with a rel.
|
|
32
|
+
*/
|
|
33
|
+
export function relValuesOf(text) {
|
|
34
|
+
const words = text
|
|
35
|
+
.trim()
|
|
36
|
+
.split(/\s+/)
|
|
37
|
+
.filter((word) => word !== '');
|
|
38
|
+
if (words.length === 0)
|
|
39
|
+
return undefined;
|
|
40
|
+
return words.every((word) => REL_VALUE_PATTERN.test(word)) ? words : undefined;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Some `rel` values as one attribute holds them: lower case, each said once,
|
|
44
|
+
* in the order they were first typed.
|
|
45
|
+
*
|
|
46
|
+
* A theme prints this string and nothing else, so it never joins a list itself
|
|
47
|
+
* and never emits two `rel` attributes on one link. Lower case is what makes
|
|
48
|
+
* saying a value twice — `me` typed on a profile link that already carries it
|
|
49
|
+
* — a no-op rather than `rel="me ME"`.
|
|
50
|
+
*/
|
|
51
|
+
export function relText(values) {
|
|
52
|
+
const said = new Set();
|
|
53
|
+
for (const value of values) {
|
|
54
|
+
for (const word of value.trim().toLowerCase().split(/\s+/)) {
|
|
55
|
+
if (word !== '')
|
|
56
|
+
said.add(word);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return [...said].join(' ');
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* One typed link line, with the `rel` values taken off the end of it.
|
|
63
|
+
*
|
|
64
|
+
* The values come off the right, one bar-separated part at a time, and only
|
|
65
|
+
* while the last part is a list of rel values — so `Mastodon |
|
|
66
|
+
* https://example.social/@me | me | nofollow` and `... | me nofollow` are the
|
|
67
|
+
* same line, and `Odd | /odd/?a=1|2` keeps its bar because `2` is not one.
|
|
68
|
+
*
|
|
69
|
+
* The one place a link line is split, for the reason the URL rule is worded
|
|
70
|
+
* once (TASK-114): the menu box on the Navigation screen and the Links box on
|
|
71
|
+
* a profile take the same line, so somebody who learns one box has learned the
|
|
72
|
+
* other.
|
|
73
|
+
*/
|
|
74
|
+
export function splitLinkRel(line) {
|
|
75
|
+
const values = [];
|
|
76
|
+
let text = line;
|
|
77
|
+
for (;;) {
|
|
78
|
+
const bar = text.lastIndexOf('|');
|
|
79
|
+
if (bar === -1)
|
|
80
|
+
break;
|
|
81
|
+
const rel = relValuesOf(text.slice(bar + 1));
|
|
82
|
+
if (rel === undefined)
|
|
83
|
+
break;
|
|
84
|
+
values.unshift(...rel);
|
|
85
|
+
text = text.slice(0, bar);
|
|
86
|
+
}
|
|
87
|
+
return { text: text.trim(), rel: relText(values) };
|
|
88
|
+
}
|
|
16
89
|
/**
|
|
17
90
|
* What a menu name may be spelled with.
|
|
18
91
|
*
|
|
@@ -140,10 +213,15 @@ export function menusOf(value) {
|
|
|
140
213
|
* One menu's stored value as the items it names, ignoring anything that is not
|
|
141
214
|
* one.
|
|
142
215
|
*
|
|
143
|
-
*
|
|
144
|
-
*
|
|
145
|
-
*
|
|
146
|
-
*
|
|
216
|
+
* `rel` is read through {@link relText}, so a file that wrote it in capitals
|
|
217
|
+
* or said a value twice renders one attribute either way, and a `rel` that is
|
|
218
|
+
* not a string is an item with none rather than an item dropped.
|
|
219
|
+
*
|
|
220
|
+
* `me: true` is still read, as `rel: "me"`. It is how every marked item was
|
|
221
|
+
* spelled before the trailing part of a line was a list of rel values
|
|
222
|
+
* (TASK-107, TASK-114), and a site that had set the only flag there was must
|
|
223
|
+
* not lose it on upgrade; the Navigation screen writes `rel` the next time
|
|
224
|
+
* that menu is saved. Only the literal `true` counts, the way it always did.
|
|
147
225
|
*/
|
|
148
226
|
export function navigationItemsOf(value) {
|
|
149
227
|
if (!Array.isArray(value))
|
|
@@ -152,12 +230,16 @@ export function navigationItemsOf(value) {
|
|
|
152
230
|
for (const entry of value) {
|
|
153
231
|
if (typeof entry !== 'object' || entry === null)
|
|
154
232
|
continue;
|
|
155
|
-
const { label, url, me } = entry;
|
|
233
|
+
const { label, url, rel, me } = entry;
|
|
156
234
|
if (typeof label !== 'string' || label === '')
|
|
157
235
|
continue;
|
|
158
236
|
if (typeof url !== 'string' || url === '')
|
|
159
237
|
continue;
|
|
160
|
-
|
|
238
|
+
const values = relText([
|
|
239
|
+
...(me === true ? ['me'] : []),
|
|
240
|
+
...(typeof rel === 'string' ? [rel] : []),
|
|
241
|
+
]);
|
|
242
|
+
items.push({ label, url, ...(values === '' ? {} : { rel: values }) });
|
|
161
243
|
}
|
|
162
244
|
return items;
|
|
163
245
|
}
|
|
@@ -180,42 +262,22 @@ function menuLines(value) {
|
|
|
180
262
|
* `about/` would be resolved against whatever page it was printed on, which is
|
|
181
263
|
* never what a menu means.
|
|
182
264
|
*
|
|
183
|
-
* A line may end in the
|
|
184
|
-
* https://example.social/@me | me`
|
|
185
|
-
*
|
|
186
|
-
* actually has ({@link MENU_ITEM_FLAGS}). That is what lets a URL still hold a
|
|
187
|
-
* bar, a query string say: a trailing `| elsewhere` is not a flag, so it stays
|
|
188
|
-
* part of the URL and the line is refused for not naming one, rather than
|
|
189
|
-
* being quietly read as a flag nobody asked for.
|
|
265
|
+
* A line may end in the `rel` values the link carries — `Mastodon |
|
|
266
|
+
* https://example.social/@me | me`, `A source | https://example.com/thing |
|
|
267
|
+
* nofollow noopener` — which {@link splitLinkRel} takes off the end first.
|
|
190
268
|
*
|
|
191
269
|
* What is left splits at its first bar, so a label still cannot hold one.
|
|
192
270
|
*/
|
|
193
271
|
export function menuItemOf(line) {
|
|
194
|
-
const
|
|
195
|
-
|
|
196
|
-
for (;;) {
|
|
197
|
-
const bar = rest.lastIndexOf('|');
|
|
198
|
-
if (bar === -1)
|
|
199
|
-
break;
|
|
200
|
-
const flag = rest
|
|
201
|
-
.slice(bar + 1)
|
|
202
|
-
.trim()
|
|
203
|
-
.toLowerCase();
|
|
204
|
-
if (!MENU_ITEM_FLAGS.includes(flag))
|
|
205
|
-
break;
|
|
206
|
-
flags.add(flag);
|
|
207
|
-
rest = rest.slice(0, bar);
|
|
208
|
-
}
|
|
209
|
-
const bar = rest.indexOf('|');
|
|
272
|
+
const { text, rel } = splitLinkRel(line);
|
|
273
|
+
const bar = text.indexOf('|');
|
|
210
274
|
if (bar === -1)
|
|
211
275
|
return undefined;
|
|
212
|
-
const label =
|
|
213
|
-
const url =
|
|
214
|
-
if (label === '' || url
|
|
215
|
-
return undefined;
|
|
216
|
-
if (!url.startsWith('/') && !isAbsoluteHttpUrl(url))
|
|
276
|
+
const label = text.slice(0, bar).trim();
|
|
277
|
+
const url = text.slice(bar + 1).trim();
|
|
278
|
+
if (label === '' || !isLinkUrl(url))
|
|
217
279
|
return undefined;
|
|
218
|
-
return { label, url, ...(
|
|
280
|
+
return { label, url, ...(rel === '' ? {} : { rel }) };
|
|
219
281
|
}
|
|
220
282
|
/**
|
|
221
283
|
* A menu box as the ordered items it names, dropping the lines that are not
|
|
@@ -238,7 +300,7 @@ export function menuItemsFromText(value) {
|
|
|
238
300
|
/** The items as a box shows them: one line each, in order. */
|
|
239
301
|
export function menuItemsText(items) {
|
|
240
302
|
return items
|
|
241
|
-
.map((item) => `${item.label} | ${item.url}${item.
|
|
303
|
+
.map((item) => `${item.label} | ${item.url}${item.rel === undefined ? '' : ` | ${item.rel}`}`)
|
|
242
304
|
.join('\n');
|
|
243
305
|
}
|
|
244
306
|
/**
|
|
@@ -252,18 +314,42 @@ export function menuItemLineProblem(value) {
|
|
|
252
314
|
const bad = menuLines(value).find((line) => menuItemOf(line) === undefined);
|
|
253
315
|
return bad === undefined
|
|
254
316
|
? undefined
|
|
255
|
-
: `A menu item is "Label | URL", one per line, where the URL is
|
|
256
|
-
|
|
257
|
-
`"| me" for a link that should carry rel="me". "${bad}" is not one.`;
|
|
317
|
+
: `A menu item is "Label | URL", one per line, where the URL is ` +
|
|
318
|
+
`${LINK_URL_RULE}, and may end in ${LINK_REL_RULE}. "${bad}" is not one.`;
|
|
258
319
|
}
|
|
259
320
|
/**
|
|
260
|
-
*
|
|
321
|
+
* What a URL typed into a link box may be, in the words both boxes explain it
|
|
322
|
+
* in: the menu on the Navigation screen and the Links on a user's profile.
|
|
323
|
+
*
|
|
324
|
+
* One sentence fragment rather than two, so the rule {@link isLinkUrl} applies
|
|
325
|
+
* is worded the same wherever somebody is told about it (TASK-112). Somebody
|
|
326
|
+
* who learns one box should not be taught the wrong thing about the other.
|
|
327
|
+
*/
|
|
328
|
+
export const LINK_URL_RULE = 'a path like /about/ or an absolute http:// or https:// URL, with no spaces in it';
|
|
329
|
+
/**
|
|
330
|
+
* Whether a typed URL is one a link box accepts: a site-root path, or an
|
|
331
|
+
* absolute http(s) URL, and no whitespace either way.
|
|
332
|
+
*
|
|
333
|
+
* A bare `about/` would be resolved against whatever page it was printed on,
|
|
334
|
+
* which is never what a link box means, and a `javascript:` or `data:` URL is
|
|
335
|
+
* not somewhere a reader goes.
|
|
261
336
|
*
|
|
262
|
-
*
|
|
337
|
+
* The whitespace half is not decoration. `new URL` does not throw on
|
|
338
|
+
* `https://shll.me/@a | me`: it reads the space and the bar as path
|
|
339
|
+
* characters and percent-encodes them, so a check that only asked the parser
|
|
340
|
+
* took a label, a URL and a trailing word as one URL and stored it — which is
|
|
341
|
+
* how a reader of shll.me ended up at `/author/a%20%7C%20me/` (TASK-112). A
|
|
342
|
+
* URL somebody types has no spaces in it; a space means they typed two things.
|
|
343
|
+
*
|
|
344
|
+
* Deliberately a test rather than a normaliser: a link keeps the URL as it was
|
|
263
345
|
* typed, so `https://example.social/@me` is what the link says and what the
|
|
264
346
|
* box shows it back as.
|
|
265
347
|
*/
|
|
266
|
-
function
|
|
348
|
+
export function isLinkUrl(url) {
|
|
349
|
+
if (url === '' || /\s/.test(url))
|
|
350
|
+
return false;
|
|
351
|
+
if (url.startsWith('/'))
|
|
352
|
+
return true;
|
|
267
353
|
let parsed;
|
|
268
354
|
try {
|
|
269
355
|
parsed = new URL(url);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"navigation.js","sourceRoot":"","sources":["../../src/web/navigation.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,SAAS,CAAC;AAE3C;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,
|
|
1
|
+
{"version":3,"file":"navigation.js","sourceRoot":"","sources":["../../src/web/navigation.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
package/themes/default/README.md
CHANGED
|
@@ -262,7 +262,9 @@ person on an author archive.
|
|
|
262
262
|
What the bio prints: a round `u-photo` at 50px when they have an avatar,
|
|
263
263
|
"Written by" and their name as a `p-name u-url` linked `rel="author me"` to
|
|
264
264
|
their archive, then `p-job-title` and `p-locality` when the profile says, then
|
|
265
|
-
their `p-note` and their `rel="me"` links as a `ul.hlist.bio-links
|
|
265
|
+
their `p-note` and their `rel="me"` links as a `ul.hlist.bio-links` — each
|
|
266
|
+
link's `rel` is on it ready to print, `me` first and then whatever else the
|
|
267
|
+
Links box typed. Each of
|
|
266
268
|
those is printed only when the profile says it, so a profile holding a name
|
|
267
269
|
alone prints a name alone. A name this site has no account for is printed
|
|
268
270
|
unlinked, because the file still said somebody wrote this.
|
|
@@ -834,7 +836,7 @@ A site stores its menus by name in `content/_data/site.json`:
|
|
|
834
836
|
],
|
|
835
837
|
"footer": [
|
|
836
838
|
{ "label": "Colophon", "url": "/colophon/" },
|
|
837
|
-
{ "label": "Mastodon", "url": "https://example.social/@me", "
|
|
839
|
+
{ "label": "Mastodon", "url": "https://example.social/@me", "rel": "me" }
|
|
838
840
|
]
|
|
839
841
|
}
|
|
840
842
|
}
|
|
@@ -882,14 +884,17 @@ rather than failing to load it.
|
|
|
882
884
|
|
|
883
885
|
### One item
|
|
884
886
|
|
|
885
|
-
Each item is `{ label, url, current }`, plus `
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
887
|
+
Each item is `{ label, url, current }`, plus `rel` on a link that carries `rel`
|
|
888
|
+
values. `url` is a site-root path or an absolute URL for somewhere else, so put
|
|
889
|
+
it through the `url` filter and a site served from a subdirectory still links
|
|
890
|
+
correctly. `current` is true for the item whose path is the one being rendered,
|
|
891
|
+
comparing without the trailing slash; an item pointing off the site is never
|
|
892
|
+
current. `rel` is one string holding every value the line carried, already
|
|
893
|
+
lower case and each said once — print it as the whole attribute,
|
|
894
|
+
`rel="{{ item.rel }}"`, rather than building one of your own. `me` is the value
|
|
895
|
+
most menus have, for the IndieWeb's identity check: Mastodon verifies a link on
|
|
896
|
+
a profile by looking for a `rel="me"` link back, so a footer link to a profile
|
|
897
|
+
marked `me` is what makes the tick appear.
|
|
893
898
|
|
|
894
899
|
### Where this theme prints them
|
|
895
900
|
|
|
@@ -920,6 +925,16 @@ account: it used to print one `rel="me"` link per entry of `siteAuthor.links`,
|
|
|
920
925
|
which meant one nominated user's links stood for the site and nobody else's
|
|
921
926
|
appeared at all.
|
|
922
927
|
|
|
928
|
+
Every menu reads the same: a link in one is the colour, the underline and the
|
|
929
|
+
size of a link in the page's prose, in the sans heading font a `nav` is set in
|
|
930
|
+
(TASK-111). A menu is navigation somebody is meant to use rather than a
|
|
931
|
+
footnote, so none of them is set smaller than the words around it, and the same
|
|
932
|
+
goes for the `rel="me"` links `partials/bio.njk` prints under a post. The two
|
|
933
|
+
links that read as plain text instead are the site title and
|
|
934
|
+
`a.header-link-home`, which are the page saying where it is rather than
|
|
935
|
+
somewhere to go, and the stylesheet names those two rather than reaching for
|
|
936
|
+
every link that happens to sit in a `<header>`.
|
|
937
|
+
|
|
923
938
|
`menus.primary` is the whole of the site menu, in the order the Navigation
|
|
924
939
|
screen names it. Both areas are declared in this theme's `theme.json`, which is
|
|
925
940
|
what puts them on `/admin/navigation` with those labels; a theme that renders a
|
|
@@ -29,6 +29,10 @@
|
|
|
29
29
|
otherwise. An author archive sets "Posts by", because the card there heads a
|
|
30
30
|
list of somebody's writing rather than crediting one piece of it.
|
|
31
31
|
|
|
32
|
+
A link's `rel` always holds `me` — the CMS puts it there whether or not the
|
|
33
|
+
Links box typed it — followed by whatever else that line carried, so this
|
|
34
|
+
prints one attribute rather than one of its own beside one of theirs.
|
|
35
|
+
|
|
32
36
|
The `p-note` and the `rel="me"` links are printed wherever the card is, which
|
|
33
37
|
is what `bioProfile` used to ask for on an author archive alone. An entry
|
|
34
38
|
left that switch unset because the page footer carried the site's identity
|
|
@@ -62,7 +66,7 @@
|
|
|
62
66
|
{%- if bioAuthor.links.length %}
|
|
63
67
|
<ul class="hlist bio-links">
|
|
64
68
|
{%- for link in bioAuthor.links %}
|
|
65
|
-
<li><a class="u-url" rel="
|
|
69
|
+
<li><a class="u-url" rel="{{ link.rel }}" href="{{ link.href }}">{{ link.label }}</a></li>
|
|
66
70
|
{%- endfor %}
|
|
67
71
|
</ul>
|
|
68
72
|
{%- endif %}
|
|
@@ -17,15 +17,17 @@
|
|
|
17
17
|
which of a page's menus this one is. Two navs with no labels are two navs
|
|
18
18
|
called "navigation".
|
|
19
19
|
|
|
20
|
-
An item
|
|
21
|
-
|
|
22
|
-
|
|
20
|
+
An item's `rel` is whatever `rel` values its line carried, already lower case
|
|
21
|
+
and each said once, so it is printed as one attribute and nothing is joined
|
|
22
|
+
here. `me` is the one most menus want: it is how Mastodon and the rest of the
|
|
23
|
+
IndieWeb verify that the site and the profile it links belong to the same
|
|
24
|
+
person — the profile links back, and the pair is the proof.
|
|
23
25
|
#}
|
|
24
26
|
{% macro list(items, label) %}{% if items.length %}
|
|
25
27
|
<nav class="site-nav" aria-label="{{ label }}">
|
|
26
28
|
<ul class="hlist">
|
|
27
29
|
{% for item in items %}
|
|
28
|
-
<li><a href="{{ item.url | url }}"{% if item.
|
|
30
|
+
<li><a href="{{ item.url | url }}"{% if item.rel %} rel="{{ item.rel }}"{% endif %}{% if item.current %} class="is-current" aria-current="page"{% endif %}>{{ item.label }}</a></li>
|
|
29
31
|
{% endfor %}
|
|
30
32
|
</ul>
|
|
31
33
|
</nav>
|