@mannisto/astro-metadata 2.0.0-alpha.4 → 2.0.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 +247 -108
- package/package.json +2 -2
- package/src/components/Canonical.astro +10 -0
- package/src/components/Description.astro +10 -0
- package/src/components/Favicon.astro +9 -0
- package/src/components/Feeds.astro +29 -0
- package/src/components/LanguageAlternates.astro +13 -0
- package/src/components/Manifest.astro +11 -0
- package/src/components/Metadata.astro +27 -199
- package/src/components/OpenGraph.astro +70 -0
- package/src/components/Robots.astro +10 -0
- package/src/components/Schema.astro +42 -0
- package/src/components/ThemeColor.astro +15 -0
- package/src/components/Title.astro +10 -0
- package/src/components/Twitter.astro +21 -0
- package/src/components/index.ts +13 -1
- package/src/integration.js +0 -6
- package/src/lib/checks.js +6 -1
- package/src/lib/manifest.ts +0 -1
- package/src/lib/open-graph.ts +54 -0
- package/src/lib/page.ts +164 -0
- package/src/lib/rules.js +0 -1
- package/src/lib/schema.ts +5 -2
- package/src/lib/site.ts +65 -43
- package/src/lib/url.ts +4 -1
- package/src/runtime.ts +1 -3
- package/src/toolbar/app.ts +1 -15
- package/src/toolbar/data.ts +0 -2
- package/src/toolbar/highlight.ts +0 -3
- package/src/toolbar/ui/alert/alert.css +0 -2
- package/src/toolbar/ui/section/section.css +0 -7
- package/src/toolbar/ui/table/table.css +0 -2
- package/src/toolbar/views/metadata-panel.css +0 -7
- package/src/toolbar/views/metadata-panel.ts +1 -5
- package/src/toolbar/views/overview-view.css +0 -2
- package/src/toolbar/views/overview-view.ts +13 -0
- package/src/toolbar/views/previews/previews-view.css +0 -1
- package/src/toolbar/views/previews/previews-view.ts +0 -1
package/src/lib/page.ts
ADDED
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import { getConfig, localize } from "./config.ts"
|
|
2
|
+
import type { MetadataImage } from "./config.ts"
|
|
3
|
+
import { i18n } from "./i18n.ts"
|
|
4
|
+
import type { I18nPage } from "./i18n.ts"
|
|
5
|
+
import { openGraphAlternates, openGraphLocale } from "./open-graph.ts"
|
|
6
|
+
import type { SchemaAuthor, SchemaBreadcrumb, SchemaData } from "./schema.ts"
|
|
7
|
+
import { pageUrl } from "./url.ts"
|
|
8
|
+
|
|
9
|
+
export type HreflangLink = {
|
|
10
|
+
/** The locale of the version, e.g. "en", "fi" or "en-US", or "x-default". */
|
|
11
|
+
hreflang: string
|
|
12
|
+
/** The path or URL of the page in that locale. */
|
|
13
|
+
href: string
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/** The props of `<Metadata>`. The small components take the props that they use. */
|
|
17
|
+
export type MetadataProps = {
|
|
18
|
+
/** The page title. The title template of the config adds the site name. */
|
|
19
|
+
title?: string
|
|
20
|
+
/** Defaults to the description of the config. */
|
|
21
|
+
description?: string
|
|
22
|
+
/** The social image. Defaults to the image of the config. */
|
|
23
|
+
image?: MetadataImage
|
|
24
|
+
/**
|
|
25
|
+
* The Open Graph type. Defaults to "website". "article" also adds the `Article`
|
|
26
|
+
* structured data, with the title, the description, the image and the dates.
|
|
27
|
+
*/
|
|
28
|
+
type?: string
|
|
29
|
+
/**
|
|
30
|
+
* The author of an article: a name, or `{ name, url, twitter }`. The Twitter (X) handle
|
|
31
|
+
* gives `twitter:creator`.
|
|
32
|
+
*/
|
|
33
|
+
author?: SchemaAuthor | SchemaAuthor[]
|
|
34
|
+
/** The publication date of an article. */
|
|
35
|
+
published?: Date | string
|
|
36
|
+
/** The date of the last change of an article. */
|
|
37
|
+
modified?: Date | string
|
|
38
|
+
/**
|
|
39
|
+
* The canonical path or URL. Defaults to the current page. With `@mannisto/astro-i18n`,
|
|
40
|
+
* the default is the public URL of the page.
|
|
41
|
+
*/
|
|
42
|
+
canonical?: string
|
|
43
|
+
/** Set to false to keep the page out of search results. Defaults to true. */
|
|
44
|
+
index?: boolean
|
|
45
|
+
/** Set to false to tell search engines not to follow the links. Defaults to true. */
|
|
46
|
+
follow?: boolean
|
|
47
|
+
/**
|
|
48
|
+
* The hreflang links: the page in each locale, and `x-default`. With `@mannisto/astro-i18n`
|
|
49
|
+
* or the `i18n` option of Astro, the links are made for you. `[]` turns them off.
|
|
50
|
+
*/
|
|
51
|
+
hreflang?: HreflangLink[]
|
|
52
|
+
/** The path from the home page to this page. It adds the `BreadcrumbList` structured data. */
|
|
53
|
+
breadcrumbs?: SchemaBreadcrumb[]
|
|
54
|
+
/**
|
|
55
|
+
* Structured data items of your own, e.g. a `Product`. They go into the structured data
|
|
56
|
+
* of the page, with the site from the config.
|
|
57
|
+
*/
|
|
58
|
+
schema?: SchemaData | SchemaData[]
|
|
59
|
+
/**
|
|
60
|
+
* The locale of the page. Defaults to `Astro.currentLocale`, or the locale of
|
|
61
|
+
* `@mannisto/astro-i18n`.
|
|
62
|
+
*/
|
|
63
|
+
locale?: string
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Returns the full title of a page: the title in the title template, or the site name
|
|
68
|
+
* for a page without a title.
|
|
69
|
+
*
|
|
70
|
+
* @example fullTitle("About") // "About | My Site"
|
|
71
|
+
*/
|
|
72
|
+
export function fullTitle(title?: string, locale?: string): string | undefined {
|
|
73
|
+
const config = getConfig()
|
|
74
|
+
if (!title) return localize(config.siteName, locale)
|
|
75
|
+
|
|
76
|
+
const template = localize(config.titleTemplate, locale)
|
|
77
|
+
if (!template?.includes("%s")) return title
|
|
78
|
+
|
|
79
|
+
// A replacer function keeps "$&" and other "$" patterns in the title as text
|
|
80
|
+
return template.replace("%s", () => {
|
|
81
|
+
return title
|
|
82
|
+
})
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Returns the locale of the page: the prop, `Astro.currentLocale`, or the locale of the
|
|
87
|
+
* i18n setup of the project.
|
|
88
|
+
*/
|
|
89
|
+
export function pageLocale(page: I18nPage, props: MetadataProps): string | undefined {
|
|
90
|
+
return props.locale ?? page.currentLocale ?? i18n?.locale(page)
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Returns the absolute canonical URL of the page: the prop, the public path of the
|
|
95
|
+
* i18n setup, or the current path.
|
|
96
|
+
*/
|
|
97
|
+
export function pageCanonical(page: I18nPage, props: MetadataProps): string {
|
|
98
|
+
return pageUrl(props.canonical ?? i18n?.path(page) ?? page.url.pathname)
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Returns the hreflang links of the page: the prop, or the links of the i18n setup.
|
|
103
|
+
*/
|
|
104
|
+
export function pageHreflang(page: I18nPage, props: MetadataProps): HreflangLink[] {
|
|
105
|
+
return props.hreflang ?? i18n?.alternates(page) ?? []
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Returns the social image of the page: the prop, or the image of the config with the alt
|
|
110
|
+
* text of the locale.
|
|
111
|
+
*/
|
|
112
|
+
export function pageImage(props: MetadataProps, locale?: string): MetadataImage | undefined {
|
|
113
|
+
if (props.image) return props.image
|
|
114
|
+
|
|
115
|
+
const image = getConfig().image
|
|
116
|
+
if (!image) return undefined
|
|
117
|
+
return { ...image, alt: localize(image.alt, locale) }
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Returns the description of the page: the prop, or the description of the config.
|
|
122
|
+
*/
|
|
123
|
+
export function pageDescription(props: MetadataProps, locale?: string): string | undefined {
|
|
124
|
+
return props.description ?? localize(getConfig().description, locale)
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Returns the content of the robots tag, or an empty text. Search engines index and follow
|
|
129
|
+
* by default, so the tag is only necessary for other values.
|
|
130
|
+
*/
|
|
131
|
+
export function pageRobots(props: MetadataProps): string {
|
|
132
|
+
const robots = getConfig().robots
|
|
133
|
+
const index = props.index ?? robots?.index ?? true
|
|
134
|
+
const follow = props.follow ?? robots?.follow ?? true
|
|
135
|
+
|
|
136
|
+
return [!index && "noindex", !follow && "nofollow", robots?.extra].filter(Boolean).join(", ")
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Returns the Open Graph locale of the page and its other locales: "fi_FI".
|
|
141
|
+
*/
|
|
142
|
+
export function pageOpenGraphLocales(
|
|
143
|
+
locale: string | undefined,
|
|
144
|
+
hreflang: HreflangLink[]
|
|
145
|
+
): { locale?: string; alternates: string[] } {
|
|
146
|
+
const codes = hreflang.map((link) => {
|
|
147
|
+
return link.hreflang
|
|
148
|
+
})
|
|
149
|
+
return { locale: openGraphLocale(locale), alternates: openGraphAlternates(locale, codes) }
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Returns the authors of an article as a list.
|
|
154
|
+
*/
|
|
155
|
+
export function pageAuthors(props: MetadataProps): SchemaAuthor[] {
|
|
156
|
+
return props.author === undefined ? [] : [props.author].flat()
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Returns a date as an ISO 8601 text. A text stays unchanged.
|
|
161
|
+
*/
|
|
162
|
+
export function isoDate(value: Date | string): string {
|
|
163
|
+
return typeof value === "string" ? value : value.toISOString()
|
|
164
|
+
}
|
package/src/lib/rules.js
CHANGED
package/src/lib/schema.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Graph, Thing
|
|
1
|
+
import type { Graph, Thing } from "schema-dts"
|
|
2
2
|
|
|
3
3
|
import { getConfig, localize } from "./config.ts"
|
|
4
4
|
import type { Localized } from "./config.ts"
|
|
@@ -8,8 +8,11 @@ import { url } from "./url.ts"
|
|
|
8
8
|
* A structured data item of your own, e.g. a `Product` or an `Event`. The schema.org types
|
|
9
9
|
* give the editor completion, and other objects are also accepted. `@context` is not
|
|
10
10
|
* necessary: the items go into one graph with the context.
|
|
11
|
+
*
|
|
12
|
+
* `@context` is an optional property, not `WithContext<Thing>`: that type makes the union of
|
|
13
|
+
* schema-dts too large for TypeScript when a layout spreads its props into `<Metadata>`.
|
|
11
14
|
*/
|
|
12
|
-
export type SchemaData = Thing
|
|
15
|
+
export type SchemaData = (Thing & { "@context"?: string }) | Record<string, unknown>
|
|
13
16
|
|
|
14
17
|
/**
|
|
15
18
|
* The organization or the person behind the site. It is the publisher of the site and of
|
package/src/lib/site.ts
CHANGED
|
@@ -1,73 +1,95 @@
|
|
|
1
1
|
import { getConfig, localize } from "./config.ts"
|
|
2
|
-
import type {
|
|
3
|
-
import {
|
|
2
|
+
import type { MetadataImage, ThemeColor } from "./config.ts"
|
|
3
|
+
import { Favicon } from "./favicon.ts"
|
|
4
|
+
import type { ManifestIcon } from "./favicon.ts"
|
|
5
|
+
import { url } from "./url.ts"
|
|
6
|
+
|
|
7
|
+
/** The options of the `Site` values that can have a text for each locale. */
|
|
8
|
+
export type SiteOptions = {
|
|
9
|
+
/** The locale of the text, e.g. "fi". Defaults to the first text. */
|
|
10
|
+
locale?: string
|
|
11
|
+
}
|
|
4
12
|
|
|
5
13
|
/**
|
|
6
|
-
* The
|
|
14
|
+
* The values that you set in the integration. Each method returns `undefined` for an
|
|
15
|
+
* option that is not set.
|
|
16
|
+
*
|
|
17
|
+
* @example Site.description({ locale: "fi" })
|
|
7
18
|
*/
|
|
8
19
|
export const Site = {
|
|
9
20
|
/**
|
|
10
|
-
* Returns the
|
|
21
|
+
* Returns the `siteName` of the locale.
|
|
22
|
+
*
|
|
23
|
+
* @example Site.name() // "Acme Studio"
|
|
11
24
|
*/
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
manifest: config.manifest,
|
|
24
|
-
schema: config.schema,
|
|
25
|
-
}
|
|
25
|
+
name(options: SiteOptions = {}): string | undefined {
|
|
26
|
+
return localize(getConfig().siteName, options.locale)
|
|
27
|
+
},
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Returns the `titleTemplate` of the locale.
|
|
31
|
+
*
|
|
32
|
+
* @example Site.template() // "%s | Acme Studio"
|
|
33
|
+
*/
|
|
34
|
+
template(options: SiteOptions = {}): string | undefined {
|
|
35
|
+
return localize(getConfig().titleTemplate, options.locale)
|
|
26
36
|
},
|
|
27
37
|
|
|
28
38
|
/**
|
|
29
|
-
* Returns the
|
|
39
|
+
* Returns the `description` of the locale.
|
|
30
40
|
*
|
|
31
|
-
* @
|
|
41
|
+
* @example Site.description({ locale: "fi" })
|
|
32
42
|
*/
|
|
33
|
-
|
|
34
|
-
return localize(getConfig().
|
|
43
|
+
description(options: SiteOptions = {}): string | undefined {
|
|
44
|
+
return localize(getConfig().description, options.locale)
|
|
35
45
|
},
|
|
36
46
|
|
|
37
47
|
/**
|
|
38
|
-
* Returns the
|
|
39
|
-
* for a page without a title.
|
|
48
|
+
* Returns the `image` with an absolute URL, and the alt text of the locale.
|
|
40
49
|
*
|
|
41
|
-
* @example Site.
|
|
50
|
+
* @example Site.image() // { url: "https://example.com/og.jpg", alt: "Acme Studio", … }
|
|
42
51
|
*/
|
|
43
|
-
|
|
44
|
-
const
|
|
45
|
-
if (!
|
|
46
|
-
if (!template?.includes("%s")) return title
|
|
52
|
+
image(options: SiteOptions = {}): MetadataImage | undefined {
|
|
53
|
+
const image = getConfig().image
|
|
54
|
+
if (!image) return undefined
|
|
47
55
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
56
|
+
return {
|
|
57
|
+
url: url(image.url),
|
|
58
|
+
alt: localize(image.alt, options.locale),
|
|
59
|
+
width: image.width,
|
|
60
|
+
height: image.height,
|
|
61
|
+
}
|
|
52
62
|
},
|
|
53
63
|
|
|
54
64
|
/**
|
|
55
|
-
* Returns the
|
|
56
|
-
* Absolute URLs stay unchanged.
|
|
65
|
+
* Returns the `twitter` values. The card defaults to "summary_large_image".
|
|
57
66
|
*
|
|
58
|
-
* @example Site.
|
|
67
|
+
* @example Site.twitter() // { site: "@acmestudio", card: "summary_large_image" }
|
|
68
|
+
*/
|
|
69
|
+
twitter(): { site?: string; card: "summary" | "summary_large_image" } | undefined {
|
|
70
|
+
const twitter = getConfig().twitter
|
|
71
|
+
if (!twitter) return undefined
|
|
72
|
+
|
|
73
|
+
return {
|
|
74
|
+
site: twitter.site,
|
|
75
|
+
card: twitter.card ?? "summary_large_image",
|
|
76
|
+
}
|
|
77
|
+
},
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Returns the `themeColor`: one color, or `{ light, dark }`.
|
|
59
81
|
*/
|
|
60
|
-
|
|
61
|
-
return
|
|
82
|
+
themeColor(): ThemeColor | undefined {
|
|
83
|
+
return getConfig().themeColor
|
|
62
84
|
},
|
|
63
85
|
|
|
64
86
|
/**
|
|
65
|
-
* Returns the
|
|
66
|
-
*
|
|
87
|
+
* Returns the icons of the generated favicons, for the `icons` field of your own web app
|
|
88
|
+
* manifest. Without the `favicon` option, the list is empty.
|
|
67
89
|
*
|
|
68
|
-
* @example
|
|
90
|
+
* @example export const GET = () => Response.json({ name: "Acme Studio", icons: Site.icons() })
|
|
69
91
|
*/
|
|
70
|
-
|
|
71
|
-
return
|
|
92
|
+
icons(): ManifestIcon[] {
|
|
93
|
+
return Favicon.icons()
|
|
72
94
|
},
|
|
73
95
|
}
|
package/src/lib/url.ts
CHANGED
|
@@ -62,7 +62,10 @@ export function pagePath(path: string): string {
|
|
|
62
62
|
if (buildFormat === "preserve") return path
|
|
63
63
|
slash = buildFormat !== "file"
|
|
64
64
|
}
|
|
65
|
-
|
|
65
|
+
// A loop and not a regular expression: /\/+$/ is slow for a path with many slashes
|
|
66
|
+
let end = path.length
|
|
67
|
+
while (end > 0 && path[end - 1] === "/") end--
|
|
68
|
+
const clean = path.slice(0, end)
|
|
66
69
|
return slash ? `${clean}/` : clean || "/"
|
|
67
70
|
}
|
|
68
71
|
|
package/src/runtime.ts
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
export type { Feed, Localized, MetadataConfig, MetadataImage, ThemeColor } from "./lib/config.ts"
|
|
2
2
|
export type { FaviconFile, ResolvedFaviconFile } from "./lib/favicon-files.js"
|
|
3
3
|
export type { FaviconConfig, FaviconLink, ManifestIcon } from "./lib/favicon.ts"
|
|
4
|
-
export { Favicon } from "./lib/favicon.ts"
|
|
5
4
|
export type { ManifestConfig, ManifestPath } from "./lib/manifest.ts"
|
|
6
|
-
export { Manifest } from "./lib/manifest.ts"
|
|
7
5
|
export type {
|
|
8
6
|
SchemaAuthor,
|
|
9
7
|
SchemaBreadcrumb,
|
|
@@ -12,5 +10,5 @@ export type {
|
|
|
12
10
|
SchemaPage,
|
|
13
11
|
SchemaPublisher,
|
|
14
12
|
} from "./lib/schema.ts"
|
|
15
|
-
export {
|
|
13
|
+
export type { SiteOptions } from "./lib/site.ts"
|
|
16
14
|
export { Site } from "./lib/site.ts"
|
package/src/toolbar/app.ts
CHANGED
|
@@ -15,7 +15,6 @@ export default defineToolbarApp({
|
|
|
15
15
|
canvas.append(panel)
|
|
16
16
|
let open = false
|
|
17
17
|
|
|
18
|
-
// The badge shows the worst problem of the page
|
|
19
18
|
async function updateBadge() {
|
|
20
19
|
const { checks } = await checkPage()
|
|
21
20
|
const { level } = summarize(checks)
|
|
@@ -44,18 +43,8 @@ export default defineToolbarApp({
|
|
|
44
43
|
setOpen(false)
|
|
45
44
|
})
|
|
46
45
|
|
|
47
|
-
// Alt+M (Option+M on a Mac) opens and closes the panel. Escape closes it.
|
|
48
|
-
// `code` is the key, not the character: Option+M types "µ" on a Mac.
|
|
49
46
|
document.addEventListener("keydown", (event) => {
|
|
50
|
-
|
|
51
|
-
if (target?.closest("input, textarea, select, [contenteditable]")) return
|
|
52
|
-
|
|
53
|
-
if (event.altKey && event.code === "KeyM") {
|
|
54
|
-
event.preventDefault()
|
|
55
|
-
setOpen(!open)
|
|
56
|
-
} else if (event.key === "Escape" && open) {
|
|
57
|
-
setOpen(false)
|
|
58
|
-
}
|
|
47
|
+
if (event.key === "Escape" && open) setOpen(false)
|
|
59
48
|
})
|
|
60
49
|
|
|
61
50
|
// View transitions change the page without a full load
|
|
@@ -65,8 +54,5 @@ export default defineToolbarApp({
|
|
|
65
54
|
})
|
|
66
55
|
|
|
67
56
|
updateBadge()
|
|
68
|
-
|
|
69
|
-
// ?metadata in the URL opens the panel when the page loads
|
|
70
|
-
if (new URLSearchParams(location.search).has("metadata")) setOpen(true)
|
|
71
57
|
},
|
|
72
58
|
})
|
package/src/toolbar/data.ts
CHANGED
|
@@ -109,7 +109,6 @@ function jsonLd(): JsonLd[] {
|
|
|
109
109
|
const source = script.textContent ?? ""
|
|
110
110
|
try {
|
|
111
111
|
const data = JSON.parse(source)
|
|
112
|
-
// The items of a list, or of a graph
|
|
113
112
|
const list = Array.isArray(data) ? data : [data]
|
|
114
113
|
const items = list.flatMap((item) => {
|
|
115
114
|
return Array.isArray(item?.["@graph"]) ? item["@graph"] : [item]
|
|
@@ -239,7 +238,6 @@ export type PageChecks = {
|
|
|
239
238
|
checks: Check[]
|
|
240
239
|
}
|
|
241
240
|
|
|
242
|
-
// The check endpoint of the dev server. The integration adds it.
|
|
243
241
|
const CHECK_PATH = "/__astro-metadata/check"
|
|
244
242
|
|
|
245
243
|
/**
|
package/src/toolbar/highlight.ts
CHANGED
|
@@ -61,7 +61,6 @@
|
|
|
61
61
|
font-weight: 400;
|
|
62
62
|
height: 100%;
|
|
63
63
|
|
|
64
|
-
/* Inline code in the alert uses the colors of the level */
|
|
65
64
|
--code-color: var(--text);
|
|
66
65
|
--code-border: color-mix(in srgb, var(--border) 40%, transparent);
|
|
67
66
|
--code-background: color-mix(in srgb, var(--border) 20%, transparent);
|
|
@@ -73,7 +72,6 @@
|
|
|
73
72
|
}
|
|
74
73
|
}
|
|
75
74
|
|
|
76
|
-
/* The list of items, each with a dot in the color of its level */
|
|
77
75
|
.items {
|
|
78
76
|
display: grid;
|
|
79
77
|
gap: var(--space-50);
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
/* A box around the section, so its content belongs together */
|
|
2
1
|
:host {
|
|
3
2
|
display: block;
|
|
4
3
|
padding: var(--space-300);
|
|
@@ -7,7 +6,6 @@
|
|
|
7
6
|
background: var(--zinc-900);
|
|
8
7
|
}
|
|
9
8
|
|
|
10
|
-
/* A closed or empty section shows only its heading */
|
|
11
9
|
:host([closed]),
|
|
12
10
|
:host([empty]) {
|
|
13
11
|
h2 {
|
|
@@ -15,17 +13,14 @@
|
|
|
15
13
|
}
|
|
16
14
|
}
|
|
17
15
|
|
|
18
|
-
/* An empty section has no caret */
|
|
19
16
|
:host([empty]) astro-metadata-icon {
|
|
20
17
|
visibility: hidden;
|
|
21
18
|
}
|
|
22
19
|
|
|
23
|
-
/* The caret turns down when the section is open */
|
|
24
20
|
:host(:not([closed])) astro-metadata-icon {
|
|
25
21
|
transform: rotate(90deg);
|
|
26
22
|
}
|
|
27
23
|
|
|
28
|
-
/* The heading: small, monospace, uppercase, wide letter spacing */
|
|
29
24
|
h2 {
|
|
30
25
|
margin: 0 0 var(--space-200);
|
|
31
26
|
font-family: var(--font-mono);
|
|
@@ -36,7 +31,6 @@ h2 {
|
|
|
36
31
|
letter-spacing: 0.05em;
|
|
37
32
|
}
|
|
38
33
|
|
|
39
|
-
/* The heading is a button that closes or opens the section */
|
|
40
34
|
button {
|
|
41
35
|
display: flex;
|
|
42
36
|
align-items: center;
|
|
@@ -56,7 +50,6 @@ button {
|
|
|
56
50
|
color: var(--zinc-100);
|
|
57
51
|
}
|
|
58
52
|
|
|
59
|
-
/* An empty section: a dim heading and "None" at the right */
|
|
60
53
|
&:disabled {
|
|
61
54
|
color: var(--zinc-600);
|
|
62
55
|
cursor: default;
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
/* A framed container, a hairline between the rows, and a hover color */
|
|
2
1
|
:host {
|
|
3
2
|
display: block;
|
|
4
3
|
}
|
|
@@ -31,7 +30,6 @@ tr {
|
|
|
31
30
|
background: var(--zinc-800);
|
|
32
31
|
}
|
|
33
32
|
|
|
34
|
-
/* The child rows are indented under their row */
|
|
35
33
|
&.child th {
|
|
36
34
|
padding-left: calc(var(--space-400) + var(--space-400));
|
|
37
35
|
}
|
|
@@ -6,7 +6,6 @@ import type { PageMetadata } from "../data.ts"
|
|
|
6
6
|
import { define } from "../define.ts"
|
|
7
7
|
import tokens from "../styles/tokens.css?inline"
|
|
8
8
|
|
|
9
|
-
import "../ui/badge/badge.ts"
|
|
10
9
|
import "../ui/icon/icon.ts"
|
|
11
10
|
import "../ui/tab-group/tab-group.ts"
|
|
12
11
|
|
|
@@ -105,10 +104,7 @@ export class MetadataPanel extends LitElement {
|
|
|
105
104
|
|
|
106
105
|
<header class="header">
|
|
107
106
|
<div class="heading">
|
|
108
|
-
<
|
|
109
|
-
<h1>Metadata</h1>
|
|
110
|
-
<astro-metadata-badge level="info">Beta</astro-metadata-badge>
|
|
111
|
-
</div>
|
|
107
|
+
<h1>Metadata</h1>
|
|
112
108
|
<p class="path">${location.pathname}</p>
|
|
113
109
|
</div>
|
|
114
110
|
<button class="close" @click=${this.close} aria-label="Close the panel">
|
|
@@ -327,6 +327,13 @@ export class OverviewView extends LitElement {
|
|
|
327
327
|
const data = this.data
|
|
328
328
|
const og = data.openGraph
|
|
329
329
|
const image = findMeta(og, "og:image")
|
|
330
|
+
const alternates = og
|
|
331
|
+
.filter((tag) => {
|
|
332
|
+
return tag.key === "og:locale:alternate"
|
|
333
|
+
})
|
|
334
|
+
.map((tag) => {
|
|
335
|
+
return tag.value
|
|
336
|
+
})
|
|
330
337
|
const width = findMeta(og, "og:image:width")
|
|
331
338
|
const height = findMeta(og, "og:image:height")
|
|
332
339
|
|
|
@@ -366,6 +373,12 @@ export class OverviewView extends LitElement {
|
|
|
366
373
|
tagRow("Description", findMeta(og, "og:description"), "og:description"),
|
|
367
374
|
tagRow("Type", code(findMeta(og, "og:type")), "og:type"),
|
|
368
375
|
tagRow("Site name", findMeta(og, "og:site_name"), "og:site_name"),
|
|
376
|
+
tagRow("Locale", code(findMeta(og, "og:locale")), "og:locale"),
|
|
377
|
+
tagRow(
|
|
378
|
+
"Other locales",
|
|
379
|
+
code(alternates.join(", ") || undefined),
|
|
380
|
+
"og:locale:alternate"
|
|
381
|
+
),
|
|
369
382
|
tagRow("Image", image, "og:image"),
|
|
370
383
|
tagRow(
|
|
371
384
|
"Image size",
|
|
@@ -17,7 +17,6 @@ import { slack } from "./slack.ts"
|
|
|
17
17
|
import { twitter } from "./twitter.ts"
|
|
18
18
|
import { whatsapp } from "./whatsapp.ts"
|
|
19
19
|
|
|
20
|
-
// The platforms in the order of the sections
|
|
21
20
|
const PLATFORMS: Platform[] = [google, twitter, facebook, linkedin, whatsapp, discord, slack]
|
|
22
21
|
|
|
23
22
|
/**
|