@ytspar/devbar 0.0.1 → 1.0.0-canary.2b99e1e

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.
@@ -0,0 +1,248 @@
1
+ /**
2
+ * Document Outline Extraction
3
+ *
4
+ * Functions for extracting and formatting the semantic document outline.
5
+ */
6
+ // ============================================================================
7
+ // Semantic Element Sets
8
+ // ============================================================================
9
+ const semanticElements = new Set([
10
+ 'article',
11
+ 'aside',
12
+ 'nav',
13
+ 'section',
14
+ 'main',
15
+ 'body',
16
+ 'header',
17
+ 'footer',
18
+ 'figure',
19
+ 'figcaption',
20
+ 'details',
21
+ 'summary',
22
+ 'dialog',
23
+ 'address',
24
+ 'hgroup',
25
+ 'form',
26
+ 'fieldset',
27
+ 'legend',
28
+ 'ul',
29
+ 'ol',
30
+ 'dl',
31
+ 'menu',
32
+ 'table',
33
+ 'thead',
34
+ 'tbody',
35
+ 'tfoot',
36
+ 'caption',
37
+ 'h1',
38
+ 'h2',
39
+ 'h3',
40
+ 'h4',
41
+ 'h5',
42
+ 'h6',
43
+ ]);
44
+ const headingElements = new Set(['h1', 'h2', 'h3', 'h4', 'h5', 'h6']);
45
+ // ============================================================================
46
+ // Helper Functions
47
+ // ============================================================================
48
+ /**
49
+ * Get the semantic category for an element tag
50
+ */
51
+ function getSemanticCategory(tag) {
52
+ if (headingElements.has(tag))
53
+ return 'heading';
54
+ if (['article', 'section', 'aside', 'nav'].includes(tag))
55
+ return 'sectioning';
56
+ if (['main', 'header', 'footer'].includes(tag))
57
+ return 'landmark';
58
+ if (['figure', 'figcaption', 'details', 'summary'].includes(tag))
59
+ return 'grouping';
60
+ if (['form', 'fieldset', 'legend'].includes(tag))
61
+ return 'form';
62
+ if (['table', 'thead', 'tbody', 'tfoot', 'caption'].includes(tag))
63
+ return 'table';
64
+ if (['ul', 'ol', 'dl', 'menu'].includes(tag))
65
+ return 'list';
66
+ return 'other';
67
+ }
68
+ /**
69
+ * Get trimmed text content from an element with optional max length
70
+ */
71
+ function getTextContent(el, maxLen) {
72
+ return el?.textContent?.trim().slice(0, maxLen) || '';
73
+ }
74
+ /**
75
+ * Mapping of tag names to their child element selector for text extraction
76
+ */
77
+ const childTextSelectors = {
78
+ figure: 'figcaption',
79
+ details: 'summary',
80
+ fieldset: 'legend',
81
+ table: 'caption',
82
+ };
83
+ /**
84
+ * Get descriptive text for an element
85
+ */
86
+ function getElementText(el, tagName) {
87
+ // Check ARIA attributes first
88
+ const ariaLabel = el.getAttribute('aria-label');
89
+ if (ariaLabel)
90
+ return ariaLabel;
91
+ const labelledBy = el.getAttribute('aria-labelledby');
92
+ if (labelledBy) {
93
+ const labelEl = document.getElementById(labelledBy);
94
+ if (labelEl)
95
+ return getTextContent(labelEl, 80);
96
+ }
97
+ // Headings: use direct text content
98
+ if (headingElements.has(tagName)) {
99
+ return getTextContent(el, 100);
100
+ }
101
+ // Elements with child selectors (figure, details, fieldset, table)
102
+ const childSelector = childTextSelectors[tagName];
103
+ if (childSelector) {
104
+ const childEl = el.querySelector(childSelector);
105
+ if (childEl)
106
+ return getTextContent(childEl, 80);
107
+ }
108
+ // Form: use name or id attribute
109
+ if (tagName === 'form') {
110
+ const name = el.getAttribute('name') || el.getAttribute('id');
111
+ if (name)
112
+ return name;
113
+ }
114
+ // Nav: try heading first, then first link
115
+ if (tagName === 'nav') {
116
+ const heading = el.querySelector('h1, h2, h3, h4, h5, h6');
117
+ if (heading)
118
+ return getTextContent(heading, 50);
119
+ const firstLink = el.querySelector('a');
120
+ if (firstLink)
121
+ return `Navigation (${getTextContent(firstLink, 30)}...)`;
122
+ }
123
+ // Sectioning elements: try direct child heading, then class name
124
+ if (['section', 'article', 'aside'].includes(tagName)) {
125
+ const heading = el.querySelector(':scope > h1, :scope > h2, :scope > h3, :scope > h4, :scope > h5, :scope > h6');
126
+ if (heading)
127
+ return getTextContent(heading, 80);
128
+ const className = el.className?.toString().split(' ')[0];
129
+ if (className && className.length < 30)
130
+ return className;
131
+ }
132
+ // Lists: count items
133
+ if (['ul', 'ol'].includes(tagName)) {
134
+ return `${el.querySelectorAll(':scope > li').length} items`;
135
+ }
136
+ if (tagName === 'dl') {
137
+ return `${el.querySelectorAll(':scope > dt').length} terms`;
138
+ }
139
+ // Fallback to role attribute
140
+ const role = el.getAttribute('role');
141
+ if (role)
142
+ return `[role="${role}"]`;
143
+ return '';
144
+ }
145
+ /**
146
+ * Check if an element is visible
147
+ */
148
+ function isVisible(el) {
149
+ const style = window.getComputedStyle(el);
150
+ return style.display !== 'none' && style.visibility !== 'hidden';
151
+ }
152
+ /**
153
+ * Recursively extract outline nodes from an element
154
+ */
155
+ function extractFromElement(root) {
156
+ const nodes = [];
157
+ for (const child of Array.from(root.children)) {
158
+ const tagName = child.tagName.toLowerCase();
159
+ if (!isVisible(child))
160
+ continue;
161
+ if (child.getAttribute('data-devbar'))
162
+ continue;
163
+ if (semanticElements.has(tagName)) {
164
+ const text = getElementText(child, tagName);
165
+ const isHeading = headingElements.has(tagName);
166
+ const isLandmark = [
167
+ 'main',
168
+ 'nav',
169
+ 'header',
170
+ 'footer',
171
+ 'article',
172
+ 'section',
173
+ 'aside',
174
+ ].includes(tagName);
175
+ const hasText = text.length > 0;
176
+ if (isHeading || isLandmark || hasText) {
177
+ const level = isHeading ? parseInt(tagName[1], 10) : 0;
178
+ const node = {
179
+ tagName,
180
+ level,
181
+ text: text || `<${tagName}>`,
182
+ id: child.id || undefined,
183
+ children: [],
184
+ category: getSemanticCategory(tagName),
185
+ };
186
+ if (!isHeading) {
187
+ node.children = extractFromElement(child);
188
+ }
189
+ nodes.push(node);
190
+ }
191
+ else {
192
+ const childNodes = extractFromElement(child);
193
+ nodes.push(...childNodes);
194
+ }
195
+ }
196
+ else {
197
+ const childNodes = extractFromElement(child);
198
+ nodes.push(...childNodes);
199
+ }
200
+ }
201
+ return nodes;
202
+ }
203
+ // ============================================================================
204
+ // Public API
205
+ // ============================================================================
206
+ /**
207
+ * Extract the document outline from the page
208
+ */
209
+ export function extractDocumentOutline() {
210
+ const body = document.body;
211
+ if (!body)
212
+ return [];
213
+ return extractFromElement(body);
214
+ }
215
+ /**
216
+ * Convert an outline to markdown format
217
+ */
218
+ export function outlineToMarkdown(outline, indent = 0) {
219
+ let md = '';
220
+ if (indent === 0) {
221
+ md += '# Document Outline\n\n';
222
+ md += '**Semantic Categories:**\n';
223
+ md += '- `heading` - h1-h6 elements\n';
224
+ md += '- `sectioning` - article, section, aside, nav\n';
225
+ md += '- `landmark` - main, header, footer\n';
226
+ md += '- `grouping` - figure, details, summary\n';
227
+ md += '- `form` - form, fieldset\n';
228
+ md += '- `table` - table elements\n';
229
+ md += '- `list` - ul, ol, dl\n\n';
230
+ md += '---\n\n';
231
+ }
232
+ for (const node of outline) {
233
+ const prefix = ' '.repeat(indent);
234
+ const tagLabel = `\`<${node.tagName}>\``;
235
+ const anchor = node.id ? ` \`#${node.id}\`` : '';
236
+ const category = node.category ? ` [${node.category}]` : '';
237
+ if (node.category === 'heading' && indent === 0) {
238
+ md += `${'#'.repeat(node.level)} ${tagLabel} ${node.text}${anchor}\n\n`;
239
+ }
240
+ else {
241
+ md += `${prefix}- ${tagLabel}${category} ${node.text}${anchor}\n`;
242
+ }
243
+ if (node.children.length > 0) {
244
+ md += outlineToMarkdown(node.children, indent + 1);
245
+ }
246
+ }
247
+ return md;
248
+ }
@@ -0,0 +1,57 @@
1
+ /**
2
+ * DevBar Configuration Presets
3
+ *
4
+ * Pre-configured options for common DevBar use cases.
5
+ */
6
+ import { type GlobalDevBar } from './GlobalDevBar.js';
7
+ import type { GlobalDevBarOptions } from './types.js';
8
+ /**
9
+ * Minimal preset - shows only essential features
10
+ * Good for production debugging with minimal visual footprint
11
+ */
12
+ export declare const PRESET_MINIMAL: GlobalDevBarOptions;
13
+ /**
14
+ * Full preset - shows all features
15
+ * Good for comprehensive development monitoring
16
+ */
17
+ export declare const PRESET_FULL: GlobalDevBarOptions;
18
+ /**
19
+ * Performance preset - focuses on Core Web Vitals
20
+ * Good for performance optimization work
21
+ */
22
+ export declare const PRESET_PERFORMANCE: GlobalDevBarOptions;
23
+ /**
24
+ * Responsive preset - focuses on responsive design
25
+ * Good for layout and breakpoint work
26
+ */
27
+ export declare const PRESET_RESPONSIVE: GlobalDevBarOptions;
28
+ /**
29
+ * Debug preset - full features with debug logging
30
+ * Good for troubleshooting DevBar itself
31
+ */
32
+ export declare const PRESET_DEBUG: GlobalDevBarOptions;
33
+ /**
34
+ * Initialize DevBar with minimal preset
35
+ * @param options Additional options to merge with preset
36
+ */
37
+ export declare function initMinimal(options?: Partial<GlobalDevBarOptions>): GlobalDevBar;
38
+ /**
39
+ * Initialize DevBar with full preset
40
+ * @param options Additional options to merge with preset
41
+ */
42
+ export declare function initFull(options?: Partial<GlobalDevBarOptions>): GlobalDevBar;
43
+ /**
44
+ * Initialize DevBar with performance preset
45
+ * @param options Additional options to merge with preset
46
+ */
47
+ export declare function initPerformance(options?: Partial<GlobalDevBarOptions>): GlobalDevBar;
48
+ /**
49
+ * Initialize DevBar with responsive preset
50
+ * @param options Additional options to merge with preset
51
+ */
52
+ export declare function initResponsive(options?: Partial<GlobalDevBarOptions>): GlobalDevBar;
53
+ /**
54
+ * Initialize DevBar with debug preset
55
+ * @param options Additional options to merge with preset
56
+ */
57
+ export declare function initDebug(options?: Partial<GlobalDevBarOptions>): GlobalDevBar;
@@ -0,0 +1,133 @@
1
+ /**
2
+ * DevBar Configuration Presets
3
+ *
4
+ * Pre-configured options for common DevBar use cases.
5
+ */
6
+ import { initGlobalDevBar } from './GlobalDevBar.js';
7
+ // ============================================================================
8
+ // Preset Configurations
9
+ // ============================================================================
10
+ /**
11
+ * Minimal preset - shows only essential features
12
+ * Good for production debugging with minimal visual footprint
13
+ */
14
+ export const PRESET_MINIMAL = {
15
+ showMetrics: {
16
+ breakpoint: false,
17
+ fcp: false,
18
+ lcp: false,
19
+ cls: false,
20
+ inp: false,
21
+ pageSize: false,
22
+ },
23
+ showScreenshot: false,
24
+ showConsoleBadges: true,
25
+ showTooltips: false,
26
+ };
27
+ /**
28
+ * Full preset - shows all features
29
+ * Good for comprehensive development monitoring
30
+ */
31
+ export const PRESET_FULL = {
32
+ showMetrics: {
33
+ breakpoint: true,
34
+ fcp: true,
35
+ lcp: true,
36
+ cls: true,
37
+ inp: true,
38
+ pageSize: true,
39
+ },
40
+ showScreenshot: true,
41
+ showConsoleBadges: true,
42
+ showTooltips: true,
43
+ };
44
+ /**
45
+ * Performance preset - focuses on Core Web Vitals
46
+ * Good for performance optimization work
47
+ */
48
+ export const PRESET_PERFORMANCE = {
49
+ showMetrics: {
50
+ breakpoint: false,
51
+ fcp: true,
52
+ lcp: true,
53
+ cls: true,
54
+ inp: true,
55
+ pageSize: true,
56
+ },
57
+ showScreenshot: false,
58
+ showConsoleBadges: false,
59
+ showTooltips: true,
60
+ };
61
+ /**
62
+ * Responsive preset - focuses on responsive design
63
+ * Good for layout and breakpoint work
64
+ */
65
+ export const PRESET_RESPONSIVE = {
66
+ showMetrics: {
67
+ breakpoint: true,
68
+ fcp: false,
69
+ lcp: false,
70
+ cls: false,
71
+ inp: false,
72
+ pageSize: false,
73
+ },
74
+ showScreenshot: true,
75
+ showConsoleBadges: false,
76
+ showTooltips: true,
77
+ };
78
+ /**
79
+ * Debug preset - full features with debug logging
80
+ * Good for troubleshooting DevBar itself
81
+ */
82
+ export const PRESET_DEBUG = {
83
+ showMetrics: {
84
+ breakpoint: true,
85
+ fcp: true,
86
+ lcp: true,
87
+ cls: true,
88
+ inp: true,
89
+ pageSize: true,
90
+ },
91
+ showScreenshot: true,
92
+ showConsoleBadges: true,
93
+ showTooltips: true,
94
+ debug: true,
95
+ };
96
+ // ============================================================================
97
+ // Convenience Initialization Functions
98
+ // ============================================================================
99
+ /**
100
+ * Initialize DevBar with minimal preset
101
+ * @param options Additional options to merge with preset
102
+ */
103
+ export function initMinimal(options) {
104
+ return initGlobalDevBar({ ...PRESET_MINIMAL, ...options });
105
+ }
106
+ /**
107
+ * Initialize DevBar with full preset
108
+ * @param options Additional options to merge with preset
109
+ */
110
+ export function initFull(options) {
111
+ return initGlobalDevBar({ ...PRESET_FULL, ...options });
112
+ }
113
+ /**
114
+ * Initialize DevBar with performance preset
115
+ * @param options Additional options to merge with preset
116
+ */
117
+ export function initPerformance(options) {
118
+ return initGlobalDevBar({ ...PRESET_PERFORMANCE, ...options });
119
+ }
120
+ /**
121
+ * Initialize DevBar with responsive preset
122
+ * @param options Additional options to merge with preset
123
+ */
124
+ export function initResponsive(options) {
125
+ return initGlobalDevBar({ ...PRESET_RESPONSIVE, ...options });
126
+ }
127
+ /**
128
+ * Initialize DevBar with debug preset
129
+ * @param options Additional options to merge with preset
130
+ */
131
+ export function initDebug(options) {
132
+ return initGlobalDevBar({ ...PRESET_DEBUG, ...options });
133
+ }
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Page Schema Extraction
3
+ *
4
+ * Functions for extracting and formatting structured data from pages.
5
+ */
6
+ import type { PageSchema } from './types.js';
7
+ /**
8
+ * Extract structured data (JSON-LD, meta tags, Open Graph, etc.) from the page
9
+ */
10
+ export declare function extractPageSchema(): PageSchema;
11
+ /**
12
+ * Convert a page schema to markdown format
13
+ */
14
+ export declare function schemaToMarkdown(schema: PageSchema): string;
package/dist/schema.js ADDED
@@ -0,0 +1,114 @@
1
+ /**
2
+ * Page Schema Extraction
3
+ *
4
+ * Functions for extracting and formatting structured data from pages.
5
+ */
6
+ // ============================================================================
7
+ // Public API
8
+ // ============================================================================
9
+ /**
10
+ * Extract structured data (JSON-LD, meta tags, Open Graph, etc.) from the page
11
+ */
12
+ export function extractPageSchema() {
13
+ const schema = {
14
+ jsonLd: [],
15
+ metaTags: {},
16
+ openGraph: {},
17
+ twitter: {},
18
+ microdata: [],
19
+ };
20
+ // Extract JSON-LD
21
+ const jsonLdScripts = document.querySelectorAll('script[type="application/ld+json"]');
22
+ jsonLdScripts.forEach((script) => {
23
+ try {
24
+ const data = JSON.parse(script.textContent || '');
25
+ schema.jsonLd.push(data);
26
+ }
27
+ catch {
28
+ // Invalid JSON-LD, skip
29
+ }
30
+ });
31
+ // Extract meta tags
32
+ const metaTags = document.querySelectorAll('meta[name], meta[property]');
33
+ metaTags.forEach((meta) => {
34
+ const name = meta.getAttribute('name') || meta.getAttribute('property') || '';
35
+ const content = meta.getAttribute('content') || '';
36
+ if (name.startsWith('og:')) {
37
+ schema.openGraph[name.replace('og:', '')] = content;
38
+ }
39
+ else if (name.startsWith('twitter:')) {
40
+ schema.twitter[name.replace('twitter:', '')] = content;
41
+ }
42
+ else if (name) {
43
+ schema.metaTags[name] = content;
44
+ }
45
+ });
46
+ // Extract microdata
47
+ const microdataItems = document.querySelectorAll('[itemscope]');
48
+ microdataItems.forEach((item) => {
49
+ const itemType = item.getAttribute('itemtype');
50
+ const props = {};
51
+ item.querySelectorAll('[itemprop]').forEach((prop) => {
52
+ const propName = prop.getAttribute('itemprop') || '';
53
+ const propValue = prop.getAttribute('content') ||
54
+ prop.getAttribute('href') ||
55
+ prop.textContent?.trim().slice(0, 200) ||
56
+ '';
57
+ if (propName)
58
+ props[propName] = propValue;
59
+ });
60
+ if (itemType || Object.keys(props).length > 0) {
61
+ schema.microdata.push({ type: itemType, properties: props });
62
+ }
63
+ });
64
+ return schema;
65
+ }
66
+ /**
67
+ * Convert a page schema to markdown format
68
+ */
69
+ export function schemaToMarkdown(schema) {
70
+ let md = '';
71
+ if (schema.jsonLd.length > 0) {
72
+ md += '## JSON-LD\n\n';
73
+ schema.jsonLd.forEach((item, i) => {
74
+ md += `### Schema ${i + 1}\n\n`;
75
+ md += `\`\`\`json\n${JSON.stringify(item, null, 2)}\n\`\`\`\n\n`;
76
+ });
77
+ }
78
+ if (Object.keys(schema.openGraph).length > 0) {
79
+ md += '## Open Graph\n\n';
80
+ for (const [key, value] of Object.entries(schema.openGraph)) {
81
+ md += `- **${key}**: ${value}\n`;
82
+ }
83
+ md += '\n';
84
+ }
85
+ if (Object.keys(schema.twitter).length > 0) {
86
+ md += '## Twitter Cards\n\n';
87
+ for (const [key, value] of Object.entries(schema.twitter)) {
88
+ md += `- **${key}**: ${value}\n`;
89
+ }
90
+ md += '\n';
91
+ }
92
+ if (Object.keys(schema.metaTags).length > 0) {
93
+ md += '## Meta Tags\n\n';
94
+ for (const [key, value] of Object.entries(schema.metaTags)) {
95
+ md += `- **${key}**: ${value}\n`;
96
+ }
97
+ md += '\n';
98
+ }
99
+ if (schema.microdata.length > 0) {
100
+ md += '## Microdata\n\n';
101
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
102
+ schema.microdata.forEach((item, i) => {
103
+ md += `### Item ${i + 1}${item.type ? ` (${item.type})` : ''}\n\n`;
104
+ for (const [key, value] of Object.entries(item.properties || {})) {
105
+ md += `- **${key}**: ${value}\n`;
106
+ }
107
+ md += '\n';
108
+ });
109
+ }
110
+ if (!md) {
111
+ md = '_No structured data found on this page_\n';
112
+ }
113
+ return md;
114
+ }