diff --git a/app/workspace/Spaces/Command/index.js b/app/workspace/Spaces/Command/index.js
index 5d0e27fee..28dd7e83b 100644
--- a/app/workspace/Spaces/Command/index.js
+++ b/app/workspace/Spaces/Command/index.js
@@ -5,8 +5,6 @@ import React from 'react'
import { Cluster, ClusterRow, ClusterValue } from '../../../../resources/Components/Cluster'
-import Dock from './Dock'
-
const Container = styled.div`
position: relative;
display: flex;
@@ -160,27 +158,24 @@ const AssetList = styled.div`
`
const Home = ({ data }) => (
- <>
-
- {data.station === 'command' ? (
-
- ) : data.station === 'dashboard' ? (
- <>
-
- Value
- Assets
- Inventory
-
-
- {'Account activity'}
-
- >
- ) : (
- {JSON.stringify(data, null, 4)}
- )}
-
-
- >
+
+ {data.station === 'command' ? (
+
+ ) : data.station === 'dashboard' ? (
+ <>
+
+ Value
+ Assets
+ Inventory
+
+
+ {'Account activity'}
+
+ >
+ ) : (
+ {JSON.stringify(data, null, 4)}
+ )}
+
)
export default Home
diff --git a/app/workspaceDock/Dock/index.js b/app/workspaceDock/Dock/index.js
new file mode 100644
index 000000000..cc85d6485
--- /dev/null
+++ b/app/workspaceDock/Dock/index.js
@@ -0,0 +1,194 @@
+import svg from '../../../resources/svg'
+import styled, { createGlobalStyle } from 'styled-components'
+import link from '../../../resources/link'
+import useStore from '../../../resources/Hooks/useStore.js'
+import { useState, useEffect } from 'react'
+
+const GlobalStyle = createGlobalStyle`
+ body {
+ width: 100%;
+ height: 100%;
+ margin: 0;
+ padding: 0px;
+ font-family: 'MainFont';
+ font-weight: 400;
+ color: var(--outerspace);
+ }
+`
+
+const Dock = styled.div`
+ border-radius: 16px;
+ background: var(--ghostA);
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ padding: 12px 0px 12px 12px;
+`
+
+const DappRow = styled.div`
+ display: flex;
+ align-items: center;
+ justify-content: center;
+`
+
+const DappIcon = styled.div`
+ width: 42px;
+ height: 42px;
+ margin-right: 12px;
+ /* margin: 0px 2px 2px 12px; */
+ border-radius: 16px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ cursor: pointer;
+ background: var(--ghostB);
+ border-radius: 8px;
+
+ * {
+ pointer-events: none;
+ }
+
+ &:hover {
+ background: var(--ghostC);
+ transform: scale(1.2);
+ transition: transform 0.2s ease-in-out;
+ }
+`
+
+const DappIconBreak = styled.div`
+ width: 3px;
+ height: 42px;
+ background: var(--ghostZ);
+ border-radius: 1.5px;
+ margin: 0px 12px 0px 8px;
+`
+const DockWrap = styled.div`
+ position: fixed;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ transform: ${({ hide }) => (hide ? 'translateY(100px)' : 'translateY(0)')};
+ transition: transform 0.4s ease-in-out;
+`
+
+const Wrap = styled.div`
+ position: fixed;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+`
+
+const DockHandle = styled.div`
+ position: absolute;
+ left: 50%;
+ bottom: 2px;
+ height: 4px;
+ width: 32px;
+ margin-left: -16px;
+ border-radius: 2px;
+ background: var(--outerspace);
+ display: none;
+`
+
+const RoundedElement = styled.div`
+ position: absolute;
+ left: 8px;
+ bottom: 8px;
+
+ width: 5px;
+ height: 5px;
+ background-color: var(--ghostAZ); /* Your actual background color */
+
+ /* The mask */
+ -webkit-mask-box-image: radial-gradient(circle at top right, transparent 0% 5px, black 5px);
+ mask-box-image: radial-gradient(circle at top right, transparent 0% 5px, black 5px);
+`
+
+const RoundedElementRight = styled.div`
+ position: absolute;
+ right: 8px;
+ bottom: 8px;
+
+ width: 5px;
+ height: 5px;
+ background-color: var(--ghostAZ); /* Your actual background color */
+
+ /* The mask */
+ -webkit-mask-box-image: radial-gradient(circle at top left, transparent 0% 5px, black 5px);
+ mask-box-image: radial-gradient(circle at top left, transparent 0% 5px, black 5px);
+`
+let hideTimeout
+export default () => {
+ const frameState = useStore('windows.workspaces', window.frameId)
+ const nav = frameState?.nav[0] || { space: 'command', data: {} }
+ if (!nav || !nav.space) return null
+
+ const [hideDockWrap, setHideDockWrap] = useState(false)
+
+ const setHide = () => {
+ setHideDockWrap(true)
+ clearTimeout(hideTimeout)
+ hideTimeout = setTimeout(() => {
+ link.send('workspace:nav:update:data', window.frameId, { hidden: true })
+ }, 500)
+ }
+
+ const setShow = () => {
+ clearTimeout(hideTimeout)
+ setHideDockWrap(false)
+ link.send('workspace:nav:update:data', window.frameId, { hidden: false })
+ }
+
+ const hidden = (nav.space === 'dapp' && hideDockWrap) || (nav.space !== 'dapp' && nav.space !== 'command')
+
+ return (
+ setShow()}>
+
+
+
+
+ {
+ setHide()
+ }}
+ >
+
+ {
+ link.send('workspace:nav', window.frameId, 'command', { station: 'command' })
+ }}
+ >
+ {'C'}
+
+ {
+ link.send('workspace:nav', window.frameId, 'command', { station: 'dashboard' })
+ }}
+ >
+ {'D'}
+
+
+ {
+ // link.send('workspace:nav:update:data', window.frameId, { station: 'dapp' })
+ link.send('workspace:run', 'dapp', {}, ['send.frame.eth'])
+ }}
+ >
+ {svg.send(15)}
+
+ {'-'}
+ {'-'}
+ {'-'}
+ {'-'}
+ {'-'}
+
+
+
+ )
+}
diff --git a/app/workspaceDock/index.dev.html b/app/workspaceDock/index.dev.html
new file mode 100644
index 000000000..87bbdf775
--- /dev/null
+++ b/app/workspaceDock/index.dev.html
@@ -0,0 +1,23 @@
+
+
+
+
+ Dock
+
+
+
+
+
+
+
diff --git a/app/workspaceDock/index.html b/app/workspaceDock/index.html
new file mode 100644
index 000000000..03a1ba8bb
--- /dev/null
+++ b/app/workspaceDock/index.html
@@ -0,0 +1,24 @@
+
+
+
+
+ Dock
+
+
+
+
+
+
+
diff --git a/app/workspaceDock/index.js b/app/workspaceDock/index.js
new file mode 100644
index 000000000..0bc778bb7
--- /dev/null
+++ b/app/workspaceDock/index.js
@@ -0,0 +1,44 @@
+import * as Sentry from '@sentry/electron'
+import { createRoot } from 'react-dom/client'
+
+import link from '../../resources/link'
+import restore from '../store'
+
+import { StoreContext } from '../../resources/Hooks/useStore'
+
+import Dock from './Dock'
+
+Sentry.init({ dsn: 'https://7b09a85b26924609bef5882387e2c4dc@o1204372.ingest.sentry.io/6331069' })
+
+document.addEventListener('dragover', (e) => e.preventDefault())
+document.addEventListener('drop', (e) => e.preventDefault())
+
+if (process.env.NODE_ENV !== 'development') {
+ window.eval = global.eval = () => {
+ throw new Error(`This app does not support window.eval()`)
+ } // eslint-disable-line
+}
+
+link.rpc('getFrameId', (err, frameId) => {
+ if (err) return console.error('Could not get frameId from main', err)
+ window.frameId = frameId
+ link.rpc('getState', (err, state) => {
+ if (err) return console.error('Could not get initial state from main')
+ const store = restore(state)
+ store.observer(() => {
+ document.body.classList.remove('dark', 'light')
+ document.body.classList.add('clip', store('main.colorway'))
+ setTimeout(() => {
+ document.body.classList.remove('clip')
+ }, 100)
+ })
+ const root = createRoot(document.getElementById('dock'))
+ root.render(
+
+
+
+ )
+ })
+})
+
+document.addEventListener('contextmenu', (e) => link.send('*:contextmenu', e.clientX, e.clientY))
diff --git a/main/windows/frames/frameInstances.ts b/main/windows/frames/frameInstances.ts
index 32c33dc72..da1df231f 100644
--- a/main/windows/frames/frameInstances.ts
+++ b/main/windows/frames/frameInstances.ts
@@ -12,6 +12,8 @@ const isDev = process.env.NODE_ENV === 'development'
export interface FrameInstance extends BrowserWindow {
frameId?: string
views?: Record
+ overlays?: Record
+ overlay?: BrowserView
showingView?: string
}
@@ -77,6 +79,18 @@ export default {
// })
frameInstance.on('resize', () => {
+ // TODO: reflect correct state of dock
+ if (frameInstance.overlay) {
+ const { width, height } = frameInstance.getBounds()
+
+ frameInstance.overlay.setBounds({
+ y: height - 96,
+ x: 0,
+ width: width,
+ height: 96
+ })
+ }
+
Object.values(frameInstance.views || {}).forEach((viewInstance) => {
const { frameId } = frameInstance
// const { fullscreen } = store('windows.workspaces', frameId)
diff --git a/main/windows/frames/index.ts b/main/windows/frames/index.ts
index e1397cdef..ad89754e0 100644
--- a/main/windows/frames/index.ts
+++ b/main/windows/frames/index.ts
@@ -12,6 +12,7 @@ import store from '../../store'
import frameInstances, { FrameInstance } from './frameInstances.js'
import viewInstances from './viewInstances'
+import overlayInstances from './overlayInstances'
import { Workspace, Nav, View } from '../workspace/types'
@@ -46,12 +47,47 @@ export default class WorkspaceManager {
const frames = getFrames()
this.manageFrames(frames, inFocus)
this.manageViews(frames)
- // this.manageOverlays(frames)
+ this.manageOverlays(frames)
})
}
- manageOverlays(frames: Record, inFocus: string) {
- // If there is an overlay, create a frame instance for it
+ manageOverlays(frames: Record) {
+ const frameIds = Object.keys(frames)
+
+ frameIds.forEach((frameId) => {
+ const frameInstance = this.frameInstances[frameId]
+ if (!frameInstance) return log.error('Instance not found when managing views')
+
+ // Frame definition in the state
+ const frame = frames[frameId]
+
+ // Current Nav
+ const currentNav = frame?.nav[0]
+
+ if (!frameInstance.overlay) {
+ frameInstance.overlay = overlayInstances.create(frameInstance)
+ }
+ const { width, height } = frameInstance.getBounds()
+ if (currentNav?.space === 'dapp' && currentNav?.data.hidden === true) {
+ frameInstance.overlay.setBounds({
+ y: height - 13,
+ x: 0,
+ width: width,
+ height: 13
+ })
+ } else {
+ frameInstance.overlay.setBounds({
+ y: height - 96,
+ x: 0,
+ width: width,
+ height: 96
+ })
+ }
+
+ // We could track this on the instance to add it only when necessary
+ frameInstance.addBrowserView(frameInstance.overlay)
+ frameInstance.setTopBrowserView(frameInstance.overlay)
+ })
}
manageFrames(frames: Record, inFocus: string) {
@@ -175,7 +211,7 @@ export default class WorkspaceManager {
if (!viewInstance) return log.error('View instance not found when managing views')
// Get view stats
- const viewMeta = { ready: true } //TODO: store('workspacesMeta', frame.id, 'views', view.id)
+ const viewMeta = { ready: true } // TODO: store('workspacesMeta', frame.id, 'views', view.id)
// Show all in the current nav
if (viewMeta.ready && currentNavViewIds.includes(view.id)) {
frameInstance.addBrowserView(viewInstance)
@@ -223,6 +259,8 @@ export default class WorkspaceManager {
if (frameInstance && !frameInstance.isDestroyed()) {
const webContents = frameInstance.webContents
if (webContents) webContents.send(channel, ...args)
+ const overlayWebContents = frameInstance.overlay?.webContents
+ if (overlayWebContents) overlayWebContents.send(channel, ...args)
} else {
log.error(
new Error(
diff --git a/main/windows/frames/overlayInstances.ts b/main/windows/frames/overlayInstances.ts
new file mode 100644
index 000000000..6c9ccfe9a
--- /dev/null
+++ b/main/windows/frames/overlayInstances.ts
@@ -0,0 +1,83 @@
+import { URL } from 'url'
+import log from 'electron-log'
+
+import { FrameInstance } from './frameInstances'
+import store from '../../store'
+import server from '../../dapps/server'
+import { createViewInstance, createOverlayInstance } from '../window'
+
+import type { Nav, View } from '../workspace/types'
+
+const isDev = process.env.NODE_ENV === 'development'
+
+export default {
+ hide: (frameInstance: FrameInstance) => {
+ const overlayInstance = frameInstance.overlay
+ if (frameInstance && !frameInstance.isDestroyed() && overlayInstance)
+ frameInstance.removeBrowserView(overlayInstance)
+ },
+ show: (frameInstance: FrameInstance) => {
+ const overlayInstance = frameInstance.overlay
+ if (frameInstance && !frameInstance.isDestroyed() && overlayInstance) {
+ frameInstance.addBrowserView(overlayInstance)
+ frameInstance.setTopBrowserView(overlayInstance)
+ }
+ },
+ // Create a view instance on a frame
+ create: (frameInstance: FrameInstance) => {
+ const overlayInstance = createOverlayInstance()
+ frameInstance.addBrowserView(overlayInstance)
+ overlayInstance.webContents.setVisualZoomLevelLimits(1, 1)
+
+ overlayInstance.webContents.loadURL(
+ isDev
+ ? 'http://localhost:1234/workspaceDock/index.dev.html'
+ : `file://${process.env.BUNDLE_LOCATION}/workspaceDock.html`
+ )
+
+ const { width, height } = frameInstance.getBounds()
+ overlayInstance.setBounds({
+ y: height - 72,
+ x: 0,
+ width: width,
+ height: 72
+ })
+
+ return overlayInstance
+
+ // frameInstance.removeBrowserView(viewInstance)
+ // viewInstance.webContents.openDevTools({ mode: 'detach' })
+
+ // Keep reference to view on frame instance
+ // frameInstance.overlays = { ...(frameInstance.overlays || {}), [overlay.id]: overlayInstance }
+ },
+ // Destroy a view instance on a frame
+ destroy: (frameInstance: FrameInstance, viewId: string) => {
+ const views = frameInstance.views || {}
+ const { frameId } = frameInstance
+
+ if (frameInstance && !frameInstance.isDestroyed()) frameInstance.removeBrowserView(views[viewId])
+
+ const webcontents = views[viewId].webContents as any
+ webcontents.destroy()
+
+ delete views[viewId]
+ },
+ position: (frameInstance: FrameInstance, viewId: string) => {
+ const { frameId } = frameInstance
+ const { fullscreen } = store('windows.workspaces', frameId)
+ const viewInstance = (frameInstance.views || {})[viewId]
+
+ if (viewInstance) {
+ const { width, height } = frameInstance.getBounds()
+ viewInstance.setBounds({
+ x: 0,
+ y: 64,
+ width: width,
+ height: height - 96
+ })
+ // viewInstance.setBounds({ x: 73, y: 16, width: width - 73, height: height - 16 })
+ // viewInstance.setAutoResize({ width: true, height: true })
+ }
+ }
+}
diff --git a/main/windows/frames/viewInstances.ts b/main/windows/frames/viewInstances.ts
index f1295a87e..6c762939a 100644
--- a/main/windows/frames/viewInstances.ts
+++ b/main/windows/frames/viewInstances.ts
@@ -130,10 +130,10 @@ export default {
// height: fullscreen ? height : height - 32
// })
viewInstance.setBounds({
- x: 0,
- y: 64,
- width: width,
- height: height - 96
+ x: 8,
+ y: 8,
+ width: width - 16,
+ height: height - 16
})
// viewInstance.setBounds({ x: 73, y: 16, width: width - 73, height: height - 16 })
// viewInstance.setAutoResize({ width: true, height: true })
diff --git a/main/windows/window.ts b/main/windows/window.ts
index 4a2c4163b..dd9533ef1 100644
--- a/main/windows/window.ts
+++ b/main/windows/window.ts
@@ -90,6 +90,32 @@ export function createViewInstance(
return viewInstance
}
+export function createOverlayInstance(
+ url = '',
+ webPreferences: BrowserWindowConstructorOptions['webPreferences'] = {}
+) {
+ const overlayInstance = new BrowserView({
+ webPreferences: {
+ ...webPreferences,
+ contextIsolation: true,
+ webviewTag: false,
+ sandbox: true,
+ defaultEncoding: 'utf-8',
+ nodeIntegration: false,
+ scrollBounce: true,
+ navigateOnDragDrop: false,
+ disableBlinkFeatures: 'Auxclick',
+ preload: path.resolve(process.env.BUNDLE_LOCATION, 'bridge.js')
+ }
+ })
+
+ overlayInstance.webContents.on('will-navigate', (e) => e.preventDefault())
+ overlayInstance.webContents.on('will-attach-webview', (e) => e.preventDefault())
+ overlayInstance.webContents.setWindowOpenHandler(() => ({ action: 'deny' }))
+
+ return overlayInstance
+}
+
const externalWhitelist = [
'https://frame.sh',
'https://chrome.google.com/webstore/detail/frame-alpha/ldcoohedfbjoobcadoglnnmmfbdlmmhf',
diff --git a/package.json b/package.json
index bef6b1652..211a0a762 100644
--- a/package.json
+++ b/package.json
@@ -12,10 +12,11 @@
"setup:scripts": "allow-scripts",
"setup": "npm run setup:deps && husky install",
"start": "npm run launch",
- "bundle": "npm run bundle:bridge && npm run bundle:tray && npm run bundle:workspace && npm run bundle:onboard && npm run bundle:notify && npm run bundle:workspace:inject",
+ "bundle": "npm run bundle:bridge && npm run bundle:tray && npm run bundle:workspace && npm run bundle:workspaceDock && npm run bundle:onboard && npm run bundle:notify && npm run bundle:workspace:inject",
"bundle:bridge": "parcel build resources/bridge/index.js --target bridge --public-url .",
"bundle:tray": "parcel build app/tray/index.html --target tray --public-url .",
"bundle:workspace": "parcel build app/workspace/index.html --target workspace --public-url .",
+ "bundle:workspaceDock": "parcel build app/workspaceDock/index.html --target workspaceDock --public-url .",
"bundle:onboard": "parcel build app/onboard/index.html --target onboard --public-url .",
"bundle:notify": "parcel build app/notify/index.html --target notify --public-url .",
"bundle:workspace:inject": "parcel build main/dapps/server/inject/index.js --target inject --public-url .",
@@ -209,6 +210,7 @@
"inject": "./bundle/inject.js",
"tray": "./bundle/tray.html",
"workspace": "./bundle/workspace.html",
+ "workspaceDock": "./bundle/workspaceDock.html",
"onboard": "./bundle/onboard.html",
"notify": "./bundle/notify.html",
"targets": {
@@ -223,6 +225,9 @@
"workspace": {
"context": "browser"
},
+ "workspaceDock": {
+ "context": "browser"
+ },
"onboard": {
"context": "browser"
},
diff --git a/scripts/bundler.mjs b/scripts/bundler.mjs
index b9ba488df..c550b6626 100644
--- a/scripts/bundler.mjs
+++ b/scripts/bundler.mjs
@@ -10,6 +10,7 @@ const bundler = new Parcel({
entries: [
'app/tray/index.dev.html',
'app/workspace/index.dev.html',
+ 'app/workspaceDock/index.dev.html',
'app/onboard/index.dev.html',
'app/notify/index.dev.html'
],