diff --git a/theatre/studio/src/panels/DetailPanel/DetailPanel.tsx b/theatre/studio/src/panels/DetailPanel/DetailPanel.tsx
index 0025fff..f64eddc 100644
--- a/theatre/studio/src/panels/DetailPanel/DetailPanel.tsx
+++ b/theatre/studio/src/panels/DetailPanel/DetailPanel.tsx
@@ -10,7 +10,7 @@ import {
} from '@theatre/studio/panels/BasePanel/common'
import {pointerEventsAutoInNormalMode} from '@theatre/studio/css'
import ObjectDetails from './ObjectDetails'
-import ProjectDetails from './ProjectDetails'
+import ProjectDetails from './ProjectDetails/ProjectDetails'
const Container = styled.div`
background-color: transparent;
diff --git a/theatre/studio/src/panels/DetailPanel/ProjectDetails.tsx b/theatre/studio/src/panels/DetailPanel/ProjectDetails/ProjectDetails.tsx
similarity index 51%
rename from theatre/studio/src/panels/DetailPanel/ProjectDetails.tsx
rename to theatre/studio/src/panels/DetailPanel/ProjectDetails/ProjectDetails.tsx
index 093cada..4746d74 100644
--- a/theatre/studio/src/panels/DetailPanel/ProjectDetails.tsx
+++ b/theatre/studio/src/panels/DetailPanel/ProjectDetails/ProjectDetails.tsx
@@ -1,13 +1,12 @@
import type Project from '@theatre/core/projects/Project'
-import {val} from '@theatre/dataverse'
-import {usePrism} from '@theatre/dataverse-react'
import getStudio from '@theatre/studio/getStudio'
-import {generateDiskStateRevision} from '@theatre/studio/StudioStore/generateDiskStateRevision'
import BasicPopover from '@theatre/studio/uiComponents/Popover/BasicPopover'
import usePopover from '@theatre/studio/uiComponents/Popover/usePopover'
import React, {useCallback, useState} from 'react'
import styled from 'styled-components'
-import {rowBgColor} from './propEditors/utils/SingleRowPropEditor'
+import {rowBgColor} from '@theatre/studio/panels/DetailPanel/propEditors/utils/SingleRowPropEditor'
+import StateConflictRow from './StateConflictRow'
+import DetailPanelButton from '@theatre/studio/uiComponents/DetailPanelButton'
const Container = styled.div`
background-color: ${rowBgColor};
@@ -20,26 +19,6 @@ const TheExportRow = styled.div`
align-items: stretch;
`
-const Button = styled.button<{disabled?: boolean}>`
- text-align: center;
- padding: 8px;
- border-radius: 2px;
- border: 1px solid #627b7b87;
- background-color: #4b787d3d;
- color: #eaeaea;
- font-weight: 400;
- display: block;
- appearance: none;
- flex-grow: 1;
- cursor: ${(props) => (props.disabled ? 'none' : 'pointer')};
- opacity: ${(props) => (props.disabled ? 0.4 : 1)};
-
- &:hover {
- background-color: #7dc1c878;
- border-color: #9ebcbf;
- }
-`
-
const ExportTooltip = styled(BasicPopover)`
width: 280px;
padding: 1em;
@@ -51,51 +30,6 @@ const ProjectDetails: React.FC<{
const project = projects[0]
const projectId = project.address.projectId
- const nn = usePrism(() => {
- const loadingState = val(
- getStudio().atomP.ephemeral.coreByProject[projectId].loadingState,
- )
- if (!loadingState) return
- if (loadingState.type === 'browserStateIsNotBasedOnDiskState') {
- /**
- * This stuff is not undo-safe, but once we switch to the new persistence
- * scheme, these will be unnecessary anyway.
- */
- const useBrowserState = () => {
- getStudio().transaction(({drafts, stateEditors}) => {
- stateEditors.coreByProject.historic.revisionHistory.add({
- projectId,
- revision: loadingState.onDiskState.revisionHistory[0],
- })
-
- stateEditors.coreByProject.historic.revisionHistory.add({
- projectId,
- revision: generateDiskStateRevision(),
- })
-
- drafts.ephemeral.coreByProject[projectId].loadingState = {
- type: 'loaded',
- }
- })
- }
-
- const useOnDiskState = () => {
- getStudio().transaction(({drafts}) => {
- drafts.historic.coreByProject[projectId] = loadingState.onDiskState
- drafts.ephemeral.coreByProject[projectId].loadingState = {
- type: 'loaded',
- }
- })
- }
- return (
-
- Browser state is not based on disk state.
-
-
-
- )
- }
- }, [project])
const [downloaded, setDownloaded] = useState(false)
@@ -140,11 +74,11 @@ const ProjectDetails: React.FC<{
return (
<>
- {nn}
{tooltip}
+
-
+
>
diff --git a/theatre/studio/src/panels/DetailPanel/ProjectDetails/StateConflictRow.tsx b/theatre/studio/src/panels/DetailPanel/ProjectDetails/StateConflictRow.tsx
new file mode 100644
index 0000000..5fcfba5
--- /dev/null
+++ b/theatre/studio/src/panels/DetailPanel/ProjectDetails/StateConflictRow.tsx
@@ -0,0 +1,124 @@
+import {useVal} from '@theatre/dataverse-react'
+import getStudio from '@theatre/studio/getStudio'
+import React from 'react'
+import styled from 'styled-components'
+import {generateDiskStateRevision} from '@theatre/studio/StudioStore/generateDiskStateRevision'
+import type {ProjectEphemeralState} from '@theatre/core/projects/store/storeTypes'
+import useTooltip from '@theatre/studio/uiComponents/Popover/useTooltip'
+import BasicTooltip from '@theatre/studio/uiComponents/Popover/BasicTooltip'
+import type {$FixMe} from '@theatre/shared/utils/types'
+import DetailPanelButton from '@theatre/studio/uiComponents/DetailPanelButton'
+
+const Container = styled.div`
+ padding: 8px 10px;
+ position: relative;
+ background-color: #6d232352;
+ &:before {
+ position: absolute;
+ content: ' ';
+ display: block;
+ left: 0;
+ top: 0;
+ bottom: 0;
+ width: 2px;
+ background-color: #ff000070;
+ }
+`
+
+const Message = styled.div`
+ margin-bottom: 1em;
+`
+
+const ChooseStateRow = styled.div`
+ display: flex;
+ gap: 8px;
+`
+
+const StateConflictRow: React.FC<{projectId: string}> = ({projectId}) => {
+ const loadingState = useVal(
+ getStudio().atomP.ephemeral.coreByProject[projectId].loadingState,
+ )
+
+ if (!loadingState) return null
+
+ if (loadingState.type === 'browserStateIsNotBasedOnDiskState') {
+ return
+ } else {
+ return null
+ }
+}
+
+const InConflict: React.FC<{
+ projectId: string
+ loadingState: Extract<
+ ProjectEphemeralState['loadingState'],
+ {type: 'browserStateIsNotBasedOnDiskState'}
+ >
+}> = ({projectId, loadingState}) => {
+ /**
+ * This stuff is not undo-safe, but once we switch to the new persistence
+ * scheme, these will be unnecessary anyway.
+ */
+ const useBrowserState = () => {
+ getStudio().transaction(({drafts, stateEditors}) => {
+ stateEditors.coreByProject.historic.revisionHistory.add({
+ projectId,
+ revision: loadingState.onDiskState.revisionHistory[0],
+ })
+
+ stateEditors.coreByProject.historic.revisionHistory.add({
+ projectId,
+ revision: generateDiskStateRevision(),
+ })
+
+ drafts.ephemeral.coreByProject[projectId]!.loadingState = {
+ type: 'loaded',
+ }
+ })
+ }
+
+ const useOnDiskState = () => {
+ getStudio().transaction(({drafts}) => {
+ drafts.historic.coreByProject[projectId] = loadingState.onDiskState
+ drafts.ephemeral.coreByProject[projectId]!.loadingState = {
+ type: 'loaded',
+ }
+ })
+ }
+
+ const [browserStateNode, browserStateRef] = useTooltip({}, () => (
+
+ The browser's state will override the disk state.
+
+ ))
+
+ const [diskStateNode, diskStateRef] = useTooltip({}, () => (
+
+ The disk's state will override the browser's state.
+
+ ))
+
+ return (
+
+ Browser state is not based on disk state.
+
+ {browserStateNode}
+
+ Use browser's state
+
+ {diskStateNode}
+
+ Use disk state
+
+
+
+ )
+}
+
+export default StateConflictRow
diff --git a/theatre/studio/src/uiComponents/DetailPanelButton.tsx b/theatre/studio/src/uiComponents/DetailPanelButton.tsx
new file mode 100644
index 0000000..3a1d29c
--- /dev/null
+++ b/theatre/studio/src/uiComponents/DetailPanelButton.tsx
@@ -0,0 +1,23 @@
+import styled from 'styled-components'
+
+const DetailPanelButton = styled.button<{disabled?: boolean}>`
+ text-align: center;
+ padding: 8px;
+ border-radius: 2px;
+ border: 1px solid #627b7b87;
+ background-color: #4b787d3d;
+ color: #eaeaea;
+ font-weight: 400;
+ display: block;
+ appearance: none;
+ flex-grow: 1;
+ cursor: ${(props) => (props.disabled ? 'none' : 'pointer')};
+ opacity: ${(props) => (props.disabled ? 0.4 : 1)};
+
+ &:hover {
+ background-color: #7dc1c878;
+ border-color: #9ebcbf;
+ }
+`
+
+export default DetailPanelButton