summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas F. K. Jorna <[email protected]>2021-10-01 19:11:08 +0200
committerThomas F. K. Jorna <[email protected]>2021-10-01 19:11:08 +0200
commit56eeb1fc6b03c4e8ed9405e301c7edc7be5fd5d9 (patch)
tree0c7e889f2d749abad3d65a16d4ee0449002c4d89
parent56e81e56ad181129c9f36b75e5ca076162b7cac7 (diff)
feat: basic preview feature
-rw-r--r--components/Sidebar/index.tsx273
-rw-r--r--components/contextmenu.tsx22
-rw-r--r--pages/index.tsx137
-rw-r--r--pages/uniorg.tsx3
-rw-r--r--util/uniorg.tsx8
-rw-r--r--util/webSocketFunctions.ts25
6 files changed, 384 insertions, 84 deletions
diff --git a/components/Sidebar/index.tsx b/components/Sidebar/index.tsx
new file mode 100644
index 0000000..64436f2
--- /dev/null
+++ b/components/Sidebar/index.tsx
@@ -0,0 +1,273 @@
+import React, { useContext, useEffect, useState } from 'react'
+
+import { UniOrg } from "../../util/uniorg"
+import { getOrgText } from "../../util/webSocketFunctions"
+
+import {
+ Button,
+ Slide,
+ VStack,
+ Flex,
+ Heading,
+ Box,
+ CloseButton,
+ Text,
+ Drawer,
+ DrawerOverlay,
+ DrawerHeader,
+ DrawerBody,
+ DrawerCloseButton,
+ DrawerContent,
+ DrawerFooter,
+ IconButton,
+} from '@chakra-ui/react'
+import { Scrollbars } from 'react-custom-scrollbars-2'
+import { ChevronLeftIcon, ChevronRightIcon } from '@chakra-ui/icons'
+
+import { GraphData, NodeObject, LinkObject } from 'force-graph'
+import { OrgRoamNode } from '../../api'
+import {ThemeContext} from '../../util/themecontext'
+
+export interface SidebarProps {
+ isOpen: boolean
+ onClose: any
+ openNode: string
+ nodeById: any
+ previewNode: NodeObject,
+ orgText: string
+}
+
+const Sidebar = (props: SidebarProps) => {
+
+ const { isOpen, onClose, openNode, nodeById, previewNode, orgText } = props
+
+ const {highlightColor}= useContext(ThemeContext)
+
+ useEffect(() => {
+ (async () => {
+ if (!openNode) {
+ return
+ }
+ if (openNode === 'nil') {
+ return
+ }
+ })()
+
+ const previewRoamNode = previewNode as OrgRoamNode
+ }, [previewNode])
+
+ //maybe want to close it when clicking outside, but not sure
+ //const outsideClickRef = useRef();
+ return (
+ <Slide direction="right" in={isOpen} style={{ zIndex: 200, width: 500 }} unmountOnExit>
+ <Flex flexDirection="row" height="100%">
+ <IconButton
+ icon={<ChevronRightIcon height={30} />}
+ colorScheme="white"
+ aria-label="Close file-viewer"
+ height={100}
+ variant="ghost"
+ marginRight={-2}
+ bg="alt.100"
+ onClick={onClose}
+ marginTop={20}
+ />
+ <Box
+ color="gray.800"
+ bg="alt.100"
+ boxShadow="xl"
+ w={500}
+ height="98%"
+ position="relative"
+ zIndex="overlay"
+ marginTop={10}
+ paddingBottom={15}
+ borderRadius="xl"
+ right={-2}
+ >
+ <Flex
+ justifyContent="space-between"
+ padding={4}
+ paddingTop={10}
+ paddingLeft={10}
+ width="80%"
+ alignItems="center"
+ color="black"
+ >
+ <Heading size="md">{previewNode.title}</Heading>
+ </Flex>
+ <Scrollbars
+ //autoHeight
+ //autoHeightMax={600}
+ autoHide
+ renderThumbVertical={({ style, ...props }) => (
+ <Box
+ {...props}
+ style={{
+ ...style,
+ borderRadius: 10,
+ }}
+ bg={highlightColor}
+ />
+ )}
+ >
+ <VStack alignItems="left" bg="alt.100" paddingLeft={10} paddingRight={10}>
+ <Box
+ className="org"
+ sx={{
+ h1: { display: 'none' },
+ h2: {
+ fontSize: '20',
+ fontWeight: 'bold !important',
+ marginBottom: '1em',
+ color: 'black',
+ },
+ h3: {
+ fontSize: '18',
+ fontWeight: '600 !important',
+ marginBottom: '.5em',
+ },
+ h4: {
+ fontSize: '16',
+ fontWeight: '500 !important',
+ marginBottom: '.25em',
+ fontStyle: 'italic',
+ },
+ a: {
+ color: highlightColor,
+ pointerEvents: 'none',
+ },
+ ol: {
+ paddingLeft: '5',
+ },
+ ul: {
+ paddingLeft: '5',
+ },
+ p: {
+ paddingBottom: '.5em',
+ },
+ ".katex-html": {visibility: 'hidden',
+ width: '0px', position: 'absolute'},
+ '#content': { textAlign: 'justify' },
+ '.title': {
+ textAlign: 'center',
+ marginBottom: '.2em',
+ },
+ '.subtitle': {
+ textAlign: 'center',
+ fontSize: 'medium',
+ fontWeight: 'bold',
+ marginTop: 0,
+ },
+ '.todo': { fontFamily: 'monospace', color: 'red' },
+ '.equationContainer': {
+ display: 'table',
+ textAlign: 'center',
+ width: '100%',
+ },
+ '.equation': {
+ verticalAlign: 'middle',
+ },
+ '.equation-label': {
+ display: 'tableCell',
+ textAlign: 'right',
+ verticalAlign: 'middle',
+ },
+ '.inlinetask': {
+ padding: '10px',
+ border: '2px solid gray',
+ margin: '10px',
+ background: '#ffffcc',
+ },
+ '#org-div-home-and-up': {
+ textAlign: 'right',
+ fontSize: '70 % ',
+ whiteSpace: 'nowrap',
+ },
+ textarea: { overflowX: 'auto' },
+ '.linenr': { fontSize: 'smaller' },
+ '.code-highlighted': { backgroundColor: '#ffff00' },
+ '.org-info-js_info-navigation': { borderStyle: 'none' },
+ '#org-info-js_console-label': {
+ fontSize: '10px',
+ fontWeight: 'bold',
+ whiteSpace: 'nowrap',
+ },
+ '.org-info-js_search-highlight': {
+ backgroundColor: '#ffff00',
+ color: '#000000',
+ fontWeight: 'bold',
+ },
+ '.org-svg': { width: '90%' },
+ '.done': { fontFamily: 'monospace', color: 'green' },
+ '.priority': { fontFamily: 'monospace', color: 'orange' },
+ '.tag': {
+ backgroundColor: '#eee',
+ fontFamily: 'monospace',
+ padding: '2px',
+ fontSize: '80%',
+ fontWeight: 'normal',
+ },
+ '.timestamp': { color: '#bebebe' },
+ '.timestamp-kwd': { color: '#5f9ea0' },
+ '.org-right': { marginLeft: 'auto', marginRight: '0px', textAlign: 'right' },
+ '.org-left': { marginLeft: '0px', marginRight: 'auto', textAlign: 'left' },
+ '.org-center': { marginLeft: 'auto', marginRight: 'auto', textAlign: 'center' },
+ '.underline': { textDecoration: 'underline' },
+ '#postamble p': { fontSize: '90%', margin: '.2em' },
+ '#preamble p': { fontSize: '90%', margin: '.2em' },
+ 'p.verse': { marginLeft: '3%' },
+ pre: {
+ border: '1px solid #e6e6e6',
+ borderRadius: '3px',
+ backgroundColor: '#f2f2f2',
+ padding: '8pt',
+ fontFamily: 'monospace',
+ overflow: 'auto',
+ margin: '1.2em',
+ },
+ 'pre.src': {
+ position: 'relative',
+ overflow: 'auto',
+ },
+ 'pre.src:before': {
+ display: 'none',
+ position: 'absolute',
+ top: '-8px',
+ right: '12px',
+ padding: '3px',
+ color: '#555',
+ backgroundColor: '#f2f2f299',
+ },
+ 'caption.t-above': { captionSide: 'top' },
+ 'caption.t-bottom': { captionSide: 'bottom' },
+ 'th.org-right': { textAlign: 'center' },
+ 'th.org-left': { textAlign: 'center' },
+ 'th.org-center': { textAlign: 'center' },
+ 'td.org-right': { textAlign: 'right' },
+ 'td.org-left': { textAlign: 'left' },
+ 'td.org-center': { textAlign: 'center' },
+ '.footpara': { display: 'inline' },
+ '.footdef': { marginBottom: '1em' },
+ '.figure': { padding: '1em' },
+ '.figure p': { textAlign: 'center' },
+ }}
+ >
+ <UniOrg orgText={orgText} />
+ </Box>
+ </VStack>
+ </Scrollbars>
+ </Box>
+ </Flex>
+ </Slide>
+ )
+}
+
+export default Sidebar
+
+
+
+
+/* <Box marginLeft="auto" zIndex={5000} bg="alt.100" maxHeight="100%" width= "30%" padding={8} borderRadius={2}>
+ <Heading size="sm">{previewNode.title}</Heading>
+</Box> */
diff --git a/components/contextmenu.tsx b/components/contextmenu.tsx
index 39b1895..ff3dd81 100644
--- a/components/contextmenu.tsx
+++ b/components/contextmenu.tsx
@@ -43,6 +43,7 @@ import {
} from '@chakra-ui/icons'
import { OrgRoamGraphReponse, OrgRoamLink, OrgRoamNode } from '../api'
+import { getOrgText, deleteNodeInEmacs, openNodeInEmacs, createNodeInEmacs } from "../util/webSocketFunctions"
export default interface ContextMenuProps {
background: Boolean
@@ -50,12 +51,10 @@ export default interface ContextMenuProps {
nodeType?: string
coordinates: number[]
handleLocal: (node: OrgRoamNode, add: string) => void
- openNodeInEmacs: (node: OrgRoamNode) => void
menuClose: () => void
scope: { nodeIds: string[] }
- deleteNodeInEmacs: (node: OrgRoamNode) => void
- createNodeInEmacs: (node: OrgRoamNode) => void
- getOrgText: any
+ webSocket: any
+ setPreviewNode: any,
}
export const ContextMenu = (props: ContextMenuProps) => {
@@ -67,10 +66,8 @@ export const ContextMenu = (props: ContextMenuProps) => {
handleLocal,
menuClose,
scope,
- openNodeInEmacs,
- deleteNodeInEmacs,
- createNodeInEmacs,
- getOrgText,
+ webSocket,
+ setPreviewNode,
} = props
const { isOpen, onOpen, onClose } = useDisclosure()
const copyRef = useRef<any>()
@@ -104,11 +101,11 @@ export const ContextMenu = (props: ContextMenuProps) => {
</>
)}
{!node?.properties.FILELESS ? (
- <MenuItem icon={<EditIcon />} onClick={() => openNodeInEmacs(node as OrgRoamNode)}>
+ <MenuItem icon={<EditIcon />} onClick={() => openNodeInEmacs(node as OrgRoamNode, webSocket)}>
Open in Emacs
</MenuItem>
) : (
- <MenuItem icon={<AddIcon />} onClick={() => createNodeInEmacs(node)}>
+ <MenuItem icon={<AddIcon />} onClick={() => createNodeInEmacs(node, webSocket)}>
Create node
</MenuItem>
)}
@@ -159,7 +156,8 @@ export const ContextMenu = (props: ContextMenuProps) => {
Permenantly delete note
</MenuItem>
)}
- <MenuItem onClick={() => getOrgText(node)}>Preview</MenuItem>
+ <MenuItem onClick={() => {getOrgText(node!, webSocket)
+ setPreviewNode(node)}}>Preview</MenuItem>
</MenuList>
</Menu>
</Box>
@@ -198,7 +196,7 @@ export const ContextMenu = (props: ContextMenuProps) => {
ml={3}
onClick={() => {
console.log('aaaaa')
- deleteNodeInEmacs(node!)
+ deleteNodeInEmacs(node!, webSocket)
onClose()
menuClose()
}}
diff --git a/pages/index.tsx b/pages/index.tsx
index 5b3c35d..ad01741 100644
--- a/pages/index.tsx
+++ b/pages/index.tsx
@@ -17,11 +17,12 @@ import type {
} from 'react-force-graph'
import { OrgRoamGraphReponse, OrgRoamLink, OrgRoamNode } from '../api'
import { GraphData, NodeObject, LinkObject } from 'force-graph'
+import Sidebar from '../components/Sidebar'
import { useWindowSize } from '@react-hook/window-size'
import { useAnimation } from '@lilib/hooks'
-import { Box, useDisclosure, useTheme } from '@chakra-ui/react'
+import { Box, Flex, IconButton, useDisclosure, useTheme } from '@chakra-ui/react'
import {
initialPhysics,
@@ -41,6 +42,8 @@ import SpriteText from 'three-spritetext'
import wrap from 'word-wrap'
import ReconnectingWebSocket from 'reconnecting-websocket'
+import { getOrgText, deleteNodeInEmacs, openNodeInEmacs, createNodeInEmacs } from "../util/webSocketFunctions"
+import { ChevronLeftIcon } from '@chakra-ui/icons'
// react-force-graph fails on import when server-rendered
// https://github.com/vasturiano/react-force-graph/issues/155
const ForceGraph2D = (
@@ -84,11 +87,9 @@ export function GraphPage() {
const [emacsNodeId, setEmacsNodeId] = useState<string | null>(null)
const [behavior, setBehavior] = usePersistantState('behavior', initialBehavior)
const [mouse, setMouse] = usePersistantState('mouse', initialMouse)
- const [orgText, setOrgText] = useState('')
-
- useEffect(() => {
- console.log(orgText)
- }, [orgText])
+ const [orgText, setOrgText] = useState("")
+ const [previewNode, setPreviewNode] = useState<NodeObject>({})
+ const { isOpen, onOpen, onClose } = useDisclosure()
const nodeByIdRef = useRef<NodeById>({})
const linksByNodeIdRef = useRef<LinksByNodeId>({})
@@ -364,7 +365,7 @@ export function GraphPage() {
case 'graphdata':
return updateGraphData(message.data)
case 'orgText':
- return setOrgText(message.data)
+ return setOrgText(message.data)
case 'theme':
return setEmacsTheme(message.data)
case 'command':
@@ -414,25 +415,50 @@ export function GraphPage() {
return (
<Box display="flex" alignItems="flex-start" flexDirection="row" height="100%" overflow="hidden">
- <Tweaks
- {...{
- physics,
- setPhysics,
- threeDim,
- setThreeDim,
- filter,
- setFilter,
- visuals,
- setVisuals,
- mouse,
- setMouse,
- behavior,
- setBehavior,
- tagColors,
- setTagColors,
+ <Box display="flex" justifyContent="space-between" flexDirection="row" height="100%" width="100%">
+ <Sidebar
+ {...{
+ isOpen,
+ onClose,
+ previewNode,
+ orgText,
}}
- tags={tagsRef.current}
- />
+ />
+ <Tweaks
+ {...{
+ physics,
+ setPhysics,
+ threeDim,
+ setThreeDim,
+ filter,
+ setFilter,
+ visuals,
+ setVisuals,
+ mouse,
+ setMouse,
+ behavior,
+ setBehavior,
+ tagColors,
+ setTagColors,
+ }}
+ tags={tagsRef.current}
+ />
+ <Flex height="100%" flexDirection="column" marginLeft="auto">
+ {!isOpen && (
+ <IconButton
+ icon={<ChevronLeftIcon />}
+ height={100}
+ aria-label="Open org-viewer"
+ position="relative"
+ zIndex="overlay"
+ colorScheme="purple"
+ onClick={onOpen}
+ variant="ghost"
+ marginTop={10}
+ />
+ )}
+ </Flex>
+ </Box>
<Box position="absolute" alignItems="top" overflow="hidden">
<Graph
ref={graphRef}
@@ -451,6 +477,7 @@ export function GraphPage() {
scope,
setScope,
tagColors,
+ setPreviewNode,
}}
/>
</Box>
@@ -473,9 +500,10 @@ export interface GraphProps {
setScope: any
webSocket: any
tagColors: { [tag: string]: string }
+ setPreviewNode: any
}
-export const Graph = forwardRef(function (props: GraphProps, graphRef: any) {
+export const Graph = forwardRef(function(props: GraphProps, graphRef: any) {
const {
physics,
graphData,
@@ -491,6 +519,7 @@ export const Graph = forwardRef(function (props: GraphProps, graphRef: any) {
setScope,
webSocket,
tagColors,
+ setPreviewNode,
} = props
// react-force-graph does not track window size
@@ -522,28 +551,6 @@ export const Graph = forwardRef(function (props: GraphProps, graphRef: any) {
return
}
- const sendMessageToEmacs = (command: string, data: {}) => {
- webSocket.send(JSON.stringify({ command: command, data: data }))
- }
-
- const getOrgText = (node: OrgRoamNode) => {
- sendMessageToEmacs('getText', { id: node.id })
- }
-
- const openNodeInEmacs = (node: OrgRoamNode) => {
- sendMessageToEmacs('open', { id: node.id })
- }
-
- const deleteNodeInEmacs = (node: OrgRoamNode) => {
- if (node.level !== 0) {
- return
- }
- sendMessageToEmacs('delete', { id: node.id, file: node.file })
- }
-
- const createNodeInEmacs = (node: OrgRoamNode) => {
- sendMessageToEmacs('create', { id: node.id, title: node.title, ref: node.properties.ROAM_REFS })
- }
const contextMenu = useDisclosure()
@@ -560,7 +567,7 @@ export const Graph = forwardRef(function (props: GraphProps, graphRef: any) {
break
}
case mouse.follow: {
- openNodeInEmacs(node)
+ openNodeInEmacs(node, webSocket)
break
}
case mouse.context: {
@@ -772,7 +779,7 @@ export const Graph = forwardRef(function (props: GraphProps, graphRef: any) {
])
useEffect(() => {
- ;(async () => {
+ ; (async () => {
const fg = graphRef.current
const d3 = await d3promise
if (physics.gravityOn && !(scope.nodeIds.length && !physics.gravityLocal)) {
@@ -934,15 +941,15 @@ export const Graph = forwardRef(function (props: GraphProps, graphRef: any) {
return needsHighlighting
? getThemeColor(visuals.citeNodeColor)
: highlightColors[visuals.citeNodeColor][visuals.backgroundColor](
- visuals.highlightFade * opacity,
- )
+ visuals.highlightFade * opacity,
+ )
}
if (visuals.refNodeColor && node.properties.ROAM_REFS) {
return needsHighlighting
? getThemeColor(visuals.refNodeColor)
: highlightColors[visuals.refNodeColor][visuals.backgroundColor](
- visuals.highlightFade * opacity,
- )
+ visuals.highlightFade * opacity,
+ )
}
if (!needsHighlighting) {
return highlightColors[getNodeColorById(node.id as string)][visuals.backgroundColor](
@@ -1090,20 +1097,20 @@ export const Graph = forwardRef(function (props: GraphProps, graphRef: any) {
if (visuals.refLinkColor && roamLink.type === 'ref') {
return needsHighlighting && (visuals.refLinkHighlightColor || visuals.linkHighlight)
? highlightColors[visuals.refLinkColor][
- visuals.refLinkHighlightColor || visuals.linkHighlight
- ](opacity)
+ visuals.refLinkHighlightColor || visuals.linkHighlight
+ ](opacity)
: highlightColors[visuals.refLinkColor][visuals.backgroundColor](
- visuals.highlightFade * opacity,
- )
+ visuals.highlightFade * opacity,
+ )
}
if (visuals.citeLinkColor && roamLink.type?.includes('cite')) {
return needsHighlighting && (visuals.citeLinkHighlightColor || visuals.linkHighlight)
? highlightColors[visuals.citeLinkColor][
- visuals.citeLinkHighlightColor || visuals.linkHighlight
- ](opacity)
+ visuals.citeLinkHighlightColor || visuals.linkHighlight
+ ](opacity)
: highlightColors[visuals.citeLinkColor][visuals.backgroundColor](
- visuals.highlightFade * opacity,
- )
+ visuals.highlightFade * opacity,
+ )
}
return getLinkColor(sourceId as string, targetId as string, needsHighlighting)
@@ -1192,10 +1199,8 @@ export const Graph = forwardRef(function (props: GraphProps, graphRef: any) {
coordinates={contextPos}
handleLocal={handleLocal}
menuClose={contextMenu.onClose.bind(contextMenu)}
- openNodeInEmacs={openNodeInEmacs}
- deleteNodeInEmacs={deleteNodeInEmacs}
- createNodeInEmacs={createNodeInEmacs}
- getOrgText={getOrgText}
+ webSocket={webSocket}
+ setPreviewNode={setPreviewNode}
/>
)}
{threeDim ? (
diff --git a/pages/uniorg.tsx b/pages/uniorg.tsx
index 65786c9..0c77cdb 100644
--- a/pages/uniorg.tsx
+++ b/pages/uniorg.tsx
@@ -11,7 +11,7 @@ export interface uniorgProps {
orgText: string
}
-const UniOrg = (props: uniorgProps) => {
+export const UniOrg = (props: uniorgProps) => {
const { orgText } = props
const processor = unified()
.use(uniorgParse)
@@ -22,4 +22,3 @@ const UniOrg = (props: uniorgProps) => {
return <div>processor.processSync(orgText)</div>
}
-export default UniOrg
diff --git a/util/uniorg.tsx b/util/uniorg.tsx
index e4d8dfd..ba86a89 100644
--- a/util/uniorg.tsx
+++ b/util/uniorg.tsx
@@ -7,11 +7,11 @@ import katex from 'rehype-katex'
import rehype2react from 'rehype-react'
import React from 'react'
-export interface uniorgProps {
+export interface UniOrgProps {
orgText: string
}
-const uniorg = (props: uniorgProps) => {
+export const UniOrg = (props: UniOrgProps) => {
const { orgText } = props
const processor = unified()
.use(uniorgParse)
@@ -19,7 +19,7 @@ const uniorg = (props: uniorgProps) => {
.use(katex)
.use(rehype2react, { createElement: React.createElement })
- return processor.processSync(orgText)
+ console.log(processor.processSync(orgText))
+ return <div> {processor.processSync(orgText).result}</div>
}
-export default uniorg
diff --git a/util/webSocketFunctions.ts b/util/webSocketFunctions.ts
new file mode 100644
index 0000000..8e2bd7e
--- /dev/null
+++ b/util/webSocketFunctions.ts
@@ -0,0 +1,25 @@
+import { OrgRoamNode } from '../api'
+import ReconnectingWebSocket from 'reconnecting-websocket'
+
+export function sendMessageToEmacs(command: string, data: {}, webSocket: ReconnectingWebSocket) {
+ webSocket.send(JSON.stringify({ command: command, data: data }))
+}
+
+export function getOrgText(node: OrgRoamNode, webSocket: ReconnectingWebSocket) {
+ sendMessageToEmacs('getText', { id: node.id }, webSocket)
+}
+
+export function openNodeInEmacs(node: OrgRoamNode, webSocket: ReconnectingWebSocket) {
+ sendMessageToEmacs('open', { id: node.id }, webSocket)
+}
+
+export function deleteNodeInEmacs(node: OrgRoamNode, webSocket: ReconnectingWebSocket) {
+ if (node.level !== 0) {
+ return
+ }
+ sendMessageToEmacs('delete', { id: node.id, file: node.file }, webSocket)
+}
+
+export function createNodeInEmacs(node: OrgRoamNode, webSocket: ReconnectingWebSocket) {
+ sendMessageToEmacs('create', { id: node.id, title: node.title, ref: node.properties.ROAM_REFS }, webSocket)
+}