Babs Babs commited on
Commit
9ded4fe
·
1 Parent(s): 9e56b81

UI: latest design tweaks

Browse files
Files changed (2) hide show
  1. frontend/app.js +206 -19
  2. frontend/index.html +2 -2
frontend/app.js CHANGED
@@ -1,4 +1,38 @@
1
- const API_BASE = '/api';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2
  // Application state
3
  const state = {
4
  currentStep: 1,
@@ -169,12 +203,9 @@ function unlockNextStep() {
169
  }
170
 
171
  function validateStep1() {
172
- const mode = document.getElementById('setup-mode').value;
173
  const gpu = document.getElementById('gpu-device').value;
174
  const model = document.getElementById('base-model').value;
175
  const datasetSource = document.getElementById('dataset-source').value;
176
- const schema = document.getElementById('dataset-schema').value;
177
- const precision = document.getElementById('precision-mode').value;
178
 
179
  let isValid = true;
180
 
@@ -193,13 +224,21 @@ function validateStep1() {
193
  }
194
 
195
  function validateStep2() {
 
 
 
 
 
 
 
 
 
196
  const trainSplit = parseInt(document.getElementById('train-split').value);
197
  const valSplit = parseInt(document.getElementById('val-split').value);
198
  const testSplit = parseInt(document.getElementById('test-split').value);
199
 
200
  const total = trainSplit + valSplit + testSplit;
201
  const validationText = document.getElementById('split-validation');
202
- const continueBtn = document.getElementById('step-2-continue');
203
 
204
  if (total === 100) {
205
  validationText.textContent = `✓ Total: ${total}% (valid)`;
@@ -222,10 +261,15 @@ function validateStep4() {
222
 
223
  function updateConfigFromForm() {
224
  // Step 1 - Mode & Essentials
225
- state.mode = document.getElementById('setup-mode').value;
226
- state.config.compute.gpu = document.getElementById('gpu-device').value;
227
- state.config.model.repo = document.getElementById('base-model').value;
228
- state.config.model.precision_mode = document.getElementById('precision-mode').value;
 
 
 
 
 
229
 
230
  const datasetSource = document.getElementById('dataset-source').value;
231
  if (datasetSource === 'Local JSONL File') {
@@ -275,6 +319,127 @@ function handleDatasetSourceChange() {
275
  validateStep1();
276
  }
277
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
278
  function handleTrainingToggleChange() {
279
  const trainingEnabled = document.getElementById('training-toggle').value === 'enabled';
280
  const trainingNotice = document.getElementById('training-notice');
@@ -324,10 +489,28 @@ function setupEventListeners() {
324
  smoothScrollTo(elements.wizardSection);
325
  });
326
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
327
  // Step 1 form elements
328
  const step1Elements = [
329
- 'setup-mode', 'gpu-device', 'base-model', 'dataset-source',
330
- 'dataset-schema', 'precision-mode', 'file-path'
 
331
  ];
332
 
333
  step1Elements.forEach(id => {
@@ -477,20 +660,21 @@ function setupEventListeners() {
477
  }, 500);
478
  });
479
 
480
- // File picker buttons (demo only)
481
- document.querySelectorAll('.file-picker-btn').forEach(btn => {
482
- btn.addEventListener('click', function() {
483
- showToast('File picker not implemented in demo', 'warning');
484
- });
485
- });
486
  }
487
 
488
  // Initialize form values
489
  function initializeFormValues() {
490
- // Step 1
491
- document.getElementById('setup-mode').value = state.mode;
492
  document.getElementById('gpu-device').value = state.config.compute.gpu;
493
  document.getElementById('base-model').value = state.config.model.repo;
 
 
 
 
 
 
494
  document.getElementById('precision-mode').value = state.config.model.precision_mode;
495
  document.getElementById('dataset-schema').value = state.config.data.schema;
496
  document.getElementById('file-path').value = state.config.data.raw_path;
@@ -526,6 +710,9 @@ function init() {
526
  // Initialize UI state
527
  showStep(1);
528
  handleDatasetSourceChange();
 
 
 
529
  updateJsonPreview();
530
  updateCommandPreview();
531
  validateStep1();
 
1
+ // Constants for default values
2
+ const DEFAULTS_FOR_BASIC = {
3
+ 'epochs': '3',
4
+ 'learning-rate': '', // Always start empty as per spec
5
+ 'warmup-ratio': '0.03',
6
+ 'weight-decay': '0',
7
+ 'scheduler': 'linear',
8
+ 'max-seq-len': '2048',
9
+ 'packing': 'on',
10
+ 'template': 'qwen_chat_basic_v1',
11
+ 'train-split': '80',
12
+ 'val-split': '10',
13
+ 'test-split': '10',
14
+ 'lora-rank': '8',
15
+ 'lora-alpha': '16',
16
+ 'lora-dropout': '0.05',
17
+ 'dataset-schema': 'chat_messages',
18
+ 'precision-mode': 'qlora_nf4'
19
+ };
20
+
21
+ // Function to prefill advanced defaults when in Basic mode
22
+ function prefillAdvancedDefaults() {
23
+ for (const [fieldId, value] of Object.entries(DEFAULTS_FOR_BASIC)) {
24
+ const element = document.getElementById(fieldId);
25
+ if (element && fieldId !== 'learning-rate') {
26
+ element.value = value;
27
+ }
28
+ }
29
+ // Always keep learning rate empty
30
+ const learningRate = document.getElementById('learning-rate');
31
+ if (learningRate) {
32
+ learningRate.value = '';
33
+ }
34
+ }
35
+
36
  // Application state
37
  const state = {
38
  currentStep: 1,
 
203
  }
204
 
205
  function validateStep1() {
 
206
  const gpu = document.getElementById('gpu-device').value;
207
  const model = document.getElementById('base-model').value;
208
  const datasetSource = document.getElementById('dataset-source').value;
 
 
209
 
210
  let isValid = true;
211
 
 
224
  }
225
 
226
  function validateStep2() {
227
+ const continueBtn = document.getElementById('step-2-continue');
228
+
229
+ // In basic mode, skip validation since advanced fields are hidden
230
+ if (state.mode === 'basic') {
231
+ continueBtn.disabled = false;
232
+ return true;
233
+ }
234
+
235
+ // In advanced mode, validate split ratios
236
  const trainSplit = parseInt(document.getElementById('train-split').value);
237
  const valSplit = parseInt(document.getElementById('val-split').value);
238
  const testSplit = parseInt(document.getElementById('test-split').value);
239
 
240
  const total = trainSplit + valSplit + testSplit;
241
  const validationText = document.getElementById('split-validation');
 
242
 
243
  if (total === 100) {
244
  validationText.textContent = `✓ Total: ${total}% (valid)`;
 
261
 
262
  function updateConfigFromForm() {
263
  // Step 1 - Mode & Essentials
264
+ if (state.mode === 'basic') {
265
+ state.config.compute.gpu = document.getElementById('gpu-device').value;
266
+ state.config.model.repo = document.getElementById('base-model').value;
267
+ state.config.model.precision_mode = document.getElementById('precision-mode-basic').value;
268
+ } else {
269
+ state.config.compute.gpu = document.getElementById('gpu-device-adv').value;
270
+ state.config.model.repo = document.getElementById('base-model-adv').value;
271
+ state.config.model.precision_mode = document.getElementById('precision-mode').value;
272
+ }
273
 
274
  const datasetSource = document.getElementById('dataset-source').value;
275
  if (datasetSource === 'Local JSONL File') {
 
319
  validateStep1();
320
  }
321
 
322
+ function handleTabSwitch(tabName) {
323
+ console.log('Switching to tab:', tabName);
324
+
325
+ // Direct DOM manipulation - no setTimeout needed
326
+ const basicTab = document.getElementById('tab-basic');
327
+ const advancedTab = document.getElementById('tab-advanced');
328
+ const basicContent = document.getElementById('basic-content');
329
+ const advancedContent = document.getElementById('advanced-content');
330
+
331
+ console.log('Elements found:', { basicTab, advancedTab, basicContent, advancedContent });
332
+ console.log('Basic content element:', basicContent ? 'FOUND' : 'NOT FOUND');
333
+ console.log('Advanced content element:', advancedContent ? 'FOUND' : 'NOT FOUND');
334
+ console.log('DOM query check - basic:', document.querySelector('#basic-content'));
335
+ console.log('DOM query check - advanced:', document.querySelector('#advanced-content'));
336
+
337
+ // Remove active from all tabs and content
338
+ if (basicTab) basicTab.classList.remove('active');
339
+ if (advancedTab) advancedTab.classList.remove('active');
340
+ if (basicContent) basicContent.classList.remove('active');
341
+ if (advancedContent) advancedContent.classList.remove('active');
342
+
343
+ // Activate the selected tab with both classes and inline styles
344
+ if (tabName === 'basic') {
345
+ if (basicTab) basicTab.classList.add('active');
346
+ if (basicContent) {
347
+ basicContent.classList.add('active');
348
+ basicContent.style.display = 'block';
349
+ }
350
+ if (advancedContent) {
351
+ advancedContent.style.display = 'none';
352
+ }
353
+ } else if (tabName === 'advanced') {
354
+ if (advancedTab) advancedTab.classList.add('active');
355
+ if (advancedContent) {
356
+ advancedContent.classList.add('active');
357
+ advancedContent.style.display = 'block';
358
+ }
359
+ if (basicContent) {
360
+ basicContent.style.display = 'none';
361
+ }
362
+ }
363
+
364
+ // Update mode indicator
365
+ const modeIndicator = document.getElementById('mode-indicator');
366
+ if (modeIndicator) {
367
+ modeIndicator.textContent = tabName === 'basic' ? 'Basic' : 'Advanced';
368
+ modeIndicator.className = tabName === 'basic' ? 'mode-indicator basic' : 'mode-indicator';
369
+ }
370
+
371
+ // Update state
372
+ state.mode = tabName;
373
+
374
+ if (tabName === 'basic') {
375
+ // Hide other steps and progress bar in Basic mode
376
+ document.getElementById('step-2').classList.add('hidden');
377
+ document.getElementById('step-3').classList.add('hidden');
378
+ document.getElementById('step-4').classList.add('hidden');
379
+ document.getElementById('step-5').classList.add('hidden');
380
+ document.querySelector('.progress-bar').classList.add('hidden');
381
+
382
+ // Apply defaults for basic mode
383
+ prefillAdvancedDefaults();
384
+ } else {
385
+ // Show all steps and progress bar in Advanced mode
386
+ document.getElementById('step-2').classList.remove('hidden');
387
+ document.getElementById('step-3').classList.remove('hidden');
388
+ document.getElementById('step-4').classList.remove('hidden');
389
+ document.getElementById('step-5').classList.remove('hidden');
390
+ document.querySelector('.progress-bar').classList.remove('hidden');
391
+ }
392
+
393
+ // Update previews
394
+ updateConfigFromForm();
395
+ updateJsonPreview();
396
+ updateCommandPreview();
397
+
398
+ showToast(`Switched to ${tabName === 'basic' ? 'Basic' : 'Advanced'} mode`);
399
+ console.log('Tab switch complete - Basic display:', basicContent ? basicContent.style.display : 'N/A');
400
+ console.log('Tab switch complete - Advanced display:', advancedContent ? advancedContent.style.display : 'N/A');
401
+ }
402
+
403
+ function setupFileBrowser() {
404
+ const fileInput = document.getElementById('file-input');
405
+ const browseBtn = document.getElementById('file-browse-btn');
406
+ const filePathInput = document.getElementById('file-path');
407
+
408
+ if (!fileInput || !browseBtn || !filePathInput) return;
409
+
410
+ browseBtn.addEventListener('click', () => {
411
+ fileInput.click();
412
+ });
413
+
414
+ fileInput.addEventListener('change', (e) => {
415
+ const file = e.target.files[0];
416
+ if (file) {
417
+ // Show only filename, not full path for security
418
+ filePathInput.value = file.name;
419
+ filePathInput.removeAttribute('readonly');
420
+ filePathInput.setAttribute('placeholder', 'File selected: ' + file.name);
421
+ validateStep1();
422
+ }
423
+ });
424
+
425
+ // Allow manual editing of file path
426
+ filePathInput.addEventListener('click', () => {
427
+ if (filePathInput.hasAttribute('readonly')) {
428
+ filePathInput.removeAttribute('readonly');
429
+ filePathInput.focus();
430
+ }
431
+ });
432
+
433
+ filePathInput.addEventListener('blur', () => {
434
+ if (!filePathInput.value.trim()) {
435
+ filePathInput.setAttribute('readonly', '');
436
+ filePathInput.value = '';
437
+ filePathInput.setAttribute('placeholder', 'Select a file or paste a path');
438
+ }
439
+ validateStep1();
440
+ });
441
+ }
442
+
443
  function handleTrainingToggleChange() {
444
  const trainingEnabled = document.getElementById('training-toggle').value === 'enabled';
445
  const trainingNotice = document.getElementById('training-notice');
 
489
  smoothScrollTo(elements.wizardSection);
490
  });
491
 
492
+ // Tab buttons
493
+ const tabBasic = document.getElementById('tab-basic');
494
+ const tabAdvanced = document.getElementById('tab-advanced');
495
+
496
+ if (tabBasic && tabAdvanced) {
497
+ tabBasic.addEventListener('click', () => {
498
+ console.log('Basic tab clicked');
499
+ handleTabSwitch('basic');
500
+ });
501
+ tabAdvanced.addEventListener('click', () => {
502
+ console.log('Advanced tab clicked');
503
+ handleTabSwitch('advanced');
504
+ });
505
+ } else {
506
+ console.error('Tab buttons not found:', { tabBasic, tabAdvanced });
507
+ }
508
+
509
  // Step 1 form elements
510
  const step1Elements = [
511
+ 'gpu-device', 'base-model', 'download-model', 'dataset-source',
512
+ 'dataset-schema', 'precision-mode', 'precision-mode-basic', 'file-path',
513
+ 'gpu-device-adv', 'base-model-adv', 'dataset-source-adv'
514
  ];
515
 
516
  step1Elements.forEach(id => {
 
660
  }, 500);
661
  });
662
 
663
+ // Setup file browser functionality
664
+ setupFileBrowser();
 
 
 
 
665
  }
666
 
667
  // Initialize form values
668
  function initializeFormValues() {
669
+ // Initialize both Basic and Advanced form values
 
670
  document.getElementById('gpu-device').value = state.config.compute.gpu;
671
  document.getElementById('base-model').value = state.config.model.repo;
672
+ document.getElementById('download-model').value = 'yes';
673
+ document.getElementById('precision-mode-basic').value = state.config.model.precision_mode;
674
+
675
+ // Advanced tab values
676
+ document.getElementById('gpu-device-adv').value = state.config.compute.gpu;
677
+ document.getElementById('base-model-adv').value = state.config.model.repo;
678
  document.getElementById('precision-mode').value = state.config.model.precision_mode;
679
  document.getElementById('dataset-schema').value = state.config.data.schema;
680
  document.getElementById('file-path').value = state.config.data.raw_path;
 
710
  // Initialize UI state
711
  showStep(1);
712
  handleDatasetSourceChange();
713
+
714
+ // Apply initial tab state - start with Basic mode
715
+ handleTabSwitch('basic');
716
  updateJsonPreview();
717
  updateCommandPreview();
718
  validateStep1();
frontend/index.html CHANGED
@@ -4,7 +4,7 @@
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
  <title>Humigence - Local MLOps Suite</title>
7
- <link rel="stylesheet" href="styles.css">
8
  </head>
9
  <body>
10
  <!-- Toast notifications -->
@@ -489,5 +489,5 @@
489
  </div>
490
  </section>
491
  </body>
492
- <script src="app.js"></script>
493
  </html>
 
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
  <title>Humigence - Local MLOps Suite</title>
7
+ <link rel="stylesheet" href="styles.css?v=2">
8
  </head>
9
  <body>
10
  <!-- Toast notifications -->
 
489
  </div>
490
  </section>
491
  </body>
492
+ <script src="app.js?v=2"></script>
493
  </html>