jbrowse-plugin-protein3d 0.4.12 → 0.4.13

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/version.d.ts CHANGED
@@ -1 +1 @@
1
- export declare const version = "0.4.12";
1
+ export declare const version = "0.4.13";
package/dist/version.js CHANGED
@@ -1 +1 @@
1
- export const version = '0.4.12';
1
+ export const version = '0.4.13';
package/package.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "0.4.12",
2
+ "version": "0.4.13",
3
3
  "name": "jbrowse-plugin-protein3d",
4
4
  "repository": {
5
5
  "type": "git",
@@ -88,7 +88,7 @@
88
88
  "lint": "eslint src --report-unused-disable-directives --max-warnings 0",
89
89
  "pretest": "test -d .test-jbrowse-nightly || jbrowse create .test-jbrowse-nightly --nightly",
90
90
  "preversion": "pnpm lint",
91
- "version": "node scripts/sync-version.mjs && git add src/version.ts distconfig.json",
91
+ "version": "node scripts/sync-version.mjs && git add src/version.ts",
92
92
  "postversion": "git push --follow-tags"
93
93
  }
94
94
  }
@@ -0,0 +1,63 @@
1
+ import React, { useState } from 'react'
2
+
3
+ import {
4
+ Button,
5
+ Checkbox,
6
+ Dialog,
7
+ DialogActions,
8
+ DialogContent,
9
+ DialogTitle,
10
+ FormControlLabel,
11
+ FormGroup,
12
+ Typography,
13
+ } from '@mui/material'
14
+
15
+ import { getLaunchSideBySide, setLaunchSideBySide } from '../utils/sideBySide'
16
+
17
+ // Small, self-contained launch settings (NOT the global preferences dialog):
18
+ // just the options that affect how this protein view opens.
19
+ export default function LaunchSettingsDialog({
20
+ open,
21
+ onClose,
22
+ }: {
23
+ open: boolean
24
+ onClose: () => void
25
+ }) {
26
+ const [sideBySide, setSideBySide] = useState(() => getLaunchSideBySide())
27
+ return (
28
+ <Dialog
29
+ open={open}
30
+ onClose={() => {
31
+ onClose()
32
+ }}
33
+ >
34
+ <DialogTitle>Launch settings</DialogTitle>
35
+ <DialogContent>
36
+ <FormGroup>
37
+ <FormControlLabel
38
+ control={<Checkbox checked={sideBySide} />}
39
+ label="Open protein view side-by-side with the genome view"
40
+ onChange={(_, checked) => {
41
+ setSideBySide(checked)
42
+ setLaunchSideBySide(checked)
43
+ }}
44
+ />
45
+ </FormGroup>
46
+ <Typography variant="body2" color="text.secondary">
47
+ When enabled, launching a protein view places it to the right of the
48
+ connected genome view in a split layout instead of stacking it below.
49
+ </Typography>
50
+ </DialogContent>
51
+ <DialogActions>
52
+ <Button
53
+ variant="contained"
54
+ onClick={() => {
55
+ onClose()
56
+ }}
57
+ >
58
+ Close
59
+ </Button>
60
+ </DialogActions>
61
+ </Dialog>
62
+ )
63
+ }
@@ -2,9 +2,11 @@ import React, { useState } from 'react'
2
2
 
3
3
  import { ErrorMessage } from '@jbrowse/core/ui'
4
4
  import ArrowDropDownIcon from '@mui/icons-material/ArrowDropDown'
5
- import { Button, ButtonGroup, Typography } from '@mui/material'
5
+ import SettingsIcon from '@mui/icons-material/Settings'
6
+ import { Button, ButtonGroup, IconButton, Tooltip, Typography } from '@mui/material'
6
7
 
7
8
  import LaunchOptionsDialog from './LaunchOptionsDialog'
9
+ import LaunchSettingsDialog from './LaunchSettingsDialog'
8
10
  import SequenceMismatchNotice from './SequenceMismatchNotice'
9
11
  import { getLaunchMissingReasons, safeLaunch } from '../utils/launchHelpers'
10
12
  import {
@@ -58,6 +60,7 @@ export default function ProteinViewActions({
58
60
  error,
59
61
  }: ProteinViewActionsProps) {
60
62
  const [dialogOpen, setDialogOpen] = useState(false)
63
+ const [settingsOpen, setSettingsOpen] = useState(false)
61
64
  const [launchError, setLaunchError] = useState<unknown>()
62
65
  // Disable launch while loading — SWR's keepPreviousData would otherwise let
63
66
  // a user click Launch on stale results (wrong UniProt ID) during a refetch.
@@ -155,6 +158,17 @@ export default function ProteinViewActions({
155
158
  onAlignmentAlgorithmChange={onAlignmentAlgorithmChange}
156
159
  />
157
160
  ) : null}
161
+ <Tooltip title="Launch settings">
162
+ <IconButton
163
+ size="small"
164
+ aria-label="Launch settings"
165
+ onClick={() => {
166
+ setSettingsOpen(true)
167
+ }}
168
+ >
169
+ <SettingsIcon fontSize="small" />
170
+ </IconButton>
171
+ </Tooltip>
158
172
  <Button
159
173
  variant="contained"
160
174
  color="secondary"
@@ -189,6 +203,12 @@ export default function ProteinViewActions({
189
203
  onClose={closeMenu}
190
204
  options={launchOptions}
191
205
  />
206
+ <LaunchSettingsDialog
207
+ open={settingsOpen}
208
+ onClose={() => {
209
+ setSettingsOpen(false)
210
+ }}
211
+ />
192
212
  </>
193
213
  )
194
214
  }
@@ -6,6 +6,7 @@ declare global {
6
6
  }
7
7
  }
8
8
 
9
+ import { getLaunchSideBySide, launchViewSideBySide } from './sideBySide'
9
10
  import { getGeneDisplayName, getTranscriptDisplayName } from './util'
10
11
  import { launchProteinAnnotationView } from '../components/launchProteinAnnotationView'
11
12
 
@@ -113,6 +114,7 @@ export function launch3DProteinView({
113
114
  alignmentAlgorithm,
114
115
  displayName,
115
116
  connectedMsaViewId,
117
+ sideBySide,
116
118
  }: LaunchViewParams & {
117
119
  url?: string
118
120
  data?: string
@@ -120,6 +122,9 @@ export function launch3DProteinView({
120
122
  alignmentAlgorithm?: string
121
123
  displayName?: string
122
124
  connectedMsaViewId?: string
125
+ // explicit override; when undefined the launch-dialog localStorage preference
126
+ // decides (left genome | right protein)
127
+ sideBySide?: boolean
123
128
  }) {
124
129
  const snap = {
125
130
  type: 'ProteinView',
@@ -138,7 +143,11 @@ export function launch3DProteinView({
138
143
  displayName ??
139
144
  formatViewName('Protein view', feature, selectedTranscript, uniprotId),
140
145
  }
141
- return session.addView('ProteinView', snap)
146
+ const proteinView = session.addView('ProteinView', snap)
147
+ if (sideBySide ?? getLaunchSideBySide()) {
148
+ launchViewSideBySide(session, proteinView.id)
149
+ }
150
+ return proteinView
142
151
  }
143
152
 
144
153
  export async function launch1DProteinView({
@@ -0,0 +1,55 @@
1
+ import type { AbstractSessionModel } from '@jbrowse/core/util'
2
+
3
+ // Self-contained launch preference (NOT the global/core preferences system):
4
+ // whether a protein view launched from a genome feature opens side-by-side with
5
+ // its connected genome view (left genome | right protein) instead of stacked.
6
+ const SIDE_BY_SIDE_KEY = 'proteinView-launchSideBySide'
7
+
8
+ // Default to side-by-side: a connected genome+protein pair reads best as a
9
+ // left/right split. Users can turn it off in the launch dialog's settings.
10
+ const DEFAULT_SIDE_BY_SIDE = true
11
+
12
+ export function getLaunchSideBySide() {
13
+ const stored = localStorage.getItem(SIDE_BY_SIDE_KEY)
14
+ return stored === null ? DEFAULT_SIDE_BY_SIDE : stored === 'true'
15
+ }
16
+
17
+ export function setLaunchSideBySide(value: boolean) {
18
+ localStorage.setItem(SIDE_BY_SIDE_KEY, value ? 'true' : 'false')
19
+ }
20
+
21
+ // The workspaces split is driven by two session actions that only exist on the
22
+ // web/desktop session (MultipleViews + DockviewLayout mixins). Embedded sessions
23
+ // lack them, so feature-detect before using.
24
+ interface SessionWithWorkspaces {
25
+ setUseWorkspaces: (useWorkspaces: boolean) => void
26
+ setPendingMove: (move: { type: 'splitRight'; viewId: string }) => void
27
+ }
28
+
29
+ function isSessionWithWorkspaces(
30
+ session: AbstractSessionModel,
31
+ ): session is AbstractSessionModel & SessionWithWorkspaces {
32
+ return (
33
+ 'setUseWorkspaces' in session &&
34
+ typeof session.setUseWorkspaces === 'function' &&
35
+ 'setPendingMove' in session &&
36
+ typeof session.setPendingMove === 'function'
37
+ )
38
+ }
39
+
40
+ /**
41
+ * Place a freshly-added view to the right of the others in a workspaces (tiled)
42
+ * layout. Mirrors the "Move to split view" view-menu action: queue a splitRight
43
+ * pending move for this view, then enable workspaces so TiledViewsContainer
44
+ * consumes the move on mount (other views land in the left panel, this one in a
45
+ * new right panel). No-op on sessions without workspaces support.
46
+ */
47
+ export function launchViewSideBySide(
48
+ session: AbstractSessionModel,
49
+ viewId: string,
50
+ ) {
51
+ if (isSessionWithWorkspaces(session)) {
52
+ session.setPendingMove({ type: 'splitRight', viewId })
53
+ session.setUseWorkspaces(true)
54
+ }
55
+ }
@@ -1,4 +1,8 @@
1
1
  import { type ConnectedViewSpec, resolveShortLaunch } from './resolveShortLaunch'
2
+ import {
3
+ getLaunchSideBySide,
4
+ launchViewSideBySide,
5
+ } from '../LaunchProteinView/utils/sideBySide'
2
6
 
3
7
  import type PluginManager from '@jbrowse/core/PluginManager'
4
8
  import type { AbstractSessionModel } from '@jbrowse/core/util'
@@ -28,6 +32,7 @@ export default function LaunchProteinViewExtensionPointF(
28
32
  showControls,
29
33
  showHighlight,
30
34
  zoomToBaseLevel,
35
+ sideBySide,
31
36
  }: {
32
37
  session: AbstractSessionModel
33
38
  url?: string
@@ -43,6 +48,10 @@ export default function LaunchProteinViewExtensionPointF(
43
48
  showControls?: boolean
44
49
  showHighlight?: boolean
45
50
  zoomToBaseLevel?: boolean
51
+ // when this launch creates its own connected genome view, place the
52
+ // protein view side-by-side (left genome | right protein). Explicit
53
+ // override; falls back to the launch-dialog localStorage preference.
54
+ sideBySide?: boolean
46
55
  }) => {
47
56
  // Short-URL form: `uniprotId` + `transcriptId` + `connectedView` (no
48
57
  // explicit `url`/`feature`/sequence). Derive the structure URL, the
@@ -77,6 +86,9 @@ export default function LaunchProteinViewExtensionPointF(
77
86
  // `connectedView` is supplied we create the LinearGenomeView here and wire
78
87
  // its id, letting a single spec entry produce a connected genome+protein
79
88
  // pair (e.g. hover a variant to highlight the residue).
89
+ // a connected view this launch created itself can be split beside the
90
+ // protein view; a pre-existing connectedViewId is left in place
91
+ const ownsConnectedView = !connectedViewId && !!connectedView
80
92
  const resolvedConnectedViewId =
81
93
  connectedViewId ??
82
94
  (connectedView
@@ -86,7 +98,7 @@ export default function LaunchProteinViewExtensionPointF(
86
98
  }).id
87
99
  : undefined)
88
100
 
89
- session.addView('ProteinView', {
101
+ const proteinView = session.addView('ProteinView', {
90
102
  type: 'ProteinView',
91
103
  alignmentAlgorithm,
92
104
  displayName,
@@ -106,6 +118,10 @@ export default function LaunchProteinViewExtensionPointF(
106
118
  },
107
119
  ],
108
120
  })
121
+
122
+ if (ownsConnectedView && (sideBySide ?? getLaunchSideBySide())) {
123
+ launchViewSideBySide(session, proteinView.id)
124
+ }
109
125
  },
110
126
  )
111
127
  }
package/src/version.ts CHANGED
@@ -1 +1 @@
1
- export const version = '0.4.12'
1
+ export const version = '0.4.13'