loom-browser 0.0.3 → 0.0.5

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.
Files changed (47) hide show
  1. package/README.md +28 -3
  2. package/dist/loom-react.esm.js +13967 -0
  3. package/dist/loom-react.esm.min.js +2 -0
  4. package/dist/loom-react.esm.min.js.map +1 -0
  5. package/dist/loom.esm.js +175 -148
  6. package/dist/loom.esm.min.js +1 -1
  7. package/dist/loom.esm.min.js.map +1 -1
  8. package/dist/loom.js +174 -149
  9. package/dist/loom.min.js +1 -1
  10. package/dist/loom.min.js.map +1 -1
  11. package/dist/tsconfig.src.tsbuildinfo +1 -0
  12. package/dist/types/headlessGenomeBrowser.d.ts +8 -1
  13. package/dist/types/index.d.ts +0 -1
  14. package/dist/types/react/GenomeBrowserContext.d.ts +6 -0
  15. package/dist/types/react/LoomBrowser.d.ts +86 -0
  16. package/dist/types/react/hooks/index.d.ts +3 -0
  17. package/dist/types/react/hooks/useBrowserEvent.d.ts +7 -0
  18. package/dist/types/react/hooks/useGenomeBrowser.d.ts +7 -0
  19. package/dist/types/react/hooks/useLocus.d.ts +11 -0
  20. package/dist/types/react/hooks/useTrackManager.d.ts +11 -0
  21. package/dist/types/react/index.d.ts +9 -0
  22. package/dist/types/react/tracks/BedTrack.d.ts +14 -0
  23. package/dist/types/react/tracks/GeneTrack.d.ts +14 -0
  24. package/dist/types/react/tracks/GtxTrack.d.ts +13 -0
  25. package/dist/types/react/tracks/InteractionTrack.d.ts +12 -0
  26. package/dist/types/react/tracks/RulerTrack.d.ts +6 -0
  27. package/dist/types/react/tracks/SequenceTrack.d.ts +6 -0
  28. package/dist/types/react/tracks/WigTrack.d.ts +12 -0
  29. package/dist/types/react/tracks/index.d.ts +14 -0
  30. package/dist/types/react/ui/ChromosomeSelect.d.ts +7 -0
  31. package/dist/types/react/ui/ExportControls.d.ts +7 -0
  32. package/dist/types/react/ui/LocusInput.d.ts +7 -0
  33. package/dist/types/react/ui/Navbar.d.ts +7 -0
  34. package/dist/types/react/ui/WindowSize.d.ts +7 -0
  35. package/dist/types/react/ui/ZoomControls.d.ts +7 -0
  36. package/dist/types/react/ui/ensureRegistered.d.ts +5 -0
  37. package/dist/types/react/ui/index.d.ts +12 -0
  38. package/dist/types/react/ui/types.d.ts +24 -0
  39. package/dist/types/tracks/annotation/annotationTrackCanvas.d.ts +1 -0
  40. package/dist/types/tracks/baseTrackCanvas.d.ts +2 -0
  41. package/dist/types/tracks/interaction/interactionTrackCanvas.d.ts +1 -0
  42. package/dist/types/tracks/ruler/rulerTrackCanvas.d.ts +1 -0
  43. package/dist/types/tracks/sequence/sequenceTrackCanvas.d.ts +1 -0
  44. package/dist/types/tracks/wig/wigTrackCanvas.d.ts +1 -0
  45. package/dist/types/types.d.ts +2 -0
  46. package/dist/types/ui/components/LoomBrowserShell.d.ts +9 -1
  47. package/package.json +20 -1
package/dist/loom.js CHANGED
@@ -1296,6 +1296,11 @@
1296
1296
  }
1297
1297
  return results;
1298
1298
  }
1299
+ setTheme(theme) {
1300
+ this._config = resolveAnnotationConfig(theme);
1301
+ this.background = theme.palette.background;
1302
+ this.render();
1303
+ }
1299
1304
  serializeConfig(theme) {
1300
1305
  const defaults = resolveAnnotationConfig(theme);
1301
1306
  const overrides = {};
@@ -1617,6 +1622,10 @@
1617
1622
  renderRulerTrack(ctx, this.config, rc);
1618
1623
  }
1619
1624
  }
1625
+ setTheme(theme) {
1626
+ this._config = resolveRulerConfig(theme);
1627
+ this.render();
1628
+ }
1620
1629
  serializeConfig(theme) {
1621
1630
  const defaults = resolveRulerConfig(theme);
1622
1631
  const overrides = {};
@@ -2671,6 +2680,11 @@
2671
2680
  }
2672
2681
  return [];
2673
2682
  }
2683
+ setTheme(theme) {
2684
+ const dataRange = this._config.dataRange;
2685
+ this._config = { ...resolveWigConfig(theme), dataRange };
2686
+ this.render();
2687
+ }
2674
2688
  serializeConfig(theme) {
2675
2689
  const defaults = resolveWigConfig(theme);
2676
2690
  const overrides = {};
@@ -5254,6 +5268,11 @@
5254
5268
  doRender(ctx, _width, _height, rc) {
5255
5269
  renderSequenceTrack(ctx, this._sequence, this._config, rc);
5256
5270
  }
5271
+ setTheme(theme) {
5272
+ this._config = resolveSequenceConfig(theme);
5273
+ this._theme = theme;
5274
+ this.render();
5275
+ }
5257
5276
  serializeConfig(theme) {
5258
5277
  const defaults = resolveSequenceConfig(theme);
5259
5278
  const overrides = {};
@@ -9475,6 +9494,10 @@
9475
9494
  data.push({ name: 'Type', value: feature.type });
9476
9495
  return data;
9477
9496
  }
9497
+ setTheme(theme) {
9498
+ this._config = resolveInteractionConfig(theme);
9499
+ this.render();
9500
+ }
9478
9501
  /** Serialize config for session save/restore, diffed against theme defaults. */
9479
9502
  serializeConfig(theme) {
9480
9503
  const resolved = resolveInteractionConfig(resolveTheme(theme));
@@ -10447,6 +10470,7 @@
10447
10470
  ROIChanged: 'roichanged',
10448
10471
  ROIClick: 'roiclick',
10449
10472
  ROIContextMenu: 'roicontextmenu',
10473
+ ThemeChanged: 'themechange',
10450
10474
  };
10451
10475
  /** Generate a unique track ID: "{type}-{counter}" or "track-{counter}" if type is unknown. */
10452
10476
  let nextTrackId = 0;
@@ -10454,6 +10478,7 @@
10454
10478
  return `${type !== null && type !== void 0 ? type : 'track'}-${nextTrackId++}`;
10455
10479
  }
10456
10480
  class HeadlessGenomeBrowser {
10481
+ get theme() { return this._theme; }
10457
10482
  get locus() { return this._locus; }
10458
10483
  get viewportWidth() { return this._viewportWidth; }
10459
10484
  /** Agent-friendly state projection manager. Lazily created on first access. */
@@ -10479,7 +10504,7 @@
10479
10504
  this.workerProvider = options.workerProvider;
10480
10505
  this.popupProvider = (_g = options.popupProvider) !== null && _g !== void 0 ? _g : undefined;
10481
10506
  this.contextMenuProvider = (_h = options.contextMenuProvider) !== null && _h !== void 0 ? _h : undefined;
10482
- this.theme = resolveTheme(options.theme);
10507
+ this._theme = resolveTheme(options.theme);
10483
10508
  if (options.stateProjection)
10484
10509
  this._state = options.stateProjection;
10485
10510
  }
@@ -11219,6 +11244,14 @@
11219
11244
  }
11220
11245
  return track;
11221
11246
  }
11247
+ /** Apply a new render theme to the browser and all existing tracks. */
11248
+ setTheme(theme) {
11249
+ this._theme = resolveTheme(theme);
11250
+ for (const mt of this.managedTracks) {
11251
+ mt.track.setTheme(this._theme);
11252
+ }
11253
+ this.events.emit(BrowserEvent.ThemeChanged, { theme: this._theme });
11254
+ }
11222
11255
  /** Clean up event listeners, abort in-flight requests, clear tracks and ROIs. */
11223
11256
  dispose() {
11224
11257
  this.events.removeAllListeners();
@@ -12069,6 +12102,13 @@
12069
12102
  this.events.on(BrowserEvent.DataLoaded, ({ track }) => {
12070
12103
  this.updateAxisContent(track);
12071
12104
  });
12105
+ // Repaint axis canvases when theme changes (track canvases update via
12106
+ // track.setTheme(), but axis sidebar is managed here in GenomeBrowser).
12107
+ this.events.on(BrowserEvent.ThemeChanged, () => {
12108
+ for (const mt of this.managedTracks) {
12109
+ this.updateAxisContent(mt.track);
12110
+ }
12111
+ });
12072
12112
  // Re-render ROI overlays when ROIs change or viewport moves
12073
12113
  this.events.on(BrowserEvent.ROIAdded, () => this.renderROIOverlays());
12074
12114
  this.events.on(BrowserEvent.ROIRemoved, () => this.renderROIOverlays());
@@ -12271,9 +12311,10 @@
12271
12311
  const { axisDiv } = entry;
12272
12312
  const rawInfo = (_a = track.getAxisInfo) === null || _a === void 0 ? void 0 : _a.call(track);
12273
12313
  if (!rawInfo) {
12274
- // No axis info — clear content and hide border (e.g., ruler tracks)
12314
+ // No axis info — keep the column for alignment but clear content
12275
12315
  axisDiv.innerHTML = "";
12276
12316
  axisDiv.style.borderRight = "none";
12317
+ axisDiv.style.backgroundColor = this.theme.palette.background;
12277
12318
  entry.axisCanvas = null;
12278
12319
  return;
12279
12320
  }
@@ -13932,6 +13973,102 @@ button svg {
13932
13973
  }
13933
13974
  }
13934
13975
 
13976
+ /**
13977
+ * Built-in RenderTheme presets.
13978
+ *
13979
+ * These are Partial<RenderTheme> objects — pass them to resolveTheme() to get
13980
+ * a fully resolved theme with defaults filled in.
13981
+ *
13982
+ * Usage:
13983
+ * import {resolveTheme} from './renderTheme'
13984
+ * import {modernRenderTheme} from './renderThemePresets'
13985
+ * const theme = resolveTheme(modernRenderTheme)
13986
+ */
13987
+ /** Modern theme — blue/pink palette, rounded exons, system font. */
13988
+ const modernRenderTheme = {
13989
+ palette: {
13990
+ primary: '#4A90D9',
13991
+ secondary: '#D94A7A',
13992
+ accent: '#4A90D9',
13993
+ background: '#ffffff',
13994
+ foreground: '#333333',
13995
+ muted: '#B0B0B0',
13996
+ },
13997
+ fontFamily: 'Inter, system-ui, sans-serif',
13998
+ annotation: {
13999
+ utrColor: '#A8C8E8',
14000
+ altUtrColor: '#E8A8BE',
14001
+ borderColor: 'rgba(0, 0, 0, 0.15)',
14002
+ borderWidth: 0.5,
14003
+ borderRadius: 3,
14004
+ arrowInExonColor: 'rgba(255, 255, 255, 0.7)',
14005
+ labelFont: 'italic 10px Inter, system-ui, sans-serif',
14006
+ featureHeight: 12,
14007
+ expandedRowHeight: 28,
14008
+ margin: 8,
14009
+ },
14010
+ wig: {
14011
+ overflowColor: '#FF6B6B',
14012
+ },
14013
+ sequence: {
14014
+ frameColor1: 'hsl(220, 15%, 90%)',
14015
+ frameColor2: 'hsl(220, 15%, 95%)',
14016
+ startCodonColor: 'hsl(160, 55%, 42%)',
14017
+ stopCodonColor: 'hsl(0, 60%, 55%)',
14018
+ useFillText: true,
14019
+ frameLabelColor: '#444444',
14020
+ codonLabelColor: '#ffffff',
14021
+ frameFont: '600 10px Inter, system-ui, sans-serif',
14022
+ codonBorderRadius: 3,
14023
+ },
14024
+ };
14025
+ /** Dark theme — adapted for dark backgrounds, high contrast. */
14026
+ const darkRenderTheme = {
14027
+ palette: {
14028
+ primary: '#6CB4EE',
14029
+ secondary: '#EE6C9E',
14030
+ accent: '#6a9fd9',
14031
+ background: '#1a1a2e',
14032
+ foreground: '#e0e0e0',
14033
+ muted: '#555555',
14034
+ },
14035
+ nucleotideColors: {
14036
+ A: '#4ade80',
14037
+ C: '#60a5fa',
14038
+ G: '#facc15',
14039
+ T: '#f87171',
14040
+ N: '#666666',
14041
+ },
14042
+ annotation: {
14043
+ utrColor: '#3D6B8E',
14044
+ altUtrColor: '#8E3D5E',
14045
+ borderColor: 'rgba(255, 255, 255, 0.12)',
14046
+ borderWidth: 0.5,
14047
+ borderRadius: 3,
14048
+ arrowInExonColor: 'rgba(0, 0, 0, 0.4)',
14049
+ labelBackground: '#1a1a2e',
14050
+ labelFont: 'italic 10px sans-serif',
14051
+ featureHeight: 12,
14052
+ expandedRowHeight: 28,
14053
+ margin: 8,
14054
+ },
14055
+ wig: {
14056
+ overflowColor: '#FF6B6B',
14057
+ },
14058
+ sequence: {
14059
+ background: '#1a1a2e',
14060
+ frameColor1: 'hsl(230, 15%, 28%)',
14061
+ frameColor2: 'hsl(230, 15%, 22%)',
14062
+ startCodonColor: 'hsl(160, 50%, 35%)',
14063
+ stopCodonColor: 'hsl(0, 55%, 45%)',
14064
+ useFillText: true,
14065
+ frameLabelColor: '#c8c8c8',
14066
+ codonLabelColor: '#ffffff',
14067
+ frameFont: '600 10px sans-serif',
14068
+ codonBorderRadius: 3,
14069
+ },
14070
+ };
14071
+
13935
14072
  /**
13936
14073
  * <loom-browser> — Top-level shell combining navbar + GenomeBrowser.
13937
14074
  *
@@ -14039,6 +14176,19 @@ button svg {
14039
14176
  var _a;
14040
14177
  return (_a = this._browser) === null || _a === void 0 ? void 0 : _a.on(event, handler);
14041
14178
  }
14179
+ /** Switch both the UI shell theme and the render theme in one call. */
14180
+ setTheme(shellTheme) {
14181
+ this.applyTheme(shellTheme);
14182
+ this.setAttribute('theme', shellTheme);
14183
+ if (this._browser) {
14184
+ const shellToRenderTheme = {
14185
+ modern: modernRenderTheme,
14186
+ dark: darkRenderTheme,
14187
+ };
14188
+ const mapped = shellToRenderTheme[shellTheme];
14189
+ this._browser.setTheme(mapped !== null && mapped !== void 0 ? mapped : {});
14190
+ }
14191
+ }
14042
14192
  disconnectedCallback() {
14043
14193
  if (this._browser) {
14044
14194
  this._browser.dispose();
@@ -14055,12 +14205,31 @@ button svg {
14055
14205
  if (!customElements.get('loom-browser')) {
14056
14206
  customElements.define('loom-browser', LoomBrowserShell);
14057
14207
  }
14208
+ const { shellTheme, ruler, genes, sequence, ...browserOptions } = options;
14209
+ // Auto-map shellTheme to matching RenderTheme when theme is not explicitly set
14210
+ if (shellTheme && !browserOptions.theme) {
14211
+ const shellToRenderTheme = {
14212
+ modern: modernRenderTheme,
14213
+ dark: darkRenderTheme,
14214
+ };
14215
+ const mapped = shellToRenderTheme[shellTheme];
14216
+ if (mapped) {
14217
+ browserOptions.theme = mapped;
14218
+ }
14219
+ }
14058
14220
  const shell = document.createElement('loom-browser');
14059
- if (options.shellTheme) {
14060
- shell.setAttribute('theme', options.shellTheme);
14221
+ if (shellTheme) {
14222
+ shell.setAttribute('theme', shellTheme);
14061
14223
  }
14062
14224
  container.appendChild(shell);
14063
- shell.initialize(options);
14225
+ const browser = shell.initialize(browserOptions);
14226
+ // Add default tracks
14227
+ if (ruler !== false)
14228
+ browser.addRuler();
14229
+ if (genes !== false)
14230
+ browser.addGeneTrack({ maxTrackHeight: 150 });
14231
+ if (sequence === true)
14232
+ browser.addSequenceTrack();
14064
14233
  return shell;
14065
14234
  }
14066
14235
 
@@ -14652,148 +14821,6 @@ tr.border-top td {
14652
14821
  customElements.define('loom-browser', LoomBrowserShell);
14653
14822
  }
14654
14823
 
14655
- /**
14656
- * Built-in RenderTheme presets.
14657
- *
14658
- * These are Partial<RenderTheme> objects — pass them to resolveTheme() to get
14659
- * a fully resolved theme with defaults filled in.
14660
- *
14661
- * Usage:
14662
- * import {resolveTheme} from './renderTheme'
14663
- * import {modernRenderTheme} from './renderThemePresets'
14664
- * const theme = resolveTheme(modernRenderTheme)
14665
- */
14666
- /** Modern theme — blue/pink palette, rounded exons, system font. */
14667
- const modernRenderTheme = {
14668
- palette: {
14669
- primary: '#4A90D9',
14670
- secondary: '#D94A7A',
14671
- accent: '#4A90D9',
14672
- background: '#ffffff',
14673
- foreground: '#333333',
14674
- muted: '#B0B0B0',
14675
- },
14676
- fontFamily: 'Inter, system-ui, sans-serif',
14677
- annotation: {
14678
- utrColor: '#A8C8E8',
14679
- altUtrColor: '#E8A8BE',
14680
- borderColor: 'rgba(0, 0, 0, 0.15)',
14681
- borderWidth: 0.5,
14682
- borderRadius: 3,
14683
- arrowInExonColor: 'rgba(255, 255, 255, 0.7)',
14684
- labelFont: 'italic 10px Inter, system-ui, sans-serif',
14685
- featureHeight: 12,
14686
- expandedRowHeight: 28,
14687
- margin: 8,
14688
- },
14689
- wig: {
14690
- overflowColor: '#FF6B6B',
14691
- },
14692
- sequence: {
14693
- frameColor1: 'hsl(220, 15%, 90%)',
14694
- frameColor2: 'hsl(220, 15%, 95%)',
14695
- startCodonColor: 'hsl(160, 55%, 42%)',
14696
- stopCodonColor: 'hsl(0, 60%, 55%)',
14697
- useFillText: true,
14698
- frameLabelColor: '#444444',
14699
- codonLabelColor: '#ffffff',
14700
- frameFont: '600 10px Inter, system-ui, sans-serif',
14701
- codonBorderRadius: 3,
14702
- },
14703
- };
14704
- /** Dark theme — adapted for dark backgrounds, high contrast. */
14705
- const darkRenderTheme = {
14706
- palette: {
14707
- primary: '#6CB4EE',
14708
- secondary: '#EE6C9E',
14709
- accent: '#6a9fd9',
14710
- background: '#1a1a2e',
14711
- foreground: '#e0e0e0',
14712
- muted: '#555555',
14713
- },
14714
- nucleotideColors: {
14715
- A: '#4ade80',
14716
- C: '#60a5fa',
14717
- G: '#facc15',
14718
- T: '#f87171',
14719
- N: '#666666',
14720
- },
14721
- annotation: {
14722
- utrColor: '#3D6B8E',
14723
- altUtrColor: '#8E3D5E',
14724
- borderColor: 'rgba(255, 255, 255, 0.12)',
14725
- borderWidth: 0.5,
14726
- borderRadius: 3,
14727
- arrowInExonColor: 'rgba(0, 0, 0, 0.4)',
14728
- labelBackground: '#1a1a2e',
14729
- labelFont: 'italic 10px sans-serif',
14730
- featureHeight: 12,
14731
- expandedRowHeight: 28,
14732
- margin: 8,
14733
- },
14734
- wig: {
14735
- overflowColor: '#FF6B6B',
14736
- },
14737
- sequence: {
14738
- background: '#1a1a2e',
14739
- frameColor1: 'hsl(230, 15%, 28%)',
14740
- frameColor2: 'hsl(230, 15%, 22%)',
14741
- startCodonColor: 'hsl(160, 50%, 35%)',
14742
- stopCodonColor: 'hsl(0, 55%, 45%)',
14743
- useFillText: true,
14744
- frameLabelColor: '#c8c8c8',
14745
- codonLabelColor: '#ffffff',
14746
- frameFont: '600 10px sans-serif',
14747
- codonBorderRadius: 3,
14748
- },
14749
- };
14750
-
14751
- /**
14752
- * Preset render themes for annotation tracks.
14753
- *
14754
- * Each theme is a Partial<AnnotationRenderConfig> that can be spread over defaults.
14755
- * Usage: { ...defaultAnnotationRenderConfig(), ...modernTheme }
14756
- */
14757
- /** Modern theme — softer colors, rounded exons, strand-based coloring. */
14758
- const modernTheme = {
14759
- color: '#4A90D9',
14760
- altColor: '#D94A7A',
14761
- utrColor: '#A8C8E8',
14762
- altUtrColor: '#E8A8BE',
14763
- borderColor: 'rgba(0, 0, 0, 0.15)',
14764
- borderWidth: 0.5,
14765
- borderRadius: 3,
14766
- intronColor: '#B0B0B0',
14767
- intronLineWidth: 1,
14768
- arrowColor: '#999999',
14769
- arrowInExonColor: 'rgba(255, 255, 255, 0.7)',
14770
- labelColor: '#333333',
14771
- labelFont: 'italic 10px sans-serif',
14772
- featureHeight: 12,
14773
- expandedRowHeight: 28,
14774
- margin: 8,
14775
- };
14776
- /** Dark theme — modern theme adapted for dark backgrounds. */
14777
- const darkTheme = {
14778
- color: '#6CB4EE',
14779
- altColor: '#EE6C9E',
14780
- utrColor: '#3D6B8E',
14781
- altUtrColor: '#8E3D5E',
14782
- borderColor: 'rgba(255, 255, 255, 0.12)',
14783
- borderWidth: 0.5,
14784
- borderRadius: 3,
14785
- intronColor: '#555555',
14786
- intronLineWidth: 1,
14787
- arrowColor: '#666666',
14788
- arrowInExonColor: 'rgba(0, 0, 0, 0.4)',
14789
- labelColor: '#C8C8C8',
14790
- labelBackground: '#1a1a2e',
14791
- labelFont: 'italic 10px sans-serif',
14792
- featureHeight: 12,
14793
- expandedRowHeight: 28,
14794
- margin: 8,
14795
- };
14796
-
14797
14824
  /**
14798
14825
  * Preset render themes for wig tracks.
14799
14826
  *
@@ -15004,7 +15031,6 @@ tr.border-top td {
15004
15031
  exports.createTrackFromSession = createTrackFromSession;
15005
15032
  exports.darkRenderTheme = darkRenderTheme;
15006
15033
  exports.darkSequenceTheme = darkSequenceTheme;
15007
- exports.darkTheme = darkTheme;
15008
15034
  exports.darkWigTheme = darkWigTheme;
15009
15035
  exports.dataSourceCacheKey = dataSourceCacheKey;
15010
15036
  exports.decodeBed = decodeBed;
@@ -15073,7 +15099,6 @@ tr.border-top td {
15073
15099
  exports.matchesSelector = matchesSelector;
15074
15100
  exports.modernRenderTheme = modernRenderTheme;
15075
15101
  exports.modernSequenceTheme = modernSequenceTheme;
15076
- exports.modernTheme = modernTheme;
15077
15102
  exports.modernThemeCSS = modernThemeCSS;
15078
15103
  exports.modernWigTheme = modernWigTheme;
15079
15104
  exports.numericDataMenuItems = numericDataMenuItems;