nextjs-ide-helper 1.0.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/README.md ADDED
@@ -0,0 +1,167 @@
1
+ # NextJS Cursor Helper
2
+
3
+ A Next.js plugin that automatically adds cursor buttons to React components in development mode, enabling seamless IDE integration and faster development workflow.
4
+
5
+ ## Features
6
+
7
+ - 🚀 **Automatic Integration**: Automatically wraps React components with cursor buttons
8
+ - 🎯 **Smart Detection**: Only processes components in specified directories
9
+ - 🔧 **Zero Configuration**: Works out of the box with sensible defaults
10
+ - 🏎️ **Development Only**: Only active in development mode, no production overhead
11
+ - 🎨 **Non-intrusive**: Uses absolute positioning to avoid layout disruption
12
+ - ⚡ **Hydration Safe**: No SSR/client hydration mismatches
13
+ - 📱 **TypeScript Support**: Full TypeScript definitions included
14
+
15
+ ## Installation
16
+
17
+ ```bash
18
+ npm install nextjs-cursor-helper --save-dev
19
+ # or
20
+ yarn add nextjs-cursor-helper --dev
21
+ # or
22
+ pnpm add nextjs-cursor-helper --save-dev
23
+ ```
24
+
25
+ ## Quick Start
26
+
27
+ ### 1. Configure Next.js
28
+
29
+ Add the plugin to your `next.config.js`:
30
+
31
+ ```javascript
32
+ const withCursorHelper = require('nextjs-cursor-helper');
33
+
34
+ /** @type {import('next').NextConfig} */
35
+ const nextConfig = {
36
+ // your existing config
37
+ };
38
+
39
+ module.exports = withCursorHelper()(nextConfig);
40
+ ```
41
+
42
+ ### 2. That's it!
43
+
44
+ All React components in your `src/components` directory will automatically get cursor buttons in development mode.
45
+
46
+ ## Configuration
47
+
48
+ You can customize the plugin behavior:
49
+
50
+ ```javascript
51
+ const withCursorHelper = require('nextjs-cursor-helper');
52
+
53
+ const nextConfig = {
54
+ // your existing config
55
+ };
56
+
57
+ module.exports = withCursorHelper({
58
+ componentPaths: ['src/components', 'components', 'src/ui'], // directories to scan
59
+ projectRoot: process.cwd(), // project root directory
60
+ importPath: 'nextjs-cursor-helper/withCursorButton', // import path for the HOC
61
+ enabled: process.env.NODE_ENV === 'development' // enable/disable
62
+ })(nextConfig);
63
+ ```
64
+
65
+ ### Configuration Options
66
+
67
+ | Option | Type | Default | Description |
68
+ |--------|------|---------|-------------|
69
+ | `componentPaths` | `string[]` | `['src/components']` | Directories to scan for React components |
70
+ | `projectRoot` | `string` | `process.cwd()` | Root directory of your project |
71
+ | `importPath` | `string` | `'nextjs-cursor-helper/withCursorButton'` | Import path for the withCursorButton HOC |
72
+ | `enabled` | `boolean` | `process.env.NODE_ENV === 'development'` | Enable/disable the plugin |
73
+
74
+ ## Manual Usage
75
+
76
+ You can also manually wrap components:
77
+
78
+ ```tsx
79
+ import { withCursorButton } from 'nextjs-cursor-helper';
80
+
81
+ const MyComponent = () => {
82
+ return <div>Hello World</div>;
83
+ };
84
+
85
+ export default withCursorButton(MyComponent, 'src/components/MyComponent.tsx');
86
+ ```
87
+
88
+ ## How It Works
89
+
90
+ 1. **Webpack Loader**: The plugin uses a custom webpack loader to transform your React components at build time
91
+ 2. **Automatic Detection**: It scans specified directories for `.tsx` and `.jsx` files
92
+ 3. **Smart Wrapping**: Only wraps components that export a default React component
93
+ 4. **Development Only**: The cursor buttons only appear in development mode
94
+ 5. **Hydration Safe**: Uses client-side state to prevent SSR/hydration mismatches
95
+
96
+ ## Example
97
+
98
+ Before (your original component):
99
+ ```tsx
100
+ // src/components/Button.tsx
101
+ import React from 'react';
102
+
103
+ const Button = ({ children, onClick }) => {
104
+ return (
105
+ <button onClick={onClick}>
106
+ {children}
107
+ </button>
108
+ );
109
+ };
110
+
111
+ export default Button;
112
+ ```
113
+
114
+ After (automatically transformed):
115
+ ```tsx
116
+ // src/components/Button.tsx (transformed by the plugin)
117
+ import React from 'react';
118
+ import { withCursorButton } from 'nextjs-cursor-helper/withCursorButton';
119
+
120
+ const Button = ({ children, onClick }) => {
121
+ return (
122
+ <button onClick={onClick}>
123
+ {children}
124
+ </button>
125
+ );
126
+ };
127
+
128
+ export default withCursorButton(Button, 'src/components/Button.tsx', { projectRoot: '/path/to/project' });
129
+ ```
130
+
131
+ ## Troubleshooting
132
+
133
+ ### Components not getting wrapped
134
+
135
+ 1. Check that your components are in the specified `componentPaths`
136
+ 2. Ensure your components export a default export with a capitalized name
137
+ 3. Verify you're in development mode (`NODE_ENV=development`)
138
+
139
+ ### Hydration errors
140
+
141
+ The plugin is designed to prevent hydration errors, but if you encounter any:
142
+
143
+ 1. Make sure you're using the latest version
144
+ 2. Check that the 'use client' directive is present in the withCursorButton module
145
+ 3. File an issue with details about your setup
146
+
147
+ ### Cursor not opening files
148
+
149
+ 1. Ensure you have Cursor IDE installed
150
+ 2. Check that the file paths are correct
151
+ 3. Verify your browser allows `cursor://` protocol links
152
+
153
+ ## Contributing
154
+
155
+ Contributions are welcome! Please feel free to submit a Pull Request.
156
+
157
+ ## License
158
+
159
+ MIT
160
+
161
+ ## Changelog
162
+
163
+ ### 1.0.0
164
+ - Initial release
165
+ - Automatic component wrapping
166
+ - TypeScript support
167
+ - Hydration-safe implementation
package/lib/index.d.ts ADDED
@@ -0,0 +1,5 @@
1
+ export { withCursorButton } from './withCursorButton';
2
+ export type { WithCursorButtonOptions } from './withCursorButton';
3
+ declare const withCursorHelper: any;
4
+ export default withCursorHelper;
5
+ export { withCursorHelper };
package/lib/index.js ADDED
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.withCursorHelper = exports.withCursorButton = void 0;
4
+ // Main exports
5
+ var withCursorButton_1 = require("./withCursorButton");
6
+ Object.defineProperty(exports, "withCursorButton", { enumerable: true, get: function () { return withCursorButton_1.withCursorButton; } });
7
+ // Next.js plugin
8
+ const withCursorHelper = require('./plugin');
9
+ exports.withCursorHelper = withCursorHelper;
10
+ exports.default = withCursorHelper;
package/lib/loader.js ADDED
@@ -0,0 +1,79 @@
1
+ const path = require('path');
2
+
3
+ /**
4
+ * Webpack loader that automatically wraps React components with cursor buttons
5
+ * @param {string} source - The source code of the file
6
+ * @returns {string} - The transformed source code
7
+ */
8
+ module.exports = function cursorButtonLoader(source) {
9
+ const filename = this.resourcePath;
10
+ const options = this.getOptions() || {};
11
+
12
+ const {
13
+ componentPaths = ['src/components'],
14
+ projectRoot = process.cwd(),
15
+ importPath = 'nextjs-cursor-helper',
16
+ enabled = process.env.NODE_ENV === 'development'
17
+ } = options;
18
+
19
+ // Only process if enabled
20
+ if (!enabled) {
21
+ return source;
22
+ }
23
+
24
+ // Only process files in specified component directories
25
+ const shouldProcess = componentPaths.some(componentPath =>
26
+ filename.includes(path.resolve(projectRoot, componentPath))
27
+ );
28
+
29
+ if (!shouldProcess || (!filename.endsWith('.tsx') && !filename.endsWith('.jsx'))) {
30
+ return source;
31
+ }
32
+
33
+ // Check if it's already wrapped
34
+ if (source.includes('withCursorButton')) {
35
+ return source;
36
+ }
37
+
38
+ // Check if it has a default export that looks like a React component
39
+ const defaultExportMatch = source.match(/export\s+default\s+(\w+)/);
40
+ if (!defaultExportMatch) {
41
+ return source;
42
+ }
43
+
44
+ const componentName = defaultExportMatch[1];
45
+
46
+ // Check if component name starts with uppercase (React component convention)
47
+ if (!componentName || componentName[0] !== componentName[0].toUpperCase()) {
48
+ return source;
49
+ }
50
+
51
+ // Get relative path from project root
52
+ const relativePath = path.relative(projectRoot, filename);
53
+
54
+ // Find the last import to insert our import after it
55
+ const imports = source.match(/^import.*$/gm) || [];
56
+
57
+ let modifiedSource;
58
+
59
+ if (imports.length === 0) {
60
+ // No imports found, add at top
61
+ const importStatement = `import { withCursorButton } from '${importPath}';\n`;
62
+ const wrappedExport = `export default withCursorButton(${componentName}, '${relativePath}', { projectRoot: '${projectRoot}' });`;
63
+ modifiedSource = importStatement + source.replace(/export\s+default\s+\w+;?/, wrappedExport);
64
+ } else {
65
+ const lastImportIndex = source.lastIndexOf(imports[imports.length - 1]) + imports[imports.length - 1].length;
66
+
67
+ // Add import statement
68
+ const importStatement = `\nimport { withCursorButton } from '${importPath}';`;
69
+
70
+ // Replace the export
71
+ const wrappedExport = `export default withCursorButton(${componentName}, '${relativePath}', { projectRoot: '${projectRoot}' });`;
72
+
73
+ modifiedSource = source.slice(0, lastImportIndex) +
74
+ importStatement +
75
+ source.slice(lastImportIndex).replace(/export\s+default\s+\w+;?/, wrappedExport);
76
+ }
77
+
78
+ return modifiedSource;
79
+ };
package/lib/plugin.js ADDED
@@ -0,0 +1,61 @@
1
+ const path = require('path');
2
+
3
+ /**
4
+ * Configuration options for the NextJS Cursor Helper plugin
5
+ * @typedef {Object} CursorHelperOptions
6
+ * @property {string[]} [componentPaths=['src/components']] - Paths to scan for React components
7
+ * @property {string} [projectRoot=process.cwd()] - Root directory of the project
8
+ * @property {string} [importPath='nextjs-cursor-helper/withCursorButton'] - Import path for withCursorButton
9
+ * @property {boolean} [enabled=process.env.NODE_ENV === 'development'] - Enable/disable the plugin
10
+ */
11
+
12
+ /**
13
+ * NextJS Cursor Helper plugin
14
+ * @param {CursorHelperOptions} options - Configuration options
15
+ * @returns {function} Next.js config modifier function
16
+ */
17
+ function withCursorHelper(options = {}) {
18
+ const defaultOptions = {
19
+ componentPaths: ['src/components'],
20
+ projectRoot: process.cwd(),
21
+ importPath: 'nextjs-cursor-helper/withCursorButton',
22
+ enabled: process.env.NODE_ENV === 'development'
23
+ };
24
+
25
+ const config = { ...defaultOptions, ...options };
26
+
27
+ return (nextConfig = {}) => {
28
+ return {
29
+ ...nextConfig,
30
+ webpack: (webpackConfig, context) => {
31
+ const { dev, isServer } = context;
32
+
33
+ // Only apply in development and for client-side
34
+ if (config.enabled && dev && !isServer) {
35
+ const rule = {
36
+ test: /\.tsx?$/,
37
+ include: config.componentPaths.map(p => path.resolve(config.projectRoot, p)),
38
+ use: [
39
+ {
40
+ loader: require.resolve('./loader'),
41
+ options: config
42
+ }
43
+ ],
44
+ enforce: 'pre'
45
+ };
46
+
47
+ webpackConfig.module.rules.unshift(rule);
48
+ }
49
+
50
+ // Call the existing webpack function if it exists
51
+ if (typeof nextConfig.webpack === 'function') {
52
+ return nextConfig.webpack(webpackConfig, context);
53
+ }
54
+
55
+ return webpackConfig;
56
+ }
57
+ };
58
+ };
59
+ }
60
+
61
+ module.exports = withCursorHelper;
@@ -0,0 +1,6 @@
1
+ import React from 'react';
2
+ export interface WithCursorButtonOptions {
3
+ projectRoot?: string;
4
+ enabled?: boolean;
5
+ }
6
+ export declare function withCursorButton<T extends object>(WrappedComponent: React.ComponentType<T>, filePath: string, options?: WithCursorButtonOptions): React.FC<T>;
@@ -0,0 +1,44 @@
1
+ "use strict";
2
+ 'use client';
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.withCursorButton = withCursorButton;
5
+ const jsx_runtime_1 = require("react/jsx-runtime");
6
+ const react_1 = require("react");
7
+ const CursorButton = ({ filePath, projectRoot }) => {
8
+ const handleClick = () => {
9
+ const fullPath = projectRoot ? `${projectRoot}/${filePath}` : filePath;
10
+ window.open(`cursor://file${fullPath}`, '_blank');
11
+ };
12
+ return ((0, jsx_runtime_1.jsx)("button", { onClick: handleClick, style: {
13
+ position: 'absolute',
14
+ top: '8px',
15
+ right: '8px',
16
+ background: '#007acc',
17
+ color: 'white',
18
+ border: 'none',
19
+ borderRadius: '4px',
20
+ padding: '4px 8px',
21
+ fontSize: '12px',
22
+ cursor: 'pointer',
23
+ zIndex: 1000,
24
+ opacity: 0.7,
25
+ transition: 'opacity 0.2s',
26
+ fontFamily: 'monospace'
27
+ }, onMouseEnter: (e) => (e.currentTarget.style.opacity = '1'), onMouseLeave: (e) => (e.currentTarget.style.opacity = '0.7'), title: `Open ${filePath} in Cursor`, children: "\uD83D\uDCDD" }));
28
+ };
29
+ function withCursorButton(WrappedComponent, filePath, options = {}) {
30
+ const { projectRoot, enabled = process.env.NODE_ENV === 'development' } = options;
31
+ const WithCursorButtonComponent = (props) => {
32
+ const [isClient, setIsClient] = (0, react_1.useState)(false);
33
+ (0, react_1.useEffect)(() => {
34
+ setIsClient(true);
35
+ }, []);
36
+ // In production or when disabled, just return the component without wrapper
37
+ if (!enabled || !isClient) {
38
+ return (0, jsx_runtime_1.jsx)(WrappedComponent, { ...props });
39
+ }
40
+ return ((0, jsx_runtime_1.jsxs)("div", { style: { position: 'relative' }, children: [(0, jsx_runtime_1.jsx)(CursorButton, { filePath: filePath, projectRoot: projectRoot }), (0, jsx_runtime_1.jsx)(WrappedComponent, { ...props })] }));
41
+ };
42
+ WithCursorButtonComponent.displayName = `withCursorButton(${WrappedComponent.displayName || WrappedComponent.name})`;
43
+ return WithCursorButtonComponent;
44
+ }
package/package.json ADDED
@@ -0,0 +1,45 @@
1
+ {
2
+ "name": "nextjs-ide-helper",
3
+ "version": "1.0.0",
4
+ "description": "A Next.js plugin that automatically adds cursor buttons to React components for seamless IDE integration",
5
+ "main": "lib/index.js",
6
+ "types": "lib/index.d.ts",
7
+ "files": [
8
+ "lib",
9
+ "src"
10
+ ],
11
+ "scripts": {
12
+ "build": "tsc",
13
+ "prepare": "npm run build",
14
+ "test": "echo \"Error: no test specified\" && exit 1"
15
+ },
16
+ "keywords": [
17
+ "nextjs",
18
+ "react",
19
+ "cursor",
20
+ "ide",
21
+ "development",
22
+ "webpack",
23
+ "plugin",
24
+ "components"
25
+ ],
26
+ "author": "Your Name",
27
+ "license": "MIT",
28
+ "peerDependencies": {
29
+ "next": ">=13.0.0",
30
+ "react": ">=18.0.0"
31
+ },
32
+ "devDependencies": {
33
+ "@types/node": "^20.19.9",
34
+ "@types/react": "^18.3.23",
35
+ "typescript": "^5.8.3"
36
+ },
37
+ "repository": {
38
+ "type": "git",
39
+ "url": "https://github.com/yourusername/nextjs-cursor-helper.git"
40
+ },
41
+ "bugs": {
42
+ "url": "https://github.com/yourusername/nextjs-cursor-helper/issues"
43
+ },
44
+ "homepage": "https://github.com/yourusername/nextjs-cursor-helper#readme"
45
+ }
package/src/index.ts ADDED
@@ -0,0 +1,8 @@
1
+ // Main exports
2
+ export { withCursorButton } from './withCursorButton';
3
+ export type { WithCursorButtonOptions } from './withCursorButton';
4
+
5
+ // Next.js plugin
6
+ const withCursorHelper = require('./plugin');
7
+ export default withCursorHelper;
8
+ export { withCursorHelper };
package/src/loader.js ADDED
@@ -0,0 +1,79 @@
1
+ const path = require('path');
2
+
3
+ /**
4
+ * Webpack loader that automatically wraps React components with cursor buttons
5
+ * @param {string} source - The source code of the file
6
+ * @returns {string} - The transformed source code
7
+ */
8
+ module.exports = function cursorButtonLoader(source) {
9
+ const filename = this.resourcePath;
10
+ const options = this.getOptions() || {};
11
+
12
+ const {
13
+ componentPaths = ['src/components'],
14
+ projectRoot = process.cwd(),
15
+ importPath = 'nextjs-cursor-helper/withCursorButton',
16
+ enabled = process.env.NODE_ENV === 'development'
17
+ } = options;
18
+
19
+ // Only process if enabled
20
+ if (!enabled) {
21
+ return source;
22
+ }
23
+
24
+ // Only process files in specified component directories
25
+ const shouldProcess = componentPaths.some(componentPath =>
26
+ filename.includes(path.resolve(projectRoot, componentPath))
27
+ );
28
+
29
+ if (!shouldProcess || (!filename.endsWith('.tsx') && !filename.endsWith('.jsx'))) {
30
+ return source;
31
+ }
32
+
33
+ // Check if it's already wrapped
34
+ if (source.includes('withCursorButton')) {
35
+ return source;
36
+ }
37
+
38
+ // Check if it has a default export that looks like a React component
39
+ const defaultExportMatch = source.match(/export\s+default\s+(\w+)/);
40
+ if (!defaultExportMatch) {
41
+ return source;
42
+ }
43
+
44
+ const componentName = defaultExportMatch[1];
45
+
46
+ // Check if component name starts with uppercase (React component convention)
47
+ if (!componentName || componentName[0] !== componentName[0].toUpperCase()) {
48
+ return source;
49
+ }
50
+
51
+ // Get relative path from project root
52
+ const relativePath = path.relative(projectRoot, filename);
53
+
54
+ // Find the last import to insert our import after it
55
+ const imports = source.match(/^import.*$/gm) || [];
56
+
57
+ let modifiedSource;
58
+
59
+ if (imports.length === 0) {
60
+ // No imports found, add at top
61
+ const importStatement = `import { withCursorButton } from '${importPath}';\n`;
62
+ const wrappedExport = `export default withCursorButton(${componentName}, '${relativePath}', { projectRoot: '${projectRoot}' });`;
63
+ modifiedSource = importStatement + source.replace(/export\s+default\s+\w+;?/, wrappedExport);
64
+ } else {
65
+ const lastImportIndex = source.lastIndexOf(imports[imports.length - 1]) + imports[imports.length - 1].length;
66
+
67
+ // Add import statement
68
+ const importStatement = `\nimport { withCursorButton } from '${importPath}';`;
69
+
70
+ // Replace the export
71
+ const wrappedExport = `export default withCursorButton(${componentName}, '${relativePath}', { projectRoot: '${projectRoot}' });`;
72
+
73
+ modifiedSource = source.slice(0, lastImportIndex) +
74
+ importStatement +
75
+ source.slice(lastImportIndex).replace(/export\s+default\s+\w+;?/, wrappedExport);
76
+ }
77
+
78
+ return modifiedSource;
79
+ };
package/src/plugin.js ADDED
@@ -0,0 +1,61 @@
1
+ const path = require('path');
2
+
3
+ /**
4
+ * Configuration options for the NextJS Cursor Helper plugin
5
+ * @typedef {Object} CursorHelperOptions
6
+ * @property {string[]} [componentPaths=['src/components']] - Paths to scan for React components
7
+ * @property {string} [projectRoot=process.cwd()] - Root directory of the project
8
+ * @property {string} [importPath='nextjs-cursor-helper/withCursorButton'] - Import path for withCursorButton
9
+ * @property {boolean} [enabled=process.env.NODE_ENV === 'development'] - Enable/disable the plugin
10
+ */
11
+
12
+ /**
13
+ * NextJS Cursor Helper plugin
14
+ * @param {CursorHelperOptions} options - Configuration options
15
+ * @returns {function} Next.js config modifier function
16
+ */
17
+ function withCursorHelper(options = {}) {
18
+ const defaultOptions = {
19
+ componentPaths: ['src/components'],
20
+ projectRoot: process.cwd(),
21
+ importPath: 'nextjs-cursor-helper/withCursorButton',
22
+ enabled: process.env.NODE_ENV === 'development'
23
+ };
24
+
25
+ const config = { ...defaultOptions, ...options };
26
+
27
+ return (nextConfig = {}) => {
28
+ return {
29
+ ...nextConfig,
30
+ webpack: (webpackConfig, context) => {
31
+ const { dev, isServer } = context;
32
+
33
+ // Only apply in development and for client-side
34
+ if (config.enabled && dev && !isServer) {
35
+ const rule = {
36
+ test: /\.tsx?$/,
37
+ include: config.componentPaths.map(p => path.resolve(config.projectRoot, p)),
38
+ use: [
39
+ {
40
+ loader: require.resolve('./loader.js'),
41
+ options: config
42
+ }
43
+ ],
44
+ enforce: 'pre'
45
+ };
46
+
47
+ webpackConfig.module.rules.unshift(rule);
48
+ }
49
+
50
+ // Call the existing webpack function if it exists
51
+ if (typeof nextConfig.webpack === 'function') {
52
+ return nextConfig.webpack(webpackConfig, context);
53
+ }
54
+
55
+ return webpackConfig;
56
+ }
57
+ };
58
+ };
59
+ }
60
+
61
+ module.exports = withCursorHelper;
@@ -0,0 +1,78 @@
1
+ 'use client';
2
+ import React, { useState, useEffect } from 'react';
3
+
4
+ interface CursorButtonProps {
5
+ filePath: string;
6
+ projectRoot?: string;
7
+ }
8
+
9
+ const CursorButton: React.FC<CursorButtonProps> = ({ filePath, projectRoot }) => {
10
+ const handleClick = () => {
11
+ const fullPath = projectRoot ? `${projectRoot}/${filePath}` : filePath;
12
+ window.open(`cursor://file${fullPath}`, '_blank');
13
+ };
14
+
15
+ return (
16
+ <button
17
+ onClick={handleClick}
18
+ style={{
19
+ position: 'absolute',
20
+ top: '8px',
21
+ right: '8px',
22
+ background: '#007acc',
23
+ color: 'white',
24
+ border: 'none',
25
+ borderRadius: '4px',
26
+ padding: '4px 8px',
27
+ fontSize: '12px',
28
+ cursor: 'pointer',
29
+ zIndex: 1000,
30
+ opacity: 0.7,
31
+ transition: 'opacity 0.2s',
32
+ fontFamily: 'monospace'
33
+ }}
34
+ onMouseEnter={(e) => (e.currentTarget.style.opacity = '1')}
35
+ onMouseLeave={(e) => (e.currentTarget.style.opacity = '0.7')}
36
+ title={`Open ${filePath} in Cursor`}
37
+ >
38
+ 📝
39
+ </button>
40
+ );
41
+ };
42
+
43
+ export interface WithCursorButtonOptions {
44
+ projectRoot?: string;
45
+ enabled?: boolean;
46
+ }
47
+
48
+ export function withCursorButton<T extends object>(
49
+ WrappedComponent: React.ComponentType<T>,
50
+ filePath: string,
51
+ options: WithCursorButtonOptions = {}
52
+ ) {
53
+ const { projectRoot, enabled = process.env.NODE_ENV === 'development' } = options;
54
+
55
+ const WithCursorButtonComponent: React.FC<T> = (props) => {
56
+ const [isClient, setIsClient] = useState(false);
57
+
58
+ useEffect(() => {
59
+ setIsClient(true);
60
+ }, []);
61
+
62
+ // In production or when disabled, just return the component without wrapper
63
+ if (!enabled || !isClient) {
64
+ return <WrappedComponent {...props} />;
65
+ }
66
+
67
+ return (
68
+ <div style={{ position: 'relative' }}>
69
+ <CursorButton filePath={filePath} projectRoot={projectRoot} />
70
+ <WrappedComponent {...props} />
71
+ </div>
72
+ );
73
+ };
74
+
75
+ WithCursorButtonComponent.displayName = `withCursorButton(${WrappedComponent.displayName || WrappedComponent.name})`;
76
+
77
+ return WithCursorButtonComponent;
78
+ }