pizzaz-mcp 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/.vscode/settings.json +3 -0
- package/README.md +139 -0
- package/build-all.mts +188 -0
- package/docs/DEPLOYMENT_GUIDE.md +226 -0
- package/package.json +41 -0
- package/render.yaml +12 -0
- package/server/server.ts +400 -0
- package/src/index.css +39 -0
- package/src/media-queries.ts +15 -0
- package/src/pizzaz/Inspector.jsx +109 -0
- package/src/pizzaz/Sidebar.jsx +165 -0
- package/src/pizzaz/index.jsx +295 -0
- package/src/pizzaz/map.css +707 -0
- package/src/pizzaz/markers.json +104 -0
- package/src/pizzaz-albums/AlbumCard.jsx +45 -0
- package/src/pizzaz-albums/FilmStrip.jsx +30 -0
- package/src/pizzaz-albums/FullscreenViewer.jsx +43 -0
- package/src/pizzaz-albums/albums.json +112 -0
- package/src/pizzaz-albums/index.jsx +153 -0
- package/src/pizzaz-carousel/PlaceCard.jsx +40 -0
- package/src/pizzaz-carousel/index.jsx +121 -0
- package/src/pizzaz-list/index.jsx +115 -0
- package/src/pizzaz-shop/index.tsx +1482 -0
- package/src/types.ts +103 -0
- package/src/use-display-mode.ts +6 -0
- package/src/use-max-height.ts +5 -0
- package/src/use-openai-global.ts +37 -0
- package/src/use-widget-props.ts +14 -0
- package/src/use-widget-state.ts +46 -0
- package/tailwind.config.ts +7 -0
- package/tsconfig.app.json +24 -0
- package/tsconfig.json +7 -0
- package/tsconfig.node.json +13 -0
- package/vite-env.d.ts +1 -0
- package/vite.config.mts +232 -0
package/src/types.ts
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
export type OpenAiGlobals<
|
|
2
|
+
ToolInput = UnknownObject,
|
|
3
|
+
ToolOutput = UnknownObject,
|
|
4
|
+
ToolResponseMetadata = UnknownObject,
|
|
5
|
+
WidgetState = UnknownObject
|
|
6
|
+
> = {
|
|
7
|
+
// visuals
|
|
8
|
+
theme: Theme;
|
|
9
|
+
|
|
10
|
+
userAgent: UserAgent;
|
|
11
|
+
locale: string;
|
|
12
|
+
|
|
13
|
+
// layout
|
|
14
|
+
maxHeight: number;
|
|
15
|
+
displayMode: DisplayMode;
|
|
16
|
+
safeArea: SafeArea;
|
|
17
|
+
|
|
18
|
+
// state
|
|
19
|
+
toolInput: ToolInput;
|
|
20
|
+
toolOutput: ToolOutput | null;
|
|
21
|
+
toolResponseMetadata: ToolResponseMetadata | null;
|
|
22
|
+
widgetState: WidgetState | null;
|
|
23
|
+
setWidgetState: (state: WidgetState) => Promise<void>;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
// currently copied from types.ts in chatgpt/web-sandbox.
|
|
27
|
+
// Will eventually use a public package.
|
|
28
|
+
type API = {
|
|
29
|
+
callTool: CallTool;
|
|
30
|
+
sendFollowUpMessage: (args: { prompt: string }) => Promise<void>;
|
|
31
|
+
openExternal(payload: { href: string }): void;
|
|
32
|
+
|
|
33
|
+
// Layout controls
|
|
34
|
+
requestDisplayMode: RequestDisplayMode;
|
|
35
|
+
requestModal: (args: { title?: string; params?: UnknownObject }) => Promise<unknown>;
|
|
36
|
+
requestClose: () => Promise<void>;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
export type UnknownObject = Record<string, unknown>;
|
|
40
|
+
|
|
41
|
+
export type Theme = "light" | "dark";
|
|
42
|
+
|
|
43
|
+
export type SafeAreaInsets = {
|
|
44
|
+
top: number;
|
|
45
|
+
bottom: number;
|
|
46
|
+
left: number;
|
|
47
|
+
right: number;
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
export type SafeArea = {
|
|
51
|
+
insets: SafeAreaInsets;
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
export type DeviceType = "mobile" | "tablet" | "desktop" | "unknown";
|
|
55
|
+
|
|
56
|
+
export type UserAgent = {
|
|
57
|
+
device: { type: DeviceType };
|
|
58
|
+
capabilities: {
|
|
59
|
+
hover: boolean;
|
|
60
|
+
touch: boolean;
|
|
61
|
+
};
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
/** Display mode */
|
|
65
|
+
export type DisplayMode = "pip" | "inline" | "fullscreen";
|
|
66
|
+
export type RequestDisplayMode = (args: { mode: DisplayMode }) => Promise<{
|
|
67
|
+
/**
|
|
68
|
+
* The granted display mode. The host may reject the request.
|
|
69
|
+
* For mobile, PiP is always coerced to fullscreen.
|
|
70
|
+
*/
|
|
71
|
+
mode: DisplayMode;
|
|
72
|
+
}>;
|
|
73
|
+
|
|
74
|
+
export type CallToolResponse = {
|
|
75
|
+
result: string;
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
/** Calling APIs */
|
|
79
|
+
export type CallTool = (
|
|
80
|
+
name: string,
|
|
81
|
+
args: Record<string, unknown>
|
|
82
|
+
) => Promise<CallToolResponse>;
|
|
83
|
+
|
|
84
|
+
/** Extra events */
|
|
85
|
+
export const SET_GLOBALS_EVENT_TYPE = "openai:set_globals";
|
|
86
|
+
export class SetGlobalsEvent extends CustomEvent<{
|
|
87
|
+
globals: Partial<OpenAiGlobals>;
|
|
88
|
+
}> {
|
|
89
|
+
readonly type = SET_GLOBALS_EVENT_TYPE;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Global oai object injected by the web sandbox for communicating with chatgpt host page.
|
|
94
|
+
*/
|
|
95
|
+
declare global {
|
|
96
|
+
interface Window {
|
|
97
|
+
openai: API & OpenAiGlobals;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
interface WindowEventMap {
|
|
101
|
+
[SET_GLOBALS_EVENT_TYPE]: SetGlobalsEvent;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { useSyncExternalStore } from "react";
|
|
2
|
+
import {
|
|
3
|
+
SET_GLOBALS_EVENT_TYPE,
|
|
4
|
+
SetGlobalsEvent,
|
|
5
|
+
type OpenAiGlobals,
|
|
6
|
+
} from "./types";
|
|
7
|
+
|
|
8
|
+
export function useOpenAiGlobal<K extends keyof OpenAiGlobals>(
|
|
9
|
+
key: K
|
|
10
|
+
): OpenAiGlobals[K] | null {
|
|
11
|
+
return useSyncExternalStore(
|
|
12
|
+
(onChange) => {
|
|
13
|
+
if (typeof window === "undefined") {
|
|
14
|
+
return () => {};
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const handleSetGlobal = (event: SetGlobalsEvent) => {
|
|
18
|
+
const value = event.detail.globals[key];
|
|
19
|
+
if (value === undefined) {
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
onChange();
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
window.addEventListener(SET_GLOBALS_EVENT_TYPE, handleSetGlobal, {
|
|
27
|
+
passive: true,
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
return () => {
|
|
31
|
+
window.removeEventListener(SET_GLOBALS_EVENT_TYPE, handleSetGlobal);
|
|
32
|
+
};
|
|
33
|
+
},
|
|
34
|
+
() => window.openai?.[key] ?? null,
|
|
35
|
+
() => window.openai?.[key] ?? null
|
|
36
|
+
);
|
|
37
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { useOpenAiGlobal } from "./use-openai-global";
|
|
2
|
+
|
|
3
|
+
export function useWidgetProps<T extends Record<string, unknown>>(
|
|
4
|
+
defaultState?: T | (() => T)
|
|
5
|
+
): T {
|
|
6
|
+
const props = useOpenAiGlobal("toolOutput") as T;
|
|
7
|
+
|
|
8
|
+
const fallback =
|
|
9
|
+
typeof defaultState === "function"
|
|
10
|
+
? (defaultState as () => T | null)()
|
|
11
|
+
: defaultState ?? null;
|
|
12
|
+
|
|
13
|
+
return props ?? fallback;
|
|
14
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { useCallback, useEffect, useState, type SetStateAction } from "react";
|
|
2
|
+
import { useOpenAiGlobal } from "./use-openai-global";
|
|
3
|
+
import type { UnknownObject } from "./types";
|
|
4
|
+
|
|
5
|
+
export function useWidgetState<T extends UnknownObject>(
|
|
6
|
+
defaultState: T | (() => T)
|
|
7
|
+
): readonly [T, (state: SetStateAction<T>) => void];
|
|
8
|
+
export function useWidgetState<T extends UnknownObject>(
|
|
9
|
+
defaultState?: T | (() => T | null) | null
|
|
10
|
+
): readonly [T | null, (state: SetStateAction<T | null>) => void];
|
|
11
|
+
export function useWidgetState<T extends UnknownObject>(
|
|
12
|
+
defaultState?: T | (() => T | null) | null
|
|
13
|
+
): readonly [T | null, (state: SetStateAction<T | null>) => void] {
|
|
14
|
+
const widgetStateFromWindow = useOpenAiGlobal("widgetState") as T;
|
|
15
|
+
|
|
16
|
+
const [widgetState, _setWidgetState] = useState<T | null>(() => {
|
|
17
|
+
if (widgetStateFromWindow != null) {
|
|
18
|
+
return widgetStateFromWindow;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return typeof defaultState === "function"
|
|
22
|
+
? defaultState()
|
|
23
|
+
: defaultState ?? null;
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
useEffect(() => {
|
|
27
|
+
_setWidgetState(widgetStateFromWindow);
|
|
28
|
+
}, [widgetStateFromWindow]);
|
|
29
|
+
|
|
30
|
+
const setWidgetState = useCallback(
|
|
31
|
+
(state: SetStateAction<T | null>) => {
|
|
32
|
+
_setWidgetState((prevState) => {
|
|
33
|
+
const newState = typeof state === "function" ? state(prevState) : state;
|
|
34
|
+
|
|
35
|
+
if (newState != null && typeof window !== "undefined") {
|
|
36
|
+
void window.openai?.setWidgetState?.(newState);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return newState;
|
|
40
|
+
});
|
|
41
|
+
},
|
|
42
|
+
[]
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
return [widgetState, setWidgetState] as const;
|
|
46
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
|
|
4
|
+
"target": "ES2022",
|
|
5
|
+
"useDefineForClassFields": true,
|
|
6
|
+
"lib": ["ES2022", "DOM", "DOM.Iterable"],
|
|
7
|
+
"module": "ESNext",
|
|
8
|
+
"skipLibCheck": true,
|
|
9
|
+
"moduleResolution": "bundler",
|
|
10
|
+
"allowImportingTsExtensions": true,
|
|
11
|
+
"verbatimModuleSyntax": false,
|
|
12
|
+
"moduleDetection": "force",
|
|
13
|
+
"noEmit": true,
|
|
14
|
+
"jsx": "react-jsx",
|
|
15
|
+
"jsxImportSource": "react",
|
|
16
|
+
"types": ["vite/client"],
|
|
17
|
+
"baseUrl": ".",
|
|
18
|
+
"strict": true,
|
|
19
|
+
"noUnusedLocals": false,
|
|
20
|
+
"noUnusedParameters": false,
|
|
21
|
+
"noFallthroughCasesInSwitch": true
|
|
22
|
+
},
|
|
23
|
+
"include": ["src", "vite-env.d.ts"]
|
|
24
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "NodeNext",
|
|
5
|
+
"moduleResolution": "NodeNext",
|
|
6
|
+
"strict": true,
|
|
7
|
+
"esModuleInterop": true,
|
|
8
|
+
"resolveJsonModule": true,
|
|
9
|
+
"skipLibCheck": true,
|
|
10
|
+
"types": ["node"]
|
|
11
|
+
},
|
|
12
|
+
"include": ["server/**/*.ts", "build-all.mts", "vite.config.mts"]
|
|
13
|
+
}
|
package/vite-env.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
/// <reference types="vite/client" />
|
package/vite.config.mts
ADDED
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
import { defineConfig, type Plugin } from "vite";
|
|
2
|
+
import react from "@vitejs/plugin-react";
|
|
3
|
+
import fg from "fast-glob";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import fs from "node:fs";
|
|
6
|
+
import tailwindcss from "@tailwindcss/vite";
|
|
7
|
+
|
|
8
|
+
function buildInputs() {
|
|
9
|
+
const files = fg.sync("src/**/index.{tsx,jsx}", { dot: false });
|
|
10
|
+
return Object.fromEntries(
|
|
11
|
+
files.map((f) => [path.basename(path.dirname(f)), path.resolve(f)])
|
|
12
|
+
);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const toFs = (abs: string) => "/@fs/" + abs.replace(/\\/g, "/");
|
|
16
|
+
|
|
17
|
+
const toServerRoot = (abs: string) => {
|
|
18
|
+
const rel = path.relative(process.cwd(), abs).replace(/\\/g, "/");
|
|
19
|
+
// If it's not really relative (different drive or absolute), fall back to fs URL
|
|
20
|
+
if (!rel || rel.startsWith("..") || path.isAbsolute(rel)) return toFs(abs);
|
|
21
|
+
return "./" + rel;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
function multiEntryDevEndpoints(options: {
|
|
25
|
+
entries: Record<string, string>;
|
|
26
|
+
globalCss?: string[];
|
|
27
|
+
perEntryCssGlob?: string;
|
|
28
|
+
perEntryCssIgnore?: string[];
|
|
29
|
+
}): Plugin {
|
|
30
|
+
const {
|
|
31
|
+
entries,
|
|
32
|
+
globalCss = ["src/index.css"],
|
|
33
|
+
perEntryCssGlob = "**/*.{css,pcss,scss,sass}",
|
|
34
|
+
perEntryCssIgnore = ["**/*.module.*"],
|
|
35
|
+
} = options;
|
|
36
|
+
|
|
37
|
+
const V_PREFIX = "\0multi-entry:"; // Rollup “virtual module” prefix
|
|
38
|
+
|
|
39
|
+
const HIDE_FROM_HOME = new Set(["flashcards", "daw"]);
|
|
40
|
+
|
|
41
|
+
const renderIndexHtml = (names: string[]): string => `<!doctype html>
|
|
42
|
+
<html>
|
|
43
|
+
<head>
|
|
44
|
+
<meta charset="utf-8" />
|
|
45
|
+
<title>ecosystem ui examples</title>
|
|
46
|
+
<style>
|
|
47
|
+
body { font: 15px/1.5 system-ui, sans-serif; margin: 32px; color: #1f2933; }
|
|
48
|
+
h1 { font-size: 20px; margin-bottom: 12px; }
|
|
49
|
+
ul { padding-left: 18px; }
|
|
50
|
+
li { margin-bottom: 6px; }
|
|
51
|
+
a { color: #2563eb; text-decoration: none; }
|
|
52
|
+
a:hover { text-decoration: underline; }
|
|
53
|
+
code { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 13px; margin-left: 6px; color: #64748b; }
|
|
54
|
+
</style>
|
|
55
|
+
</head>
|
|
56
|
+
<body>
|
|
57
|
+
<h1>Examples</h1>
|
|
58
|
+
<ul>
|
|
59
|
+
${names
|
|
60
|
+
.filter((n) => !HIDE_FROM_HOME.has(n))
|
|
61
|
+
.toSorted()
|
|
62
|
+
.map(
|
|
63
|
+
(name) =>
|
|
64
|
+
`<li><a href="/${name}.html">${name}</a><code>/${name}.html</code></li>`
|
|
65
|
+
)
|
|
66
|
+
.join("\n ")}
|
|
67
|
+
</ul>
|
|
68
|
+
</body>
|
|
69
|
+
</html>`;
|
|
70
|
+
|
|
71
|
+
const renderDevHtml = (name: string): string => `<!doctype html>
|
|
72
|
+
<html>
|
|
73
|
+
<head>
|
|
74
|
+
<script type="module" src="/${name}.js"></script>
|
|
75
|
+
<link rel="stylesheet" href="/${name}.css">
|
|
76
|
+
</head>
|
|
77
|
+
<body>
|
|
78
|
+
<div id="${name}-root"></div>
|
|
79
|
+
</body>
|
|
80
|
+
</html>`;
|
|
81
|
+
|
|
82
|
+
return {
|
|
83
|
+
name: "multi-entry-dev-endpoints",
|
|
84
|
+
configureServer(server) {
|
|
85
|
+
const names = Object.keys(entries);
|
|
86
|
+
const list = names
|
|
87
|
+
.map((n) => `/${n}.html, /${n}.js, /${n}.css`)
|
|
88
|
+
.join("\n ");
|
|
89
|
+
server.config.logger.info(`\nDev endpoints:\n ${list}\n`);
|
|
90
|
+
|
|
91
|
+
server.middlewares.use((req, res, next) => {
|
|
92
|
+
try {
|
|
93
|
+
if (req.method !== "GET" || !req.url) return next();
|
|
94
|
+
const url = req.url.split("?")[0];
|
|
95
|
+
if (url === "/" || url === "" || url === "/index.html") {
|
|
96
|
+
const html = renderIndexHtml(names);
|
|
97
|
+
res.setHeader("Content-Type", "text/html; charset=utf-8");
|
|
98
|
+
res.end(html);
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
const bareMatch = url.match(/^\/?([\w-]+)\/?$/);
|
|
102
|
+
if (bareMatch && entries[bareMatch[1]]) {
|
|
103
|
+
const name = bareMatch[1];
|
|
104
|
+
const html = renderDevHtml(name);
|
|
105
|
+
res.setHeader("Content-Type", "text/html; charset=utf-8");
|
|
106
|
+
res.end(html);
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (!url.endsWith(".html")) return next();
|
|
111
|
+
|
|
112
|
+
const m = url.match(/^\/?([\w-]+)\.html$/);
|
|
113
|
+
if (!m) return next();
|
|
114
|
+
const name = m[1];
|
|
115
|
+
if (!entries[name]) return next();
|
|
116
|
+
|
|
117
|
+
const html = renderDevHtml(name);
|
|
118
|
+
res.setHeader("Content-Type", "text/html");
|
|
119
|
+
res.end(html);
|
|
120
|
+
return;
|
|
121
|
+
} catch {
|
|
122
|
+
// fall through
|
|
123
|
+
}
|
|
124
|
+
next();
|
|
125
|
+
});
|
|
126
|
+
},
|
|
127
|
+
resolveId(id: string) {
|
|
128
|
+
// Map request paths to virtual ids
|
|
129
|
+
if (id.startsWith("/")) id = id.slice(1);
|
|
130
|
+
if (id.endsWith(".js")) {
|
|
131
|
+
const name = id.slice(0, -3);
|
|
132
|
+
if (entries[name]) return `${V_PREFIX}entry:${name}`;
|
|
133
|
+
}
|
|
134
|
+
if (id.endsWith(".css")) {
|
|
135
|
+
const name = id.slice(0, -4);
|
|
136
|
+
if (entries[name]) return `${V_PREFIX}style:${name}.css`;
|
|
137
|
+
}
|
|
138
|
+
if (id.startsWith(V_PREFIX)) return id;
|
|
139
|
+
return null;
|
|
140
|
+
},
|
|
141
|
+
load(id: string) {
|
|
142
|
+
if (!id.startsWith(V_PREFIX)) return null;
|
|
143
|
+
|
|
144
|
+
const rest = id.slice(V_PREFIX.length); // "entry:foo" or "style:foo.css"
|
|
145
|
+
const [kind, nameWithExt] = rest.split(":", 2);
|
|
146
|
+
const name = nameWithExt.replace(/\.css$/, "");
|
|
147
|
+
const entry = entries[name];
|
|
148
|
+
if (!entry) return null;
|
|
149
|
+
|
|
150
|
+
const entryDir = path.dirname(entry);
|
|
151
|
+
|
|
152
|
+
// Collect CSS (global first for stable cascade)
|
|
153
|
+
const globals = globalCss
|
|
154
|
+
.map((p) => path.resolve(p))
|
|
155
|
+
.filter((p) => fs.existsSync(p));
|
|
156
|
+
const perEntry = fg.sync(perEntryCssGlob, {
|
|
157
|
+
cwd: entryDir,
|
|
158
|
+
absolute: true,
|
|
159
|
+
dot: false,
|
|
160
|
+
ignore: perEntryCssIgnore,
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
if (kind === "style") {
|
|
164
|
+
const allCss = [...globals, ...perEntry]; // absolute paths on disk
|
|
165
|
+
const lines = [
|
|
166
|
+
`@source "./src";`,
|
|
167
|
+
...allCss.map((p) => `@import "${toServerRoot(p)}";`),
|
|
168
|
+
];
|
|
169
|
+
return lines.join("\n");
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
if (kind === "entry") {
|
|
173
|
+
const spec = toFs(entry);
|
|
174
|
+
|
|
175
|
+
const lines: string[] = [];
|
|
176
|
+
|
|
177
|
+
// Import Vite HMR client from root
|
|
178
|
+
lines.push(`import "/@vite/client";`);
|
|
179
|
+
|
|
180
|
+
lines.push(`
|
|
181
|
+
import RefreshRuntime from "/@react-refresh";
|
|
182
|
+
|
|
183
|
+
if (!window.__vite_plugin_react_preamble_installed__) {
|
|
184
|
+
RefreshRuntime.injectIntoGlobalHook(window);
|
|
185
|
+
window.$RefreshReg$ = () => {};
|
|
186
|
+
window.$RefreshSig$ = () => (type) => type;
|
|
187
|
+
window.__vite_plugin_react_preamble_installed__ = true;
|
|
188
|
+
}
|
|
189
|
+
`);
|
|
190
|
+
|
|
191
|
+
lines.push(`import "/${name}.css";`);
|
|
192
|
+
lines.push(`await import(${JSON.stringify(spec)});`);
|
|
193
|
+
|
|
194
|
+
return lines.join("\n");
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
return null;
|
|
198
|
+
},
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
const inputs = buildInputs();
|
|
203
|
+
|
|
204
|
+
export default defineConfig(({}) => ({
|
|
205
|
+
plugins: [
|
|
206
|
+
tailwindcss(),
|
|
207
|
+
react(),
|
|
208
|
+
multiEntryDevEndpoints({ entries: inputs }),
|
|
209
|
+
],
|
|
210
|
+
cacheDir: "node_modules/.vite-react",
|
|
211
|
+
server: {
|
|
212
|
+
port: 4444,
|
|
213
|
+
strictPort: true,
|
|
214
|
+
cors: true,
|
|
215
|
+
},
|
|
216
|
+
esbuild: {
|
|
217
|
+
jsx: "automatic",
|
|
218
|
+
jsxImportSource: "react",
|
|
219
|
+
target: "es2022",
|
|
220
|
+
},
|
|
221
|
+
build: {
|
|
222
|
+
target: "es2022",
|
|
223
|
+
sourcemap: true,
|
|
224
|
+
minify: "esbuild",
|
|
225
|
+
outDir: "assets",
|
|
226
|
+
assetsDir: ".",
|
|
227
|
+
rollupOptions: {
|
|
228
|
+
input: inputs,
|
|
229
|
+
preserveEntrySignatures: "strict",
|
|
230
|
+
},
|
|
231
|
+
},
|
|
232
|
+
}));
|