@mannisto/astro-metadata 1.0.2 → 2.0.0-alpha.2

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.
Files changed (102) hide show
  1. package/README.md +427 -82
  2. package/package.json +25 -14
  3. package/src/assets/icons/LICENSE.md +35 -0
  4. package/src/assets/icons/binoculars.svg +1 -0
  5. package/src/assets/icons/caret-right.svg +1 -0
  6. package/src/assets/icons/check.svg +1 -0
  7. package/src/assets/icons/copy.svg +1 -0
  8. package/src/assets/icons/seal-check-duotone.svg +1 -0
  9. package/src/assets/icons/seal-question-duotone.svg +1 -0
  10. package/src/assets/icons/warning-circle-duotone.svg +1 -0
  11. package/src/assets/icons/warning-duotone.svg +1 -0
  12. package/src/assets/icons/x.svg +1 -0
  13. package/src/components/Metadata.astro +145 -0
  14. package/src/components/index.ts +2 -0
  15. package/src/integration.d.ts +112 -0
  16. package/src/integration.js +508 -0
  17. package/src/lib/checks.d.ts +70 -0
  18. package/src/lib/checks.js +193 -0
  19. package/src/lib/config.ts +111 -0
  20. package/src/lib/favicon-files.d.ts +29 -0
  21. package/src/lib/favicon-files.js +103 -0
  22. package/src/lib/favicon-image.ts +124 -0
  23. package/src/lib/favicon.ts +167 -0
  24. package/src/lib/head.d.ts +25 -0
  25. package/src/lib/head.js +141 -0
  26. package/src/lib/manifest.ts +126 -0
  27. package/src/lib/rules.d.ts +73 -0
  28. package/src/lib/rules.js +929 -0
  29. package/src/lib/schema.ts +208 -0
  30. package/src/lib/site.ts +72 -0
  31. package/src/lib/url.ts +79 -0
  32. package/src/routes/favicon.ts +14 -0
  33. package/src/routes/manifest.ts +18 -0
  34. package/src/runtime.ts +16 -0
  35. package/src/toolbar/app.ts +72 -0
  36. package/src/toolbar/assets.ts +81 -0
  37. package/src/toolbar/data.ts +282 -0
  38. package/src/toolbar/define.ts +7 -0
  39. package/src/toolbar/env.d.ts +10 -0
  40. package/src/toolbar/highlight.ts +83 -0
  41. package/src/toolbar/styles/shared.css +18 -0
  42. package/src/toolbar/styles/tokens.css +182 -0
  43. package/src/toolbar/toolbar-badge.ts +78 -0
  44. package/src/toolbar/ui/alert/alert.css +126 -0
  45. package/src/toolbar/ui/alert/alert.ts +102 -0
  46. package/src/toolbar/ui/badge/badge.css +41 -0
  47. package/src/toolbar/ui/badge/badge.ts +24 -0
  48. package/src/toolbar/ui/code-block/code-block.css +90 -0
  49. package/src/toolbar/ui/code-block/code-block.ts +68 -0
  50. package/src/toolbar/ui/color-swatch/color-swatch.css +15 -0
  51. package/src/toolbar/ui/color-swatch/color-swatch.ts +31 -0
  52. package/src/toolbar/ui/icon/icon.css +11 -0
  53. package/src/toolbar/ui/icon/icon.ts +46 -0
  54. package/src/toolbar/ui/image/image.css +18 -0
  55. package/src/toolbar/ui/image/image.ts +58 -0
  56. package/src/toolbar/ui/inline-code/inline-code.css +16 -0
  57. package/src/toolbar/ui/inline-code/inline-code.ts +41 -0
  58. package/src/toolbar/ui/section/section.css +88 -0
  59. package/src/toolbar/ui/section/section.ts +78 -0
  60. package/src/toolbar/ui/tab/tab.css +7 -0
  61. package/src/toolbar/ui/tab/tab.ts +47 -0
  62. package/src/toolbar/ui/tab-group/tab-group.css +42 -0
  63. package/src/toolbar/ui/tab-group/tab-group.ts +101 -0
  64. package/src/toolbar/ui/table/table.css +124 -0
  65. package/src/toolbar/ui/table/table.ts +155 -0
  66. package/src/toolbar/views/code-blocks.ts +35 -0
  67. package/src/toolbar/views/metadata-panel.css +105 -0
  68. package/src/toolbar/views/metadata-panel.ts +125 -0
  69. package/src/toolbar/views/overview-view.css +27 -0
  70. package/src/toolbar/views/overview-view.ts +401 -0
  71. package/src/toolbar/views/previews/discord.css +70 -0
  72. package/src/toolbar/views/previews/discord.ts +60 -0
  73. package/src/toolbar/views/previews/facebook.css +65 -0
  74. package/src/toolbar/views/previews/facebook.ts +36 -0
  75. package/src/toolbar/views/previews/google.css +75 -0
  76. package/src/toolbar/views/previews/google.ts +36 -0
  77. package/src/toolbar/views/previews/linkedin.css +58 -0
  78. package/src/toolbar/views/previews/linkedin.ts +34 -0
  79. package/src/toolbar/views/previews/page.ts +129 -0
  80. package/src/toolbar/views/previews/platform.ts +14 -0
  81. package/src/toolbar/views/previews/previews-view.css +37 -0
  82. package/src/toolbar/views/previews/previews-view.ts +52 -0
  83. package/src/toolbar/views/previews/slack.css +61 -0
  84. package/src/toolbar/views/previews/slack.ts +47 -0
  85. package/src/toolbar/views/previews/twitter.css +89 -0
  86. package/src/toolbar/views/previews/twitter.ts +57 -0
  87. package/src/toolbar/views/previews/whatsapp.css +88 -0
  88. package/src/toolbar/views/previews/whatsapp.ts +65 -0
  89. package/src/toolbar/views/raw-view.ts +41 -0
  90. package/index.ts +0 -31
  91. package/src/components/Canonical.astro +0 -11
  92. package/src/components/Description.astro +0 -9
  93. package/src/components/Favicon.astro +0 -96
  94. package/src/components/Head.astro +0 -94
  95. package/src/components/Keywords.astro +0 -9
  96. package/src/components/LanguageAlternates.astro +0 -18
  97. package/src/components/OpenGraph.astro +0 -75
  98. package/src/components/Robots.astro +0 -25
  99. package/src/components/Schema.astro +0 -18
  100. package/src/components/Title.astro +0 -13
  101. package/src/components/Twitter.astro +0 -27
  102. package/src/lib/metadata.ts +0 -77
@@ -0,0 +1,126 @@
1
+ /*
2
+ * The style of the Supabase alert: a dark tinted background and border, and the icon
3
+ * in a filled square of the level color. The text keeps the normal text colors.
4
+ */
5
+
6
+ :host {
7
+ display: grid;
8
+ grid-template-columns: auto 1fr;
9
+ align-items: flex-start;
10
+ gap: var(--space-200);
11
+ background: var(--background);
12
+ border-radius: var(--radius);
13
+ border: 1px solid var(--border);
14
+ font-size: var(--font-size-small);
15
+ color: var(--color);
16
+ padding: var(--space-300) var(--space-200);
17
+ }
18
+
19
+ :host([level="success"]) {
20
+ --background: color-mix(in srgb, var(--green-350) 15%, transparent);
21
+ --icon: var(--green-250);
22
+ --text: var(--green-50);
23
+ --border: var(--green-350);
24
+ }
25
+
26
+ :host([level="info"]) {
27
+ --background: color-mix(in srgb, var(--blue-350) 15%, transparent);
28
+ --icon: var(--blue-250);
29
+ --text: var(--blue-50);
30
+ --border: var(--blue-350);
31
+ }
32
+
33
+ :host([level="warning"]) {
34
+ --background: color-mix(in srgb, var(--yellow-350) 10%, transparent);
35
+ --icon: var(--yellow-250);
36
+ --text: var(--yellow-150);
37
+ --border: var(--yellow-500);
38
+ }
39
+
40
+ :host([level="error"]) {
41
+ --background: var(--red-950);
42
+ --icon: var(--red-250);
43
+ --text: var(--red-150);
44
+ --border: var(--red-500);
45
+ }
46
+
47
+ .icon {
48
+ --icon-size: 18px;
49
+ color: var(--icon);
50
+ margin: 0;
51
+ display: flex;
52
+ align-items: center;
53
+ }
54
+
55
+ .content {
56
+ display: flex;
57
+ flex-direction: column;
58
+ justify-content: center;
59
+ color: var(--text);
60
+ font-size: var(--font-size-small);
61
+ font-weight: 400;
62
+ height: 100%;
63
+
64
+ /* Inline code in the alert uses the colors of the level */
65
+ --code-color: var(--text);
66
+ --code-border: color-mix(in srgb, var(--border) 40%, transparent);
67
+ --code-background: color-mix(in srgb, var(--border) 20%, transparent);
68
+
69
+ .heading {
70
+ font-weight: 500;
71
+ margin: 0;
72
+ line-height: 1.2;
73
+ }
74
+ }
75
+
76
+ /* The list of items, each with a dot in the color of its level */
77
+ .items {
78
+ display: grid;
79
+ gap: var(--space-50);
80
+ margin: 0.5em 0 0;
81
+ padding: 0;
82
+ list-style: none;
83
+ }
84
+
85
+ .item {
86
+ display: flex;
87
+ gap: var(--space-200);
88
+
89
+ &::before {
90
+ flex: none;
91
+ width: 6px;
92
+ height: 6px;
93
+ margin-top: 0.6em;
94
+ content: "";
95
+ border-radius: 50%;
96
+ background: currentColor;
97
+ }
98
+
99
+ &.error::before {
100
+ background: var(--red-400);
101
+ }
102
+
103
+ &.warning::before {
104
+ background: var(--yellow-400);
105
+ }
106
+
107
+ &.info::before {
108
+ background: var(--blue-400);
109
+ }
110
+ }
111
+
112
+ /* An item with a target: a click shows the row of the problem */
113
+ .target {
114
+ padding: 0;
115
+ font: inherit;
116
+ color: inherit;
117
+ text-align: left;
118
+ cursor: pointer;
119
+ border: 0;
120
+ background: none;
121
+
122
+ &:hover {
123
+ text-decoration: underline;
124
+ text-underline-offset: 2px;
125
+ }
126
+ }
@@ -0,0 +1,102 @@
1
+ import { html, LitElement, nothing, unsafeCSS } from "lit"
2
+ import type { TemplateResult } from "lit"
3
+
4
+ import { define } from "../../define.ts"
5
+ import type { Level } from "../badge/badge.ts"
6
+ import type { IconName } from "../icon/icon.ts"
7
+ import styles from "./alert.css?inline"
8
+
9
+ import "../icon/icon.ts"
10
+
11
+ const ICONS: Record<Level, IconName> = {
12
+ success: "seal-check",
13
+ info: "seal-question",
14
+ warning: "warning",
15
+ error: "warning-circle",
16
+ }
17
+
18
+ /** An item in the list of an alert, e.g. one problem of the page. */
19
+ export type AlertItem = {
20
+ /** The level of the item gives the color of its dot. */
21
+ level?: Level
22
+ content: string | TemplateResult
23
+ /** Makes the item a button: a click fires a `select` event with this value. */
24
+ target?: string
25
+ }
26
+
27
+ /**
28
+ * A message with a level: a heading, an optional list of items, and optional content under it.
29
+ * A click on an item with a `target` fires a `select` event with the target as its detail.
30
+ *
31
+ * @example
32
+ * <astro-metadata-alert level="warning" heading="2 warnings" .items=${items}>
33
+ * </astro-metadata-alert>
34
+ */
35
+ export class Alert extends LitElement {
36
+ static properties = {
37
+ level: { reflect: true },
38
+ heading: {},
39
+ items: { attribute: false },
40
+ }
41
+
42
+ static styles = unsafeCSS(styles)
43
+
44
+ declare level: Level
45
+ declare heading: string
46
+ declare items: AlertItem[]
47
+
48
+ constructor() {
49
+ super()
50
+ this.level = "info"
51
+ this.heading = ""
52
+ this.items = []
53
+ }
54
+
55
+ select(target: string): void {
56
+ this.dispatchEvent(new CustomEvent("select", { detail: target }))
57
+ }
58
+
59
+ renderContent(item: AlertItem) {
60
+ if (!item.target) return html`<span>${item.content}</span>`
61
+
62
+ const target = item.target
63
+ return html`
64
+ <button
65
+ type="button"
66
+ class="target"
67
+ @click=${() => {
68
+ this.select(target)
69
+ }}
70
+ >
71
+ ${item.content}
72
+ </button>
73
+ `
74
+ }
75
+
76
+ renderItems() {
77
+ if (this.items.length === 0) return nothing
78
+
79
+ return html`
80
+ <ul class="items">
81
+ ${this.items.map((item) => {
82
+ return html`<li class="item ${item.level ?? ""}">${this.renderContent(item)}</li>`
83
+ })}
84
+ </ul>
85
+ `
86
+ }
87
+
88
+ render() {
89
+ return html`
90
+ <div class="icon">
91
+ <astro-metadata-icon name=${ICONS[this.level]}></astro-metadata-icon>
92
+ </div>
93
+ <div class="content">
94
+ <p class="heading">${this.heading}</p>
95
+ ${this.renderItems()}
96
+ <slot></slot>
97
+ </div>
98
+ `
99
+ }
100
+ }
101
+
102
+ define("astro-metadata-alert", Alert)
@@ -0,0 +1,41 @@
1
+ :host {
2
+ display: inline-flex;
3
+ align-items: center;
4
+ justify-content: center;
5
+ box-sizing: border-box;
6
+ height: 20px;
7
+ min-width: 20px;
8
+ padding: 0 var(--space-150);
9
+ font-family: var(--font-mono);
10
+ font-size: var(--font-size-tiny);
11
+ line-height: 1;
12
+ color: var(--zinc-300);
13
+ border: 1px solid var(--zinc-800);
14
+ border-radius: 999px;
15
+ background: var(--zinc-900);
16
+ }
17
+
18
+ /* The levels use the same colors as the alert */
19
+ :host([level="error"]) {
20
+ color: var(--red-150);
21
+ border-color: var(--red-500);
22
+ background: var(--red-950);
23
+ }
24
+
25
+ :host([level="info"]) {
26
+ color: var(--blue-50);
27
+ border-color: var(--blue-350);
28
+ background: color-mix(in srgb, var(--blue-350) 15%, transparent);
29
+ }
30
+
31
+ :host([level="warning"]) {
32
+ color: var(--yellow-150);
33
+ border-color: var(--yellow-500);
34
+ background: color-mix(in srgb, var(--yellow-350) 10%, transparent);
35
+ }
36
+
37
+ :host([level="success"]) {
38
+ color: var(--green-50);
39
+ border-color: var(--green-350);
40
+ background: color-mix(in srgb, var(--green-350) 15%, transparent);
41
+ }
@@ -0,0 +1,24 @@
1
+ import { html, LitElement, unsafeCSS } from "lit"
2
+
3
+ import { define } from "../../define.ts"
4
+ import styles from "./badge.css?inline"
5
+
6
+ export type Level = "success" | "info" | "warning" | "error"
7
+
8
+ /**
9
+ * A small rounded label, e.g. the number of problems. `level` sets the colors.
10
+ *
11
+ * @example <astro-metadata-badge level="warning">2</astro-metadata-badge>
12
+ */
13
+ export class Badge extends LitElement {
14
+ static properties = { level: { reflect: true } }
15
+ static styles = unsafeCSS(styles)
16
+
17
+ declare level: Level | undefined
18
+
19
+ render() {
20
+ return html`<slot></slot>`
21
+ }
22
+ }
23
+
24
+ define("astro-metadata-badge", Badge)
@@ -0,0 +1,90 @@
1
+ :host {
2
+ display: block;
3
+ }
4
+
5
+ .block {
6
+ position: relative;
7
+ overflow: hidden;
8
+ border: 1px solid var(--zinc-800);
9
+ border-radius: var(--radius);
10
+ background: var(--zinc-950);
11
+ }
12
+
13
+ .toolbar {
14
+ display: flex;
15
+ align-items: center;
16
+ justify-content: space-between;
17
+ padding: var(--space-100) var(--space-100) var(--space-100) var(--space-300);
18
+ font-size: var(--font-size-small);
19
+ color: var(--zinc-400);
20
+ border-bottom: 1px solid var(--zinc-800);
21
+ }
22
+
23
+ button {
24
+ display: grid;
25
+ place-items: center;
26
+ width: 26px;
27
+ height: 26px;
28
+ color: inherit;
29
+ cursor: pointer;
30
+ background: none;
31
+ border: 0;
32
+ border-radius: var(--radius-small);
33
+
34
+ &:hover {
35
+ color: var(--zinc-100);
36
+ background: var(--zinc-800);
37
+ }
38
+ }
39
+
40
+ pre {
41
+ margin: 0;
42
+ padding: var(--space-300);
43
+ overflow: auto;
44
+ font-family: var(--font-mono);
45
+ font-size: var(--font-size-small);
46
+ line-height: 1.6;
47
+ counter-reset: line;
48
+ }
49
+
50
+ /* One block for each line: CSS counters number the lines, so a copy has no numbers */
51
+ .line {
52
+ display: block;
53
+ min-height: 1lh;
54
+
55
+ &::before {
56
+ display: inline-block;
57
+ width: 2.5em;
58
+ margin-right: var(--space-300);
59
+ color: var(--zinc-400);
60
+ text-align: right;
61
+ content: counter(line);
62
+ counter-increment: line;
63
+ user-select: none;
64
+ }
65
+ }
66
+
67
+ .token {
68
+ &.key,
69
+ &.attribute {
70
+ color: var(--blue-300);
71
+ }
72
+
73
+ &.string,
74
+ &.value {
75
+ color: var(--green-300);
76
+ }
77
+
78
+ &.number,
79
+ &.keyword {
80
+ color: var(--red-300);
81
+ }
82
+
83
+ &.tag {
84
+ color: var(--orange-300);
85
+ }
86
+
87
+ &.punctuation {
88
+ color: var(--zinc-400);
89
+ }
90
+ }
@@ -0,0 +1,68 @@
1
+ import { html, LitElement, unsafeCSS } from "lit"
2
+ import { unsafeHTML } from "lit/directives/unsafe-html.js"
3
+
4
+ import { define } from "../../define.ts"
5
+ import { highlight } from "../../highlight.ts"
6
+ import type { Language } from "../../highlight.ts"
7
+ import styles from "./code-block.css?inline"
8
+
9
+ import "../icon/icon.ts"
10
+
11
+ /**
12
+ * A code block with line numbers, highlighting and a copy button.
13
+ *
14
+ * @example <astro-metadata-code .code=${json} language="json" label="WebSite"></astro-metadata-code>
15
+ */
16
+ export class CodeBlock extends LitElement {
17
+ static properties = {
18
+ code: {},
19
+ language: {},
20
+ label: {},
21
+ copied: { state: true },
22
+ }
23
+
24
+ static styles = unsafeCSS(styles)
25
+
26
+ declare code: string
27
+ declare language: Language
28
+ declare label: string | undefined
29
+ declare copied: boolean
30
+
31
+ constructor() {
32
+ super()
33
+ this.code = ""
34
+ this.language = "html"
35
+ this.label = undefined
36
+ this.copied = false
37
+ }
38
+
39
+ async copy(): Promise<void> {
40
+ await navigator.clipboard.writeText(this.code)
41
+ this.copied = true
42
+ setTimeout(() => {
43
+ this.copied = false
44
+ }, 1500)
45
+ }
46
+
47
+ render() {
48
+ const lines = highlight(this.code, this.language).split("\n")
49
+ const code = lines.map((line) => {
50
+ return html`<span class="line">${unsafeHTML(line)}</span>`
51
+ })
52
+
53
+ // No spaces inside <pre>: the template text would show in the code
54
+ return html`
55
+ <div class="block">
56
+ <div class="toolbar">
57
+ <span>${this.label ?? this.language.toUpperCase()}</span>
58
+ <button @click=${this.copy} aria-label="Copy the code">
59
+ <astro-metadata-icon name=${this.copied ? "check" : "copy"}></astro-metadata-icon>
60
+ </button>
61
+ </div>
62
+ <pre><code>${code}</code></pre>
63
+ </div>
64
+ `
65
+ }
66
+ }
67
+
68
+ define("astro-metadata-code", CodeBlock)
@@ -0,0 +1,15 @@
1
+ :host {
2
+ display: inline-flex;
3
+ align-items: center;
4
+ gap: var(--space-100);
5
+ margin-right: var(--space-300);
6
+ font-family: var(--font-mono);
7
+ font-size: var(--font-size-small);
8
+ }
9
+
10
+ .color {
11
+ width: 12px;
12
+ height: 12px;
13
+ border: 1px solid var(--zinc-700);
14
+ border-radius: 3px;
15
+ }
@@ -0,0 +1,31 @@
1
+ import { html, LitElement, unsafeCSS } from "lit"
2
+ import { styleMap } from "lit/directives/style-map.js"
3
+
4
+ import { define } from "../../define.ts"
5
+ import styles from "./color-swatch.css?inline"
6
+
7
+ /**
8
+ * A small square of a color, and the color value.
9
+ *
10
+ * @example <astro-metadata-color-swatch color="#6366f1"></astro-metadata-color-swatch>
11
+ */
12
+ export class ColorSwatch extends LitElement {
13
+ static properties = { color: {} }
14
+ static styles = unsafeCSS(styles)
15
+
16
+ declare color: string
17
+
18
+ constructor() {
19
+ super()
20
+ this.color = ""
21
+ }
22
+
23
+ render() {
24
+ return html`
25
+ <span class="color" style=${styleMap({ background: this.color })}></span>
26
+ ${this.color}
27
+ `
28
+ }
29
+ }
30
+
31
+ define("astro-metadata-color-swatch", ColorSwatch)
@@ -0,0 +1,11 @@
1
+ :host {
2
+ display: inline-flex;
3
+ flex: none;
4
+ width: var(--icon-size, 16px);
5
+ height: var(--icon-size, 16px);
6
+ }
7
+
8
+ svg {
9
+ width: 100%;
10
+ height: 100%;
11
+ }
@@ -0,0 +1,46 @@
1
+ import { html, LitElement, nothing, unsafeCSS } from "lit"
2
+ import { unsafeSVG } from "lit/directives/unsafe-svg.js"
3
+
4
+ import caretRight from "../../../assets/icons/caret-right.svg?raw"
5
+ import check from "../../../assets/icons/check.svg?raw"
6
+ import copy from "../../../assets/icons/copy.svg?raw"
7
+ import sealCheck from "../../../assets/icons/seal-check-duotone.svg?raw"
8
+ import sealQuestion from "../../../assets/icons/seal-question-duotone.svg?raw"
9
+ import warningCircle from "../../../assets/icons/warning-circle-duotone.svg?raw"
10
+ import warning from "../../../assets/icons/warning-duotone.svg?raw"
11
+ import close from "../../../assets/icons/x.svg?raw"
12
+ import { define } from "../../define.ts"
13
+ import styles from "./icon.css?inline"
14
+
15
+ // All icons of the panel. Add a new icon to src/assets/icons/ and to this list.
16
+ const ICONS = {
17
+ "caret-right": caretRight,
18
+ check,
19
+ close,
20
+ copy,
21
+ "seal-check": sealCheck,
22
+ "seal-question": sealQuestion,
23
+ warning,
24
+ "warning-circle": warningCircle,
25
+ }
26
+
27
+ export type IconName = keyof typeof ICONS
28
+
29
+ /**
30
+ * An icon from `src/assets/icons/`. The color is `currentColor`, and `--icon-size` sets the size.
31
+ *
32
+ * @example <astro-metadata-icon name="close"></astro-metadata-icon>
33
+ */
34
+ export class Icon extends LitElement {
35
+ static properties = { name: {} }
36
+ static styles = unsafeCSS(styles)
37
+
38
+ declare name: IconName | undefined
39
+
40
+ render() {
41
+ const svg = this.name ? ICONS[this.name] : undefined
42
+ return svg ? html`${unsafeSVG(svg)}` : nothing
43
+ }
44
+ }
45
+
46
+ define("astro-metadata-icon", Icon)
@@ -0,0 +1,18 @@
1
+ /* The view sets the size, the radius and the colors of the image */
2
+ :host {
3
+ display: block;
4
+ overflow: hidden;
5
+ }
6
+
7
+ img {
8
+ display: block;
9
+ width: 100%;
10
+ height: 100%;
11
+ object-fit: cover;
12
+ }
13
+
14
+ .message {
15
+ display: grid;
16
+ place-items: center;
17
+ height: 100%;
18
+ }
@@ -0,0 +1,58 @@
1
+ import { html, LitElement, unsafeCSS } from "lit"
2
+
3
+ import { define } from "../../define.ts"
4
+ import styles from "./image.css?inline"
5
+
6
+ /**
7
+ * An image that shows a message when it does not load. The social images use the `site`
8
+ * of the Astro config, so in dev they often do not exist yet: then the image loads
9
+ * from the dev server. The `missing` attribute shows when the image does not load.
10
+ *
11
+ * @example <astro-metadata-image src=${url}></astro-metadata-image>
12
+ */
13
+ export class Image extends LitElement {
14
+ static properties = {
15
+ src: {},
16
+ missing: { type: Boolean, reflect: true },
17
+ current: { state: true },
18
+ }
19
+
20
+ static styles = unsafeCSS(styles)
21
+
22
+ declare src: string | undefined
23
+ declare missing: boolean
24
+ /** The URL that the image loads now: `src`, or the same path on the dev server. */
25
+ declare current: string | undefined
26
+
27
+ constructor() {
28
+ super()
29
+ this.src = undefined
30
+ this.missing = false
31
+ this.current = undefined
32
+ }
33
+
34
+ willUpdate(changed: Map<string, unknown>): void {
35
+ if (changed.has("src")) {
36
+ this.current = this.src
37
+ this.missing = !this.src
38
+ }
39
+ }
40
+
41
+ onError(): void {
42
+ const url = new URL(this.current ?? "", location.href)
43
+ if (url.origin !== location.origin) {
44
+ this.current = `${location.origin}${url.pathname}${url.search}`
45
+ return
46
+ }
47
+ this.missing = true
48
+ }
49
+
50
+ render() {
51
+ if (this.missing) {
52
+ return html`<span class="message">${this.src ? "The image does not load" : "No image"}</span>`
53
+ }
54
+ return html`<img src=${this.current ?? ""} alt="" @error=${this.onError} />`
55
+ }
56
+ }
57
+
58
+ define("astro-metadata-image", Image)
@@ -0,0 +1,16 @@
1
+ /*
2
+ * The look of backticks in Markdown: a small monospace label on a zinc background.
3
+ * A parent can change the colors with --code-color, --code-border and --code-background,
4
+ * e.g. the alert uses the colors of its level.
5
+ */
6
+ :host {
7
+ display: inline;
8
+ padding: 0 var(--space-100);
9
+ font-family: var(--font-mono);
10
+ font-size: 0.9em;
11
+ color: var(--code-color, var(--zinc-200));
12
+ white-space: nowrap;
13
+ border: 1px solid var(--code-border, var(--zinc-700));
14
+ border-radius: var(--radius-small);
15
+ background: var(--code-background, var(--zinc-800));
16
+ }
@@ -0,0 +1,41 @@
1
+ import { html, LitElement, unsafeCSS } from "lit"
2
+
3
+ import { define } from "../../define.ts"
4
+ import styles from "./inline-code.css?inline"
5
+
6
+ /**
7
+ * A code word in a text, e.g. a tag name or a value.
8
+ *
9
+ * @example <astro-metadata-inline-code>og:image</astro-metadata-inline-code>
10
+ */
11
+ export class InlineCode extends LitElement {
12
+ static styles = unsafeCSS(styles)
13
+
14
+ render() {
15
+ return html`<slot></slot>`
16
+ }
17
+ }
18
+
19
+ define("astro-metadata-inline-code", InlineCode)
20
+
21
+ /**
22
+ * Returns a text with each part between backticks as inline code.
23
+ *
24
+ * @example withCode("The `og:image` has no width.")
25
+ */
26
+ export function withCode(text: string) {
27
+ const parts = text.split(/`([^`]+)`/)
28
+ return html`${parts.map((part, index) => {
29
+ // The odd parts are the parts between the backticks
30
+ return index % 2 === 1
31
+ ? html`<astro-metadata-inline-code>${part}</astro-metadata-inline-code>`
32
+ : part
33
+ })}`
34
+ }
35
+
36
+ /**
37
+ * Returns a value as inline code, or undefined for an empty value.
38
+ */
39
+ export function code(value: string | undefined) {
40
+ return value ? html`<astro-metadata-inline-code>${value}</astro-metadata-inline-code>` : undefined
41
+ }