vista-core-js 0.0.2
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/ErrorOverlay.d.ts +7 -0
- package/dist/ErrorOverlay.js +68 -0
- package/dist/app.d.ts +21 -0
- package/dist/app.js +119 -0
- package/dist/client/link.d.ts +23 -0
- package/dist/client/link.js +42 -0
- package/dist/client.d.ts +2 -0
- package/dist/client.js +290 -0
- package/dist/components/PixelBlast.d.ts +28 -0
- package/dist/components/PixelBlast.js +584 -0
- package/dist/entry-client.d.ts +1 -0
- package/dist/entry-client.js +56 -0
- package/dist/entry-server.d.ts +9 -0
- package/dist/entry-server.js +33 -0
- package/dist/error-overlay.d.ts +1 -0
- package/dist/error-overlay.js +166 -0
- package/dist/font/google/index.d.ts +1923 -0
- package/dist/font/google/index.js +1948 -0
- package/dist/image/get-img-props.d.ts +20 -0
- package/dist/image/get-img-props.js +46 -0
- package/dist/image/image-config.d.ts +20 -0
- package/dist/image/image-config.js +17 -0
- package/dist/image/image-loader.d.ts +7 -0
- package/dist/image/image-loader.js +10 -0
- package/dist/image/index.d.ts +12 -0
- package/dist/image/index.js +12 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +4 -0
- package/dist/plugin.d.ts +5 -0
- package/dist/plugin.js +88 -0
- package/dist/router.d.ts +18 -0
- package/dist/router.js +55 -0
- package/package.json +47 -0
- package/src/ErrorOverlay.tsx +194 -0
- package/src/app.tsx +138 -0
- package/src/assets/vista.gif +0 -0
- package/src/client/link.tsx +85 -0
- package/src/client.tsx +368 -0
- package/src/entry-client.tsx +70 -0
- package/src/entry-server.tsx +58 -0
- package/src/error-overlay.ts +187 -0
- package/src/font/google/index.d.ts +19011 -0
- package/src/font/google/index.ts +1968 -0
- package/src/font/types.d.ts +13 -0
- package/src/image/get-img-props.ts +100 -0
- package/src/image/image-config.ts +22 -0
- package/src/image/image-loader.ts +23 -0
- package/src/image/index.tsx +21 -0
- package/src/index.ts +7 -0
- package/src/plugin.ts +100 -0
- package/src/router-loader.ts +51 -0
- package/src/router.tsx +80 -0
- package/tsconfig.json +23 -0
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { ImageConfigComplete } from './image-config';
|
|
2
|
+
import { ImageLoader } from './image-loader';
|
|
3
|
+
export type ImageProps = React.ImgHTMLAttributes<HTMLImageElement> & {
|
|
4
|
+
src: string;
|
|
5
|
+
alt: string;
|
|
6
|
+
width?: number | string;
|
|
7
|
+
height?: number | string;
|
|
8
|
+
fill?: boolean;
|
|
9
|
+
loader?: ImageLoader;
|
|
10
|
+
quality?: number | string;
|
|
11
|
+
priority?: boolean;
|
|
12
|
+
unoptimized?: boolean;
|
|
13
|
+
};
|
|
14
|
+
type ImgProps = React.ImgHTMLAttributes<HTMLImageElement> & {
|
|
15
|
+
width?: number;
|
|
16
|
+
height?: number;
|
|
17
|
+
srcSet?: string;
|
|
18
|
+
};
|
|
19
|
+
export declare function getImgProps(props: ImageProps, config: ImageConfigComplete | undefined, defaultLoader: ImageLoader): ImgProps;
|
|
20
|
+
export {};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { imageConfigDefault } from './image-config';
|
|
2
|
+
// Helper: Generate srcSet
|
|
3
|
+
function generateSrcSet(src, width, loader, config, unoptimized) {
|
|
4
|
+
if (unoptimized)
|
|
5
|
+
return undefined;
|
|
6
|
+
const { deviceSizes, imageSizes } = config;
|
|
7
|
+
const sizes = [...deviceSizes, ...imageSizes].sort((a, b) => a - b);
|
|
8
|
+
// If width is known, only generate sizes up to that width (plus maybe 2x/3x for density)
|
|
9
|
+
// For simplicity MVP, we'll generate device sizes.
|
|
10
|
+
return sizes
|
|
11
|
+
.map((size) => {
|
|
12
|
+
const url = loader({ src, width: size });
|
|
13
|
+
return `${url} ${size}w`;
|
|
14
|
+
})
|
|
15
|
+
.join(', ');
|
|
16
|
+
}
|
|
17
|
+
export function getImgProps(props, config = imageConfigDefault, defaultLoader) {
|
|
18
|
+
const { src, alt, width, height, fill, loader = defaultLoader, quality, priority, unoptimized, style, sizes, className, loading, ...rest } = props;
|
|
19
|
+
const imgStyle = { ...style };
|
|
20
|
+
// Handle Fill Mode
|
|
21
|
+
if (fill) {
|
|
22
|
+
imgStyle.position = 'absolute';
|
|
23
|
+
imgStyle.height = '100%';
|
|
24
|
+
imgStyle.width = '100%';
|
|
25
|
+
imgStyle.inset = 0;
|
|
26
|
+
imgStyle.objectFit = 'cover'; // Default to cover for bg images
|
|
27
|
+
}
|
|
28
|
+
// Handle Dimensions
|
|
29
|
+
let widthInt = width ? Number(width) : undefined;
|
|
30
|
+
let heightInt = height ? Number(height) : undefined;
|
|
31
|
+
// Generate SrcSet
|
|
32
|
+
const srcSet = generateSrcSet(src, widthInt, loader, config, !!unoptimized);
|
|
33
|
+
return {
|
|
34
|
+
...rest,
|
|
35
|
+
src,
|
|
36
|
+
alt,
|
|
37
|
+
width: widthInt,
|
|
38
|
+
height: heightInt,
|
|
39
|
+
loading: priority ? 'eager' : (loading || 'lazy'),
|
|
40
|
+
// fetchPriority: priority ? 'high' : undefined, // React types might strict on this
|
|
41
|
+
style: imgStyle,
|
|
42
|
+
sizes: sizes || (fill ? '100vw' : undefined),
|
|
43
|
+
srcSet,
|
|
44
|
+
className,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export declare const imageConfigDefault: {
|
|
2
|
+
deviceSizes: number[];
|
|
3
|
+
imageSizes: number[];
|
|
4
|
+
path: string;
|
|
5
|
+
loader: "default";
|
|
6
|
+
loaderFile: string;
|
|
7
|
+
domains: never[];
|
|
8
|
+
disableStaticImages: boolean;
|
|
9
|
+
minimumCacheTTL: number;
|
|
10
|
+
formats: readonly ["image/webp"];
|
|
11
|
+
dangerouslyAllowSVG: boolean;
|
|
12
|
+
contentSecurityPolicy: string;
|
|
13
|
+
contentDispositionType: "inline";
|
|
14
|
+
remotePatterns: never[];
|
|
15
|
+
unoptimized: boolean;
|
|
16
|
+
};
|
|
17
|
+
export type ImageConfigComplete = typeof imageConfigDefault;
|
|
18
|
+
export type ImageConfig = Partial<ImageConfigComplete>;
|
|
19
|
+
export declare const VALID_LOADERS: readonly ["default", "imgix", "cloudinary", "akamai", "custom"];
|
|
20
|
+
export type LoaderValue = typeof VALID_LOADERS[number];
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export const imageConfigDefault = {
|
|
2
|
+
deviceSizes: [640, 750, 828, 1080, 1200, 1920, 2048, 3840],
|
|
3
|
+
imageSizes: [16, 32, 48, 64, 96, 128, 256, 384],
|
|
4
|
+
path: '/_next/image',
|
|
5
|
+
loader: 'default',
|
|
6
|
+
loaderFile: '',
|
|
7
|
+
domains: [],
|
|
8
|
+
disableStaticImages: false,
|
|
9
|
+
minimumCacheTTL: 60,
|
|
10
|
+
formats: ['image/webp'],
|
|
11
|
+
dangerouslyAllowSVG: false,
|
|
12
|
+
contentSecurityPolicy: "script-src 'none'; frame-src 'none'; sandbox;",
|
|
13
|
+
contentDispositionType: 'inline',
|
|
14
|
+
remotePatterns: [],
|
|
15
|
+
unoptimized: false,
|
|
16
|
+
};
|
|
17
|
+
export const VALID_LOADERS = ['default', 'imgix', 'cloudinary', 'akamai', 'custom'];
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// Default loader: simple pass-through for now,
|
|
2
|
+
// envisioning a future where this hits an optimization endpoint.
|
|
3
|
+
export const defaultLoader = ({ src, width, quality }) => {
|
|
4
|
+
// If user wants optimization, they would hook up a real loader here.
|
|
5
|
+
// For static dev, we just return the src.
|
|
6
|
+
// In a real Next.js app, this would be: `/_next/image?url=${encodeURIComponent(src)}&w=${width}&q=${quality || 75}`
|
|
7
|
+
// NOTE: Emulating optimizing behavior by appending query params for now (if backend supported it)
|
|
8
|
+
// return `${src}?w=${width}&q=${quality || 75}`;
|
|
9
|
+
return src;
|
|
10
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
export declare const Image: React.ForwardRefExoticComponent<React.ImgHTMLAttributes<HTMLImageElement> & {
|
|
3
|
+
src: string;
|
|
4
|
+
alt: string;
|
|
5
|
+
width?: number | string;
|
|
6
|
+
height?: number | string;
|
|
7
|
+
fill?: boolean;
|
|
8
|
+
loader?: import("./image-loader").ImageLoader;
|
|
9
|
+
quality?: number | string;
|
|
10
|
+
priority?: boolean;
|
|
11
|
+
unoptimized?: boolean;
|
|
12
|
+
} & React.RefAttributes<HTMLImageElement>>;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { forwardRef } from 'react';
|
|
3
|
+
import { getImgProps } from './get-img-props';
|
|
4
|
+
import { imageConfigDefault } from './image-config';
|
|
5
|
+
import { defaultLoader } from './image-loader';
|
|
6
|
+
export const Image = forwardRef((props, ref) => {
|
|
7
|
+
// In a real implementation, we might consume config from a Context Provider
|
|
8
|
+
// For now, we use the default config.
|
|
9
|
+
const imgProps = getImgProps(props, imageConfigDefault, defaultLoader);
|
|
10
|
+
return (_jsx("img", { ...imgProps, ref: ref }));
|
|
11
|
+
});
|
|
12
|
+
Image.displayName = 'Image';
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { useRouter, Link } from './router.js';
|
|
2
|
+
import { mount, mountError } from './client.js';
|
|
3
|
+
export { Image } from './image/index.js';
|
|
4
|
+
export type { ImageProps } from './image/get-img-props.js';
|
|
5
|
+
export type { ImageLoaderProps } from './image/image-loader.js';
|
|
6
|
+
export { useRouter, mount, mountError, Link };
|
package/dist/index.js
ADDED
package/dist/plugin.d.ts
ADDED
package/dist/plugin.js
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import path from 'path';
|
|
2
|
+
import { fileURLToPath } from 'url';
|
|
3
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
4
|
+
const clientPath = path.resolve(__dirname, './client.tsx'); // Resolves to src/client.tsx
|
|
5
|
+
export function vistaPlugin(options = {}) {
|
|
6
|
+
const logoPath = options.logo || '/favicon.ico';
|
|
7
|
+
return {
|
|
8
|
+
name: 'vista-core',
|
|
9
|
+
enforce: 'pre',
|
|
10
|
+
configureServer(server) {
|
|
11
|
+
if (server.config.appType === 'custom') {
|
|
12
|
+
return; // Skip SPA middleware for SSR
|
|
13
|
+
}
|
|
14
|
+
// Disable default Vite overlay
|
|
15
|
+
server.config.server.hmr = {
|
|
16
|
+
...(typeof server.config.server.hmr === 'object' ? server.config.server.hmr : {}),
|
|
17
|
+
overlay: false
|
|
18
|
+
};
|
|
19
|
+
server.middlewares.use(async (req, res, next) => {
|
|
20
|
+
const url = req.url || '/';
|
|
21
|
+
const pathname = url.split('?')[0];
|
|
22
|
+
// SPA Fallback logic:
|
|
23
|
+
// Serve index.html if request is:
|
|
24
|
+
// 1. Root (/)
|
|
25
|
+
// 2. Not an internal Vite request (starts with /@)
|
|
26
|
+
// 3. Not a file with an extension (check pathname, ignore query params)
|
|
27
|
+
const isFile = pathname.includes('.');
|
|
28
|
+
const isInternal = url.startsWith('/@');
|
|
29
|
+
if (url === '/' || (!isFile && !isInternal)) {
|
|
30
|
+
const html = `
|
|
31
|
+
<!DOCTYPE html>
|
|
32
|
+
<html lang="en">
|
|
33
|
+
<head>
|
|
34
|
+
<meta charset="UTF-8" />
|
|
35
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
36
|
+
<title>Vista App</title>
|
|
37
|
+
<link rel="icon" href="${logoPath}" />
|
|
38
|
+
</head>
|
|
39
|
+
<body>
|
|
40
|
+
<div id="root"></div>
|
|
41
|
+
<script type="module">
|
|
42
|
+
import { mount, mountError } from '@vista/core';
|
|
43
|
+
console.log('[Vista] Bootstrapping...');
|
|
44
|
+
try {
|
|
45
|
+
// use lazy loading (default) to allow catching syntax errors per module
|
|
46
|
+
// APP Folder scanning: Scan app/**/index.tsx (Pages) and app/**/layout.tsx (Layouts)
|
|
47
|
+
const modules = import.meta.glob(['/app/**/index.tsx', '/app/**/layout.tsx', '/app/layout.tsx', '/app/index.tsx']); console.log('[Vista] Modules discovered:', Object.keys(modules));
|
|
48
|
+
mount(modules);
|
|
49
|
+
} catch (e) {
|
|
50
|
+
console.error('[Vista] Module load failed, calling mountError', e);
|
|
51
|
+
if (typeof mountError === 'function') {
|
|
52
|
+
mountError(e);
|
|
53
|
+
} else {
|
|
54
|
+
console.error('[Vista] mountError is NOT a function! Check exports.', mountError);
|
|
55
|
+
document.body.innerHTML = '<div style="color:red; padding:20px;">CRITICAL: mountError missing. Check console.</div>';
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
</script>
|
|
59
|
+
</body>
|
|
60
|
+
</html>
|
|
61
|
+
`;
|
|
62
|
+
// Transform the HTML to handle imports/HMR
|
|
63
|
+
const transformedHtml = await server.transformIndexHtml(url, html);
|
|
64
|
+
res.statusCode = 200;
|
|
65
|
+
res.setHeader('Content-Type', 'text/html');
|
|
66
|
+
res.end(transformedHtml);
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
next();
|
|
70
|
+
});
|
|
71
|
+
},
|
|
72
|
+
resolveId(id) {
|
|
73
|
+
if (id === 'virtual:vista-routes') {
|
|
74
|
+
return '\0virtual:vista-routes';
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
load(id) {
|
|
78
|
+
if (id === '\0virtual:vista-routes') {
|
|
79
|
+
return `export const routes = []`;
|
|
80
|
+
}
|
|
81
|
+
},
|
|
82
|
+
transform(code, id) {
|
|
83
|
+
// REMOVED: <head> injection caused Hydration Mismatch because React 18 component tree didn't include it.
|
|
84
|
+
// We will enforce <head> being present in the User's Layout instead.
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
}
|
package/dist/router.d.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
type RouterContextType = {
|
|
3
|
+
push: (path: string) => void;
|
|
4
|
+
replace: (path: string) => void;
|
|
5
|
+
back: () => void;
|
|
6
|
+
path: string;
|
|
7
|
+
};
|
|
8
|
+
export declare function useRouter(): RouterContextType;
|
|
9
|
+
export declare function RouterProvider({ children, url }: {
|
|
10
|
+
children: React.ReactNode;
|
|
11
|
+
url?: string;
|
|
12
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
13
|
+
export declare function Link({ href, children, className }: {
|
|
14
|
+
href: string;
|
|
15
|
+
children: React.ReactNode;
|
|
16
|
+
className?: string;
|
|
17
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
18
|
+
export {};
|
package/dist/router.js
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import React, { createContext, useContext, useEffect, useState } from 'react';
|
|
3
|
+
// Create Context
|
|
4
|
+
const RouterContext = createContext(null);
|
|
5
|
+
// Hook
|
|
6
|
+
export function useRouter() {
|
|
7
|
+
const context = useContext(RouterContext);
|
|
8
|
+
if (!context) {
|
|
9
|
+
throw new Error('useRouter must be used within a RouterProvider. This is handled automatically by Vista.');
|
|
10
|
+
}
|
|
11
|
+
return context;
|
|
12
|
+
}
|
|
13
|
+
// Internal Provider
|
|
14
|
+
export function RouterProvider({ children, url }) {
|
|
15
|
+
const [path, setPath] = useState(() => {
|
|
16
|
+
if (url)
|
|
17
|
+
return url;
|
|
18
|
+
if (typeof window !== 'undefined')
|
|
19
|
+
return window.location.pathname;
|
|
20
|
+
return '/';
|
|
21
|
+
});
|
|
22
|
+
useEffect(() => {
|
|
23
|
+
if (typeof window === 'undefined')
|
|
24
|
+
return;
|
|
25
|
+
const onPopState = () => setPath(window.location.pathname);
|
|
26
|
+
window.addEventListener('popstate', onPopState);
|
|
27
|
+
return () => window.removeEventListener('popstate', onPopState);
|
|
28
|
+
}, []);
|
|
29
|
+
// Import startTransition
|
|
30
|
+
const { startTransition } = React;
|
|
31
|
+
const push = (newPath) => {
|
|
32
|
+
window.history.pushState({}, '', newPath);
|
|
33
|
+
setPath(newPath);
|
|
34
|
+
};
|
|
35
|
+
const replace = (newPath) => {
|
|
36
|
+
window.history.replaceState({}, '', newPath);
|
|
37
|
+
setPath(newPath);
|
|
38
|
+
};
|
|
39
|
+
const back = () => {
|
|
40
|
+
window.history.back();
|
|
41
|
+
};
|
|
42
|
+
const value = { push, replace, back, path };
|
|
43
|
+
return (_jsx(RouterContext.Provider, { value: value, children: children }));
|
|
44
|
+
}
|
|
45
|
+
export function Link({ href, children, className }) {
|
|
46
|
+
const { push } = useRouter();
|
|
47
|
+
const handleClick = (e) => {
|
|
48
|
+
// Allow default behavior for modifier keys (new tab)
|
|
49
|
+
if (e.metaKey || e.ctrlKey || e.shiftKey || e.altKey)
|
|
50
|
+
return;
|
|
51
|
+
e.preventDefault();
|
|
52
|
+
push(href);
|
|
53
|
+
};
|
|
54
|
+
return (_jsx("a", { href: href, onClick: handleClick, className: className, children: children }));
|
|
55
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "vista-core-js",
|
|
3
|
+
"version": "0.0.2",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"exports": {
|
|
7
|
+
".": {
|
|
8
|
+
"vite": "./src/index.ts",
|
|
9
|
+
"import": "./dist/index.js",
|
|
10
|
+
"require": "./dist/index.js",
|
|
11
|
+
"default": "./dist/index.js"
|
|
12
|
+
},
|
|
13
|
+
"./client": "./src/client.tsx",
|
|
14
|
+
"./entry-client": "./src/entry-client.tsx",
|
|
15
|
+
"./entry-server": "./src/entry-server.tsx",
|
|
16
|
+
"./client/link": "./src/client/link.tsx",
|
|
17
|
+
"./font/google": {
|
|
18
|
+
"types": "./src/font/google/index.d.ts",
|
|
19
|
+
"import": "./dist/font/google/index.js",
|
|
20
|
+
"default": "./dist/font/google/index.js"
|
|
21
|
+
},
|
|
22
|
+
"./plugin": "./dist/plugin.js"
|
|
23
|
+
},
|
|
24
|
+
"scripts": {
|
|
25
|
+
"dev": "tsc --watch",
|
|
26
|
+
"build": "tsc"
|
|
27
|
+
},
|
|
28
|
+
"peerDependencies": {
|
|
29
|
+
"vite": "^5.0.0",
|
|
30
|
+
"react": "^18.2.0",
|
|
31
|
+
"react-dom": "^18.2.0"
|
|
32
|
+
},
|
|
33
|
+
"devDependencies": {
|
|
34
|
+
"@types/node": "^20.0.0",
|
|
35
|
+
"@types/react": "^18.2.0",
|
|
36
|
+
"@types/react-dom": "^18.2.0",
|
|
37
|
+
"react": "^18.2.0",
|
|
38
|
+
"react-dom": "^18.2.0",
|
|
39
|
+
"typescript": "^5.0.0",
|
|
40
|
+
"vite": "^5.0.0"
|
|
41
|
+
},
|
|
42
|
+
"dependencies": {
|
|
43
|
+
"@types/three": "^0.181.0",
|
|
44
|
+
"postprocessing": "^6.38.0",
|
|
45
|
+
"three": "^0.181.2"
|
|
46
|
+
}
|
|
47
|
+
}
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
|
|
2
|
+
import React, { useState, useEffect } from 'react';
|
|
3
|
+
|
|
4
|
+
interface ErrorOverlayProps {
|
|
5
|
+
error: any;
|
|
6
|
+
onClose?: () => void;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const VSCodeColors = {
|
|
10
|
+
bg: '#1e1e1e',
|
|
11
|
+
sidebar: '#252526',
|
|
12
|
+
activityBar: '#333333',
|
|
13
|
+
statusBar: '#007acc',
|
|
14
|
+
titleBar: '#3c3c3c',
|
|
15
|
+
text: '#d4d4d4',
|
|
16
|
+
red: '#f14c4c',
|
|
17
|
+
yellow: '#cca700',
|
|
18
|
+
green: '#238636',
|
|
19
|
+
terminalBg: '#1e1e1e',
|
|
20
|
+
editorBg: '#1e1e1e',
|
|
21
|
+
lineNo: '#858585',
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export const ErrorOverlay: React.FC<ErrorOverlayProps> = ({ error, onClose }) => {
|
|
25
|
+
if (!error) return null;
|
|
26
|
+
|
|
27
|
+
const isBuildError = error.type === 'error' || error.plugin; // Vite error shape
|
|
28
|
+
const message = error.message || 'Unknown Error';
|
|
29
|
+
const file = error.id || error.file || error.filename || 'Unknown File';
|
|
30
|
+
const frame = error.frame || '';
|
|
31
|
+
const stack = error.stack || '';
|
|
32
|
+
|
|
33
|
+
// Parse location if available
|
|
34
|
+
const loc = error.loc ? `${error.loc.line}:${error.loc.column}` : '';
|
|
35
|
+
|
|
36
|
+
return (
|
|
37
|
+
<div
|
|
38
|
+
style={{
|
|
39
|
+
position: 'fixed',
|
|
40
|
+
top: 0,
|
|
41
|
+
left: 0,
|
|
42
|
+
width: '100vw',
|
|
43
|
+
height: '100vh',
|
|
44
|
+
zIndex: 99999,
|
|
45
|
+
backgroundColor: 'rgba(0, 0, 0, 0.6)',
|
|
46
|
+
backdropFilter: 'blur(8px)',
|
|
47
|
+
display: 'flex',
|
|
48
|
+
alignItems: 'center',
|
|
49
|
+
justifyContent: 'center',
|
|
50
|
+
fontFamily: "'Segoe UI', Tahoma, Geneva, Verdana, sans-serif",
|
|
51
|
+
}}
|
|
52
|
+
>
|
|
53
|
+
<div
|
|
54
|
+
style={{
|
|
55
|
+
width: '85vw',
|
|
56
|
+
maxWidth: '1000px',
|
|
57
|
+
height: '80vh',
|
|
58
|
+
backgroundColor: VSCodeColors.bg,
|
|
59
|
+
borderRadius: '10px',
|
|
60
|
+
boxShadow: '0 20px 50px rgba(0,0,0,0.5)',
|
|
61
|
+
overflow: 'hidden',
|
|
62
|
+
display: 'flex',
|
|
63
|
+
flexDirection: 'column',
|
|
64
|
+
border: '1px solid #454545',
|
|
65
|
+
animation: 'vista-slide-up 0.3s ease-out',
|
|
66
|
+
}}
|
|
67
|
+
>
|
|
68
|
+
{/* Title Bar */}
|
|
69
|
+
<div
|
|
70
|
+
style={{
|
|
71
|
+
height: '35px',
|
|
72
|
+
backgroundColor: VSCodeColors.titleBar,
|
|
73
|
+
display: 'flex',
|
|
74
|
+
alignItems: 'center',
|
|
75
|
+
padding: '0 15px',
|
|
76
|
+
borderBottom: '1px solid #333',
|
|
77
|
+
}}
|
|
78
|
+
>
|
|
79
|
+
<div style={{ display: 'flex', gap: '8px', marginRight: '20px' }}>
|
|
80
|
+
<div onClick={onClose} style={{ width: '12px', height: '12px', borderRadius: '50%', backgroundColor: '#ff5f56', cursor: 'pointer' }}></div>
|
|
81
|
+
<div style={{ width: '12px', height: '12px', borderRadius: '50%', backgroundColor: '#ffbd2e' }}></div>
|
|
82
|
+
<div style={{ width: '12px', height: '12px', borderRadius: '50%', backgroundColor: '#27c93f' }}></div>
|
|
83
|
+
</div>
|
|
84
|
+
<div style={{ color: '#ccc', fontSize: '13px', flex: 1, textAlign: 'center', opacity: 0.8 }}>
|
|
85
|
+
{file.split('/').pop()} {loc ? `— ${loc}` : ''}
|
|
86
|
+
</div>
|
|
87
|
+
</div>
|
|
88
|
+
|
|
89
|
+
<div style={{ display: 'flex', flex: 1, overflow: 'hidden' }}>
|
|
90
|
+
{/* Activity Bar (Left Strip) */}
|
|
91
|
+
<div style={{ width: '50px', backgroundColor: VSCodeColors.activityBar, display: 'flex', flexDirection: 'column', alignItems: 'center', paddingTop: '15px' }}>
|
|
92
|
+
<div style={{ marginBottom: '20px', opacity: 0.6, cursor: 'pointer' }} title="Explorer">
|
|
93
|
+
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#d4d4d4" strokeWidth="1.5"><path d="M13 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V9z"></path><polyline points="13 2 13 9 20 9"></polyline></svg>
|
|
94
|
+
</div>
|
|
95
|
+
<div style={{ marginBottom: '20px', opacity: 0.6, cursor: 'pointer' }} title="Search">
|
|
96
|
+
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#d4d4d4" strokeWidth="1.5"><circle cx="11" cy="11" r="8"></circle><line x1="21" y1="21" x2="16.65" y2="16.65"></line></svg>
|
|
97
|
+
</div>
|
|
98
|
+
<div style={{ marginBottom: '20px', opacity: 1, borderLeft: '2px solid white', paddingLeft: '11px', cursor: 'pointer' }} title="Run and Debug">
|
|
99
|
+
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#d4d4d4" strokeWidth="1.5"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"></path><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"></path></svg>
|
|
100
|
+
</div>
|
|
101
|
+
</div>
|
|
102
|
+
|
|
103
|
+
{/* Sidebar (File Explorer Simulation) */}
|
|
104
|
+
<div className="vista-sidebar" style={{ width: '200px', backgroundColor: VSCodeColors.sidebar, fontSize: '13px', color: VSCodeColors.text, padding: '10px', borderRight: '1px solid #333' }}>
|
|
105
|
+
<div style={{ fontSize: '11px', fontWeight: 'bold', marginBottom: '10px', opacity: 0.7, textTransform: 'uppercase' }}>Open Editors</div>
|
|
106
|
+
<div style={{ padding: '5px 10px', backgroundColor: '#37373d', borderRadius: '3px', cursor: 'pointer', display: 'flex', alignItems: 'center' }}>
|
|
107
|
+
<span style={{ marginRight: '8px', display: 'flex', alignItems: 'center' }}>
|
|
108
|
+
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#61dafb" strokeWidth="2"><circle cx="12" cy="12" r="10"></circle><line x1="12" y1="8" x2="12" y2="12"></line><line x1="12" y1="16" x2="12.01" y2="16"></line></svg>
|
|
109
|
+
</span>
|
|
110
|
+
{file.split('/').pop()}
|
|
111
|
+
</div>
|
|
112
|
+
</div>
|
|
113
|
+
|
|
114
|
+
{/* Main Content Area */}
|
|
115
|
+
<div style={{ flex: 1, display: 'flex', flexDirection: 'column', backgroundColor: VSCodeColors.editorBg }}>
|
|
116
|
+
|
|
117
|
+
{/* Editor Tabs */}
|
|
118
|
+
<div style={{ height: '35px', backgroundColor: VSCodeColors.bg, display: 'flex', alignItems: 'center', borderBottom: '1px solid #333' }}>
|
|
119
|
+
<div style={{ padding: '0 15px', height: '100%', backgroundColor: '#1e1e1e', borderTop: '2px solid #e11c1c', color: '#fff', fontSize: '13px', display: 'flex', alignItems: 'center', gap: '8px' }}>
|
|
120
|
+
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#61dafb" strokeWidth="2"><circle cx="12" cy="12" r="10"></circle><line x1="12" y1="8" x2="12" y2="12"></line><line x1="12" y1="16" x2="12.01" y2="16"></line></svg>
|
|
121
|
+
{file.split('/').pop()}
|
|
122
|
+
<span style={{ marginLeft: '10px', cursor: 'pointer', opacity: 0.7 }}>
|
|
123
|
+
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><line x1="18" y1="6" x2="6" y2="18"></line><line x1="6" y1="6" x2="18" y2="18"></line></svg>
|
|
124
|
+
</span>
|
|
125
|
+
</div>
|
|
126
|
+
</div>
|
|
127
|
+
|
|
128
|
+
{/* Editor Pane (Code Frame or Stack Fallback) */}
|
|
129
|
+
<div style={{ flex: 2, padding: '20px', overflow: 'auto', fontFamily: "'Fira Code', 'Consolas', monospace", fontSize: '14px', lineHeight: '1.5', color: '#d4d4d4' }}>
|
|
130
|
+
{/* Explicitly show message if it's a build error to ensure context is seen */}
|
|
131
|
+
{isBuildError && (
|
|
132
|
+
<div style={{ color: '#f14c4c', marginBottom: '15px', fontWeight: 'bold' }}>
|
|
133
|
+
{message}
|
|
134
|
+
</div>
|
|
135
|
+
)}
|
|
136
|
+
|
|
137
|
+
{frame ? (
|
|
138
|
+
<pre style={{ margin: 0, whiteSpace: 'pre-wrap', fontFamily: 'inherit' }}>{frame}</pre>
|
|
139
|
+
) : (
|
|
140
|
+
<div style={{ color: '#d4d4d4' }}>
|
|
141
|
+
<div style={{ color: '#666', marginBottom: '20px' }}>// Source code not available for runtime execution error.</div>
|
|
142
|
+
<pre style={{ margin: 0, color: '#9cdcfe', whiteSpace: 'pre-wrap' }}>{stack}</pre>
|
|
143
|
+
</div>
|
|
144
|
+
)}
|
|
145
|
+
</div>
|
|
146
|
+
|
|
147
|
+
{/* Terminal Pane */}
|
|
148
|
+
<div style={{ flex: 1, backgroundColor: '#1e1e1e', borderTop: '1px solid #444', display: 'flex', flexDirection: 'column' }}>
|
|
149
|
+
<div style={{ display: 'flex', padding: '5px 15px', borderBottom: '1px solid #333', fontSize: '11px', color: '#ccc', gap: '15px' }}>
|
|
150
|
+
<span style={{ textDecoration: 'underline', cursor: 'pointer' }}>PROBLEMS</span>
|
|
151
|
+
<span style={{ opacity: 0.5, cursor: 'pointer' }}>OUTPUT</span>
|
|
152
|
+
<span style={{ opacity: 0.5, cursor: 'pointer' }}>DEBUG CONSOLE</span>
|
|
153
|
+
<span style={{ opacity: 0.5, cursor: 'pointer' }}>TERMINAL</span>
|
|
154
|
+
</div>
|
|
155
|
+
<div style={{ padding: '15px', overflow: 'auto', fontFamily: 'monospace', fontSize: '13px', color: '#f14c4c' }}>
|
|
156
|
+
<div style={{ color: '#d4d4d4', marginBottom: '5px' }}>$ npm run dev</div>
|
|
157
|
+
<div>> Error: {message}</div>
|
|
158
|
+
</div>
|
|
159
|
+
</div>
|
|
160
|
+
|
|
161
|
+
</div>
|
|
162
|
+
</div>
|
|
163
|
+
|
|
164
|
+
{/* Status Bar */}
|
|
165
|
+
<div style={{ height: '22px', backgroundColor: '#e11c1c', display: 'flex', alignItems: 'center', padding: '0 10px', fontSize: '12px', color: 'white', justifyContent: 'space-between' }}>
|
|
166
|
+
<div style={{ display: 'flex', gap: '15px', alignItems: 'center' }}>
|
|
167
|
+
<span style={{ display: 'flex', alignItems: 'center', gap: '5px' }}>
|
|
168
|
+
<svg width="12" height="12" viewBox="0 0 24 24" fill="currentColor"><circle cx="12" cy="12" r="10"></circle><line x1="15" y1="9" x2="9" y2="15"></line><line x1="9" y1="9" x2="15" y2="15"></line></svg>
|
|
169
|
+
1 Error
|
|
170
|
+
</span>
|
|
171
|
+
<span style={{ display: 'flex', alignItems: 'center', gap: '5px' }}>
|
|
172
|
+
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"></path><line x1="12" y1="9" x2="12" y2="13"></line><line x1="12" y1="17" x2="12.01" y2="17"></line></svg>
|
|
173
|
+
0 Warnings
|
|
174
|
+
</span>
|
|
175
|
+
</div>
|
|
176
|
+
<div>
|
|
177
|
+
Ln {error.loc?.line || 0}, Col {error.loc?.column || 0}
|
|
178
|
+
</div>
|
|
179
|
+
</div>
|
|
180
|
+
|
|
181
|
+
</div>
|
|
182
|
+
<style>{`
|
|
183
|
+
.vista-sidebar { display: none; }
|
|
184
|
+
@media (min-width: 768px) {
|
|
185
|
+
.vista-sidebar { display: block; }
|
|
186
|
+
}
|
|
187
|
+
@keyframes vista-slide-up {
|
|
188
|
+
from { opacity: 0; transform: translateY(20px) scale(0.95); }
|
|
189
|
+
to { opacity: 1; transform: translateY(0) scale(1); }
|
|
190
|
+
}
|
|
191
|
+
`}</style>
|
|
192
|
+
</div>
|
|
193
|
+
);
|
|
194
|
+
};
|