@tanstack/devtools 0.5.1 → 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/dist/esm/components/main-panel.js +8 -2
- package/dist/esm/components/main-panel.js.map +1 -1
- package/dist/esm/components/tabs.js +10 -0
- package/dist/esm/components/tabs.js.map +1 -1
- package/dist/esm/context/draw-context.d.ts +13 -0
- package/dist/esm/context/draw-context.js +55 -0
- package/dist/esm/context/draw-context.js.map +1 -0
- package/dist/esm/context/use-devtools-context.js +10 -1
- package/dist/esm/context/use-devtools-context.js.map +1 -1
- package/dist/esm/hooks/use-head-changes.d.ts +39 -0
- package/dist/esm/hooks/use-head-changes.js +65 -0
- package/dist/esm/hooks/use-head-changes.js.map +1 -0
- package/dist/esm/styles/tokens.js +4 -1
- package/dist/esm/styles/tokens.js.map +1 -1
- package/dist/esm/styles/use-styles.d.ts +19 -0
- package/dist/esm/styles/use-styles.js +143 -3
- package/dist/esm/styles/use-styles.js.map +1 -1
- package/dist/esm/tabs/index.d.ts +5 -0
- package/dist/esm/tabs/index.js +8 -2
- package/dist/esm/tabs/index.js.map +1 -1
- package/dist/esm/tabs/plugins-tab.js +31 -13
- package/dist/esm/tabs/plugins-tab.js.map +1 -1
- package/dist/esm/tabs/seo-tab.d.ts +1 -0
- package/dist/esm/tabs/seo-tab.js +291 -0
- package/dist/esm/tabs/seo-tab.js.map +1 -0
- package/package.json +1 -1
- package/src/components/main-panel.tsx +5 -1
- package/src/components/tabs.tsx +9 -0
- package/src/context/draw-context.tsx +67 -0
- package/src/context/use-devtools-context.ts +12 -2
- package/src/hooks/use-head-changes.ts +110 -0
- package/src/styles/use-styles.ts +148 -3
- package/src/tabs/index.tsx +25 -0
- package/src/tabs/plugins-tab.tsx +51 -23
- package/src/tabs/seo-tab.tsx +238 -0
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
import { For, createSignal } from 'solid-js'
|
|
2
|
+
import { useStyles } from '../styles/use-styles'
|
|
3
|
+
import { useHeadChanges } from '../hooks/use-head-changes'
|
|
4
|
+
|
|
5
|
+
type SocialMeta = {
|
|
6
|
+
title?: string
|
|
7
|
+
description?: string
|
|
8
|
+
image?: string
|
|
9
|
+
url?: string
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
type SocialReport = {
|
|
13
|
+
network: string
|
|
14
|
+
found: Partial<SocialMeta>
|
|
15
|
+
missing: Array<string>
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const SOCIALS = [
|
|
19
|
+
{
|
|
20
|
+
network: 'Facebook',
|
|
21
|
+
tags: [
|
|
22
|
+
{ key: 'og:title', prop: 'title' },
|
|
23
|
+
{ key: 'og:description', prop: 'description' },
|
|
24
|
+
{ key: 'og:image', prop: 'image' },
|
|
25
|
+
{ key: 'og:url', prop: 'url' },
|
|
26
|
+
],
|
|
27
|
+
color: '#4267B2',
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
network: 'X/Twitter',
|
|
31
|
+
tags: [
|
|
32
|
+
{ key: 'twitter:title', prop: 'title' },
|
|
33
|
+
{ key: 'twitter:description', prop: 'description' },
|
|
34
|
+
{ key: 'twitter:image', prop: 'image' },
|
|
35
|
+
{ key: 'twitter:url', prop: 'url' },
|
|
36
|
+
],
|
|
37
|
+
color: '#1DA1F2',
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
network: 'LinkedIn',
|
|
41
|
+
tags: [
|
|
42
|
+
{ key: 'og:title', prop: 'title' },
|
|
43
|
+
{ key: 'og:description', prop: 'description' },
|
|
44
|
+
{ key: 'og:image', prop: 'image' },
|
|
45
|
+
{ key: 'og:url', prop: 'url' },
|
|
46
|
+
],
|
|
47
|
+
color: '#0077B5',
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
network: 'Discord',
|
|
51
|
+
tags: [
|
|
52
|
+
{ key: 'og:title', prop: 'title' },
|
|
53
|
+
{ key: 'og:description', prop: 'description' },
|
|
54
|
+
{ key: 'og:image', prop: 'image' },
|
|
55
|
+
{ key: 'og:url', prop: 'url' },
|
|
56
|
+
],
|
|
57
|
+
color: '#5865F2',
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
network: 'Slack',
|
|
61
|
+
tags: [
|
|
62
|
+
{ key: 'og:title', prop: 'title' },
|
|
63
|
+
{ key: 'og:description', prop: 'description' },
|
|
64
|
+
{ key: 'og:image', prop: 'image' },
|
|
65
|
+
{ key: 'og:url', prop: 'url' },
|
|
66
|
+
],
|
|
67
|
+
color: '#4A154B',
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
network: 'Mastodon',
|
|
71
|
+
tags: [
|
|
72
|
+
{ key: 'og:title', prop: 'title' },
|
|
73
|
+
{ key: 'og:description', prop: 'description' },
|
|
74
|
+
{ key: 'og:image', prop: 'image' },
|
|
75
|
+
{ key: 'og:url', prop: 'url' },
|
|
76
|
+
],
|
|
77
|
+
color: '#6364FF',
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
network: 'Bluesky',
|
|
81
|
+
tags: [
|
|
82
|
+
{ key: 'og:title', prop: 'title' },
|
|
83
|
+
{ key: 'og:description', prop: 'description' },
|
|
84
|
+
{ key: 'og:image', prop: 'image' },
|
|
85
|
+
{ key: 'og:url', prop: 'url' },
|
|
86
|
+
],
|
|
87
|
+
color: '#1185FE',
|
|
88
|
+
},
|
|
89
|
+
// Add more networks as needed
|
|
90
|
+
]
|
|
91
|
+
function SocialPreview(props: {
|
|
92
|
+
meta: SocialMeta
|
|
93
|
+
color: string
|
|
94
|
+
network: string
|
|
95
|
+
}) {
|
|
96
|
+
const styles = useStyles()
|
|
97
|
+
|
|
98
|
+
return (
|
|
99
|
+
<div
|
|
100
|
+
class={styles().seoPreviewCard}
|
|
101
|
+
style={{ 'border-color': props.color }}
|
|
102
|
+
>
|
|
103
|
+
<div class={styles().seoPreviewHeader} style={{ color: props.color }}>
|
|
104
|
+
{props.network} Preview
|
|
105
|
+
</div>
|
|
106
|
+
{props.meta.image ? (
|
|
107
|
+
<img
|
|
108
|
+
src={props.meta.image}
|
|
109
|
+
alt="Preview"
|
|
110
|
+
class={styles().seoPreviewImage}
|
|
111
|
+
/>
|
|
112
|
+
) : (
|
|
113
|
+
<div
|
|
114
|
+
class={styles().seoPreviewImage}
|
|
115
|
+
style={{
|
|
116
|
+
background: '#222',
|
|
117
|
+
color: '#888',
|
|
118
|
+
display: 'flex',
|
|
119
|
+
'align-items': 'center',
|
|
120
|
+
'justify-content': 'center',
|
|
121
|
+
'min-height': '80px',
|
|
122
|
+
width: '100%',
|
|
123
|
+
}}
|
|
124
|
+
>
|
|
125
|
+
No Image
|
|
126
|
+
</div>
|
|
127
|
+
)}
|
|
128
|
+
<div class={styles().seoPreviewTitle}>
|
|
129
|
+
{props.meta.title || 'No Title'}
|
|
130
|
+
</div>
|
|
131
|
+
<div class={styles().seoPreviewDesc}>
|
|
132
|
+
{props.meta.description || 'No Description'}
|
|
133
|
+
</div>
|
|
134
|
+
<div class={styles().seoPreviewUrl}>
|
|
135
|
+
{props.meta.url || window.location.href}
|
|
136
|
+
</div>
|
|
137
|
+
</div>
|
|
138
|
+
)
|
|
139
|
+
}
|
|
140
|
+
export const SeoTab = () => {
|
|
141
|
+
const [reports, setReports] = createSignal<Array<SocialReport>>(analyzeHead())
|
|
142
|
+
const styles = useStyles()
|
|
143
|
+
|
|
144
|
+
function analyzeHead(): Array<SocialReport> {
|
|
145
|
+
const metaTags = Array.from(document.head.querySelectorAll('meta'))
|
|
146
|
+
const reports: Array<SocialReport> = []
|
|
147
|
+
|
|
148
|
+
for (const social of SOCIALS) {
|
|
149
|
+
const found: Partial<SocialMeta> = {}
|
|
150
|
+
const missing: Array<string> = []
|
|
151
|
+
for (const tag of social.tags) {
|
|
152
|
+
const meta = metaTags.find(
|
|
153
|
+
(m) =>
|
|
154
|
+
(tag.key.includes('twitter:')
|
|
155
|
+
? false
|
|
156
|
+
: m.getAttribute('property') === tag.key) ||
|
|
157
|
+
m.getAttribute('name') === tag.key,
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
if (meta && meta.getAttribute('content')) {
|
|
161
|
+
found[tag.prop as keyof SocialMeta] =
|
|
162
|
+
meta.getAttribute('content') || undefined
|
|
163
|
+
} else {
|
|
164
|
+
missing.push(tag.key)
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
reports.push({ network: social.network, found, missing })
|
|
168
|
+
}
|
|
169
|
+
return reports
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
useHeadChanges(() => {
|
|
173
|
+
setReports(analyzeHead())
|
|
174
|
+
})
|
|
175
|
+
|
|
176
|
+
return (
|
|
177
|
+
<div class={styles().seoTabContainer}>
|
|
178
|
+
<section class={styles().seoTabSection}>
|
|
179
|
+
<h3 class={styles().sectionTitle}>
|
|
180
|
+
<svg
|
|
181
|
+
class={styles().sectionIcon}
|
|
182
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
183
|
+
width="24"
|
|
184
|
+
height="24"
|
|
185
|
+
viewBox="0 0 24 24"
|
|
186
|
+
fill="none"
|
|
187
|
+
stroke="currentColor"
|
|
188
|
+
stroke-width="2"
|
|
189
|
+
stroke-linecap="round"
|
|
190
|
+
stroke-linejoin="round"
|
|
191
|
+
>
|
|
192
|
+
<path d="m10 9-3 3 3 3" />
|
|
193
|
+
<path d="m14 15 3-3-3-3" />
|
|
194
|
+
<path d="M2.992 16.342a2 2 0 0 1 .094 1.167l-1.065 3.29a1 1 0 0 0 1.236 1.168l3.413-.998a2 2 0 0 1 1.099.092 10 10 0 1 0-4.777-4.719" />
|
|
195
|
+
</svg>
|
|
196
|
+
Social previews
|
|
197
|
+
</h3>
|
|
198
|
+
<p class={styles().sectionDescription}>
|
|
199
|
+
See how your current page will look when shared on popular social
|
|
200
|
+
networks. The tool checks for essential meta tags and highlights any
|
|
201
|
+
that are missing.
|
|
202
|
+
</p>
|
|
203
|
+
<div class={styles().seoPreviewSection}>
|
|
204
|
+
<For each={reports()}>
|
|
205
|
+
{(report, i) => {
|
|
206
|
+
const social = SOCIALS[i()]
|
|
207
|
+
return (
|
|
208
|
+
<div>
|
|
209
|
+
<SocialPreview
|
|
210
|
+
meta={report.found}
|
|
211
|
+
color={social!.color}
|
|
212
|
+
network={social!.network}
|
|
213
|
+
/>
|
|
214
|
+
{report.missing.length > 0 ? (
|
|
215
|
+
<>
|
|
216
|
+
<div class={styles().seoMissingTagsSection}>
|
|
217
|
+
<strong>Missing tags for {social?.network}:</strong>
|
|
218
|
+
|
|
219
|
+
<ul class={styles().seoMissingTagsList}>
|
|
220
|
+
<For each={report.missing}>
|
|
221
|
+
{(tag) => (
|
|
222
|
+
<li class={styles().seoMissingTag}>{tag}</li>
|
|
223
|
+
)}
|
|
224
|
+
</For>
|
|
225
|
+
</ul>
|
|
226
|
+
</div>
|
|
227
|
+
</>
|
|
228
|
+
) : null}
|
|
229
|
+
</div>
|
|
230
|
+
)
|
|
231
|
+
}}
|
|
232
|
+
</For>
|
|
233
|
+
</div>
|
|
234
|
+
</section>
|
|
235
|
+
{/* Future sections can be added here as <section class={styles().seoTabSection}>...</section> */}
|
|
236
|
+
</div>
|
|
237
|
+
)
|
|
238
|
+
}
|