owenkaplinsky commited on
Commit
9c91326
·
1 Parent(s): a9a6399

Add local API env

Browse files
project/chat.py CHANGED
@@ -2,22 +2,14 @@ import os
2
  from fastapi import FastAPI, Request
3
  from fastapi.middleware.cors import CORSMiddleware
4
  from openai import OpenAI
5
- from dotenv import load_dotenv
6
  import uvicorn
7
  import gradio as gr
8
 
 
 
9
 
10
- # Load environment variables
11
- load_dotenv()
12
-
13
- # Initialize OpenAI client
14
- api_key = os.getenv("OPENAI_API_KEY")
15
- if api_key:
16
- client = OpenAI(api_key=api_key)
17
- else:
18
- print("Warning: OPENAI_API_KEY not found in environment variables.")
19
- print("Chat functionality will be limited.")
20
- client = None
21
 
22
  # Global variable to store the latest chat context
23
  latest_blockly_chat_code = ""
@@ -41,6 +33,20 @@ async def update_chat(request: Request):
41
  print("\n[FASTAPI] Updated Blockly chat code:\n", latest_blockly_chat_code)
42
  return {"code": latest_blockly_chat_code}
43
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
44
  def create_gradio_interface():
45
  # Hardcoded system prompt
46
  SYSTEM_PROMPT = """You are an AI assistant created to help with Blockly MCP tasks. Users can create MCP (multi-context-protocol) servers
@@ -61,8 +67,20 @@ When the user asks questions or talks about their project, don't talk like a rob
61
  are doing, state the goal or things. Remember, that info is just for you and you need to speak normally to the user."""
62
 
63
  def chat_with_context(message, history):
64
- if not client:
65
- return "OpenAI API key not configured. Please set OPENAI_API_KEY in your .env file."
 
 
 
 
 
 
 
 
 
 
 
 
66
 
67
  # Get the chat context from the global variable
68
  global latest_blockly_chat_code
 
2
  from fastapi import FastAPI, Request
3
  from fastapi.middleware.cors import CORSMiddleware
4
  from openai import OpenAI
 
5
  import uvicorn
6
  import gradio as gr
7
 
8
+ # Initialize OpenAI client (will be updated when API key is set)
9
+ client = None
10
 
11
+ # Store API key in memory for this process
12
+ stored_api_key = ""
 
 
 
 
 
 
 
 
 
13
 
14
  # Global variable to store the latest chat context
15
  latest_blockly_chat_code = ""
 
33
  print("\n[FASTAPI] Updated Blockly chat code:\n", latest_blockly_chat_code)
34
  return {"code": latest_blockly_chat_code}
35
 
36
+ @app.post("/set_api_key_chat")
37
+ async def set_api_key_chat(request: Request):
38
+ """Receive API key from frontend and store it"""
39
+ global stored_api_key
40
+ data = await request.json()
41
+ api_key = data.get("api_key", "").strip()
42
+
43
+ # Store in memory and set environment variable for this process
44
+ stored_api_key = api_key
45
+ os.environ["OPENAI_API_KEY"] = api_key
46
+
47
+ print(f"[CHAT API KEY] Set OPENAI_API_KEY in chat.py environment")
48
+ return {"success": True}
49
+
50
  def create_gradio_interface():
51
  # Hardcoded system prompt
52
  SYSTEM_PROMPT = """You are an AI assistant created to help with Blockly MCP tasks. Users can create MCP (multi-context-protocol) servers
 
67
  are doing, state the goal or things. Remember, that info is just for you and you need to speak normally to the user."""
68
 
69
  def chat_with_context(message, history):
70
+ # Check if API key is set and create/update client
71
+ global client, stored_api_key
72
+
73
+ # Use stored key or check environment
74
+ api_key = stored_api_key or os.environ.get("OPENAI_API_KEY")
75
+
76
+ if api_key and (not client or (hasattr(client, 'api_key') and client.api_key != api_key)):
77
+ try:
78
+ client = OpenAI(api_key=api_key)
79
+ except Exception as e:
80
+ return f"Error initializing OpenAI client: {str(e)}"
81
+
82
+ if not client or not api_key:
83
+ return "OpenAI API key not configured. Please set it in File > Settings in the Blockly interface."
84
 
85
  # Get the chat context from the global variable
86
  global latest_blockly_chat_code
project/src/index.html CHANGED
@@ -18,7 +18,9 @@
18
  <div class="dropdown">
19
  <a href="#" id="newButton" class="dropdownItem" data-action="new">New</a>
20
  <a href="#" id="loadButton" class="dropdownItem" data-action="open">Open</a>
21
- <a href="#" id="saveButton" class="dropdownItem" data-action="download">Download</a>
 
 
22
  </div>
23
  </div>
24
 
@@ -189,6 +191,20 @@
189
  verticalResizer.addEventListener('pointerup', onVerticalPointerUp);
190
  });
191
  </script>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
192
  </body>
193
 
194
  </html>
 
18
  <div class="dropdown">
19
  <a href="#" id="newButton" class="dropdownItem" data-action="new">New</a>
20
  <a href="#" id="loadButton" class="dropdownItem" data-action="open">Open</a>
21
+ <a href="#" id="saveButton" class="dropdownItem" data-action="download">Download Project</a>
22
+ <a href="#" id="downloadCodeButton" class="dropdownItem" data-action="downloadCode">Download Code</a>
23
+ <a href="#" id="settingsButton" class="dropdownItem" data-action="downloadCode">API Key</a>
24
  </div>
25
  </div>
26
 
 
191
  verticalResizer.addEventListener('pointerup', onVerticalPointerUp);
192
  });
193
  </script>
194
+
195
+ <!-- API Key Modal -->
196
+ <div id="apiKeyModal" style="display: none; position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.5); z-index: 9999; align-items: center; justify-content: center;">
197
+ <div style="background: white; padding: 30px; border-radius: 10px; width: 90%; max-width: 500px; box-shadow: 0 10px 30px rgba(0,0,0,0.2);">
198
+ <h2 style="margin-top: 0; margin-bottom: 20px; color: #333;">Settings</h2>
199
+ <label for="apiKeyInput" style="display: block; margin-bottom: 10px; color: #666; font-size: 14px;">OpenAI API Key:</label>
200
+ <input type="password" id="apiKeyInput" style="width: 100%; padding: 10px; border: 1px solid #ddd; border-radius: 5px; font-size: 14px; box-sizing: border-box;" placeholder="sk-...">
201
+ <p style="margin-top: 10px; color: #999; font-size: 12px;">Your API key will be stored securely for this session.</p>
202
+ <div style="margin-top: 20px; display: flex; justify-content: flex-end; gap: 10px;">
203
+ <button id="cancelApiKey" style="padding: 10px 20px; background: #e5e7eb; border: none; border-radius: 5px; cursor: pointer; font-size: 14px;">Cancel</button>
204
+ <button id="saveApiKey" style="padding: 10px 20px; background: #6366f1; color: white; border: none; border-radius: 5px; cursor: pointer; font-size: 14px;">Save</button>
205
+ </div>
206
+ </div>
207
+ </div>
208
  </body>
209
 
210
  </html>
project/src/index.js CHANGED
@@ -84,7 +84,7 @@ saveButton.addEventListener("click", () => {
84
  const state = Blockly.serialization.workspaces.save(ws);
85
  const stateString = JSON.stringify(state);
86
 
87
- var filename = "mcpBlockly.txt";
88
  var element = document.createElement('a');
89
 
90
  element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(stateString));
@@ -95,6 +95,87 @@ saveButton.addEventListener("click", () => {
95
  document.body.removeChild(element);
96
  });
97
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
98
  const weatherText = `{"workspaceComments":[{"height":120,"width":479,"id":"XI5[EHp-Ow+kinXf6n5y","x":51.234375,"y":-83,"text":"Gets temperature of location with a latitude and a longitude.\\n\\nThe API requires a minimum of one decimal point to work."}],"blocks":{"languageVersion":0,"blocks":[{"type":"create_mcp","id":")N.HEG1x]Z/,k#TeWr,S","x":50,"y":50,"deletable":false,"extraState":{"inputCount":2,"inputNames":["latitude","longitude"],"inputTypes":["integer","integer"],"outputCount":1,"outputNames":["output0"],"outputTypes":["string"],"toolCount":0},"inputs":{"X0":{"block":{"type":"input_reference_latitude","id":"]3mj!y}qfRt+!okheU7L","deletable":false,"extraState":{"ownerBlockId":")N.HEG1x]Z/,k#TeWr,S"},"fields":{"VARNAME":"latitude"}}},"X1":{"block":{"type":"input_reference_longitude","id":"Do/{HFNGSd.!;POiKS?D","deletable":false,"extraState":{"ownerBlockId":")N.HEG1x]Z/,k#TeWr,S"},"fields":{"VARNAME":"longitude"}}},"R0":{"block":{"type":"in_json","id":"R|j?_8s^H{l0;UZ-oQt3","fields":{"NAME":"temperature_2m"},"inputs":{"JSON":{"block":{"type":"in_json","id":"X=M,R1@7bRjJVZIPi[qD","fields":{"NAME":"current"},"inputs":{"JSON":{"block":{"type":"call_api","id":"^(.vyM.yni08S~c1EBm=","fields":{"METHOD":"GET"},"inputs":{"URL":{"shadow":{"type":"text","id":"}.T;_U_OsRS)B_y09p % { ","fields":{"TEXT":""}},"block":{"type":"text_replace","id":"OwH9uERJPTGQG!UER#ch","inputs":{"FROM":{"shadow":{"type":"text","id":"ya05#^ 7 % UbUeXX#eDSmH","fields":{"TEXT":"{latitude}"}}},"TO":{"shadow":{"type":"text","id":": _ZloQuh9c-MNf-U]!k5","fields":{"TEXT":""}},"block":{"type":"input_reference_latitude","id":"?%@)3sErZ)}=#4ags#gu","extraState":{"ownerBlockId":")N.HEG1x]Z/,k#TeWr,S"},"fields":{"VARNAME":"latitude"}}},"TEXT":{"shadow":{"type":"text","id":"w@zsP)m6:WjkUp,ln3$x","fields":{"TEXT":""}},"block":{"type":"text_replace","id":"ImNPsvzD7r^+1MJ%IirV","inputs":{"FROM":{"shadow":{"type":"text","id":"%o(3rro?WLIFpmE0#MMM","fields":{"TEXT":"{longitude}"}}},"TO":{"shadow":{"type":"text","id":"Zpql-%oJ_sdSi | r |* er | ","fields":{"TEXT":""}},"block":{"type":"input_reference_longitude","id":"WUgiJP$X + zY#f$5nhnTX","extraState":{"ownerBlockId":") N.HEG1x]Z /, k#TeWr, S"},"fields":{"VARNAME":"longitude"}}},"TEXT":{"shadow":{"type":"text","id":", (vw$o_s7P = b4P; 8]}yj","fields":{"TEXT":"https://api.open-meteo.com/v1/forecast?latitude={latitude}&longitude={longitude}&current=temperature_2m,wind_speed_10m"}}}}}}}}}}}}}}}}}}}}]}}`;
99
  weatherButton.addEventListener("click", () => {
100
  try {
@@ -140,8 +221,13 @@ const updateCode = () => {
140
  const call = `def llm_call(prompt, model):
141
  from openai import OpenAI
142
  import os
143
-
144
- client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
 
 
 
 
 
145
 
146
  messages = [{"role": "user", "content": prompt}]
147
 
 
84
  const state = Blockly.serialization.workspaces.save(ws);
85
  const stateString = JSON.stringify(state);
86
 
87
+ var filename = "mcpBlockly_project.txt";
88
  var element = document.createElement('a');
89
 
90
  element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(stateString));
 
95
  document.body.removeChild(element);
96
  });
97
 
98
+ // Download Code button
99
+ const downloadCodeButton = document.querySelector('#downloadCodeButton');
100
+ downloadCodeButton.addEventListener("click", () => {
101
+ // Get the current generated code
102
+ const codeEl = document.querySelector('#generatedCode code');
103
+ const code = codeEl ? codeEl.textContent : '';
104
+
105
+ if (!code) {
106
+ alert('No code to download');
107
+ return;
108
+ }
109
+
110
+ var filename = "mcp_generated.py";
111
+ var element = document.createElement('a');
112
+
113
+ element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(code));
114
+ element.setAttribute('download', filename);
115
+ element.style.display = 'none';
116
+ document.body.appendChild(element);
117
+ element.click();
118
+ document.body.removeChild(element);
119
+ });
120
+
121
+ // Settings button and API Key Modal
122
+ const settingsButton = document.querySelector('#settingsButton');
123
+ const apiKeyModal = document.querySelector('#apiKeyModal');
124
+ const apiKeyInput = document.querySelector('#apiKeyInput');
125
+ const saveApiKeyButton = document.querySelector('#saveApiKey');
126
+ const cancelApiKeyButton = document.querySelector('#cancelApiKey');
127
+
128
+ settingsButton.addEventListener("click", () => {
129
+ apiKeyModal.style.display = 'flex';
130
+
131
+ // Load current API key from backend
132
+ fetch("http://127.0.0.1:7860/get_api_key", {
133
+ method: "GET",
134
+ })
135
+ .then(response => response.json())
136
+ .then(data => {
137
+ apiKeyInput.value = data.api_key || '';
138
+ })
139
+ .catch(err => {
140
+ console.error("Error loading API key:", err);
141
+ });
142
+ });
143
+
144
+ saveApiKeyButton.addEventListener("click", () => {
145
+ const apiKey = apiKeyInput.value;
146
+
147
+ // Save API key to both backend servers (test.py and chat.py)
148
+ Promise.all([
149
+ fetch("http://127.0.0.1:7860/set_api_key", {
150
+ method: "POST",
151
+ headers: { "Content-Type": "application/json" },
152
+ body: JSON.stringify({ api_key: apiKey }),
153
+ }),
154
+ fetch("http://127.0.0.1:7861/set_api_key_chat", {
155
+ method: "POST",
156
+ headers: { "Content-Type": "application/json" },
157
+ body: JSON.stringify({ api_key: apiKey }),
158
+ })
159
+ ])
160
+ .then(async (responses) => {
161
+ const results = await Promise.all(responses.map(r => r.json()));
162
+ if (results.every(r => r.success)) {
163
+ alert('API key saved successfully');
164
+ apiKeyModal.style.display = 'none';
165
+ } else {
166
+ alert('Failed to save API key to all services');
167
+ }
168
+ })
169
+ .catch(err => {
170
+ console.error("Error saving API key:", err);
171
+ alert('Failed to save API key');
172
+ });
173
+ });
174
+
175
+ cancelApiKeyButton.addEventListener("click", () => {
176
+ apiKeyModal.style.display = 'none';
177
+ });
178
+
179
  const weatherText = `{"workspaceComments":[{"height":120,"width":479,"id":"XI5[EHp-Ow+kinXf6n5y","x":51.234375,"y":-83,"text":"Gets temperature of location with a latitude and a longitude.\\n\\nThe API requires a minimum of one decimal point to work."}],"blocks":{"languageVersion":0,"blocks":[{"type":"create_mcp","id":")N.HEG1x]Z/,k#TeWr,S","x":50,"y":50,"deletable":false,"extraState":{"inputCount":2,"inputNames":["latitude","longitude"],"inputTypes":["integer","integer"],"outputCount":1,"outputNames":["output0"],"outputTypes":["string"],"toolCount":0},"inputs":{"X0":{"block":{"type":"input_reference_latitude","id":"]3mj!y}qfRt+!okheU7L","deletable":false,"extraState":{"ownerBlockId":")N.HEG1x]Z/,k#TeWr,S"},"fields":{"VARNAME":"latitude"}}},"X1":{"block":{"type":"input_reference_longitude","id":"Do/{HFNGSd.!;POiKS?D","deletable":false,"extraState":{"ownerBlockId":")N.HEG1x]Z/,k#TeWr,S"},"fields":{"VARNAME":"longitude"}}},"R0":{"block":{"type":"in_json","id":"R|j?_8s^H{l0;UZ-oQt3","fields":{"NAME":"temperature_2m"},"inputs":{"JSON":{"block":{"type":"in_json","id":"X=M,R1@7bRjJVZIPi[qD","fields":{"NAME":"current"},"inputs":{"JSON":{"block":{"type":"call_api","id":"^(.vyM.yni08S~c1EBm=","fields":{"METHOD":"GET"},"inputs":{"URL":{"shadow":{"type":"text","id":"}.T;_U_OsRS)B_y09p % { ","fields":{"TEXT":""}},"block":{"type":"text_replace","id":"OwH9uERJPTGQG!UER#ch","inputs":{"FROM":{"shadow":{"type":"text","id":"ya05#^ 7 % UbUeXX#eDSmH","fields":{"TEXT":"{latitude}"}}},"TO":{"shadow":{"type":"text","id":": _ZloQuh9c-MNf-U]!k5","fields":{"TEXT":""}},"block":{"type":"input_reference_latitude","id":"?%@)3sErZ)}=#4ags#gu","extraState":{"ownerBlockId":")N.HEG1x]Z/,k#TeWr,S"},"fields":{"VARNAME":"latitude"}}},"TEXT":{"shadow":{"type":"text","id":"w@zsP)m6:WjkUp,ln3$x","fields":{"TEXT":""}},"block":{"type":"text_replace","id":"ImNPsvzD7r^+1MJ%IirV","inputs":{"FROM":{"shadow":{"type":"text","id":"%o(3rro?WLIFpmE0#MMM","fields":{"TEXT":"{longitude}"}}},"TO":{"shadow":{"type":"text","id":"Zpql-%oJ_sdSi | r |* er | ","fields":{"TEXT":""}},"block":{"type":"input_reference_longitude","id":"WUgiJP$X + zY#f$5nhnTX","extraState":{"ownerBlockId":") N.HEG1x]Z /, k#TeWr, S"},"fields":{"VARNAME":"longitude"}}},"TEXT":{"shadow":{"type":"text","id":", (vw$o_s7P = b4P; 8]}yj","fields":{"TEXT":"https://api.open-meteo.com/v1/forecast?latitude={latitude}&longitude={longitude}&current=temperature_2m,wind_speed_10m"}}}}}}}}}}}}}}}}}}}}]}}`;
180
  weatherButton.addEventListener("click", () => {
181
  try {
 
221
  const call = `def llm_call(prompt, model):
222
  from openai import OpenAI
223
  import os
224
+
225
+ api_key = os.environ.get("OPENAI_API_KEY")
226
+
227
+ if not api_key:
228
+ return "Error: OpenAI API key not configured. Please set it in File > Settings"
229
+
230
+ client = OpenAI(api_key=api_key)
231
 
232
  messages = [{"role": "user", "content": prompt}]
233
 
project/test.py CHANGED
@@ -3,7 +3,7 @@ from fastapi.middleware.cors import CORSMiddleware
3
  import gradio as gr
4
  import uvicorn
5
  import re
6
- from dotenv import load_dotenv
7
 
8
  app = FastAPI()
9
 
@@ -15,9 +15,8 @@ app.add_middleware(
15
  allow_headers=["*"],
16
  )
17
 
18
- load_dotenv()
19
-
20
  latest_blockly_code = ""
 
21
 
22
 
23
  @app.post("/update_code")
@@ -28,12 +27,48 @@ async def update_code(request: Request):
28
  print("\n[FASTAPI] Updated Blockly code:\n", latest_blockly_code)
29
  return {"ok": True}
30
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
31
 
32
  def execute_blockly_logic(user_inputs):
33
- global latest_blockly_code
34
  if not latest_blockly_code.strip():
35
  return "No Blockly code available"
36
 
 
 
 
 
37
  result = ""
38
 
39
  # More comprehensive filtering of Gradio-related code
@@ -232,5 +267,5 @@ demo = build_interface()
232
  app = gr.mount_gradio_app(app, demo, path="/")
233
 
234
  if __name__ == "__main__":
235
- print("[BOOT] Running Gradio+FastAPI combo on http://127.0.0.1:8000")
236
- uvicorn.run(app, host="0.0.0.0", port=8000)
 
3
  import gradio as gr
4
  import uvicorn
5
  import re
6
+ import os
7
 
8
  app = FastAPI()
9
 
 
15
  allow_headers=["*"],
16
  )
17
 
 
 
18
  latest_blockly_code = ""
19
+ stored_api_key = "" # Store the API key in memory
20
 
21
 
22
  @app.post("/update_code")
 
27
  print("\n[FASTAPI] Updated Blockly code:\n", latest_blockly_code)
28
  return {"ok": True}
29
 
30
+ @app.get("/get_api_key")
31
+ async def get_api_key_endpoint():
32
+ """Get the current API key from memory"""
33
+ global stored_api_key
34
+ api_key = stored_api_key or os.environ.get("OPENAI_API_KEY", "")
35
+
36
+ # Mask the API key for security (show only first 7 and last 4 characters)
37
+ if api_key and len(api_key) > 15:
38
+ masked_key = api_key[:7] + '...' + api_key[-4:]
39
+ else:
40
+ masked_key = api_key if api_key else ""
41
+
42
+ return {"api_key": masked_key}
43
+
44
+ @app.post("/set_api_key")
45
+ async def set_api_key_endpoint(request: Request):
46
+ """Save API key to environment variable"""
47
+ global stored_api_key
48
+ data = await request.json()
49
+ api_key = data.get("api_key", "").strip()
50
+
51
+ try:
52
+ # Store in memory and set environment variable
53
+ stored_api_key = api_key
54
+ os.environ["OPENAI_API_KEY"] = api_key
55
+
56
+ print(f"[API KEY] Set OPENAI_API_KEY in environment")
57
+ return {"success": True}
58
+ except Exception as e:
59
+ print(f"Error setting API key: {e}")
60
+ return {"success": False, "error": str(e)}
61
+
62
 
63
  def execute_blockly_logic(user_inputs):
64
+ global latest_blockly_code, stored_api_key
65
  if not latest_blockly_code.strip():
66
  return "No Blockly code available"
67
 
68
+ # Ensure API key is set in environment before executing
69
+ if stored_api_key:
70
+ os.environ["OPENAI_API_KEY"] = stored_api_key
71
+
72
  result = ""
73
 
74
  # More comprehensive filtering of Gradio-related code
 
267
  app = gr.mount_gradio_app(app, demo, path="/")
268
 
269
  if __name__ == "__main__":
270
+ print("[BOOT] Running Gradio+FastAPI combo on http://127.0.0.1:7860")
271
+ uvicorn.run(app, host="0.0.0.0", port=7860)
project/webpack.config.js CHANGED
@@ -1,59 +1,43 @@
1
- const path = require('path');
2
  const HtmlWebpackPlugin = require('html-webpack-plugin');
 
3
 
4
- // Base config that applies to either development or production mode.
5
- const config = {
6
  entry: './src/index.js',
7
  output: {
8
- // Compile the source files into a bundle.
9
- filename: 'bundle.js',
10
  path: path.resolve(__dirname, 'dist'),
 
11
  clean: true,
12
  },
13
- // Enable webpack-dev-server to get hot refresh of the app.
14
- devServer: {
15
- static: './build',
16
- },
17
  module: {
18
  rules: [
19
  {
20
- // Load CSS files. They can be imported into JS files.
21
  test: /\.css$/i,
22
  use: ['style-loader', 'css-loader'],
23
  },
 
 
 
 
 
 
24
  ],
25
  },
26
  plugins: [
27
- // Generate the HTML index page based on our template.
28
- // This will output the same index page with the bundle we
29
- // created above added in a script tag.
30
  new HtmlWebpackPlugin({
31
- template: 'src/index.html',
32
  }),
33
  ],
34
- };
35
-
36
- module.exports = (env, argv) => {
37
- if (argv.mode === 'development') {
38
- // Set the output path to the `build` directory
39
- // so we don't clobber production builds.
40
- config.output.path = path.resolve(__dirname, 'build');
41
-
42
- // Generate source maps for our code for easier debugging.
43
- // Not suitable for production builds. If you want source maps in
44
- // production, choose a different one from https://webpack.js.org/configuration/devtool
45
- config.devtool = 'eval-cheap-module-source-map';
46
-
47
- // Include the source maps for Blockly for easier debugging Blockly code.
48
- config.module.rules.push({
49
- test: /(blockly\/.*\.js)$/,
50
- use: [require.resolve('source-map-loader')],
51
- enforce: 'pre',
52
- });
53
-
54
- // Ignore spurious warnings from source-map-loader
55
- // It can't find source maps for some Closure modules and that is expected
56
- config.ignoreWarnings = [/Failed to parse source map/];
57
- }
58
- return config;
59
- };
 
 
1
  const HtmlWebpackPlugin = require('html-webpack-plugin');
2
+ const path = require('path');
3
 
4
+ module.exports = {
 
5
  entry: './src/index.js',
6
  output: {
 
 
7
  path: path.resolve(__dirname, 'dist'),
8
+ filename: 'bundle.js',
9
  clean: true,
10
  },
 
 
 
 
11
  module: {
12
  rules: [
13
  {
 
14
  test: /\.css$/i,
15
  use: ['style-loader', 'css-loader'],
16
  },
17
+ {
18
+ test: /\.js$/,
19
+ exclude: /node_modules/, // Exclude all node_modules from source map processing
20
+ use: ["source-map-loader"],
21
+ enforce: "pre"
22
+ }
23
  ],
24
  },
25
  plugins: [
 
 
 
26
  new HtmlWebpackPlugin({
27
+ template: './src/index.html',
28
  }),
29
  ],
30
+ devServer: {
31
+ static: {
32
+ directory: path.join(__dirname, 'public'),
33
+ },
34
+ compress: true,
35
+ port: 8081,
36
+ hot: true,
37
+ open: true,
38
+ },
39
+ resolve: {
40
+ extensions: ['.js', '.json', '.css'],
41
+ },
42
+ devtool: 'source-map',
43
+ };