@tanstack/devtools 0.5.1 → 0.6.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.
Files changed (38) 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 -2
  14. package/dist/esm/styles/tokens.js.map +1 -1
  15. package/dist/esm/styles/use-styles.d.ts +19 -5
  16. package/dist/esm/styles/use-styles.js +142 -39
  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 +295 -0
  25. package/dist/esm/tabs/seo-tab.js.map +1 -0
  26. package/dist/esm/tabs/settings-tab.js +261 -222
  27. package/dist/esm/tabs/settings-tab.js.map +1 -1
  28. package/package.json +2 -2
  29. package/src/components/main-panel.tsx +5 -1
  30. package/src/components/tabs.tsx +9 -0
  31. package/src/context/draw-context.tsx +67 -0
  32. package/src/context/use-devtools-context.ts +12 -2
  33. package/src/hooks/use-head-changes.ts +110 -0
  34. package/src/styles/use-styles.ts +147 -39
  35. package/src/tabs/index.tsx +25 -0
  36. package/src/tabs/plugins-tab.tsx +51 -23
  37. package/src/tabs/seo-tab.tsx +245 -0
  38. package/src/tabs/settings-tab.tsx +109 -95
@@ -0,0 +1,245 @@
1
+ import { For, createSignal } from 'solid-js'
2
+ import {
3
+ MainPanel,
4
+ Section,
5
+ SectionDescription,
6
+ SectionIcon,
7
+ SectionTitle,
8
+ } from '@tanstack/devtools-ui'
9
+ import { useStyles } from '../styles/use-styles'
10
+ import { useHeadChanges } from '../hooks/use-head-changes'
11
+
12
+ type SocialMeta = {
13
+ title?: string
14
+ description?: string
15
+ image?: string
16
+ url?: string
17
+ }
18
+
19
+ type SocialReport = {
20
+ network: string
21
+ found: Partial<SocialMeta>
22
+ missing: Array<string>
23
+ }
24
+
25
+ const SOCIALS = [
26
+ {
27
+ network: 'Facebook',
28
+ tags: [
29
+ { key: 'og:title', prop: 'title' },
30
+ { key: 'og:description', prop: 'description' },
31
+ { key: 'og:image', prop: 'image' },
32
+ { key: 'og:url', prop: 'url' },
33
+ ],
34
+ color: '#4267B2',
35
+ },
36
+ {
37
+ network: 'X/Twitter',
38
+ tags: [
39
+ { key: 'twitter:title', prop: 'title' },
40
+ { key: 'twitter:description', prop: 'description' },
41
+ { key: 'twitter:image', prop: 'image' },
42
+ { key: 'twitter:url', prop: 'url' },
43
+ ],
44
+ color: '#1DA1F2',
45
+ },
46
+ {
47
+ network: 'LinkedIn',
48
+ tags: [
49
+ { key: 'og:title', prop: 'title' },
50
+ { key: 'og:description', prop: 'description' },
51
+ { key: 'og:image', prop: 'image' },
52
+ { key: 'og:url', prop: 'url' },
53
+ ],
54
+ color: '#0077B5',
55
+ },
56
+ {
57
+ network: 'Discord',
58
+ tags: [
59
+ { key: 'og:title', prop: 'title' },
60
+ { key: 'og:description', prop: 'description' },
61
+ { key: 'og:image', prop: 'image' },
62
+ { key: 'og:url', prop: 'url' },
63
+ ],
64
+ color: '#5865F2',
65
+ },
66
+ {
67
+ network: 'Slack',
68
+ tags: [
69
+ { key: 'og:title', prop: 'title' },
70
+ { key: 'og:description', prop: 'description' },
71
+ { key: 'og:image', prop: 'image' },
72
+ { key: 'og:url', prop: 'url' },
73
+ ],
74
+ color: '#4A154B',
75
+ },
76
+ {
77
+ network: 'Mastodon',
78
+ tags: [
79
+ { key: 'og:title', prop: 'title' },
80
+ { key: 'og:description', prop: 'description' },
81
+ { key: 'og:image', prop: 'image' },
82
+ { key: 'og:url', prop: 'url' },
83
+ ],
84
+ color: '#6364FF',
85
+ },
86
+ {
87
+ network: 'Bluesky',
88
+ tags: [
89
+ { key: 'og:title', prop: 'title' },
90
+ { key: 'og:description', prop: 'description' },
91
+ { key: 'og:image', prop: 'image' },
92
+ { key: 'og:url', prop: 'url' },
93
+ ],
94
+ color: '#1185FE',
95
+ },
96
+ // Add more networks as needed
97
+ ]
98
+ function SocialPreview(props: {
99
+ meta: SocialMeta
100
+ color: string
101
+ network: string
102
+ }) {
103
+ const styles = useStyles()
104
+
105
+ return (
106
+ <div
107
+ class={styles().seoPreviewCard}
108
+ style={{ 'border-color': props.color }}
109
+ >
110
+ <div class={styles().seoPreviewHeader} style={{ color: props.color }}>
111
+ {props.network} Preview
112
+ </div>
113
+ {props.meta.image ? (
114
+ <img
115
+ src={props.meta.image}
116
+ alt="Preview"
117
+ class={styles().seoPreviewImage}
118
+ />
119
+ ) : (
120
+ <div
121
+ class={styles().seoPreviewImage}
122
+ style={{
123
+ background: '#222',
124
+ color: '#888',
125
+ display: 'flex',
126
+ 'align-items': 'center',
127
+ 'justify-content': 'center',
128
+ 'min-height': '80px',
129
+ width: '100%',
130
+ }}
131
+ >
132
+ No Image
133
+ </div>
134
+ )}
135
+ <div class={styles().seoPreviewTitle}>
136
+ {props.meta.title || 'No Title'}
137
+ </div>
138
+ <div class={styles().seoPreviewDesc}>
139
+ {props.meta.description || 'No Description'}
140
+ </div>
141
+ <div class={styles().seoPreviewUrl}>
142
+ {props.meta.url || window.location.href}
143
+ </div>
144
+ </div>
145
+ )
146
+ }
147
+ export const SeoTab = () => {
148
+ const [reports, setReports] = createSignal<Array<SocialReport>>(analyzeHead())
149
+ const styles = useStyles()
150
+
151
+ function analyzeHead(): Array<SocialReport> {
152
+ const metaTags = Array.from(document.head.querySelectorAll('meta'))
153
+ const reports: Array<SocialReport> = []
154
+
155
+ for (const social of SOCIALS) {
156
+ const found: Partial<SocialMeta> = {}
157
+ const missing: Array<string> = []
158
+ for (const tag of social.tags) {
159
+ const meta = metaTags.find(
160
+ (m) =>
161
+ (tag.key.includes('twitter:')
162
+ ? false
163
+ : m.getAttribute('property') === tag.key) ||
164
+ m.getAttribute('name') === tag.key,
165
+ )
166
+
167
+ if (meta && meta.getAttribute('content')) {
168
+ found[tag.prop as keyof SocialMeta] =
169
+ meta.getAttribute('content') || undefined
170
+ } else {
171
+ missing.push(tag.key)
172
+ }
173
+ }
174
+ reports.push({ network: social.network, found, missing })
175
+ }
176
+ return reports
177
+ }
178
+
179
+ useHeadChanges(() => {
180
+ setReports(analyzeHead())
181
+ })
182
+
183
+ return (
184
+ <MainPanel withPadding>
185
+ <Section>
186
+ <SectionTitle>
187
+ <SectionIcon>
188
+ <svg
189
+ xmlns="http://www.w3.org/2000/svg"
190
+ width="20"
191
+ height="20"
192
+ viewBox="0 0 24 24"
193
+ fill="none"
194
+ stroke="currentColor"
195
+ stroke-width="2"
196
+ stroke-linecap="round"
197
+ stroke-linejoin="round"
198
+ >
199
+ <path d="m10 9-3 3 3 3" />
200
+ <path d="m14 15 3-3-3-3" />
201
+ <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" />
202
+ </svg>
203
+ </SectionIcon>
204
+ Social previews
205
+ </SectionTitle>
206
+ <SectionDescription>
207
+ See how your current page will look when shared on popular social
208
+ networks. The tool checks for essential meta tags and highlights any
209
+ that are missing.
210
+ </SectionDescription>
211
+ <div class={styles().seoPreviewSection}>
212
+ <For each={reports()}>
213
+ {(report, i) => {
214
+ const social = SOCIALS[i()]
215
+ return (
216
+ <div>
217
+ <SocialPreview
218
+ meta={report.found}
219
+ color={social!.color}
220
+ network={social!.network}
221
+ />
222
+ {report.missing.length > 0 ? (
223
+ <>
224
+ <div class={styles().seoMissingTagsSection}>
225
+ <strong>Missing tags for {social?.network}:</strong>
226
+
227
+ <ul class={styles().seoMissingTagsList}>
228
+ <For each={report.missing}>
229
+ {(tag) => (
230
+ <li class={styles().seoMissingTag}>{tag}</li>
231
+ )}
232
+ </For>
233
+ </ul>
234
+ </div>
235
+ </>
236
+ ) : null}
237
+ </div>
238
+ )
239
+ }}
240
+ </For>
241
+ </div>
242
+ </Section>
243
+ </MainPanel>
244
+ )
245
+ }
@@ -1,5 +1,15 @@
1
1
  import { Show, createMemo } from 'solid-js'
2
- import { Button, Checkbox, Input, Select } from '@tanstack/devtools-ui'
2
+ import {
3
+ Button,
4
+ Checkbox,
5
+ Input,
6
+ MainPanel,
7
+ Section,
8
+ SectionDescription,
9
+ SectionIcon,
10
+ SectionTitle,
11
+ Select,
12
+ } from '@tanstack/devtools-ui'
3
13
  import { useDevtoolsSettings } from '../context/use-devtools-context'
4
14
  import { uppercaseFirstLetter } from '../utils/sanitize'
5
15
  import { useStyles } from '../styles/use-styles'
@@ -27,30 +37,31 @@ export const SettingsTab = () => {
27
37
  })
28
38
  }
29
39
  return (
30
- <div class={styles().settingsContainer}>
40
+ <MainPanel withPadding>
31
41
  {/* General Settings */}
32
- <div class={styles().settingsSection}>
33
- <h3 class={styles().sectionTitle}>
34
- <svg
35
- xmlns="http://www.w3.org/2000/svg"
36
- width="20"
37
- height="20"
38
- viewBox="0 0 24 24"
39
- fill="none"
40
- stroke="currentColor"
41
- stroke-width="2"
42
- stroke-linecap="round"
43
- stroke-linejoin="round"
44
- class={styles().sectionIcon}
45
- >
46
- <path d="M9.671 4.136a2.34 2.34 0 0 1 4.659 0 2.34 2.34 0 0 0 3.319 1.915 2.34 2.34 0 0 1 2.33 4.033 2.34 2.34 0 0 0 0 3.831 2.34 2.34 0 0 1-2.33 4.033 2.34 2.34 0 0 0-3.319 1.915 2.34 2.34 0 0 1-4.659 0 2.34 2.34 0 0 0-3.32-1.915 2.34 2.34 0 0 1-2.33-4.033 2.34 2.34 0 0 0 0-3.831A2.34 2.34 0 0 1 6.35 6.051a2.34 2.34 0 0 0 3.319-1.915" />
47
- <circle cx="12" cy="12" r="3" />
48
- </svg>
42
+ <Section>
43
+ <SectionTitle>
44
+ <SectionIcon>
45
+ <svg
46
+ xmlns="http://www.w3.org/2000/svg"
47
+ width="20"
48
+ height="20"
49
+ viewBox="0 0 24 24"
50
+ fill="none"
51
+ stroke="currentColor"
52
+ stroke-width="2"
53
+ stroke-linecap="round"
54
+ stroke-linejoin="round"
55
+ >
56
+ <path d="M9.671 4.136a2.34 2.34 0 0 1 4.659 0 2.34 2.34 0 0 0 3.319 1.915 2.34 2.34 0 0 1 2.33 4.033 2.34 2.34 0 0 0 0 3.831 2.34 2.34 0 0 1-2.33 4.033 2.34 2.34 0 0 0-3.319 1.915 2.34 2.34 0 0 1-4.659 0 2.34 2.34 0 0 0-3.32-1.915 2.34 2.34 0 0 1-2.33-4.033 2.34 2.34 0 0 0 0-3.831A2.34 2.34 0 0 1 6.35 6.051a2.34 2.34 0 0 0 3.319-1.915" />
57
+ <circle cx="12" cy="12" r="3" />
58
+ </svg>
59
+ </SectionIcon>
49
60
  General
50
- </h3>
51
- <p class={styles().sectionDescription}>
61
+ </SectionTitle>
62
+ <SectionDescription>
52
63
  Configure general behavior of the devtools panel.
53
- </p>
64
+ </SectionDescription>
54
65
  <div class={styles().settingsGroup}>
55
66
  <Checkbox
56
67
  label="Default open"
@@ -69,32 +80,33 @@ export const SettingsTab = () => {
69
80
  checked={settings().hideUntilHover}
70
81
  />
71
82
  </div>
72
- </div>
83
+ </Section>
73
84
 
74
85
  {/* URL Flag Settings */}
75
- <div class={styles().settingsSection}>
76
- <h3 class={styles().sectionTitle}>
77
- <svg
78
- class={styles().sectionIcon}
79
- xmlns="http://www.w3.org/2000/svg"
80
- width="20"
81
- height="20"
82
- viewBox="0 0 24 24"
83
- fill="none"
84
- stroke="currentColor"
85
- stroke-width="2"
86
- stroke-linecap="round"
87
- stroke-linejoin="round"
88
- >
89
- <path d="M9 17H7A5 5 0 0 1 7 7h2" />
90
- <path d="M15 7h2a5 5 0 1 1 0 10h-2" />
91
- <line x1="8" x2="16" y1="12" y2="12" />
92
- </svg>
86
+ <Section>
87
+ <SectionTitle>
88
+ <SectionIcon>
89
+ <svg
90
+ xmlns="http://www.w3.org/2000/svg"
91
+ width="20"
92
+ height="20"
93
+ viewBox="0 0 24 24"
94
+ fill="none"
95
+ stroke="currentColor"
96
+ stroke-width="2"
97
+ stroke-linecap="round"
98
+ stroke-linejoin="round"
99
+ >
100
+ <path d="M9 17H7A5 5 0 0 1 7 7h2" />
101
+ <path d="M15 7h2a5 5 0 1 1 0 10h-2" />
102
+ <line x1="8" x2="16" y1="12" y2="12" />
103
+ </svg>
104
+ </SectionIcon>
93
105
  URL Configuration
94
- </h3>
95
- <p class={styles().sectionDescription}>
106
+ </SectionTitle>
107
+ <SectionDescription>
96
108
  Control when devtools are available based on URL parameters.
97
- </p>
109
+ </SectionDescription>
98
110
  <div class={styles().settingsGroup}>
99
111
  <Checkbox
100
112
  label="Require URL Flag"
@@ -122,38 +134,39 @@ export const SettingsTab = () => {
122
134
  </div>
123
135
  </Show>
124
136
  </div>
125
- </div>
137
+ </Section>
126
138
 
127
139
  {/* Keyboard Settings */}
128
- <div class={styles().settingsSection}>
129
- <h3 class={styles().sectionTitle}>
130
- <svg
131
- class={styles().sectionIcon}
132
- xmlns="http://www.w3.org/2000/svg"
133
- width="20"
134
- height="20"
135
- viewBox="0 0 24 24"
136
- fill="none"
137
- stroke="currentColor"
138
- stroke-width="2"
139
- stroke-linecap="round"
140
- stroke-linejoin="round"
141
- >
142
- <path d="M10 8h.01" />
143
- <path d="M12 12h.01" />
144
- <path d="M14 8h.01" />
145
- <path d="M16 12h.01" />
146
- <path d="M18 8h.01" />
147
- <path d="M6 8h.01" />
148
- <path d="M7 16h10" />
149
- <path d="M8 12h.01" />
150
- <rect width="20" height="16" x="2" y="4" rx="2" />
151
- </svg>
140
+ <Section>
141
+ <SectionTitle>
142
+ <SectionIcon>
143
+ <svg
144
+ xmlns="http://www.w3.org/2000/svg"
145
+ width="20"
146
+ height="20"
147
+ viewBox="0 0 24 24"
148
+ fill="none"
149
+ stroke="currentColor"
150
+ stroke-width="2"
151
+ stroke-linecap="round"
152
+ stroke-linejoin="round"
153
+ >
154
+ <path d="M10 8h.01" />
155
+ <path d="M12 12h.01" />
156
+ <path d="M14 8h.01" />
157
+ <path d="M16 12h.01" />
158
+ <path d="M18 8h.01" />
159
+ <path d="M6 8h.01" />
160
+ <path d="M7 16h10" />
161
+ <path d="M8 12h.01" />
162
+ <rect width="20" height="16" x="2" y="4" rx="2" />
163
+ </svg>
164
+ </SectionIcon>
152
165
  Keyboard
153
- </h3>
154
- <p class={styles().sectionDescription}>
166
+ </SectionTitle>
167
+ <SectionDescription>
155
168
  Customize keyboard shortcuts for quick access.
156
- </p>
169
+ </SectionDescription>
157
170
  <div class={styles().settingsGroup}>
158
171
  <div class={styles().settingsModifiers}>
159
172
  <Show keyed when={hotkey()}>
@@ -220,31 +233,32 @@ export const SettingsTab = () => {
220
233
  />
221
234
  Final shortcut is: {hotkey().join(' + ')}
222
235
  </div>
223
- </div>
236
+ </Section>
224
237
 
225
238
  {/* Position Settings */}
226
- <div class={styles().settingsSection}>
227
- <h3 class={styles().sectionTitle}>
228
- <svg
229
- class={styles().sectionIcon}
230
- xmlns="http://www.w3.org/2000/svg"
231
- width="20"
232
- height="20"
233
- viewBox="0 0 24 24"
234
- fill="none"
235
- stroke="currentColor"
236
- stroke-width="2"
237
- stroke-linecap="round"
238
- stroke-linejoin="round"
239
- >
240
- <path d="M20 10c0 4.993-5.539 10.193-7.399 11.799a1 1 0 0 1-1.202 0C9.539 20.193 4 14.993 4 10a8 8 0 0 1 16 0" />
241
- <circle cx="12" cy="10" r="3" />
242
- </svg>
239
+ <Section>
240
+ <SectionTitle>
241
+ <SectionIcon>
242
+ <svg
243
+ xmlns="http://www.w3.org/2000/svg"
244
+ width="20"
245
+ height="20"
246
+ viewBox="0 0 24 24"
247
+ fill="none"
248
+ stroke="currentColor"
249
+ stroke-width="2"
250
+ stroke-linecap="round"
251
+ stroke-linejoin="round"
252
+ >
253
+ <path d="M20 10c0 4.993-5.539 10.193-7.399 11.799a1 1 0 0 1-1.202 0C9.539 20.193 4 14.993 4 10a8 8 0 0 1 16 0" />
254
+ <circle cx="12" cy="10" r="3" />
255
+ </svg>
256
+ </SectionIcon>
243
257
  Position
244
- </h3>
245
- <p class={styles().sectionDescription}>
258
+ </SectionTitle>
259
+ <SectionDescription>
246
260
  Adjust the position of the trigger button and devtools panel.
247
- </p>
261
+ </SectionDescription>
248
262
  <div class={styles().settingsGroup}>
249
263
  <div class={styles().settingRow}>
250
264
  <Select
@@ -279,7 +293,7 @@ export const SettingsTab = () => {
279
293
  />
280
294
  </div>
281
295
  </div>
282
- </div>
283
- </div>
296
+ </Section>
297
+ </MainPanel>
284
298
  )
285
299
  }