@teselagen/ove 0.8.19 → 0.8.20

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.
package/index.cjs.js CHANGED
@@ -84349,6 +84349,160 @@ function calculateNebTm(sequence2, { monovalentCationConc = 0.05, primerConc = 5
84349
84349
  }
84350
84350
  }
84351
84351
  __name(calculateNebTm, "calculateNebTm");
84352
+ const PRIMER3_PARAMS = {
84353
+ saltMonovalent: 50,
84354
+ // mM
84355
+ saltDivalent: 1.5,
84356
+ // mM
84357
+ dntpConc: 0.6,
84358
+ // mM
84359
+ dnaConc: 50,
84360
+ // nM
84361
+ R: 1.987
84362
+ // Gas constant (cal/K·mol)
84363
+ };
84364
+ const SANTA_LUCIA_NN = {
84365
+ AA: { dH: -7.9, dS: -22.2 },
84366
+ TT: { dH: -7.9, dS: -22.2 },
84367
+ AT: { dH: -7.2, dS: -20.4 },
84368
+ TA: { dH: -7.2, dS: -21.3 },
84369
+ CA: { dH: -8.5, dS: -22.7 },
84370
+ TG: { dH: -8.5, dS: -22.7 },
84371
+ GT: { dH: -8.4, dS: -22.4 },
84372
+ AC: { dH: -8.4, dS: -22.4 },
84373
+ CT: { dH: -7.8, dS: -21 },
84374
+ AG: { dH: -7.8, dS: -21 },
84375
+ GA: { dH: -8.2, dS: -22.2 },
84376
+ TC: { dH: -8.2, dS: -22.2 },
84377
+ CG: { dH: -10.6, dS: -27.2 },
84378
+ GC: { dH: -9.8, dS: -24.4 },
84379
+ GG: { dH: -8, dS: -19.9 },
84380
+ CC: { dH: -8, dS: -19.9 }
84381
+ };
84382
+ const SANTA_LUCIA_INIT = {
84383
+ GC: { dH: 0.1, dS: -2.8 },
84384
+ // initiation with terminal GC
84385
+ AT: { dH: 2.3, dS: 4.1 }
84386
+ // initiation with terminal AT
84387
+ };
84388
+ function getEffectiveMonovalentConc() {
84389
+ let effectiveMono = PRIMER3_PARAMS.saltMonovalent;
84390
+ {
84391
+ const freeMg = Math.max(
84392
+ 0,
84393
+ PRIMER3_PARAMS.saltDivalent - PRIMER3_PARAMS.dntpConc
84394
+ );
84395
+ effectiveMono += 120 * Math.sqrt(freeMg);
84396
+ }
84397
+ return effectiveMono;
84398
+ }
84399
+ __name(getEffectiveMonovalentConc, "getEffectiveMonovalentConc");
84400
+ function applySaltCorrection(deltaS, nnPairs) {
84401
+ const effectiveMono = getEffectiveMonovalentConc();
84402
+ return deltaS + 0.368 * nnPairs * Math.log(effectiveMono / 1e3);
84403
+ }
84404
+ __name(applySaltCorrection, "applySaltCorrection");
84405
+ function isValidSequence(sequence2) {
84406
+ return /^[ATGCN]+$/.test(sequence2);
84407
+ }
84408
+ __name(isValidSequence, "isValidSequence");
84409
+ function calculateSantaLuciaTm(sequence2) {
84410
+ try {
84411
+ sequence2 = sequence2 == null ? void 0 : sequence2.toUpperCase().trim();
84412
+ if (!isValidSequence(sequence2)) {
84413
+ throw new Error("Invalid sequence: contains non-DNA characters");
84414
+ }
84415
+ if (sequence2.length < 2) {
84416
+ throw new Error("Sequence too short: minimum length is 2 bases");
84417
+ }
84418
+ let deltaH = 0;
84419
+ let deltaS = 0;
84420
+ for (let i = 0; i < sequence2.length - 1; i++) {
84421
+ const dinucleotide = sequence2.substring(i, i + 2);
84422
+ if (dinucleotide.includes("N")) {
84423
+ continue;
84424
+ }
84425
+ const params = SANTA_LUCIA_NN[dinucleotide];
84426
+ if (params) {
84427
+ deltaH += params.dH;
84428
+ deltaS += params.dS;
84429
+ }
84430
+ }
84431
+ const firstBase = sequence2[0];
84432
+ const lastBase = sequence2[sequence2.length - 1];
84433
+ if (firstBase === "G" || firstBase === "C") {
84434
+ deltaH += SANTA_LUCIA_INIT.GC.dH;
84435
+ deltaS += SANTA_LUCIA_INIT.GC.dS;
84436
+ } else {
84437
+ deltaH += SANTA_LUCIA_INIT.AT.dH;
84438
+ deltaS += SANTA_LUCIA_INIT.AT.dS;
84439
+ }
84440
+ if (lastBase === "G" || lastBase === "C") {
84441
+ deltaH += SANTA_LUCIA_INIT.GC.dH;
84442
+ deltaS += SANTA_LUCIA_INIT.GC.dS;
84443
+ } else {
84444
+ deltaH += SANTA_LUCIA_INIT.AT.dH;
84445
+ deltaS += SANTA_LUCIA_INIT.AT.dS;
84446
+ }
84447
+ const nnPairs = sequence2.length - 1;
84448
+ deltaS = applySaltCorrection(deltaS, nnPairs);
84449
+ const C = PRIMER3_PARAMS.dnaConc * 1e-9;
84450
+ const Tm = deltaH * 1e3 / (deltaS + PRIMER3_PARAMS.R * Math.log(C / 4));
84451
+ return Tm - 273.15;
84452
+ } catch (e) {
84453
+ return `Error calculating Tm for sequence ${sequence2}. ${e}`;
84454
+ }
84455
+ }
84456
+ __name(calculateSantaLuciaTm, "calculateSantaLuciaTm");
84457
+ function calculateEndStability(sequence2) {
84458
+ try {
84459
+ sequence2 = sequence2 == null ? void 0 : sequence2.toUpperCase().trim();
84460
+ if (!isValidSequence(sequence2)) {
84461
+ throw new Error("Invalid sequence: contains non-DNA characters");
84462
+ }
84463
+ if (sequence2.length < 5) {
84464
+ throw new Error(
84465
+ "Sequence too short: minimum length is 5 bases for end stability calculation"
84466
+ );
84467
+ }
84468
+ const last5Bases = sequence2.substring(sequence2.length - 5);
84469
+ let deltaH = 0;
84470
+ let deltaS = 0;
84471
+ for (let i = 0; i < 4; i++) {
84472
+ const dinucleotide = last5Bases.substring(i, i + 2);
84473
+ if (dinucleotide.includes("N")) {
84474
+ continue;
84475
+ }
84476
+ const params = SANTA_LUCIA_NN[dinucleotide];
84477
+ if (params) {
84478
+ deltaH += params.dH;
84479
+ deltaS += params.dS;
84480
+ }
84481
+ }
84482
+ const firstBase = last5Bases[0];
84483
+ const lastBase = last5Bases[last5Bases.length - 1];
84484
+ if (firstBase === "G" || firstBase === "C") {
84485
+ deltaH += SANTA_LUCIA_INIT.GC.dH;
84486
+ deltaS += SANTA_LUCIA_INIT.GC.dS;
84487
+ } else {
84488
+ deltaH += SANTA_LUCIA_INIT.AT.dH;
84489
+ deltaS += SANTA_LUCIA_INIT.AT.dS;
84490
+ }
84491
+ if (lastBase === "G" || lastBase === "C") {
84492
+ deltaH += SANTA_LUCIA_INIT.GC.dH;
84493
+ deltaS += SANTA_LUCIA_INIT.GC.dS;
84494
+ } else {
84495
+ deltaH += SANTA_LUCIA_INIT.AT.dH;
84496
+ deltaS += SANTA_LUCIA_INIT.AT.dS;
84497
+ }
84498
+ const T2 = 310.15;
84499
+ const deltaG = deltaH - T2 * deltaS / 1e3;
84500
+ return Math.round(Math.abs(deltaG) * 100) / 100;
84501
+ } catch (e) {
84502
+ return `Error calculating end stability for sequence ${sequence2}. ${e}`;
84503
+ }
84504
+ }
84505
+ __name(calculateEndStability, "calculateEndStability");
84352
84506
  function convertAACaretPositionOrRangeToDna(rangeOrCaret) {
84353
84507
  if (typeof rangeOrCaret === "object" && rangeOrCaret !== null) {
84354
84508
  return convertAARangeToDnaRange(__spreadProps(__spreadValues({}, rangeOrCaret), {
@@ -116654,7 +116808,7 @@ function showFileDialog({ multiple = false, onSelect }) {
116654
116808
  input.click();
116655
116809
  }
116656
116810
  __name(showFileDialog, "showFileDialog");
116657
- const version = "0.8.18";
116811
+ const version = "0.8.19";
116658
116812
  const packageJson = {
116659
116813
  version
116660
116814
  };
@@ -137354,7 +137508,7 @@ function MeltingTemp({
137354
137508
  /* , setMonovalentCationConc */
137355
137509
  ] = React.useState(0.05);
137356
137510
  const [tmType, setTmType] = useTmType();
137357
- let tm = (tmType === "neb_tm" ? calculateNebTm : calculateTm)(sequence2, {
137511
+ let tm = (tmType === "neb_tm" ? calculateNebTm : tmType === "default" ? calculateSantaLuciaTm : calculateTm)(sequence2, {
137358
137512
  monovalentCationConc,
137359
137513
  primerConc
137360
137514
  });
@@ -137379,8 +137533,9 @@ function MeltingTemp({
137379
137533
  {
137380
137534
  label: "Choose Tm Type:",
137381
137535
  options: [
137382
- { value: "default", label: "Default Tm (Breslauer)" },
137383
- { value: "neb_tm", label: "NEB Tm (SantaLucia)" }
137536
+ { value: "default", label: "Santa Lucia (Default)" },
137537
+ { value: "breslauer", label: "Breslauer" },
137538
+ { value: "neb_tm", label: "NEB Tm" }
137384
137539
  ],
137385
137540
  onChange: /* @__PURE__ */ __name((e) => setTmType(e.target.value), "onChange"),
137386
137541
  selectedValue: tmType
@@ -137394,7 +137549,7 @@ function MeltingTemp({
137394
137549
  }
137395
137550
  ), hasWarning, /* @__PURE__ */ React.createElement("br", null), /* @__PURE__ */ React.createElement("br", null), "Try using the Default Tm"))
137396
137551
  },
137397
- /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(InnerWrapper, null, "Melting Temp: ", Number(tm) || 0, " "), hasWarning && /* @__PURE__ */ React.createElement(
137552
+ /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(InnerWrapper, null, "Melting Temp: ", Number(tm) || 0, "°C"), hasWarning && /* @__PURE__ */ React.createElement(
137398
137553
  core.Icon,
137399
137554
  {
137400
137555
  style: { marginLeft: 5, marginRight: 5 },
@@ -137658,12 +137813,24 @@ const RenderBases = /* @__PURE__ */ __name((props) => {
137658
137813
  )), /* @__PURE__ */ React.createElement(
137659
137814
  MeltingTemp,
137660
137815
  {
137661
- InnerWrapper: InnerWrapperMeltingTemp,
137816
+ InnerWrapper: TextInnerWrapper,
137662
137817
  sequence: bases
137663
137818
  }
137664
- ))
137819
+ ), /* @__PURE__ */ React.createElement(TextInnerWrapper, null, "GC content: ", bases && calculatePercentGC(bases).toFixed(1), "%"), /* @__PURE__ */ React.createElement(TextInnerWrapper, null, "3' Stability: ", bases && calculateEndStability(bases), " kcal/mol"))
137665
137820
  );
137666
137821
  }, "RenderBases");
137822
+ const TextInnerWrapper = /* @__PURE__ */ __name((p2) => /* @__PURE__ */ React.createElement(
137823
+ "div",
137824
+ {
137825
+ className: "bp3-text-muted bp3-text-small",
137826
+ style: {
137827
+ marginBottom: 15,
137828
+ marginTop: -5,
137829
+ fontStyle: "italic"
137830
+ }
137831
+ },
137832
+ p2.children
137833
+ ), "TextInnerWrapper");
137667
137834
  const AddOrEditPrimerDialog = AddOrEditAnnotationDialog$1({
137668
137835
  formName: "AddOrEditPrimerDialog",
137669
137836
  getProps: /* @__PURE__ */ __name((props) => ({
@@ -137672,14 +137839,6 @@ const AddOrEditPrimerDialog = AddOrEditAnnotationDialog$1({
137672
137839
  RenderBases
137673
137840
  }), "getProps")
137674
137841
  });
137675
- const InnerWrapperMeltingTemp = /* @__PURE__ */ __name((p2) => /* @__PURE__ */ React.createElement(
137676
- "div",
137677
- {
137678
- className: "bp3-text-muted bp3-text-small",
137679
- style: { marginBottom: 15, marginTop: -5, fontStyle: "italic" }
137680
- },
137681
- p2.children
137682
- ), "InnerWrapperMeltingTemp");
137683
137842
  const Dialogs = {
137684
137843
  RenameSequenceDialog,
137685
137844
  PrintDialog: PrintDialog$1,
package/index.es.js CHANGED
@@ -84331,6 +84331,160 @@ function calculateNebTm(sequence2, { monovalentCationConc = 0.05, primerConc = 5
84331
84331
  }
84332
84332
  }
84333
84333
  __name(calculateNebTm, "calculateNebTm");
84334
+ const PRIMER3_PARAMS = {
84335
+ saltMonovalent: 50,
84336
+ // mM
84337
+ saltDivalent: 1.5,
84338
+ // mM
84339
+ dntpConc: 0.6,
84340
+ // mM
84341
+ dnaConc: 50,
84342
+ // nM
84343
+ R: 1.987
84344
+ // Gas constant (cal/K·mol)
84345
+ };
84346
+ const SANTA_LUCIA_NN = {
84347
+ AA: { dH: -7.9, dS: -22.2 },
84348
+ TT: { dH: -7.9, dS: -22.2 },
84349
+ AT: { dH: -7.2, dS: -20.4 },
84350
+ TA: { dH: -7.2, dS: -21.3 },
84351
+ CA: { dH: -8.5, dS: -22.7 },
84352
+ TG: { dH: -8.5, dS: -22.7 },
84353
+ GT: { dH: -8.4, dS: -22.4 },
84354
+ AC: { dH: -8.4, dS: -22.4 },
84355
+ CT: { dH: -7.8, dS: -21 },
84356
+ AG: { dH: -7.8, dS: -21 },
84357
+ GA: { dH: -8.2, dS: -22.2 },
84358
+ TC: { dH: -8.2, dS: -22.2 },
84359
+ CG: { dH: -10.6, dS: -27.2 },
84360
+ GC: { dH: -9.8, dS: -24.4 },
84361
+ GG: { dH: -8, dS: -19.9 },
84362
+ CC: { dH: -8, dS: -19.9 }
84363
+ };
84364
+ const SANTA_LUCIA_INIT = {
84365
+ GC: { dH: 0.1, dS: -2.8 },
84366
+ // initiation with terminal GC
84367
+ AT: { dH: 2.3, dS: 4.1 }
84368
+ // initiation with terminal AT
84369
+ };
84370
+ function getEffectiveMonovalentConc() {
84371
+ let effectiveMono = PRIMER3_PARAMS.saltMonovalent;
84372
+ {
84373
+ const freeMg = Math.max(
84374
+ 0,
84375
+ PRIMER3_PARAMS.saltDivalent - PRIMER3_PARAMS.dntpConc
84376
+ );
84377
+ effectiveMono += 120 * Math.sqrt(freeMg);
84378
+ }
84379
+ return effectiveMono;
84380
+ }
84381
+ __name(getEffectiveMonovalentConc, "getEffectiveMonovalentConc");
84382
+ function applySaltCorrection(deltaS, nnPairs) {
84383
+ const effectiveMono = getEffectiveMonovalentConc();
84384
+ return deltaS + 0.368 * nnPairs * Math.log(effectiveMono / 1e3);
84385
+ }
84386
+ __name(applySaltCorrection, "applySaltCorrection");
84387
+ function isValidSequence(sequence2) {
84388
+ return /^[ATGCN]+$/.test(sequence2);
84389
+ }
84390
+ __name(isValidSequence, "isValidSequence");
84391
+ function calculateSantaLuciaTm(sequence2) {
84392
+ try {
84393
+ sequence2 = sequence2 == null ? void 0 : sequence2.toUpperCase().trim();
84394
+ if (!isValidSequence(sequence2)) {
84395
+ throw new Error("Invalid sequence: contains non-DNA characters");
84396
+ }
84397
+ if (sequence2.length < 2) {
84398
+ throw new Error("Sequence too short: minimum length is 2 bases");
84399
+ }
84400
+ let deltaH = 0;
84401
+ let deltaS = 0;
84402
+ for (let i = 0; i < sequence2.length - 1; i++) {
84403
+ const dinucleotide = sequence2.substring(i, i + 2);
84404
+ if (dinucleotide.includes("N")) {
84405
+ continue;
84406
+ }
84407
+ const params = SANTA_LUCIA_NN[dinucleotide];
84408
+ if (params) {
84409
+ deltaH += params.dH;
84410
+ deltaS += params.dS;
84411
+ }
84412
+ }
84413
+ const firstBase = sequence2[0];
84414
+ const lastBase = sequence2[sequence2.length - 1];
84415
+ if (firstBase === "G" || firstBase === "C") {
84416
+ deltaH += SANTA_LUCIA_INIT.GC.dH;
84417
+ deltaS += SANTA_LUCIA_INIT.GC.dS;
84418
+ } else {
84419
+ deltaH += SANTA_LUCIA_INIT.AT.dH;
84420
+ deltaS += SANTA_LUCIA_INIT.AT.dS;
84421
+ }
84422
+ if (lastBase === "G" || lastBase === "C") {
84423
+ deltaH += SANTA_LUCIA_INIT.GC.dH;
84424
+ deltaS += SANTA_LUCIA_INIT.GC.dS;
84425
+ } else {
84426
+ deltaH += SANTA_LUCIA_INIT.AT.dH;
84427
+ deltaS += SANTA_LUCIA_INIT.AT.dS;
84428
+ }
84429
+ const nnPairs = sequence2.length - 1;
84430
+ deltaS = applySaltCorrection(deltaS, nnPairs);
84431
+ const C = PRIMER3_PARAMS.dnaConc * 1e-9;
84432
+ const Tm = deltaH * 1e3 / (deltaS + PRIMER3_PARAMS.R * Math.log(C / 4));
84433
+ return Tm - 273.15;
84434
+ } catch (e) {
84435
+ return `Error calculating Tm for sequence ${sequence2}. ${e}`;
84436
+ }
84437
+ }
84438
+ __name(calculateSantaLuciaTm, "calculateSantaLuciaTm");
84439
+ function calculateEndStability(sequence2) {
84440
+ try {
84441
+ sequence2 = sequence2 == null ? void 0 : sequence2.toUpperCase().trim();
84442
+ if (!isValidSequence(sequence2)) {
84443
+ throw new Error("Invalid sequence: contains non-DNA characters");
84444
+ }
84445
+ if (sequence2.length < 5) {
84446
+ throw new Error(
84447
+ "Sequence too short: minimum length is 5 bases for end stability calculation"
84448
+ );
84449
+ }
84450
+ const last5Bases = sequence2.substring(sequence2.length - 5);
84451
+ let deltaH = 0;
84452
+ let deltaS = 0;
84453
+ for (let i = 0; i < 4; i++) {
84454
+ const dinucleotide = last5Bases.substring(i, i + 2);
84455
+ if (dinucleotide.includes("N")) {
84456
+ continue;
84457
+ }
84458
+ const params = SANTA_LUCIA_NN[dinucleotide];
84459
+ if (params) {
84460
+ deltaH += params.dH;
84461
+ deltaS += params.dS;
84462
+ }
84463
+ }
84464
+ const firstBase = last5Bases[0];
84465
+ const lastBase = last5Bases[last5Bases.length - 1];
84466
+ if (firstBase === "G" || firstBase === "C") {
84467
+ deltaH += SANTA_LUCIA_INIT.GC.dH;
84468
+ deltaS += SANTA_LUCIA_INIT.GC.dS;
84469
+ } else {
84470
+ deltaH += SANTA_LUCIA_INIT.AT.dH;
84471
+ deltaS += SANTA_LUCIA_INIT.AT.dS;
84472
+ }
84473
+ if (lastBase === "G" || lastBase === "C") {
84474
+ deltaH += SANTA_LUCIA_INIT.GC.dH;
84475
+ deltaS += SANTA_LUCIA_INIT.GC.dS;
84476
+ } else {
84477
+ deltaH += SANTA_LUCIA_INIT.AT.dH;
84478
+ deltaS += SANTA_LUCIA_INIT.AT.dS;
84479
+ }
84480
+ const T2 = 310.15;
84481
+ const deltaG = deltaH - T2 * deltaS / 1e3;
84482
+ return Math.round(Math.abs(deltaG) * 100) / 100;
84483
+ } catch (e) {
84484
+ return `Error calculating end stability for sequence ${sequence2}. ${e}`;
84485
+ }
84486
+ }
84487
+ __name(calculateEndStability, "calculateEndStability");
84334
84488
  function convertAACaretPositionOrRangeToDna(rangeOrCaret) {
84335
84489
  if (typeof rangeOrCaret === "object" && rangeOrCaret !== null) {
84336
84490
  return convertAARangeToDnaRange(__spreadProps(__spreadValues({}, rangeOrCaret), {
@@ -116636,7 +116790,7 @@ function showFileDialog({ multiple = false, onSelect }) {
116636
116790
  input.click();
116637
116791
  }
116638
116792
  __name(showFileDialog, "showFileDialog");
116639
- const version = "0.8.18";
116793
+ const version = "0.8.19";
116640
116794
  const packageJson = {
116641
116795
  version
116642
116796
  };
@@ -137336,7 +137490,7 @@ function MeltingTemp({
137336
137490
  /* , setMonovalentCationConc */
137337
137491
  ] = React__default.useState(0.05);
137338
137492
  const [tmType, setTmType] = useTmType();
137339
- let tm = (tmType === "neb_tm" ? calculateNebTm : calculateTm)(sequence2, {
137493
+ let tm = (tmType === "neb_tm" ? calculateNebTm : tmType === "default" ? calculateSantaLuciaTm : calculateTm)(sequence2, {
137340
137494
  monovalentCationConc,
137341
137495
  primerConc
137342
137496
  });
@@ -137361,8 +137515,9 @@ function MeltingTemp({
137361
137515
  {
137362
137516
  label: "Choose Tm Type:",
137363
137517
  options: [
137364
- { value: "default", label: "Default Tm (Breslauer)" },
137365
- { value: "neb_tm", label: "NEB Tm (SantaLucia)" }
137518
+ { value: "default", label: "Santa Lucia (Default)" },
137519
+ { value: "breslauer", label: "Breslauer" },
137520
+ { value: "neb_tm", label: "NEB Tm" }
137366
137521
  ],
137367
137522
  onChange: /* @__PURE__ */ __name((e) => setTmType(e.target.value), "onChange"),
137368
137523
  selectedValue: tmType
@@ -137376,7 +137531,7 @@ function MeltingTemp({
137376
137531
  }
137377
137532
  ), hasWarning, /* @__PURE__ */ React__default.createElement("br", null), /* @__PURE__ */ React__default.createElement("br", null), "Try using the Default Tm"))
137378
137533
  },
137379
- /* @__PURE__ */ React__default.createElement(React__default.Fragment, null, /* @__PURE__ */ React__default.createElement(InnerWrapper, null, "Melting Temp: ", Number(tm) || 0, " "), hasWarning && /* @__PURE__ */ React__default.createElement(
137534
+ /* @__PURE__ */ React__default.createElement(React__default.Fragment, null, /* @__PURE__ */ React__default.createElement(InnerWrapper, null, "Melting Temp: ", Number(tm) || 0, "°C"), hasWarning && /* @__PURE__ */ React__default.createElement(
137380
137535
  Icon,
137381
137536
  {
137382
137537
  style: { marginLeft: 5, marginRight: 5 },
@@ -137640,12 +137795,24 @@ const RenderBases = /* @__PURE__ */ __name((props) => {
137640
137795
  )), /* @__PURE__ */ React__default.createElement(
137641
137796
  MeltingTemp,
137642
137797
  {
137643
- InnerWrapper: InnerWrapperMeltingTemp,
137798
+ InnerWrapper: TextInnerWrapper,
137644
137799
  sequence: bases
137645
137800
  }
137646
- ))
137801
+ ), /* @__PURE__ */ React__default.createElement(TextInnerWrapper, null, "GC content: ", bases && calculatePercentGC(bases).toFixed(1), "%"), /* @__PURE__ */ React__default.createElement(TextInnerWrapper, null, "3' Stability: ", bases && calculateEndStability(bases), " kcal/mol"))
137647
137802
  );
137648
137803
  }, "RenderBases");
137804
+ const TextInnerWrapper = /* @__PURE__ */ __name((p2) => /* @__PURE__ */ React__default.createElement(
137805
+ "div",
137806
+ {
137807
+ className: "bp3-text-muted bp3-text-small",
137808
+ style: {
137809
+ marginBottom: 15,
137810
+ marginTop: -5,
137811
+ fontStyle: "italic"
137812
+ }
137813
+ },
137814
+ p2.children
137815
+ ), "TextInnerWrapper");
137649
137816
  const AddOrEditPrimerDialog = AddOrEditAnnotationDialog$1({
137650
137817
  formName: "AddOrEditPrimerDialog",
137651
137818
  getProps: /* @__PURE__ */ __name((props) => ({
@@ -137654,14 +137821,6 @@ const AddOrEditPrimerDialog = AddOrEditAnnotationDialog$1({
137654
137821
  RenderBases
137655
137822
  }), "getProps")
137656
137823
  });
137657
- const InnerWrapperMeltingTemp = /* @__PURE__ */ __name((p2) => /* @__PURE__ */ React__default.createElement(
137658
- "div",
137659
- {
137660
- className: "bp3-text-muted bp3-text-small",
137661
- style: { marginBottom: 15, marginTop: -5, fontStyle: "italic" }
137662
- },
137663
- p2.children
137664
- ), "InnerWrapperMeltingTemp");
137665
137824
  const Dialogs = {
137666
137825
  RenameSequenceDialog,
137667
137826
  PrintDialog: PrintDialog$1,
package/index.umd.js CHANGED
@@ -113252,6 +113252,160 @@ ${latestSubscriptionCallbackError.current.stack}
113252
113252
  }
113253
113253
  }
113254
113254
  __name(calculateNebTm, "calculateNebTm");
113255
+ const PRIMER3_PARAMS = {
113256
+ saltMonovalent: 50,
113257
+ // mM
113258
+ saltDivalent: 1.5,
113259
+ // mM
113260
+ dntpConc: 0.6,
113261
+ // mM
113262
+ dnaConc: 50,
113263
+ // nM
113264
+ R: 1.987
113265
+ // Gas constant (cal/K·mol)
113266
+ };
113267
+ const SANTA_LUCIA_NN = {
113268
+ AA: { dH: -7.9, dS: -22.2 },
113269
+ TT: { dH: -7.9, dS: -22.2 },
113270
+ AT: { dH: -7.2, dS: -20.4 },
113271
+ TA: { dH: -7.2, dS: -21.3 },
113272
+ CA: { dH: -8.5, dS: -22.7 },
113273
+ TG: { dH: -8.5, dS: -22.7 },
113274
+ GT: { dH: -8.4, dS: -22.4 },
113275
+ AC: { dH: -8.4, dS: -22.4 },
113276
+ CT: { dH: -7.8, dS: -21 },
113277
+ AG: { dH: -7.8, dS: -21 },
113278
+ GA: { dH: -8.2, dS: -22.2 },
113279
+ TC: { dH: -8.2, dS: -22.2 },
113280
+ CG: { dH: -10.6, dS: -27.2 },
113281
+ GC: { dH: -9.8, dS: -24.4 },
113282
+ GG: { dH: -8, dS: -19.9 },
113283
+ CC: { dH: -8, dS: -19.9 }
113284
+ };
113285
+ const SANTA_LUCIA_INIT = {
113286
+ GC: { dH: 0.1, dS: -2.8 },
113287
+ // initiation with terminal GC
113288
+ AT: { dH: 2.3, dS: 4.1 }
113289
+ // initiation with terminal AT
113290
+ };
113291
+ function getEffectiveMonovalentConc() {
113292
+ let effectiveMono = PRIMER3_PARAMS.saltMonovalent;
113293
+ {
113294
+ const freeMg = Math.max(
113295
+ 0,
113296
+ PRIMER3_PARAMS.saltDivalent - PRIMER3_PARAMS.dntpConc
113297
+ );
113298
+ effectiveMono += 120 * Math.sqrt(freeMg);
113299
+ }
113300
+ return effectiveMono;
113301
+ }
113302
+ __name(getEffectiveMonovalentConc, "getEffectiveMonovalentConc");
113303
+ function applySaltCorrection(deltaS, nnPairs) {
113304
+ const effectiveMono = getEffectiveMonovalentConc();
113305
+ return deltaS + 0.368 * nnPairs * Math.log(effectiveMono / 1e3);
113306
+ }
113307
+ __name(applySaltCorrection, "applySaltCorrection");
113308
+ function isValidSequence(sequence2) {
113309
+ return /^[ATGCN]+$/.test(sequence2);
113310
+ }
113311
+ __name(isValidSequence, "isValidSequence");
113312
+ function calculateSantaLuciaTm(sequence2) {
113313
+ try {
113314
+ sequence2 = sequence2 == null ? void 0 : sequence2.toUpperCase().trim();
113315
+ if (!isValidSequence(sequence2)) {
113316
+ throw new Error("Invalid sequence: contains non-DNA characters");
113317
+ }
113318
+ if (sequence2.length < 2) {
113319
+ throw new Error("Sequence too short: minimum length is 2 bases");
113320
+ }
113321
+ let deltaH = 0;
113322
+ let deltaS = 0;
113323
+ for (let i2 = 0; i2 < sequence2.length - 1; i2++) {
113324
+ const dinucleotide = sequence2.substring(i2, i2 + 2);
113325
+ if (dinucleotide.includes("N")) {
113326
+ continue;
113327
+ }
113328
+ const params = SANTA_LUCIA_NN[dinucleotide];
113329
+ if (params) {
113330
+ deltaH += params.dH;
113331
+ deltaS += params.dS;
113332
+ }
113333
+ }
113334
+ const firstBase = sequence2[0];
113335
+ const lastBase = sequence2[sequence2.length - 1];
113336
+ if (firstBase === "G" || firstBase === "C") {
113337
+ deltaH += SANTA_LUCIA_INIT.GC.dH;
113338
+ deltaS += SANTA_LUCIA_INIT.GC.dS;
113339
+ } else {
113340
+ deltaH += SANTA_LUCIA_INIT.AT.dH;
113341
+ deltaS += SANTA_LUCIA_INIT.AT.dS;
113342
+ }
113343
+ if (lastBase === "G" || lastBase === "C") {
113344
+ deltaH += SANTA_LUCIA_INIT.GC.dH;
113345
+ deltaS += SANTA_LUCIA_INIT.GC.dS;
113346
+ } else {
113347
+ deltaH += SANTA_LUCIA_INIT.AT.dH;
113348
+ deltaS += SANTA_LUCIA_INIT.AT.dS;
113349
+ }
113350
+ const nnPairs = sequence2.length - 1;
113351
+ deltaS = applySaltCorrection(deltaS, nnPairs);
113352
+ const C = PRIMER3_PARAMS.dnaConc * 1e-9;
113353
+ const Tm = deltaH * 1e3 / (deltaS + PRIMER3_PARAMS.R * Math.log(C / 4));
113354
+ return Tm - 273.15;
113355
+ } catch (e2) {
113356
+ return `Error calculating Tm for sequence ${sequence2}. ${e2}`;
113357
+ }
113358
+ }
113359
+ __name(calculateSantaLuciaTm, "calculateSantaLuciaTm");
113360
+ function calculateEndStability(sequence2) {
113361
+ try {
113362
+ sequence2 = sequence2 == null ? void 0 : sequence2.toUpperCase().trim();
113363
+ if (!isValidSequence(sequence2)) {
113364
+ throw new Error("Invalid sequence: contains non-DNA characters");
113365
+ }
113366
+ if (sequence2.length < 5) {
113367
+ throw new Error(
113368
+ "Sequence too short: minimum length is 5 bases for end stability calculation"
113369
+ );
113370
+ }
113371
+ const last5Bases = sequence2.substring(sequence2.length - 5);
113372
+ let deltaH = 0;
113373
+ let deltaS = 0;
113374
+ for (let i2 = 0; i2 < 4; i2++) {
113375
+ const dinucleotide = last5Bases.substring(i2, i2 + 2);
113376
+ if (dinucleotide.includes("N")) {
113377
+ continue;
113378
+ }
113379
+ const params = SANTA_LUCIA_NN[dinucleotide];
113380
+ if (params) {
113381
+ deltaH += params.dH;
113382
+ deltaS += params.dS;
113383
+ }
113384
+ }
113385
+ const firstBase = last5Bases[0];
113386
+ const lastBase = last5Bases[last5Bases.length - 1];
113387
+ if (firstBase === "G" || firstBase === "C") {
113388
+ deltaH += SANTA_LUCIA_INIT.GC.dH;
113389
+ deltaS += SANTA_LUCIA_INIT.GC.dS;
113390
+ } else {
113391
+ deltaH += SANTA_LUCIA_INIT.AT.dH;
113392
+ deltaS += SANTA_LUCIA_INIT.AT.dS;
113393
+ }
113394
+ if (lastBase === "G" || lastBase === "C") {
113395
+ deltaH += SANTA_LUCIA_INIT.GC.dH;
113396
+ deltaS += SANTA_LUCIA_INIT.GC.dS;
113397
+ } else {
113398
+ deltaH += SANTA_LUCIA_INIT.AT.dH;
113399
+ deltaS += SANTA_LUCIA_INIT.AT.dS;
113400
+ }
113401
+ const T2 = 310.15;
113402
+ const deltaG = deltaH - T2 * deltaS / 1e3;
113403
+ return Math.round(Math.abs(deltaG) * 100) / 100;
113404
+ } catch (e2) {
113405
+ return `Error calculating end stability for sequence ${sequence2}. ${e2}`;
113406
+ }
113407
+ }
113408
+ __name(calculateEndStability, "calculateEndStability");
113255
113409
  function convertAACaretPositionOrRangeToDna(rangeOrCaret) {
113256
113410
  if (typeof rangeOrCaret === "object" && rangeOrCaret !== null) {
113257
113411
  return convertAARangeToDnaRange(__spreadProps(__spreadValues({}, rangeOrCaret), {
@@ -144752,7 +144906,7 @@ Part of ${annotation.translationType} Translation from BPs ${annotation.start +
144752
144906
  input.click();
144753
144907
  }
144754
144908
  __name(showFileDialog, "showFileDialog");
144755
- const version = "0.8.18";
144909
+ const version = "0.8.19";
144756
144910
  const packageJson = {
144757
144911
  version
144758
144912
  };
@@ -163850,7 +164004,7 @@ Part of ${annotation.translationType} Translation from BPs ${annotation.start +
163850
164004
  /* , setMonovalentCationConc */
163851
164005
  ] = React.useState(0.05);
163852
164006
  const [tmType, setTmType] = useTmType();
163853
- let tm = (tmType === "neb_tm" ? calculateNebTm : calculateTm)(sequence2, {
164007
+ let tm = (tmType === "neb_tm" ? calculateNebTm : tmType === "default" ? calculateSantaLuciaTm : calculateTm)(sequence2, {
163854
164008
  monovalentCationConc,
163855
164009
  primerConc
163856
164010
  });
@@ -163875,8 +164029,9 @@ Part of ${annotation.translationType} Translation from BPs ${annotation.start +
163875
164029
  {
163876
164030
  label: "Choose Tm Type:",
163877
164031
  options: [
163878
- { value: "default", label: "Default Tm (Breslauer)" },
163879
- { value: "neb_tm", label: "NEB Tm (SantaLucia)" }
164032
+ { value: "default", label: "Santa Lucia (Default)" },
164033
+ { value: "breslauer", label: "Breslauer" },
164034
+ { value: "neb_tm", label: "NEB Tm" }
163880
164035
  ],
163881
164036
  onChange: /* @__PURE__ */ __name((e2) => setTmType(e2.target.value), "onChange"),
163882
164037
  selectedValue: tmType
@@ -163890,7 +164045,7 @@ Part of ${annotation.translationType} Translation from BPs ${annotation.start +
163890
164045
  }
163891
164046
  ), hasWarning, /* @__PURE__ */ React.createElement("br", null), /* @__PURE__ */ React.createElement("br", null), "Try using the Default Tm"))
163892
164047
  },
163893
- /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(InnerWrapper, null, "Melting Temp: ", Number(tm) || 0, " "), hasWarning && /* @__PURE__ */ React.createElement(
164048
+ /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(InnerWrapper, null, "Melting Temp: ", Number(tm) || 0, "°C"), hasWarning && /* @__PURE__ */ React.createElement(
163894
164049
  Icon,
163895
164050
  {
163896
164051
  style: { marginLeft: 5, marginRight: 5 },
@@ -164154,12 +164309,24 @@ Part of ${annotation.translationType} Translation from BPs ${annotation.start +
164154
164309
  )), /* @__PURE__ */ React.createElement(
164155
164310
  MeltingTemp,
164156
164311
  {
164157
- InnerWrapper: InnerWrapperMeltingTemp,
164312
+ InnerWrapper: TextInnerWrapper,
164158
164313
  sequence: bases
164159
164314
  }
164160
- ))
164315
+ ), /* @__PURE__ */ React.createElement(TextInnerWrapper, null, "GC content: ", bases && calculatePercentGC(bases).toFixed(1), "%"), /* @__PURE__ */ React.createElement(TextInnerWrapper, null, "3' Stability: ", bases && calculateEndStability(bases), " kcal/mol"))
164161
164316
  );
164162
164317
  }, "RenderBases");
164318
+ const TextInnerWrapper = /* @__PURE__ */ __name((p2) => /* @__PURE__ */ React.createElement(
164319
+ "div",
164320
+ {
164321
+ className: "bp3-text-muted bp3-text-small",
164322
+ style: {
164323
+ marginBottom: 15,
164324
+ marginTop: -5,
164325
+ fontStyle: "italic"
164326
+ }
164327
+ },
164328
+ p2.children
164329
+ ), "TextInnerWrapper");
164163
164330
  const AddOrEditPrimerDialog = AddOrEditAnnotationDialog$1({
164164
164331
  formName: "AddOrEditPrimerDialog",
164165
164332
  getProps: /* @__PURE__ */ __name((props) => ({
@@ -164168,14 +164335,6 @@ Part of ${annotation.translationType} Translation from BPs ${annotation.start +
164168
164335
  RenderBases
164169
164336
  }), "getProps")
164170
164337
  });
164171
- const InnerWrapperMeltingTemp = /* @__PURE__ */ __name((p2) => /* @__PURE__ */ React.createElement(
164172
- "div",
164173
- {
164174
- className: "bp3-text-muted bp3-text-small",
164175
- style: { marginBottom: 15, marginTop: -5, fontStyle: "italic" }
164176
- },
164177
- p2.children
164178
- ), "InnerWrapperMeltingTemp");
164179
164338
  const Dialogs = {
164180
164339
  RenameSequenceDialog,
164181
164340
  PrintDialog: PrintDialog$1,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@teselagen/ove",
3
- "version": "0.8.19",
3
+ "version": "0.8.20",
4
4
  "main": "./src/index.js",
5
5
  "type": "module",
6
6
  "exports": {
@@ -18,7 +18,7 @@
18
18
  "@teselagen/file-utils": "0.3.20",
19
19
  "@teselagen/range-utils": "0.3.13",
20
20
  "@teselagen/react-list": "0.8.18",
21
- "@teselagen/sequence-utils": "0.3.35",
21
+ "@teselagen/sequence-utils": "0.3.36",
22
22
  "@teselagen/ui": "0.10.9",
23
23
  "@use-gesture/react": "10.3.0",
24
24
  "biomsa": "^0.2.4",
@@ -1,7 +1,11 @@
1
1
  import React from "react";
2
2
  import { Button, Icon, Popover, RadioGroup } from "@blueprintjs/core";
3
3
 
4
- import { calculateTm, calculateNebTm } from "@teselagen/sequence-utils";
4
+ import {
5
+ calculateTm,
6
+ calculateNebTm,
7
+ calculateSantaLuciaTm
8
+ } from "@teselagen/sequence-utils";
5
9
 
6
10
  import { isNumber, isString } from "lodash-es";
7
11
  import { popoverOverflowModifiers } from "@teselagen/ui";
@@ -20,7 +24,13 @@ export default function MeltingTemp({
20
24
  const [monovalentCationConc /* , setMonovalentCationConc */] =
21
25
  React.useState(0.05);
22
26
  const [tmType, setTmType] = useTmType();
23
- let tm = (tmType === "neb_tm" ? calculateNebTm : calculateTm)(sequence, {
27
+ let tm = (
28
+ tmType === "neb_tm"
29
+ ? calculateNebTm
30
+ : tmType === "default"
31
+ ? calculateSantaLuciaTm
32
+ : calculateTm
33
+ )(sequence, {
24
34
  monovalentCationConc,
25
35
  primerConc
26
36
  });
@@ -47,8 +57,9 @@ export default function MeltingTemp({
47
57
  <RadioGroup
48
58
  label="Choose Tm Type:"
49
59
  options={[
50
- { value: "default", label: "Default Tm (Breslauer)" },
51
- { value: "neb_tm", label: "NEB Tm (SantaLucia)" }
60
+ { value: "default", label: "Santa Lucia (Default)" },
61
+ { value: "breslauer", label: "Breslauer" },
62
+ { value: "neb_tm", label: "NEB Tm" }
52
63
  ]}
53
64
  onChange={e => setTmType(e.target.value)}
54
65
  selectedValue={tmType}
@@ -70,7 +81,7 @@ export default function MeltingTemp({
70
81
  }
71
82
  >
72
83
  <React.Fragment>
73
- <InnerWrapper>Melting Temp: {Number(tm) || 0} </InnerWrapper>
84
+ <InnerWrapper>Melting Temp: {Number(tm) || 0}°C</InnerWrapper>
74
85
  {hasWarning && (
75
86
  <Icon
76
87
  style={{ marginLeft: 5, marginRight: 5 }}
@@ -8,7 +8,9 @@ import {
8
8
  } from "@teselagen/ui";
9
9
  import {
10
10
  filterSequenceString,
11
- getReverseComplementSequenceString
11
+ getReverseComplementSequenceString,
12
+ calculatePercentGC,
13
+ calculateEndStability
12
14
  } from "@teselagen/sequence-utils";
13
15
 
14
16
  import AddOrEditAnnotationDialog from "../AddOrEditAnnotationDialog";
@@ -294,15 +296,34 @@ const RenderBases = props => {
294
296
  </AdvancedOptions>
295
297
 
296
298
  <MeltingTemp
297
- InnerWrapper={InnerWrapperMeltingTemp}
299
+ InnerWrapper={TextInnerWrapper}
298
300
  sequence={bases}
299
301
  ></MeltingTemp>
302
+ <TextInnerWrapper>
303
+ GC content: {bases && calculatePercentGC(bases).toFixed(1)}%
304
+ </TextInnerWrapper>
305
+ <TextInnerWrapper>
306
+ 3' Stability: {bases && calculateEndStability(bases)} kcal/mol
307
+ </TextInnerWrapper>
300
308
  </div>
301
309
  )}
302
310
  </div>
303
311
  );
304
312
  };
305
313
 
314
+ const TextInnerWrapper = p => (
315
+ <div
316
+ className="bp3-text-muted bp3-text-small"
317
+ style={{
318
+ marginBottom: 15,
319
+ marginTop: -5,
320
+ fontStyle: "italic"
321
+ }}
322
+ >
323
+ {p.children}
324
+ </div>
325
+ );
326
+
306
327
  export default AddOrEditAnnotationDialog({
307
328
  formName: "AddOrEditPrimerDialog",
308
329
  getProps: props => ({
@@ -311,12 +332,3 @@ export default AddOrEditAnnotationDialog({
311
332
  RenderBases
312
333
  })
313
334
  });
314
-
315
- const InnerWrapperMeltingTemp = p => (
316
- <div
317
- className="bp3-text-muted bp3-text-small"
318
- style={{ marginBottom: 15, marginTop: -5, fontStyle: "italic" }}
319
- >
320
- {p.children}
321
- </div>
322
- );