gstaff commited on
Commit
a8e9c78
β€’
1 Parent(s): 713a4fb

Add requirements support to gradio.

Browse files
app.py CHANGED
@@ -86,11 +86,11 @@ def transcribe(audio: str) -> (str, str):
86
  return result["text"], None
87
 
88
 
89
- def link_copy_notify(code):
90
  gr.Info("Share link copied!")
91
 
92
 
93
- def copy_notify(code):
94
  gr.Info("App code snippet copied!")
95
 
96
 
@@ -98,6 +98,15 @@ def add_hotkeys() -> str:
98
  return Path("hotkeys.js").read_text()
99
 
100
 
 
 
 
 
 
 
 
 
 
101
  with gr.Blocks() as demo:
102
  gr.Markdown("<h1 id=\"TEST\" align=\"center\"><a href=\"?\">KiteWind</a> πŸͺπŸƒ</h1>")
103
  gr.Markdown(
@@ -112,19 +121,24 @@ with gr.Blocks() as demo:
112
  with gr.Row():
113
  with gr.Column():
114
  with gr.Group():
115
- in_audio = gr.Audio(label="Record a voice request (click or press ctrl + ` to start/stop)", source='microphone', type='filepath', elem_classes=["record-btn"])
 
116
  in_prompt = gr.Textbox(label="Or type a text request and press Enter",
117
  placeholder="Need an idea? Try one of these:\n- Add a button to reverse the name\n- Change the greeting to Spanish\n- Put the reversed name output into a separate textbox")
118
  out_text = gr.TextArea(label="πŸ€– Chat Assistant Response")
119
  clear = gr.ClearButton([in_prompt, in_audio, out_text])
120
  with gr.Column():
121
- code_area = gr.Code(label="App Code - You can also edit directly and then click Update App or ctrl + space",
122
- language='python', value=starting_app_code(DemoType.GRADIO))
 
 
123
  update_btn = gr.Button("Update App (Ctrl + Space)", variant="primary", elem_classes=["update-btn"])
124
- code_update_params = {'fn': None, 'inputs': code_area, 'outputs': None,
 
 
125
  '_js': update_iframe_js(DemoType.GRADIO)}
126
- gen_text_params = {'fn': generate_text, 'inputs': [code_area, in_prompt],
127
- 'outputs': [out_text, code_area]}
128
  transcribe_params = {'fn': transcribe, 'inputs': [in_audio], 'outputs': [in_prompt, in_audio]}
129
  update_btn.click(**code_update_params)
130
  in_prompt.submit(**gen_text_params).then(**code_update_params)
@@ -133,11 +147,11 @@ with gr.Blocks() as demo:
133
  with gr.Column():
134
  gr.Markdown("## 3. Export your app to share!")
135
  share_link_btn = gr.Button("πŸ”— Copy share link to clipboard")
136
- share_link_btn.click(link_copy_notify, code_area, None, _js=copy_share_link_js(DemoType.GRADIO))
137
  copy_snippet_btn = gr.Button("βœ‚οΈ Copy app snippet to paste into another page")
138
- copy_snippet_btn.click(copy_notify, code_area, None, _js=copy_snippet_js(DemoType.GRADIO))
139
  download_btn = gr.Button("πŸ—Ž Download app as a standalone file")
140
- download_btn.click(None, code_area, None, _js=download_code_js(DemoType.GRADIO))
141
  with gr.Row():
142
  with gr.Column():
143
  gr.Markdown("## Current limitations")
@@ -153,19 +167,22 @@ with gr.Blocks() as demo:
153
  with gr.Row():
154
  with gr.Column():
155
  with gr.Group():
156
- in_audio = gr.Audio(label="Record a voice request (click or press ctrl + ` to start/stop)", source='microphone', type='filepath', elem_classes=["record-btn"])
 
157
  in_prompt = gr.Textbox(label="Or type a text request and press Enter",
158
- placeholder="Need an idea? Try one of these:\n- Add a button to reverse the name\n- Change the greeting to Spanish\n- Make the button primary")
159
  out_text = gr.TextArea(label="πŸ€– Chat Assistant Response")
160
  clear_btn = gr.ClearButton([in_prompt, in_audio, out_text])
161
  with gr.Column():
162
- code_area = gr.Code(label="App Code - You can also edit directly and then click Update App or ctrl + space",
163
- language='python', value=starting_app_code(DemoType.STREAMLIT))
 
 
164
  update_btn = gr.Button("Update App (Ctrl + Space)", variant="primary", elem_classes=["update-btn"])
165
- code_update_params = {'fn': None, 'inputs': code_area, 'outputs': None,
166
  '_js': update_iframe_js(DemoType.STREAMLIT)}
167
- gen_text_params = {'fn': generate_text, 'inputs': [code_area, in_prompt],
168
- 'outputs': [out_text, code_area]}
169
  transcribe_params = {'fn': transcribe, 'inputs': [in_audio], 'outputs': [in_prompt, in_audio]}
170
  update_btn.click(**code_update_params)
171
  in_prompt.submit(**gen_text_params).then(**code_update_params)
@@ -174,9 +191,9 @@ with gr.Blocks() as demo:
174
  with gr.Column():
175
  gr.Markdown("## 3. Export your app to share!")
176
  copy_snippet_btn = gr.Button("βœ‚οΈ Copy app snippet into paste in another page")
177
- copy_snippet_btn.click(copy_notify, code_area, None, _js=copy_snippet_js(DemoType.STREAMLIT))
178
  download_btn = gr.Button("πŸ—Ž Download app as a standalone file")
179
- download_btn.click(None, code_area, None, _js=download_code_js(DemoType.STREAMLIT))
180
  with gr.Row():
181
  with gr.Column():
182
  gr.Markdown("## Current limitations")
@@ -188,6 +205,8 @@ with gr.Blocks() as demo:
188
  stlite_tab.select(lambda: "stlite", None, selectedTab).then(None, None, None, _js=load_js(DemoType.STREAMLIT))
189
  demo.load(None, None, None, _js=load_js(DemoType.GRADIO))
190
  demo.load(None, None, None, _js=add_hotkeys())
 
 
191
  demo.css = "footer {visibility: hidden}"
192
 
193
  if __name__ == "__main__":
 
86
  return result["text"], None
87
 
88
 
89
+ def link_copy_notify():
90
  gr.Info("Share link copied!")
91
 
92
 
93
+ def copy_notify(code: str, requirements: str):
94
  gr.Info("App code snippet copied!")
95
 
96
 
 
98
  return Path("hotkeys.js").read_text()
99
 
100
 
101
+ def apply_query_params(code: str, request: gr.Request) -> (str, str):
102
+ params = dict(request.query_params)
103
+ return params.get('code') or code, params.get('requirements') or ''
104
+
105
+
106
+ def update_state(requirements: [str], error: str):
107
+ return '\n'.join(sorted(requirements)), error
108
+
109
+
110
  with gr.Blocks() as demo:
111
  gr.Markdown("<h1 id=\"TEST\" align=\"center\"><a href=\"?\">KiteWind</a> πŸͺπŸƒ</h1>")
112
  gr.Markdown(
 
121
  with gr.Row():
122
  with gr.Column():
123
  with gr.Group():
124
+ in_audio = gr.Audio(label="Record a voice request (click or press ctrl + ` to start/stop)",
125
+ source='microphone', type='filepath', elem_classes=["record-btn"])
126
  in_prompt = gr.Textbox(label="Or type a text request and press Enter",
127
  placeholder="Need an idea? Try one of these:\n- Add a button to reverse the name\n- Change the greeting to Spanish\n- Put the reversed name output into a separate textbox")
128
  out_text = gr.TextArea(label="πŸ€– Chat Assistant Response")
129
  clear = gr.ClearButton([in_prompt, in_audio, out_text])
130
  with gr.Column():
131
+ gradio_code_area = gr.Code(
132
+ label="App Code - You can also edit directly and then click Update App or ctrl + space",
133
+ language='python', value=starting_app_code(DemoType.GRADIO))
134
+ gradio_requirements_area = gr.Code(label="App Requirements (additional modules pip installed for pyodide)")
135
  update_btn = gr.Button("Update App (Ctrl + Space)", variant="primary", elem_classes=["update-btn"])
136
+ last_error = gr.State()
137
+ code_update_params = {'fn': update_state, 'inputs': [gradio_code_area, gradio_requirements_area],
138
+ 'outputs': [gradio_requirements_area, last_error],
139
  '_js': update_iframe_js(DemoType.GRADIO)}
140
+ gen_text_params = {'fn': generate_text, 'inputs': [gradio_code_area, in_prompt],
141
+ 'outputs': [out_text, gradio_code_area]}
142
  transcribe_params = {'fn': transcribe, 'inputs': [in_audio], 'outputs': [in_prompt, in_audio]}
143
  update_btn.click(**code_update_params)
144
  in_prompt.submit(**gen_text_params).then(**code_update_params)
 
147
  with gr.Column():
148
  gr.Markdown("## 3. Export your app to share!")
149
  share_link_btn = gr.Button("πŸ”— Copy share link to clipboard")
150
+ share_link_btn.click(link_copy_notify, None, None, _js=copy_share_link_js(DemoType.GRADIO))
151
  copy_snippet_btn = gr.Button("βœ‚οΈ Copy app snippet to paste into another page")
152
+ copy_snippet_btn.click(copy_notify, [gradio_code_area, gradio_requirements_area], None, _js=copy_snippet_js(DemoType.GRADIO))
153
  download_btn = gr.Button("πŸ—Ž Download app as a standalone file")
154
+ download_btn.click(None, [gradio_code_area, gradio_requirements_area], None, _js=download_code_js(DemoType.GRADIO))
155
  with gr.Row():
156
  with gr.Column():
157
  gr.Markdown("## Current limitations")
 
167
  with gr.Row():
168
  with gr.Column():
169
  with gr.Group():
170
+ in_audio = gr.Audio(label="Record a voice request (click or press ctrl + ` to start/stop)",
171
+ source='microphone', type='filepath', elem_classes=["record-btn"])
172
  in_prompt = gr.Textbox(label="Or type a text request and press Enter",
173
+ placeholder="Need an idea? Try one of these:\n- Add a button to reverse the name\n- Change the greeting to Spanish\n- Change the theme to soft")
174
  out_text = gr.TextArea(label="πŸ€– Chat Assistant Response")
175
  clear_btn = gr.ClearButton([in_prompt, in_audio, out_text])
176
  with gr.Column():
177
+ stlite_code_area = gr.Code(
178
+ label="App Code - You can also edit directly and then click Update App or ctrl + space",
179
+ language='python', value=starting_app_code(DemoType.STREAMLIT))
180
+ requirements_area = gr.Code(label="App Requirements (additional modules pip installed for pyodide)")
181
  update_btn = gr.Button("Update App (Ctrl + Space)", variant="primary", elem_classes=["update-btn"])
182
+ code_update_params = {'fn': None, 'inputs': [stlite_code_area, requirements_area], 'outputs': None,
183
  '_js': update_iframe_js(DemoType.STREAMLIT)}
184
+ gen_text_params = {'fn': generate_text, 'inputs': [stlite_code_area, in_prompt],
185
+ 'outputs': [out_text, stlite_code_area]}
186
  transcribe_params = {'fn': transcribe, 'inputs': [in_audio], 'outputs': [in_prompt, in_audio]}
187
  update_btn.click(**code_update_params)
188
  in_prompt.submit(**gen_text_params).then(**code_update_params)
 
191
  with gr.Column():
192
  gr.Markdown("## 3. Export your app to share!")
193
  copy_snippet_btn = gr.Button("βœ‚οΈ Copy app snippet into paste in another page")
194
+ copy_snippet_btn.click(copy_notify, [stlite_code_area, requirements_area], None, _js=copy_snippet_js(DemoType.STREAMLIT))
195
  download_btn = gr.Button("πŸ—Ž Download app as a standalone file")
196
+ download_btn.click(None, stlite_code_area, None, _js=download_code_js(DemoType.STREAMLIT))
197
  with gr.Row():
198
  with gr.Column():
199
  gr.Markdown("## Current limitations")
 
205
  stlite_tab.select(lambda: "stlite", None, selectedTab).then(None, None, None, _js=load_js(DemoType.STREAMLIT))
206
  demo.load(None, None, None, _js=load_js(DemoType.GRADIO))
207
  demo.load(None, None, None, _js=add_hotkeys())
208
+ # TODO: select stlite tab and populate that code based on query param type
209
+ demo.load(apply_query_params, gradio_code_area, [gradio_code_area, gradio_requirements_area])
210
  demo.css = "footer {visibility: hidden}"
211
 
212
  if __name__ == "__main__":
templates.py CHANGED
@@ -49,6 +49,7 @@ def load_js(demo_type: DemoType) -> str:
49
  const queryParams = parseQueryString(queryString);
50
  // Access individual parameters
51
  const codeValue = queryParams.code;
 
52
 
53
  const htmlString = '<iframe id="gradio-iframe" width="100%" height="512px" src="about:blank"></iframe>';
54
  const parser = new DOMParser();
@@ -59,8 +60,9 @@ def load_js(demo_type: DemoType) -> str:
59
 
60
  let template = `{gradio_lite_html_template.replace('STARTING_CODE', starting_app_code(demo_type))}`;
61
  if (codeValue) {{
62
- template = `{gradio_lite_html_template}`.replace('STARTING_CODE', codeValue);
63
  }}
 
64
  const frame = document.getElementById('gradio-iframe');
65
  frame.contentWindow.document.open('text/html', 'replace');
66
  frame.contentWindow.document.write(template);
@@ -91,7 +93,11 @@ def load_js(demo_type: DemoType) -> str:
91
 
92
  def update_iframe_js(demo_type: DemoType) -> str:
93
  if demo_type == DemoType.GRADIO:
94
- return f"""async (code, requirements) => {{
 
 
 
 
95
  async function update() {{
96
  // Remove existing stylesheet so it will be reloaded;
97
  // see https://github.com/gradio-app/gradio/blob/200237d73c169f39514465efc163db756969d3ac/js/app/src/lite/css.ts#L41
@@ -101,28 +107,55 @@ def update_iframe_js(demo_type: DemoType) -> str:
101
  const appController = demoFrameWindow.window.appController;
102
  const newCode = code + ` # Update tag ${{Math.random()}}`;
103
  try {{
 
104
  await appController.run_code(newCode);
105
  }}
106
- catch (e) {{
107
  // Replace old style if code error prevented new style from loading.
108
  const newStyle = demoFrameWindow.document.querySelector("head style");
109
  if (!newStyle) {{
110
  demoFrameWindow.document.head.appendChild(oldStyle);
111
  }}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
112
  // Hide app so the error traceback is visible.
113
  // First div in main is the error traceback, second is the app.
114
  const appBody = demoFrameWindow.document.querySelectorAll("div.main > div")[1];
115
  appBody.style.visibility = "hidden";
116
- return e;
 
 
 
117
  }}
118
  }};
119
  await update();
120
 
 
121
  // Update URL query params to include the current demo code state
122
  const currentUrl = new URL(window.location.href);
 
123
  currentUrl.searchParams.set('code', code);
124
  // Replace the current URL with the updated one
125
  history.replaceState({{}}, '', currentUrl.href);
 
 
126
  }}"""
127
  elif demo_type == DemoType.STREAMLIT:
128
  return f"""async (code, requirements) => {{
@@ -139,7 +172,7 @@ def update_iframe_js(demo_type: DemoType) -> str:
139
 
140
  def copy_share_link_js(demo_type: DemoType) -> str:
141
  if demo_type == DemoType.GRADIO:
142
- return f"""async (code) => {{
143
  const shareLink = document.URL;
144
  await navigator.clipboard.writeText(shareLink);
145
  }}"""
@@ -148,14 +181,14 @@ def copy_share_link_js(demo_type: DemoType) -> str:
148
 
149
  def copy_snippet_js(demo_type: DemoType) -> str:
150
  if demo_type == DemoType.GRADIO:
151
- return f"""async (code) => {{
152
- const escapedCode = code.replace('`', String.fromCharCode(92) + '`');
153
  const template = `{gradio_lite_snippet_template}`;
154
  // Step 1: Generate the HTML content
155
- const completedTemplate = template.replace('STARTING_CODE', code);
156
-
157
  const snippet = completedTemplate;
158
  await navigator.clipboard.writeText(snippet);
 
159
  }}"""
160
  elif demo_type == DemoType.STREAMLIT:
161
  return f"""async (code) => {{
@@ -172,10 +205,10 @@ def copy_snippet_js(demo_type: DemoType) -> str:
172
 
173
  def download_code_js(demo_type: DemoType) -> str:
174
  if demo_type == demo_type.GRADIO:
175
- return f"""(code) => {{
176
- const escapedCode = code.replace('`', String.fromCharCode(92) + '`');
177
  // Step 1: Generate the HTML content
178
- const completedTemplate = `{gradio_lite_html_template}`.replace('STARTING_CODE', escapedCode);
179
 
180
  // Step 2: Create a Blob from the HTML content
181
  const blob = new Blob([completedTemplate], {{ type: "text/html" }});
 
49
  const queryParams = parseQueryString(queryString);
50
  // Access individual parameters
51
  const codeValue = queryParams.code;
52
+ const requirementsValue = queryParams.requirements;
53
 
54
  const htmlString = '<iframe id="gradio-iframe" width="100%" height="512px" src="about:blank"></iframe>';
55
  const parser = new DOMParser();
 
60
 
61
  let template = `{gradio_lite_html_template.replace('STARTING_CODE', starting_app_code(demo_type))}`;
62
  if (codeValue) {{
63
+ template = `{gradio_lite_html_template}`.replace('STARTING_CODE', codeValue.replace(String.fromCharCode(92), String.fromCharCode(92) + String.fromCharCode(92)).replace('`', String.fromCharCode(92) + '`'));
64
  }}
65
+ template = template.replace('STARTING_REQUIREMENTS', requirementsValue || '');
66
  const frame = document.getElementById('gradio-iframe');
67
  frame.contentWindow.document.open('text/html', 'replace');
68
  frame.contentWindow.document.write(template);
 
93
 
94
  def update_iframe_js(demo_type: DemoType) -> str:
95
  if demo_type == DemoType.GRADIO:
96
+ return f"""async (code, requirements) => {{
97
+ const formattedRequirements = requirements.split('\\n').filter(x => x && !x.startsWith('#')).map(x => x.trim());
98
+ let errorResult = null;
99
+ const attemptedRequirements = new Set();
100
+ const installedRequirements = [];
101
  async function update() {{
102
  // Remove existing stylesheet so it will be reloaded;
103
  // see https://github.com/gradio-app/gradio/blob/200237d73c169f39514465efc163db756969d3ac/js/app/src/lite/css.ts#L41
 
107
  const appController = demoFrameWindow.window.appController;
108
  const newCode = code + ` # Update tag ${{Math.random()}}`;
109
  try {{
110
+ await appController.install(formattedRequirements);
111
  await appController.run_code(newCode);
112
  }}
113
+ catch (e) {{
114
  // Replace old style if code error prevented new style from loading.
115
  const newStyle = demoFrameWindow.document.querySelector("head style");
116
  if (!newStyle) {{
117
  demoFrameWindow.document.head.appendChild(oldStyle);
118
  }}
119
+
120
+ // If the error is caused by a missing module try once to install it and update again.
121
+ if (e.toString().includes('ModuleNotFoundError')) {{
122
+ try {{
123
+ const guessedModuleName = e.toString().split("'")[1].replaceAll('_', '-');
124
+ if (attemptedRequirements.has(guessedModuleName)) {{
125
+ throw Error(`Could not install pyodide module ${{guessedModuleName}}`);
126
+ }}
127
+ console.log(`Attempting to install missing pyodide module "${{guessedModuleName}}"`);
128
+ attemptedRequirements.add(guessedModuleName);
129
+ await appController.install([guessedModuleName]);
130
+ installedRequirements.push(guessedModuleName);
131
+ return await update();
132
+ }}
133
+ catch (err) {{
134
+ console.log(err);
135
+ }}
136
+ }}
137
+
138
  // Hide app so the error traceback is visible.
139
  // First div in main is the error traceback, second is the app.
140
  const appBody = demoFrameWindow.document.querySelectorAll("div.main > div")[1];
141
  appBody.style.visibility = "hidden";
142
+ errorResult = e.toString();
143
+ const allRequirements = formattedRequirements.concat(installedRequirements);
144
+ console.log(allRequirements, errorResult);
145
+ return [allRequirements, errorResult];
146
  }}
147
  }};
148
  await update();
149
 
150
+ const allRequirements = formattedRequirements.concat(installedRequirements);
151
  // Update URL query params to include the current demo code state
152
  const currentUrl = new URL(window.location.href);
153
+ currentUrl.searchParams.set('requirements', allRequirements.join('\\n'));
154
  currentUrl.searchParams.set('code', code);
155
  // Replace the current URL with the updated one
156
  history.replaceState({{}}, '', currentUrl.href);
157
+
158
+ return [allRequirements, errorResult];
159
  }}"""
160
  elif demo_type == DemoType.STREAMLIT:
161
  return f"""async (code, requirements) => {{
 
172
 
173
  def copy_share_link_js(demo_type: DemoType) -> str:
174
  if demo_type == DemoType.GRADIO:
175
+ return f"""async () => {{
176
  const shareLink = document.URL;
177
  await navigator.clipboard.writeText(shareLink);
178
  }}"""
 
181
 
182
  def copy_snippet_js(demo_type: DemoType) -> str:
183
  if demo_type == DemoType.GRADIO:
184
+ return f"""async (code, requirements) => {{
185
+ const escapedCode = code.replace(String.fromCharCode(92), String.fromCharCode(92) + String.fromCharCode(92)).replace('`', String.fromCharCode(92) + '`');
186
  const template = `{gradio_lite_snippet_template}`;
187
  // Step 1: Generate the HTML content
188
+ const completedTemplate = template.replace('STARTING_CODE', code).replace('STARTING_REQUIREMENTS', requirements);
 
189
  const snippet = completedTemplate;
190
  await navigator.clipboard.writeText(snippet);
191
+ return [code, requirements];
192
  }}"""
193
  elif demo_type == DemoType.STREAMLIT:
194
  return f"""async (code) => {{
 
205
 
206
  def download_code_js(demo_type: DemoType) -> str:
207
  if demo_type == demo_type.GRADIO:
208
+ return f"""(code, requirements) => {{
209
+ const escapedCode = code.replace(String.fromCharCode(92), String.fromCharCode(92) + String.fromCharCode(92)).replace('`', String.fromCharCode(92) + '`');
210
  // Step 1: Generate the HTML content
211
+ const completedTemplate = `{gradio_lite_html_template}`.replace('STARTING_CODE', escapedCode).replace('STARTING_REQUIREMENTS', requirements);
212
 
213
  // Step 2: Create a Blob from the HTML content
214
  const blob = new Blob([completedTemplate], {{ type: "text/html" }});
templates/gradio-lite/gradio-lite-snippet-template.html CHANGED
@@ -7,6 +7,7 @@
7
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@gradio/lite/dist/lite.css" />
8
  <script type="module">
9
  const code = \\\`STARTING_CODE\\\`;
 
10
  const appController = createGradioApp({
11
  target: document.getElementById("gradio-app"),
12
  code: code,
@@ -21,6 +22,8 @@
21
  appMode: true
22
  });
23
  window.appController = appController;
 
 
24
  <\\\/script>\`
25
  const frame = document.getElementById('demo-iframe');
26
  frame.contentWindow.document.open();
 
7
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@gradio/lite/dist/lite.css" />
8
  <script type="module">
9
  const code = \\\`STARTING_CODE\\\`;
10
+ const requirements = \\\`STARTING_REQUIREMENTS\\\`.split('\\\\n').filter(x => x && !x.startsWith('#')).map(x => x.trim());
11
  const appController = createGradioApp({
12
  target: document.getElementById("gradio-app"),
13
  code: code,
 
22
  appMode: true
23
  });
24
  window.appController = appController;
25
+ await appController.install(requirements);
26
+ await appController.run_code(code);
27
  <\\\/script>\`
28
  const frame = document.getElementById('demo-iframe');
29
  frame.contentWindow.document.open();
templates/gradio-lite/gradio-lite-template.html CHANGED
@@ -9,6 +9,7 @@
9
  <div id="gradio-app"></div>
10
  <script type="module">
11
  const code = \`STARTING_CODE\`;
 
12
  // Mount options defined here: https://github.com/gradio-app/gradio/blob/1d986217f6f4fc1829e528d2afe365635788204f/js/lite/index.html#L27
13
  const appController = createGradioApp({
14
  target: document.getElementById("gradio-app"),
@@ -26,6 +27,8 @@
26
  // Returned controller functions defined here:
27
  // https://github.com/gradio-app/gradio/blob/1d986217f6f4fc1829e528d2afe365635788204f/js/app/src/lite/index.ts#L165
28
  window.appController = appController;
 
 
29
  </script>
30
  </body>
31
  </html>
 
9
  <div id="gradio-app"></div>
10
  <script type="module">
11
  const code = \`STARTING_CODE\`;
12
+ const requirements = \`STARTING_REQUIREMENTS\`.split('\\n').filter(x => x && !x.startsWith('#')).map(x => x.trim());
13
  // Mount options defined here: https://github.com/gradio-app/gradio/blob/1d986217f6f4fc1829e528d2afe365635788204f/js/lite/index.html#L27
14
  const appController = createGradioApp({
15
  target: document.getElementById("gradio-app"),
 
27
  // Returned controller functions defined here:
28
  // https://github.com/gradio-app/gradio/blob/1d986217f6f4fc1829e528d2afe365635788204f/js/app/src/lite/index.ts#L165
29
  window.appController = appController;
30
+ await appController.install(requirements);
31
+ await appController.run_code(code);
32
  </script>
33
  </body>
34
  </html>