broadfield-dev commited on
Commit
2d24a3b
·
verified ·
1 Parent(s): 645101a

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +11 -18
app.py CHANGED
@@ -77,7 +77,7 @@ def parse_endpoint():
77
  except Exception as e:
78
  return jsonify({'error': f'Failed to parse: {e}'}), 500
79
 
80
- # --- HTML & PNG BUILDER (Unchanged but correct logic) ---
81
  def build_full_html(markdown_text, styles, include_fontawesome):
82
  wrapper_id = "#output-wrapper"
83
  font_family = styles.get('font_family', "'Arial', sans-serif")
@@ -97,8 +97,6 @@ def build_full_html(markdown_text, styles, include_fontawesome):
97
  font-family: {font_family}; font-size: {styles.get('font_size', '16')}px;
98
  color: {styles.get('text_color', '#333')}; background-color: {styles.get('background_color', '#fff')};
99
  }}
100
- /* ... other scoped styles ... */
101
-
102
  {wrapper_id} table {{ border-collapse: collapse; width: 100%; }}
103
  {wrapper_id} th, {wrapper_id} td {{ border: 1px solid #ddd; padding: 8px; text-align: left; }}
104
  {wrapper_id} th {{ background-color: #f2f2f2; }}
@@ -127,7 +125,7 @@ def build_full_html(markdown_text, styles, include_fontawesome):
127
  @app.route('/convert', methods=['POST'])
128
  def convert_endpoint():
129
  data = request.json
130
- temp_html_path = None # Initialize path
131
  try:
132
  full_html = build_full_html(
133
  markdown_text=data.get('markdown_text', ''),
@@ -135,10 +133,14 @@ def convert_endpoint():
135
  include_fontawesome=data.get('include_fontawesome', False)
136
  )
137
 
138
- # Define options for wkhtmltoimage, removing the unsupported --no-cache
139
- options = {"quiet": "", 'encoding': "UTF-8"}
 
 
 
 
 
140
 
141
- # --- REQUIRED CHANGE 2: Manually create and use a temporary file ---
142
  temp_html_path = os.path.join(TEMP_DIR, f"{uuid.uuid4()}.html")
143
  with open(temp_html_path, "w", encoding="utf-8") as f:
144
  f.write(full_html)
@@ -148,11 +150,9 @@ def convert_endpoint():
148
  if download_type == 'html':
149
  return send_file(BytesIO(full_html.encode("utf-8")), as_attachment=True, download_name="output.html", mimetype="text/html")
150
  else:
151
- # Use from_file instead of from_string
152
  png_bytes = imgkit.from_file(temp_html_path, False, options=options)
153
  return send_file(BytesIO(png_bytes), as_attachment=True, download_name="output.png", mimetype="image/png")
154
  else:
155
- # Use from_file instead of from_string
156
  png_bytes = imgkit.from_file(temp_html_path, False, options=options)
157
  png_base64 = base64.b64encode(png_bytes).decode('utf-8')
158
  return jsonify({'preview_html': full_html, 'preview_png_base64': png_base64})
@@ -161,11 +161,10 @@ def convert_endpoint():
161
  traceback.print_exc()
162
  return jsonify({'error': f'Failed to convert content: {str(e)}'}), 500
163
  finally:
164
- # --- REQUIRED CHANGE 3: Clean up the temporary file ---
165
  if temp_html_path and os.path.exists(temp_html_path):
166
  os.remove(temp_html_path)
167
 
168
- # --- MAIN PAGE RENDERER (with corrected CSS) ---
169
  @app.route('/')
170
  def index():
171
  highlight_styles = sorted(list(get_all_styles()))
@@ -197,14 +196,13 @@ def index():
197
  .component-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: 15px; }
198
  .component-container { border: 1px solid #e0e0e0; border-radius: 5px; background: #fafafa; }
199
  .component-header { background: #f1f1f1; padding: 8px 12px; border-bottom: 1px solid #e0e0e0; display: flex; align-items: center; gap: 10px; }
200
- .component-content textarea { height: 150px; } /* <-- FIXED: Taller text area */
201
  .selection-controls { margin: 15px 0; display: flex; gap: 10px; }
202
  </style>
203
  </head>
204
  <body>
205
  <h1>Intelligent Markdown Converter</h1>
206
  <form id="main-form" onsubmit="return false;">
207
- <!-- Input and Styling sections -->
208
  <fieldset><legend>1. Load Content</legend><div id="info-box" class="info"></div><textarea id="markdown-text-input" name="markdown_text" rows="8"></textarea><div style="margin-top: 10px; display: flex; align-items: center; gap: 10px;"><label for="markdown-file-input">Or upload a file:</label><input type="file" id="markdown-file-input" name="markdown_file" accept=".md,.txt,text/markdown"></div><div style="margin-top: 15px;"><button type="button" id="load-btn" class="action-btn">Load & Analyze</button></div></fieldset>
209
  <fieldset id="components-fieldset" style="display:none;"><legend>2. Select Components</legend><div class="selection-controls"><button type="button" onclick="toggleAllComponents(true)">Select All</button><button type="button" onclick="toggleAllComponents(false)">Deselect All</button></div><div id="components-container" class="component-grid"></div></fieldset>
210
  <fieldset><legend>3. Configure Styles</legend><div class="style-grid"><div><label>Font Family:</label><select id="font_family"><optgroup label="Sans-Serif"><option value="'Arial', sans-serif">Arial</option><option value="'Roboto', sans-serif">Roboto</option></optgroup><optgroup label="Serif"><option value="'Times New Roman', serif">Times New Roman</option><option value="'Georgia', serif">Georgia</option></optgroup></select></div><div><label>Font Size (px):</label><input type="number" id="font_size" value="16"></div><div><label>Highlight Theme:</label><select id="highlight_theme"><option value="none">None</option>{% for style in highlight_styles %}<option value="{{ style }}" {% if style == 'default' %}selected{% endif %}>{{ style }}</option>{% endfor %}</select></div><div><label>Text Color:</label><input type="color" id="text_color" value="#333333"></div><div><label>Background Color:</label><input type="color" id="background_color" value="#ffffff"></div><div><label>Code Padding (px):</label><input type="number" id="code_padding" value="15"></div></div><div><input type="checkbox" id="include_fontawesome"><label for="include_fontawesome">Include Font Awesome</label></div><div><label for="custom_css">Custom CSS:</label><textarea id="custom_css" rows="3"></textarea></div></fieldset>
@@ -227,8 +225,6 @@ def index():
227
  <div id="png-preview-container" class="preview-container"></div>
228
  </div>
229
  <script>
230
- // --- All JavaScript is unchanged from the previous correct version ---
231
- // It correctly gathers style info without modifying the parent page.
232
  const loadBtn = document.getElementById('load-btn'), generateBtn = document.getElementById('generate-btn'),
233
  downloadHtmlBtn = document.getElementById('download-html-btn'), downloadPngBtn = document.getElementById('download-png-btn'),
234
  markdownTextInput = document.getElementById('markdown-text-input'), markdownFileInput = document.getElementById('markdown-file-input'),
@@ -258,7 +254,6 @@ def index():
258
  include_fontawesome: document.getElementById('include_fontawesome').checked,
259
  };
260
  }
261
- loadBtn.addEventListener('click', async () => { /* Logic unchanged */ });
262
  generateBtn.addEventListener('click', async () => {
263
  generateBtn.textContent = 'Generating...'; generateBtn.disabled = true; errorBox.style.display = 'none';
264
  const payload = buildPayload();
@@ -318,6 +313,4 @@ def index():
318
  """, highlight_styles=highlight_styles)
319
 
320
  if __name__ == "__main__":
321
- # Ensure you have installed the required libraries:
322
- # pip install Flask markdown imgkit pygments
323
  app.run(host="0.0.0.0", port=int(os.environ.get("PORT", 7860)))
 
77
  except Exception as e:
78
  return jsonify({'error': f'Failed to parse: {e}'}), 500
79
 
80
+ # --- HTML & PNG BUILDER (Unchanged) ---
81
  def build_full_html(markdown_text, styles, include_fontawesome):
82
  wrapper_id = "#output-wrapper"
83
  font_family = styles.get('font_family', "'Arial', sans-serif")
 
97
  font-family: {font_family}; font-size: {styles.get('font_size', '16')}px;
98
  color: {styles.get('text_color', '#333')}; background-color: {styles.get('background_color', '#fff')};
99
  }}
 
 
100
  {wrapper_id} table {{ border-collapse: collapse; width: 100%; }}
101
  {wrapper_id} th, {wrapper_id} td {{ border: 1px solid #ddd; padding: 8px; text-align: left; }}
102
  {wrapper_id} th {{ background-color: #f2f2f2; }}
 
125
  @app.route('/convert', methods=['POST'])
126
  def convert_endpoint():
127
  data = request.json
128
+ temp_html_path = None
129
  try:
130
  full_html = build_full_html(
131
  markdown_text=data.get('markdown_text', ''),
 
133
  include_fontawesome=data.get('include_fontawesome', False)
134
  )
135
 
136
+ # *** THIS IS THE ONLY CHANGE IN THIS FILE ***
137
+ # Add the 'xvfb' option to tell imgkit to use the virtual display.
138
+ options = {
139
+ "quiet": "",
140
+ 'encoding': "UTF-8",
141
+ 'xvfb': ''
142
+ }
143
 
 
144
  temp_html_path = os.path.join(TEMP_DIR, f"{uuid.uuid4()}.html")
145
  with open(temp_html_path, "w", encoding="utf-8") as f:
146
  f.write(full_html)
 
150
  if download_type == 'html':
151
  return send_file(BytesIO(full_html.encode("utf-8")), as_attachment=True, download_name="output.html", mimetype="text/html")
152
  else:
 
153
  png_bytes = imgkit.from_file(temp_html_path, False, options=options)
154
  return send_file(BytesIO(png_bytes), as_attachment=True, download_name="output.png", mimetype="image/png")
155
  else:
 
156
  png_bytes = imgkit.from_file(temp_html_path, False, options=options)
157
  png_base64 = base64.b64encode(png_bytes).decode('utf-8')
158
  return jsonify({'preview_html': full_html, 'preview_png_base64': png_base64})
 
161
  traceback.print_exc()
162
  return jsonify({'error': f'Failed to convert content: {str(e)}'}), 500
163
  finally:
 
164
  if temp_html_path and os.path.exists(temp_html_path):
165
  os.remove(temp_html_path)
166
 
167
+ # --- MAIN PAGE RENDERER (Unchanged) ---
168
  @app.route('/')
169
  def index():
170
  highlight_styles = sorted(list(get_all_styles()))
 
196
  .component-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: 15px; }
197
  .component-container { border: 1px solid #e0e0e0; border-radius: 5px; background: #fafafa; }
198
  .component-header { background: #f1f1f1; padding: 8px 12px; border-bottom: 1px solid #e0e0e0; display: flex; align-items: center; gap: 10px; }
199
+ .component-content textarea { height: 150px; }
200
  .selection-controls { margin: 15px 0; display: flex; gap: 10px; }
201
  </style>
202
  </head>
203
  <body>
204
  <h1>Intelligent Markdown Converter</h1>
205
  <form id="main-form" onsubmit="return false;">
 
206
  <fieldset><legend>1. Load Content</legend><div id="info-box" class="info"></div><textarea id="markdown-text-input" name="markdown_text" rows="8"></textarea><div style="margin-top: 10px; display: flex; align-items: center; gap: 10px;"><label for="markdown-file-input">Or upload a file:</label><input type="file" id="markdown-file-input" name="markdown_file" accept=".md,.txt,text/markdown"></div><div style="margin-top: 15px;"><button type="button" id="load-btn" class="action-btn">Load & Analyze</button></div></fieldset>
207
  <fieldset id="components-fieldset" style="display:none;"><legend>2. Select Components</legend><div class="selection-controls"><button type="button" onclick="toggleAllComponents(true)">Select All</button><button type="button" onclick="toggleAllComponents(false)">Deselect All</button></div><div id="components-container" class="component-grid"></div></fieldset>
208
  <fieldset><legend>3. Configure Styles</legend><div class="style-grid"><div><label>Font Family:</label><select id="font_family"><optgroup label="Sans-Serif"><option value="'Arial', sans-serif">Arial</option><option value="'Roboto', sans-serif">Roboto</option></optgroup><optgroup label="Serif"><option value="'Times New Roman', serif">Times New Roman</option><option value="'Georgia', serif">Georgia</option></optgroup></select></div><div><label>Font Size (px):</label><input type="number" id="font_size" value="16"></div><div><label>Highlight Theme:</label><select id="highlight_theme"><option value="none">None</option>{% for style in highlight_styles %}<option value="{{ style }}" {% if style == 'default' %}selected{% endif %}>{{ style }}</option>{% endfor %}</select></div><div><label>Text Color:</label><input type="color" id="text_color" value="#333333"></div><div><label>Background Color:</label><input type="color" id="background_color" value="#ffffff"></div><div><label>Code Padding (px):</label><input type="number" id="code_padding" value="15"></div></div><div><input type="checkbox" id="include_fontawesome"><label for="include_fontawesome">Include Font Awesome</label></div><div><label for="custom_css">Custom CSS:</label><textarea id="custom_css" rows="3"></textarea></div></fieldset>
 
225
  <div id="png-preview-container" class="preview-container"></div>
226
  </div>
227
  <script>
 
 
228
  const loadBtn = document.getElementById('load-btn'), generateBtn = document.getElementById('generate-btn'),
229
  downloadHtmlBtn = document.getElementById('download-html-btn'), downloadPngBtn = document.getElementById('download-png-btn'),
230
  markdownTextInput = document.getElementById('markdown-text-input'), markdownFileInput = document.getElementById('markdown-file-input'),
 
254
  include_fontawesome: document.getElementById('include_fontawesome').checked,
255
  };
256
  }
 
257
  generateBtn.addEventListener('click', async () => {
258
  generateBtn.textContent = 'Generating...'; generateBtn.disabled = true; errorBox.style.display = 'none';
259
  const payload = buildPayload();
 
313
  """, highlight_styles=highlight_styles)
314
 
315
  if __name__ == "__main__":
 
 
316
  app.run(host="0.0.0.0", port=int(os.environ.get("PORT", 7860)))