Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/rude-tomatoes-deny.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@qwik.dev/devtools': patch
---

chore: update Tailwind CSS packages and improve theme toggle functionality in UI
15 changes: 11 additions & 4 deletions packages/devtools/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,12 @@
},
"./ui": {
"import": "./dist/ui/index.qwik.mjs",
"require": "./dist/ui/index.qwik.cjs",
"types": "./dist/ui/lib-types/ui/src/index.d.ts"
"types": "./dist/ui/lib-types/ui/src/index.d.ts",
"style": "./dist/ui/style.css"

},
"./style": {
"import": "./dist/ui/theme.css"
}
},
"files": [
Expand All @@ -23,7 +27,10 @@
"peerDependencies": {
"@qwik.dev/core": "2.0.0-beta.11",
"@qwik.dev/router": "2.0.0-beta.11",
"vite": "7.1.3"
"vite": "7.1.3",
"@tailwindcss/postcss": "^4.1.14",
"@tailwindcss/vite": "^4.1.14",
"tailwindcss": "^4.1.14"
},
"dependencies": {
"@qwikest/icons": "^0.0.13",
Expand Down Expand Up @@ -59,4 +66,4 @@
"url": "https://github.com/QwikDev/devtools/issues"
},
"homepage": "https://github.com/QwikDev/devtools#readme"
}
}
2 changes: 1 addition & 1 deletion packages/playgrounds/src/root.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
} from '@qwik.dev/router';
import { RouterHead } from './components/router-head/router-head';
import './global.css';

import '../../devtools/dist/ui/style.css';
export default component$(() => {
/**
* The root of a QwikRouter site always start with the <QwikRouterProvider> component,
Expand Down
43 changes: 36 additions & 7 deletions packages/plugin/src/npm/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,35 @@ function setCachedPackage(name: string, data: any): void {
});
}

async function findNearestFileUp(startDir: string, fileName: string): Promise<string | null> {
try {
let currentDir = path.resolve(startDir);
// Guard against infinite loops by capping directory ascents
for (let i = 0; i < 100; i++) {
const candidate = path.join(currentDir, fileName);
const exists = await fsp
.access(candidate)
.then(() => true)
.catch(() => false);
if (exists) return candidate;

const parent = path.dirname(currentDir);
if (parent === currentDir) break;
currentDir = parent;
}
return null;
} catch {
return null;
}
}

function getProjectStartDirFromConfig(config: any): string {
// Prefer Vite's resolved root; fallback to the directory of the config file; finally cwd
if (config?.root) return config.root;
if (config?.configFile) return path.dirname(config.configFile);
return process.cwd();
}

export async function detectPackageManager(
projectRoot: string,
): Promise<'npm' | 'pnpm' | 'yarn'> {
Expand Down Expand Up @@ -84,9 +113,8 @@ const preloadDependencies = async (config: any): Promise<any[]> => {
console.log('[Qwik DevTools] Starting to preload dependencies...');

preloadPromise = (async () => {
const pathToPackageJson = config.configFileDependencies.find(
(file: string) => file.endsWith('package.json'),
);
const startDir = getProjectStartDirFromConfig(config);
const pathToPackageJson = await findNearestFileUp(startDir, 'package.json');

if (!pathToPackageJson) {
preloadedDependencies = [];
Expand Down Expand Up @@ -259,9 +287,8 @@ export async function startPreloading({ config }: { config: any }) {
export function getNpmFunctions({ config }: ServerContext) {
return {
async getQwikPackages(): Promise<NpmInfo> {
const pathToPackageJson = config.configFileDependencies.find(
(file: string) => file.endsWith('package.json'),
);
const startDir = getProjectStartDirFromConfig(config);
const pathToPackageJson = await findNearestFileUp(startDir, 'package.json');
if (!pathToPackageJson) return [];

try {
Expand Down Expand Up @@ -298,7 +325,9 @@ export function getNpmFunctions({ config }: ServerContext) {
isDev = true,
): Promise<{ success: boolean; error?: string }> {
try {
const projectRoot = path.dirname(config.configFileDependencies[0]);
const startDir = getProjectStartDirFromConfig(config);
const pathToPackageJson = await findNearestFileUp(startDir, 'package.json');
const projectRoot = pathToPackageJson ? path.dirname(pathToPackageJson) : startDir;
const pm = await detectPackageManager(projectRoot);
const devFlag = isDev ? (pm === 'npm' ? '--save-dev' : '-D') : '';

Expand Down
8 changes: 4 additions & 4 deletions packages/ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,14 @@
},
"peerDependencies": {
"@devtools/plugin": "workspace:*",
"@qwik.dev/core": "2.0.0-beta.11"
"@qwik.dev/core": "2.0.0-beta.11",
"@tailwindcss/postcss": "^4.1.14",
"@tailwindcss/vite": "^4.1.14",
"tailwindcss": "^4.1.14"
},
"devDependencies": {
"@devtools/kit": "workspace:*",
"@qwikest/icons": "^0.0.13",
"@tailwindcss/postcss": "^4.0.0",
"@tailwindcss/vite": "^4.0.0",
"@types/eslint": "8.56.10",
"@types/node": "20.14.11",
"@types/react": "^18.2.28",
Expand All @@ -63,7 +64,6 @@
"react-dom": "18.2.0",
"shiki": "^3.8.1",
"superjson": "^2.2.2",
"tailwindcss": "^4.0.0",
"typescript": "5.4.5",
"vite": "7.1.3",
"vite-hot-client": "2.0.4",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@ import { component$, Slot } from '@qwik.dev/core';

export const DevtoolsContainer = component$(() => {
return (
<div class="fixed bottom-0 right-0 z-[9999] font-sans" q:slot="content">
<Slot />
<div class="qwik-devtools">
<div class="fixed bottom-0 right-0 z-[9999] font-sans" q:slot="content">
<Slot />
</div>
</div>
);
});
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export const setTheme = (theme: ThemeName) => {
localStorage.setItem(themeStorageKey, theme);
};

export const ThemeToggle = component$(() => {
export const QwikThemeToggle = component$(() => {
useStyles$(themeTogglecss);
const onClick$ = event$(() => {
let currentTheme = getTheme();
Expand Down
135 changes: 83 additions & 52 deletions packages/ui/src/devtools.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ import { DevtoolsContainer } from './components/DevtoolsContainer/DevtoolsContai
import { DevtoolsPanel } from './components/DevtoolsPanel/DevtoolsPanel';
import { Packages } from './features/Packages/Packages';
import { Inspect } from './features/inspect/Inspect';
import { ThemeToggle } from './components/ThemeToggle/ThemeToggle';
import { QwikThemeToggle } from './components/ThemeToggle/QwikThemeToggle';
import { ThemeScript as QwikThemeScript } from './components/ThemeToggle/theme-script';
import { CodeBreack } from './features/CodeBreack/CodeBreack';
function getClientRpcFunctions() {
Expand All @@ -60,66 +60,97 @@ export const QwikDevtools = component$(() => {
isLoadingDependencies: false,
});

useVisibleTask$(async ({ track }) => {
const hot = await tryCreateHotContext(undefined, ['/']);
const clientReady = useSignal(false);

useVisibleTask$(async () => {
const hot = await tryCreateHotContext(undefined, ['/']);
if (!hot) {
throw new Error('Vite Hot Context not connected');
}

setViteClientContext(hot);
createClientRpc(getClientRpcFunctions());
clientReady.value = true;
});

// Start loading data immediately in background
// Dependencies are already being preloaded on the server side
useVisibleTask$(async ({ track }) => {
track(() => clientReady.value);
if (!clientReady.value) return;
const rpc = getViteClientRpc();
state.isLoadingDependencies = true;
try {
const assets = await rpc.getAssetsFromPublicDir();
state.assets = assets;
} catch (error) {
console.error('Failed to load assets:', error);
}
});

// Preload all data in parallel immediately
Promise.all([
rpc.getAssetsFromPublicDir(),
rpc.getComponents(),
rpc.getRoutes(),
rpc.getQwikPackages(),
rpc.getAllDependencies(), // This returns server-preloaded data instantly
])
.then(([assets, components, routes, qwikPackages, allDeps]) => {
state.assets = assets;
state.components = components;

const children: RoutesInfo[] = routes?.children || [];
const directories: RoutesInfo[] = children.filter(
(child) => child.type === 'directory',
);

const values: RoutesInfo[] = [
{
relativePath: '',
name: 'index',
type: RouteType.DIRECTORY,
path: '',
isSymbolicLink: false,
children: undefined,
},
...directories,
];

state.routes = noSerialize(values);
state.npmPackages = qwikPackages;
state.allDependencies = allDeps;
state.isLoadingDependencies = false;
})
.catch((error) => {
console.error('Failed to load devtools data:', error);
state.isLoadingDependencies = false;
});

// Track devtools open state for other purposes if needed
track(() => {
if (state.isOpen.value) {
// Devtools is now open, data should already be loaded or loading
}
});
useVisibleTask$(async ({ track }) => {
track(() => clientReady.value);
if (!clientReady.value) return;
const rpc = getViteClientRpc();
try {
const components = await rpc.getComponents();
state.components = components;
} catch (error) {
console.error('Failed to load components:', error);
}
});

useVisibleTask$(async ({ track }) => {
track(() => clientReady.value);
if (!clientReady.value) return;
const rpc = getViteClientRpc();
try {
const routes = await rpc.getRoutes();
const children: RoutesInfo[] = routes?.children || [];
const directories: RoutesInfo[] = children.filter(
(child) => child.type === 'directory',
);

const values: RoutesInfo[] = [
{
relativePath: '',
name: 'index',
type: RouteType.DIRECTORY,
path: '',
isSymbolicLink: false,
children: undefined,
},
...directories,
];

state.routes = noSerialize(values);
} catch (error) {
console.error('Failed to load routes:', error);
}
});

useVisibleTask$(async ({ track }) => {
track(() => clientReady.value);
if (!clientReady.value) return;
const rpc = getViteClientRpc();
try {
const qwikPackages = await rpc.getQwikPackages();
state.npmPackages = qwikPackages;
} catch (error) {
console.error('Failed to load Qwik packages:', error);
}
});

useVisibleTask$(async ({ track }) => {
track(() => clientReady.value);
if (!clientReady.value) return;
const rpc = getViteClientRpc();
state.isLoadingDependencies = true;
try {
const allDeps = await rpc.getAllDependencies();
state.allDependencies = allDeps;
} catch (error) {
console.error('Failed to load all dependencies:', error);
} finally {
state.isLoadingDependencies = false;
}
});

return (
Expand Down Expand Up @@ -153,7 +184,7 @@ export const QwikDevtools = component$(() => {
< HiCodeBracketSolid class="h-5 w-5" />
</Tab>
<div class="mt-auto">
<ThemeToggle />
<QwikThemeToggle />
</div>
</div>

Expand Down
Loading
Loading