@mannisto/astro-metadata 1.0.2 → 2.0.0-alpha.1
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 +427 -82
- package/package.json +25 -14
- package/src/assets/icons/LICENSE.md +35 -0
- package/src/assets/icons/binoculars.svg +1 -0
- package/src/assets/icons/caret-right.svg +1 -0
- package/src/assets/icons/check.svg +1 -0
- package/src/assets/icons/copy.svg +1 -0
- package/src/assets/icons/seal-check-duotone.svg +1 -0
- package/src/assets/icons/seal-question-duotone.svg +1 -0
- package/src/assets/icons/warning-circle-duotone.svg +1 -0
- package/src/assets/icons/warning-duotone.svg +1 -0
- package/src/assets/icons/x.svg +1 -0
- package/src/components/Metadata.astro +145 -0
- package/src/components/index.ts +2 -0
- package/src/integration.d.ts +112 -0
- package/src/integration.js +508 -0
- package/src/lib/checks.d.ts +70 -0
- package/src/lib/checks.js +193 -0
- package/src/lib/config.ts +111 -0
- package/src/lib/favicon-files.d.ts +29 -0
- package/src/lib/favicon-files.js +103 -0
- package/src/lib/favicon-image.ts +124 -0
- package/src/lib/favicon.ts +167 -0
- package/src/lib/head.d.ts +25 -0
- package/src/lib/head.js +141 -0
- package/src/lib/manifest.ts +126 -0
- package/src/lib/rules.d.ts +73 -0
- package/src/lib/rules.js +902 -0
- package/src/lib/schema.ts +208 -0
- package/src/lib/site.ts +72 -0
- package/src/lib/url.ts +79 -0
- package/src/routes/favicon.ts +14 -0
- package/src/routes/manifest.ts +18 -0
- package/src/runtime.ts +16 -0
- package/src/toolbar/app.ts +72 -0
- package/src/toolbar/assets.ts +81 -0
- package/src/toolbar/data.ts +282 -0
- package/src/toolbar/define.ts +7 -0
- package/src/toolbar/env.d.ts +10 -0
- package/src/toolbar/highlight.ts +83 -0
- package/src/toolbar/styles/shared.css +18 -0
- package/src/toolbar/styles/tokens.css +182 -0
- package/src/toolbar/toolbar-badge.ts +78 -0
- package/src/toolbar/ui/alert/alert.css +126 -0
- package/src/toolbar/ui/alert/alert.ts +102 -0
- package/src/toolbar/ui/badge/badge.css +41 -0
- package/src/toolbar/ui/badge/badge.ts +24 -0
- package/src/toolbar/ui/code-block/code-block.css +90 -0
- package/src/toolbar/ui/code-block/code-block.ts +68 -0
- package/src/toolbar/ui/color-swatch/color-swatch.css +15 -0
- package/src/toolbar/ui/color-swatch/color-swatch.ts +31 -0
- package/src/toolbar/ui/icon/icon.css +11 -0
- package/src/toolbar/ui/icon/icon.ts +46 -0
- package/src/toolbar/ui/image/image.css +18 -0
- package/src/toolbar/ui/image/image.ts +58 -0
- package/src/toolbar/ui/inline-code/inline-code.css +16 -0
- package/src/toolbar/ui/inline-code/inline-code.ts +41 -0
- package/src/toolbar/ui/section/section.css +88 -0
- package/src/toolbar/ui/section/section.ts +78 -0
- package/src/toolbar/ui/tab/tab.css +7 -0
- package/src/toolbar/ui/tab/tab.ts +47 -0
- package/src/toolbar/ui/tab-group/tab-group.css +42 -0
- package/src/toolbar/ui/tab-group/tab-group.ts +101 -0
- package/src/toolbar/ui/table/table.css +124 -0
- package/src/toolbar/ui/table/table.ts +155 -0
- package/src/toolbar/views/code-blocks.ts +35 -0
- package/src/toolbar/views/metadata-panel.css +101 -0
- package/src/toolbar/views/metadata-panel.ts +125 -0
- package/src/toolbar/views/overview-view.css +27 -0
- package/src/toolbar/views/overview-view.ts +401 -0
- package/src/toolbar/views/previews/discord.css +70 -0
- package/src/toolbar/views/previews/discord.ts +60 -0
- package/src/toolbar/views/previews/facebook.css +65 -0
- package/src/toolbar/views/previews/facebook.ts +36 -0
- package/src/toolbar/views/previews/google.css +75 -0
- package/src/toolbar/views/previews/google.ts +36 -0
- package/src/toolbar/views/previews/linkedin.css +58 -0
- package/src/toolbar/views/previews/linkedin.ts +34 -0
- package/src/toolbar/views/previews/page.ts +129 -0
- package/src/toolbar/views/previews/platform.ts +14 -0
- package/src/toolbar/views/previews/previews-view.css +37 -0
- package/src/toolbar/views/previews/previews-view.ts +52 -0
- package/src/toolbar/views/previews/slack.css +61 -0
- package/src/toolbar/views/previews/slack.ts +47 -0
- package/src/toolbar/views/previews/twitter.css +89 -0
- package/src/toolbar/views/previews/twitter.ts +57 -0
- package/src/toolbar/views/previews/whatsapp.css +88 -0
- package/src/toolbar/views/previews/whatsapp.ts +65 -0
- package/src/toolbar/views/raw-view.ts +41 -0
- package/index.ts +0 -31
- package/src/components/Canonical.astro +0 -11
- package/src/components/Description.astro +0 -9
- package/src/components/Favicon.astro +0 -96
- package/src/components/Head.astro +0 -94
- package/src/components/Keywords.astro +0 -9
- package/src/components/LanguageAlternates.astro +0 -18
- package/src/components/OpenGraph.astro +0 -75
- package/src/components/Robots.astro +0 -25
- package/src/components/Schema.astro +0 -18
- package/src/components/Title.astro +0 -13
- package/src/components/Twitter.astro +0 -27
- package/src/lib/metadata.ts +0 -77
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/* Slack: a link unfurl under a message */
|
|
2
|
+
.slack {
|
|
3
|
+
width: 100%;
|
|
4
|
+
max-width: 520px;
|
|
5
|
+
padding: 16px;
|
|
6
|
+
font-family: Lato, "Helvetica Neue", Helvetica, "Segoe UI", sans-serif;
|
|
7
|
+
border-radius: 8px;
|
|
8
|
+
background: #1a1d21;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
.slack .unfurl {
|
|
12
|
+
display: flex;
|
|
13
|
+
gap: 12px;
|
|
14
|
+
padding-left: 12px;
|
|
15
|
+
border-left: 4px solid #35373b;
|
|
16
|
+
|
|
17
|
+
.body {
|
|
18
|
+
display: grid;
|
|
19
|
+
gap: 2px;
|
|
20
|
+
min-width: 0;
|
|
21
|
+
font-size: 15px;
|
|
22
|
+
line-height: 22px;
|
|
23
|
+
color: #d1d2d3;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
.site {
|
|
27
|
+
display: flex;
|
|
28
|
+
align-items: center;
|
|
29
|
+
gap: 6px;
|
|
30
|
+
font-weight: 700;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
.favicon {
|
|
34
|
+
width: 16px;
|
|
35
|
+
height: 16px;
|
|
36
|
+
border-radius: 2px;
|
|
37
|
+
object-fit: contain;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
.title {
|
|
41
|
+
font-weight: 700;
|
|
42
|
+
color: #1d9bd1;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/* A large image: under the text, at most 360 px wide */
|
|
46
|
+
.image {
|
|
47
|
+
width: 100%;
|
|
48
|
+
max-width: 360px;
|
|
49
|
+
aspect-ratio: 1.91 / 1;
|
|
50
|
+
margin-top: 8px;
|
|
51
|
+
border-radius: 8px;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/* A small image: on the right */
|
|
55
|
+
.thumbnail {
|
|
56
|
+
flex: none;
|
|
57
|
+
width: 72px;
|
|
58
|
+
height: 72px;
|
|
59
|
+
border-radius: 4px;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { html, nothing, unsafeCSS } from "lit"
|
|
2
|
+
|
|
3
|
+
import "../../ui/image/image.ts"
|
|
4
|
+
|
|
5
|
+
import type { Platform } from "./platform.ts"
|
|
6
|
+
import styles from "./slack.css?inline"
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* A link unfurl in a message, in dark mode.
|
|
10
|
+
*
|
|
11
|
+
* - Uses `og:site_name` with the favicon, `og:title`, `og:description` and `og:image`.
|
|
12
|
+
* - `twitter:card` with `summary_large_image` shows a large image under the text. Else the image is small, on the right.
|
|
13
|
+
* - An image smaller than 60 × 60 px does not show.
|
|
14
|
+
*/
|
|
15
|
+
export const slack: Platform = {
|
|
16
|
+
name: "Slack",
|
|
17
|
+
styles: unsafeCSS(styles),
|
|
18
|
+
render(page) {
|
|
19
|
+
const large = page.twitter.card === "summary_large_image"
|
|
20
|
+
const image = page.og.image
|
|
21
|
+
|
|
22
|
+
return html`
|
|
23
|
+
<div class="slack">
|
|
24
|
+
<div class="unfurl">
|
|
25
|
+
<div class="body">
|
|
26
|
+
<div class="site">
|
|
27
|
+
${page.favicon ? html`<img class="favicon" src=${page.favicon} alt="" />` : nothing}
|
|
28
|
+
${page.og.siteName ?? page.url.host}
|
|
29
|
+
</div>
|
|
30
|
+
<div class="title">${page.og.title ?? page.title}</div>
|
|
31
|
+
<div class="text">${page.og.description ?? page.description}</div>
|
|
32
|
+
${
|
|
33
|
+
large && image
|
|
34
|
+
? html`<astro-metadata-image class="image" .src=${image}></astro-metadata-image>`
|
|
35
|
+
: nothing
|
|
36
|
+
}
|
|
37
|
+
</div>
|
|
38
|
+
${
|
|
39
|
+
!large && image
|
|
40
|
+
? html`<astro-metadata-image class="thumbnail" .src=${image}></astro-metadata-image>`
|
|
41
|
+
: nothing
|
|
42
|
+
}
|
|
43
|
+
</div>
|
|
44
|
+
</div>
|
|
45
|
+
`
|
|
46
|
+
},
|
|
47
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/* Twitter (X): a link card in a post, 506 px wide */
|
|
2
|
+
.twitter {
|
|
3
|
+
width: 100%;
|
|
4
|
+
max-width: 506px;
|
|
5
|
+
padding: 12px;
|
|
6
|
+
font-family: -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
|
7
|
+
border-radius: 8px;
|
|
8
|
+
background: #000000;
|
|
9
|
+
|
|
10
|
+
.from {
|
|
11
|
+
margin-top: 4px;
|
|
12
|
+
font-size: 13px;
|
|
13
|
+
line-height: 16px;
|
|
14
|
+
color: #71767b;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
.twitter .large {
|
|
19
|
+
position: relative;
|
|
20
|
+
overflow: hidden;
|
|
21
|
+
border: 1px solid #2f3336;
|
|
22
|
+
border-radius: 16px;
|
|
23
|
+
|
|
24
|
+
.image {
|
|
25
|
+
width: 100%;
|
|
26
|
+
aspect-ratio: 1.91 / 1;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/* The title on the image: one line */
|
|
30
|
+
.overlay {
|
|
31
|
+
position: absolute;
|
|
32
|
+
bottom: 12px;
|
|
33
|
+
left: 12px;
|
|
34
|
+
max-width: calc(100% - 24px);
|
|
35
|
+
overflow: hidden;
|
|
36
|
+
padding: 0 4px;
|
|
37
|
+
font-size: 13px;
|
|
38
|
+
line-height: 20px;
|
|
39
|
+
color: #ffffff;
|
|
40
|
+
text-overflow: ellipsis;
|
|
41
|
+
white-space: nowrap;
|
|
42
|
+
border-radius: 4px;
|
|
43
|
+
background: rgba(0, 0, 0, 0.77);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
.twitter .small {
|
|
48
|
+
display: flex;
|
|
49
|
+
overflow: hidden;
|
|
50
|
+
border: 1px solid #2f3336;
|
|
51
|
+
border-radius: 16px;
|
|
52
|
+
|
|
53
|
+
.image {
|
|
54
|
+
flex: none;
|
|
55
|
+
width: 130px;
|
|
56
|
+
height: 130px;
|
|
57
|
+
border-right: 1px solid #2f3336;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
.body {
|
|
61
|
+
display: flex;
|
|
62
|
+
flex-direction: column;
|
|
63
|
+
justify-content: center;
|
|
64
|
+
gap: 2px;
|
|
65
|
+
min-width: 0;
|
|
66
|
+
padding: 12px;
|
|
67
|
+
font-size: 15px;
|
|
68
|
+
line-height: 20px;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
.host {
|
|
72
|
+
color: #71767b;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
.title {
|
|
76
|
+
overflow: hidden;
|
|
77
|
+
color: #e7e9ea;
|
|
78
|
+
text-overflow: ellipsis;
|
|
79
|
+
white-space: nowrap;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.text {
|
|
83
|
+
display: -webkit-box;
|
|
84
|
+
overflow: hidden;
|
|
85
|
+
color: #71767b;
|
|
86
|
+
-webkit-box-orient: vertical;
|
|
87
|
+
-webkit-line-clamp: 2;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { html, unsafeCSS } from "lit"
|
|
2
|
+
|
|
3
|
+
import "../../ui/image/image.ts"
|
|
4
|
+
|
|
5
|
+
import { withCode } from "../../ui/inline-code/inline-code.ts"
|
|
6
|
+
import type { Platform } from "./platform.ts"
|
|
7
|
+
import styles from "./twitter.css?inline"
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* A link card in a post in dark mode ("Lights out").
|
|
11
|
+
*
|
|
12
|
+
* - Twitter (X) shows a card only with `twitter:card`.
|
|
13
|
+
* - `summary_large_image` shows a large image, `summary` a small square image.
|
|
14
|
+
* - Uses `twitter:title`, `twitter:description` and `twitter:image`, or else the Open Graph values.
|
|
15
|
+
* - The large card shows only the title, on one line on the image, and the domain under it.
|
|
16
|
+
*/
|
|
17
|
+
export const twitter: Platform = {
|
|
18
|
+
name: "Twitter (X)",
|
|
19
|
+
styles: unsafeCSS(styles),
|
|
20
|
+
render(page) {
|
|
21
|
+
const card = page.twitter.card
|
|
22
|
+
if (!card) {
|
|
23
|
+
return html`<p class="notice">
|
|
24
|
+
${withCode("Twitter (X) shows no card: the page has no `twitter:card`.")}
|
|
25
|
+
</p>`
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const title = page.twitter.title ?? page.og.title ?? page.title
|
|
29
|
+
const text = page.twitter.description ?? page.og.description ?? page.description
|
|
30
|
+
const image = page.twitter.image ?? page.og.image
|
|
31
|
+
|
|
32
|
+
if (card === "summary_large_image") {
|
|
33
|
+
return html`
|
|
34
|
+
<div class="twitter">
|
|
35
|
+
<div class="large">
|
|
36
|
+
<astro-metadata-image class="image" .src=${image}></astro-metadata-image>
|
|
37
|
+
<span class="overlay">${title}</span>
|
|
38
|
+
</div>
|
|
39
|
+
<div class="from">From ${page.url.host}</div>
|
|
40
|
+
</div>
|
|
41
|
+
`
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return html`
|
|
45
|
+
<div class="twitter">
|
|
46
|
+
<div class="small">
|
|
47
|
+
<astro-metadata-image class="image" .src=${image}></astro-metadata-image>
|
|
48
|
+
<div class="body">
|
|
49
|
+
<div class="host">${page.url.host}</div>
|
|
50
|
+
<div class="title">${title}</div>
|
|
51
|
+
<div class="text">${text}</div>
|
|
52
|
+
</div>
|
|
53
|
+
</div>
|
|
54
|
+
</div>
|
|
55
|
+
`
|
|
56
|
+
},
|
|
57
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/* WhatsApp: a sent message with the link preview, at most 330 px wide */
|
|
2
|
+
.whatsapp {
|
|
3
|
+
width: 100%;
|
|
4
|
+
max-width: 330px;
|
|
5
|
+
font-family: -apple-system, "Segoe UI", Helvetica, Arial, sans-serif;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
.whatsapp .bubble {
|
|
9
|
+
width: 100%;
|
|
10
|
+
padding: 4px;
|
|
11
|
+
border-radius: 8px;
|
|
12
|
+
background: #005c4b;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
.whatsapp .preview {
|
|
16
|
+
overflow: hidden;
|
|
17
|
+
border-radius: 6px;
|
|
18
|
+
background: #025144;
|
|
19
|
+
|
|
20
|
+
&.large .image {
|
|
21
|
+
width: 100%;
|
|
22
|
+
aspect-ratio: 1.91 / 1;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
.row {
|
|
26
|
+
display: flex;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/* A small image: a square on the left of the text */
|
|
30
|
+
&.small .image {
|
|
31
|
+
flex: none;
|
|
32
|
+
width: 82px;
|
|
33
|
+
height: 82px;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
.body {
|
|
37
|
+
min-width: 0;
|
|
38
|
+
padding: 6px 10px 8px;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.title {
|
|
42
|
+
display: -webkit-box;
|
|
43
|
+
overflow: hidden;
|
|
44
|
+
font-size: 14px;
|
|
45
|
+
font-weight: 600;
|
|
46
|
+
line-height: 19px;
|
|
47
|
+
color: #e9edef;
|
|
48
|
+
-webkit-box-orient: vertical;
|
|
49
|
+
-webkit-line-clamp: 2;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
.text {
|
|
53
|
+
display: -webkit-box;
|
|
54
|
+
overflow: hidden;
|
|
55
|
+
margin-top: 2px;
|
|
56
|
+
font-size: 13px;
|
|
57
|
+
line-height: 17px;
|
|
58
|
+
color: rgba(233, 237, 239, 0.7);
|
|
59
|
+
-webkit-box-orient: vertical;
|
|
60
|
+
-webkit-line-clamp: 2;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
.host {
|
|
64
|
+
margin-top: 4px;
|
|
65
|
+
font-size: 12px;
|
|
66
|
+
line-height: 16px;
|
|
67
|
+
color: rgba(233, 237, 239, 0.6);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/* The message text: the link, and the time */
|
|
72
|
+
.whatsapp .message {
|
|
73
|
+
padding: 6px 6px 2px;
|
|
74
|
+
font-size: 14px;
|
|
75
|
+
line-height: 19px;
|
|
76
|
+
|
|
77
|
+
.link {
|
|
78
|
+
overflow-wrap: anywhere;
|
|
79
|
+
color: #53bdeb;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.time {
|
|
83
|
+
float: right;
|
|
84
|
+
margin: 6px 0 0 8px;
|
|
85
|
+
font-size: 11px;
|
|
86
|
+
color: rgba(255, 255, 255, 0.6);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { html, nothing, unsafeCSS } from "lit"
|
|
2
|
+
|
|
3
|
+
import "../../ui/image/image.ts"
|
|
4
|
+
|
|
5
|
+
import type { Platform } from "./platform.ts"
|
|
6
|
+
import styles from "./whatsapp.css?inline"
|
|
7
|
+
|
|
8
|
+
// The image sizes of WhatsApp: from 300 px a large image, from 100 px a small square
|
|
9
|
+
const LARGE_WIDTH = 300
|
|
10
|
+
const SMALL_WIDTH = 100
|
|
11
|
+
// A large image can be at most 4 times as wide as it is high
|
|
12
|
+
const MAX_RATIO = 4
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* A sent message with a link preview, in dark mode.
|
|
16
|
+
*
|
|
17
|
+
* - Uses `og:title`, `og:description` and `og:image`, or else the `<title>` and the description.
|
|
18
|
+
* - An image of 300 px or wider, with a ratio up to 4:1, shows large. An image of 100–299 px shows as a small square. A smaller image does not show.
|
|
19
|
+
* - The image must be smaller than about 600 KB, and the URL must be absolute.
|
|
20
|
+
* - The title and the description show on two lines at most.
|
|
21
|
+
*/
|
|
22
|
+
export const whatsapp: Platform = {
|
|
23
|
+
name: "WhatsApp",
|
|
24
|
+
styles: unsafeCSS(styles),
|
|
25
|
+
render(page) {
|
|
26
|
+
const { image, imageWidth: width, imageHeight: height } = page.og
|
|
27
|
+
const ratio = width && height ? width / height : 1.91
|
|
28
|
+
|
|
29
|
+
let size: "large" | "small" | "none" = image ? "large" : "none"
|
|
30
|
+
if (image && width !== undefined) {
|
|
31
|
+
if (width < SMALL_WIDTH) size = "none"
|
|
32
|
+
else if (width < LARGE_WIDTH || ratio > MAX_RATIO) size = "small"
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return html`
|
|
36
|
+
<div class="whatsapp">
|
|
37
|
+
<div class="bubble">
|
|
38
|
+
<div class="preview ${size}">
|
|
39
|
+
${
|
|
40
|
+
size === "large"
|
|
41
|
+
? html`<astro-metadata-image class="image" .src=${image}></astro-metadata-image>`
|
|
42
|
+
: nothing
|
|
43
|
+
}
|
|
44
|
+
<div class="row">
|
|
45
|
+
${
|
|
46
|
+
size === "small"
|
|
47
|
+
? html`<astro-metadata-image class="image" .src=${image}></astro-metadata-image>`
|
|
48
|
+
: nothing
|
|
49
|
+
}
|
|
50
|
+
<div class="body">
|
|
51
|
+
<div class="title">${page.og.title ?? page.title}</div>
|
|
52
|
+
<div class="text">${page.og.description ?? page.description}</div>
|
|
53
|
+
<div class="host">${page.url.host}</div>
|
|
54
|
+
</div>
|
|
55
|
+
</div>
|
|
56
|
+
</div>
|
|
57
|
+
<div class="message">
|
|
58
|
+
<span class="link">${page.url.href}</span>
|
|
59
|
+
<span class="time">12:00 ✓✓</span>
|
|
60
|
+
</div>
|
|
61
|
+
</div>
|
|
62
|
+
</div>
|
|
63
|
+
`
|
|
64
|
+
},
|
|
65
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { html, LitElement, unsafeCSS } from "lit"
|
|
2
|
+
|
|
3
|
+
import type { PageMetadata } from "../data.ts"
|
|
4
|
+
import shared from "../styles/shared.css?inline"
|
|
5
|
+
|
|
6
|
+
import "../ui/code-block/code-block.ts"
|
|
7
|
+
|
|
8
|
+
import { define } from "../define.ts"
|
|
9
|
+
|
|
10
|
+
import "../ui/section/section.ts"
|
|
11
|
+
|
|
12
|
+
import { manifestBlock, schemaBlocks } from "./code-blocks.ts"
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* All metadata as raw code: the head tags, the JSON-LD scripts and the web app manifest.
|
|
16
|
+
*/
|
|
17
|
+
export class RawView extends LitElement {
|
|
18
|
+
static properties = { data: { attribute: false } }
|
|
19
|
+
static styles = unsafeCSS(shared)
|
|
20
|
+
|
|
21
|
+
declare data: PageMetadata
|
|
22
|
+
|
|
23
|
+
render() {
|
|
24
|
+
return html`
|
|
25
|
+
<astro-metadata-section heading="Head">
|
|
26
|
+
<astro-metadata-code .code=${this.data.source} language="html" label="HTML">
|
|
27
|
+
</astro-metadata-code>
|
|
28
|
+
</astro-metadata-section>
|
|
29
|
+
|
|
30
|
+
<astro-metadata-section heading="Structured data" ?empty=${this.data.schemas.length === 0}
|
|
31
|
+
>${schemaBlocks(this.data.schemas)}</astro-metadata-section
|
|
32
|
+
>
|
|
33
|
+
|
|
34
|
+
<astro-metadata-section heading="Web app manifest" ?empty=${!this.data.manifest}>
|
|
35
|
+
${manifestBlock(this.data.manifest)}
|
|
36
|
+
</astro-metadata-section>
|
|
37
|
+
`
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
define("astro-metadata-raw", RawView)
|
package/index.ts
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
export type { Props as CanonicalProps } from "./src/components/Canonical.astro"
|
|
2
|
-
export { default as Canonical } from "./src/components/Canonical.astro"
|
|
3
|
-
export type { Props as DescriptionProps } from "./src/components/Description.astro"
|
|
4
|
-
export { default as Description } from "./src/components/Description.astro"
|
|
5
|
-
export type { FaviconFile, Props as FaviconProps } from "./src/components/Favicon.astro"
|
|
6
|
-
export { default as Favicon } from "./src/components/Favicon.astro"
|
|
7
|
-
export type { Props as HeadProps } from "./src/components/Head.astro"
|
|
8
|
-
export { default as Head } from "./src/components/Head.astro"
|
|
9
|
-
export type { Props as KeywordsProps } from "./src/components/Keywords.astro"
|
|
10
|
-
export { default as Keywords } from "./src/components/Keywords.astro"
|
|
11
|
-
export type {
|
|
12
|
-
LanguageAlternate,
|
|
13
|
-
Props as LanguageAlternatesProps,
|
|
14
|
-
} from "./src/components/LanguageAlternates.astro"
|
|
15
|
-
export { default as LanguageAlternates } from "./src/components/LanguageAlternates.astro"
|
|
16
|
-
export type {
|
|
17
|
-
OpenGraphAudio,
|
|
18
|
-
OpenGraphImage,
|
|
19
|
-
OpenGraphVideo,
|
|
20
|
-
Props as OpenGraphProps,
|
|
21
|
-
} from "./src/components/OpenGraph.astro"
|
|
22
|
-
export { default as OpenGraph } from "./src/components/OpenGraph.astro"
|
|
23
|
-
export type { Props as RobotsProps } from "./src/components/Robots.astro"
|
|
24
|
-
export { default as Robots } from "./src/components/Robots.astro"
|
|
25
|
-
export type { Props as SchemaProps } from "./src/components/Schema.astro"
|
|
26
|
-
export { default as Schema } from "./src/components/Schema.astro"
|
|
27
|
-
export type { Props as TitleProps } from "./src/components/Title.astro"
|
|
28
|
-
export { default as Title } from "./src/components/Title.astro"
|
|
29
|
-
export type { Props as TwitterProps } from "./src/components/Twitter.astro"
|
|
30
|
-
export { default as Twitter } from "./src/components/Twitter.astro"
|
|
31
|
-
export { Metadata } from "./src/lib/metadata.ts"
|
|
@@ -1,96 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
export type FaviconFile = {
|
|
3
|
-
path: string
|
|
4
|
-
size?: number
|
|
5
|
-
theme?: "light" | "dark"
|
|
6
|
-
apple?: boolean
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export type Props = {
|
|
10
|
-
icons: FaviconFile[]
|
|
11
|
-
manifest?: string
|
|
12
|
-
sort?: boolean
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
const { icons, manifest, sort = true } = Astro.props
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* Returns the file extension of a favicon path in lowercase, without the
|
|
19
|
-
* query string or the hash. For example, "/icon.SVG?v=2" gives "svg".
|
|
20
|
-
*
|
|
21
|
-
* @param path - The path to the favicon file.
|
|
22
|
-
* @returns The file extension, or an empty string if the path has none.
|
|
23
|
-
*/
|
|
24
|
-
function getExtension(path: string): string {
|
|
25
|
-
const file = path.split(/[?#]/)[0].toLowerCase()
|
|
26
|
-
const dot = file.lastIndexOf(".")
|
|
27
|
-
return dot === -1 ? "" : file.slice(dot + 1)
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* Detects the MIME type of a favicon file from its extension.
|
|
32
|
-
*
|
|
33
|
-
* @param path - The path to the favicon file.
|
|
34
|
-
* @returns The MIME type of the file.
|
|
35
|
-
*/
|
|
36
|
-
function getMimeType(path: string): string {
|
|
37
|
-
const extension = getExtension(path)
|
|
38
|
-
if (extension === "svg") return "image/svg+xml"
|
|
39
|
-
if (extension === "png") return "image/png"
|
|
40
|
-
if (extension === "webp") return "image/webp"
|
|
41
|
-
if (extension === "jpg" || extension === "jpeg") return "image/jpeg"
|
|
42
|
-
return "image/x-icon"
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* Builds the media query string for a given theme.
|
|
47
|
-
*
|
|
48
|
-
* @param theme - The theme to build the media query for.
|
|
49
|
-
* @returns The media query string, or undefined if no theme was provided.
|
|
50
|
-
*/
|
|
51
|
-
function getMedia(theme?: "light" | "dark"): string | undefined {
|
|
52
|
-
if (theme === "light") return "(prefers-color-scheme: light)"
|
|
53
|
-
if (theme === "dark") return "(prefers-color-scheme: dark)"
|
|
54
|
-
return undefined
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* Returns a sort priority for a given favicon file.
|
|
59
|
-
* Lower number = rendered first.
|
|
60
|
-
*
|
|
61
|
-
* Order: ico → png → svg → apple → themed variants
|
|
62
|
-
*/
|
|
63
|
-
function getSortOrder(file: FaviconFile): number {
|
|
64
|
-
if (file.apple) return 4
|
|
65
|
-
if (file.theme) return 5
|
|
66
|
-
const extension = getExtension(file.path)
|
|
67
|
-
if (extension === "ico") return 0
|
|
68
|
-
if (["png", "webp", "jpg", "jpeg"].includes(extension)) return 1
|
|
69
|
-
if (extension === "svg") return 2
|
|
70
|
-
return 3
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
const sorted = sort ? [...icons].sort((a, b) => getSortOrder(a) - getSortOrder(b)) : icons
|
|
74
|
-
|
|
75
|
-
const prepared = sorted.map((file) => ({
|
|
76
|
-
path: file.path,
|
|
77
|
-
size: file.size ? `${file.size}x${file.size}` : undefined,
|
|
78
|
-
type: getMimeType(file.path),
|
|
79
|
-
media: getMedia(file.theme),
|
|
80
|
-
apple: file.apple ?? false,
|
|
81
|
-
}))
|
|
82
|
-
---
|
|
83
|
-
|
|
84
|
-
{manifest && <link rel="manifest" href={manifest} />}
|
|
85
|
-
|
|
86
|
-
{
|
|
87
|
-
prepared.map((icon) => {
|
|
88
|
-
if (icon.apple) {
|
|
89
|
-
return <link rel="apple-touch-icon" href={icon.path} sizes={icon.size} />
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
return (
|
|
93
|
-
<link rel="icon" type={icon.type} href={icon.path} sizes={icon.size} media={icon.media} />
|
|
94
|
-
)
|
|
95
|
-
})
|
|
96
|
-
}
|
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
import type { ComponentProps } from "astro/types"
|
|
3
|
-
|
|
4
|
-
import Canonical from "./Canonical.astro"
|
|
5
|
-
import Description from "./Description.astro"
|
|
6
|
-
import Favicon from "./Favicon.astro"
|
|
7
|
-
import Keywords from "./Keywords.astro"
|
|
8
|
-
import LanguageAlternates from "./LanguageAlternates.astro"
|
|
9
|
-
import OpenGraph from "./OpenGraph.astro"
|
|
10
|
-
import type { OpenGraphImage } from "./OpenGraph.astro"
|
|
11
|
-
import Robots from "./Robots.astro"
|
|
12
|
-
import Schema from "./Schema.astro"
|
|
13
|
-
import Title from "./Title.astro"
|
|
14
|
-
import Twitter from "./Twitter.astro"
|
|
15
|
-
|
|
16
|
-
export type Props = {
|
|
17
|
-
title: string
|
|
18
|
-
titleTemplate?: `${string}%s${string}`
|
|
19
|
-
description?: string | false
|
|
20
|
-
canonical?: string | false
|
|
21
|
-
keywords?: string[] | false
|
|
22
|
-
charset?: string
|
|
23
|
-
viewport?: string
|
|
24
|
-
image?: OpenGraphImage | false
|
|
25
|
-
robots?: ComponentProps<typeof Robots> | false
|
|
26
|
-
openGraph?: ComponentProps<typeof OpenGraph> | false
|
|
27
|
-
twitter?: ComponentProps<typeof Twitter> | false
|
|
28
|
-
favicon?: ComponentProps<typeof Favicon> | false
|
|
29
|
-
schema?: ComponentProps<typeof Schema> | false
|
|
30
|
-
languageAlternates?: ComponentProps<typeof LanguageAlternates>["alternates"] | false
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
const {
|
|
34
|
-
title,
|
|
35
|
-
titleTemplate,
|
|
36
|
-
description,
|
|
37
|
-
canonical,
|
|
38
|
-
keywords,
|
|
39
|
-
charset = "UTF-8",
|
|
40
|
-
viewport = "width=device-width, initial-scale=1.0",
|
|
41
|
-
image,
|
|
42
|
-
robots,
|
|
43
|
-
openGraph,
|
|
44
|
-
twitter,
|
|
45
|
-
favicon,
|
|
46
|
-
schema,
|
|
47
|
-
languageAlternates,
|
|
48
|
-
} = Astro.props
|
|
49
|
-
|
|
50
|
-
const enabled = <T,>(value: T | false | undefined): value is T => {
|
|
51
|
-
return value !== false && value !== undefined
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
const og =
|
|
55
|
-
openGraph !== false
|
|
56
|
-
? {
|
|
57
|
-
title,
|
|
58
|
-
description: enabled(description) ? description : undefined,
|
|
59
|
-
image: enabled(image) ? image : undefined,
|
|
60
|
-
url: enabled(canonical) ? canonical : undefined,
|
|
61
|
-
...openGraph,
|
|
62
|
-
}
|
|
63
|
-
: false
|
|
64
|
-
|
|
65
|
-
const tw =
|
|
66
|
-
twitter !== false
|
|
67
|
-
? {
|
|
68
|
-
title,
|
|
69
|
-
description: enabled(description) ? description : undefined,
|
|
70
|
-
image: enabled(image) ? image : undefined,
|
|
71
|
-
...twitter,
|
|
72
|
-
}
|
|
73
|
-
: false
|
|
74
|
-
---
|
|
75
|
-
|
|
76
|
-
<head>
|
|
77
|
-
<meta charset={charset} />
|
|
78
|
-
<slot name="top" />
|
|
79
|
-
<meta name="viewport" content={viewport} />
|
|
80
|
-
|
|
81
|
-
<Title value={title} template={titleTemplate} />
|
|
82
|
-
|
|
83
|
-
{enabled(description) && <Description value={description} />}
|
|
84
|
-
{enabled(canonical) && <Canonical value={canonical} />}
|
|
85
|
-
{enabled(keywords) && <Keywords value={keywords} />}
|
|
86
|
-
{enabled(robots) && <Robots {...robots} />}
|
|
87
|
-
{enabled(og) && <OpenGraph {...og} />}
|
|
88
|
-
{enabled(tw) && <Twitter {...tw} />}
|
|
89
|
-
{enabled(favicon) && <Favicon {...favicon} />}
|
|
90
|
-
{enabled(schema) && <Schema {...schema} />}
|
|
91
|
-
{enabled(languageAlternates) && <LanguageAlternates alternates={languageAlternates} />}
|
|
92
|
-
|
|
93
|
-
<slot />
|
|
94
|
-
</head>
|