@platforma-open/milaboratories.top-antibodies 4.4.2 → 4.5.0

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/dist/index.js CHANGED
@@ -13791,6 +13791,13 @@ function anchorInitializedId(ref) {
13791
13791
  const id = createGlobalPObjectId(ref.blockId, ref.name);
13792
13792
  return isGlobalPObjectId(id) ? id : void 0;
13793
13793
  }
13794
+ /** The input a run, a config or a list is for: the anchor plus its dataset filter, as canonical
13795
+ * column ids (undefined = no anchor). A different filter is a different input, as a different
13796
+ * anchor is. Outputs carry it so the UI can tell those computed for a previous input. */
13797
+ function inputKeyOf(anchor, filter) {
13798
+ if (!anchor) return void 0;
13799
+ return `${anchorInitializedId(anchor) ?? ""}|${filter ? anchorInitializedId(filter) ?? "" : ""}`;
13800
+ }
13794
13801
  /**
13795
13802
  * Reads a stored defaults-init slot in either shape it has had: the
13796
13803
  * `{ anchor, preset }` object, or the older `JSON.stringify(anchor) + "::" +
@@ -13895,12 +13902,43 @@ function isProducedByLeadSelection(spec) {
13895
13902
  const trace = readAnnotationJson(spec, Annotation.Trace);
13896
13903
  return Array.isArray(trace) && trace.length > 0 && trace[trace.length - 1]?.type === LEAD_SELECTION_TRACE_TYPE;
13897
13904
  }
13898
- /** Repertoire-labeling emits one such column per label. `PColumnName` has no entry for it. */
13899
- const LABELING_TAG_COLUMN_NAME = "pl7.app/tag";
13900
- /** `pl7.app/isSubset` marks both a dataset restriction and a presence-only column. A labeling
13901
- * tag is only the second, so the dataset picker must not offer it. */
13902
- function isDatasetScopingSubset(spec) {
13903
- return isPColumnSpec(spec) && spec.name !== LABELING_TAG_COLUMN_NAME;
13905
+ /** The earlier name of {@link SUBSET_DOMAIN}, still carried by columns from runs made before
13906
+ * the rename. */
13907
+ const LEGACY_SUBSET_DOMAIN = "pl7.app/subset";
13908
+ /** The subset a column or axis was computed on, read from its domain under either name
13909
+ * (undefined = full data). */
13910
+ function inputSubsetOf(domain) {
13911
+ return domain?.["pl7.app/inputSubset"] ?? domain?.[LEGACY_SUBSET_DOMAIN];
13912
+ }
13913
+ /**
13914
+ * The columns this block may pick as automatic defaults when it runs on `subsetId` (undefined =
13915
+ * the full dataset). Manual picks are not restricted; only the defaults follow the rule:
13916
+ *
13917
+ * - A column computed on a different subset is never a default: outside that subset it has no
13918
+ * values, so defaulting to it would silently rank or filter out everything else.
13919
+ * - A full-data run takes only full-data columns.
13920
+ * - A subset run prefers the column computed on its own subset, and falls back to the full-data
13921
+ * version of the same column.
13922
+ */
13923
+ function defaultsForSubset(columns, subsetId) {
13924
+ const subsetOf = (c) => inputSubsetOf(c.getSpec().domain);
13925
+ const allowed = columns.filter((c) => {
13926
+ const subset = subsetOf(c);
13927
+ return subset === void 0 || subset === subsetId;
13928
+ });
13929
+ if (subsetId === void 0) return allowed;
13930
+ const sansSubset = (c) => {
13931
+ const spec = c.getSpec();
13932
+ const domain = Object.entries(spec.domain ?? {}).filter(([key]) => key !== "pl7.app/inputSubset" && key !== LEGACY_SUBSET_DOMAIN).sort(([a], [b]) => a.localeCompare(b));
13933
+ return spec.name + JSON.stringify(domain);
13934
+ };
13935
+ const subsetVersions = new Set(allowed.filter((c) => subsetOf(c) === subsetId).map(sansSubset));
13936
+ return allowed.filter((c) => subsetOf(c) !== void 0 || !subsetVersions.has(sansSubset(c)));
13937
+ }
13938
+ /** Which clusterings may be the automatic diversification default on `subsetId`: those on the
13939
+ * run's own subset if any, otherwise the full-data ones. */
13940
+ function defaultClusteringSubset(available, subsetId) {
13941
+ return subsetId !== void 0 && available.includes(subsetId) ? subsetId : void 0;
13904
13942
  }
13905
13943
  /**
13906
13944
  * True when a column carries no readable value. Presence is the whole signal.
@@ -14033,7 +14071,7 @@ const IN_VITRO_RANKING_SPEC_NAMES = /* @__PURE__ */ new Set([
14033
14071
  * Relies on the ambient render ctx (set during output evaluation) for
14034
14072
  * `Column` / `ColumnsCollection` resolution — no `ctx` argument needed.
14035
14073
  */
14036
- function buildCollection(inputAnchor) {
14074
+ function buildCollection(inputAnchor, inputFilter) {
14037
14075
  if (!inputAnchor) return void 0;
14038
14076
  const anchorSpec = getSpecByRef(inputAnchor);
14039
14077
  if (!anchorSpec) return void 0;
@@ -14048,7 +14086,8 @@ function buildCollection(inputAnchor) {
14048
14086
  const spec = c.getSpec();
14049
14087
  return spec.annotations?.[Annotation.IsScore] === "true" && isOnAnchorAxes(spec, anchorSpec);
14050
14088
  });
14051
- const defaultFilters = computeDefaultFilters(scores, inputAnchor);
14089
+ const defaultCandidates = defaultsForSubset(scores, inputFilter && anchorInitializedId(inputFilter));
14090
+ const defaultFilters = computeDefaultFilters(defaultCandidates, inputAnchor);
14052
14091
  return {
14053
14092
  collection,
14054
14093
  anchorSpec,
@@ -14057,7 +14096,7 @@ function buildCollection(inputAnchor) {
14057
14096
  allMatches,
14058
14097
  scores,
14059
14098
  defaultFilters,
14060
- ...computePresets(scores, defaultFilters, inputAnchor, anchorSpec)
14099
+ ...computePresets(defaultCandidates, defaultFilters, inputAnchor, anchorSpec)
14061
14100
  }
14062
14101
  };
14063
14102
  }
@@ -14719,7 +14758,7 @@ const platforma$1 = BlockModelV3.create({
14719
14758
  const rowAxis = spec.axesSpec[1]?.name;
14720
14759
  return rowAxis === "pl7.app/vdj/clonotypeKey" || rowAxis === "pl7.app/vdj/scClonotypeKey" || rowAxis === "pl7.app/variantKey";
14721
14760
  },
14722
- filter: isDatasetScopingSubset
14761
+ filter: isPColumnSpec
14723
14762
  });
14724
14763
  if (!opts) return opts;
14725
14764
  const selfBlockId = ctx.outputs?.resolve({
@@ -14753,13 +14792,12 @@ const platforma$1 = BlockModelV3.create({
14753
14792
  return isPeptideOrAmplicon(spec) ? "peptide" : "antibody_tcr";
14754
14793
  }, { retentive: true }).output("filterConfig", (ctx) => {
14755
14794
  const inputAnchor = getInputAnchorRef(ctx.data);
14756
- const result = buildCollection(inputAnchor);
14795
+ const inputFilter = getInputFilterRef(ctx.data);
14796
+ const result = buildCollection(inputAnchor, inputFilter);
14757
14797
  if (!result) return void 0;
14758
- const filterableMatches = dedupByLeafId(result.collection.discover({
14759
- anchors: { main: result.anchorSpec },
14760
- ...ANCHORED_DISCOVERY,
14761
- exclude: discoveryExcludeSelectors(result.sampleAxisName)
14762
- }).getColumns()).filter(isSelectableMatch);
14798
+ const datasetFilterId = inputFilter && anchorInitializedId(inputFilter);
14799
+ const isDatasetFilter = (c) => datasetFilterId !== void 0 && extractPObjectId(c.id) === datasetFilterId;
14800
+ const filterableMatches = result.meta.allMatches.filter((c) => !isDatasetFilter(c));
14763
14801
  const labels = deriveDistinctLabels(filterableMatches.map((c) => c.getSpec()), { includeNativeLabel: true });
14764
14802
  return {
14765
14803
  options: filterableMatches.map((c, i) => {
@@ -14774,6 +14812,7 @@ const platforma$1 = BlockModelV3.create({
14774
14812
  presenceOnly: isPresenceOnlyColumn(spec, result.anchorSpec)
14775
14813
  };
14776
14814
  }),
14815
+ inputKey: inputKeyOf(inputAnchor, inputFilter),
14777
14816
  defaults: result.meta.defaultFilters,
14778
14817
  inVivoDefaults: result.meta.inVivoDefaults.filters,
14779
14818
  inVitroDefaults: result.meta.inVitroDefaults.filters,
@@ -14781,34 +14820,24 @@ const platforma$1 = BlockModelV3.create({
14781
14820
  };
14782
14821
  }).output("rankingConfig", (ctx) => {
14783
14822
  const inputAnchor = getInputAnchorRef(ctx.data);
14784
- const result = buildCollection(inputAnchor);
14823
+ const inputFilter = getInputFilterRef(ctx.data);
14824
+ const result = buildCollection(inputAnchor, inputFilter);
14785
14825
  if (!result) return void 0;
14786
14826
  const rankedColumnIds = new Set(ctx.data.rankingOrder.flatMap((r) => r.value ? [r.value.column] : []));
14787
- const rankableMatches = dedupByLeafId(result.collection.discover({
14788
- anchors: { main: result.anchorSpec },
14789
- ...ANCHORED_DISCOVERY,
14790
- exclude: [...discoveryExcludeSelectors(result.sampleAxisName), { type: "String" }]
14791
- }).getColumns()).filter((c) => isRankableMatch(c, result.anchorSpec, rankedColumnIds));
14827
+ const rankableMatches = result.meta.allMatches.filter((c) => c.getSpec().valueType !== "String").filter((c) => isRankableMatch(c, result.anchorSpec, rankedColumnIds));
14792
14828
  const labels = deriveDistinctLabels(rankableMatches.map((c) => c.getSpec()), { includeNativeLabel: true });
14793
14829
  return {
14794
14830
  options: rankableMatches.map((c, i) => ({
14795
14831
  label: labels[i],
14796
14832
  value: matchToColumnId(c, inputAnchor)
14797
14833
  })),
14834
+ inputKey: inputKeyOf(inputAnchor, inputFilter),
14798
14835
  defaults: result.meta.defaultRankingOrder,
14799
14836
  inVivoDefaults: result.meta.inVivoDefaults.rankingOrder,
14800
14837
  inVitroDefaults: result.meta.inVitroDefaults.rankingOrder,
14801
14838
  inPeptideDefaults: result.meta.inPeptideDefaults.rankingOrder
14802
14839
  };
14803
- }).output("presetConfig", (ctx) => {
14804
- const result = buildCollection(getInputAnchorRef(ctx.data));
14805
- if (!result) return void 0;
14806
- return {
14807
- detectedPreset: result.meta.detectedPreset,
14808
- hasRepertoireScore: result.meta.hasRepertoireScore,
14809
- hasEnrichmentScores: result.meta.hasEnrichmentScores
14810
- };
14811
- }, { retentive: true }).outputWithStatus("pf", (ctx) => {
14840
+ }).outputWithStatus("pf", (ctx) => {
14812
14841
  const anchor = getInputAnchorRef(ctx.data);
14813
14842
  if (!anchor) return void 0;
14814
14843
  const result = buildCollection(anchor);
@@ -14992,10 +15021,6 @@ const platforma$1 = BlockModelV3.create({
14992
15021
  columnId: c.id,
14993
15022
  spec: c.spec
14994
15023
  }));
14995
- }).output("hasClusterData", (ctx) => {
14996
- const result = buildCollection(getInputAnchorRef(ctx.data));
14997
- if (!result) return false;
14998
- return result.meta.allMatches.some((m) => m.getSpec().axesSpec.some((a) => isClusterIdAxisName(a.name)));
14999
15024
  }).output("clusterColumnOptions", (ctx) => {
15000
15025
  const anchor = getInputAnchorRef(ctx.data);
15001
15026
  if (anchor === void 0) return void 0;
@@ -15003,6 +15028,9 @@ const platforma$1 = BlockModelV3.create({
15003
15028
  if (anchorSpec === void 0) return void 0;
15004
15029
  const datasetClusteringId = anchorSpec.axesSpec[1]?.domain?.["pl7.app/peptide/extractionRunId"] ?? anchorSpec.axesSpec[1]?.domain?.["pl7.app/repertoire/extractionRunId"];
15005
15030
  const options = [];
15031
+ const inputFilter = getInputFilterRef(ctx.data);
15032
+ const subsetId = inputFilter && anchorInitializedId(inputFilter);
15033
+ const optionSubsets = [];
15006
15034
  for (const idx of [0, 1]) {
15007
15035
  let axesToMatch;
15008
15036
  if (idx === 0) axesToMatch = [{}, anchorSpec.axesSpec[1]];
@@ -15022,12 +15050,23 @@ const platforma$1 = BlockModelV3.create({
15022
15050
  const clusteringElement = JSON.parse(linkerSpec.annotations?.[Annotation.Trace] ?? "[]").find((t) => CLUSTERING_TRACE_TYPES.includes(t.type ?? ""));
15023
15051
  if (clusteringElement?.label) label = clusteringElement.label;
15024
15052
  } catch {}
15053
+ const clusterSubset = inputSubsetOf(clusterAxis.domain);
15054
+ if (clusterSubset !== void 0) {
15055
+ const subsetRef = parseJsonSafely(clusterSubset);
15056
+ const subsetLabel = isPlRef(subsetRef) ? getSpecByRef(subsetRef)?.annotations?.[Annotation.Label] : void 0;
15057
+ if (subsetLabel) label = `${label} / ${subsetLabel}`;
15058
+ }
15025
15059
  options.push({
15026
15060
  label,
15027
- ref: link.ref
15061
+ ref: link.ref,
15062
+ isDefault: false,
15063
+ forInput: inputKeyOf(anchor, inputFilter)
15028
15064
  });
15065
+ optionSubsets.push(clusterSubset);
15029
15066
  }
15030
15067
  }
15068
+ const defaultSubset = defaultClusteringSubset(optionSubsets, subsetId);
15069
+ options.forEach((o, i) => o.isDefault = optionSubsets[i] === defaultSubset);
15031
15070
  return options.length > 0 ? options : void 0;
15032
15071
  }).output("kabatWarning", (ctx) => {
15033
15072
  if (!ctx.data.kabatNumbering) return void 0;
@@ -15049,11 +15088,11 @@ const platforma$1 = BlockModelV3.create({
15049
15088
  href: "/",
15050
15089
  label: titles.main
15051
15090
  },
15052
- {
15091
+ ...umapPoolColumns(ctx) !== void 0 ? [{
15053
15092
  type: "link",
15054
15093
  href: "/umap",
15055
15094
  label: spaceLabel
15056
- },
15095
+ }] : [],
15057
15096
  {
15058
15097
  type: "link",
15059
15098
  href: "/selection",