@teselagen/ove 0.8.40 → 0.8.41

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.
@@ -0,0 +1,53 @@
1
+ /**
2
+ * @typedef {"mismatch"|"insertion"|"deletion"|"gap"} DifferenceType
3
+ *
4
+ * @typedef {Object} AlignmentDifference
5
+ * @property {number} position - 0-based column index in the aligned sequence
6
+ * @property {DifferenceType} type
7
+ * @property {string[]} bases - bases at this column for each track (template first)
8
+ */
9
+ /**
10
+ * Group consecutive same-type differences into regions.
11
+ * Mismatches are never grouped — each is its own entry.
12
+ * Insertions, deletions, and gaps that are side-by-side are collapsed into
13
+ * one entry with a `start` and `end` (both inclusive, 0-based).
14
+ *
15
+ * @param {AlignmentDifference[]} differences
16
+ * @returns {Array<AlignmentDifference & { start: number, end: number }>}
17
+ */
18
+ export function groupConsecutiveDifferences(differences: AlignmentDifference[]): Array<AlignmentDifference & {
19
+ start: number;
20
+ end: number;
21
+ }>;
22
+ /**
23
+ * Classify alignment columns into difference types relative to the template track.
24
+ *
25
+ * Template is alignedSeqs[0]. Non-template tracks are alignedSeqs[1+].
26
+ *
27
+ * Classification rules (per column):
28
+ * - "gap" : no non-template track is in its aligned region at this position
29
+ * - "insertion" : template has '-', at least one aligned non-template has a non-gap base
30
+ * - "deletion" : template has a non-gap base, at least one aligned non-template has '-'
31
+ * - "mismatch" : no gaps among aligned tracks, unique base set has more than one member
32
+ *
33
+ * Only tracks whose aligned region covers position i participate in classification.
34
+ * This correctly handles multi-read alignments (e.g. Sanger) where reads cover
35
+ * different sub-ranges of the full alignment.
36
+ *
37
+ * @param {string[]} alignedSeqs - Aligned sequence strings, all same length
38
+ * @returns {AlignmentDifference[]}
39
+ */
40
+ export function findAlignmentDifferences(alignedSeqs: string[]): AlignmentDifference[];
41
+ export default findAlignmentDifferences;
42
+ export type DifferenceType = "mismatch" | "insertion" | "deletion" | "gap";
43
+ export type AlignmentDifference = {
44
+ /**
45
+ * - 0-based column index in the aligned sequence
46
+ */
47
+ position: number;
48
+ type: DifferenceType;
49
+ /**
50
+ * - bases at this column for each track (template first)
51
+ */
52
+ bases: string[];
53
+ };
package/index.cjs.js CHANGED
@@ -95510,14 +95510,34 @@ function addHighlightedDifferences(alignmentTracks) {
95510
95510
  track.alignmentData.sequence
95511
95511
  );
95512
95512
  const mismatches = matchHighlightRanges.filter(({ isMatch }) => !isMatch);
95513
+ const alignedSeq = track.alignmentData.sequence;
95514
+ const seqLen = alignedSeq.length;
95515
+ const startIndex = seqLen - alignedSeq.replace(/^-+/, "").length;
95516
+ const endIndex = alignedSeq.replace(/-+$/, "").length;
95517
+ const gapRanges = [
95518
+ startIndex > 0 && {
95519
+ start: 0,
95520
+ end: startIndex - 1,
95521
+ differenceType: "gap"
95522
+ },
95523
+ endIndex < seqLen && {
95524
+ start: endIndex,
95525
+ end: seqLen - 1,
95526
+ differenceType: "gap"
95527
+ }
95528
+ ].filter(Boolean);
95513
95529
  return __spreadProps(__spreadValues({}, track), {
95514
95530
  sequenceData: sequenceData2,
95515
95531
  matchHighlightRanges,
95516
- additionalSelectionLayers: matchHighlightRanges.filter(({ isMatch }) => !isMatch).map((range2) => {
95517
- return __spreadProps(__spreadValues(__spreadValues({}, range2), highlightRangeProps), {
95532
+ additionalSelectionLayers: [
95533
+ ...matchHighlightRanges.filter(({ isMatch }) => !isMatch).map((range2) => __spreadProps(__spreadValues(__spreadValues({}, range2), highlightRangeProps), {
95518
95534
  className: "veAlignmentMismatch"
95519
- });
95520
- }),
95535
+ })),
95536
+ ...gapRanges.map((range2) => __spreadProps(__spreadValues(__spreadValues({}, range2), highlightRangeProps), {
95537
+ className: "veAlignmentMismatch"
95538
+ }))
95539
+ ],
95540
+ gapRanges,
95521
95541
  mismatches
95522
95542
  });
95523
95543
  });
@@ -95651,23 +95671,30 @@ function getRangeMatchesBetweenTemplateAndNonTemplate(tempSeq, nonTempSeq) {
95651
95671
  const startIndex = seqLength - nonTempSeqWithoutLeadingDashes.length;
95652
95672
  const endIndex = seqLength - (seqLength - nonTempSeqWithoutTrailingDashes.length);
95653
95673
  for (let index2 = startIndex; index2 < endIndex; index2++) {
95654
- const isMatch = tempSeq[index2].toLowerCase() === nonTempSeq[index2].toLowerCase();
95655
- const previousRange = ranges[ranges.length - 1];
95656
- if (previousRange) {
95657
- if (previousRange.isMatch === isMatch) {
95658
- previousRange.end++;
95674
+ const tempBase = tempSeq[index2].toLowerCase();
95675
+ const nonTempBase = nonTempSeq[index2].toLowerCase();
95676
+ const isMatch = tempBase === nonTempBase;
95677
+ let differenceType = null;
95678
+ if (!isMatch) {
95679
+ if (tempBase === "-") {
95680
+ differenceType = "insertion";
95681
+ } else if (nonTempBase === "-") {
95682
+ differenceType = "deletion";
95659
95683
  } else {
95660
- ranges.push({
95661
- start: index2,
95662
- end: index2,
95663
- isMatch
95664
- });
95684
+ differenceType = "mismatch";
95665
95685
  }
95686
+ }
95687
+ const previousRange = ranges[ranges.length - 1];
95688
+ if (previousRange && previousRange.isMatch === isMatch && previousRange.differenceType === differenceType) {
95689
+ previousRange.end++;
95690
+ } else if (previousRange) {
95691
+ ranges.push({ start: index2, end: index2, isMatch, differenceType });
95666
95692
  } else {
95667
95693
  ranges.push({
95668
95694
  start: startIndex,
95669
95695
  end: startIndex,
95670
- isMatch
95696
+ isMatch,
95697
+ differenceType
95671
95698
  });
95672
95699
  }
95673
95700
  }
@@ -117245,7 +117272,7 @@ function showFileDialog({ multiple = false, onSelect }) {
117245
117272
  input.click();
117246
117273
  }
117247
117274
  __name(showFileDialog, "showFileDialog");
117248
- const version = "0.8.40";
117275
+ const version = "0.8.41";
117249
117276
  const packageJson = {
117250
117277
  version
117251
117278
  };
@@ -125114,11 +125141,13 @@ const _Minimap = class _Minimap extends React.Component {
125114
125141
  dimensions: { width = 200 },
125115
125142
  laneHeight,
125116
125143
  laneSpacing = 1,
125117
- isTrackSelected = []
125144
+ isTrackSelected = [],
125145
+ activeFilterType = "all"
125118
125146
  } = this.props;
125119
125147
  const charWidth2 = this.getCharWidth();
125120
125148
  const {
125121
125149
  matchHighlightRanges: _matchHighlightRanges,
125150
+ gapRanges = [],
125122
125151
  alignmentData: { trimmedRange } = {}
125123
125152
  } = alignmentTracks[i];
125124
125153
  const matchHighlightRanges = !trimmedRange ? _matchHighlightRanges : flatMap(_matchHighlightRanges, (r2) => {
@@ -125145,10 +125174,19 @@ const _Minimap = class _Minimap extends React.Component {
125145
125174
  charWidth2
125146
125175
  );
125147
125176
  const toAdd = `M${xStart},${y2} L${xStart + width2},${y2} L${xStart + width2},${y2 + height} L${xStart},${y2 + height}`;
125148
- if (!range2.isMatch) {
125177
+ if (!range2.isMatch && (activeFilterType === "all" || range2.differenceType === activeFilterType)) {
125149
125178
  redPath += toAdd;
125150
125179
  }
125151
125180
  });
125181
+ if (activeFilterType === "gap") {
125182
+ gapRanges.forEach((range2) => {
125183
+ const { xStart, width: width2 } = getXStartAndWidthFromNonCircularRange(
125184
+ range2,
125185
+ charWidth2
125186
+ );
125187
+ redPath += `M${xStart},${y2} L${xStart + width2},${y2} L${xStart + width2},${y2 + height} L${xStart},${y2 + height}`;
125188
+ });
125189
+ }
125152
125190
  return /* @__PURE__ */ React.createElement(
125153
125191
  "div",
125154
125192
  {
@@ -125178,7 +125216,8 @@ const _Minimap = class _Minimap extends React.Component {
125178
125216
  "scrollAlignmentView",
125179
125217
  "laneHeight",
125180
125218
  "laneSpacing",
125181
- "isTrackSelected"
125219
+ "isTrackSelected",
125220
+ "activeFilterType"
125182
125221
  ].some((key) => props[key] !== newProps[key]))
125183
125222
  return true;
125184
125223
  return false;
@@ -125456,6 +125495,66 @@ function getTrimmedRangesToDisplay({ trimmedRange, seqLen }) {
125456
125495
  return splitRangeIntoTwoPartsIfItIsCircular(inverted, seqLen);
125457
125496
  }
125458
125497
  __name(getTrimmedRangesToDisplay, "getTrimmedRangesToDisplay");
125498
+ function groupConsecutiveDifferences(differences) {
125499
+ const grouped = [];
125500
+ for (const diff of differences) {
125501
+ if (diff.type === "mismatch") {
125502
+ grouped.push(__spreadProps(__spreadValues({}, diff), { start: diff.position, end: diff.position }));
125503
+ continue;
125504
+ }
125505
+ const last2 = grouped[grouped.length - 1];
125506
+ if (last2 && last2.type === diff.type && last2.end === diff.position - 1) {
125507
+ grouped[grouped.length - 1] = __spreadProps(__spreadValues({}, last2), { end: diff.position });
125508
+ } else {
125509
+ grouped.push(__spreadProps(__spreadValues({}, diff), { start: diff.position, end: diff.position }));
125510
+ }
125511
+ }
125512
+ return grouped;
125513
+ }
125514
+ __name(groupConsecutiveDifferences, "groupConsecutiveDifferences");
125515
+ function findAlignmentDifferences(alignedSeqs) {
125516
+ var _a2;
125517
+ if (alignedSeqs.length < 2 || !((_a2 = alignedSeqs[0]) == null ? void 0 : _a2.length)) return [];
125518
+ const template = alignedSeqs[0].toLowerCase();
125519
+ const nonTemplates = alignedSeqs.slice(1).map((s2) => s2.toLowerCase());
125520
+ const trackBounds = nonTemplates.map((seq) => {
125521
+ const withoutLeading = seq.replace(/^-+/, "");
125522
+ const withoutTrailing = seq.replace(/-+$/, "");
125523
+ const start2 = seq.length - withoutLeading.length;
125524
+ const end2 = seq.length - (seq.length - withoutTrailing.length);
125525
+ return { start: start2, end: end2 };
125526
+ });
125527
+ const differences = [];
125528
+ for (let i = 0; i < template.length; i++) {
125529
+ const templateBase = template[i];
125530
+ const allNonTemplateBases = nonTemplates.map((seq) => seq[i]);
125531
+ const bases = [templateBase, ...allNonTemplateBases];
125532
+ const alignedIndices = trackBounds.reduce((acc, { start: start2, end: end2 }, idx) => {
125533
+ if (i >= start2 && i < end2) acc.push(idx);
125534
+ return acc;
125535
+ }, []);
125536
+ if (alignedIndices.length === 0) {
125537
+ differences.push({ position: i, type: "gap", bases });
125538
+ continue;
125539
+ }
125540
+ const alignedBases = alignedIndices.map((idx) => allNonTemplateBases[idx]);
125541
+ const templateIsGap = templateBase === "-";
125542
+ const nonTemplateHasBase = alignedBases.some((b3) => b3 !== "-");
125543
+ const nonTemplateHasGap = alignedBases.some((b3) => b3 === "-");
125544
+ if (templateIsGap && nonTemplateHasBase) {
125545
+ differences.push({ position: i, type: "insertion", bases });
125546
+ } else if (!templateIsGap && nonTemplateHasGap) {
125547
+ differences.push({ position: i, type: "deletion", bases });
125548
+ } else if (!templateIsGap) {
125549
+ const uniqueBases = /* @__PURE__ */ new Set([templateBase, ...alignedBases]);
125550
+ if (uniqueBases.size > 1) {
125551
+ differences.push({ position: i, type: "mismatch", bases });
125552
+ }
125553
+ }
125554
+ }
125555
+ return differences;
125556
+ }
125557
+ __name(findAlignmentDifferences, "findAlignmentDifferences");
125459
125558
  function scrollToAlignmentSelection() {
125460
125559
  const el = document.querySelector(".veCaret");
125461
125560
  if (el) {
@@ -125469,159 +125568,145 @@ function updateCaretPosition({ start: start2, end: end2 }) {
125469
125568
  }
125470
125569
  }
125471
125570
  __name(updateCaretPosition, "updateCaretPosition");
125571
+ const FILTER_OPTIONS = [
125572
+ { value: "all", label: "All" },
125573
+ { value: "mismatch", label: "Mismatches" },
125574
+ { value: "insertion", label: "Insertions" },
125575
+ { value: "deletion", label: "Deletions" },
125576
+ { value: "gap", label: "Gaps" }
125577
+ ];
125472
125578
  function FindMismatches(props) {
125473
- const { alignmentJson, id: id2 } = props;
125579
+ var _a2;
125580
+ const { alignmentJson, id: id2, onFilterChange } = props;
125474
125581
  const alignedSeqs = React.useMemo(
125475
125582
  () => alignmentJson.map((t2) => {
125476
- var _a2;
125477
- return ((_a2 = t2.alignmentData) == null ? void 0 : _a2.sequence) || "";
125583
+ var _a3;
125584
+ return ((_a3 = t2.alignmentData) == null ? void 0 : _a3.sequence) || "";
125478
125585
  }),
125479
125586
  [alignmentJson]
125480
125587
  );
125481
- const mismatches = React.useMemo(() => {
125482
- const result = [{ position: 0, bases: [""] }];
125483
- if (alignedSeqs.length > 1 && alignedSeqs[0].length) {
125484
- for (let i = 0; i < alignedSeqs[0].length; i++) {
125485
- const bases = alignedSeqs.map((seq) => seq[i]);
125486
- const uniqueBases = new Set(bases);
125487
- if (uniqueBases.size > 1 && !uniqueBases.has("-")) {
125488
- result.push({ position: i, bases });
125489
- }
125490
- }
125491
- }
125492
- return result;
125493
- }, [alignedSeqs]);
125588
+ const [activeFilter, setActiveFilter] = React.useState("all");
125589
+ const allDifferences = React.useMemo(
125590
+ () => groupConsecutiveDifferences(findAlignmentDifferences(alignedSeqs)),
125591
+ [alignedSeqs]
125592
+ );
125593
+ const countsByType = React.useMemo(() => {
125594
+ const counts = { all: 0, mismatch: 0, insertion: 0, deletion: 0, gap: 0 };
125595
+ allDifferences.forEach((d2) => {
125596
+ counts[d2.type] = (counts[d2.type] || 0) + 1;
125597
+ counts.all++;
125598
+ });
125599
+ return counts;
125600
+ }, [allDifferences]);
125601
+ const differences = React.useMemo(() => {
125602
+ const filtered = activeFilter === "all" ? allDifferences : allDifferences.filter((d2) => d2.type === activeFilter);
125603
+ return [{ position: -1, start: -1, end: -1, bases: [""] }, ...filtered];
125604
+ }, [allDifferences, activeFilter]);
125494
125605
  const currentCaretPosition = reactRedux.useSelector(
125495
125606
  (state2) => {
125496
- var _a2;
125497
- return (_a2 = state2.VectorEditor.__allEditorsOptions.alignments[id2]) == null ? void 0 : _a2.caretPosition;
125607
+ var _a3;
125608
+ return (_a3 = state2.VectorEditor.__allEditorsOptions.alignments[id2]) == null ? void 0 : _a3.caretPosition;
125498
125609
  }
125499
125610
  );
125500
125611
  const [currentIdx, setCurrentIdx] = React.useState(0);
125501
- const [disablePrev, setDisablePrev] = React.useState(true);
125502
- const [disableNext, setDisableNext] = React.useState(false);
125503
- const currentMismatch = mismatches[currentIdx];
125504
- const handleButtonsState = React.useCallback(
125505
- (caret) => {
125506
- if (mismatches.length <= 1) {
125507
- setDisablePrev(true);
125508
- setDisableNext(true);
125509
- return;
125510
- }
125511
- const firstMismatchPos = mismatches[1].position;
125512
- const lastMismatchPos = mismatches[mismatches.length - 1].position;
125513
- setDisablePrev(caret <= firstMismatchPos);
125514
- setDisableNext(caret >= lastMismatchPos);
125515
- },
125516
- [mismatches]
125517
- );
125612
+ const currentDiff = differences[currentIdx];
125613
+ const disablePrev = currentIdx <= 1;
125614
+ const disableNext = currentIdx >= differences.length - 1;
125615
+ React.useEffect(() => {
125616
+ setCurrentIdx(0);
125617
+ }, [activeFilter]);
125618
+ React.useEffect(() => {
125619
+ onFilterChange == null ? void 0 : onFilterChange({ activeFilter });
125620
+ }, [activeFilter, onFilterChange]);
125518
125621
  React.useEffect(() => {
125519
125622
  if (currentCaretPosition !== -1) {
125520
- const mismatchIdx = mismatches.findIndex(
125521
- (m2) => m2.position === currentCaretPosition || m2.position === currentCaretPosition - 1
125623
+ const diffIdx = differences.findIndex(
125624
+ (d2, i) => i > 0 && currentCaretPosition >= d2.start && currentCaretPosition <= d2.end + 1
125522
125625
  );
125523
- if (mismatchIdx !== -1 && mismatchIdx !== currentIdx) {
125524
- handleButtonsState(currentCaretPosition);
125525
- setCurrentIdx(mismatchIdx);
125626
+ if (diffIdx !== -1 && diffIdx !== currentIdx) {
125627
+ setCurrentIdx(diffIdx);
125526
125628
  }
125527
125629
  }
125528
- }, [currentCaretPosition, mismatches, currentIdx, handleButtonsState]);
125529
- const updateView = /* @__PURE__ */ __name((mismatch) => {
125530
- const idx = mismatches.indexOf(mismatch);
125531
- const position2 = mismatch.position;
125532
- handleButtonsState(position2);
125630
+ }, [currentCaretPosition, differences, currentIdx]);
125631
+ const updateView = /* @__PURE__ */ __name((diff) => {
125632
+ const idx = differences.indexOf(diff);
125633
+ const { start: start2, end: end2 } = diff;
125533
125634
  setCurrentIdx(idx);
125534
- updateCaretPosition({ start: position2, end: position2 });
125635
+ updateCaretPosition({ start: start2, end: end2 });
125535
125636
  setTimeout(() => {
125536
125637
  scrollToAlignmentSelection();
125537
125638
  }, 0);
125538
125639
  }, "updateView");
125539
- const prevMismatch = /* @__PURE__ */ __name(() => {
125540
- if (currentIdx > 1) {
125541
- const newIdx = Math.max(0, currentIdx - 1);
125542
- let prev = mismatches[newIdx];
125543
- if (currentCaretPosition > 0) {
125544
- prev = [...mismatches].reverse().find((m2) => m2.position < currentCaretPosition);
125545
- }
125546
- if (prev) {
125547
- updateView(prev);
125548
- }
125549
- }
125550
- }, "prevMismatch");
125551
- const nextMismatch = /* @__PURE__ */ __name(() => {
125552
- if (currentIdx < mismatches.length - 1) {
125553
- const newIdx = Math.min(mismatches.length - 1, currentIdx + 1);
125554
- let next = mismatches[newIdx];
125555
- if (currentCaretPosition > 0) {
125556
- next = mismatches.find(
125557
- (m2) => m2.position > currentCaretPosition && m2.position > 1
125558
- );
125559
- }
125560
- if (next) {
125561
- updateView(next);
125640
+ const prevDifference = /* @__PURE__ */ __name(() => {
125641
+ var _a3, _b2;
125642
+ const pivot = currentCaretPosition >= 0 ? currentCaretPosition : (_b2 = (_a3 = differences[currentIdx]) == null ? void 0 : _a3.start) != null ? _b2 : 0;
125643
+ const prev = [...differences].reverse().find((d2) => d2.start >= 0 && d2.start < pivot);
125644
+ if (prev) updateView(prev);
125645
+ }, "prevDifference");
125646
+ const nextDifference = /* @__PURE__ */ __name(() => {
125647
+ var _a3, _b2;
125648
+ const pivot = currentCaretPosition >= 0 ? currentCaretPosition : (_b2 = (_a3 = differences[currentIdx]) == null ? void 0 : _a3.start) != null ? _b2 : -1;
125649
+ const next = differences.find((d2) => d2.start > pivot && d2.start >= 0);
125650
+ if (next) updateView(next);
125651
+ }, "nextDifference");
125652
+ const noDifferences = differences.length <= 1;
125653
+ const activeOption = FILTER_OPTIONS.find((o2) => o2.value === activeFilter);
125654
+ const activeLabel = (_a2 = activeOption == null ? void 0 : activeOption.label) != null ? _a2 : "Differences";
125655
+ const filterMenu = /* @__PURE__ */ React.createElement(core.Menu, null, FILTER_OPTIONS.map(({ value, label }) => {
125656
+ var _a3;
125657
+ const count2 = (_a3 = countsByType[value]) != null ? _a3 : 0;
125658
+ const isActive2 = activeFilter === value;
125659
+ return /* @__PURE__ */ React.createElement(
125660
+ core.MenuItem,
125661
+ {
125662
+ key: value,
125663
+ active: isActive2,
125664
+ onClick: /* @__PURE__ */ __name(() => setActiveFilter(value), "onClick"),
125665
+ text: /* @__PURE__ */ React.createElement("span", { className: "veDiffMenuItem-inner" }, label, /* @__PURE__ */ React.createElement(core.Tag, { round: true, minimal: true, style: { marginLeft: 6 } }, count2))
125562
125666
  }
125563
- }
125564
- }, "nextMismatch");
125565
- return /* @__PURE__ */ React.createElement(
125566
- "div",
125667
+ );
125668
+ }));
125669
+ return /* @__PURE__ */ React.createElement("div", { className: "veDiffNavigator" }, /* @__PURE__ */ React.createElement(
125670
+ core.Popover,
125567
125671
  {
125568
- style: {
125569
- display: "flex",
125570
- flexDirection: "row",
125571
- justifyContent: "center",
125572
- alignItems: "center",
125573
- gap: 10
125574
- }
125575
- },
125576
- mismatches.length === 1 ? /* @__PURE__ */ React.createElement("span", { style: { fontStyle: "italic", color: "grey" } }, "no mismatches") : /* @__PURE__ */ React.createElement("div", { style: { display: "flex", flexDirection: "column" } }, /* @__PURE__ */ React.createElement(
125577
- "div",
125578
- {
125579
- style: {
125580
- display: "flex",
125581
- alignItems: "center"
125582
- }
125583
- },
125584
- /* @__PURE__ */ React.createElement("strong", null, "Mismatches"),
125585
- /* @__PURE__ */ React.createElement("div", { style: { display: "flex", gap: 2 } }, /* @__PURE__ */ React.createElement(
125586
- core.Button,
125587
- {
125588
- intent: "primary",
125589
- icon: "arrow-left",
125590
- "data-tip": "Previous Mismatch",
125591
- onClick: prevMismatch,
125592
- disabled: disablePrev,
125593
- small: true,
125594
- minimal: true
125595
- }
125596
- ), /* @__PURE__ */ React.createElement(
125672
+ minimal: true,
125673
+ position: core.Position.BOTTOM_LEFT,
125674
+ content: filterMenu,
125675
+ target: /* @__PURE__ */ React.createElement(
125597
125676
  core.Button,
125598
125677
  {
125599
- intent: "primary",
125600
- icon: "arrow-right",
125601
- "data-tip": "Next Mismatch",
125602
- onClick: nextMismatch,
125603
- disabled: disableNext,
125678
+ minimal: true,
125679
+ "data-tip": "Filter Difference Type",
125604
125680
  small: true,
125605
- minimal: true
125606
- }
125607
- ))
125608
- ), /* @__PURE__ */ React.createElement(
125609
- "span",
125610
- {
125611
- style: {
125612
- fontSize: "0.8em",
125613
- color: "grey",
125614
- lineHeight: "0.8em"
125615
- }
125616
- },
125617
- currentMismatch.position > 1 && /* @__PURE__ */ React.createElement("span", null, "Position: ", currentMismatch.position + 1, " | "),
125618
- "(",
125619
- currentIdx,
125620
- " of ",
125621
- mismatches.length - 1,
125622
- ")"
125623
- ))
125624
- );
125681
+ rightIcon: "caret-down",
125682
+ className: "veDiffFilter-trigger"
125683
+ },
125684
+ activeLabel
125685
+ )
125686
+ }
125687
+ ), noDifferences ? /* @__PURE__ */ React.createElement("span", { className: "veDiffNav-empty" }, "no", " ", activeFilter === "all" ? "differences" : activeLabel.toLowerCase()) : /* @__PURE__ */ React.createElement("div", { className: "veDiffNav" }, /* @__PURE__ */ React.createElement(
125688
+ core.Button,
125689
+ {
125690
+ minimal: true,
125691
+ small: true,
125692
+ "data-tip": "Previous Difference",
125693
+ icon: "arrow-left",
125694
+ intent: core.Intent.PRIMARY,
125695
+ onClick: prevDifference,
125696
+ disabled: disablePrev
125697
+ }
125698
+ ), /* @__PURE__ */ React.createElement("div", { className: "veDiffNav-center" }, /* @__PURE__ */ React.createElement("span", { className: "veDiffNav-fraction" }, currentIdx, /* @__PURE__ */ React.createElement("span", { className: "veDiffNav-sep" }, "/"), differences.length - 1), (currentDiff == null ? void 0 : currentDiff.start) > -1 && /* @__PURE__ */ React.createElement("span", { className: "veDiffNav-pos" }, ":", currentDiff.start + 1)), /* @__PURE__ */ React.createElement(
125699
+ core.Button,
125700
+ {
125701
+ minimal: true,
125702
+ small: true,
125703
+ "data-tip": "Next Difference",
125704
+ icon: "arrow-right",
125705
+ intent: core.Intent.PRIMARY,
125706
+ onClick: nextDifference,
125707
+ disabled: disableNext
125708
+ }
125709
+ )));
125625
125710
  }
125626
125711
  __name(FindMismatches, "FindMismatches");
125627
125712
  function getGapMap(sequence2) {
@@ -126310,17 +126395,18 @@ const AlignmentVisibilityTool = pure(/* @__PURE__ */ __name(function AlignmentVi
126310
126395
  minimal: true,
126311
126396
  position: "bottom",
126312
126397
  content: /* @__PURE__ */ React.createElement(VisibilityOptions$2, __spreadValues({}, props)),
126313
- target: /* @__PURE__ */ React.createElement(core.Tooltip, { content: "Visibility Options" }, /* @__PURE__ */ React.createElement(
126398
+ target: /* @__PURE__ */ React.createElement(
126314
126399
  core.Button,
126315
126400
  {
126316
126401
  className: "tg-alignment-visibility-toggle",
126317
126402
  small: true,
126403
+ "data-tip": "Visibility Options",
126318
126404
  rightIcon: "caret-down",
126319
126405
  intent: core.Intent.PRIMARY,
126320
126406
  minimal: true,
126321
126407
  icon: "eye-open"
126322
126408
  }
126323
- ))
126409
+ )
126324
126410
  }
126325
126411
  );
126326
126412
  }, "AlignmentVisibilityTool"));
@@ -140108,6 +140194,10 @@ const AlignmentView = /* @__PURE__ */ __name((props) => {
140108
140194
  const [tempTrimAfter, setTempTrimAfter] = React.useState({});
140109
140195
  const [tempTrimmingCaret, setTempTrimmingCaret] = React.useState({});
140110
140196
  const [searchMatchLayers, setSearchMatchLayers] = React.useState([]);
140197
+ const [activeFilterType, setActiveFilterType] = React.useState("all");
140198
+ const handleFilterChange = React.useCallback(({ activeFilter }) => {
140199
+ setActiveFilterType(activeFilter);
140200
+ }, []);
140111
140201
  const bindOutsideChangeHelper = React.useRef({});
140112
140202
  const alignmentHolder = React.useRef(null);
140113
140203
  const alignmentHolderTop = React.useRef(null);
@@ -140833,7 +140923,9 @@ ${seqDataToCopy}\r
140833
140923
  chromatogramData
140834
140924
  }) : linearViewOptions)), {
140835
140925
  additionalSelectionLayers: [
140836
- ...additionalSelectionLayers || [],
140926
+ ...i !== 0 ? (additionalSelectionLayers || []).filter(
140927
+ (layer) => activeFilterType === "all" ? layer.differenceType !== "gap" : layer.differenceType === activeFilterType
140928
+ ) : additionalSelectionLayers || [],
140837
140929
  ...searchMatchLayers || []
140838
140930
  ],
140839
140931
  dimensions: {
@@ -141492,7 +141584,14 @@ ${seqDataToCopy}\r
141492
141584
  setSearchMatchLayers
141493
141585
  }
141494
141586
  ),
141495
- /* @__PURE__ */ React.createElement(FindMismatches, { alignmentJson: alignmentTracks, id: id2 }),
141587
+ /* @__PURE__ */ React.createElement(
141588
+ FindMismatches,
141589
+ {
141590
+ alignmentJson: alignmentTracks,
141591
+ id: id2,
141592
+ onFilterChange: handleFilterChange
141593
+ }
141594
+ ),
141496
141595
  additionalTopEl,
141497
141596
  saveMessage && /* @__PURE__ */ React.createElement(
141498
141597
  "div",
@@ -141593,6 +141692,7 @@ ${seqDataToCopy}\r
141593
141692
  }
141594
141693
  )),
141595
141694
  alignmentTracks,
141695
+ activeFilterType,
141596
141696
  dimensions: {
141597
141697
  width: Math.max(width, 10) || 10
141598
141698
  },