@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.
Files changed (35) hide show
  1. package/dist/esm/components/main-panel.js +8 -2
  2. package/dist/esm/components/main-panel.js.map +1 -1
  3. package/dist/esm/components/tabs.js +10 -0
  4. package/dist/esm/components/tabs.js.map +1 -1
  5. package/dist/esm/context/draw-context.d.ts +13 -0
  6. package/dist/esm/context/draw-context.js +55 -0
  7. package/dist/esm/context/draw-context.js.map +1 -0
  8. package/dist/esm/context/use-devtools-context.js +10 -1
  9. package/dist/esm/context/use-devtools-context.js.map +1 -1
  10. package/dist/esm/hooks/use-head-changes.d.ts +39 -0
  11. package/dist/esm/hooks/use-head-changes.js +65 -0
  12. package/dist/esm/hooks/use-head-changes.js.map +1 -0
  13. package/dist/esm/styles/tokens.js +4 -1
  14. package/dist/esm/styles/tokens.js.map +1 -1
  15. package/dist/esm/styles/use-styles.d.ts +19 -0
  16. package/dist/esm/styles/use-styles.js +143 -3
  17. package/dist/esm/styles/use-styles.js.map +1 -1
  18. package/dist/esm/tabs/index.d.ts +5 -0
  19. package/dist/esm/tabs/index.js +8 -2
  20. package/dist/esm/tabs/index.js.map +1 -1
  21. package/dist/esm/tabs/plugins-tab.js +31 -13
  22. package/dist/esm/tabs/plugins-tab.js.map +1 -1
  23. package/dist/esm/tabs/seo-tab.d.ts +1 -0
  24. package/dist/esm/tabs/seo-tab.js +291 -0
  25. package/dist/esm/tabs/seo-tab.js.map +1 -0
  26. package/package.json +1 -1
  27. package/src/components/main-panel.tsx +5 -1
  28. package/src/components/tabs.tsx +9 -0
  29. package/src/context/draw-context.tsx +67 -0
  30. package/src/context/use-devtools-context.ts +12 -2
  31. package/src/hooks/use-head-changes.ts +110 -0
  32. package/src/styles/use-styles.ts +148 -3
  33. package/src/tabs/index.tsx +25 -0
  34. package/src/tabs/plugins-tab.tsx +51 -23
  35. 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
+ }