gradsyntax commited on
Commit
38d1c62
·
verified ·
1 Parent(s): 8e423ea

Enhance the MutateX web app with: - A professional header area for GradSyntax - A clearly visible footer: "Developed by GradSyntax – www.gradsyntax.com" - Improved spacing, fonts, and color scheme for better readability Make it look like a production-ready tool suitable for research or healthcare use. - Initial Deployment

Browse files
Files changed (2) hide show
  1. README.md +7 -5
  2. index.html +996 -19
README.md CHANGED
@@ -1,10 +1,12 @@
1
  ---
2
- title: Mutatex
3
- emoji: 🏃
4
- colorFrom: pink
5
- colorTo: green
6
  sdk: static
7
  pinned: false
 
 
8
  ---
9
 
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
1
  ---
2
+ title: mutatex
3
+ emoji: 🐳
4
+ colorFrom: red
5
+ colorTo: blue
6
  sdk: static
7
  pinned: false
8
+ tags:
9
+ - deepsite
10
  ---
11
 
12
+ Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
index.html CHANGED
@@ -1,19 +1,996 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
19
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>MutateX - DNA Mutation Detection</title>
7
+ <script src="https://cdn.tailwindcss.com"></script>
8
+ <script src="https://cdn.plot.ly/plotly-latest.min.js"></script>
9
+ <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
10
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js"></script>
11
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf-autotable/3.5.25/jspdf.plugin.autotable.min.js"></script>
12
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
13
+ <style>
14
+ @import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap');
15
+
16
+ body {
17
+ font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
18
+ }
19
+
20
+ .dna-bg {
21
+ background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100" viewBox="0 0 100 100"><path d="M50 10 L30 30 L50 50 L30 70 L50 90 M50 10 L70 30 L50 50 L70 70 L50 90" stroke="rgba(16, 185, 129, 0.08)" stroke-width="2" fill="none"/></svg>');
22
+ background-size: 200px;
23
+ background-repeat: repeat;
24
+ }
25
+
26
+ h1, h2, h3 {
27
+ font-weight: 600;
28
+ color: #1f2937;
29
+ }
30
+ .file-upload {
31
+ border: 2px dashed rgba(74, 222, 128, 0.5);
32
+ transition: all 0.3s ease;
33
+ }
34
+ .file-upload:hover {
35
+ border-color: rgba(74, 222, 128, 0.8);
36
+ background-color: rgba(74, 222, 128, 0.05);
37
+ }
38
+ .file-upload.drag-over {
39
+ border-color: rgba(74, 222, 128, 1);
40
+ background-color: rgba(74, 222, 128, 0.1);
41
+ }
42
+ .mutation-highlight {
43
+ animation: pulse 2s infinite;
44
+ }
45
+ @keyframes pulse {
46
+ 0% { background-color: rgba(239, 68, 68, 0.1); }
47
+ 50% { background-color: rgba(239, 68, 68, 0.3); }
48
+ 100% { background-color: rgba(239, 68, 68, 0.1); }
49
+ }
50
+ .sequence-viewer {
51
+ font-family: 'Courier New', monospace;
52
+ letter-spacing: 2px;
53
+ line-height: 1.8;
54
+ }
55
+ </style>
56
+ </head>
57
+ <body class="bg-gray-50 dna-bg min-h-screen">
58
+ <div class="container mx-auto px-4 py-8 max-w-7xl">
59
+ <!-- Header -->
60
+ <header class="text-center mb-12">
61
+ <div class="flex items-center justify-center mb-4">
62
+ <i class="fas fa-dna text-4xl text-emerald-400 mr-3"></i>
63
+ <h1 class="text-4xl font-bold text-gray-800">MutateX</h1>
64
+ </div>
65
+ <p class="text-lg text-gray-600 max-w-2xl mx-auto">
66
+ Advanced DNA mutation detection powered by AI models like DNABERT and HyenaDNA.
67
+ Upload your CSV file containing DNA sequences for comprehensive mutation analysis.
68
+ </p>
69
+ </header>
70
+
71
+ <!-- Main Content -->
72
+ <div class="grid grid-cols-1 lg:grid-cols-3 gap-8">
73
+ <!-- Upload Section -->
74
+ <div class="lg:col-span-1 bg-white rounded-xl shadow-md p-6 border border-gray-100">
75
+ <h2 class="text-2xl font-semibold text-gray-800 mb-4 flex items-center">
76
+ <i class="fas fa-file-upload text-emerald-500 mr-2"></i> Upload DNA Data
77
+ </h2>
78
+
79
+ <div id="dropZone" class="file-upload rounded-lg p-8 text-center cursor-pointer mb-6">
80
+ <i class="fas fa-dna text-4xl text-emerald-400 mb-3"></i>
81
+ <p class="text-gray-600 mb-2">Drag & drop your CSV file here</p>
82
+ <p class="text-sm text-gray-500 mb-4">or</p>
83
+ <label for="fileInput" class="bg-emerald-500 hover:bg-emerald-600 text-white font-medium py-2 px-4 rounded-lg cursor-pointer transition">
84
+ <i class="fas fa-folder-open mr-2"></i> Browse Files
85
+ </label>
86
+ <input id="fileInput" type="file" accept=".csv" class="hidden">
87
+ </div>
88
+
89
+ <div class="mb-4">
90
+ <label class="block text-gray-700 text-sm font-medium mb-2">Select AI Model</label>
91
+ <select id="modelSelect" class="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-emerald-500 focus:border-emerald-500">
92
+ <option value="dnabert">DNABERT (Default)</option>
93
+ <option value="hyenadna">HyenaDNA</option>
94
+ <option value="ensemble">Ensemble (Both Models)</option>
95
+ </select>
96
+ </div>
97
+
98
+ <div class="mb-4">
99
+ <label class="block text-gray-700 text-sm font-medium mb-2">Analysis Parameters</label>
100
+ <div class="space-y-2">
101
+ <div class="flex items-center">
102
+ <input id="checkSNP" type="checkbox" checked class="h-4 w-4 text-emerald-600 focus:ring-emerald-500 border-gray-300 rounded">
103
+ <label for="checkSNP" class="ml-2 block text-sm text-gray-700">Detect SNPs</label>
104
+ </div>
105
+ <div class="flex items-center">
106
+ <input id="checkIndels" type="checkbox" checked class="h-4 w-4 text-emerald-600 focus:ring-emerald-500 border-gray-300 rounded">
107
+ <label for="checkIndels" class="ml-2 block text-sm text-gray-700">Detect Indels</label>
108
+ </div>
109
+ <div class="flex items-center">
110
+ <input id="checkStructural" type="checkbox" class="h-4 w-4 text-emerald-600 focus:ring-emerald-500 border-gray-300 rounded">
111
+ <label for="checkStructural" class="ml-2 block text-sm text-gray-700">Structural Variations</label>
112
+ </div>
113
+ </div>
114
+ </div>
115
+
116
+ <div class="flex space-x-4">
117
+ <button id="analyzeBtn" class="flex-1 bg-emerald-600 hover:bg-emerald-700 text-white font-semibold py-3 px-4 rounded-lg transition flex items-center justify-center disabled:opacity-50 shadow-sm hover:shadow-md disabled:shadow-none" disabled>
118
+ <i class="fas fa-search mr-2"></i> Analyze Sequences
119
+ </button>
120
+ <button id="exampleBtn" class="flex-1 bg-blue-500 hover:bg-blue-600 text-white font-bold py-3 px-4 rounded-lg transition flex items-center justify-center">
121
+ <i class="fas fa-vial mr-2"></i> Example Data
122
+ </button>
123
+ </div>
124
+
125
+ <div class="mt-4 text-sm text-gray-500">
126
+ <p><i class="fas fa-info-circle mr-1"></i> CSV Format: Sequence ID, DNA Sequence</p>
127
+ <p class="mt-1"><i class="fas fa-exclamation-triangle mr-1"></i> Max file size: 5MB</p>
128
+ </div>
129
+ </div>
130
+
131
+ <!-- Results Section -->
132
+ <div class="lg:col-span-2 space-y-6">
133
+ <!-- Loading State -->
134
+ <div id="loadingState" class="hidden bg-white rounded-xl shadow-lg p-6 text-center">
135
+ <div class="animate-pulse flex flex-col items-center">
136
+ <i class="fas fa-dna text-4xl text-emerald-400 mb-4 animate-spin"></i>
137
+ <h3 class="text-xl font-medium text-gray-800 mb-2">Analyzing DNA Sequences</h3>
138
+ <p class="text-gray-600">This may take a few moments...</p>
139
+ <div class="w-full bg-gray-200 rounded-full h-2.5 mt-4">
140
+ <div id="progressBar" class="bg-emerald-600 h-2.5 rounded-full" style="width: 0%"></div>
141
+ </div>
142
+ </div>
143
+ </div>
144
+
145
+ <!-- Results Overview -->
146
+ <div id="resultsOverview" class="hidden bg-white rounded-xl shadow-lg p-6">
147
+ <h2 class="text-2xl font-semibold text-gray-800 mb-4 flex items-center">
148
+ <i class="fas fa-chart-bar text-emerald-500 mr-2"></i> Analysis Results
149
+ </h2>
150
+
151
+ <div class="grid grid-cols-1 md:grid-cols-3 gap-4 mb-6">
152
+ <div class="bg-emerald-50 border border-emerald-100 rounded-lg p-4">
153
+ <div class="flex items-center">
154
+ <div class="p-2 rounded-full bg-emerald-100 text-emerald-600 mr-3">
155
+ <i class="fas fa-dna"></i>
156
+ </div>
157
+ <div>
158
+ <p class="text-sm text-gray-500">Sequences Analyzed</p>
159
+ <p id="totalSequences" class="text-xl font-bold text-gray-800">0</p>
160
+ </div>
161
+ </div>
162
+ </div>
163
+ <div class="bg-red-50 border border-red-100 rounded-lg p-4">
164
+ <div class="flex items-center">
165
+ <div class="p-2 rounded-full bg-red-100 text-red-600 mr-3">
166
+ <i class="fas fa-exclamation-triangle"></i>
167
+ </div>
168
+ <div>
169
+ <p class="text-sm text-gray-500">Mutations Found</p>
170
+ <p id="totalMutations" class="text-xl font-bold text-gray-800">0</p>
171
+ </div>
172
+ </div>
173
+ </div>
174
+ <div class="bg-blue-50 border border-blue-100 rounded-lg p-4">
175
+ <div class="flex items-center">
176
+ <div class="p-2 rounded-full bg-blue-100 text-blue-600 mr-3">
177
+ <i class="fas fa-percentage"></i>
178
+ </div>
179
+ <div>
180
+ <p class="text-sm text-gray-500">Mutation Rate</p>
181
+ <p id="mutationRate" class="text-xl font-bold text-gray-800">0%</p>
182
+ </div>
183
+ </div>
184
+ </div>
185
+ </div>
186
+
187
+ <div class="mb-6">
188
+ <h3 class="text-lg font-medium text-gray-800 mb-3">Mutation Distribution</h3>
189
+ <div class="h-64">
190
+ <canvas id="mutationChart"></canvas>
191
+ </div>
192
+ </div>
193
+
194
+ <div class="mb-6">
195
+ <h3 class="text-lg font-medium text-gray-800 mb-3">Mutation Types</h3>
196
+ <div class="h-80">
197
+ <div id="mutationTypeChart" style="width: 100%; height: 100%;"></div>
198
+ </div>
199
+ </div>
200
+
201
+ <div class="mt-6">
202
+ <h3 class="text-lg font-medium text-gray-800 mb-3">Export Full Analysis</h3>
203
+ <div class="flex flex-col sm:flex-row space-y-3 sm:space-y-0 sm:space-x-4">
204
+ <button id="downloadFullCsv" class="bg-emerald-500 hover:bg-emerald-600 text-white font-medium py-3 px-6 rounded-lg transition flex items-center justify-center">
205
+ <i class="fas fa-file-csv mr-2"></i> Download Full Report (CSV)
206
+ </button>
207
+ <button id="downloadFullPdf" class="bg-red-500 hover:bg-red-600 text-white font-medium py-3 px-6 rounded-lg transition flex items-center justify-center">
208
+ <i class="fas fa-file-pdf mr-2"></i> Download Full Report (PDF)
209
+ </button>
210
+ </div>
211
+ </div>
212
+
213
+ <div class="mt-6">
214
+ <h3 class="text-lg font-medium text-gray-800 mb-3">Export Mutation Details</h3>
215
+ <div class="flex space-x-4">
216
+ <button id="downloadCsv" class="bg-emerald-500 hover:bg-emerald-600 text-white font-medium py-2 px-4 rounded-lg transition flex items-center">
217
+ <i class="fas fa-file-csv mr-2"></i> Download CSV
218
+ </button>
219
+ <button id="downloadPdf" class="bg-red-500 hover:bg-red-600 text-white font-medium py-2 px-4 rounded-lg transition flex items-center">
220
+ <i class="fas fa-file-pdf mr-2"></i> Download PDF
221
+ </button>
222
+ </div>
223
+ </div>
224
+ </div>
225
+
226
+ <!-- Detailed Results Table -->
227
+ <div id="resultsTable" class="hidden bg-white rounded-xl shadow-lg overflow-hidden">
228
+ <div class="p-6 pb-0">
229
+ <h2 class="text-2xl font-semibold text-gray-800 mb-4 flex items-center">
230
+ <i class="fas fa-table text-emerald-500 mr-2"></i> Mutation Details
231
+ </h2>
232
+ </div>
233
+
234
+ <div class="overflow-x-auto">
235
+ <table class="min-w-full divide-y divide-gray-200">
236
+ <thead class="bg-gray-50">
237
+ <tr>
238
+ <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Sequence ID</th>
239
+ <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Position</th>
240
+ <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Reference</th>
241
+ <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Mutation</th>
242
+ <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Type</th>
243
+ <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Confidence</th>
244
+ </tr>
245
+ </thead>
246
+ <tbody id="resultsTableBody" class="bg-white divide-y divide-gray-200">
247
+ <!-- Results will be populated here -->
248
+ </tbody>
249
+ </table>
250
+ </div>
251
+
252
+ <div class="px-6 py-4 bg-gray-50 border-t border-gray-200 flex items-center justify-between">
253
+ <div class="text-sm text-gray-500">
254
+ Showing <span id="startItem">1</span> to <span id="endItem">10</span> of <span id="totalItems">0</span> mutations
255
+ </div>
256
+ <div class="flex space-x-2">
257
+ <button id="prevPage" class="px-3 py-1 border border-gray-300 rounded-md text-sm font-medium text-gray-700 bg-white hover:bg-gray-50 disabled:opacity-50" disabled>
258
+ Previous
259
+ </button>
260
+ <button id="nextPage" class="px-3 py-1 border border-gray-300 rounded-md text-sm font-medium text-gray-700 bg-white hover:bg-gray-50 disabled:opacity-50" disabled>
261
+ Next
262
+ </button>
263
+ </div>
264
+ </div>
265
+ </div>
266
+
267
+ <!-- Sequence Viewer -->
268
+ <div id="sequenceViewer" class="hidden bg-white rounded-xl shadow-lg p-6">
269
+ <h2 class="text-2xl font-semibold text-gray-800 mb-4 flex items-center">
270
+ <i class="fas fa-eye text-emerald-500 mr-2"></i> Sequence Viewer
271
+ </h2>
272
+
273
+ <div class="mb-4">
274
+ <label class="block text-gray-700 text-sm font-medium mb-2">Select Sequence</label>
275
+ <select id="sequenceSelect" class="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-emerald-500 focus:border-emerald-500">
276
+ <option value="">Select a sequence to view</option>
277
+ </select>
278
+ </div>
279
+
280
+ <div class="bg-gray-50 p-4 rounded-lg overflow-x-auto">
281
+ <div id="sequenceDisplay" class="sequence-viewer text-sm">
282
+ <!-- Sequence will be displayed here -->
283
+ </div>
284
+ </div>
285
+
286
+ <div class="mt-4 flex flex-wrap gap-2">
287
+ <div class="flex items-center">
288
+ <div class="w-3 h-3 rounded-full bg-emerald-400 mr-1"></div>
289
+ <span class="text-xs">Normal</span>
290
+ </div>
291
+ <div class="flex items-center">
292
+ <div class="w-3 h-3 rounded-full bg-red-400 mr-1"></div>
293
+ <span class="text-xs">SNP</span>
294
+ </div>
295
+ <div class="flex items-center">
296
+ <div class="w-3 h-3 rounded-full bg-yellow-400 mr-1"></div>
297
+ <span class="text-xs">Insertion</span>
298
+ </div>
299
+ <div class="flex items-center">
300
+ <div class="w-3 h-3 rounded-full bg-blue-400 mr-1"></div>
301
+ <span class="text-xs">Deletion</span>
302
+ </div>
303
+ </div>
304
+ </div>
305
+ </div>
306
+ </div>
307
+
308
+ <!-- Footer -->
309
+ <footer class="mt-16 bg-gray-100 py-6 border-t border-gray-200">
310
+ <div class="container mx-auto px-4">
311
+ <div class="flex flex-col md:flex-row justify-between items-center">
312
+ <div class="mb-4 md:mb-0">
313
+ <p class="text-gray-700 font-medium">MutateX - Professional DNA Analysis Tool</p>
314
+ <p class="text-gray-500 text-sm">Powered by DNABERT & HyenaDNA AI models</p>
315
+ </div>
316
+ <div class="text-center md:text-right">
317
+ <p class="text-gray-600">Developed by <a href="https://www.gradsyntax.com" target="_blank" class="text-emerald-600 hover:text-emerald-800 font-medium">GradSyntax</a></p>
318
+ <p class="text-gray-400 text-xs mt-1">© 2023 GradSyntax. All rights reserved.</p>
319
+ </div>
320
+ </div>
321
+ </div>
322
+ </footer>
323
+ </div>
324
+
325
+ <script>
326
+ // DOM Elements
327
+ const dropZone = document.getElementById('dropZone');
328
+ const fileInput = document.getElementById('fileInput');
329
+ const analyzeBtn = document.getElementById('analyzeBtn');
330
+ const loadingState = document.getElementById('loadingState');
331
+ const resultsOverview = document.getElementById('resultsOverview');
332
+ const resultsTable = document.getElementById('resultsTable');
333
+ const resultsTableBody = document.getElementById('resultsTableBody');
334
+ const sequenceViewer = document.getElementById('sequenceViewer');
335
+ const sequenceSelect = document.getElementById('sequenceSelect');
336
+ const sequenceDisplay = document.getElementById('sequenceDisplay');
337
+ const progressBar = document.getElementById('progressBar');
338
+ const totalSequences = document.getElementById('totalSequences');
339
+ const totalMutations = document.getElementById('totalMutations');
340
+ const mutationRate = document.getElementById('mutationRate');
341
+ const prevPage = document.getElementById('prevPage');
342
+ const nextPage = document.getElementById('nextPage');
343
+ const startItem = document.getElementById('startItem');
344
+ const endItem = document.getElementById('endItem');
345
+ const totalItems = document.getElementById('totalItems');
346
+
347
+ // App State
348
+ let uploadedFile = null;
349
+ let analysisResults = [];
350
+ let filteredResults = [];
351
+ let currentPage = 1;
352
+ const itemsPerPage = 10;
353
+ let sequences = {};
354
+ let mutationStats = {
355
+ total: 0,
356
+ snps: 0,
357
+ insertions: 0,
358
+ deletions: 0,
359
+ others: 0
360
+ };
361
+
362
+ // Event Listeners
363
+ dropZone.addEventListener('click', () => fileInput.click());
364
+ dropZone.addEventListener('dragover', (e) => {
365
+ e.preventDefault();
366
+ dropZone.classList.add('drag-over');
367
+ });
368
+ dropZone.addEventListener('dragleave', () => {
369
+ dropZone.classList.remove('drag-over');
370
+ });
371
+ dropZone.addEventListener('drop', (e) => {
372
+ e.preventDefault();
373
+ dropZone.classList.remove('drag-over');
374
+ if (e.dataTransfer.files.length) {
375
+ handleFileUpload(e.dataTransfer.files[0]);
376
+ }
377
+ });
378
+
379
+ fileInput.addEventListener('change', (e) => {
380
+ if (e.target.files.length) {
381
+ handleFileUpload(e.target.files[0]);
382
+ }
383
+ });
384
+
385
+ analyzeBtn.addEventListener('click', analyzeSequences);
386
+ document.getElementById('exampleBtn').addEventListener('click', loadExampleData);
387
+ sequenceSelect.addEventListener('change', displaySelectedSequence);
388
+ prevPage.addEventListener('click', () => updateTable(currentPage - 1));
389
+ nextPage.addEventListener('click', () => updateTable(currentPage + 1));
390
+
391
+ // Functions
392
+ function handleFileUpload(file) {
393
+ if (file.type !== 'text/csv' && !file.name.endsWith('.csv')) {
394
+ alert('Please upload a CSV file.');
395
+ return;
396
+ }
397
+
398
+ if (file.size > 5 * 1024 * 1024) {
399
+ alert('File size exceeds 5MB limit.');
400
+ return;
401
+ }
402
+
403
+ uploadedFile = file;
404
+ analyzeBtn.disabled = false;
405
+
406
+ // Update UI
407
+ dropZone.innerHTML = `
408
+ <div class="flex flex-col items-center">
409
+ <i class="fas fa-check-circle text-4xl text-emerald-400 mb-2"></i>
410
+ <p class="font-medium text-gray-800">${file.name}</p>
411
+ <p class="text-sm text-gray-500 mt-1">${(file.size / 1024).toFixed(1)} KB</p>
412
+ <button id="changeFile" class="mt-3 text-emerald-500 hover:text-emerald-600 text-sm">
413
+ <i class="fas fa-sync-alt mr-1"></i> Change File
414
+ </button>
415
+ </div>
416
+ `;
417
+
418
+ document.getElementById('changeFile').addEventListener('click', () => {
419
+ fileInput.value = '';
420
+ uploadedFile = null;
421
+ analyzeBtn.disabled = true;
422
+ dropZone.innerHTML = `
423
+ <i class="fas fa-dna text-4xl text-emerald-400 mb-3"></i>
424
+ <p class="text-gray-600 mb-2">Drag & drop your CSV file here</p>
425
+ <p class="text-sm text-gray-500 mb-4">or</p>
426
+ <label for="fileInput" class="bg-emerald-500 hover:bg-emerald-600 text-white font-medium py-2 px-4 rounded-lg cursor-pointer transition">
427
+ <i class="fas fa-folder-open mr-2"></i> Browse Files
428
+ </label>
429
+ `;
430
+ });
431
+ }
432
+
433
+ function loadExampleData() {
434
+ // Simulate loading example data
435
+ dropZone.innerHTML = `
436
+ <div class="flex flex-col items-center">
437
+ <i class="fas fa-vial text-4xl text-blue-400 mb-2"></i>
438
+ <p class="font-medium text-gray-800">example_data.csv</p>
439
+ <p class="text-sm text-gray-500 mt-1">Example Data</p>
440
+ <button id="changeFile" class="mt-3 text-emerald-500 hover:text-emerald-600 text-sm">
441
+ <i class="fas fa-sync-alt mr-1"></i> Change File
442
+ </button>
443
+ </div>
444
+ `;
445
+
446
+ document.getElementById('changeFile').addEventListener('click', () => {
447
+ fileInput.value = '';
448
+ uploadedFile = null;
449
+ analyzeBtn.disabled = true;
450
+ dropZone.innerHTML = `
451
+ <i class="fas fa-dna text-4xl text-emerald-400 mb-3"></i>
452
+ <p class="text-gray-600 mb-2">Drag & drop your CSV file here</p>
453
+ <p class="text-sm text-gray-500 mb-4">or</p>
454
+ <label for="fileInput" class="bg-emerald-500 hover:bg-emerald-600 text-white font-medium py-2 px-4 rounded-lg cursor-pointer transition">
455
+ <i class="fas fa-folder-open mr-2"></i> Browse Files
456
+ </label>
457
+ `;
458
+ });
459
+
460
+ // Enable analyze button
461
+ analyzeBtn.disabled = false;
462
+
463
+ // Set flag for example data
464
+ uploadedFile = { name: 'example_data.csv', size: 1024 };
465
+ }
466
+
467
+ function analyzeSequences() {
468
+ if (!uploadedFile) return;
469
+
470
+ // Show loading state
471
+ loadingState.classList.remove('hidden');
472
+ resultsOverview.classList.add('hidden');
473
+ resultsTable.classList.add('hidden');
474
+ sequenceViewer.classList.add('hidden');
475
+
476
+ // Simulate analysis progress
477
+ let progress = 0;
478
+ const progressInterval = setInterval(() => {
479
+ progress += Math.random() * 10;
480
+ if (progress > 100) progress = 100;
481
+ progressBar.style.width = `${progress}%`;
482
+
483
+ if (progress === 100) {
484
+ clearInterval(progressInterval);
485
+ simulateAnalysisComplete();
486
+ }
487
+ }, 300);
488
+ }
489
+
490
+ function simulateAnalysisComplete() {
491
+ // This would be replaced with actual API calls to DNABERT/HyenaDNA models
492
+ // For demo purposes, we'll generate mock data
493
+
494
+ // Generate mock sequences
495
+ sequences = {
496
+ 'seq1': generateRandomSequence(150),
497
+ 'seq2': generateRandomSequence(200),
498
+ 'seq3': generateRandomSequence(180),
499
+ 'seq4': generateRandomSequence(220),
500
+ 'seq5': generateRandomSequence(190)
501
+ };
502
+
503
+ // Generate mock mutations
504
+ analysisResults = [];
505
+ mutationStats = {
506
+ total: 0,
507
+ snps: 0,
508
+ insertions: 0,
509
+ deletions: 0,
510
+ others: 0
511
+ };
512
+
513
+ Object.keys(sequences).forEach(seqId => {
514
+ const seq = sequences[seqId];
515
+ const seqLength = seq.length;
516
+
517
+ // Random number of mutations per sequence (0-5)
518
+ const mutationCount = Math.floor(Math.random() * 6);
519
+
520
+ for (let i = 0; i < mutationCount; i++) {
521
+ const position = Math.floor(Math.random() * seqLength) + 1;
522
+ const refBase = seq[position - 1];
523
+ let mutationType, altBase, confidence;
524
+
525
+ // Random mutation type
526
+ const typeRand = Math.random();
527
+ if (typeRand < 0.6) {
528
+ mutationType = 'SNP';
529
+ mutationStats.snps++;
530
+ altBase = ['A', 'T', 'C', 'G'].filter(b => b !== refBase)[Math.floor(Math.random() * 3)];
531
+ confidence = (0.8 + Math.random() * 0.2).toFixed(2);
532
+ } else if (typeRand < 0.85) {
533
+ mutationType = 'Insertion';
534
+ mutationStats.insertions++;
535
+ altBase = refBase + ['A', 'T', 'C', 'G'][Math.floor(Math.random() * 4)];
536
+ confidence = (0.7 + Math.random() * 0.25).toFixed(2);
537
+ } else if (typeRand < 0.95) {
538
+ mutationType = 'Deletion';
539
+ mutationStats.deletions++;
540
+ altBase = '-';
541
+ confidence = (0.6 + Math.random() * 0.3).toFixed(2);
542
+ } else {
543
+ mutationType = 'Complex';
544
+ mutationStats.others++;
545
+ altBase = 'VAR';
546
+ confidence = (0.5 + Math.random() * 0.4).toFixed(2);
547
+ }
548
+
549
+ analysisResults.push({
550
+ seqId,
551
+ position,
552
+ refBase,
553
+ altBase,
554
+ type: mutationType,
555
+ confidence
556
+ });
557
+ mutationStats.total++;
558
+ }
559
+ });
560
+
561
+ // Update UI with results
562
+ loadingState.classList.add('hidden');
563
+ displayResults();
564
+ }
565
+
566
+ function generateRandomSequence(length) {
567
+ const bases = ['A', 'T', 'C', 'G'];
568
+ let sequence = '';
569
+ for (let i = 0; i < length; i++) {
570
+ sequence += bases[Math.floor(Math.random() * 4)];
571
+ }
572
+ return sequence;
573
+ }
574
+
575
+ function displayResults() {
576
+ // Update overview stats
577
+ totalSequences.textContent = Object.keys(sequences).length;
578
+ totalMutations.textContent = mutationStats.total;
579
+ mutationRate.textContent = ((mutationStats.total / Object.keys(sequences).length) * 100).toFixed(1) + '%';
580
+
581
+ // Create charts
582
+ createMutationChart();
583
+ createMutationTypeChart();
584
+
585
+ // Populate sequence selector
586
+ sequenceSelect.innerHTML = '<option value="">Select a sequence to view</option>';
587
+ Object.keys(sequences).forEach(seqId => {
588
+ const option = document.createElement('option');
589
+ option.value = seqId;
590
+ option.textContent = seqId;
591
+ sequenceSelect.appendChild(option);
592
+ });
593
+
594
+ // Filter results based on selected options
595
+ filterResults();
596
+
597
+ // Show results sections
598
+ resultsOverview.classList.remove('hidden');
599
+ resultsTable.classList.remove('hidden');
600
+ sequenceViewer.classList.remove('hidden');
601
+
602
+ // Initialize table with first page
603
+ updateTable(1);
604
+ }
605
+
606
+ function filterResults() {
607
+ const model = document.getElementById('modelSelect').value;
608
+ const checkSNP = document.getElementById('checkSNP').checked;
609
+ const checkIndels = document.getElementById('checkIndels').checked;
610
+ const checkStructural = document.getElementById('checkStructural').checked;
611
+
612
+ filteredResults = analysisResults.filter(mutation => {
613
+ // Filter by mutation type
614
+ if (mutation.type === 'SNP' && !checkSNP) return false;
615
+ if ((mutation.type === 'Insertion' || mutation.type === 'Deletion') && !checkIndels) return false;
616
+ if (mutation.type === 'Complex' && !checkStructural) return false;
617
+
618
+ // In a real app, we would also filter by model results here
619
+ return true;
620
+ });
621
+
622
+ totalItems.textContent = filteredResults.length;
623
+ }
624
+
625
+ function updateTable(page) {
626
+ currentPage = page;
627
+ const startIdx = (page - 1) * itemsPerPage;
628
+ const endIdx = Math.min(startIdx + itemsPerPage, filteredResults.length);
629
+
630
+ resultsTableBody.innerHTML = '';
631
+
632
+ for (let i = startIdx; i < endIdx; i++) {
633
+ const mutation = filteredResults[i];
634
+ const row = document.createElement('tr');
635
+
636
+ // Highlight row based on mutation type
637
+ let rowClass = '';
638
+ if (mutation.type === 'SNP') rowClass = 'bg-red-50';
639
+ else if (mutation.type === 'Insertion') rowClass = 'bg-yellow-50';
640
+ else if (mutation.type === 'Deletion') rowClass = 'bg-blue-50';
641
+ else rowClass = 'bg-purple-50';
642
+
643
+ row.className = rowClass;
644
+ row.innerHTML = `
645
+ <td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">${mutation.seqId}</td>
646
+ <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">${mutation.position}</td>
647
+ <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">${mutation.refBase}</td>
648
+ <td class="px-6 py-4 whitespace-nowrap text-sm font-medium ${mutation.type === 'SNP' ? 'text-red-600' : mutation.type === 'Insertion' ? 'text-yellow-600' : mutation.type === 'Deletion' ? 'text-blue-600' : 'text-purple-600'}">${mutation.altBase}</td>
649
+ <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
650
+ <span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full
651
+ ${mutation.type === 'SNP' ? 'bg-red-100 text-red-800' :
652
+ mutation.type === 'Insertion' ? 'bg-yellow-100 text-yellow-800' :
653
+ mutation.type === 'Deletion' ? 'bg-blue-100 text-blue-800' : 'bg-purple-100 text-purple-800'}">
654
+ ${mutation.type}
655
+ </span>
656
+ </td>
657
+ <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
658
+ <div class="w-full bg-gray-200 rounded-full h-2.5">
659
+ <div class="bg-emerald-600 h-2.5 rounded-full" style="width: ${mutation.confidence * 100}%"></div>
660
+ </div>
661
+ <span class="text-xs">${mutation.confidence}</span>
662
+ </td>
663
+ `;
664
+
665
+ resultsTableBody.appendChild(row);
666
+ }
667
+
668
+ // Update pagination info
669
+ startItem.textContent = startIdx + 1;
670
+ endItem.textContent = endIdx;
671
+
672
+ // Update pagination buttons
673
+ prevPage.disabled = page === 1;
674
+ nextPage.disabled = endIdx >= filteredResults.length;
675
+ }
676
+
677
+ function displaySelectedSequence() {
678
+ const seqId = sequenceSelect.value;
679
+ if (!seqId) {
680
+ sequenceDisplay.innerHTML = '';
681
+ return;
682
+ }
683
+
684
+ const sequence = sequences[seqId];
685
+ const mutations = analysisResults.filter(m => m.seqId === seqId);
686
+
687
+ let html = '';
688
+ let position = 1;
689
+ let lineCount = 0;
690
+
691
+ // Split sequence into chunks of 50 bases for better readability
692
+ for (let i = 0; i < sequence.length; i += 50) {
693
+ const chunk = sequence.slice(i, i + 50);
694
+ let chunkHtml = '';
695
+
696
+ for (let j = 0; j < chunk.length; j++) {
697
+ const currentPos = i + j + 1;
698
+ const mutation = mutations.find(m => m.position === currentPos);
699
+
700
+ if (mutation) {
701
+ let baseClass = '';
702
+ if (mutation.type === 'SNP') baseClass = 'text-red-600 font-bold';
703
+ else if (mutation.type === 'Insertion') baseClass = 'text-yellow-600 font-bold';
704
+ else if (mutation.type === 'Deletion') baseClass = 'text-blue-600 font-bold';
705
+ else baseClass = 'text-purple-600 font-bold';
706
+
707
+ chunkHtml += `<span class="${baseClass} mutation-highlight" title="${mutation.type} at position ${currentPos}: ${mutation.refBase}→${mutation.altBase} (Confidence: ${mutation.confidence})">${chunk[j]}</span>`;
708
+ } else {
709
+ chunkHtml += `<span class="text-emerald-800">${chunk[j]}</span>`;
710
+ }
711
+
712
+ // Add space every 10 bases
713
+ if ((j + 1) % 10 === 0 && j !== chunk.length - 1) {
714
+ chunkHtml += ' ';
715
+ }
716
+ }
717
+
718
+ // Add position numbers
719
+ const startPos = i + 1;
720
+ const endPos = Math.min(i + 50, sequence.length);
721
+
722
+ html += `
723
+ <div class="mb-1">
724
+ <span class="text-gray-400 text-xs mr-4">${startPos.toString().padStart(5, ' ')}</span>
725
+ ${chunkHtml}
726
+ <span class="text-gray-400 text-xs ml-4">${endPos}</span>
727
+ </div>
728
+ `;
729
+
730
+ lineCount++;
731
+ if (lineCount % 5 === 0) {
732
+ html += '<div class="my-2 border-t border-gray-200"></div>';
733
+ }
734
+ }
735
+
736
+ sequenceDisplay.innerHTML = html;
737
+ }
738
+
739
+ function createMutationChart() {
740
+ const ctx = document.getElementById('mutationChart').getContext('2d');
741
+
742
+ // Group mutations by sequence
743
+ const mutationCounts = {};
744
+ Object.keys(sequences).forEach(seqId => {
745
+ mutationCounts[seqId] = analysisResults.filter(m => m.seqId === seqId).length;
746
+ });
747
+
748
+ new Chart(ctx, {
749
+ type: 'bar',
750
+ data: {
751
+ labels: Object.keys(mutationCounts),
752
+ datasets: [{
753
+ label: 'Mutations per Sequence',
754
+ data: Object.values(mutationCounts),
755
+ backgroundColor: 'rgba(74, 222, 128, 0.7)',
756
+ borderColor: 'rgba(74, 222, 128, 1)',
757
+ borderWidth: 1
758
+ }]
759
+ },
760
+ options: {
761
+ responsive: true,
762
+ maintainAspectRatio: false,
763
+ scales: {
764
+ y: {
765
+ beginAtZero: true,
766
+ title: {
767
+ display: true,
768
+ text: 'Number of Mutations'
769
+ }
770
+ },
771
+ x: {
772
+ title: {
773
+ display: true,
774
+ text: 'Sequence ID'
775
+ }
776
+ }
777
+ }
778
+ }
779
+ });
780
+ }
781
+
782
+ function createMutationTypeChart() {
783
+ const data = [{
784
+ values: [mutationStats.snps, mutationStats.insertions, mutationStats.deletions, mutationStats.others],
785
+ labels: ['SNPs', 'Insertions', 'Deletions', 'Others'],
786
+ marker: {
787
+ colors: ['#ef4444', '#f59e0b', '#3b82f6', '#8b5cf6']
788
+ },
789
+ type: 'pie',
790
+ textinfo: 'label+percent',
791
+ insidetextorientation: 'radial',
792
+ hoverinfo: 'label+value+percent',
793
+ hole: 0.4
794
+ }];
795
+
796
+ const layout = {
797
+ margin: { t: 0, b: 0, l: 0, r: 0 },
798
+ showlegend: false
799
+ };
800
+
801
+ Plotly.newPlot('mutationTypeChart', data, layout);
802
+ }
803
+
804
+ // Download functionality
805
+ document.getElementById('downloadCsv').addEventListener('click', downloadCsv);
806
+ document.getElementById('downloadPdf').addEventListener('click', downloadPdf);
807
+
808
+ function downloadCsv() {
809
+ if (filteredResults.length === 0) {
810
+ alert('No results to download');
811
+ return;
812
+ }
813
+
814
+ // Create CSV header
815
+ let csvContent = "Sequence ID,Position,Reference,Mutation,Type,Confidence\n";
816
+
817
+ // Add each mutation result
818
+ filteredResults.forEach(mutation => {
819
+ csvContent += `${mutation.seqId},${mutation.position},${mutation.refBase},${mutation.altBase},${mutation.type},${mutation.confidence}\n`;
820
+ });
821
+
822
+ // Create download link
823
+ const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' });
824
+ const url = URL.createObjectURL(blob);
825
+ const link = document.createElement('a');
826
+ link.setAttribute('href', url);
827
+ link.setAttribute('download', `mutatex_results_${new Date().toISOString().slice(0,10)}.csv`);
828
+ link.style.visibility = 'hidden';
829
+ document.body.appendChild(link);
830
+ link.click();
831
+ document.body.removeChild(link);
832
+ }
833
+
834
+ function downloadPdf() {
835
+ if (filteredResults.length === 0) {
836
+ alert('No results to download');
837
+ return;
838
+ }
839
+
840
+ const { jsPDF } = window.jspdf;
841
+ const doc = new jsPDF();
842
+
843
+ // Add title
844
+ doc.setFontSize(18);
845
+ doc.text('MutateX - Mutation Results', 14, 20);
846
+ doc.setFontSize(12);
847
+ doc.text(`Generated on: ${new Date().toLocaleString()}`, 14, 30);
848
+
849
+ // Prepare data for table
850
+ const tableData = filteredResults.map(mutation => [
851
+ mutation.seqId,
852
+ mutation.position,
853
+ mutation.refBase,
854
+ mutation.altBase,
855
+ mutation.type,
856
+ mutation.confidence
857
+ ]);
858
+
859
+ // Add table
860
+ doc.autoTable({
861
+ head: [['Sequence ID', 'Position', 'Reference', 'Mutation', 'Type', 'Confidence']],
862
+ body: tableData,
863
+ startY: 40,
864
+ styles: {
865
+ cellPadding: 5,
866
+ fontSize: 10,
867
+ valign: 'middle'
868
+ },
869
+ columnStyles: {
870
+ 0: { cellWidth: 'auto' },
871
+ 1: { cellWidth: 'auto' },
872
+ 2: { cellWidth: 'auto' },
873
+ 3: { cellWidth: 'auto' },
874
+ 4: { cellWidth: 'auto' },
875
+ 5: { cellWidth: 'auto' }
876
+ }
877
+ });
878
+
879
+ // Save the PDF
880
+ doc.save(`mutatex_results_${new Date().toISOString().slice(0,10)}.pdf`);
881
+ }
882
+
883
+ // Full report download functionality
884
+ document.getElementById('downloadFullCsv').addEventListener('click', downloadFullReportCsv);
885
+ document.getElementById('downloadFullPdf').addEventListener('click', downloadFullReportPdf);
886
+
887
+ function downloadFullReportCsv() {
888
+ if (analysisResults.length === 0) {
889
+ alert('No analysis results to download');
890
+ return;
891
+ }
892
+
893
+ // Create CSV header
894
+ let csvContent = "MutateX Full Analysis Report\n\n";
895
+ csvContent += "Overview Statistics\n";
896
+ csvContent += `Sequences Analyzed,${Object.keys(sequences).length}\n`;
897
+ csvContent += `Total Mutations Found,${mutationStats.total}\n`;
898
+ csvContent += `Mutation Rate,${((mutationStats.total / Object.keys(sequences).length) * 100).toFixed(1)}%\n\n`;
899
+
900
+ csvContent += "Mutation Type Distribution\n";
901
+ csvContent += `SNPs,${mutationStats.snps}\n`;
902
+ csvContent += `Insertions,${mutationStats.insertions}\n`;
903
+ csvContent += `Deletions,${mutationStats.deletions}\n`;
904
+ csvContent += `Other Mutations,${mutationStats.others}\n\n`;
905
+
906
+ csvContent += "Detailed Mutation Results\n";
907
+ csvContent += "Sequence ID,Position,Reference,Mutation,Type,Confidence\n";
908
+
909
+ // Add each mutation result
910
+ analysisResults.forEach(mutation => {
911
+ csvContent += `${mutation.seqId},${mutation.position},${mutation.refBase},${mutation.altBase},${mutation.type},${mutation.confidence}\n`;
912
+ });
913
+
914
+ // Create download link
915
+ const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' });
916
+ const url = URL.createObjectURL(blob);
917
+ const link = document.createElement('a');
918
+ link.setAttribute('href', url);
919
+ link.setAttribute('download', `mutatex_full_report_${new Date().toISOString().slice(0,10)}.csv`);
920
+ link.style.visibility = 'hidden';
921
+ document.body.appendChild(link);
922
+ link.click();
923
+ document.body.removeChild(link);
924
+ }
925
+
926
+ function downloadFullReportPdf() {
927
+ if (analysisResults.length === 0) {
928
+ alert('No analysis results to download');
929
+ return;
930
+ }
931
+
932
+ const { jsPDF } = window.jspdf;
933
+ const doc = new jsPDF();
934
+
935
+ // Add title and overview
936
+ doc.setFontSize(18);
937
+ doc.text('MutateX - Full Analysis Report', 14, 20);
938
+ doc.setFontSize(12);
939
+ doc.text(`Generated on: ${new Date().toLocaleString()}`, 14, 30);
940
+
941
+ // Add overview statistics
942
+ doc.setFontSize(14);
943
+ doc.text('Overview Statistics', 14, 50);
944
+ doc.setFontSize(12);
945
+ doc.text(`Sequences Analyzed: ${Object.keys(sequences).length}`, 14, 60);
946
+ doc.text(`Total Mutations Found: ${mutationStats.total}`, 14, 70);
947
+ doc.text(`Mutation Rate: ${((mutationStats.total / Object.keys(sequences).length) * 100).toFixed(1)}%`, 14, 80);
948
+
949
+ // Add mutation type distribution
950
+ doc.setFontSize(14);
951
+ doc.text('Mutation Type Distribution', 14, 100);
952
+ doc.setFontSize(12);
953
+ doc.text(`SNPs: ${mutationStats.snps}`, 14, 110);
954
+ doc.text(`Insertions: ${mutationStats.insertions}`, 14, 120);
955
+ doc.text(`Deletions: ${mutationStats.deletions}`, 14, 130);
956
+ doc.text(`Other Mutations: ${mutationStats.others}`, 14, 140);
957
+
958
+ // Add detailed results table
959
+ const tableData = analysisResults.map(mutation => [
960
+ mutation.seqId,
961
+ mutation.position,
962
+ mutation.refBase,
963
+ mutation.altBase,
964
+ mutation.type,
965
+ mutation.confidence
966
+ ]);
967
+
968
+ doc.autoTable({
969
+ head: [['Sequence ID', 'Position', 'Reference', 'Mutation', 'Type', 'Confidence']],
970
+ body: tableData,
971
+ startY: 160,
972
+ styles: {
973
+ cellPadding: 5,
974
+ fontSize: 8,
975
+ valign: 'middle'
976
+ },
977
+ columnStyles: {
978
+ 0: { cellWidth: 'auto' },
979
+ 1: { cellWidth: 'auto' },
980
+ 2: { cellWidth: 'auto' },
981
+ 3: { cellWidth: 'auto' },
982
+ 4: { cellWidth: 'auto' },
983
+ 5: { cellWidth: 'auto' }
984
+ }
985
+ });
986
+
987
+ // Save the PDF
988
+ doc.save(`mutatex_full_report_${new Date().toISOString().slice(0,10)}.pdf`);
989
+ }
990
+
991
+ // Initialize with empty results
992
+ filterResults();
993
+ updateTable(1);
994
+ </script>
995
+ <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=gradsyntax/mutatex" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
996
+ </html>