pvanand commited on
Commit
c7678bc
·
verified ·
1 Parent(s): aaabdca

Update templates/apps-hub/article-writer.html

Browse files
Files changed (1) hide show
  1. templates/apps-hub/article-writer.html +118 -296
templates/apps-hub/article-writer.html CHANGED
@@ -10,75 +10,39 @@
10
  <script src="https://cdn.jsdelivr.net/npm/appwrite@15.0.0"></script>
11
  <script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
12
  <style>
13
- /* Main styles */
14
- /* body {
15
- margin: 0;
16
- padding: 0;
17
- text-rendering: optimizeLegibility;
18
- -webkit-font-smoothing: antialiased;
19
- color: rgba(0,0,0,0.8);
20
- position: relative;
21
- min-height: 100vh;
22
  font-family: source-serif-pro, Georgia, Cambria, "Times New Roman", Times, serif;
23
  font-size: 20px;
24
  line-height: 1.58;
25
  color: rgba(0, 0, 0, 0.8);
26
  background-color: #fff;
27
- } */
28
- .container {
29
  text-rendering: optimizeLegibility;
30
  -webkit-font-smoothing: antialiased;
31
- color: rgba(0,0,0,0.8);
32
- position: relative;
33
- min-height: 100vh;
34
- font-family: source-serif-pro, Georgia, Cambria, "Times New Roman", Times, serif;
35
- font-size: 20px;
36
- line-height: 1.58;
37
- color: rgba(0, 0, 0, 0.8);
38
- background-color: #fff;
39
- max-width: 800px;
40
- margin: 0 auto;
41
- padding: 0 20px;
42
  }
43
- /* Typography */
44
  h1, h2, h3, h4, h5, h6 {
45
- font-family: sohne, "Helvetica Neue", Helvetica, Arial, sans-serif;
46
- font-weight: 700;
47
- color: rgba(0, 0, 0, 0.84);
48
- letter-spacing: -0.022em;
49
- line-height: 1.2;
50
- }
51
- h1 {
52
- font-size: 40px;
53
- margin-bottom: 0.5em;
54
- }
55
- h2 {
56
- font-size: 32px;
57
- margin-top: 1.5em;
58
- margin-bottom: 0.5em;
59
- }
60
- h3 {
61
- font-size: 26px;
62
- margin-top: 1.5em;
63
- margin-bottom: 0.5em;
64
- }
65
- p {
66
- margin-bottom: 32px;
67
  }
68
- /* Links */
 
 
 
69
  a {
70
  color: #1a8917;
71
  text-decoration: none;
72
  }
73
- a:hover {
74
- text-decoration: underline;
75
- }
76
- /* Input container */
77
- #input-container {
78
- margin-bottom: 40px;
79
  background-color: #f9f9f9;
80
  padding: 32px;
81
  border-radius: 5px;
 
82
  }
83
  textarea {
84
  width: 100%;
@@ -101,25 +65,12 @@
101
  font-size: 18px;
102
  transition: background-color 0.3s;
103
  }
104
- button:hover {
105
- background-color: #0f6b0f;
106
- }
107
- /* Output container */
108
  #output-container {
109
  display: flex;
110
  flex-direction: column;
111
  gap: 40px;
112
  }
113
- #report-container, #sources-container {
114
- background-color: #fff;
115
- padding: 32px;
116
- border-radius: 5px;
117
- box-shadow: 0 1px 4px rgba(0, 0, 0, 0.1);
118
- overflow-wrap: break-word;
119
- word-wrap: break-word;
120
- word-break: break-word;
121
- }
122
- /* Sources */
123
  .source-item {
124
  margin-bottom: 32px;
125
  padding: 24px;
@@ -130,9 +81,7 @@
130
  cursor: pointer;
131
  transition: box-shadow 0.3s;
132
  }
133
- .source-item:hover {
134
- box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
135
- }
136
  .source-url {
137
  color: #1a8917;
138
  text-decoration: none;
@@ -147,13 +96,8 @@
147
  position: relative;
148
  overflow: hidden;
149
  }
150
- .source-snippet {
151
- max-height: 150px;
152
- overflow: hidden;
153
- }
154
- .source-full {
155
- display: none;
156
- }
157
  .expand-indicator {
158
  position: absolute;
159
  bottom: 0;
@@ -170,54 +114,39 @@
170
  font-size: 14px;
171
  color: #666;
172
  }
173
- .expanded .expand-indicator::after {
174
- content: '▲';
175
- }
176
- /* Responsive adjustments */
177
  @media (max-width: 768px) {
178
- .container {
179
- padding: 0 16px;
180
- }
181
- h1 {
182
- font-size: 32px;
183
- }
184
- h2 {
185
- font-size: 24px;
186
- }
187
- #input-container, #report-container, #sources-container {
188
- padding: 24px;
189
- }
190
  }
191
  </style>
192
- <link rel="stylesheet" href="/css/ai-sidebar.css">
193
  </head>
194
  <body>
195
  <div id="app" class="ai-sidebar__container">
196
  <sidebar-component></sidebar-component>
197
  <main class="ai-sidebar__content">
198
- <div class="container">
199
- <div id="input-container">
200
- <h1>Blog Post Generator</h1>
201
- <textarea id="description" rows="4" placeholder="Enter description">write a medium article on nvidia stock performance</textarea>
202
- <button onclick="generateReport()">Generate Blog</button>
203
- </div>
204
- <div id="output-container">
205
- <div id="report-container"></div>
206
- <div id="sources-container"></div>
207
- <div class="button-container">
208
- <button id="downloadBtn" onclick="downloadHTML()" style="display: none;" title="Download HTML Blog">
209
- <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
210
- <path d="M3 15v4c0 1.1.9 2 2 2h14a2 2 0 0 0 2-2v-4M17 9l-5 5-5-5M12 12.8V2.5"/>
211
- </svg>
212
- </button>
 
 
213
  </div>
214
- </div>
215
  </div>
216
- </main>
217
- </div>
218
- <script src="/js/auth.js"></script>
219
- <script src="/js/sidebar-component.js"></script>
220
- <script src="/js/main-app.js"></script>
221
  <script>
222
  async function generateReport() {
223
  const description = document.getElementById('description').value;
@@ -272,35 +201,30 @@
272
  markdown += chunk;
273
  renderMarkdown(markdown);
274
  }
275
-
276
  }
277
  document.getElementById('downloadBtn').style.display = 'block';
278
  } catch (error) {
279
  reportContainer.innerHTML = `Error generating blog: ${error.message}`;
280
  }
281
  }
 
282
  function renderMarkdown(markdown) {
283
  const reportContainer = document.getElementById('report-container');
284
  const reportContent = markdown.match(/<report>([\s\S]*)<\/report>/);
285
 
286
- if (reportContent) {
287
- reportContainer.innerHTML = marked.parse(reportContent[1]);
288
- } else {
289
- reportContainer.innerHTML = marked.parse(markdown);
290
- }
291
 
292
- const scripts = reportContainer.getElementsByTagName('script');
293
- Array.from(scripts).forEach(script => {
294
  const newScript = document.createElement('script');
295
  newScript.textContent = script.textContent;
296
  script.parentNode.replaceChild(newScript, script);
297
  });
298
- // Make Plotly charts responsive
299
- const plots = reportContainer.querySelectorAll('.js-plotly-plot');
300
- plots.forEach(plot => {
301
  Plotly.Plots.resize(plot);
302
  });
303
  }
 
304
  function processMetadata(metadata) {
305
  const sourcesContainer = document.getElementById('sources-container');
306
  const metadataMatch = metadata.match(/all-text-with-urls: (.+)/);
@@ -324,204 +248,102 @@
324
  `;
325
  sourcesContainer.appendChild(sourceItem);
326
 
327
- const sourceUrl = sourceItem.querySelector('.source-url');
328
  const sourceContent = sourceItem.querySelector('.source-content');
329
- const snippetDiv = sourceItem.querySelector('.source-snippet');
330
- const fullDiv = sourceItem.querySelector('.source-full');
331
-
332
  sourceContent.addEventListener('click', function(e) {
333
- if (!sourceItem.classList.contains('expanded')) {
334
- sourceItem.classList.add('expanded');
335
- snippetDiv.style.display = 'none';
336
- fullDiv.style.display = 'block';
337
- } else if (e.clientY > sourceContent.getBoundingClientRect().bottom - 30) {
338
- sourceItem.classList.remove('expanded');
339
- snippetDiv.style.display = 'block';
340
- fullDiv.style.display = 'none';
341
  }
342
  });
343
 
344
- sourceUrl.addEventListener('click', function(e) {
345
- e.stopPropagation();
346
- });
347
  }
348
  });
349
  } else {
350
  sourcesContainer.innerHTML = '<h2>Sources</h2><p>No source information available.</p>';
351
  }
352
  }
353
- // Make Plotly charts responsive on window resize
354
  window.addEventListener('resize', function() {
355
- const plots = document.querySelectorAll('.js-plotly-plot');
356
- plots.forEach(plot => {
357
  Plotly.Plots.resize(plot);
358
  });
359
  });
360
 
361
  function sanitizeFileName(name) {
362
- // Keep only alphanumeric characters and spaces
363
- name = name.replace(/[^a-z0-9\s]/gi, '');
364
- // Convert to lowercase
365
- name = name.toLowerCase();
366
- // Replace spaces with underscores
367
- name = name.replace(/\s+/g, '_');
368
- // Limit the length to 50 characters
369
- name = name.substring(0, 50);
370
- // If the name is empty after sanitization, use a default name
371
- return name || 'generated_report';
372
- }
373
- async function downloadHTML() {
374
- try {
375
- // Get styles from current document
376
- let css = '';
377
- const styleTags = document.getElementsByTagName('style');
378
- for (let styleTag of styleTags) {
379
- css += styleTag.innerHTML;
380
- }
381
-
382
- // Add additional styles for expanded sources and body max-width
383
- css += `
384
- body.report-body {
385
- max-width: 804px;
386
- margin: 0 auto;
387
- text-rendering: optimizeLegibility;
388
- -webkit-font-smoothing: antialiased;
389
- color: rgba(0,0,0,0.8);
390
- position: relative;
391
- min-height: 100vh;
392
- font-family: source-serif-pro, Georgia, Cambria, "Times New Roman", Times, serif;
393
- font-size: 20px;
394
- line-height: 1.58;
395
- color: rgba(0, 0, 0, 0.8);
396
- background-color: #fff;
397
- padding: 0 20px;
398
- }
399
- .source-item {
400
- margin-bottom: 20px;
401
- }
402
- .source-content {
403
- margin-top: 10px;
404
- }
405
- .source-snippet, .expand-indicator {
406
- display: none;
407
- }
408
- .source-full {
409
- display: block;
410
- }
411
- h1, h2, h3, h4, h5, h6 {
412
- font-family: sohne, "Helvetica Neue", Helvetica, Arial, sans-serif;
413
- font-weight: 700;
414
- color: rgba(0, 0, 0, 0.84);
415
- letter-spacing: -0.022em;
416
- line-height: 1.2;
417
- }
418
- h1 {
419
- font-size: 40px;
420
- margin-bottom: 0.5em;
421
- }
422
- h2 {
423
- font-size: 32px;
424
- margin-top: 1.5em;
425
- margin-bottom: 0.5em;
426
- }
427
- h3 {
428
- font-size: 26px;
429
- margin-top: 1.5em;
430
- margin-bottom: 0.5em;
431
  }
432
- p {
433
- margin-bottom: 32px;
434
- }
435
- /* Links */
436
- a {
437
- color: #1a8917;
438
- text-decoration: none;
439
- }
440
- a:hover {
441
- text-decoration: underline;
442
- }
443
- `;
444
-
445
- // Clone the document
446
- const htmlContent = document.implementation.createHTMLDocument('Report');
447
-
448
- // Set up the basic structure
449
- htmlContent.documentElement.lang = 'en';
450
- const head = htmlContent.head;
451
- const body = htmlContent.body;
452
- body.className = 'report-body';
453
-
454
- // Add meta tags
455
- const meta = htmlContent.createElement('meta');
456
- meta.charset = 'UTF-8';
457
- head.appendChild(meta);
458
 
459
- const viewport = htmlContent.createElement('meta');
460
- viewport.name = 'viewport';
461
- viewport.content = 'width=device-width, initial-scale=1.0';
462
- head.appendChild(viewport);
463
-
464
- // Copy the report content
465
- const reportContainer = document.getElementById('report-container');
466
- body.innerHTML = reportContainer.innerHTML;
467
-
468
- // Generate file name from content
469
- let fileName = 'generatedreport';
470
- const headings = body.querySelector('h1, h2, h3');
471
- if (headings) {
472
- fileName = sanitizeFileName(headings.textContent);
473
- }
474
 
475
- // Add title
476
- const title = htmlContent.createElement('title');
477
- title.textContent = fileName;
478
- head.appendChild(title);
 
479
 
480
- // Add style
481
- const style = htmlContent.createElement('style');
482
- style.textContent = css;
483
- head.appendChild(style);
 
 
 
 
 
 
484
 
485
- // Add necessary scripts
486
- const markedScript = htmlContent.createElement('script');
487
- markedScript.src = 'https://cdn.jsdelivr.net/npm/marked/marked.min.js';
488
- head.appendChild(markedScript);
489
 
490
- const plotlyScript = htmlContent.createElement('script');
491
- plotlyScript.src = 'https://cdn.plot.ly/plotly-latest.min.js';
492
- head.appendChild(plotlyScript);
493
 
494
- // Copy and modify the sources content
495
- const sourcesContainer = document.getElementById('sources-container');
496
- const sourcesDiv = htmlContent.createElement('div');
497
- sourcesDiv.innerHTML = sourcesContainer.innerHTML;
498
 
499
- // Expand all sources
500
- const sourceItems = sourcesDiv.querySelectorAll('.source-item');
501
- sourceItems.forEach(item => {
502
- item.classList.add('expanded');
503
- const snippetDiv = item.querySelector('.source-snippet');
504
- const fullDiv = item.querySelector('.source-full');
505
- if (snippetDiv) snippetDiv.style.display = 'none';
506
- if (fullDiv) fullDiv.style.display = 'block';
507
- });
508
 
509
- body.appendChild(sourcesDiv);
 
 
 
 
 
 
 
510
 
511
- // Create blob and download
512
- const blob = new Blob([htmlContent.documentElement.outerHTML], { type: 'text/html' });
513
- const url = URL.createObjectURL(blob);
514
- const a = document.createElement('a');
515
- a.href = url;
516
- a.download = `${fileName}.html`;
517
- document.body.appendChild(a);
518
- a.click();
519
- document.body.removeChild(a);
520
- URL.revokeObjectURL(url);
521
- } catch (error) {
522
- console.error('Error downloading HTML:', error);
523
- }
524
- }
525
  </script>
526
  </body>
527
  </html>
 
10
  <script src="https://cdn.jsdelivr.net/npm/appwrite@15.0.0"></script>
11
  <script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
12
  <style>
13
+ .container {
14
+ max-width: 800px;
15
+ margin: 0 auto;
16
+ padding: 0 20px;
 
 
 
 
 
17
  font-family: source-serif-pro, Georgia, Cambria, "Times New Roman", Times, serif;
18
  font-size: 20px;
19
  line-height: 1.58;
20
  color: rgba(0, 0, 0, 0.8);
21
  background-color: #fff;
 
 
22
  text-rendering: optimizeLegibility;
23
  -webkit-font-smoothing: antialiased;
 
 
 
 
 
 
 
 
 
 
 
24
  }
 
25
  h1, h2, h3, h4, h5, h6 {
26
+ font-family: sohne, "Helvetica Neue", Helvetica, Arial, sans-serif;
27
+ font-weight: 700;
28
+ color: rgba(0, 0, 0, 0.84);
29
+ letter-spacing: -0.022em;
30
+ line-height: 1.2;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
31
  }
32
+ h1 { font-size: 40px; margin-bottom: 0.5em; }
33
+ h2 { font-size: 32px; margin-top: 1.5em; margin-bottom: 0.5em; }
34
+ h3 { font-size: 26px; margin-top: 1.5em; margin-bottom: 0.5em; }
35
+ p { margin-bottom: 32px; }
36
  a {
37
  color: #1a8917;
38
  text-decoration: none;
39
  }
40
+ a:hover { text-decoration: underline; }
41
+ #input-container, #report-container, #sources-container {
 
 
 
 
42
  background-color: #f9f9f9;
43
  padding: 32px;
44
  border-radius: 5px;
45
+ margin-bottom: 40px;
46
  }
47
  textarea {
48
  width: 100%;
 
65
  font-size: 18px;
66
  transition: background-color 0.3s;
67
  }
68
+ button:hover { background-color: #0f6b0f; }
 
 
 
69
  #output-container {
70
  display: flex;
71
  flex-direction: column;
72
  gap: 40px;
73
  }
 
 
 
 
 
 
 
 
 
 
74
  .source-item {
75
  margin-bottom: 32px;
76
  padding: 24px;
 
81
  cursor: pointer;
82
  transition: box-shadow 0.3s;
83
  }
84
+ .source-item:hover { box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); }
 
 
85
  .source-url {
86
  color: #1a8917;
87
  text-decoration: none;
 
96
  position: relative;
97
  overflow: hidden;
98
  }
99
+ .source-snippet { max-height: 150px; overflow: hidden; }
100
+ .source-full { display: none; }
 
 
 
 
 
101
  .expand-indicator {
102
  position: absolute;
103
  bottom: 0;
 
114
  font-size: 14px;
115
  color: #666;
116
  }
117
+ .expanded .expand-indicator::after { content: '▲'; }
 
 
 
118
  @media (max-width: 768px) {
119
+ .container { padding: 0 16px; }
120
+ h1 { font-size: 32px; }
121
+ h2 { font-size: 24px; }
122
+ #input-container, #report-container, #sources-container { padding: 24px; }
 
 
 
 
 
 
 
 
123
  }
124
  </style>
 
125
  </head>
126
  <body>
127
  <div id="app" class="ai-sidebar__container">
128
  <sidebar-component></sidebar-component>
129
  <main class="ai-sidebar__content">
130
+ <div class="container">
131
+ <div id="input-container">
132
+ <h1>Blog Post Generator</h1>
133
+ <textarea id="description" rows="4" placeholder="Enter description">write a medium article on nvidia stock performance</textarea>
134
+ <button onclick="generateReport()">Generate Blog</button>
135
+ </div>
136
+ <div id="output-container">
137
+ <div id="report-container"></div>
138
+ <div id="sources-container"></div>
139
+ <div class="button-container">
140
+ <button id="downloadBtn" onclick="downloadHTML()" style="display: none;" title="Download HTML Blog">
141
+ <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
142
+ <path d="M3 15v4c0 1.1.9 2 2 2h14a2 2 0 0 0 2-2v-4M17 9l-5 5-5-5M12 12.8V2.5"/>
143
+ </svg>
144
+ </button>
145
+ </div>
146
+ </div>
147
  </div>
148
+ </main>
149
  </div>
 
 
 
 
 
150
  <script>
151
  async function generateReport() {
152
  const description = document.getElementById('description').value;
 
201
  markdown += chunk;
202
  renderMarkdown(markdown);
203
  }
 
204
  }
205
  document.getElementById('downloadBtn').style.display = 'block';
206
  } catch (error) {
207
  reportContainer.innerHTML = `Error generating blog: ${error.message}`;
208
  }
209
  }
210
+
211
  function renderMarkdown(markdown) {
212
  const reportContainer = document.getElementById('report-container');
213
  const reportContent = markdown.match(/<report>([\s\S]*)<\/report>/);
214
 
215
+ reportContainer.innerHTML = marked.parse(reportContent ? reportContent[1] : markdown);
 
 
 
 
216
 
217
+ Array.from(reportContainer.getElementsByTagName('script')).forEach(script => {
 
218
  const newScript = document.createElement('script');
219
  newScript.textContent = script.textContent;
220
  script.parentNode.replaceChild(newScript, script);
221
  });
222
+
223
+ reportContainer.querySelectorAll('.js-plotly-plot').forEach(plot => {
 
224
  Plotly.Plots.resize(plot);
225
  });
226
  }
227
+
228
  function processMetadata(metadata) {
229
  const sourcesContainer = document.getElementById('sources-container');
230
  const metadataMatch = metadata.match(/all-text-with-urls: (.+)/);
 
248
  `;
249
  sourcesContainer.appendChild(sourceItem);
250
 
 
251
  const sourceContent = sourceItem.querySelector('.source-content');
 
 
 
252
  sourceContent.addEventListener('click', function(e) {
253
+ const isExpanded = sourceItem.classList.contains('expanded');
254
+ const isClickOnBottom = e.clientY > sourceContent.getBoundingClientRect().bottom - 30;
255
+
256
+ if (!isExpanded || (isExpanded && isClickOnBottom)) {
257
+ sourceItem.classList.toggle('expanded');
258
+ sourceItem.querySelector('.source-snippet').style.display = isExpanded ? 'block' : 'none';
259
+ sourceItem.querySelector('.source-full').style.display = isExpanded ? 'none' : 'block';
 
260
  }
261
  });
262
 
263
+ sourceItem.querySelector('.source-url').addEventListener('click', e => e.stopPropagation());
 
 
264
  }
265
  });
266
  } else {
267
  sourcesContainer.innerHTML = '<h2>Sources</h2><p>No source information available.</p>';
268
  }
269
  }
270
+
271
  window.addEventListener('resize', function() {
272
+ document.querySelectorAll('.js-plotly-plot').forEach(plot => {
 
273
  Plotly.Plots.resize(plot);
274
  });
275
  });
276
 
277
  function sanitizeFileName(name) {
278
+ return name.replace(/[^a-z0-9\s]/gi, '').toLowerCase().replace(/\s+/g, '_').substring(0, 50) || 'generated_report';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
279
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
280
 
281
+ async function downloadHTML() {
282
+ try {
283
+ let css = Array.from(document.getElementsByTagName('style')).map(style => style.innerHTML).join('\n');
284
+ css += `
285
+ body.report-body {
286
+ max-width: 804px;
287
+ margin: 0 auto;
288
+ padding: 0 20px;
289
+ }
290
+ .source-item { margin-bottom: 20px; }
291
+ .source-content { margin-top: 10px; }
292
+ .source-snippet, .expand-indicator { display: none; }
293
+ .source-full { display: block; }
294
+ `;
 
295
 
296
+ const htmlContent = document.implementation.createHTMLDocument('Report');
297
+ htmlContent.documentElement.lang = 'en';
298
+ const head = htmlContent.head;
299
+ const body = htmlContent.body;
300
+ body.className = 'report-body';
301
 
302
+ ['charset', 'viewport'].forEach(name => {
303
+ const meta = htmlContent.createElement('meta');
304
+ if (name === 'charset') {
305
+ meta.charset = 'UTF-8';
306
+ } else {
307
+ meta.name = name;
308
+ meta.content = 'width=device-width, initial-scale=1.0';
309
+ }
310
+ head.appendChild(meta);
311
+ });
312
 
313
+ body.innerHTML = document.getElementById('report-container').innerHTML;
 
 
 
314
 
315
+ const fileName = sanitizeFileName(body.querySelector('h1, h2, h3')?.textContent || 'generatedreport');
 
 
316
 
317
+ head.appendChild(Object.assign(htmlContent.createElement('title'), { textContent: fileName }));
318
+ head.appendChild(Object.assign(htmlContent.createElement('style'), { textContent: css }));
 
 
319
 
320
+ ['https://cdn.jsdelivr.net/npm/marked/marked.min.js', 'https://cdn.plot.ly/plotly-latest.min.js'].forEach(src => {
321
+ head.appendChild(Object.assign(htmlContent.createElement('script'), { src }));
322
+ });
 
 
 
 
 
 
323
 
324
+ const sourcesDiv = htmlContent.createElement('div');
325
+ sourcesDiv.innerHTML = document.getElementById('sources-container').innerHTML;
326
+ sourcesDiv.querySelectorAll('.source-item').forEach(item => {
327
+ item.classList.add('expanded');
328
+ item.querySelector('.source-snippet').style.display = 'none';
329
+ item.querySelector('.source-full').style.display = 'block';
330
+ });
331
+ body.appendChild(sourcesDiv);
332
 
333
+ const blob = new Blob([htmlContent.documentElement.outerHTML], { type: 'text/html' });
334
+ const url = URL.createObjectURL(blob);
335
+ const a = Object.assign(document.createElement('a'), {
336
+ href: url,
337
+ download: `${fileName}.html`
338
+ });
339
+ document.body.appendChild(a);
340
+ a.click();
341
+ document.body.removeChild(a);
342
+ URL.revokeObjectURL(url);
343
+ } catch (error) {
344
+ console.error('Error downloading HTML:', error);
345
+ }
346
+ }
347
  </script>
348
  </body>
349
  </html>