@tanstack/devtools 0.6.23 → 0.7.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/chunk/{XF4JFOLU.js → VZEY7HNC.js} +32 -9
- package/dist/dev.js +3 -3
- package/dist/devtools/{YRFZDV5N.js → 7NDEDZB7.js} +182 -54
- package/dist/devtools/{MBQPV7BO.js → JEZZ2PQE.js} +133 -29
- package/dist/index.d.ts +6 -0
- package/dist/index.js +3 -3
- package/dist/server.js +2 -2
- package/package.json +3 -3
- package/src/context/devtools-context.test.ts +268 -1
- package/src/context/devtools-context.tsx +29 -9
- package/src/context/use-devtools-context.ts +2 -1
- package/src/styles/use-styles.ts +69 -0
- package/src/tabs/marketplace/plugin-section.tsx +52 -0
- package/src/tabs/marketplace/plugin-utils.test.ts +22 -14
- package/src/tabs/marketplace/plugin-utils.ts +14 -15
- package/src/tabs/plugin-registry.ts +29 -0
- package/src/utils/constants.ts +4 -0
- package/src/utils/get-default-active-plugins.test.ts +194 -0
- package/src/utils/get-default-active-plugins.ts +36 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { usePiPWindow, keyboardModifiers, getAllPermutations, TANSTACK_DEVTOOLS, DevtoolsContext, PLUGIN_TITLE_CONTAINER_ID, PLUGIN_CONTAINER_ID, uppercaseFirstLetter } from '../chunk/
|
|
1
|
+
import { usePiPWindow, keyboardModifiers, getAllPermutations, TANSTACK_DEVTOOLS, DevtoolsContext, PLUGIN_TITLE_CONTAINER_ID, PLUGIN_CONTAINER_ID, uppercaseFirstLetter, MAX_ACTIVE_PLUGINS } from '../chunk/VZEY7HNC.js';
|
|
2
2
|
import { createComponent, Portal, ssr, ssrAttribute, escape, ssrStyle } from 'solid-js/web';
|
|
3
3
|
import { createContext, createSignal, createEffect, Show, createMemo, For, useContext, onCleanup, onMount } from 'solid-js';
|
|
4
4
|
import { createShortcut, useKeyDownList } from '@solid-primitives/keyboard';
|
|
@@ -94,7 +94,7 @@ var usePlugins = () => {
|
|
|
94
94
|
setStore((prev) => {
|
|
95
95
|
const isActive = prev.state.activePlugins.includes(pluginId);
|
|
96
96
|
const updatedPlugins = isActive ? prev.state.activePlugins.filter((id) => id !== pluginId) : [...prev.state.activePlugins, pluginId];
|
|
97
|
-
if (updatedPlugins.length >
|
|
97
|
+
if (updatedPlugins.length > MAX_ACTIVE_PLUGINS) return prev;
|
|
98
98
|
return {
|
|
99
99
|
...prev,
|
|
100
100
|
state: {
|
|
@@ -1597,6 +1597,75 @@ var stylesFactory = (theme) => {
|
|
|
1597
1597
|
text-transform: uppercase;
|
|
1598
1598
|
letter-spacing: 0.05em;
|
|
1599
1599
|
`,
|
|
1600
|
+
pluginMarketplaceFeatureBanner: css2`
|
|
1601
|
+
margin-top: 1rem;
|
|
1602
|
+
padding: 1.25rem 1.5rem;
|
|
1603
|
+
background: ${t(
|
|
1604
|
+
"linear-gradient(135deg, #3b82f6 0%, #2563eb 100%)",
|
|
1605
|
+
"linear-gradient(135deg, #1e3a8a 0%, #1e40af 100%)"
|
|
1606
|
+
)};
|
|
1607
|
+
border-radius: 0.75rem;
|
|
1608
|
+
border: 1px solid ${t(colors.blue[400], colors.blue[800])};
|
|
1609
|
+
box-shadow:
|
|
1610
|
+
0 4px 6px -1px rgba(0, 0, 0, 0.1),
|
|
1611
|
+
0 2px 4px -1px rgba(0, 0, 0, 0.06);
|
|
1612
|
+
`,
|
|
1613
|
+
pluginMarketplaceFeatureBannerContent: css2`
|
|
1614
|
+
display: flex;
|
|
1615
|
+
flex-direction: column;
|
|
1616
|
+
gap: 0.75rem;
|
|
1617
|
+
`,
|
|
1618
|
+
pluginMarketplaceFeatureBannerTitle: css2`
|
|
1619
|
+
font-size: 1.125rem;
|
|
1620
|
+
font-weight: 700;
|
|
1621
|
+
color: white;
|
|
1622
|
+
margin: 0;
|
|
1623
|
+
display: flex;
|
|
1624
|
+
align-items: center;
|
|
1625
|
+
gap: 0.5rem;
|
|
1626
|
+
`,
|
|
1627
|
+
pluginMarketplaceFeatureBannerIcon: css2`
|
|
1628
|
+
width: 24px;
|
|
1629
|
+
height: 24px;
|
|
1630
|
+
display: inline-flex;
|
|
1631
|
+
`,
|
|
1632
|
+
pluginMarketplaceFeatureBannerText: css2`
|
|
1633
|
+
font-size: 0.95rem;
|
|
1634
|
+
color: ${t("rgba(255, 255, 255, 0.95)", "rgba(255, 255, 255, 0.9)")};
|
|
1635
|
+
line-height: 1.5;
|
|
1636
|
+
margin: 0;
|
|
1637
|
+
`,
|
|
1638
|
+
pluginMarketplaceFeatureBannerButton: css2`
|
|
1639
|
+
display: inline-flex;
|
|
1640
|
+
align-items: center;
|
|
1641
|
+
gap: 0.5rem;
|
|
1642
|
+
padding: 0.625rem 1.25rem;
|
|
1643
|
+
background: white;
|
|
1644
|
+
color: ${colors.blue[600]};
|
|
1645
|
+
font-weight: 600;
|
|
1646
|
+
font-size: 0.95rem;
|
|
1647
|
+
border-radius: 0.5rem;
|
|
1648
|
+
border: none;
|
|
1649
|
+
cursor: pointer;
|
|
1650
|
+
transition: all 0.2s ease;
|
|
1651
|
+
text-decoration: none;
|
|
1652
|
+
align-self: flex-start;
|
|
1653
|
+
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
|
1654
|
+
|
|
1655
|
+
&:hover {
|
|
1656
|
+
background: ${t(colors.gray[50], colors.gray[100])};
|
|
1657
|
+
transform: translateY(-1px);
|
|
1658
|
+
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15);
|
|
1659
|
+
}
|
|
1660
|
+
|
|
1661
|
+
&:active {
|
|
1662
|
+
transform: translateY(0);
|
|
1663
|
+
}
|
|
1664
|
+
`,
|
|
1665
|
+
pluginMarketplaceFeatureBannerButtonIcon: css2`
|
|
1666
|
+
width: 18px;
|
|
1667
|
+
height: 18px;
|
|
1668
|
+
`,
|
|
1600
1669
|
pluginMarketplaceCardDisabled: css2`
|
|
1601
1670
|
opacity: 0.6;
|
|
1602
1671
|
filter: grayscale(0.3);
|
|
@@ -2249,16 +2318,28 @@ var PluginCardComponent = (props) => {
|
|
|
2249
2318
|
};
|
|
2250
2319
|
|
|
2251
2320
|
// src/tabs/marketplace/plugin-section.tsx
|
|
2252
|
-
var _tmpl$12 =
|
|
2253
|
-
var _tmpl$25 =
|
|
2321
|
+
var _tmpl$12 = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z"></path></svg>';
|
|
2322
|
+
var _tmpl$25 = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="2" y="4" width="20" height="16" rx="2"></rect><path d="m22 7-8.97 5.7a1.94 1.94 0 0 1-2.06 0L2 7"></path></svg>';
|
|
2323
|
+
var _tmpl$34 = ["<div", "><div", "><h4", "><span", ">", "</span>Want to be featured here?</h4><p", `>If you've built a plugin for TanStack Devtools and would like to showcase it in the featured section, we'd love to hear from you! Reach out to us to discuss partnership opportunities.</p><a href="mailto:partners+devtools@tanstack.com?subject=Featured%20Plugin%20Partnership%20Inquiry"`, "><span", ">", "</span>Contact Us</a></div></div>"];
|
|
2324
|
+
var _tmpl$44 = ["<div", ">", "</div>"];
|
|
2325
|
+
var _tmpl$53 = ["<div", "><div", "><div", '><div class="', '">', "</div><h3", ">", "</h3></div></div>", "</div>"];
|
|
2326
|
+
var StarIcon = () => ssr(_tmpl$12);
|
|
2327
|
+
var MailIcon = () => ssr(_tmpl$25);
|
|
2254
2328
|
var PluginSectionComponent = (props) => {
|
|
2255
2329
|
const styles = useStyles();
|
|
2256
|
-
return ssr(_tmpl$
|
|
2330
|
+
return ssr(_tmpl$53, ssrAttribute("class", escape(styles().pluginMarketplaceSection, true), false), ssrAttribute("class", escape(styles().pluginMarketplaceSectionHeader, true), false), ssrAttribute("class", escape(styles().pluginMarketplaceSectionHeaderLeft, true), false), `${escape(styles().pluginMarketplaceSectionChevron, true) || ""} ${props.isCollapsed() ? escape(escape(styles().pluginMarketplaceSectionChevronCollapsed, true), true) : ""}`, escape(createComponent(ChevronDownIcon, {})), ssrAttribute("class", escape(styles().pluginMarketplaceSectionTitle, true), false), escape(props.section.displayName), escape(createComponent(Show, {
|
|
2257
2331
|
get when() {
|
|
2258
2332
|
return !props.isCollapsed();
|
|
2259
2333
|
},
|
|
2260
2334
|
get children() {
|
|
2261
|
-
return
|
|
2335
|
+
return [createComponent(Show, {
|
|
2336
|
+
get when() {
|
|
2337
|
+
return props.section.id === "featured";
|
|
2338
|
+
},
|
|
2339
|
+
get children() {
|
|
2340
|
+
return ssr(_tmpl$34, ssrAttribute("class", escape(styles().pluginMarketplaceFeatureBanner, true), false), ssrAttribute("class", escape(styles().pluginMarketplaceFeatureBannerContent, true), false), ssrAttribute("class", escape(styles().pluginMarketplaceFeatureBannerTitle, true), false), ssrAttribute("class", escape(styles().pluginMarketplaceFeatureBannerIcon, true), false), escape(createComponent(StarIcon, {})), ssrAttribute("class", escape(styles().pluginMarketplaceFeatureBannerText, true), false), ssrAttribute("class", escape(styles().pluginMarketplaceFeatureBannerButton, true), false), ssrAttribute("class", escape(styles().pluginMarketplaceFeatureBannerButtonIcon, true), false), escape(createComponent(MailIcon, {})));
|
|
2341
|
+
}
|
|
2342
|
+
}), ssr(_tmpl$44, ssrAttribute("class", escape(styles().pluginMarketplaceGrid, true), false), escape(createComponent(For, {
|
|
2262
2343
|
get each() {
|
|
2263
2344
|
return props.section.cards;
|
|
2264
2345
|
},
|
|
@@ -2268,7 +2349,7 @@ var PluginSectionComponent = (props) => {
|
|
|
2268
2349
|
return props.onCardAction;
|
|
2269
2350
|
}
|
|
2270
2351
|
})
|
|
2271
|
-
})));
|
|
2352
|
+
})))];
|
|
2272
2353
|
}
|
|
2273
2354
|
})));
|
|
2274
2355
|
};
|
|
@@ -2467,11 +2548,36 @@ var PLUGIN_REGISTRY = {
|
|
|
2467
2548
|
isNew: true,
|
|
2468
2549
|
// New plugin banner
|
|
2469
2550
|
tags: ["TanStack"]
|
|
2470
|
-
}
|
|
2551
|
+
},
|
|
2471
2552
|
// ==========================================
|
|
2472
2553
|
// THIRD-PARTY PLUGINS - Examples
|
|
2473
2554
|
// ==========================================
|
|
2474
2555
|
// External contributors can add their plugins below!
|
|
2556
|
+
// Dimano — Prefetch Heatmap for TanStack Router
|
|
2557
|
+
"@dimano/ts-devtools-plugin-prefetch-heatmap": {
|
|
2558
|
+
packageName: "@dimano/ts-devtools-plugin-prefetch-heatmap",
|
|
2559
|
+
title: "Prefetch Heatmap",
|
|
2560
|
+
description: "Visualize TanStack Router prefetch intent, hits, and waste with a color overlay and a live metrics panel.",
|
|
2561
|
+
requires: {
|
|
2562
|
+
packageName: "@tanstack/react-router",
|
|
2563
|
+
minVersion: "1.0.0"
|
|
2564
|
+
},
|
|
2565
|
+
// default export registers the plugin
|
|
2566
|
+
pluginImport: {
|
|
2567
|
+
importName: "registerPrefetchHeatmapPlugin",
|
|
2568
|
+
type: "function"
|
|
2569
|
+
},
|
|
2570
|
+
// helps the host match your plugin deterministically
|
|
2571
|
+
pluginId: "prefetch-heatmap",
|
|
2572
|
+
// show a nice card in the marketplace
|
|
2573
|
+
logoUrl: "https://raw.githubusercontent.com/dimitrianoudi/tanstack-prefetch-heatmap/main/assets/prefetch-heatmap-card.png",
|
|
2574
|
+
docsUrl: "https://github.com/dimitrianoudi/tanstack-prefetch-heatmap#prefetch-heatmap-devtools-plugin",
|
|
2575
|
+
repoUrl: "https://github.com/dimitrianoudi/tanstack-prefetch-heatmap",
|
|
2576
|
+
author: "Dimitris Anoudis (@dimitrianoudi)",
|
|
2577
|
+
framework: "react",
|
|
2578
|
+
isNew: true,
|
|
2579
|
+
tags: ["Router", "Prefetch", "Analytics", "Overlay", "TanStack"]
|
|
2580
|
+
}
|
|
2475
2581
|
};
|
|
2476
2582
|
function getAllPluginMetadata() {
|
|
2477
2583
|
return Object.values(PLUGIN_REGISTRY);
|
|
@@ -2657,6 +2763,15 @@ var buildPluginCards = (pkg, currentFramework, registeredPlugins, existingCards)
|
|
|
2657
2763
|
};
|
|
2658
2764
|
var groupIntoSections = (allCards) => {
|
|
2659
2765
|
const sections = [];
|
|
2766
|
+
const featuredCards = allCards.filter(
|
|
2767
|
+
(c) => c.metadata?.featured && c.actionType !== "already-installed" && c.isCurrentFramework
|
|
2768
|
+
// Only show featured plugins for current framework
|
|
2769
|
+
);
|
|
2770
|
+
sections.push({
|
|
2771
|
+
id: "featured",
|
|
2772
|
+
displayName: "\u2B50 Featured",
|
|
2773
|
+
cards: featuredCards
|
|
2774
|
+
});
|
|
2660
2775
|
const activeCards = allCards.filter(
|
|
2661
2776
|
(c) => c.actionType === "already-installed" && c.isRegistered
|
|
2662
2777
|
);
|
|
@@ -2667,17 +2782,6 @@ var groupIntoSections = (allCards) => {
|
|
|
2667
2782
|
cards: activeCards
|
|
2668
2783
|
});
|
|
2669
2784
|
}
|
|
2670
|
-
const featuredCards = allCards.filter(
|
|
2671
|
-
(c) => c.metadata?.featured && c.actionType !== "already-installed" && c.isCurrentFramework
|
|
2672
|
-
// Only show featured plugins for current framework
|
|
2673
|
-
);
|
|
2674
|
-
if (featuredCards.length > 0) {
|
|
2675
|
-
sections.push({
|
|
2676
|
-
id: "featured",
|
|
2677
|
-
displayName: "\u2B50 Featured",
|
|
2678
|
-
cards: featuredCards
|
|
2679
|
-
});
|
|
2680
|
-
}
|
|
2681
2785
|
const availableCards = allCards.filter(
|
|
2682
2786
|
(c) => c.isCurrentFramework && c.actionType !== "already-installed" && !c.metadata?.featured
|
|
2683
2787
|
// Not featured (already in featured section)
|
|
@@ -2922,7 +3026,7 @@ var PluginMarketplace = () => {
|
|
|
2922
3026
|
// src/tabs/plugins-tab.tsx
|
|
2923
3027
|
var _tmpl$17 = ["<div", "><div", "><div", "><div", ">", "</div><div", "><h3>Add More</h3></div></div></div>", "</div>"];
|
|
2924
3028
|
var _tmpl$28 = ["<div", '><h3 id="', '"></h3></div>'];
|
|
2925
|
-
var _tmpl$
|
|
3029
|
+
var _tmpl$35 = ['<div id="', '"', "></div>"];
|
|
2926
3030
|
var PluginsTab = () => {
|
|
2927
3031
|
const {
|
|
2928
3032
|
plugins,
|
|
@@ -2987,7 +3091,7 @@ var PluginsTab = () => {
|
|
|
2987
3091
|
get each() {
|
|
2988
3092
|
return activePlugins();
|
|
2989
3093
|
},
|
|
2990
|
-
children: (pluginId) => ssr(_tmpl$
|
|
3094
|
+
children: (pluginId) => ssr(_tmpl$35, `${escape(PLUGIN_CONTAINER_ID, true)}-${escape(pluginId, true)}`, ssrAttribute("class", escape(styles().pluginsTabContent, true), false))
|
|
2991
3095
|
});
|
|
2992
3096
|
},
|
|
2993
3097
|
get children() {
|
|
@@ -3061,9 +3165,9 @@ function useHeadChanges(onChange, opts = {}) {
|
|
|
3061
3165
|
// src/tabs/seo-tab.tsx
|
|
3062
3166
|
var _tmpl$18 = ["<div", ' style="', '"><div', ' style="', '">', " Preview</div>", "<div", ">", "</div><div", ">", "</div><div", ">", "</div></div>"];
|
|
3063
3167
|
var _tmpl$29 = ["<img", ' alt="Preview"', ">"];
|
|
3064
|
-
var _tmpl$
|
|
3065
|
-
var _tmpl$
|
|
3066
|
-
var _tmpl$
|
|
3168
|
+
var _tmpl$36 = ["<div", ' style="', '">No Image</div>'];
|
|
3169
|
+
var _tmpl$45 = ["<div", ">", "</div>"];
|
|
3170
|
+
var _tmpl$54 = ["<div>", "", "</div>"];
|
|
3067
3171
|
var _tmpl$63 = ["<div", "><strong>Missing tags for ", ":</strong><ul", ">", "</ul></div>"];
|
|
3068
3172
|
var _tmpl$72 = ["<li", ">", "</li>"];
|
|
3069
3173
|
var SOCIALS = [
|
|
@@ -3190,7 +3294,7 @@ var SOCIALS = [
|
|
|
3190
3294
|
];
|
|
3191
3295
|
function SocialPreview(props) {
|
|
3192
3296
|
const styles = useStyles();
|
|
3193
|
-
return ssr(_tmpl$18, ssrAttribute("class", escape(styles().seoPreviewCard, true), false), "border-color:" + escape(props.color, true), ssrAttribute("class", escape(styles().seoPreviewHeader, true), false), "color:" + escape(props.color, true), escape(props.network), props.meta.image ? ssr(_tmpl$29, ssrAttribute("src", escape(props.meta.image, true), false), ssrAttribute("class", escape(styles().seoPreviewImage, true), false)) : ssr(_tmpl$
|
|
3297
|
+
return ssr(_tmpl$18, ssrAttribute("class", escape(styles().seoPreviewCard, true), false), "border-color:" + escape(props.color, true), ssrAttribute("class", escape(styles().seoPreviewHeader, true), false), "color:" + escape(props.color, true), escape(props.network), props.meta.image ? ssr(_tmpl$29, ssrAttribute("src", escape(props.meta.image, true), false), ssrAttribute("class", escape(styles().seoPreviewImage, true), false)) : ssr(_tmpl$36, ssrAttribute("class", escape(styles().seoPreviewImage, true), false), "background:#222;color:#888;display:flex;align-items:center;justify-content:center;min-height:80px;width:100%"), ssrAttribute("class", escape(styles().seoPreviewTitle, true), false), escape(props.meta.title) || "No Title", ssrAttribute("class", escape(styles().seoPreviewDesc, true), false), escape(props.meta.description) || "No Description", ssrAttribute("class", escape(styles().seoPreviewUrl, true), false), escape(props.meta.url) || escape(window.location.href));
|
|
3194
3298
|
}
|
|
3195
3299
|
var SeoTab = () => {
|
|
3196
3300
|
const [reports, setReports] = createSignal(analyzeHead());
|
|
@@ -3235,13 +3339,13 @@ var SeoTab = () => {
|
|
|
3235
3339
|
}
|
|
3236
3340
|
}), createComponent(SectionDescription, {
|
|
3237
3341
|
children: "See how your current page will look when shared on popular social networks. The tool checks for essential meta tags and highlights any that are missing."
|
|
3238
|
-
}), ssr(_tmpl$
|
|
3342
|
+
}), ssr(_tmpl$45, ssrAttribute("class", escape(styles().seoPreviewSection, true), false), escape(createComponent(For, {
|
|
3239
3343
|
get each() {
|
|
3240
3344
|
return reports();
|
|
3241
3345
|
},
|
|
3242
3346
|
children: (report, i) => {
|
|
3243
3347
|
const social = SOCIALS[i()];
|
|
3244
|
-
return ssr(_tmpl$
|
|
3348
|
+
return ssr(_tmpl$54, escape(createComponent(SocialPreview, {
|
|
3245
3349
|
get meta() {
|
|
3246
3350
|
return report.found;
|
|
3247
3351
|
},
|
|
@@ -3286,7 +3390,7 @@ var tabs = [{
|
|
|
3286
3390
|
// src/components/tabs.tsx
|
|
3287
3391
|
var _tmpl$19 = ["<div", ">", "", "</div>"];
|
|
3288
3392
|
var _tmpl$210 = ['<button type="button"', ">", "</button>"];
|
|
3289
|
-
var _tmpl$
|
|
3393
|
+
var _tmpl$37 = ['<div style="', '"><button type="button"', ">", '</button><button type="button"', ">", "</button></div>"];
|
|
3290
3394
|
var Tabs = (props) => {
|
|
3291
3395
|
const styles = useStyles();
|
|
3292
3396
|
const {
|
|
@@ -3302,7 +3406,7 @@ var Tabs = (props) => {
|
|
|
3302
3406
|
children: (tab) => ssr(_tmpl$210, ssrAttribute("class", escape(clsx3(styles().tab, {
|
|
3303
3407
|
active: state().activeTab === tab.id
|
|
3304
3408
|
}), true), false), escape(tab.icon()))
|
|
3305
|
-
})), pipWindow().pipWindow !== null ? escape(null) : ssr(_tmpl$
|
|
3409
|
+
})), pipWindow().pipWindow !== null ? escape(null) : ssr(_tmpl$37, "margin-top:auto", ssrAttribute("class", escape(clsx3(styles().tab, "detach"), true), false), escape(createComponent(PiP, {})), ssrAttribute("class", escape(clsx3(styles().tab, "close"), true), false), escape(createComponent(X, {}))));
|
|
3306
3410
|
};
|
|
3307
3411
|
var _tmpl$20 = ["<div", ">", "</div>"];
|
|
3308
3412
|
var TabContent = () => {
|
package/dist/index.d.ts
CHANGED
|
@@ -120,6 +120,12 @@ interface TanStackDevtoolsPlugin {
|
|
|
120
120
|
* If not provided, it will be generated based on the name.
|
|
121
121
|
*/
|
|
122
122
|
id?: string;
|
|
123
|
+
/**
|
|
124
|
+
* Whether the plugin should be open by default when there are no active plugins.
|
|
125
|
+
* If true, this plugin will be added to activePlugins on initial load when activePlugins is empty.
|
|
126
|
+
* @default false
|
|
127
|
+
*/
|
|
128
|
+
defaultOpen?: boolean;
|
|
123
129
|
/**
|
|
124
130
|
* Render the plugin UI by using the provided element. This function will be called
|
|
125
131
|
* when the plugin tab is clicked and expected to be mounted.
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { initialState, DevtoolsProvider, PiPProvider } from './chunk/
|
|
2
|
-
export { PLUGIN_CONTAINER_ID, PLUGIN_TITLE_CONTAINER_ID } from './chunk/
|
|
1
|
+
import { initialState, DevtoolsProvider, PiPProvider } from './chunk/VZEY7HNC.js';
|
|
2
|
+
export { PLUGIN_CONTAINER_ID, PLUGIN_TITLE_CONTAINER_ID } from './chunk/VZEY7HNC.js';
|
|
3
3
|
import { render, createComponent, Portal } from 'solid-js/web';
|
|
4
4
|
import { lazy } from 'solid-js';
|
|
5
5
|
import { ClientEventBus } from '@tanstack/devtools-event-bus/client';
|
|
@@ -30,7 +30,7 @@ var TanStackDevtoolsCore = class {
|
|
|
30
30
|
const mountTo = el;
|
|
31
31
|
const dispose = render(() => {
|
|
32
32
|
const _self$ = this;
|
|
33
|
-
this.#Component = lazy(() => import('./devtools/
|
|
33
|
+
this.#Component = lazy(() => import('./devtools/7NDEDZB7.js'));
|
|
34
34
|
const Devtools = this.#Component;
|
|
35
35
|
this.#eventBus = new ClientEventBus(this.#eventBusConfig);
|
|
36
36
|
this.#eventBus.start();
|
package/dist/server.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { initialState } from './chunk/
|
|
2
|
-
export { PLUGIN_CONTAINER_ID, PLUGIN_TITLE_CONTAINER_ID } from './chunk/
|
|
1
|
+
import { initialState } from './chunk/VZEY7HNC.js';
|
|
2
|
+
export { PLUGIN_CONTAINER_ID, PLUGIN_TITLE_CONTAINER_ID } from './chunk/VZEY7HNC.js';
|
|
3
3
|
import 'solid-js/web';
|
|
4
4
|
import 'solid-js';
|
|
5
5
|
import '@tanstack/devtools-event-bus/client';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tanstack/devtools",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "TanStack Devtools is a set of tools for building advanced devtools for your application.",
|
|
5
5
|
"author": "Tanner Linsley",
|
|
6
6
|
"license": "MIT",
|
|
@@ -56,8 +56,8 @@
|
|
|
56
56
|
"goober": "^2.1.16",
|
|
57
57
|
"solid-js": "^1.9.9",
|
|
58
58
|
"@tanstack/devtools-client": "0.0.3",
|
|
59
|
-
"@tanstack/devtools-
|
|
60
|
-
"@tanstack/devtools-
|
|
59
|
+
"@tanstack/devtools-ui": "0.4.4",
|
|
60
|
+
"@tanstack/devtools-event-bus": "0.3.3"
|
|
61
61
|
},
|
|
62
62
|
"peerDependencies": {
|
|
63
63
|
"solid-js": ">=1.9.7"
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { beforeEach, describe, expect, it } from 'vitest'
|
|
2
2
|
import { TANSTACK_DEVTOOLS_STATE } from '../utils/storage'
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
getExistingStateFromStorage,
|
|
5
|
+
getStateFromLocalStorage,
|
|
6
|
+
} from './devtools-context'
|
|
7
|
+
import type { TanStackDevtoolsPlugin } from './devtools-context'
|
|
4
8
|
|
|
5
9
|
describe('getStateFromLocalStorage', () => {
|
|
6
10
|
beforeEach(() => {
|
|
@@ -56,4 +60,267 @@ describe('getStateFromLocalStorage', () => {
|
|
|
56
60
|
const state = getStateFromLocalStorage(undefined)
|
|
57
61
|
expect(state).toEqual(undefined)
|
|
58
62
|
})
|
|
63
|
+
|
|
64
|
+
it('should return undefined when no localStorage state exists (allowing defaultOpen to be applied)', () => {
|
|
65
|
+
// No existing state in localStorage - this allows defaultOpen logic to trigger
|
|
66
|
+
const plugins: Array<TanStackDevtoolsPlugin> = [
|
|
67
|
+
{
|
|
68
|
+
id: 'plugin1',
|
|
69
|
+
render: () => {},
|
|
70
|
+
name: 'Plugin 1',
|
|
71
|
+
defaultOpen: true,
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
id: 'plugin2',
|
|
75
|
+
render: () => {},
|
|
76
|
+
name: 'Plugin 2',
|
|
77
|
+
defaultOpen: false,
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
id: 'plugin3',
|
|
81
|
+
render: () => {},
|
|
82
|
+
name: 'Plugin 3',
|
|
83
|
+
defaultOpen: true,
|
|
84
|
+
},
|
|
85
|
+
]
|
|
86
|
+
|
|
87
|
+
// When undefined is returned, getExistingStateFromStorage will fill activePlugins with defaultOpen plugins
|
|
88
|
+
const state = getStateFromLocalStorage(plugins)
|
|
89
|
+
expect(state).toEqual(undefined)
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
it('should preserve existing activePlugins from localStorage (defaultOpen should not override)', () => {
|
|
93
|
+
const mockState = {
|
|
94
|
+
activePlugins: ['plugin2'],
|
|
95
|
+
settings: {
|
|
96
|
+
theme: 'dark',
|
|
97
|
+
},
|
|
98
|
+
}
|
|
99
|
+
localStorage.setItem(TANSTACK_DEVTOOLS_STATE, JSON.stringify(mockState))
|
|
100
|
+
|
|
101
|
+
const plugins: Array<TanStackDevtoolsPlugin> = [
|
|
102
|
+
{
|
|
103
|
+
id: 'plugin1',
|
|
104
|
+
render: () => {},
|
|
105
|
+
name: 'Plugin 1',
|
|
106
|
+
defaultOpen: true,
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
id: 'plugin2',
|
|
110
|
+
render: () => {},
|
|
111
|
+
name: 'Plugin 2',
|
|
112
|
+
defaultOpen: false,
|
|
113
|
+
},
|
|
114
|
+
]
|
|
115
|
+
|
|
116
|
+
const state = getStateFromLocalStorage(plugins)
|
|
117
|
+
// Should keep existing activePlugins - defaultOpen logic won't override in getExistingStateFromStorage
|
|
118
|
+
expect(state?.activePlugins).toEqual(['plugin2'])
|
|
119
|
+
})
|
|
120
|
+
|
|
121
|
+
it('should automatically activate a single plugin when no active plugins exist', () => {
|
|
122
|
+
// No existing state in localStorage
|
|
123
|
+
const plugins: Array<TanStackDevtoolsPlugin> = [
|
|
124
|
+
{
|
|
125
|
+
id: 'only-plugin',
|
|
126
|
+
render: () => {},
|
|
127
|
+
name: 'Only Plugin',
|
|
128
|
+
},
|
|
129
|
+
]
|
|
130
|
+
|
|
131
|
+
const state = getStateFromLocalStorage(plugins)
|
|
132
|
+
// Should return undefined - the single plugin activation happens in getExistingStateFromStorage
|
|
133
|
+
expect(state).toEqual(undefined)
|
|
134
|
+
})
|
|
135
|
+
})
|
|
136
|
+
|
|
137
|
+
describe('getExistingStateFromStorage - integration tests', () => {
|
|
138
|
+
beforeEach(() => {
|
|
139
|
+
localStorage.clear()
|
|
140
|
+
})
|
|
141
|
+
|
|
142
|
+
it('should automatically activate a single plugin when no localStorage state exists', () => {
|
|
143
|
+
const plugins: Array<TanStackDevtoolsPlugin> = [
|
|
144
|
+
{
|
|
145
|
+
id: 'only-plugin',
|
|
146
|
+
render: () => {},
|
|
147
|
+
name: 'Only Plugin',
|
|
148
|
+
},
|
|
149
|
+
]
|
|
150
|
+
|
|
151
|
+
const state = getExistingStateFromStorage(undefined, plugins)
|
|
152
|
+
expect(state.state.activePlugins).toEqual(['only-plugin'])
|
|
153
|
+
expect(state.plugins).toHaveLength(1)
|
|
154
|
+
expect(state.plugins![0]?.id).toBe('only-plugin')
|
|
155
|
+
})
|
|
156
|
+
|
|
157
|
+
it('should activate plugins with defaultOpen: true when no localStorage state exists', () => {
|
|
158
|
+
const plugins: Array<TanStackDevtoolsPlugin> = [
|
|
159
|
+
{
|
|
160
|
+
id: 'plugin1',
|
|
161
|
+
render: () => {},
|
|
162
|
+
name: 'Plugin 1',
|
|
163
|
+
defaultOpen: true,
|
|
164
|
+
},
|
|
165
|
+
{
|
|
166
|
+
id: 'plugin2',
|
|
167
|
+
render: () => {},
|
|
168
|
+
name: 'Plugin 2',
|
|
169
|
+
defaultOpen: false,
|
|
170
|
+
},
|
|
171
|
+
{
|
|
172
|
+
id: 'plugin3',
|
|
173
|
+
render: () => {},
|
|
174
|
+
name: 'Plugin 3',
|
|
175
|
+
defaultOpen: true,
|
|
176
|
+
},
|
|
177
|
+
]
|
|
178
|
+
|
|
179
|
+
const state = getExistingStateFromStorage(undefined, plugins)
|
|
180
|
+
expect(state.state.activePlugins).toEqual(['plugin1', 'plugin3'])
|
|
181
|
+
expect(state.plugins).toHaveLength(3)
|
|
182
|
+
})
|
|
183
|
+
|
|
184
|
+
it('should limit defaultOpen plugins to MAX_ACTIVE_PLUGINS (3) when 5 have defaultOpen: true', () => {
|
|
185
|
+
const plugins: Array<TanStackDevtoolsPlugin> = [
|
|
186
|
+
{
|
|
187
|
+
id: 'plugin1',
|
|
188
|
+
render: () => {},
|
|
189
|
+
name: 'Plugin 1',
|
|
190
|
+
defaultOpen: true,
|
|
191
|
+
},
|
|
192
|
+
{
|
|
193
|
+
id: 'plugin2',
|
|
194
|
+
render: () => {},
|
|
195
|
+
name: 'Plugin 2',
|
|
196
|
+
defaultOpen: true,
|
|
197
|
+
},
|
|
198
|
+
{
|
|
199
|
+
id: 'plugin3',
|
|
200
|
+
render: () => {},
|
|
201
|
+
name: 'Plugin 3',
|
|
202
|
+
defaultOpen: true,
|
|
203
|
+
},
|
|
204
|
+
{
|
|
205
|
+
id: 'plugin4',
|
|
206
|
+
render: () => {},
|
|
207
|
+
name: 'Plugin 4',
|
|
208
|
+
defaultOpen: true,
|
|
209
|
+
},
|
|
210
|
+
{
|
|
211
|
+
id: 'plugin5',
|
|
212
|
+
render: () => {},
|
|
213
|
+
name: 'Plugin 5',
|
|
214
|
+
defaultOpen: true,
|
|
215
|
+
},
|
|
216
|
+
]
|
|
217
|
+
|
|
218
|
+
const state = getExistingStateFromStorage(undefined, plugins)
|
|
219
|
+
// Should only activate first 3 plugins
|
|
220
|
+
expect(state.state.activePlugins).toEqual(['plugin1', 'plugin2', 'plugin3'])
|
|
221
|
+
expect(state.state.activePlugins).toHaveLength(3)
|
|
222
|
+
expect(state.state.activePlugins).not.toContain('plugin4')
|
|
223
|
+
expect(state.state.activePlugins).not.toContain('plugin5')
|
|
224
|
+
// All 5 plugins should still be in the plugins array
|
|
225
|
+
expect(state.plugins).toHaveLength(5)
|
|
226
|
+
})
|
|
227
|
+
|
|
228
|
+
it('should preserve existing activePlugins from localStorage even when plugins have defaultOpen', () => {
|
|
229
|
+
const mockState = {
|
|
230
|
+
activePlugins: ['plugin2', 'plugin4'],
|
|
231
|
+
settings: {
|
|
232
|
+
theme: 'dark',
|
|
233
|
+
},
|
|
234
|
+
}
|
|
235
|
+
localStorage.setItem(TANSTACK_DEVTOOLS_STATE, JSON.stringify(mockState))
|
|
236
|
+
|
|
237
|
+
const plugins: Array<TanStackDevtoolsPlugin> = [
|
|
238
|
+
{
|
|
239
|
+
id: 'plugin1',
|
|
240
|
+
render: () => {},
|
|
241
|
+
name: 'Plugin 1',
|
|
242
|
+
defaultOpen: true,
|
|
243
|
+
},
|
|
244
|
+
{
|
|
245
|
+
id: 'plugin2',
|
|
246
|
+
render: () => {},
|
|
247
|
+
name: 'Plugin 2',
|
|
248
|
+
defaultOpen: false,
|
|
249
|
+
},
|
|
250
|
+
{
|
|
251
|
+
id: 'plugin3',
|
|
252
|
+
render: () => {},
|
|
253
|
+
name: 'Plugin 3',
|
|
254
|
+
defaultOpen: true,
|
|
255
|
+
},
|
|
256
|
+
{
|
|
257
|
+
id: 'plugin4',
|
|
258
|
+
render: () => {},
|
|
259
|
+
name: 'Plugin 4',
|
|
260
|
+
defaultOpen: false,
|
|
261
|
+
},
|
|
262
|
+
]
|
|
263
|
+
|
|
264
|
+
const state = getExistingStateFromStorage(undefined, plugins)
|
|
265
|
+
// Should preserve the localStorage state, not use defaultOpen
|
|
266
|
+
expect(state.state.activePlugins).toEqual(['plugin2', 'plugin4'])
|
|
267
|
+
expect(state.plugins).toHaveLength(4)
|
|
268
|
+
})
|
|
269
|
+
|
|
270
|
+
it('should return empty activePlugins when no defaultOpen and multiple plugins', () => {
|
|
271
|
+
const plugins: Array<TanStackDevtoolsPlugin> = [
|
|
272
|
+
{
|
|
273
|
+
id: 'plugin1',
|
|
274
|
+
render: () => {},
|
|
275
|
+
name: 'Plugin 1',
|
|
276
|
+
},
|
|
277
|
+
{
|
|
278
|
+
id: 'plugin2',
|
|
279
|
+
render: () => {},
|
|
280
|
+
name: 'Plugin 2',
|
|
281
|
+
},
|
|
282
|
+
{
|
|
283
|
+
id: 'plugin3',
|
|
284
|
+
render: () => {},
|
|
285
|
+
name: 'Plugin 3',
|
|
286
|
+
},
|
|
287
|
+
]
|
|
288
|
+
|
|
289
|
+
const state = getExistingStateFromStorage(undefined, plugins)
|
|
290
|
+
expect(state.state.activePlugins).toEqual([])
|
|
291
|
+
expect(state.plugins).toHaveLength(3)
|
|
292
|
+
})
|
|
293
|
+
|
|
294
|
+
it('should handle single plugin with defaultOpen: false by activating it anyway', () => {
|
|
295
|
+
const plugins: Array<TanStackDevtoolsPlugin> = [
|
|
296
|
+
{
|
|
297
|
+
id: 'only-plugin',
|
|
298
|
+
render: () => {},
|
|
299
|
+
name: 'Only Plugin',
|
|
300
|
+
defaultOpen: false,
|
|
301
|
+
},
|
|
302
|
+
]
|
|
303
|
+
|
|
304
|
+
const state = getExistingStateFromStorage(undefined, plugins)
|
|
305
|
+
// Single plugin should be activated regardless of defaultOpen flag
|
|
306
|
+
expect(state.state.activePlugins).toEqual(['only-plugin'])
|
|
307
|
+
})
|
|
308
|
+
|
|
309
|
+
it('should merge config settings into the returned state', () => {
|
|
310
|
+
const plugins: Array<TanStackDevtoolsPlugin> = [
|
|
311
|
+
{
|
|
312
|
+
id: 'plugin1',
|
|
313
|
+
render: () => {},
|
|
314
|
+
name: 'Plugin 1',
|
|
315
|
+
},
|
|
316
|
+
]
|
|
317
|
+
|
|
318
|
+
const config = {
|
|
319
|
+
theme: 'light' as const,
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
const state = getExistingStateFromStorage(config as any, plugins)
|
|
323
|
+
expect(state.settings.theme).toBe('light')
|
|
324
|
+
expect(state.state.activePlugins).toEqual(['plugin1'])
|
|
325
|
+
})
|
|
59
326
|
})
|