@jbrowse/product-core 4.2.1 → 5.0.0-beta.1

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 (133) hide show
  1. package/esm/RootModel/BaseRootModel.d.ts +629 -304
  2. package/esm/RootModel/BaseRootModel.js +138 -14
  3. package/esm/RootModel/HierarchicalConfig.d.ts +57 -21
  4. package/esm/RootModel/HierarchicalConfig.js +36 -0
  5. package/esm/RootModel/InternetAccounts.d.ts +18 -1
  6. package/esm/RootModel/InternetAccounts.js +120 -22
  7. package/esm/RootModel/PreferencesConfig.d.ts +53 -0
  8. package/esm/RootModel/PreferencesConfig.js +61 -0
  9. package/esm/RootModel/createConfigModel.d.ts +232 -0
  10. package/esm/RootModel/createConfigModel.js +56 -0
  11. package/esm/RootModel/index.d.ts +10 -5
  12. package/esm/RootModel/index.js +10 -5
  13. package/esm/RootModel/menuItems.d.ts +24 -0
  14. package/esm/RootModel/menuItems.js +143 -0
  15. package/esm/Session/BaseSession.d.ts +310 -11
  16. package/esm/Session/BaseSession.js +448 -14
  17. package/esm/Session/Connections.d.ts +114 -8
  18. package/esm/Session/Connections.js +195 -74
  19. package/esm/Session/DrawerWidgets.d.ts +115 -12
  20. package/esm/Session/DrawerWidgets.js +161 -42
  21. package/esm/Session/MultipleViews.d.ts +1266 -1128
  22. package/esm/Session/MultipleViews.js +357 -66
  23. package/esm/Session/Preferences.d.ts +1665 -0
  24. package/esm/Session/Preferences.js +54 -0
  25. package/esm/Session/ReferenceManagement.d.ts +28 -3
  26. package/esm/Session/ReferenceManagement.js +56 -41
  27. package/esm/Session/SessionTracks.d.ts +1275 -1138
  28. package/esm/Session/SessionTracks.js +532 -35
  29. package/esm/Session/Themes.d.ts +87 -2
  30. package/esm/Session/Themes.js +156 -23
  31. package/esm/Session/TrackMenu.d.ts +112 -0
  32. package/esm/Session/TrackMenu.js +200 -0
  33. package/esm/Session/TrackMenuSessionMixin.d.ts +27 -0
  34. package/esm/Session/TrackMenuSessionMixin.js +41 -0
  35. package/esm/Session/Tracks.d.ts +1169 -1117
  36. package/esm/Session/Tracks.js +160 -20
  37. package/esm/Session/assertSessionModel.d.ts +21 -0
  38. package/esm/Session/assertSessionModel.js +1 -0
  39. package/esm/Session/finalizeSession.d.ts +16 -0
  40. package/esm/Session/finalizeSession.js +29 -0
  41. package/esm/Session/index.d.ts +22 -10
  42. package/esm/Session/index.js +13 -10
  43. package/esm/Session/shareableSnapshot.d.ts +77 -0
  44. package/esm/Session/shareableSnapshot.js +196 -0
  45. package/esm/Session/temporaryAssemblyTracks.d.ts +23 -0
  46. package/esm/Session/temporaryAssemblyTracks.js +38 -0
  47. package/esm/assertExtends.d.ts +55 -0
  48. package/esm/assertExtends.js +1 -0
  49. package/esm/controllerTracks.d.ts +56 -0
  50. package/esm/controllerTracks.js +77 -0
  51. package/esm/destroyViewState.d.ts +21 -0
  52. package/esm/destroyViewState.js +29 -0
  53. package/esm/index.d.ts +31 -4
  54. package/esm/index.js +31 -4
  55. package/esm/loadPlugins.d.ts +33 -0
  56. package/esm/loadPlugins.js +38 -0
  57. package/esm/localFiles.d.ts +74 -0
  58. package/esm/localFiles.js +156 -0
  59. package/esm/observeSession.d.ts +56 -0
  60. package/esm/observeSession.js +91 -0
  61. package/esm/pluginInput.d.ts +32 -0
  62. package/esm/pluginInput.js +19 -0
  63. package/esm/pruneUnbuildableNodes.d.ts +109 -0
  64. package/esm/pruneUnbuildableNodes.js +568 -0
  65. package/esm/resolveAssemblies.d.ts +44 -0
  66. package/esm/resolveAssemblies.js +55 -0
  67. package/esm/rpcWorker.d.ts +26 -0
  68. package/esm/rpcWorker.js +96 -15
  69. package/esm/scheduleDetachedDestroy.d.ts +18 -0
  70. package/esm/scheduleDetachedDestroy.js +24 -0
  71. package/esm/sessionMigrations/index.d.ts +53 -0
  72. package/esm/sessionMigrations/index.js +432 -0
  73. package/esm/sessionUrl.d.ts +41 -0
  74. package/esm/sessionUrl.js +53 -0
  75. package/esm/sessionUtils.d.ts +69 -0
  76. package/esm/sessionUtils.js +613 -0
  77. package/esm/siblingCast.d.ts +14 -0
  78. package/esm/siblingCast.js +16 -0
  79. package/esm/snapshotUtils.d.ts +8 -0
  80. package/esm/snapshotUtils.js +12 -0
  81. package/esm/ui/AboutDialog.d.ts +3 -6
  82. package/esm/ui/AboutDialog.js +4 -4
  83. package/esm/ui/AboutDialogContents.d.ts +2 -6
  84. package/esm/ui/AboutDialogContents.js +34 -13
  85. package/esm/ui/AssemblyInfoPanel.d.ts +5 -0
  86. package/esm/ui/AssemblyInfoPanel.js +69 -0
  87. package/esm/ui/DisplayDefaultsSection.d.ts +36 -0
  88. package/esm/ui/DisplayDefaultsSection.js +65 -0
  89. package/esm/ui/Drawer.d.ts +21 -0
  90. package/esm/ui/Drawer.js +60 -0
  91. package/esm/ui/DrawerControls.d.ts +5 -0
  92. package/esm/ui/DrawerControls.js +28 -0
  93. package/esm/ui/DrawerHeader.d.ts +6 -0
  94. package/esm/ui/DrawerHeader.js +36 -0
  95. package/esm/ui/DrawerWidget.d.ts +5 -0
  96. package/esm/ui/DrawerWidget.js +12 -0
  97. package/esm/ui/DrawerWidgetSelector.d.ts +5 -0
  98. package/esm/ui/DrawerWidgetSelector.js +40 -0
  99. package/esm/ui/FileInfoPanel.d.ts +3 -6
  100. package/esm/ui/FileInfoPanel.js +35 -26
  101. package/esm/ui/HeaderButtons.d.ts +7 -1
  102. package/esm/ui/HeaderButtons.js +23 -18
  103. package/esm/ui/ModalWidget.d.ts +12 -0
  104. package/esm/ui/ModalWidget.js +33 -0
  105. package/esm/ui/ModalWidgetAppBar.d.ts +8 -0
  106. package/esm/ui/ModalWidgetAppBar.js +9 -0
  107. package/esm/ui/PreferencesDialog.d.ts +56 -0
  108. package/esm/ui/PreferencesDialog.js +145 -0
  109. package/esm/ui/PreferencesResetDialog.d.ts +8 -0
  110. package/esm/ui/PreferencesResetDialog.js +19 -0
  111. package/esm/ui/RefNameAliasesDialog.d.ts +9 -0
  112. package/esm/ui/RefNameAliasesDialog.js +65 -0
  113. package/esm/ui/RefNameInfoDialog.d.ts +3 -6
  114. package/esm/ui/RefNameInfoDialog.js +57 -63
  115. package/esm/ui/WidgetBody.d.ts +18 -0
  116. package/esm/ui/WidgetBody.js +22 -0
  117. package/esm/ui/WidgetHeading.d.ts +7 -0
  118. package/esm/ui/WidgetHeading.js +7 -0
  119. package/esm/ui/drawerLayout.d.ts +18 -0
  120. package/esm/ui/drawerLayout.js +22 -0
  121. package/esm/ui/index.d.ts +13 -1
  122. package/esm/ui/index.js +12 -1
  123. package/esm/ui/util.d.ts +37 -4
  124. package/esm/ui/util.js +35 -38
  125. package/esm/useEngineLifecycle.d.ts +13 -0
  126. package/esm/useEngineLifecycle.js +22 -0
  127. package/package.json +11 -15
  128. package/esm/RootModel/FormatAbout.d.ts +0 -12
  129. package/esm/RootModel/FormatAbout.js +0 -15
  130. package/esm/RootModel/FormatDetails.d.ts +0 -24
  131. package/esm/RootModel/FormatDetails.js +0 -27
  132. package/esm/Session/DialogQueue.d.ts +0 -17
  133. package/esm/Session/DialogQueue.js +0 -31
@@ -1,18 +1,103 @@
1
1
  import type PluginManager from '@jbrowse/core/PluginManager';
2
+ import type { SerializableThemeArgs, ThemeMap } from '@jbrowse/core/ui';
2
3
  import type { IAnyStateTreeNode, Instance } from '@jbrowse/mobx-state-tree';
3
4
  import type { ThemeOptions } from '@mui/material';
4
- type ThemeMap = Record<string, ThemeOptions>;
5
+ /**
6
+ * #stateModel ThemeManagerSessionMixin
7
+ */
5
8
  export declare function ThemeManagerSessionMixin(_pluginManager: PluginManager): import("@jbrowse/mobx-state-tree").IModelType<{}, {
6
9
  sessionThemeName: string;
7
10
  } & {
11
+ /**
12
+ * #method
13
+ */
8
14
  allThemes(): ThemeMap;
15
+ /**
16
+ * #getter
17
+ */
9
18
  readonly themeName: string;
19
+ /**
20
+ * #getter
21
+ */
22
+ readonly themeOptions: SerializableThemeArgs;
23
+ /**
24
+ * #getter
25
+ * Every color JBrowse renders, resolved to plain strings. This is what
26
+ * rendering reads: it needs no React context, it crosses the RPC worker
27
+ * boundary as itself, and it costs no UI toolkit. Prefer it over
28
+ * `theme` anywhere the answer wanted is a color rather than a Material
29
+ * UI component style.
30
+ */
31
+ readonly palette: import("@jbrowse/core/ui/palette").JBrowsePalette;
32
+ /**
33
+ * #getter
34
+ * The palette plus the sizing tokens `makeStyles` reads — spacing,
35
+ * corner radius, type scale. This is what a product mounts on
36
+ * `StyleThemeProvider`; it costs no UI toolkit, and it is derived from
37
+ * the same `themeOptions` as `theme`, so a config `theme` that sets
38
+ * `spacing` moves JBrowse's own styles and its Material components
39
+ * together.
40
+ */
41
+ readonly styleTheme: import("@jbrowse/core/ui/styleTheme").JBrowseStyleTheme;
42
+ /**
43
+ * #getter
44
+ * The Material UI theme, for the components that are Material UI. Its
45
+ * palette is spliced from the same `resolvePalette` call as `palette`
46
+ * above, so the two cannot disagree.
47
+ */
10
48
  readonly theme: import("@mui/material").Theme;
49
+ /**
50
+ * #method
51
+ * Raw `ThemeOptions` for the active theme, or a named override (used by
52
+ * the SVG-export theme picker). Unlike `theme` (a built,
53
+ * non-serializable MUI theme), this is the plain options object every
54
+ * view's SVG export threads into each display's `renderSvg`, which
55
+ * rebuilds the theme via `createJBrowseTheme` outside React context.
56
+ *
57
+ * The `default` entry is spliced with the config `theme` slot, because
58
+ * the preset is only half of what that entry means — the picker calls it
59
+ * "Default (from config)" and `resolvePalette` merges the two for every
60
+ * other consumer. Returning the bare preset made `view.exportSvg()`
61
+ * silently drop a host's configured palette: a config setting
62
+ * `primary.main` drew `#123456` on screen and exported the stock
63
+ * `#0D233F`, with the export dialog reporting the theme it had not used.
64
+ * Every other named theme is a fixed preset that ignores config, which is
65
+ * the distinction this ternary keeps.
66
+ */
67
+ getActiveThemeOptions(name?: string): (ThemeOptions & {
68
+ name?: string;
69
+ }) | undefined;
11
70
  } & {
71
+ /**
72
+ * #action
73
+ */
12
74
  setThemeName(name: string): void;
75
+ /**
76
+ * #action
77
+ * Point the session at light or dark, for a host that follows its own
78
+ * dark-mode state rather than offering JBrowse's theme menu. Satisfies
79
+ * `ThemeModeSession`, so `useSessionPalette` works against an app
80
+ * session and an embedded one alike.
81
+ *
82
+ * Expressed as a write to the config `theme` slot plus a return to the
83
+ * `default` theme, not as `setThemeName('darkStock')`. Only the
84
+ * `default` theme merges `configTheme.palette` (see `resolvePalette`),
85
+ * so selecting a stock theme would discard whatever the host passed as
86
+ * `configuration.theme` — their brand `primary`, say — the first time
87
+ * their toggle fired. Merging at both levels for the same reason:
88
+ * `theme` is a frozen slot, and `mode` and `primary` are siblings under
89
+ * `palette`.
90
+ *
91
+ * One write, not two: `themeOptions` is derived from the same slot and
92
+ * is what ships to the RPC worker, so the labels baked into a rendered
93
+ * image follow the mode along with what React draws.
94
+ */
95
+ setThemeMode(mode: 'light' | 'dark'): void;
13
96
  afterAttach(): void;
14
97
  }, import("@jbrowse/mobx-state-tree")._NotCustomized, import("@jbrowse/mobx-state-tree")._NotCustomized>;
98
+ /** Session mixin MST type for a session that supports theming */
15
99
  export type SessionWithThemesType = ReturnType<typeof ThemeManagerSessionMixin>;
100
+ /** Instance of a session that has theming support */
16
101
  export type SessionWithThemes = Instance<SessionWithThemesType>;
102
+ /** Type guard for SessionWithThemes */
17
103
  export declare function isSessionWithThemes(session: IAnyStateTreeNode): session is SessionWithThemes;
18
- export {};
@@ -1,43 +1,176 @@
1
- import { getConf } from '@jbrowse/core/configuration';
2
- import { createJBrowseTheme, defaultThemes } from '@jbrowse/core/ui';
1
+ import { getConf, setConf } from '@jbrowse/core/configuration';
2
+ import { createJBrowseThemeFromArgs, defaultThemes } from '@jbrowse/core/ui';
3
+ import { resolvePalette } from '@jbrowse/core/ui/palette';
4
+ import { resolveStyleTheme } from '@jbrowse/core/ui/styleTheme';
3
5
  import { localStorageGetItem, localStorageSetItem } from '@jbrowse/core/util';
4
6
  import { addDisposer, types } from '@jbrowse/mobx-state-tree';
5
7
  import { autorun } from 'mobx';
8
+ import { asSession } from '../siblingCast.js';
9
+ import { isBaseSession } from './BaseSession.js';
10
+ /**
11
+ * What `name` actually resolves to — `default` for one `themes` no longer
12
+ * holds. Every theme name is either stored or handed in from outside, and both
13
+ * go stale the same way: `sessionThemeName` outlives an `extraThemes` entry an
14
+ * admin drops, and so does a name an export dialog persisted or a saved figure
15
+ * spec carries.
16
+ */
17
+ function resolveThemeName(themes, name) {
18
+ return themes[name] ? name : 'default';
19
+ }
20
+ /**
21
+ * #stateModel ThemeManagerSessionMixin
22
+ */
6
23
  export function ThemeManagerSessionMixin(_pluginManager) {
7
24
  return types
8
25
  .model({})
9
26
  .volatile(() => ({
10
- sessionThemeName: localStorageGetItem('themeName') || 'default',
11
- }))
12
- .views(s => ({
13
- allThemes() {
14
- const self = s;
15
- const extraThemes = getConf(self.jbrowse, 'extraThemes');
16
- return { ...defaultThemes, ...extraThemes };
17
- },
18
- get themeName() {
19
- const { sessionThemeName } = s;
20
- const all = this.allThemes();
21
- return all[sessionThemeName] ? sessionThemeName : 'default';
22
- },
23
- get theme() {
24
- const self = s;
25
- const configTheme = getConf(self.jbrowse, 'theme');
26
- const all = this.allThemes();
27
- return createJBrowseTheme(configTheme, all, this.themeName);
28
- },
27
+ sessionThemeName: localStorageGetItem('themeName') ?? 'default',
29
28
  }))
29
+ .views(s => {
30
+ const self = asSession(s);
31
+ return {
32
+ /**
33
+ * #method
34
+ */
35
+ allThemes() {
36
+ const extraThemes = getConf(self.jbrowse, 'extraThemes');
37
+ return { ...defaultThemes, ...extraThemes };
38
+ },
39
+ /**
40
+ * #getter
41
+ */
42
+ get themeName() {
43
+ return resolveThemeName(this.allThemes(), self.sessionThemeName);
44
+ },
45
+ /**
46
+ * #getter
47
+ */
48
+ // Structurally-serializable description of the active theme, safe to
49
+ // send across the RPC worker boundary (the created `theme` carries
50
+ // functions and cannot be cloned). The worker rebuilds via
51
+ // createJBrowseThemeFromArgs.
52
+ get themeOptions() {
53
+ return {
54
+ configTheme: getConf(self.jbrowse, 'theme'),
55
+ extraThemes: getConf(self.jbrowse, 'extraThemes'),
56
+ themeName: this.themeName,
57
+ };
58
+ },
59
+ /**
60
+ * #getter
61
+ * Every color JBrowse renders, resolved to plain strings. This is what
62
+ * rendering reads: it needs no React context, it crosses the RPC worker
63
+ * boundary as itself, and it costs no UI toolkit. Prefer it over
64
+ * `theme` anywhere the answer wanted is a color rather than a Material
65
+ * UI component style.
66
+ */
67
+ get palette() {
68
+ return resolvePalette(this.themeOptions);
69
+ },
70
+ /**
71
+ * #getter
72
+ * The palette plus the sizing tokens `makeStyles` reads — spacing,
73
+ * corner radius, type scale. This is what a product mounts on
74
+ * `StyleThemeProvider`; it costs no UI toolkit, and it is derived from
75
+ * the same `themeOptions` as `theme`, so a config `theme` that sets
76
+ * `spacing` moves JBrowse's own styles and its Material components
77
+ * together.
78
+ */
79
+ get styleTheme() {
80
+ return resolveStyleTheme(this.themeOptions);
81
+ },
82
+ /**
83
+ * #getter
84
+ * The Material UI theme, for the components that are Material UI. Its
85
+ * palette is spliced from the same `resolvePalette` call as `palette`
86
+ * above, so the two cannot disagree.
87
+ */
88
+ get theme() {
89
+ return createJBrowseThemeFromArgs(this.themeOptions);
90
+ },
91
+ /**
92
+ * #method
93
+ * Raw `ThemeOptions` for the active theme, or a named override (used by
94
+ * the SVG-export theme picker). Unlike `theme` (a built,
95
+ * non-serializable MUI theme), this is the plain options object every
96
+ * view's SVG export threads into each display's `renderSvg`, which
97
+ * rebuilds the theme via `createJBrowseTheme` outside React context.
98
+ *
99
+ * The `default` entry is spliced with the config `theme` slot, because
100
+ * the preset is only half of what that entry means — the picker calls it
101
+ * "Default (from config)" and `resolvePalette` merges the two for every
102
+ * other consumer. Returning the bare preset made `view.exportSvg()`
103
+ * silently drop a host's configured palette: a config setting
104
+ * `primary.main` drew `#123456` on screen and exported the stock
105
+ * `#0D233F`, with the export dialog reporting the theme it had not used.
106
+ * Every other named theme is a fixed preset that ignores config, which is
107
+ * the distinction this ternary keeps.
108
+ */
109
+ getActiveThemeOptions(name) {
110
+ const all = this.allThemes();
111
+ const themeName = resolveThemeName(all, name ?? this.themeName);
112
+ const theme = all[themeName];
113
+ if (themeName !== 'default') {
114
+ return theme;
115
+ }
116
+ // shallow over the palette, which is how `resolvePalette` spreads the
117
+ // same two — `mode` and `primary` are siblings there, so both levels
118
+ // have to survive
119
+ const configTheme = getConf(self.jbrowse, 'theme');
120
+ return {
121
+ ...theme,
122
+ ...configTheme,
123
+ palette: { ...theme?.palette, ...configTheme.palette },
124
+ };
125
+ },
126
+ };
127
+ })
30
128
  .actions(self => ({
129
+ /**
130
+ * #action
131
+ */
31
132
  setThemeName(name) {
32
133
  self.sessionThemeName = name;
33
134
  },
135
+ /**
136
+ * #action
137
+ * Point the session at light or dark, for a host that follows its own
138
+ * dark-mode state rather than offering JBrowse's theme menu. Satisfies
139
+ * `ThemeModeSession`, so `useSessionPalette` works against an app
140
+ * session and an embedded one alike.
141
+ *
142
+ * Expressed as a write to the config `theme` slot plus a return to the
143
+ * `default` theme, not as `setThemeName('darkStock')`. Only the
144
+ * `default` theme merges `configTheme.palette` (see `resolvePalette`),
145
+ * so selecting a stock theme would discard whatever the host passed as
146
+ * `configuration.theme` — their brand `primary`, say — the first time
147
+ * their toggle fired. Merging at both levels for the same reason:
148
+ * `theme` is a frozen slot, and `mode` and `primary` are siblings under
149
+ * `palette`.
150
+ *
151
+ * One write, not two: `themeOptions` is derived from the same slot and
152
+ * is what ships to the RPC worker, so the labels baked into a rendered
153
+ * image follow the mode along with what React draws.
154
+ */
155
+ setThemeMode(mode) {
156
+ const { jbrowse } = asSession(self);
157
+ const theme = getConf(jbrowse, 'theme') ?? {};
158
+ setConf(jbrowse, 'theme', {
159
+ ...theme,
160
+ palette: { ...theme.palette, mode },
161
+ });
162
+ self.sessionThemeName = 'default';
163
+ },
34
164
  afterAttach() {
35
165
  addDisposer(self, autorun(function themeNameAutorun() {
36
- localStorageSetItem('themeName', self.themeName);
166
+ // persist the raw selection, not the coerced themeName, so a
167
+ // theme registered later isn't clobbered with 'default'
168
+ localStorageSetItem('themeName', self.sessionThemeName);
37
169
  }, { name: 'ThemeName' }));
38
170
  },
39
171
  }));
40
172
  }
173
+ /** Type guard for SessionWithThemes */
41
174
  export function isSessionWithThemes(session) {
42
- return 'theme' in session;
175
+ return isBaseSession(session) && 'theme' in session;
43
176
  }
@@ -0,0 +1,112 @@
1
+ import type PluginManager from '@jbrowse/core/PluginManager';
2
+ import type { AnyConfigurationModel } from '@jbrowse/core/configuration';
3
+ import type { BaseTrackConfig } from '@jbrowse/core/pluggableElementTypes';
4
+ import type { MenuItem } from '@jbrowse/core/ui';
5
+ import type { DialogComponentType } from '@jbrowse/core/util';
6
+ import type { TrackActionView } from '@jbrowse/core/util/types';
7
+ type TrackCopySnapshot = {
8
+ trackId: string;
9
+ name: string;
10
+ category?: unknown;
11
+ displays?: {
12
+ type: string;
13
+ displayId: string;
14
+ }[];
15
+ } & Record<string, unknown>;
16
+ /**
17
+ * Clone a track config for "Copy track": snapshots the config, appends a unique
18
+ * timestamp suffix to trackId (so it doesn't collide with the original), tags
19
+ * " (copy)" on the name, and regenerates each displayId to the canonical
20
+ * `${trackId}-${type}` form baseTrackConfig auto-injects — keeping them unique
21
+ * (displayId is a types.identifier, so a collision would crash MST).
22
+ */
23
+ export declare function copyTrackSnapshot(config: BaseTrackConfig, opts: {
24
+ clearCategory: boolean;
25
+ }): TrackCopySnapshot;
26
+ interface TrackActionSession<C> {
27
+ editConfiguration: (config: AnyConfigurationModel | {
28
+ trackId: string;
29
+ }, opts?: {
30
+ expandedDisplayId?: string;
31
+ }) => void;
32
+ publishTrackConf: (conf: C) => unknown;
33
+ deleteTrackConf: (conf: AnyConfigurationModel) => void;
34
+ resetTrackConfiguration?: (trackId: string) => void;
35
+ /**
36
+ * Declared here, though only the copy guard below reads it, so a session that
37
+ * loses the list loses the guard *loudly*. `namesTemporaryAssembly` takes an
38
+ * `unknown` session and answers false for one that holds no such list, which
39
+ * is right for a caller that genuinely has none and wrong for one that let it
40
+ * drop — and that second case is a copy action silently offered again.
41
+ *
42
+ * `unknown[]`, the spelling `AbstractSessionModel` already uses, and not the
43
+ * `{ name?: string }[]` the guard narrows to: that one is a weak type, so a
44
+ * real MST array of assembly configs — whose element type is an index
45
+ * signature that declares no `name` — shares no property with it and TS
46
+ * rejects the assignment. The shape belongs in the guard, which is the only
47
+ * thing that reads it.
48
+ */
49
+ temporaryAssemblies?: unknown[];
50
+ }
51
+ /**
52
+ * The shared Settings / Copy / Copy-and-open / Delete track actions. Each
53
+ * product supplies `makeCopy` (its own session-track/category rules) and
54
+ * `canEdit`; reference sequence tracks can't be copied or deleted. When
55
+ * `isSessionOverride` is set the track is a session edit shadowing an
56
+ * admin-owned config track, so the final action resets it instead of deleting.
57
+ */
58
+ export declare function trackActionItems<C extends {
59
+ trackId: string;
60
+ }>({ session, config, view, canEdit, isSessionOverride, makeCopy, }: {
61
+ session: TrackActionSession<C>;
62
+ config: BaseTrackConfig;
63
+ view?: TrackActionView;
64
+ canEdit: boolean;
65
+ isSessionOverride?: boolean;
66
+ makeCopy: () => C;
67
+ }): MenuItem[];
68
+ interface SessionWithDialog {
69
+ queueDialog: (cb: (done: () => void) => [DialogComponentType, Record<string, unknown>]) => void;
70
+ }
71
+ type TrackConfig = AnyConfigurationModel | Record<string, unknown>;
72
+ /** "About track" menu item, shared by every product's track menu */
73
+ export declare function aboutTrackMenuItem(session: SessionWithDialog, config: TrackConfig): MenuItem;
74
+ /**
75
+ * plugin-contributed per-track items (`Core-extraTrackMenuItems`), surfaced in
76
+ * both the hierarchical selector and the in-view label menu so plugins reach
77
+ * every track menu consistently
78
+ */
79
+ export declare function pluginExtraTrackItems(pluginManager: PluginManager, session: SessionWithDialog, config: AnyConfigurationModel, view?: TrackActionView): MenuItem[];
80
+ /**
81
+ * flattened track menu (About + raw actions) for the hierarchical track selector
82
+ */
83
+ export declare function trackListMenuItems(session: SessionWithDialog, config: TrackConfig, actions: MenuItem[]): MenuItem[];
84
+ /**
85
+ * track menu with an "About track" item and a "Track actions" submenu, for the
86
+ * in-view track label menu
87
+ */
88
+ export declare function trackActionMenuItems(session: SessionWithDialog, config: TrackConfig, actions: MenuItem[]): MenuItem[];
89
+ /**
90
+ * #stateModel TrackMenuItemsSessionMixin
91
+ *
92
+ * The two track-menu wrappers (`getTrackListMenuItems` for the hierarchical
93
+ * selector, `getTrackActionMenuItems` for the in-view label menu) shared by the
94
+ * full web and desktop sessions. Both are pure functions of `getTrackActions`,
95
+ * which each session supplies (web gates on edit rights; desktop adds indexing).
96
+ */
97
+ export declare function TrackMenuItemsSessionMixin(pluginManager: PluginManager): import("@jbrowse/mobx-state-tree").IModelType<{}, {
98
+ /**
99
+ * #method
100
+ * flattened menu items for use in hierarchical track selector
101
+ */
102
+ getTrackListMenuItems(config: BaseTrackConfig, view?: TrackActionView): MenuItem[];
103
+ /**
104
+ * #method
105
+ * track menu with About + "Track actions" submenu for the in-view label
106
+ */
107
+ getTrackActionMenuItems({ config, view, }: {
108
+ config: BaseTrackConfig;
109
+ view?: TrackActionView;
110
+ }): MenuItem[];
111
+ }, import("@jbrowse/mobx-state-tree")._NotCustomized, import("@jbrowse/mobx-state-tree")._NotCustomized>;
112
+ export {};
@@ -0,0 +1,200 @@
1
+ import { lazy } from 'react';
2
+ import { InfoIcon } from '@jbrowse/core/ui/Icons';
3
+ import { buildExtraTrackMenuItems } from '@jbrowse/core/ui/buildExtraTrackMenuItems';
4
+ import { namesTemporaryAssembly } from '@jbrowse/core/util';
5
+ import { getSnapshot, isStateTreeNode, types } from '@jbrowse/mobx-state-tree';
6
+ import DeleteIcon from '@mui/icons-material/Delete';
7
+ import CopyIcon from '@mui/icons-material/FileCopy';
8
+ import OpenInNewIcon from '@mui/icons-material/OpenInNew';
9
+ import SettingsIcon from '@mui/icons-material/Settings';
10
+ import SettingsBackupRestoreIcon from '@mui/icons-material/SettingsBackupRestore';
11
+ const AboutDialog = lazy(() => import('../ui/AboutDialog.js'));
12
+ /**
13
+ * Clone a track config for "Copy track": snapshots the config, appends a unique
14
+ * timestamp suffix to trackId (so it doesn't collide with the original), tags
15
+ * " (copy)" on the name, and regenerates each displayId to the canonical
16
+ * `${trackId}-${type}` form baseTrackConfig auto-injects — keeping them unique
17
+ * (displayId is a types.identifier, so a collision would crash MST).
18
+ */
19
+ export function copyTrackSnapshot(config, opts) {
20
+ const snap = structuredClone(isStateTreeNode(config) ? getSnapshot(config) : config);
21
+ snap.trackId += `-${Date.now()}`;
22
+ snap.name += ' (copy)';
23
+ if (opts.clearCategory) {
24
+ snap.category = undefined;
25
+ }
26
+ for (const d of snap.displays ?? []) {
27
+ d.displayId = `${snap.trackId}-${d.type}`;
28
+ }
29
+ return snap;
30
+ }
31
+ /**
32
+ * The shared Settings / Copy / Copy-and-open / Delete track actions. Each
33
+ * product supplies `makeCopy` (its own session-track/category rules) and
34
+ * `canEdit`; reference sequence tracks can't be copied or deleted. When
35
+ * `isSessionOverride` is set the track is a session edit shadowing an
36
+ * admin-owned config track, so the final action resets it instead of deleting.
37
+ */
38
+ export function trackActionItems({ session, config, view, canEdit, isSessionOverride, makeCopy, }) {
39
+ const isRefSeq = config.type === 'ReferenceSequenceTrack';
40
+ // A copy of a track drawn on an assembly this view synthesized is dead on
41
+ // arrival: the assembly goes back when the view closes, and the copy is left
42
+ // naming one that never existed anywhere else. Both destinations were wrong
43
+ // for it — `publishTrackConf` writes an admin's into the config.json every
44
+ // visitor is served, one per click since the copy's stamped trackId defeats
45
+ // the dedupe, and a non-admin's lands as the dead `sessionTracks` entry
46
+ // ADR-084 removed the sweep for. Neither is worth offering, so the item says
47
+ // so instead of producing garbage; the dev-only contract check on the adders
48
+ // is what found this.
49
+ //
50
+ // Greying a row tells nobody anything, and this reason is one nobody guesses:
51
+ // a refseq track explains its own greyed Copy, a synteny band that looks like
52
+ // every other track does not. `disabledHelpText` is what says it.
53
+ const isTemporary = namesTemporaryAssembly(session, config);
54
+ const copyRefusal = isRefSeq
55
+ ? 'A reference sequence track cannot be copied.'
56
+ : isTemporary
57
+ ? 'This track is drawn on an assembly this view synthesized, which goes away with the view — a copy of it would name an assembly nothing can resolve.'
58
+ : undefined;
59
+ // the display active in this view expands in the config editor, so the
60
+ // track's other (incompatible/inactive) displays start collapsed
61
+ const expandedDisplayId = view?.getActiveDisplayId?.(config.trackId);
62
+ return [
63
+ {
64
+ // always available: editing a non-session (admin-owned) track applies
65
+ // in-memory for the current session even when the user can't persist it
66
+ label: 'Settings',
67
+ icon: SettingsIcon,
68
+ onClick: () => {
69
+ session.editConfiguration(config, { expandedDisplayId });
70
+ },
71
+ },
72
+ {
73
+ label: 'Copy track',
74
+ icon: CopyIcon,
75
+ disabled: isRefSeq || isTemporary,
76
+ disabledHelpText: copyRefusal,
77
+ onClick: () => {
78
+ session.publishTrackConf(makeCopy());
79
+ },
80
+ },
81
+ {
82
+ label: 'Copy and open track',
83
+ icon: OpenInNewIcon,
84
+ disabled: isRefSeq || isTemporary || !view,
85
+ disabledHelpText: copyRefusal ?? (view ? undefined : 'No view to open the copy in.'),
86
+ onClick: () => {
87
+ const snap = makeCopy();
88
+ if (session.publishTrackConf(snap)) {
89
+ view.showTrack(snap.trackId);
90
+ }
91
+ },
92
+ },
93
+ isSessionOverride
94
+ ? {
95
+ // a session edit of an admin-owned track: the underlying config track
96
+ // can't be deleted, so offer to discard the edits instead
97
+ label: 'Reset track settings',
98
+ icon: SettingsBackupRestoreIcon,
99
+ onClick: () => {
100
+ session.resetTrackConfiguration?.(config.trackId);
101
+ },
102
+ }
103
+ : {
104
+ label: 'Delete track',
105
+ icon: DeleteIcon,
106
+ disabled: !canEdit || isRefSeq,
107
+ onClick: () => {
108
+ session.deleteTrackConf(config);
109
+ },
110
+ },
111
+ ];
112
+ }
113
+ /** "About track" menu item, shared by every product's track menu */
114
+ export function aboutTrackMenuItem(session, config) {
115
+ return {
116
+ label: 'About track',
117
+ icon: InfoIcon,
118
+ onClick: () => {
119
+ session.queueDialog(handleClose => [
120
+ AboutDialog,
121
+ { config, session, handleClose },
122
+ ]);
123
+ },
124
+ };
125
+ }
126
+ /**
127
+ * plugin-contributed per-track items (`Core-extraTrackMenuItems`), surfaced in
128
+ * both the hierarchical selector and the in-view label menu so plugins reach
129
+ * every track menu consistently
130
+ */
131
+ export function pluginExtraTrackItems(pluginManager, session, config, view) {
132
+ return buildExtraTrackMenuItems(pluginManager, {
133
+ session: session,
134
+ config,
135
+ view,
136
+ });
137
+ }
138
+ /**
139
+ * flattened track menu (About + raw actions) for the hierarchical track selector
140
+ */
141
+ export function trackListMenuItems(session, config, actions) {
142
+ return [aboutTrackMenuItem(session, config), ...actions];
143
+ }
144
+ /**
145
+ * track menu with an "About track" item and a "Track actions" submenu, for the
146
+ * in-view track label menu
147
+ */
148
+ export function trackActionMenuItems(session, config, actions) {
149
+ return [
150
+ {
151
+ ...aboutTrackMenuItem(session, config),
152
+ priority: 1002,
153
+ },
154
+ {
155
+ type: 'subMenu',
156
+ label: 'Track actions',
157
+ priority: 1001,
158
+ subMenu: actions,
159
+ },
160
+ { type: 'divider' },
161
+ ];
162
+ }
163
+ // (TrackMenuSessionMixin — the minimal embedded-view variant — lives in its own
164
+ // file so each documented state model is the sole one in its file, which the
165
+ // autogen docs generator requires.)
166
+ /**
167
+ * #stateModel TrackMenuItemsSessionMixin
168
+ *
169
+ * The two track-menu wrappers (`getTrackListMenuItems` for the hierarchical
170
+ * selector, `getTrackActionMenuItems` for the in-view label menu) shared by the
171
+ * full web and desktop sessions. Both are pure functions of `getTrackActions`,
172
+ * which each session supplies (web gates on edit rights; desktop adds indexing).
173
+ */
174
+ export function TrackMenuItemsSessionMixin(pluginManager) {
175
+ return types.model('TrackMenuItemsSessionMixin', {}).views(s => {
176
+ const self = s;
177
+ return {
178
+ /**
179
+ * #method
180
+ * flattened menu items for use in hierarchical track selector
181
+ */
182
+ getTrackListMenuItems(config, view) {
183
+ return [
184
+ ...trackListMenuItems(self, config, self.getTrackActions(config, view)),
185
+ ...pluginExtraTrackItems(pluginManager, self, config, view),
186
+ ];
187
+ },
188
+ /**
189
+ * #method
190
+ * track menu with About + "Track actions" submenu for the in-view label
191
+ */
192
+ getTrackActionMenuItems({ config, view, }) {
193
+ return trackActionMenuItems(self, config, [
194
+ ...self.getTrackActions(config, view),
195
+ ...pluginExtraTrackItems(pluginManager, self, config, view),
196
+ ]);
197
+ },
198
+ };
199
+ });
200
+ }
@@ -0,0 +1,27 @@
1
+ import type PluginManager from '@jbrowse/core/PluginManager';
2
+ import type { AnyConfigurationModel } from '@jbrowse/core/configuration';
3
+ import type { MenuItem } from '@jbrowse/core/ui';
4
+ import type { TrackActionView } from '@jbrowse/core/util/types';
5
+ /**
6
+ * #stateModel TrackMenuSessionMixin
7
+ *
8
+ * The minimal track menus used by the embedded react views, which have no
9
+ * track-editing actions to offer: just "About track" plus any plugin-contributed
10
+ * items (`Core-extraTrackMenuItems`). Mirrors the shape of the full
11
+ * `TrackMenuItemsSessionMixin` so both menu surfaces stay consistent across
12
+ * products, minus the Settings/Copy/Delete actions.
13
+ */
14
+ export declare function TrackMenuSessionMixin(pluginManager: PluginManager): import("@jbrowse/mobx-state-tree").IModelType<{}, {
15
+ /**
16
+ * #method
17
+ * flattened menu items for use in hierarchical track selector
18
+ */
19
+ getTrackListMenuItems(config: AnyConfigurationModel, view?: TrackActionView): MenuItem[];
20
+ /**
21
+ * #method
22
+ */
23
+ getTrackActionMenuItems({ config, view, }: {
24
+ config: AnyConfigurationModel;
25
+ view?: TrackActionView;
26
+ }): MenuItem[];
27
+ }, import("@jbrowse/mobx-state-tree")._NotCustomized, import("@jbrowse/mobx-state-tree")._NotCustomized>;