@morscherlab/mld-sdk 0.7.6 → 0.7.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -101,7 +101,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
101
101
  autoGroup.rawText.value = props.samples.join("\n");
102
102
  }
103
103
  }
104
- });
104
+ }, { immediate: true });
105
105
  const allSteps = [
106
106
  { id: "input", label: "Input" },
107
107
  { id: "outliers", label: "Outliers" },
@@ -297,7 +297,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
297
297
  "step-outliers": withCtx(() => [
298
298
  createElementVNode("div", _hoisted_10, [
299
299
  createElementVNode("div", _hoisted_11, [
300
- createTextVNode(toDisplayString(unref(autoGroup).outliers.value.length) + " of " + toDisplayString(unref(autoGroup).samples.value.length) + " samples have irregular structure (fewer than " + toDisplayString(unref(autoGroup).minFieldCount.value) + " fields, delimiter ", 1),
300
+ createTextVNode(toDisplayString(unref(autoGroup).outliers.value.length) + " of " + toDisplayString(unref(autoGroup).samples.value.length) + " samples have irregular structure (fewer than " + toDisplayString(unref(autoGroup).dominantFieldCount.value) + " fields, delimiter ", 1),
301
301
  createElementVNode("code", null, toDisplayString(unref(autoGroup).delimiter.value), 1),
302
302
  _cache[14] || (_cache[14] = createTextVNode(") ", -1))
303
303
  ]),
@@ -1 +1 @@
1
- {"version":3,"file":"AutoGroupModal.vue.js","sources":["../../src/components/AutoGroupModal.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { ref, computed, watch } from 'vue'\nimport BaseModal from './BaseModal.vue'\nimport BaseButton from './BaseButton.vue'\nimport BaseInput from './BaseInput.vue'\nimport StepWizard from './StepWizard.vue'\nimport { useAutoGroup, parseCSV } from '../composables/useAutoGroup'\nimport type { WizardStep } from '../types/components'\nimport type { AutoGroupResult } from '../types/auto-group'\n\ninterface Props {\n modelValue: boolean\n samples?: string[]\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n samples: () => [],\n})\n\nconst emit = defineEmits<{\n 'update:modelValue': [value: boolean]\n 'apply': [result: AutoGroupResult]\n}>()\n\nconst autoGroup = useAutoGroup()\nconst wizardRef = ref<InstanceType<typeof StepWizard> | null>(null)\nconst currentStep = ref(0)\nconst isDragOver = ref(false)\nconst csvFileName = ref('')\n\n// Pre-fill from props\nwatch(() => props.modelValue, (open) => {\n if (open) {\n autoGroup.reset()\n currentStep.value = 0\n csvFileName.value = ''\n if (props.samples.length > 0) {\n autoGroup.rawText.value = props.samples.join('\\n')\n }\n }\n})\n\n// Dynamic steps: skip outlier step when no outliers\nconst allSteps: WizardStep[] = [\n { id: 'input', label: 'Input' },\n { id: 'outliers', label: 'Outliers' },\n { id: 'fields', label: 'Fields' },\n { id: 'preview', label: 'Preview' },\n]\n\nconst dynamicSteps = computed<WizardStep[]>(() => {\n if (autoGroup.hasOutliers.value) {\n return allSteps\n }\n return allSteps.filter(s => s.id !== 'outliers')\n})\n\n// Map current step index to step ID\nconst currentStepId = computed(() => {\n const step = dynamicSteps.value[currentStep.value]\n return step?.id ?? 'input'\n})\n\n// Clamp step when dynamic steps change\nwatch(() => dynamicSteps.value.length, (newLen) => {\n if (currentStep.value >= newLen) {\n currentStep.value = newLen - 1\n }\n})\n\n// Step validity\nconst inputValid = computed(() => autoGroup.samples.value.length > 0)\nconst fieldsValid = computed(() => autoGroup.enabledFields.value.size > 0)\n\nwatch([inputValid, fieldsValid, dynamicSteps], () => {\n if (!wizardRef.value) return\n const steps = dynamicSteps.value\n for (let i = 0; i < steps.length; i++) {\n const id = steps[i].id\n if (id === 'input') wizardRef.value.setStepValid(i, inputValid.value)\n else if (id === 'outliers') wizardRef.value.setStepValid(i, true)\n else if (id === 'fields') wizardRef.value.setStepValid(i, fieldsValid.value)\n else if (id === 'preview') wizardRef.value.setStepValid(i, true)\n }\n}, { immediate: true })\n\n// Navigation\nfunction handleNext() {\n if (currentStepId.value === 'input') {\n autoGroup.parseInput()\n }\n // StepWizard handles the actual navigation via v-model\n}\n\nfunction handleApply() {\n emit('apply', autoGroup.result.value)\n emit('update:modelValue', false)\n}\n\nfunction handleCancel() {\n emit('update:modelValue', false)\n}\n\n// CSV file handling\nfunction handleFileDrop(event: DragEvent) {\n event.preventDefault()\n isDragOver.value = false\n const files = event.dataTransfer?.files\n if (files && files.length > 0) {\n processFile(files[0])\n }\n}\n\nfunction handleFileInput(event: Event) {\n const target = event.target as HTMLInputElement\n if (target.files && target.files.length > 0) {\n processFile(target.files[0])\n }\n}\n\nasync function processFile(file: File) {\n if (!file.name.endsWith('.csv') && !file.name.endsWith('.tsv')) return\n\n try {\n const text = await file.text()\n const parsed = parseCSV(text)\n autoGroup.inputMode.value = 'csv'\n autoGroup.csvData.value = parsed\n csvFileName.value = file.name\n } catch {\n // CSV parse error — stay in current state\n }\n}\n\nfunction clearCsvFile() {\n autoGroup.csvData.value = null\n autoGroup.inputMode.value = 'paste'\n csvFileName.value = ''\n}\n\n// Last step check\nconst isLastStep = computed(() => currentStep.value === dynamicSteps.value.length - 1)\nconst isFirstStep = computed(() => currentStep.value === 0)\n</script>\n\n<template>\n <BaseModal\n :model-value=\"modelValue\"\n title=\"Smart Group\"\n size=\"lg\"\n @update:model-value=\"emit('update:modelValue', $event)\"\n @close=\"handleCancel\"\n >\n <div class=\"mld-auto-group\">\n <StepWizard\n ref=\"wizardRef\"\n v-model=\"currentStep\"\n :steps=\"dynamicSteps\"\n :linear=\"true\"\n size=\"sm\"\n >\n <!-- Step: Input -->\n <template #step-input>\n <div class=\"mld-auto-group__input-step\">\n <!-- Mode toggle -->\n <div class=\"mld-auto-group__mode-toggle\">\n <BaseButton\n :variant=\"autoGroup.inputMode.value === 'paste' ? 'primary' : 'secondary'\"\n size=\"sm\"\n @click=\"autoGroup.inputMode.value = 'paste'\"\n >\n Paste\n </BaseButton>\n <BaseButton\n :variant=\"autoGroup.inputMode.value === 'csv' ? 'primary' : 'secondary'\"\n size=\"sm\"\n @click=\"autoGroup.inputMode.value = 'csv'\"\n >\n CSV\n </BaseButton>\n </div>\n\n <!-- Paste mode -->\n <div v-if=\"autoGroup.inputMode.value === 'paste'\" class=\"mld-auto-group__paste\">\n <textarea\n v-model=\"autoGroup.rawText.value\"\n class=\"mld-auto-group__textarea\"\n rows=\"12\"\n placeholder=\"Paste sample names, one per line...\"\n />\n <div v-if=\"autoGroup.samples.value.length > 0\" class=\"mld-auto-group__sample-count\">\n {{ autoGroup.samples.value.length }} samples\n </div>\n </div>\n\n <!-- CSV mode -->\n <div v-if=\"autoGroup.inputMode.value === 'csv'\" class=\"mld-auto-group__csv\">\n <div v-if=\"!autoGroup.csvData.value\"\n :class=\"[\n 'mld-auto-group__dropzone',\n isDragOver ? 'mld-auto-group__dropzone--dragover' : '',\n ]\"\n @drop=\"handleFileDrop\"\n @dragover.prevent=\"isDragOver = true\"\n @dragleave=\"isDragOver = false\"\n >\n <input\n type=\"file\"\n accept=\".csv,.tsv\"\n class=\"mld-auto-group__file-input\"\n @change=\"handleFileInput\"\n />\n <svg class=\"mld-auto-group__upload-icon\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.5\" d=\"M9 17v-2m3 2v-4m3 4v-6m2 10H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z\" />\n </svg>\n <p class=\"mld-auto-group__upload-text\">\n <span class=\"mld-auto-group__upload-highlight\">Click to upload</span>\n or drag and drop a CSV file\n </p>\n </div>\n\n <div v-else class=\"mld-auto-group__file-info\">\n <svg class=\"mld-auto-group__file-icon\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z\" />\n </svg>\n <span class=\"mld-auto-group__file-name\">{{ csvFileName }}</span>\n <span class=\"mld-auto-group__file-rows\">{{ autoGroup.csvData.value.rows.length }} rows</span>\n <button type=\"button\" class=\"mld-auto-group__file-clear\" @click=\"clearCsvFile\">\n Change file\n </button>\n </div>\n </div>\n </div>\n </template>\n\n <!-- Step: Outliers -->\n <template #step-outliers>\n <div class=\"mld-auto-group__outlier-step\">\n <div class=\"mld-auto-group__outlier-banner\">\n {{ autoGroup.outliers.value.length }} of {{ autoGroup.samples.value.length }}\n samples have irregular structure\n (fewer than {{ autoGroup.minFieldCount.value }} fields, delimiter\n <code>{{ autoGroup.delimiter.value }}</code>)\n </div>\n\n <div class=\"mld-auto-group__outlier-batch\">\n <BaseButton size=\"sm\" variant=\"secondary\" @click=\"autoGroup.setAllOutlierActions('include')\">\n Include All\n </BaseButton>\n <BaseButton size=\"sm\" variant=\"secondary\" @click=\"autoGroup.setAllOutlierActions('exclude')\">\n Exclude All\n </BaseButton>\n </div>\n\n <div class=\"mld-auto-group__outlier-list\">\n <div\n v-for=\"outlier in autoGroup.outliers.value\"\n :key=\"outlier.index\"\n class=\"mld-auto-group__outlier-item\"\n >\n <div class=\"mld-auto-group__outlier-info\">\n <code class=\"mld-auto-group__outlier-name\">{{ outlier.sample }}</code>\n <span class=\"mld-auto-group__outlier-badge\">{{ outlier.fieldCount }} fields</span>\n </div>\n <div class=\"mld-auto-group__outlier-actions\">\n <button\n type=\"button\"\n :class=\"['mld-auto-group__action-btn', outlier.action === 'include' ? 'mld-auto-group__action-btn--active' : '']\"\n @click=\"autoGroup.setOutlierAction(outlier.index, 'include')\"\n >Include</button>\n <button\n type=\"button\"\n :class=\"['mld-auto-group__action-btn', outlier.action === 'exclude' ? 'mld-auto-group__action-btn--active mld-auto-group__action-btn--exclude' : '']\"\n @click=\"autoGroup.setOutlierAction(outlier.index, 'exclude')\"\n >Exclude</button>\n <button\n type=\"button\"\n :class=\"['mld-auto-group__action-btn', outlier.action === 'qc' ? 'mld-auto-group__action-btn--active mld-auto-group__action-btn--qc' : '']\"\n @click=\"autoGroup.setOutlierAction(outlier.index, 'qc')\"\n >QC</button>\n </div>\n </div>\n </div>\n </div>\n </template>\n\n <!-- Step: Fields -->\n <template #step-fields>\n <div class=\"mld-auto-group__field-step\">\n <div class=\"mld-auto-group__field-grid\">\n <div\n v-for=\"col in autoGroup.effectiveColumns.value\"\n :key=\"col.index\"\n :class=\"[\n 'mld-auto-group__field-card',\n autoGroup.enabledFields.value.has(col.index) ? 'mld-auto-group__field-card--enabled' : '',\n ]\"\n >\n <div class=\"mld-auto-group__field-header\">\n <label class=\"mld-auto-group__field-toggle\">\n <input\n type=\"checkbox\"\n :checked=\"autoGroup.enabledFields.value.has(col.index)\"\n @change=\"autoGroup.toggleField(col.index)\"\n />\n <span class=\"mld-auto-group__field-toggle-label\">Group by</span>\n </label>\n <span class=\"mld-auto-group__field-cardinality\">{{ col.cardinality }} unique</span>\n </div>\n\n <BaseInput\n :model-value=\"col.name\"\n placeholder=\"Field name...\"\n class=\"mld-auto-group__field-name-input\"\n @update:model-value=\"autoGroup.renameField(col.index, String($event ?? ''))\"\n />\n\n <div class=\"mld-auto-group__field-values\">\n <span\n v-for=\"val in col.uniqueValues.slice(0, 5)\"\n :key=\"val\"\n class=\"mld-auto-group__field-value\"\n >{{ val }}</span>\n <span\n v-if=\"col.uniqueValues.length > 5\"\n class=\"mld-auto-group__field-more\"\n >+{{ col.uniqueValues.length - 5 }} more</span>\n </div>\n </div>\n </div>\n\n <div v-if=\"autoGroup.enabledFields.value.size > 0\" class=\"mld-auto-group__field-summary\">\n Grouping by:\n <strong>{{\n autoGroup.effectiveColumns.value\n .filter(c => autoGroup.enabledFields.value.has(c.index))\n .map(c => c.name)\n .join(' / ')\n }}</strong>\n </div>\n\n <div v-if=\"autoGroup.enabledFields.value.size === 0\" class=\"mld-auto-group__field-warning\">\n Select at least one field for grouping\n </div>\n </div>\n </template>\n\n <!-- Step: Preview -->\n <template #step-preview>\n <div class=\"mld-auto-group__preview-step\">\n <div class=\"mld-auto-group__preview-summary\">\n <span class=\"mld-auto-group__preview-stat\">\n <strong>{{ autoGroup.groups.value.length }}</strong> groups\n </span>\n <span class=\"mld-auto-group__preview-stat\">\n <strong>{{ autoGroup.groups.value.reduce((sum, g) => sum + g.samples.length, 0) }}</strong> samples\n </span>\n <span v-if=\"autoGroup.excludedSamples.value.length > 0\" class=\"mld-auto-group__preview-stat mld-auto-group__preview-stat--excluded\">\n <strong>{{ autoGroup.excludedSamples.value.length }}</strong> excluded\n </span>\n </div>\n\n <div class=\"mld-auto-group__preview-groups\">\n <details\n v-for=\"group in autoGroup.groups.value\"\n :key=\"group.name\"\n class=\"mld-auto-group__preview-group\"\n >\n <summary class=\"mld-auto-group__preview-group-header\">\n <span\n class=\"mld-auto-group__preview-dot\"\n :style=\"{ backgroundColor: group.color }\"\n />\n <span class=\"mld-auto-group__preview-group-name\">{{ group.name }}</span>\n <span\n class=\"mld-auto-group__preview-group-count\"\n :style=\"{ backgroundColor: group.color + '20', color: group.color }\"\n >{{ group.samples.length }}</span>\n </summary>\n <div class=\"mld-auto-group__preview-samples\">\n <span\n v-for=\"sample in group.samples\"\n :key=\"sample\"\n class=\"mld-auto-group__preview-sample\"\n >{{ sample }}</span>\n </div>\n </details>\n </div>\n\n <div v-if=\"autoGroup.excludedSamples.value.length > 0\" class=\"mld-auto-group__preview-excluded\">\n <div class=\"mld-auto-group__preview-excluded-title\">Excluded Samples</div>\n <div class=\"mld-auto-group__preview-excluded-list\">\n <span\n v-for=\"sample in autoGroup.excludedSamples.value\"\n :key=\"sample\"\n class=\"mld-auto-group__preview-sample mld-auto-group__preview-sample--excluded\"\n >{{ sample }}</span>\n </div>\n </div>\n </div>\n </template>\n\n <!-- Custom navigation -->\n <template #navigation=\"{ goBack, goNext, canProceed }\">\n <div class=\"mld-auto-group__nav\">\n <BaseButton variant=\"secondary\" @click=\"handleCancel\">\n Cancel\n </BaseButton>\n <div style=\"flex: 1\" />\n <BaseButton\n v-if=\"!isFirstStep\"\n variant=\"secondary\"\n @click=\"goBack\"\n >\n Back\n </BaseButton>\n <BaseButton\n v-if=\"!isLastStep\"\n variant=\"primary\"\n :disabled=\"!canProceed\"\n @click=\"() => { handleNext(); goNext() }\"\n >\n Next\n </BaseButton>\n <BaseButton\n v-if=\"isLastStep\"\n variant=\"primary\"\n @click=\"handleApply\"\n >\n Apply\n </BaseButton>\n </div>\n </template>\n </StepWizard>\n </div>\n </BaseModal>\n</template>\n\n<style>\n@import '../styles/components/auto-group-modal.css';\n</style>\n"],"names":["_createBlock","BaseModal","_createElementVNode","_createVNode","StepWizard","BaseButton","_unref","_openBlock","_createElementBlock","_toDisplayString","_normalizeClass","_Fragment","BaseInput","_normalizeStyle","_renderList"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAeA,UAAM,QAAQ;AAId,UAAM,OAAO;AAKb,UAAM,YAAY,aAAA;AAClB,UAAM,YAAY,IAA4C,IAAI;AAClE,UAAM,cAAc,IAAI,CAAC;AACzB,UAAM,aAAa,IAAI,KAAK;AAC5B,UAAM,cAAc,IAAI,EAAE;AAG1B,UAAM,MAAM,MAAM,YAAY,CAAC,SAAS;AACtC,UAAI,MAAM;AACR,kBAAU,MAAA;AACV,oBAAY,QAAQ;AACpB,oBAAY,QAAQ;AACpB,YAAI,MAAM,QAAQ,SAAS,GAAG;AAC5B,oBAAU,QAAQ,QAAQ,MAAM,QAAQ,KAAK,IAAI;AAAA,QACnD;AAAA,MACF;AAAA,IACF,CAAC;AAGD,UAAM,WAAyB;AAAA,MAC7B,EAAE,IAAI,SAAS,OAAO,QAAA;AAAA,MACtB,EAAE,IAAI,YAAY,OAAO,WAAA;AAAA,MACzB,EAAE,IAAI,UAAU,OAAO,SAAA;AAAA,MACvB,EAAE,IAAI,WAAW,OAAO,UAAA;AAAA,IAAU;AAGpC,UAAM,eAAe,SAAuB,MAAM;AAChD,UAAI,UAAU,YAAY,OAAO;AAC/B,eAAO;AAAA,MACT;AACA,aAAO,SAAS,OAAO,CAAA,MAAK,EAAE,OAAO,UAAU;AAAA,IACjD,CAAC;AAGD,UAAM,gBAAgB,SAAS,MAAM;AACnC,YAAM,OAAO,aAAa,MAAM,YAAY,KAAK;AACjD,cAAO,6BAAM,OAAM;AAAA,IACrB,CAAC;AAGD,UAAM,MAAM,aAAa,MAAM,QAAQ,CAAC,WAAW;AACjD,UAAI,YAAY,SAAS,QAAQ;AAC/B,oBAAY,QAAQ,SAAS;AAAA,MAC/B;AAAA,IACF,CAAC;AAGD,UAAM,aAAa,SAAS,MAAM,UAAU,QAAQ,MAAM,SAAS,CAAC;AACpE,UAAM,cAAc,SAAS,MAAM,UAAU,cAAc,MAAM,OAAO,CAAC;AAEzE,UAAM,CAAC,YAAY,aAAa,YAAY,GAAG,MAAM;AACnD,UAAI,CAAC,UAAU,MAAO;AACtB,YAAM,QAAQ,aAAa;AAC3B,eAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;AACrC,cAAM,KAAK,MAAM,CAAC,EAAE;AACpB,YAAI,OAAO,QAAS,WAAU,MAAM,aAAa,GAAG,WAAW,KAAK;AAAA,iBAC3D,OAAO,WAAY,WAAU,MAAM,aAAa,GAAG,IAAI;AAAA,iBACvD,OAAO,SAAU,WAAU,MAAM,aAAa,GAAG,YAAY,KAAK;AAAA,iBAClE,OAAO,UAAW,WAAU,MAAM,aAAa,GAAG,IAAI;AAAA,MACjE;AAAA,IACF,GAAG,EAAE,WAAW,MAAM;AAGtB,aAAS,aAAa;AACpB,UAAI,cAAc,UAAU,SAAS;AACnC,kBAAU,WAAA;AAAA,MACZ;AAAA,IAEF;AAEA,aAAS,cAAc;AACrB,WAAK,SAAS,UAAU,OAAO,KAAK;AACpC,WAAK,qBAAqB,KAAK;AAAA,IACjC;AAEA,aAAS,eAAe;AACtB,WAAK,qBAAqB,KAAK;AAAA,IACjC;AAGA,aAAS,eAAe,OAAkB;;AACxC,YAAM,eAAA;AACN,iBAAW,QAAQ;AACnB,YAAM,SAAQ,WAAM,iBAAN,mBAAoB;AAClC,UAAI,SAAS,MAAM,SAAS,GAAG;AAC7B,oBAAY,MAAM,CAAC,CAAC;AAAA,MACtB;AAAA,IACF;AAEA,aAAS,gBAAgB,OAAc;AACrC,YAAM,SAAS,MAAM;AACrB,UAAI,OAAO,SAAS,OAAO,MAAM,SAAS,GAAG;AAC3C,oBAAY,OAAO,MAAM,CAAC,CAAC;AAAA,MAC7B;AAAA,IACF;AAEA,mBAAe,YAAY,MAAY;AACrC,UAAI,CAAC,KAAK,KAAK,SAAS,MAAM,KAAK,CAAC,KAAK,KAAK,SAAS,MAAM,EAAG;AAEhE,UAAI;AACF,cAAM,OAAO,MAAM,KAAK,KAAA;AACxB,cAAM,SAAS,SAAS,IAAI;AAC5B,kBAAU,UAAU,QAAQ;AAC5B,kBAAU,QAAQ,QAAQ;AAC1B,oBAAY,QAAQ,KAAK;AAAA,MAC3B,QAAQ;AAAA,MAER;AAAA,IACF;AAEA,aAAS,eAAe;AACtB,gBAAU,QAAQ,QAAQ;AAC1B,gBAAU,UAAU,QAAQ;AAC5B,kBAAY,QAAQ;AAAA,IACtB;AAGA,UAAM,aAAa,SAAS,MAAM,YAAY,UAAU,aAAa,MAAM,SAAS,CAAC;AACrF,UAAM,cAAc,SAAS,MAAM,YAAY,UAAU,CAAC;;0BAIxDA,YAiSYC,aAAA;AAAA,QAhST,eAAa,QAAA;AAAA,QACd,OAAM;AAAA,QACN,MAAK;AAAA,QACJ,uBAAkB,OAAA,CAAA,MAAA,OAAA,CAAA,IAAA,CAAA,WAAE,KAAI,qBAAsB,MAAM;AAAA,QACpD,SAAO;AAAA,MAAA;yBAER,MAyRM;AAAA,UAzRNC,mBAyRM,OAzRN,YAyRM;AAAA,YAxRJC,YAuRaC,aAAA;AAAA,uBAtRP;AAAA,cAAJ,KAAI;AAAA,0BACK,YAAA;AAAA,2EAAA,YAAW,QAAA;AAAA,cACnB,OAAO,aAAA;AAAA,cACP,QAAQ;AAAA,cACT,MAAK;AAAA,YAAA;cAGM,sBACT,MAqEM;AAAA,gBArENF,mBAqEM,OArEN,YAqEM;AAAA,kBAnEJA,mBAeM,OAfN,YAeM;AAAA,oBAdJC,YAMaE,aAAA;AAAA,sBALV,SAASC,MAAA,SAAA,EAAU,UAAU,UAAK,UAAA,YAAA;AAAA,sBACnC,MAAK;AAAA,sBACJ,SAAK,OAAA,CAAA,MAAA,OAAA,CAAA,IAAA,CAAA,WAAEA,MAAA,SAAA,EAAU,UAAU,QAAK;AAAA,oBAAA;uCAClC,MAED,CAAA,GAAA,OAAA,CAAA,MAAA,OAAA,CAAA,IAAA;AAAA,wCAFC,WAED,EAAA;AAAA,sBAAA;;;oBACAH,YAMaE,aAAA;AAAA,sBALV,SAASC,MAAA,SAAA,EAAU,UAAU,UAAK,QAAA,YAAA;AAAA,sBACnC,MAAK;AAAA,sBACJ,SAAK,OAAA,CAAA,MAAA,OAAA,CAAA,IAAA,CAAA,WAAEA,MAAA,SAAA,EAAU,UAAU,QAAK;AAAA,oBAAA;uCAClC,MAED,CAAA,GAAA,OAAA,EAAA,MAAA,OAAA,EAAA,IAAA;AAAA,wCAFC,SAED,EAAA;AAAA,sBAAA;;;;kBAISA,MAAA,SAAA,EAAU,UAAU,UAAK,WAApCC,aAAAC,mBAUM,OAVN,YAUM;AAAA,mCATJN,mBAKE,YAAA;AAAA,mFAJSI,MAAA,SAAA,EAAU,QAAQ,QAAK;AAAA,sBAChC,OAAM;AAAA,sBACN,MAAK;AAAA,sBACL,aAAY;AAAA,oBAAA;mCAHHA,MAAA,SAAA,EAAU,QAAQ,KAAK;AAAA,oBAAA;oBAKvBA,MAAA,SAAA,EAAU,QAAQ,MAAM,SAAM,kBAAzCE,mBAEM,OAFN,YAEMC,gBADDH,MAAA,SAAA,EAAU,QAAQ,MAAM,MAAM,IAAG,aACtC,CAAA;;kBAISA,MAAA,SAAA,EAAU,UAAU,UAAK,SAApCC,aAAAC,mBAmCM,OAnCN,YAmCM;AAAA,qBAlCQF,MAAA,SAAA,EAAU,QAAQ,sBAA9BE,mBAsBM,OAAA;AAAA;sBArBH,OAAKE,eAAA;AAAA;wBAAoE,WAAA,QAAU,uCAAA;AAAA,sBAAA;sBAInF,QAAM;AAAA,sBACN,gEAAkB,WAAA,QAAU,MAAA,CAAA,SAAA,CAAA;AAAA,sBAC5B,mDAAW,WAAA,QAAU;AAAA,oBAAA;sBAEtBR,mBAKE,SAAA;AAAA,wBAJA,MAAK;AAAA,wBACL,QAAO;AAAA,wBACP,OAAM;AAAA,wBACL,UAAQ;AAAA,sBAAA;kDAEXA,mBAEM,OAAA;AAAA,wBAFD,OAAM;AAAA,wBAA8B,MAAK;AAAA,wBAAO,QAAO;AAAA,wBAAe,SAAQ;AAAA,sBAAA;wBACjFA,mBAA4M,QAAA;AAAA,0BAAtM,kBAAe;AAAA,0BAAQ,mBAAgB;AAAA,0BAAQ,gBAAa;AAAA,0BAAM,GAAE;AAAA,wBAAA;;kDAE5EA,mBAGI,KAAA,EAHD,OAAM,iCAA6B;AAAA,wBACpCA,mBAAqE,QAAA,EAA/D,OAAM,mCAAA,GAAmC,iBAAe;AAAA,wCAAO,+BAEvE;AAAA,sBAAA;+BAGFK,aAAAC,mBASM,OATN,YASM;AAAA,kDARJN,mBAEM,OAAA;AAAA,wBAFD,OAAM;AAAA,wBAA4B,MAAK;AAAA,wBAAO,QAAO;AAAA,wBAAe,SAAQ;AAAA,sBAAA;wBAC/EA,mBAAiM,QAAA;AAAA,0BAA3L,kBAAe;AAAA,0BAAQ,mBAAgB;AAAA,0BAAQ,gBAAa;AAAA,0BAAI,GAAE;AAAA,wBAAA;;sBAE1EA,mBAAgE,QAAhE,YAAgEO,gBAArB,YAAA,KAAW,GAAA,CAAA;AAAA,sBACtDP,mBAA6F,QAA7F,YAA6FO,gBAAlDH,MAAA,SAAA,EAAU,QAAQ,MAAM,KAAK,MAAM,IAAG,SAAK,CAAA;AAAA,sBACtFJ,mBAES,UAAA;AAAA,wBAFD,MAAK;AAAA,wBAAS,OAAM;AAAA,wBAA8B,SAAO;AAAA,sBAAA,GAAc,eAE/E;AAAA,oBAAA;;;;cAOG,yBACT,MA8CM;AAAA,gBA9CNA,mBA8CM,OA9CN,aA8CM;AAAA,kBA7CJA,mBAKM,OALN,aAKM;AAAA,oDAJDI,MAAA,SAAA,EAAU,SAAS,MAAM,MAAM,IAAG,SAAIG,gBAAGH,MAAA,SAAA,EAAU,QAAQ,MAAM,MAAM,IAAG,mDAEjEG,gBAAGH,MAAA,SAAA,EAAU,cAAc,KAAK,IAAG,uBAC/C,CAAA;AAAA,oBAAAJ,mBAA4C,QAAA,MAAAO,gBAAnCH,MAAA,SAAA,EAAU,UAAU,KAAK,GAAA,CAAA;AAAA,gEAAU,MAC9C,EAAA;AAAA,kBAAA;kBAEAJ,mBAOM,OAPN,aAOM;AAAA,oBANJC,YAEaE,aAAA;AAAA,sBAFD,MAAK;AAAA,sBAAK,SAAQ;AAAA,sBAAa,SAAK,OAAA,CAAA,MAAA,OAAA,CAAA,IAAA,CAAA,WAAEC,MAAA,SAAA,EAAU,qBAAoB,SAAA;AAAA,oBAAA;uCAAa,MAE7F,CAAA,GAAA,OAAA,EAAA,MAAA,OAAA,EAAA,IAAA;AAAA,wCAF6F,iBAE7F,EAAA;AAAA,sBAAA;;;oBACAH,YAEaE,aAAA;AAAA,sBAFD,MAAK;AAAA,sBAAK,SAAQ;AAAA,sBAAa,SAAK,OAAA,CAAA,MAAA,OAAA,CAAA,IAAA,CAAA,WAAEC,MAAA,SAAA,EAAU,qBAAoB,SAAA;AAAA,oBAAA;uCAAa,MAE7F,CAAA,GAAA,OAAA,EAAA,MAAA,OAAA,EAAA,IAAA;AAAA,wCAF6F,iBAE7F,EAAA;AAAA,sBAAA;;;;kBAGFJ,mBA4BM,OA5BN,aA4BM;AAAA,qBA3BJK,UAAA,IAAA,GAAAC,mBA0BMG,2BAzBcL,MAAA,SAAA,EAAU,SAAS,QAA9B,YAAO;0CADhBE,mBA0BM,OAAA;AAAA,wBAxBH,KAAK,QAAQ;AAAA,wBACd,OAAM;AAAA,sBAAA;wBAENN,mBAGM,OAHN,aAGM;AAAA,0BAFJA,mBAAsE,QAAtE,aAAsEO,gBAAxB,QAAQ,MAAM,GAAA,CAAA;AAAA,0BAC5DP,mBAAkF,QAAlF,aAAkFO,gBAAnC,QAAQ,UAAU,IAAG,WAAO,CAAA;AAAA,wBAAA;wBAE7EP,mBAgBM,OAhBN,aAgBM;AAAA,0BAfJA,mBAIiB,UAAA;AAAA,4BAHf,MAAK;AAAA,4BACJ,OAAKQ,eAAA,CAAA,8BAAiC,QAAQ,WAAM,YAAA,uCAAA,EAAA,CAAA;AAAA,4BACpD,qBAAOJ,MAAA,SAAA,EAAU,iBAAiB,QAAQ,OAAK,SAAA;AAAA,0BAAA,GACjD,WAAO,IAAA,WAAA;AAAA,0BACRJ,mBAIiB,UAAA;AAAA,4BAHf,MAAK;AAAA,4BACJ,OAAKQ,eAAA,CAAA,8BAAiC,QAAQ,WAAM,YAAA,2EAAA,EAAA,CAAA;AAAA,4BACpD,qBAAOJ,MAAA,SAAA,EAAU,iBAAiB,QAAQ,OAAK,SAAA;AAAA,0BAAA,GACjD,WAAO,IAAA,WAAA;AAAA,0BACRJ,mBAIY,UAAA;AAAA,4BAHV,MAAK;AAAA,4BACJ,OAAKQ,eAAA,CAAA,8BAAiC,QAAQ,WAAM,OAAA,sEAAA,EAAA,CAAA;AAAA,4BACpD,qBAAOJ,MAAA,SAAA,EAAU,iBAAiB,QAAQ,OAAK,IAAA;AAAA,0BAAA,GACjD,MAAE,IAAA,WAAA;AAAA,wBAAA;;;;;;cAQF,uBACT,MAwDM;AAAA,gBAxDNJ,mBAwDM,OAxDN,aAwDM;AAAA,kBAvDJA,mBAwCM,OAxCN,aAwCM;AAAA,qBAvCJK,UAAA,IAAA,GAAAC,mBAsCMG,2BArCUL,MAAA,SAAA,EAAU,iBAAiB,QAAlC,QAAG;0CADZE,mBAsCM,OAAA;AAAA,wBApCH,KAAK,IAAI;AAAA,wBACT,OAAKE,eAAA;AAAA;0BAAsEJ,MAAA,SAAA,EAAU,cAAc,MAAM,IAAI,IAAI,KAAK,IAAA,wCAAA;AAAA,wBAAA;;wBAKvHJ,mBAUM,OAVN,aAUM;AAAA,0BATJA,mBAOQ,SAPR,aAOQ;AAAA,4BANNA,mBAIE,SAAA;AAAA,8BAHA,MAAK;AAAA,8BACJ,SAASI,MAAA,SAAA,EAAU,cAAc,MAAM,IAAI,IAAI,KAAK;AAAA,8BACpD,sBAAQA,MAAA,SAAA,EAAU,YAAY,IAAI,KAAK;AAAA,4BAAA;4BAE1C,OAAA,EAAA,MAAA,OAAA,EAAA,IAAAJ,mBAAgE,QAAA,EAA1D,OAAM,wCAAqC,YAAQ,EAAA;AAAA,0BAAA;0BAE3DA,mBAAmF,QAAnF,aAAmFO,gBAAhC,IAAI,WAAW,IAAG,WAAO,CAAA;AAAA,wBAAA;wBAG9EN,YAKES,aAAA;AAAA,0BAJC,eAAa,IAAI;AAAA,0BAClB,aAAY;AAAA,0BACZ,OAAM;AAAA,0BACL,uBAAkB,CAAA,WAAEN,MAAA,SAAA,EAAU,YAAY,IAAI,OAAO,OAAO,UAAM,EAAA,CAAA;AAAA,wBAAA;wBAGrEJ,mBAUM,OAVN,aAUM;AAAA,2BATJK,UAAA,IAAA,GAAAC,mBAIiBG,2BAHD,IAAI,aAAa,cAAxB,QAAG;gDADZH,mBAIiB,QAAA;AAAA,8BAFd,KAAK;AAAA,8BACN,OAAM;AAAA,4BAAA,mBACJ,GAAG,GAAA,CAAA;AAAA;0BAEC,IAAI,aAAa,SAAM,kBAD/BA,mBAG+C,QAH/C,aAGC,MAACC,gBAAG,IAAI,aAAa,SAAM,CAAA,IAAO,SAAK,CAAA;;;;;kBAKnCH,MAAA,SAAA,EAAU,cAAc,MAAM,OAAI,KAA7CC,UAAA,GAAAC,mBAQM,OARN,aAQM;AAAA,gEARmF,kBAEvF,EAAA;AAAA,oBAAAN,mBAKW,UAAA,MAAAO,gBAJTH,MAAA,SAAA,EAAU,iBAAiB,MAAyB,OAAO,CAAA,MAAKA,MAAA,SAAA,EAAU,cAAc,MAAM,IAAI,EAAE,KAAK,CAAA,EAAsB,IAAI,CAAA,MAAK,EAAE,IAAI,EAAqB,KAAI,KAAA,CAAA,GAAA,CAAA;AAAA,kBAAA;kBAOhKA,MAAA,SAAA,EAAU,cAAc,MAAM,SAAI,kBAA7CE,mBAEM,OAFN,aAA2F,0CAE3F;;;cAKO,wBACT,MAkDM;AAAA,gBAlDNN,mBAkDM,OAlDN,aAkDM;AAAA,kBAjDJA,mBAUM,OAVN,aAUM;AAAA,oBATJA,mBAEO,QAFP,aAEO;AAAA,sBADLA,mBAAoD,gCAAzCI,MAAA,SAAA,EAAU,OAAO,MAAM,MAAM,GAAA,CAAA;AAAA,kEAAY,YACtD,EAAA;AAAA,oBAAA;oBACAJ,mBAEO,QAFP,aAEO;AAAA,sBADLA,mBAA2F,gCAAhFI,MAAA,SAAA,EAAU,OAAO,MAAM,OAAM,CAAE,KAAK,MAAM,MAAM,EAAE,QAAQ,QAAM,CAAA,CAAA,GAAA,CAAA;AAAA,kEAAgB,aAC7F,EAAA;AAAA,oBAAA;oBACYA,MAAA,SAAA,EAAU,gBAAgB,MAAM,SAAM,KAAlDC,UAAA,GAAAC,mBAEO,QAFP,aAEO;AAAA,sBADLN,mBAA6D,gCAAlDI,MAAA,SAAA,EAAU,gBAAgB,MAAM,MAAM,GAAA,CAAA;AAAA,kEAAY,cAC/D,EAAA;AAAA,oBAAA;;kBAGFJ,mBAyBM,OAzBN,aAyBM;AAAA,qBAxBJK,UAAA,IAAA,GAAAC,mBAuBUG,2BAtBQL,MAAA,SAAA,EAAU,OAAO,QAA1B,UAAK;0CADdE,mBAuBU,WAAA;AAAA,wBArBP,KAAK,MAAM;AAAA,wBACZ,OAAM;AAAA,sBAAA;wBAENN,mBAUU,WAVV,aAUU;AAAA,0BATRA,mBAGE,QAAA;AAAA,4BAFA,OAAM;AAAA,4BACL,OAAKW,eAAA,EAAA,iBAAqB,MAAM,OAAK;AAAA,0BAAA;0BAExCX,mBAAwE,QAAxE,aAAwEO,gBAApB,MAAM,IAAI,GAAA,CAAA;AAAA,0BAC9DP,mBAGkC,QAAA;AAAA,4BAFhC,OAAM;AAAA,4BACL,yCAA0B,MAAM,QAAK,MAAA,OAAgB,MAAM,MAAA,CAAK;AAAA,0BAAA,mBAC/D,MAAM,QAAQ,MAAM,GAAA,CAAA;AAAA,wBAAA;wBAE1BA,mBAMM,OANN,aAMM;AAAA,2BALJK,UAAA,IAAA,GAAAC,mBAIoBG,UAAA,MAAAG,WAHD,MAAM,UAAhB,WAAM;gDADfN,mBAIoB,QAAA;AAAA,8BAFjB,KAAK;AAAA,8BACN,OAAM;AAAA,4BAAA,mBACJ,MAAM,GAAA,CAAA;AAAA;;;;;kBAKLF,MAAA,SAAA,EAAU,gBAAgB,MAAM,SAAM,KAAjDC,UAAA,GAAAC,mBASM,OATN,aASM;AAAA,oBARJ,OAAA,EAAA,MAAA,OAAA,EAAA,IAAAN,mBAA0E,OAAA,EAArE,OAAM,yCAAA,GAAyC,oBAAgB,EAAA;AAAA,oBACpEA,mBAMM,OANN,aAMM;AAAA,uBALJK,UAAA,IAAA,GAAAC,mBAIoBG,2BAHDL,MAAA,SAAA,EAAU,gBAAgB,QAApC,WAAM;4CADfE,mBAIoB,QAAA;AAAA,0BAFjB,KAAK;AAAA,0BACN,OAAM;AAAA,wBAAA,mBACJ,MAAM,GAAA,CAAA;AAAA;;;;;cAOP,oBACT,CA2BM,EA5BiB,QAAQ,QAAQ,iBAAU;AAAA,gBACjDN,mBA2BM,OA3BN,aA2BM;AAAA,kBA1BJC,YAEaE,aAAA;AAAA,oBAFD,SAAQ;AAAA,oBAAa,SAAO;AAAA,kBAAA;qCAAc,MAEtD,CAAA,GAAA,OAAA,EAAA,MAAA,OAAA,EAAA,IAAA;AAAA,sCAFsD,YAEtD,EAAA;AAAA,oBAAA;;;8CACAH,mBAAuB,OAAA,EAAlB,OAAA,EAAA,QAAA,IAAA,EAAA,GAAe,MAAA,EAAA;AAAA,mBAEX,YAAA,sBADTF,YAMaK,aAAA;AAAA;oBAJX,SAAQ;AAAA,oBACP,SAAO;AAAA,kBAAA;qCACT,MAED,CAAA,GAAA,OAAA,EAAA,MAAA,OAAA,EAAA,IAAA;AAAA,sCAFC,UAED,EAAA;AAAA,oBAAA;;;mBAES,WAAA,sBADTL,YAOaK,aAAA;AAAA;oBALX,SAAQ;AAAA,oBACP,WAAW;AAAA,oBACX,SAAK,MAAA;AAAU,iCAAA;AAAc,6BAAA;AAAA,oBAAM;AAAA,kBAAA;qCACrC,MAED,CAAA,GAAA,OAAA,EAAA,MAAA,OAAA,EAAA,IAAA;AAAA,sCAFC,UAED,EAAA;AAAA,oBAAA;;;kBAEQ,WAAA,sBADRL,YAMaK,aAAA;AAAA;oBAJX,SAAQ;AAAA,oBACP,SAAO;AAAA,kBAAA;qCACT,MAED,CAAA,GAAA,OAAA,EAAA,MAAA,OAAA,EAAA,IAAA;AAAA,sCAFC,WAED,EAAA;AAAA,oBAAA;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"AutoGroupModal.vue.js","sources":["../../src/components/AutoGroupModal.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { ref, computed, watch } from 'vue'\nimport BaseModal from './BaseModal.vue'\nimport BaseButton from './BaseButton.vue'\nimport BaseInput from './BaseInput.vue'\nimport StepWizard from './StepWizard.vue'\nimport { useAutoGroup, parseCSV } from '../composables/useAutoGroup'\nimport type { WizardStep } from '../types/components'\nimport type { AutoGroupResult } from '../types/auto-group'\n\ninterface Props {\n modelValue: boolean\n samples?: string[]\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n samples: () => [],\n})\n\nconst emit = defineEmits<{\n 'update:modelValue': [value: boolean]\n 'apply': [result: AutoGroupResult]\n}>()\n\nconst autoGroup = useAutoGroup()\nconst wizardRef = ref<InstanceType<typeof StepWizard> | null>(null)\nconst currentStep = ref(0)\nconst isDragOver = ref(false)\nconst csvFileName = ref('')\n\n// Pre-fill from props\nwatch(() => props.modelValue, (open) => {\n if (open) {\n autoGroup.reset()\n currentStep.value = 0\n csvFileName.value = ''\n if (props.samples.length > 0) {\n autoGroup.rawText.value = props.samples.join('\\n')\n }\n }\n}, { immediate: true })\n\n// Dynamic steps: skip outlier step when no outliers\nconst allSteps: WizardStep[] = [\n { id: 'input', label: 'Input' },\n { id: 'outliers', label: 'Outliers' },\n { id: 'fields', label: 'Fields' },\n { id: 'preview', label: 'Preview' },\n]\n\nconst dynamicSteps = computed<WizardStep[]>(() => {\n if (autoGroup.hasOutliers.value) {\n return allSteps\n }\n return allSteps.filter(s => s.id !== 'outliers')\n})\n\n// Map current step index to step ID\nconst currentStepId = computed(() => {\n const step = dynamicSteps.value[currentStep.value]\n return step?.id ?? 'input'\n})\n\n// Clamp step when dynamic steps change\nwatch(() => dynamicSteps.value.length, (newLen) => {\n if (currentStep.value >= newLen) {\n currentStep.value = newLen - 1\n }\n})\n\n// Step validity\nconst inputValid = computed(() => autoGroup.samples.value.length > 0)\nconst fieldsValid = computed(() => autoGroup.enabledFields.value.size > 0)\n\nwatch([inputValid, fieldsValid, dynamicSteps], () => {\n if (!wizardRef.value) return\n const steps = dynamicSteps.value\n for (let i = 0; i < steps.length; i++) {\n const id = steps[i].id\n if (id === 'input') wizardRef.value.setStepValid(i, inputValid.value)\n else if (id === 'outliers') wizardRef.value.setStepValid(i, true)\n else if (id === 'fields') wizardRef.value.setStepValid(i, fieldsValid.value)\n else if (id === 'preview') wizardRef.value.setStepValid(i, true)\n }\n}, { immediate: true })\n\n// Navigation\nfunction handleNext() {\n if (currentStepId.value === 'input') {\n autoGroup.parseInput()\n }\n // StepWizard handles the actual navigation via v-model\n}\n\nfunction handleApply() {\n emit('apply', autoGroup.result.value)\n emit('update:modelValue', false)\n}\n\nfunction handleCancel() {\n emit('update:modelValue', false)\n}\n\n// CSV file handling\nfunction handleFileDrop(event: DragEvent) {\n event.preventDefault()\n isDragOver.value = false\n const files = event.dataTransfer?.files\n if (files && files.length > 0) {\n processFile(files[0])\n }\n}\n\nfunction handleFileInput(event: Event) {\n const target = event.target as HTMLInputElement\n if (target.files && target.files.length > 0) {\n processFile(target.files[0])\n }\n}\n\nasync function processFile(file: File) {\n if (!file.name.endsWith('.csv') && !file.name.endsWith('.tsv')) return\n\n try {\n const text = await file.text()\n const parsed = parseCSV(text)\n autoGroup.inputMode.value = 'csv'\n autoGroup.csvData.value = parsed\n csvFileName.value = file.name\n } catch {\n // CSV parse error — stay in current state\n }\n}\n\nfunction clearCsvFile() {\n autoGroup.csvData.value = null\n autoGroup.inputMode.value = 'paste'\n csvFileName.value = ''\n}\n\n// Last step check\nconst isLastStep = computed(() => currentStep.value === dynamicSteps.value.length - 1)\nconst isFirstStep = computed(() => currentStep.value === 0)\n</script>\n\n<template>\n <BaseModal\n :model-value=\"modelValue\"\n title=\"Smart Group\"\n size=\"lg\"\n @update:model-value=\"emit('update:modelValue', $event)\"\n @close=\"handleCancel\"\n >\n <div class=\"mld-auto-group\">\n <StepWizard\n ref=\"wizardRef\"\n v-model=\"currentStep\"\n :steps=\"dynamicSteps\"\n :linear=\"true\"\n size=\"sm\"\n >\n <!-- Step: Input -->\n <template #step-input>\n <div class=\"mld-auto-group__input-step\">\n <!-- Mode toggle -->\n <div class=\"mld-auto-group__mode-toggle\">\n <BaseButton\n :variant=\"autoGroup.inputMode.value === 'paste' ? 'primary' : 'secondary'\"\n size=\"sm\"\n @click=\"autoGroup.inputMode.value = 'paste'\"\n >\n Paste\n </BaseButton>\n <BaseButton\n :variant=\"autoGroup.inputMode.value === 'csv' ? 'primary' : 'secondary'\"\n size=\"sm\"\n @click=\"autoGroup.inputMode.value = 'csv'\"\n >\n CSV\n </BaseButton>\n </div>\n\n <!-- Paste mode -->\n <div v-if=\"autoGroup.inputMode.value === 'paste'\" class=\"mld-auto-group__paste\">\n <textarea\n v-model=\"autoGroup.rawText.value\"\n class=\"mld-auto-group__textarea\"\n rows=\"12\"\n placeholder=\"Paste sample names, one per line...\"\n />\n <div v-if=\"autoGroup.samples.value.length > 0\" class=\"mld-auto-group__sample-count\">\n {{ autoGroup.samples.value.length }} samples\n </div>\n </div>\n\n <!-- CSV mode -->\n <div v-if=\"autoGroup.inputMode.value === 'csv'\" class=\"mld-auto-group__csv\">\n <div v-if=\"!autoGroup.csvData.value\"\n :class=\"[\n 'mld-auto-group__dropzone',\n isDragOver ? 'mld-auto-group__dropzone--dragover' : '',\n ]\"\n @drop=\"handleFileDrop\"\n @dragover.prevent=\"isDragOver = true\"\n @dragleave=\"isDragOver = false\"\n >\n <input\n type=\"file\"\n accept=\".csv,.tsv\"\n class=\"mld-auto-group__file-input\"\n @change=\"handleFileInput\"\n />\n <svg class=\"mld-auto-group__upload-icon\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.5\" d=\"M9 17v-2m3 2v-4m3 4v-6m2 10H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z\" />\n </svg>\n <p class=\"mld-auto-group__upload-text\">\n <span class=\"mld-auto-group__upload-highlight\">Click to upload</span>\n or drag and drop a CSV file\n </p>\n </div>\n\n <div v-else class=\"mld-auto-group__file-info\">\n <svg class=\"mld-auto-group__file-icon\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z\" />\n </svg>\n <span class=\"mld-auto-group__file-name\">{{ csvFileName }}</span>\n <span class=\"mld-auto-group__file-rows\">{{ autoGroup.csvData.value.rows.length }} rows</span>\n <button type=\"button\" class=\"mld-auto-group__file-clear\" @click=\"clearCsvFile\">\n Change file\n </button>\n </div>\n </div>\n </div>\n </template>\n\n <!-- Step: Outliers -->\n <template #step-outliers>\n <div class=\"mld-auto-group__outlier-step\">\n <div class=\"mld-auto-group__outlier-banner\">\n {{ autoGroup.outliers.value.length }} of {{ autoGroup.samples.value.length }}\n samples have irregular structure\n (fewer than {{ autoGroup.dominantFieldCount.value }} fields, delimiter\n <code>{{ autoGroup.delimiter.value }}</code>)\n </div>\n\n <div class=\"mld-auto-group__outlier-batch\">\n <BaseButton size=\"sm\" variant=\"secondary\" @click=\"autoGroup.setAllOutlierActions('include')\">\n Include All\n </BaseButton>\n <BaseButton size=\"sm\" variant=\"secondary\" @click=\"autoGroup.setAllOutlierActions('exclude')\">\n Exclude All\n </BaseButton>\n </div>\n\n <div class=\"mld-auto-group__outlier-list\">\n <div\n v-for=\"outlier in autoGroup.outliers.value\"\n :key=\"outlier.index\"\n class=\"mld-auto-group__outlier-item\"\n >\n <div class=\"mld-auto-group__outlier-info\">\n <code class=\"mld-auto-group__outlier-name\">{{ outlier.sample }}</code>\n <span class=\"mld-auto-group__outlier-badge\">{{ outlier.fieldCount }} fields</span>\n </div>\n <div class=\"mld-auto-group__outlier-actions\">\n <button\n type=\"button\"\n :class=\"['mld-auto-group__action-btn', outlier.action === 'include' ? 'mld-auto-group__action-btn--active' : '']\"\n @click=\"autoGroup.setOutlierAction(outlier.index, 'include')\"\n >Include</button>\n <button\n type=\"button\"\n :class=\"['mld-auto-group__action-btn', outlier.action === 'exclude' ? 'mld-auto-group__action-btn--active mld-auto-group__action-btn--exclude' : '']\"\n @click=\"autoGroup.setOutlierAction(outlier.index, 'exclude')\"\n >Exclude</button>\n <button\n type=\"button\"\n :class=\"['mld-auto-group__action-btn', outlier.action === 'qc' ? 'mld-auto-group__action-btn--active mld-auto-group__action-btn--qc' : '']\"\n @click=\"autoGroup.setOutlierAction(outlier.index, 'qc')\"\n >QC</button>\n </div>\n </div>\n </div>\n </div>\n </template>\n\n <!-- Step: Fields -->\n <template #step-fields>\n <div class=\"mld-auto-group__field-step\">\n <div class=\"mld-auto-group__field-grid\">\n <div\n v-for=\"col in autoGroup.effectiveColumns.value\"\n :key=\"col.index\"\n :class=\"[\n 'mld-auto-group__field-card',\n autoGroup.enabledFields.value.has(col.index) ? 'mld-auto-group__field-card--enabled' : '',\n ]\"\n >\n <div class=\"mld-auto-group__field-header\">\n <label class=\"mld-auto-group__field-toggle\">\n <input\n type=\"checkbox\"\n :checked=\"autoGroup.enabledFields.value.has(col.index)\"\n @change=\"autoGroup.toggleField(col.index)\"\n />\n <span class=\"mld-auto-group__field-toggle-label\">Group by</span>\n </label>\n <span class=\"mld-auto-group__field-cardinality\">{{ col.cardinality }} unique</span>\n </div>\n\n <BaseInput\n :model-value=\"col.name\"\n placeholder=\"Field name...\"\n class=\"mld-auto-group__field-name-input\"\n @update:model-value=\"autoGroup.renameField(col.index, String($event ?? ''))\"\n />\n\n <div class=\"mld-auto-group__field-values\">\n <span\n v-for=\"val in col.uniqueValues.slice(0, 5)\"\n :key=\"val\"\n class=\"mld-auto-group__field-value\"\n >{{ val }}</span>\n <span\n v-if=\"col.uniqueValues.length > 5\"\n class=\"mld-auto-group__field-more\"\n >+{{ col.uniqueValues.length - 5 }} more</span>\n </div>\n </div>\n </div>\n\n <div v-if=\"autoGroup.enabledFields.value.size > 0\" class=\"mld-auto-group__field-summary\">\n Grouping by:\n <strong>{{\n autoGroup.effectiveColumns.value\n .filter(c => autoGroup.enabledFields.value.has(c.index))\n .map(c => c.name)\n .join(' / ')\n }}</strong>\n </div>\n\n <div v-if=\"autoGroup.enabledFields.value.size === 0\" class=\"mld-auto-group__field-warning\">\n Select at least one field for grouping\n </div>\n </div>\n </template>\n\n <!-- Step: Preview -->\n <template #step-preview>\n <div class=\"mld-auto-group__preview-step\">\n <div class=\"mld-auto-group__preview-summary\">\n <span class=\"mld-auto-group__preview-stat\">\n <strong>{{ autoGroup.groups.value.length }}</strong> groups\n </span>\n <span class=\"mld-auto-group__preview-stat\">\n <strong>{{ autoGroup.groups.value.reduce((sum, g) => sum + g.samples.length, 0) }}</strong> samples\n </span>\n <span v-if=\"autoGroup.excludedSamples.value.length > 0\" class=\"mld-auto-group__preview-stat mld-auto-group__preview-stat--excluded\">\n <strong>{{ autoGroup.excludedSamples.value.length }}</strong> excluded\n </span>\n </div>\n\n <div class=\"mld-auto-group__preview-groups\">\n <details\n v-for=\"group in autoGroup.groups.value\"\n :key=\"group.name\"\n class=\"mld-auto-group__preview-group\"\n >\n <summary class=\"mld-auto-group__preview-group-header\">\n <span\n class=\"mld-auto-group__preview-dot\"\n :style=\"{ backgroundColor: group.color }\"\n />\n <span class=\"mld-auto-group__preview-group-name\">{{ group.name }}</span>\n <span\n class=\"mld-auto-group__preview-group-count\"\n :style=\"{ backgroundColor: group.color + '20', color: group.color }\"\n >{{ group.samples.length }}</span>\n </summary>\n <div class=\"mld-auto-group__preview-samples\">\n <span\n v-for=\"sample in group.samples\"\n :key=\"sample\"\n class=\"mld-auto-group__preview-sample\"\n >{{ sample }}</span>\n </div>\n </details>\n </div>\n\n <div v-if=\"autoGroup.excludedSamples.value.length > 0\" class=\"mld-auto-group__preview-excluded\">\n <div class=\"mld-auto-group__preview-excluded-title\">Excluded Samples</div>\n <div class=\"mld-auto-group__preview-excluded-list\">\n <span\n v-for=\"sample in autoGroup.excludedSamples.value\"\n :key=\"sample\"\n class=\"mld-auto-group__preview-sample mld-auto-group__preview-sample--excluded\"\n >{{ sample }}</span>\n </div>\n </div>\n </div>\n </template>\n\n <!-- Custom navigation -->\n <template #navigation=\"{ goBack, goNext, canProceed }\">\n <div class=\"mld-auto-group__nav\">\n <BaseButton variant=\"secondary\" @click=\"handleCancel\">\n Cancel\n </BaseButton>\n <div style=\"flex: 1\" />\n <BaseButton\n v-if=\"!isFirstStep\"\n variant=\"secondary\"\n @click=\"goBack\"\n >\n Back\n </BaseButton>\n <BaseButton\n v-if=\"!isLastStep\"\n variant=\"primary\"\n :disabled=\"!canProceed\"\n @click=\"() => { handleNext(); goNext() }\"\n >\n Next\n </BaseButton>\n <BaseButton\n v-if=\"isLastStep\"\n variant=\"primary\"\n @click=\"handleApply\"\n >\n Apply\n </BaseButton>\n </div>\n </template>\n </StepWizard>\n </div>\n </BaseModal>\n</template>\n\n<style>\n@import '../styles/components/auto-group-modal.css';\n</style>\n"],"names":["_createBlock","BaseModal","_createElementVNode","_createVNode","StepWizard","BaseButton","_unref","_openBlock","_createElementBlock","_toDisplayString","_normalizeClass","_Fragment","BaseInput","_normalizeStyle","_renderList"],"mappings":"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"}
@@ -1,7 +1,8 @@
1
1
  import { defineComponent, computed, openBlock, createElementBlock, normalizeClass, createElementVNode } from "vue";
2
- const _hoisted_1 = ["disabled"];
3
- const _hoisted_2 = ["value", "min", "max", "step", "disabled", "placeholder"];
2
+ const _hoisted_1 = ["value", "min", "max", "step", "disabled", "placeholder"];
3
+ const _hoisted_2 = { class: "mld-number-input__buttons" };
4
4
  const _hoisted_3 = ["disabled"];
5
+ const _hoisted_4 = ["disabled"];
5
6
  const _sfc_main = /* @__PURE__ */ defineComponent({
6
7
  __name: "NumberInput",
7
8
  props: {
@@ -62,29 +63,6 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
62
63
  __props.disabled ? "mld-number-input--disabled" : ""
63
64
  ])
64
65
  }, [
65
- createElementVNode("button", {
66
- type: "button",
67
- "aria-label": "Decrease value",
68
- disabled: __props.disabled || !canDecrement.value,
69
- class: normalizeClass([
70
- "mld-number-input__button",
71
- "mld-number-input__button--decrement",
72
- `mld-number-input__button--${__props.size}`
73
- ]),
74
- onClick: decrement
75
- }, [..._cache[0] || (_cache[0] = [
76
- createElementVNode("svg", {
77
- class: "mld-number-input__button-icon",
78
- fill: "none",
79
- stroke: "currentColor",
80
- "stroke-width": "2",
81
- "stroke-linecap": "round",
82
- "stroke-linejoin": "round",
83
- viewBox: "0 0 24 24"
84
- }, [
85
- createElementVNode("path", { d: "M5 12h14" })
86
- ], -1)
87
- ])], 10, _hoisted_1),
88
66
  createElementVNode("input", {
89
67
  type: "number",
90
68
  value: __props.modelValue,
@@ -99,31 +77,56 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
99
77
  __props.disabled ? "mld-number-input__input--disabled" : ""
100
78
  ]),
101
79
  onInput: handleInput
102
- }, null, 42, _hoisted_2),
103
- createElementVNode("button", {
104
- type: "button",
105
- "aria-label": "Increase value",
106
- disabled: __props.disabled || !canIncrement.value,
107
- class: normalizeClass([
108
- "mld-number-input__button",
109
- "mld-number-input__button--increment",
110
- `mld-number-input__button--${__props.size}`
111
- ]),
112
- onClick: increment
113
- }, [..._cache[1] || (_cache[1] = [
114
- createElementVNode("svg", {
115
- class: "mld-number-input__button-icon",
116
- fill: "none",
117
- stroke: "currentColor",
118
- "stroke-width": "2",
119
- "stroke-linecap": "round",
120
- "stroke-linejoin": "round",
121
- viewBox: "0 0 24 24"
122
- }, [
123
- createElementVNode("path", { d: "M5 12h14" }),
124
- createElementVNode("path", { d: "M12 5v14" })
125
- ], -1)
126
- ])], 10, _hoisted_3)
80
+ }, null, 42, _hoisted_1),
81
+ createElementVNode("div", _hoisted_2, [
82
+ createElementVNode("button", {
83
+ type: "button",
84
+ "aria-label": "Decrease value",
85
+ disabled: __props.disabled || !canDecrement.value,
86
+ class: normalizeClass([
87
+ "mld-number-input__button",
88
+ "mld-number-input__button--decrement",
89
+ `mld-number-input__button--${__props.size}`
90
+ ]),
91
+ onClick: decrement
92
+ }, [..._cache[0] || (_cache[0] = [
93
+ createElementVNode("svg", {
94
+ class: "mld-number-input__button-icon",
95
+ fill: "none",
96
+ stroke: "currentColor",
97
+ "stroke-width": "2",
98
+ "stroke-linecap": "round",
99
+ "stroke-linejoin": "round",
100
+ viewBox: "0 0 24 24"
101
+ }, [
102
+ createElementVNode("path", { d: "M5 12h14" })
103
+ ], -1)
104
+ ])], 10, _hoisted_3),
105
+ createElementVNode("button", {
106
+ type: "button",
107
+ "aria-label": "Increase value",
108
+ disabled: __props.disabled || !canIncrement.value,
109
+ class: normalizeClass([
110
+ "mld-number-input__button",
111
+ "mld-number-input__button--increment",
112
+ `mld-number-input__button--${__props.size}`
113
+ ]),
114
+ onClick: increment
115
+ }, [..._cache[1] || (_cache[1] = [
116
+ createElementVNode("svg", {
117
+ class: "mld-number-input__button-icon",
118
+ fill: "none",
119
+ stroke: "currentColor",
120
+ "stroke-width": "2",
121
+ "stroke-linecap": "round",
122
+ "stroke-linejoin": "round",
123
+ viewBox: "0 0 24 24"
124
+ }, [
125
+ createElementVNode("path", { d: "M5 12h14" }),
126
+ createElementVNode("path", { d: "M12 5v14" })
127
+ ], -1)
128
+ ])], 10, _hoisted_4)
129
+ ])
127
130
  ], 2);
128
131
  };
129
132
  }
@@ -1 +1 @@
1
- {"version":3,"file":"NumberInput.vue.js","sources":["../../src/components/NumberInput.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed } from 'vue'\n\ninterface Props {\n modelValue?: number\n min?: number\n max?: number\n step?: number\n disabled?: boolean\n error?: boolean\n size?: 'sm' | 'md' | 'lg'\n placeholder?: string\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n step: 1,\n disabled: false,\n error: false,\n size: 'md',\n})\n\nconst emit = defineEmits<{\n 'update:modelValue': [value: number | undefined]\n}>()\n\nconst canDecrement = computed(() => {\n if (props.modelValue === undefined) return true\n if (props.min === undefined) return true\n return props.modelValue > props.min\n})\n\nconst canIncrement = computed(() => {\n if (props.modelValue === undefined) return true\n if (props.max === undefined) return true\n return props.modelValue < props.max\n})\n\nfunction clamp(value: number): number {\n let result = value\n if (props.min !== undefined && result < props.min) result = props.min\n if (props.max !== undefined && result > props.max) result = props.max\n return result\n}\n\nfunction handleInput(event: Event) {\n const target = event.target as HTMLInputElement\n const value = target.value === '' ? undefined : Number(target.value)\n if (value !== undefined && !isNaN(value)) {\n emit('update:modelValue', clamp(value))\n } else {\n emit('update:modelValue', undefined)\n }\n}\n\nfunction decrement() {\n if (props.disabled || !canDecrement.value) return\n const current = props.modelValue ?? (props.max ?? 0)\n emit('update:modelValue', clamp(current - props.step))\n}\n\nfunction increment() {\n if (props.disabled || !canIncrement.value) return\n const current = props.modelValue ?? (props.min ?? 0)\n emit('update:modelValue', clamp(current + props.step))\n}\n</script>\n\n<template>\n <div\n :class=\"[\n 'mld-number-input',\n `mld-number-input--${size}`,\n error ? 'mld-number-input--error' : '',\n disabled ? 'mld-number-input--disabled' : '',\n ]\"\n >\n <button\n type=\"button\"\n aria-label=\"Decrease value\"\n :disabled=\"disabled || !canDecrement\"\n :class=\"[\n 'mld-number-input__button',\n 'mld-number-input__button--decrement',\n `mld-number-input__button--${size}`,\n ]\"\n @click=\"decrement\"\n >\n <svg class=\"mld-number-input__button-icon\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" viewBox=\"0 0 24 24\">\n <path d=\"M5 12h14\" />\n </svg>\n </button>\n\n <input\n type=\"number\"\n :value=\"modelValue\"\n :min=\"min\"\n :max=\"max\"\n :step=\"step\"\n :disabled=\"disabled\"\n :placeholder=\"placeholder\"\n :class=\"[\n 'mld-number-input__input',\n `mld-number-input__input--${size}`,\n disabled ? 'mld-number-input__input--disabled' : '',\n ]\"\n @input=\"handleInput\"\n />\n\n <button\n type=\"button\"\n aria-label=\"Increase value\"\n :disabled=\"disabled || !canIncrement\"\n :class=\"[\n 'mld-number-input__button',\n 'mld-number-input__button--increment',\n `mld-number-input__button--${size}`,\n ]\"\n @click=\"increment\"\n >\n <svg class=\"mld-number-input__button-icon\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" viewBox=\"0 0 24 24\">\n <path d=\"M5 12h14\" /><path d=\"M12 5v14\" />\n </svg>\n </button>\n </div>\n</template>\n\n<style>\n@import '../styles/components/number-input.css';\n</style>\n"],"names":["_createElementBlock","_normalizeClass","_createElementVNode"],"mappings":"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"}
1
+ {"version":3,"file":"NumberInput.vue.js","sources":["../../src/components/NumberInput.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed } from 'vue'\n\ninterface Props {\n modelValue?: number\n min?: number\n max?: number\n step?: number\n disabled?: boolean\n error?: boolean\n size?: 'sm' | 'md' | 'lg'\n placeholder?: string\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n step: 1,\n disabled: false,\n error: false,\n size: 'md',\n})\n\nconst emit = defineEmits<{\n 'update:modelValue': [value: number | undefined]\n}>()\n\nconst canDecrement = computed(() => {\n if (props.modelValue === undefined) return true\n if (props.min === undefined) return true\n return props.modelValue > props.min\n})\n\nconst canIncrement = computed(() => {\n if (props.modelValue === undefined) return true\n if (props.max === undefined) return true\n return props.modelValue < props.max\n})\n\nfunction clamp(value: number): number {\n let result = value\n if (props.min !== undefined && result < props.min) result = props.min\n if (props.max !== undefined && result > props.max) result = props.max\n return result\n}\n\nfunction handleInput(event: Event) {\n const target = event.target as HTMLInputElement\n const value = target.value === '' ? undefined : Number(target.value)\n if (value !== undefined && !isNaN(value)) {\n emit('update:modelValue', clamp(value))\n } else {\n emit('update:modelValue', undefined)\n }\n}\n\nfunction decrement() {\n if (props.disabled || !canDecrement.value) return\n const current = props.modelValue ?? (props.max ?? 0)\n emit('update:modelValue', clamp(current - props.step))\n}\n\nfunction increment() {\n if (props.disabled || !canIncrement.value) return\n const current = props.modelValue ?? (props.min ?? 0)\n emit('update:modelValue', clamp(current + props.step))\n}\n</script>\n\n<template>\n <div\n :class=\"[\n 'mld-number-input',\n `mld-number-input--${size}`,\n error ? 'mld-number-input--error' : '',\n disabled ? 'mld-number-input--disabled' : '',\n ]\"\n >\n <input\n type=\"number\"\n :value=\"modelValue\"\n :min=\"min\"\n :max=\"max\"\n :step=\"step\"\n :disabled=\"disabled\"\n :placeholder=\"placeholder\"\n :class=\"[\n 'mld-number-input__input',\n `mld-number-input__input--${size}`,\n disabled ? 'mld-number-input__input--disabled' : '',\n ]\"\n @input=\"handleInput\"\n />\n\n <div class=\"mld-number-input__buttons\">\n <button\n type=\"button\"\n aria-label=\"Decrease value\"\n :disabled=\"disabled || !canDecrement\"\n :class=\"[\n 'mld-number-input__button',\n 'mld-number-input__button--decrement',\n `mld-number-input__button--${size}`,\n ]\"\n @click=\"decrement\"\n >\n <svg class=\"mld-number-input__button-icon\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" viewBox=\"0 0 24 24\">\n <path d=\"M5 12h14\" />\n </svg>\n </button>\n\n <button\n type=\"button\"\n aria-label=\"Increase value\"\n :disabled=\"disabled || !canIncrement\"\n :class=\"[\n 'mld-number-input__button',\n 'mld-number-input__button--increment',\n `mld-number-input__button--${size}`,\n ]\"\n @click=\"increment\"\n >\n <svg class=\"mld-number-input__button-icon\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" viewBox=\"0 0 24 24\">\n <path d=\"M5 12h14\" /><path d=\"M12 5v14\" />\n </svg>\n </button>\n </div>\n </div>\n</template>\n\n<style>\n@import '../styles/components/number-input.css';\n</style>\n"],"names":["_createElementBlock","_normalizeClass","_createElementVNode"],"mappings":";;;;;;;;;;;;;;;;;;;AAcA,UAAM,QAAQ;AAOd,UAAM,OAAO;AAIb,UAAM,eAAe,SAAS,MAAM;AAClC,UAAI,MAAM,eAAe,OAAW,QAAO;AAC3C,UAAI,MAAM,QAAQ,OAAW,QAAO;AACpC,aAAO,MAAM,aAAa,MAAM;AAAA,IAClC,CAAC;AAED,UAAM,eAAe,SAAS,MAAM;AAClC,UAAI,MAAM,eAAe,OAAW,QAAO;AAC3C,UAAI,MAAM,QAAQ,OAAW,QAAO;AACpC,aAAO,MAAM,aAAa,MAAM;AAAA,IAClC,CAAC;AAED,aAAS,MAAM,OAAuB;AACpC,UAAI,SAAS;AACb,UAAI,MAAM,QAAQ,UAAa,SAAS,MAAM,cAAc,MAAM;AAClE,UAAI,MAAM,QAAQ,UAAa,SAAS,MAAM,cAAc,MAAM;AAClE,aAAO;AAAA,IACT;AAEA,aAAS,YAAY,OAAc;AACjC,YAAM,SAAS,MAAM;AACrB,YAAM,QAAQ,OAAO,UAAU,KAAK,SAAY,OAAO,OAAO,KAAK;AACnE,UAAI,UAAU,UAAa,CAAC,MAAM,KAAK,GAAG;AACxC,aAAK,qBAAqB,MAAM,KAAK,CAAC;AAAA,MACxC,OAAO;AACL,aAAK,qBAAqB,MAAS;AAAA,MACrC;AAAA,IACF;AAEA,aAAS,YAAY;AACnB,UAAI,MAAM,YAAY,CAAC,aAAa,MAAO;AAC3C,YAAM,UAAU,MAAM,eAAe,MAAM,OAAO;AAClD,WAAK,qBAAqB,MAAM,UAAU,MAAM,IAAI,CAAC;AAAA,IACvD;AAEA,aAAS,YAAY;AACnB,UAAI,MAAM,YAAY,CAAC,aAAa,MAAO;AAC3C,YAAM,UAAU,MAAM,eAAe,MAAM,OAAO;AAClD,WAAK,qBAAqB,MAAM,UAAU,MAAM,IAAI,CAAC;AAAA,IACvD;;0BAIEA,mBAyDM,OAAA;AAAA,QAxDH,OAAKC,eAAA;AAAA;+BAAyD,QAAA,IAAI;AAAA,UAAU,QAAA,QAAK,4BAAA;AAAA,UAAyC,QAAA,WAAQ,+BAAA;AAAA,QAAA;;QAOnIC,mBAcE,SAAA;AAAA,UAbA,MAAK;AAAA,UACJ,OAAO,QAAA;AAAA,UACP,KAAK,QAAA;AAAA,UACL,KAAK,QAAA;AAAA,UACL,MAAM,QAAA;AAAA,UACN,UAAU,QAAA;AAAA,UACV,aAAa,QAAA;AAAA,UACb,OAAKD,eAAA;AAAA;wCAA2E,QAAA,IAAI;AAAA,YAAY,QAAA,WAAQ,sCAAA;AAAA,UAAA;UAKxG,SAAO;AAAA,QAAA;QAGVC,mBAgCM,OAhCN,YAgCM;AAAA,UA/BJA,mBAcS,UAAA;AAAA,YAbP,MAAK;AAAA,YACL,cAAW;AAAA,YACV,UAAU,QAAA,YAAQ,CAAK,aAAA;AAAA,YACvB,OAAKD,eAAA;AAAA;;2CAAkI,QAAA,IAAI;AAAA,YAAA;YAK3I,SAAO;AAAA,UAAA;YAERC,mBAEM,OAAA;AAAA,cAFD,OAAM;AAAA,cAAgC,MAAK;AAAA,cAAO,QAAO;AAAA,cAAe,gBAAa;AAAA,cAAI,kBAAe;AAAA,cAAQ,mBAAgB;AAAA,cAAQ,SAAQ;AAAA,YAAA;cACnJA,mBAAqB,QAAA,EAAf,GAAE,YAAU;AAAA,YAAA;;UAItBA,mBAcS,UAAA;AAAA,YAbP,MAAK;AAAA,YACL,cAAW;AAAA,YACV,UAAU,QAAA,YAAQ,CAAK,aAAA;AAAA,YACvB,OAAKD,eAAA;AAAA;;2CAAkI,QAAA,IAAI;AAAA,YAAA;YAK3I,SAAO;AAAA,UAAA;YAERC,mBAEM,OAAA;AAAA,cAFD,OAAM;AAAA,cAAgC,MAAK;AAAA,cAAO,QAAO;AAAA,cAAe,gBAAa;AAAA,cAAI,kBAAe;AAAA,cAAQ,mBAAgB;AAAA,cAAQ,SAAQ;AAAA,YAAA;cACnJA,mBAAqB,QAAA,EAAf,GAAE,YAAU;AAAA,cAAGA,mBAAqB,QAAA,EAAf,GAAE,YAAU;AAAA,YAAA;;;;;;;"}
@@ -8,6 +8,7 @@ export declare function analyzeDelimiter(lines: string[]): {
8
8
  consistency: number;
9
9
  };
10
10
  export declare function detectOutliers(lines: string[], delimiter: string, minFieldCount: number): OutlierInfo[];
11
+ export declare function classifyOutlierAction(sample: string, delimiter: string): OutlierAction;
11
12
  export declare function extractColumns(samples: string[], delimiter: string, minFieldCount: number): ColumnInfo[];
12
13
  export declare function parseCSVLine(line: string): string[];
13
14
  export declare function parseCSV(text: string): ParsedCsvData;
@@ -66,6 +66,19 @@ function detectOutliers(lines, delimiter, minFieldCount) {
66
66
  }
67
67
  return outliers;
68
68
  }
69
+ const QC_KEYWORDS = /* @__PURE__ */ new Set([
70
+ "eqc",
71
+ "iqc",
72
+ "qc",
73
+ "blank",
74
+ "std",
75
+ "standard",
76
+ "test"
77
+ ]);
78
+ function classifyOutlierAction(sample, delimiter) {
79
+ const segments = sample.split(delimiter);
80
+ return segments.some((seg) => QC_KEYWORDS.has(seg.toLowerCase())) ? "qc" : "include";
81
+ }
69
82
  function extractColumns(samples, delimiter, minFieldCount) {
70
83
  if (samples.length === 0) return [];
71
84
  const suffixCount = minFieldCount - 1;
@@ -286,14 +299,20 @@ function useAutoGroup() {
286
299
  const analysis = analyzeDelimiter(lines);
287
300
  delimiter.value = analysis.delimiter;
288
301
  dominantFieldCount.value = analysis.dominantFieldCount;
289
- minFieldCount.value = analysis.minFieldCount;
290
- outliers.value = detectOutliers(lines, analysis.delimiter, analysis.minFieldCount);
302
+ outliers.value = detectOutliers(lines, analysis.delimiter, analysis.dominantFieldCount);
303
+ for (const outlier of outliers.value) {
304
+ outlier.action = classifyOutlierAction(outlier.sample, analysis.delimiter);
305
+ }
291
306
  const conforming = lines.filter(
292
307
  (_, i) => !outliers.value.some((o) => o.index === i)
293
308
  );
294
- fields.value = extractColumns(conforming, analysis.delimiter, analysis.minFieldCount);
309
+ const conformingFieldCounts = conforming.map((s) => s.split(analysis.delimiter).length);
310
+ minFieldCount.value = conformingFieldCounts.length > 0 ? Math.min(...conformingFieldCounts) : analysis.dominantFieldCount;
311
+ fields.value = extractColumns(conforming, analysis.delimiter, minFieldCount.value);
295
312
  fieldNames.value = {};
296
- enabledFields.value = new Set(fields.value.map((f) => f.index));
313
+ enabledFields.value = new Set(
314
+ fields.value.filter((f) => f.cardinality > 1).map((f) => f.index)
315
+ );
297
316
  }
298
317
  function parseCsvInput() {
299
318
  if (!csvData.value) return;
@@ -387,6 +406,7 @@ function useAutoGroup() {
387
406
  export {
388
407
  DEFAULT_COLORS,
389
408
  analyzeDelimiter,
409
+ classifyOutlierAction,
390
410
  computeGroups,
391
411
  detectOutliers,
392
412
  extractColumns,
@@ -1 +1 @@
1
- {"version":3,"file":"useAutoGroup.js","sources":["../../src/composables/useAutoGroup.ts"],"sourcesContent":["import { ref, computed } from 'vue'\nimport type {\n InputMode,\n OutlierAction,\n OutlierInfo,\n ColumnInfo,\n MetadataRow,\n AutoGroupResult,\n ParsedCsvData,\n} from '../types/auto-group'\nimport type { SampleGroup } from '../types/components'\n\nexport const DEFAULT_COLORS = [\n '#3B82F6', '#10B981', '#F59E0B', '#EF4444', '#8B5CF6',\n '#EC4899', '#06B6D4', '#84CC16', '#F97316', '#6366F1',\n]\n\nconst DELIMITER_CANDIDATES = ['_', '-', '.'] as const\n\n// --- Pure functions (exported for testing) ---\n\nexport function analyzeDelimiter(lines: string[]): {\n delimiter: string\n dominantFieldCount: number\n minFieldCount: number\n consistency: number\n} {\n if (lines.length === 0) {\n return { delimiter: '_', dominantFieldCount: 1, minFieldCount: 1, consistency: 0 }\n }\n\n let bestDelimiter = '_'\n let bestConsistency = -1\n let bestFieldCount = 1\n\n for (const candidate of DELIMITER_CANDIDATES) {\n const fieldCounts = lines.map(line => line.split(candidate).length)\n const countFrequency = new Map<number, number>()\n\n for (const count of fieldCounts) {\n countFrequency.set(count, (countFrequency.get(count) ?? 0) + 1)\n }\n\n // Find mode (most frequent field count)\n let modeCount = 1\n let modeFrequency = 0\n for (const [count, freq] of countFrequency) {\n if (freq > modeFrequency || (freq === modeFrequency && count > modeCount)) {\n modeCount = count\n modeFrequency = freq\n }\n }\n\n const rawConsistency = modeFrequency / lines.length\n // A delimiter that produces field count 1 didn't actually split anything\n const consistency = modeCount > 1 ? rawConsistency : 0\n\n if (\n consistency > bestConsistency ||\n (consistency === bestConsistency &&\n DELIMITER_CANDIDATES.indexOf(candidate) < DELIMITER_CANDIDATES.indexOf(bestDelimiter as typeof candidate))\n ) {\n bestDelimiter = candidate\n bestConsistency = consistency\n bestFieldCount = modeCount\n }\n }\n\n const bestFieldCounts = lines.map(line => line.split(bestDelimiter).length)\n const multiFieldCounts = bestFieldCounts.filter(c => c >= 2)\n const minFieldCount = multiFieldCounts.length > 0 ? Math.min(...multiFieldCounts) : 1\n\n return {\n delimiter: bestDelimiter,\n dominantFieldCount: bestFieldCount,\n minFieldCount,\n consistency: bestConsistency,\n }\n}\n\nexport function detectOutliers(\n lines: string[],\n delimiter: string,\n minFieldCount: number,\n): OutlierInfo[] {\n const outliers: OutlierInfo[] = []\n\n for (let i = 0; i < lines.length; i++) {\n const fieldCount = lines[i].split(delimiter).length\n if (fieldCount < minFieldCount) {\n outliers.push({\n sample: lines[i],\n index: i,\n fieldCount,\n action: 'include',\n })\n }\n }\n\n return outliers\n}\n\nexport function extractColumns(\n samples: string[],\n delimiter: string,\n minFieldCount: number,\n): ColumnInfo[] {\n if (samples.length === 0) return []\n\n const suffixCount = minFieldCount - 1\n const rows = samples.map(s => {\n const parts = s.split(delimiter)\n const splitAt = parts.length - suffixCount\n return [\n parts.slice(0, splitAt).join(delimiter),\n ...parts.slice(splitAt),\n ]\n })\n\n const columnCount = minFieldCount\n const columns: ColumnInfo[] = []\n for (let col = 0; col < columnCount; col++) {\n const values = rows.map(row => row[col])\n const unique = [...new Set(values)]\n columns.push({\n index: col,\n name: col === 0 ? 'Condition' : `Field ${col + 1}`,\n uniqueValues: unique,\n cardinality: unique.length,\n type: col === 0 ? 'prefix' : 'suffix',\n })\n }\n\n return columns\n}\n\nexport function parseCSVLine(line: string): string[] {\n const result: string[] = []\n let current = ''\n let inQuotes = false\n\n for (let i = 0; i < line.length; i++) {\n const char = line[i]\n if (char === '\"') {\n inQuotes = !inQuotes\n } else if (char === ',' && !inQuotes) {\n result.push(current.trim())\n current = ''\n } else {\n current += char\n }\n }\n result.push(current.trim())\n\n return result\n}\n\nexport function parseCSV(text: string): ParsedCsvData {\n const lines = text.trim().split('\\n')\n if (lines.length < 2) {\n throw new Error('CSV must have at least a header and one data row')\n }\n\n const headers = parseCSVLine(lines[0])\n const rows: Record<string, string>[] = []\n\n for (let i = 1; i < lines.length; i++) {\n const values = parseCSVLine(lines[i])\n if (values.length !== headers.length) continue\n const row: Record<string, string> = {}\n headers.forEach((header, idx) => {\n row[header] = values[idx]\n })\n rows.push(row)\n }\n\n // Auto-detect sample column\n const sampleKeywords = ['sample', 'name', 'id', 'sample_name', 'samplename']\n const sampleColumn =\n headers.find(h => sampleKeywords.includes(h.toLowerCase())) ?? headers[0]\n\n return { columns: headers, rows, sampleColumn }\n}\n\nexport function computeGroups(\n allSamples: string[],\n columns: ColumnInfo[],\n enabledFields: Set<number>,\n outlierActions: Map<number, OutlierAction>,\n delimiter: string,\n minFieldCount: number,\n): { groups: SampleGroup[]; metadata: MetadataRow[]; excludedSamples: string[] } {\n const excludedSamples: string[] = []\n const qcSamples: string[] = []\n const conformingSamples: string[] = []\n\n for (let i = 0; i < allSamples.length; i++) {\n const action = outlierActions.get(i)\n if (action === 'exclude') {\n excludedSamples.push(allSamples[i])\n } else if (action === 'qc') {\n qcSamples.push(allSamples[i])\n } else {\n conformingSamples.push(allSamples[i])\n }\n }\n\n // Build group map\n const groupMap = new Map<string, string[]>()\n const metadata: MetadataRow[] = []\n const enabledIndices = [...enabledFields].sort((a, b) => a - b)\n\n const suffixCount = minFieldCount - 1\n\n for (const sample of conformingSamples) {\n const parts = sample.split(delimiter)\n const splitAt = Math.max(1, parts.length - suffixCount)\n const row = [\n parts.slice(0, splitAt).join(delimiter),\n ...parts.slice(splitAt),\n ]\n\n // Build group key from enabled columns\n const keyParts: string[] = []\n for (const idx of enabledIndices) {\n if (idx < row.length && idx < columns.length) {\n keyParts.push(row[idx])\n }\n }\n const groupKey = keyParts.join(' / ')\n\n if (!groupMap.has(groupKey)) {\n groupMap.set(groupKey, [])\n }\n groupMap.get(groupKey)!.push(sample)\n\n // Build metadata row with ALL columns\n const fields: Record<string, string> = {}\n for (const col of columns) {\n if (col.index < row.length) {\n fields[col.name] = row[col.index]\n }\n }\n metadata.push({ sampleName: sample, fields, group: groupKey })\n }\n\n // Convert to SampleGroup[]\n const groups: SampleGroup[] = []\n let colorIdx = 0\n for (const [name, samples] of groupMap) {\n groups.push({\n name,\n color: DEFAULT_COLORS[colorIdx % DEFAULT_COLORS.length],\n samples,\n })\n colorIdx++\n }\n\n // QC group\n if (qcSamples.length > 0) {\n groups.push({\n name: 'QC',\n color: '#6B7280',\n samples: qcSamples,\n })\n for (const sample of qcSamples) {\n metadata.push({ sampleName: sample, fields: {}, group: 'QC' })\n }\n }\n\n return { groups, metadata, excludedSamples }\n}\n\n// --- Reactive composable ---\n\nexport function useAutoGroup() {\n const inputMode = ref<InputMode>('paste')\n const rawText = ref('')\n const csvData = ref<ParsedCsvData | null>(null)\n const delimiter = ref('_')\n const dominantFieldCount = ref(1)\n const minFieldCount = ref(1)\n const outliers = ref<OutlierInfo[]>([])\n const fields = ref<ColumnInfo[]>([])\n const fieldNames = ref<Record<number, string>>({})\n const enabledFields = ref(new Set<number>())\n\n const samples = computed(() => {\n if (inputMode.value === 'csv' && csvData.value) {\n return csvData.value.rows.map(r => r[csvData.value!.sampleColumn])\n }\n return rawText.value\n .split('\\n')\n .map(l => l.trim())\n .filter(l => l.length > 0)\n })\n\n const hasOutliers = computed(() => outliers.value.length > 0)\n\n const conformingSamples = computed(() => {\n const outlierIndices = new Set(outliers.value.map(o => o.index))\n return samples.value.filter((_, i) => !outlierIndices.has(i))\n })\n\n const outlierActions = computed(() => {\n const map = new Map<number, OutlierAction>()\n for (const o of outliers.value) {\n map.set(o.index, o.action)\n }\n return map\n })\n\n const effectiveColumns = computed(() => {\n return fields.value.map(col => ({\n ...col,\n name: fieldNames.value[col.index] ?? col.name,\n }))\n })\n\n const groups = computed(() => {\n if (effectiveColumns.value.length === 0 || enabledFields.value.size === 0) {\n return []\n }\n const result = computeGroups(\n samples.value,\n effectiveColumns.value,\n enabledFields.value,\n outlierActions.value,\n delimiter.value,\n minFieldCount.value,\n )\n return result.groups\n })\n\n const metadata = computed(() => {\n if (effectiveColumns.value.length === 0 || enabledFields.value.size === 0) {\n return []\n }\n return computeGroups(\n samples.value,\n effectiveColumns.value,\n enabledFields.value,\n outlierActions.value,\n delimiter.value,\n minFieldCount.value,\n ).metadata\n })\n\n const excludedSamples = computed(() => {\n return computeGroups(\n samples.value,\n effectiveColumns.value,\n enabledFields.value,\n outlierActions.value,\n delimiter.value,\n minFieldCount.value,\n ).excludedSamples\n })\n\n const result = computed<AutoGroupResult>(() => ({\n groups: groups.value,\n metadata: metadata.value,\n excludedSamples: excludedSamples.value,\n }))\n\n function parseInput() {\n if (inputMode.value === 'csv' && csvData.value) {\n parseCsvInput()\n } else {\n parsePasteInput()\n }\n }\n\n function parsePasteInput() {\n const lines = samples.value\n if (lines.length === 0) return\n\n const analysis = analyzeDelimiter(lines)\n delimiter.value = analysis.delimiter\n dominantFieldCount.value = analysis.dominantFieldCount\n minFieldCount.value = analysis.minFieldCount\n\n outliers.value = detectOutliers(lines, analysis.delimiter, analysis.minFieldCount)\n\n const conforming = lines.filter(\n (_, i) => !outliers.value.some(o => o.index === i)\n )\n fields.value = extractColumns(conforming, analysis.delimiter, analysis.minFieldCount)\n\n // Reset field names and enable all by default\n fieldNames.value = {}\n enabledFields.value = new Set(fields.value.map(f => f.index))\n }\n\n function parseCsvInput() {\n if (!csvData.value) return\n\n const csv = csvData.value\n const nonSampleCols = csv.columns.filter(c => c !== csv.sampleColumn)\n\n fields.value = nonSampleCols.map((col, i) => {\n const values = csv.rows.map(r => r[col])\n const unique = [...new Set(values)]\n return {\n index: i,\n name: col,\n uniqueValues: unique,\n cardinality: unique.length,\n }\n })\n\n // For CSV, no outliers\n outliers.value = []\n delimiter.value = ','\n dominantFieldCount.value = csv.columns.length\n\n fieldNames.value = {}\n for (const f of fields.value) {\n fieldNames.value[f.index] = f.name\n }\n enabledFields.value = new Set(fields.value.map(f => f.index))\n }\n\n function setOutlierAction(index: number, action: OutlierAction) {\n const outlier = outliers.value.find(o => o.index === index)\n if (outlier) {\n outlier.action = action\n // Trigger reactivity\n outliers.value = [...outliers.value]\n }\n }\n\n function setAllOutlierActions(action: OutlierAction) {\n for (const outlier of outliers.value) {\n outlier.action = action\n }\n outliers.value = [...outliers.value]\n }\n\n function toggleField(index: number) {\n const newSet = new Set(enabledFields.value)\n if (newSet.has(index)) {\n newSet.delete(index)\n } else {\n newSet.add(index)\n }\n enabledFields.value = newSet\n }\n\n function renameField(index: number, name: string) {\n fieldNames.value = { ...fieldNames.value, [index]: name }\n }\n\n function reset() {\n rawText.value = ''\n csvData.value = null\n delimiter.value = '_'\n dominantFieldCount.value = 1\n minFieldCount.value = 1\n outliers.value = []\n fields.value = []\n fieldNames.value = {}\n enabledFields.value = new Set()\n }\n\n return {\n // State\n inputMode,\n rawText,\n csvData,\n delimiter,\n dominantFieldCount,\n minFieldCount,\n outliers,\n fields,\n fieldNames,\n enabledFields,\n // Computed\n samples,\n hasOutliers,\n conformingSamples,\n groups,\n metadata,\n excludedSamples,\n result,\n effectiveColumns,\n // Actions\n parseInput,\n setOutlierAction,\n setAllOutlierActions,\n toggleField,\n renameField,\n reset,\n }\n}\n"],"names":["result"],"mappings":"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"}
1
+ {"version":3,"file":"useAutoGroup.js","sources":["../../src/composables/useAutoGroup.ts"],"sourcesContent":["import { ref, computed } from 'vue'\nimport type {\n InputMode,\n OutlierAction,\n OutlierInfo,\n ColumnInfo,\n MetadataRow,\n AutoGroupResult,\n ParsedCsvData,\n} from '../types/auto-group'\nimport type { SampleGroup } from '../types/components'\n\nexport const DEFAULT_COLORS = [\n '#3B82F6', '#10B981', '#F59E0B', '#EF4444', '#8B5CF6',\n '#EC4899', '#06B6D4', '#84CC16', '#F97316', '#6366F1',\n]\n\nconst DELIMITER_CANDIDATES = ['_', '-', '.'] as const\n\n// --- Pure functions (exported for testing) ---\n\nexport function analyzeDelimiter(lines: string[]): {\n delimiter: string\n dominantFieldCount: number\n minFieldCount: number\n consistency: number\n} {\n if (lines.length === 0) {\n return { delimiter: '_', dominantFieldCount: 1, minFieldCount: 1, consistency: 0 }\n }\n\n let bestDelimiter = '_'\n let bestConsistency = -1\n let bestFieldCount = 1\n\n for (const candidate of DELIMITER_CANDIDATES) {\n const fieldCounts = lines.map(line => line.split(candidate).length)\n const countFrequency = new Map<number, number>()\n\n for (const count of fieldCounts) {\n countFrequency.set(count, (countFrequency.get(count) ?? 0) + 1)\n }\n\n // Find mode (most frequent field count)\n let modeCount = 1\n let modeFrequency = 0\n for (const [count, freq] of countFrequency) {\n if (freq > modeFrequency || (freq === modeFrequency && count > modeCount)) {\n modeCount = count\n modeFrequency = freq\n }\n }\n\n const rawConsistency = modeFrequency / lines.length\n // A delimiter that produces field count 1 didn't actually split anything\n const consistency = modeCount > 1 ? rawConsistency : 0\n\n if (\n consistency > bestConsistency ||\n (consistency === bestConsistency &&\n DELIMITER_CANDIDATES.indexOf(candidate) < DELIMITER_CANDIDATES.indexOf(bestDelimiter as typeof candidate))\n ) {\n bestDelimiter = candidate\n bestConsistency = consistency\n bestFieldCount = modeCount\n }\n }\n\n const bestFieldCounts = lines.map(line => line.split(bestDelimiter).length)\n const multiFieldCounts = bestFieldCounts.filter(c => c >= 2)\n const minFieldCount = multiFieldCounts.length > 0 ? Math.min(...multiFieldCounts) : 1\n\n return {\n delimiter: bestDelimiter,\n dominantFieldCount: bestFieldCount,\n minFieldCount,\n consistency: bestConsistency,\n }\n}\n\nexport function detectOutliers(\n lines: string[],\n delimiter: string,\n minFieldCount: number,\n): OutlierInfo[] {\n const outliers: OutlierInfo[] = []\n\n for (let i = 0; i < lines.length; i++) {\n const fieldCount = lines[i].split(delimiter).length\n if (fieldCount < minFieldCount) {\n outliers.push({\n sample: lines[i],\n index: i,\n fieldCount,\n action: 'include',\n })\n }\n }\n\n return outliers\n}\n\nconst QC_KEYWORDS = new Set([\n 'eqc', 'iqc', 'qc', 'blank', 'std', 'standard', 'test',\n])\n\nexport function classifyOutlierAction(\n sample: string,\n delimiter: string,\n): OutlierAction {\n const segments = sample.split(delimiter)\n return segments.some(seg => QC_KEYWORDS.has(seg.toLowerCase()))\n ? 'qc'\n : 'include'\n}\n\nexport function extractColumns(\n samples: string[],\n delimiter: string,\n minFieldCount: number,\n): ColumnInfo[] {\n if (samples.length === 0) return []\n\n const suffixCount = minFieldCount - 1\n const rows = samples.map(s => {\n const parts = s.split(delimiter)\n const splitAt = parts.length - suffixCount\n return [\n parts.slice(0, splitAt).join(delimiter),\n ...parts.slice(splitAt),\n ]\n })\n\n const columnCount = minFieldCount\n const columns: ColumnInfo[] = []\n for (let col = 0; col < columnCount; col++) {\n const values = rows.map(row => row[col])\n const unique = [...new Set(values)]\n columns.push({\n index: col,\n name: col === 0 ? 'Condition' : `Field ${col + 1}`,\n uniqueValues: unique,\n cardinality: unique.length,\n type: col === 0 ? 'prefix' : 'suffix',\n })\n }\n\n return columns\n}\n\nexport function parseCSVLine(line: string): string[] {\n const result: string[] = []\n let current = ''\n let inQuotes = false\n\n for (let i = 0; i < line.length; i++) {\n const char = line[i]\n if (char === '\"') {\n inQuotes = !inQuotes\n } else if (char === ',' && !inQuotes) {\n result.push(current.trim())\n current = ''\n } else {\n current += char\n }\n }\n result.push(current.trim())\n\n return result\n}\n\nexport function parseCSV(text: string): ParsedCsvData {\n const lines = text.trim().split('\\n')\n if (lines.length < 2) {\n throw new Error('CSV must have at least a header and one data row')\n }\n\n const headers = parseCSVLine(lines[0])\n const rows: Record<string, string>[] = []\n\n for (let i = 1; i < lines.length; i++) {\n const values = parseCSVLine(lines[i])\n if (values.length !== headers.length) continue\n const row: Record<string, string> = {}\n headers.forEach((header, idx) => {\n row[header] = values[idx]\n })\n rows.push(row)\n }\n\n // Auto-detect sample column\n const sampleKeywords = ['sample', 'name', 'id', 'sample_name', 'samplename']\n const sampleColumn =\n headers.find(h => sampleKeywords.includes(h.toLowerCase())) ?? headers[0]\n\n return { columns: headers, rows, sampleColumn }\n}\n\nexport function computeGroups(\n allSamples: string[],\n columns: ColumnInfo[],\n enabledFields: Set<number>,\n outlierActions: Map<number, OutlierAction>,\n delimiter: string,\n minFieldCount: number,\n): { groups: SampleGroup[]; metadata: MetadataRow[]; excludedSamples: string[] } {\n const excludedSamples: string[] = []\n const qcSamples: string[] = []\n const conformingSamples: string[] = []\n\n for (let i = 0; i < allSamples.length; i++) {\n const action = outlierActions.get(i)\n if (action === 'exclude') {\n excludedSamples.push(allSamples[i])\n } else if (action === 'qc') {\n qcSamples.push(allSamples[i])\n } else {\n conformingSamples.push(allSamples[i])\n }\n }\n\n // Build group map\n const groupMap = new Map<string, string[]>()\n const metadata: MetadataRow[] = []\n const enabledIndices = [...enabledFields].sort((a, b) => a - b)\n\n const suffixCount = minFieldCount - 1\n\n for (const sample of conformingSamples) {\n const parts = sample.split(delimiter)\n const splitAt = Math.max(1, parts.length - suffixCount)\n const row = [\n parts.slice(0, splitAt).join(delimiter),\n ...parts.slice(splitAt),\n ]\n\n // Build group key from enabled columns\n const keyParts: string[] = []\n for (const idx of enabledIndices) {\n if (idx < row.length && idx < columns.length) {\n keyParts.push(row[idx])\n }\n }\n const groupKey = keyParts.join(' / ')\n\n if (!groupMap.has(groupKey)) {\n groupMap.set(groupKey, [])\n }\n groupMap.get(groupKey)!.push(sample)\n\n // Build metadata row with ALL columns\n const fields: Record<string, string> = {}\n for (const col of columns) {\n if (col.index < row.length) {\n fields[col.name] = row[col.index]\n }\n }\n metadata.push({ sampleName: sample, fields, group: groupKey })\n }\n\n // Convert to SampleGroup[]\n const groups: SampleGroup[] = []\n let colorIdx = 0\n for (const [name, samples] of groupMap) {\n groups.push({\n name,\n color: DEFAULT_COLORS[colorIdx % DEFAULT_COLORS.length],\n samples,\n })\n colorIdx++\n }\n\n // QC group\n if (qcSamples.length > 0) {\n groups.push({\n name: 'QC',\n color: '#6B7280',\n samples: qcSamples,\n })\n for (const sample of qcSamples) {\n metadata.push({ sampleName: sample, fields: {}, group: 'QC' })\n }\n }\n\n return { groups, metadata, excludedSamples }\n}\n\n// --- Reactive composable ---\n\nexport function useAutoGroup() {\n const inputMode = ref<InputMode>('paste')\n const rawText = ref('')\n const csvData = ref<ParsedCsvData | null>(null)\n const delimiter = ref('_')\n const dominantFieldCount = ref(1)\n const minFieldCount = ref(1)\n const outliers = ref<OutlierInfo[]>([])\n const fields = ref<ColumnInfo[]>([])\n const fieldNames = ref<Record<number, string>>({})\n const enabledFields = ref(new Set<number>())\n\n const samples = computed(() => {\n if (inputMode.value === 'csv' && csvData.value) {\n return csvData.value.rows.map(r => r[csvData.value!.sampleColumn])\n }\n return rawText.value\n .split('\\n')\n .map(l => l.trim())\n .filter(l => l.length > 0)\n })\n\n const hasOutliers = computed(() => outliers.value.length > 0)\n\n const conformingSamples = computed(() => {\n const outlierIndices = new Set(outliers.value.map(o => o.index))\n return samples.value.filter((_, i) => !outlierIndices.has(i))\n })\n\n const outlierActions = computed(() => {\n const map = new Map<number, OutlierAction>()\n for (const o of outliers.value) {\n map.set(o.index, o.action)\n }\n return map\n })\n\n const effectiveColumns = computed(() => {\n return fields.value.map(col => ({\n ...col,\n name: fieldNames.value[col.index] ?? col.name,\n }))\n })\n\n const groups = computed(() => {\n if (effectiveColumns.value.length === 0 || enabledFields.value.size === 0) {\n return []\n }\n const result = computeGroups(\n samples.value,\n effectiveColumns.value,\n enabledFields.value,\n outlierActions.value,\n delimiter.value,\n minFieldCount.value,\n )\n return result.groups\n })\n\n const metadata = computed(() => {\n if (effectiveColumns.value.length === 0 || enabledFields.value.size === 0) {\n return []\n }\n return computeGroups(\n samples.value,\n effectiveColumns.value,\n enabledFields.value,\n outlierActions.value,\n delimiter.value,\n minFieldCount.value,\n ).metadata\n })\n\n const excludedSamples = computed(() => {\n return computeGroups(\n samples.value,\n effectiveColumns.value,\n enabledFields.value,\n outlierActions.value,\n delimiter.value,\n minFieldCount.value,\n ).excludedSamples\n })\n\n const result = computed<AutoGroupResult>(() => ({\n groups: groups.value,\n metadata: metadata.value,\n excludedSamples: excludedSamples.value,\n }))\n\n function parseInput() {\n if (inputMode.value === 'csv' && csvData.value) {\n parseCsvInput()\n } else {\n parsePasteInput()\n }\n }\n\n function parsePasteInput() {\n const lines = samples.value\n if (lines.length === 0) return\n\n const analysis = analyzeDelimiter(lines)\n delimiter.value = analysis.delimiter\n dominantFieldCount.value = analysis.dominantFieldCount\n\n // Use dominantFieldCount as outlier threshold so QC/test samples with\n // fewer fields than the majority are correctly flagged\n outliers.value = detectOutliers(lines, analysis.delimiter, analysis.dominantFieldCount)\n\n // Apply smart default actions: auto-classify QC/test samples\n for (const outlier of outliers.value) {\n outlier.action = classifyOutlierAction(outlier.sample, analysis.delimiter)\n }\n\n const conforming = lines.filter(\n (_, i) => !outliers.value.some(o => o.index === i)\n )\n\n // Recompute minFieldCount from conforming samples only\n const conformingFieldCounts = conforming.map(s => s.split(analysis.delimiter).length)\n minFieldCount.value = conformingFieldCounts.length > 0\n ? Math.min(...conformingFieldCounts)\n : analysis.dominantFieldCount\n\n fields.value = extractColumns(conforming, analysis.delimiter, minFieldCount.value)\n\n // Reset field names; auto-disable constant columns (cardinality 1)\n fieldNames.value = {}\n enabledFields.value = new Set(\n fields.value.filter(f => f.cardinality > 1).map(f => f.index)\n )\n }\n\n function parseCsvInput() {\n if (!csvData.value) return\n\n const csv = csvData.value\n const nonSampleCols = csv.columns.filter(c => c !== csv.sampleColumn)\n\n fields.value = nonSampleCols.map((col, i) => {\n const values = csv.rows.map(r => r[col])\n const unique = [...new Set(values)]\n return {\n index: i,\n name: col,\n uniqueValues: unique,\n cardinality: unique.length,\n }\n })\n\n // For CSV, no outliers\n outliers.value = []\n delimiter.value = ','\n dominantFieldCount.value = csv.columns.length\n\n fieldNames.value = {}\n for (const f of fields.value) {\n fieldNames.value[f.index] = f.name\n }\n enabledFields.value = new Set(fields.value.map(f => f.index))\n }\n\n function setOutlierAction(index: number, action: OutlierAction) {\n const outlier = outliers.value.find(o => o.index === index)\n if (outlier) {\n outlier.action = action\n // Trigger reactivity\n outliers.value = [...outliers.value]\n }\n }\n\n function setAllOutlierActions(action: OutlierAction) {\n for (const outlier of outliers.value) {\n outlier.action = action\n }\n outliers.value = [...outliers.value]\n }\n\n function toggleField(index: number) {\n const newSet = new Set(enabledFields.value)\n if (newSet.has(index)) {\n newSet.delete(index)\n } else {\n newSet.add(index)\n }\n enabledFields.value = newSet\n }\n\n function renameField(index: number, name: string) {\n fieldNames.value = { ...fieldNames.value, [index]: name }\n }\n\n function reset() {\n rawText.value = ''\n csvData.value = null\n delimiter.value = '_'\n dominantFieldCount.value = 1\n minFieldCount.value = 1\n outliers.value = []\n fields.value = []\n fieldNames.value = {}\n enabledFields.value = new Set()\n }\n\n return {\n // State\n inputMode,\n rawText,\n csvData,\n delimiter,\n dominantFieldCount,\n minFieldCount,\n outliers,\n fields,\n fieldNames,\n enabledFields,\n // Computed\n samples,\n hasOutliers,\n conformingSamples,\n groups,\n metadata,\n excludedSamples,\n result,\n effectiveColumns,\n // Actions\n parseInput,\n setOutlierAction,\n setAllOutlierActions,\n toggleField,\n renameField,\n reset,\n }\n}\n"],"names":["result"],"mappings":"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"}