@@ -3,7 +3,7 @@ import { Menu } from '@headlessui/react'
|
|||||||
import { useIdeContext } from 'src/helpers/hooks/useIdeContext'
|
import { useIdeContext } from 'src/helpers/hooks/useIdeContext'
|
||||||
import Svg from 'src/components/Svg/Svg'
|
import Svg from 'src/components/Svg/Svg'
|
||||||
import { useRender } from 'src/components/IdeWrapper/useRender'
|
import { useRender } from 'src/components/IdeWrapper/useRender'
|
||||||
import {makeStlDownloadHandler, PullTitleFromFirstLine} from './helpers'
|
import { makeStlDownloadHandler, PullTitleFromFirstLine } from './helpers'
|
||||||
|
|
||||||
const EditorMenu = () => {
|
const EditorMenu = () => {
|
||||||
const handleRender = useRender()
|
const handleRender = useRender()
|
||||||
@@ -17,16 +17,16 @@ const EditorMenu = () => {
|
|||||||
return (
|
return (
|
||||||
<div className="bg-gray-500 flex items-center h-9 w-full cursor-grab">
|
<div className="bg-gray-500 flex items-center h-9 w-full cursor-grab">
|
||||||
<div className=" text-gray-500 bg-gray-300 cursor-grab px-2 h-full flex items-center">
|
<div className=" text-gray-500 bg-gray-300 cursor-grab px-2 h-full flex items-center">
|
||||||
<Svg name='drag-grid' className="w-4 p-px" />
|
<Svg name="drag-grid" className="w-4 p-px" />
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
className="text-gray-300 px-3 h-full cursor-not-allowed"
|
className="text-gray-300 px-3 h-full cursor-not-allowed"
|
||||||
aria-label="editor settings"
|
aria-label="editor settings"
|
||||||
disabled
|
disabled
|
||||||
>
|
>
|
||||||
<Svg name='gear' className="w-7 p-px" />
|
<Svg name="gear" className="w-7 p-px" />
|
||||||
</button>
|
</button>
|
||||||
<div className="w-px h-full bg-gray-300"/>
|
<div className="w-px h-full bg-gray-300" />
|
||||||
<div className="flex gap-6 px-6">
|
<div className="flex gap-6 px-6">
|
||||||
<FileDropdown
|
<FileDropdown
|
||||||
handleRender={handleRender}
|
handleRender={handleRender}
|
||||||
@@ -45,7 +45,7 @@ const EditorMenu = () => {
|
|||||||
|
|
||||||
export default EditorMenu
|
export default EditorMenu
|
||||||
|
|
||||||
function FileDropdown({handleRender, handleStlDownload}) {
|
function FileDropdown({ handleRender, handleStlDownload }) {
|
||||||
return (
|
return (
|
||||||
<Menu>
|
<Menu>
|
||||||
<Menu.Button className="text-gray-100">File</Menu.Button>
|
<Menu.Button className="text-gray-100">File</Menu.Button>
|
||||||
@@ -56,11 +56,20 @@ function FileDropdown({handleRender, handleStlDownload}) {
|
|||||||
className={`${active && 'bg-gray-600'} px-2 py-1`}
|
className={`${active && 'bg-gray-600'} px-2 py-1`}
|
||||||
onClick={handleRender}
|
onClick={handleRender}
|
||||||
>
|
>
|
||||||
Save & Render <span className="text-gray-400 pl-4">{
|
Save & Render{' '}
|
||||||
/(Mac|iPhone|iPod|iPad)/i.test(navigator.platform) ?
|
<span className="text-gray-400 pl-4">
|
||||||
<><Svg name="mac-cmd-key" className="h-3 w-3 inline-block text-left" />S</> :
|
{/(Mac|iPhone|iPod|iPad)/i.test(navigator.platform) ? (
|
||||||
'Ctrl S'
|
<>
|
||||||
}</span>
|
<Svg
|
||||||
|
name="mac-cmd-key"
|
||||||
|
className="h-3 w-3 inline-block text-left"
|
||||||
|
/>
|
||||||
|
S
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
'Ctrl S'
|
||||||
|
)}
|
||||||
|
</span>
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { MeshBasicMaterial, Mesh, Scene } from 'three'
|
|||||||
import { STLExporter } from 'three/examples/jsm/exporters/STLExporter'
|
import { STLExporter } from 'three/examples/jsm/exporters/STLExporter'
|
||||||
import { requestRender } from 'src/helpers/hooks/useIdeState'
|
import { requestRender } from 'src/helpers/hooks/useIdeState'
|
||||||
|
|
||||||
export const PullTitleFromFirstLine = (code: string = '') => {
|
export const PullTitleFromFirstLine = (code = '') => {
|
||||||
const firstLine = code.split('\n').filter(identity)[0] || ''
|
const firstLine = code.split('\n').filter(identity)[0] || ''
|
||||||
if (!(firstLine.startsWith('//') || firstLine.startsWith('#'))) {
|
if (!(firstLine.startsWith('//') || firstLine.startsWith('#'))) {
|
||||||
return 'object.stl'
|
return 'object.stl'
|
||||||
@@ -16,44 +16,46 @@ export const PullTitleFromFirstLine = (code: string = '') => {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const makeStlDownloadHandler = (({ geometry, fileName, type, thunkDispatch}) => () => {
|
export const makeStlDownloadHandler =
|
||||||
const makeStlBlobFromGeo = flow(
|
({ geometry, fileName, type, thunkDispatch }) =>
|
||||||
(geo) => new Mesh(geo, new MeshBasicMaterial()),
|
() => {
|
||||||
(mesh) => new Scene().add(mesh),
|
const makeStlBlobFromGeo = flow(
|
||||||
(scene) => new STLExporter().parse(scene),
|
(geo) => new Mesh(geo, new MeshBasicMaterial()),
|
||||||
(stl) =>
|
(mesh) => new Scene().add(mesh),
|
||||||
new Blob([stl], {
|
(scene) => new STLExporter().parse(scene),
|
||||||
type: 'text/plain',
|
(stl) =>
|
||||||
})
|
new Blob([stl], {
|
||||||
)
|
type: 'text/plain',
|
||||||
const saveFile = (geometry) => {
|
})
|
||||||
const blob = makeStlBlobFromGeo(geometry)
|
)
|
||||||
fileSave(blob, {
|
const saveFile = (geometry) => {
|
||||||
fileName,
|
const blob = makeStlBlobFromGeo(geometry)
|
||||||
extensions: ['.stl'],
|
fileSave(blob, {
|
||||||
})
|
fileName,
|
||||||
}
|
extensions: ['.stl'],
|
||||||
if (geometry) {
|
|
||||||
if (type === 'geometry') {
|
|
||||||
saveFile(geometry)
|
|
||||||
} else {
|
|
||||||
thunkDispatch((dispatch, getState) => {
|
|
||||||
const state = getState()
|
|
||||||
if (state.ideType === 'openScad') {
|
|
||||||
thunkDispatch((dispatch, getState) => {
|
|
||||||
const state = getState()
|
|
||||||
dispatch({ type: 'setLoading' })
|
|
||||||
requestRender({
|
|
||||||
state,
|
|
||||||
dispatch,
|
|
||||||
code: state.code,
|
|
||||||
viewerSize: state.viewerSize,
|
|
||||||
camera: state.camera,
|
|
||||||
specialCadProcess: 'stl',
|
|
||||||
}).then((result) => result && saveFile(result.data))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
if (geometry) {
|
||||||
|
if (type === 'geometry') {
|
||||||
|
saveFile(geometry)
|
||||||
|
} else {
|
||||||
|
thunkDispatch((dispatch, getState) => {
|
||||||
|
const state = getState()
|
||||||
|
if (state.ideType === 'openScad') {
|
||||||
|
thunkDispatch((dispatch, getState) => {
|
||||||
|
const state = getState()
|
||||||
|
dispatch({ type: 'setLoading' })
|
||||||
|
requestRender({
|
||||||
|
state,
|
||||||
|
dispatch,
|
||||||
|
code: state.code,
|
||||||
|
viewerSize: state.viewerSize,
|
||||||
|
camera: state.camera,
|
||||||
|
specialCadProcess: 'stl',
|
||||||
|
}).then((result) => result && saveFile(result.data))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
|
||||||
|
|||||||
@@ -2,15 +2,25 @@ import { makeEncodedLink } from './helpers'
|
|||||||
import { copyTextToClipboard } from 'src/helpers/clipboard'
|
import { copyTextToClipboard } from 'src/helpers/clipboard'
|
||||||
import { useIdeContext } from 'src/helpers/hooks/useIdeContext'
|
import { useIdeContext } from 'src/helpers/hooks/useIdeContext'
|
||||||
|
|
||||||
|
|
||||||
const FullScriptEncoding = () => {
|
const FullScriptEncoding = () => {
|
||||||
const { state } = useIdeContext()
|
const { state } = useIdeContext()
|
||||||
const encodedLink = makeEncodedLink(state.code)
|
const encodedLink = makeEncodedLink(state.code)
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<p className="text-sm pb-4 border-b border-gray-700">Encodes your CodeCad script into a URL so that you can share your work</p>
|
<p className="text-sm pb-4 border-b border-gray-700">
|
||||||
<input value={encodedLink.replace(/^.+:\/\//g, '')} readOnly className="p-1 mt-4 text-xs rounded-t border border-gray-700 w-full" />
|
Encodes your CodeCad script into a URL so that you can share your work
|
||||||
<button className="w-full bg-gray-700 py-1 rounded-b text-gray-300" onClick={() => copyTextToClipboard(encodedLink)} >Copy URL</button>
|
</p>
|
||||||
|
<input
|
||||||
|
value={encodedLink.replace(/^.+:\/\//g, '')}
|
||||||
|
readOnly
|
||||||
|
className="p-1 mt-4 text-xs rounded-t border border-gray-700 w-full"
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
className="w-full bg-gray-700 py-1 rounded-b text-gray-300"
|
||||||
|
onClick={() => copyTextToClipboard(encodedLink)}
|
||||||
|
>
|
||||||
|
Copy URL
|
||||||
|
</button>
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import { useEffect } from 'react'
|
import { useEffect } from 'react'
|
||||||
import { flow } from 'lodash/fp'
|
import { flow } from 'lodash/fp'
|
||||||
|
|
||||||
import {useIdeContext} from 'src/helpers/hooks/useIdeContext'
|
import { useIdeContext } from 'src/helpers/hooks/useIdeContext'
|
||||||
import { useRender } from 'src/components/IdeWrapper/useRender'
|
import { useRender } from 'src/components/IdeWrapper/useRender'
|
||||||
import {encode, decode} from 'src/helpers/compress'
|
import { encode, decode } from 'src/helpers/compress'
|
||||||
import { isBrowser } from '@redwoodjs/prerender/browserUtils'
|
import { isBrowser } from '@redwoodjs/prerender/browserUtils'
|
||||||
|
|
||||||
const scriptKey = 'encoded_script'
|
const scriptKey = 'encoded_script'
|
||||||
@@ -25,7 +25,7 @@ export const githubSafe = (url: string): string =>
|
|||||||
const prepareEncodedUrl = flow(decodeURIComponent, githubSafe)
|
const prepareEncodedUrl = flow(decodeURIComponent, githubSafe)
|
||||||
|
|
||||||
export function useIdeInit(cadPackage: string) {
|
export function useIdeInit(cadPackage: string) {
|
||||||
const {thunkDispatch} = useIdeContext()
|
const { thunkDispatch } = useIdeContext()
|
||||||
const handleRender = useRender()
|
const handleRender = useRender()
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
thunkDispatch({
|
thunkDispatch({
|
||||||
|
|||||||
@@ -13,28 +13,31 @@ const IdeConsole = () => {
|
|||||||
}, [state.consoleMessages])
|
}, [state.consoleMessages])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="p-2 px-8 pt-14 min-h-full relative" style={matchEditorVsDarkTheme.Bg}>
|
<div
|
||||||
<PanelToolbar panelName="console" />
|
className="p-2 px-8 pt-14 min-h-full relative"
|
||||||
<div>
|
style={matchEditorVsDarkTheme.Bg}
|
||||||
{state.consoleMessages?.map(({ type, message, time }, index) => (
|
>
|
||||||
<pre
|
<PanelToolbar panelName="console" />
|
||||||
className="font-mono text-sm"
|
<div>
|
||||||
style={matchEditorVsDarkTheme.Text}
|
{state.consoleMessages?.map(({ type, message, time }, index) => (
|
||||||
key={`${message} ${index}`}
|
<pre
|
||||||
|
className="font-mono text-sm"
|
||||||
|
style={matchEditorVsDarkTheme.Text}
|
||||||
|
key={`${message} ${index}`}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className="text-xs font-bold pt-2"
|
||||||
|
style={matchEditorVsDarkTheme.TextBrown}
|
||||||
>
|
>
|
||||||
<div
|
{time?.toLocaleString()}
|
||||||
className="text-xs font-bold pt-2"
|
</div>
|
||||||
style={matchEditorVsDarkTheme.TextBrown}
|
<div className={(type === 'error' ? 'text-red-400' : '') + ' pl-4'}>
|
||||||
>
|
{message}
|
||||||
{time?.toLocaleString()}
|
</div>
|
||||||
</div>
|
</pre>
|
||||||
<div className={(type === 'error' ? 'text-red-400' : '') + ' pl-4'}>
|
))}
|
||||||
{message}
|
|
||||||
</div>
|
|
||||||
</pre>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -65,7 +65,15 @@ const IdeContainer = () => {
|
|||||||
return (
|
return (
|
||||||
<MosaicWindow
|
<MosaicWindow
|
||||||
path={path}
|
path={path}
|
||||||
renderToolbar={() => id === 'Editor' ? <div className="w-full"><EditorMenu /></div> : <div/>} // needs an empty element, otherwise it adds it's own toolbar
|
renderToolbar={() =>
|
||||||
|
id === 'Editor' ? (
|
||||||
|
<div className="w-full">
|
||||||
|
<EditorMenu />
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div /> // needs an empty element, otherwise it adds it's own toolbar
|
||||||
|
)
|
||||||
|
}
|
||||||
className={`${id.toLowerCase()} ${id.toLowerCase()}-tile`}
|
className={`${id.toLowerCase()} ${id.toLowerCase()}-tile`}
|
||||||
>
|
>
|
||||||
{id === 'Viewer' ? (
|
{id === 'Viewer' ? (
|
||||||
|
|||||||
@@ -1,62 +1,70 @@
|
|||||||
import { Popover } from '@headlessui/react'
|
import { Popover } from '@headlessui/react'
|
||||||
import { Tab, Tabs, TabList, TabPanel } from 'react-tabs';
|
import { Tab, Tabs, TabList, TabPanel } from 'react-tabs'
|
||||||
import FullScriptEncoding from 'src/components/EncodedUrl/FullScriptEncoding'
|
import FullScriptEncoding from 'src/components/EncodedUrl/FullScriptEncoding'
|
||||||
|
|
||||||
const TopButton = ({
|
const TopButton = ({
|
||||||
onClick,
|
onClick,
|
||||||
children,
|
children,
|
||||||
className,
|
className,
|
||||||
iconColor,
|
iconColor,
|
||||||
}: {
|
}: {
|
||||||
onClick?: () => void
|
onClick?: () => void
|
||||||
children: React.ReactNode
|
children: React.ReactNode
|
||||||
className?: string
|
className?: string
|
||||||
iconColor: string
|
iconColor: string
|
||||||
}) => (
|
}) => (
|
||||||
<button onClick={onClick} className={`flex bg-gray-200 h-10 justify-center items-center px-4 rounded ${className}`}>
|
<button
|
||||||
<div className={`rounded-full h-6 w-6 mr-4 ${iconColor}`}/>
|
onClick={onClick}
|
||||||
|
className={`flex bg-gray-200 h-10 justify-center items-center px-4 rounded ${className}`}
|
||||||
|
>
|
||||||
|
<div className={`rounded-full h-6 w-6 mr-4 ${iconColor}`} />
|
||||||
{children}
|
{children}
|
||||||
</button>
|
</button>
|
||||||
)
|
)
|
||||||
|
|
||||||
const IdeHeader = ({handleRender}: {handleRender: () => void}) => {
|
const IdeHeader = ({ handleRender }: { handleRender: () => void }) => {
|
||||||
return (
|
return (
|
||||||
<div className="h-16 w-full bg-gray-900 flex justify-between items-center">
|
<div className="h-16 w-full bg-gray-900 flex justify-between items-center">
|
||||||
<div className="bg-gray-700 pr-48 h-full">
|
<div className="bg-gray-700 pr-48 h-full"></div>
|
||||||
</div>
|
|
||||||
<div className="text-gray-200 flex gap-4 mr-4">
|
<div className="text-gray-200 flex gap-4 mr-4">
|
||||||
<TopButton
|
<TopButton
|
||||||
className="bg-gray-600 text-gray-200"
|
className="bg-gray-600 text-gray-200"
|
||||||
iconColor="bg-gray-300"
|
iconColor="bg-gray-300"
|
||||||
onClick={handleRender}
|
onClick={handleRender}
|
||||||
>Render</TopButton>
|
>
|
||||||
|
Render
|
||||||
|
</TopButton>
|
||||||
|
|
||||||
<Popover className="relative outline-none w-full h-full">
|
<Popover className="relative outline-none w-full h-full">
|
||||||
{({open}) => {
|
{({ open }) => {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Popover.Button className="h-full w-full outline-none">
|
<Popover.Button className="h-full w-full outline-none">
|
||||||
<TopButton iconColor="bg-gray-600" className="text-gray-700">Share</TopButton>
|
<TopButton iconColor="bg-gray-600" className="text-gray-700">
|
||||||
|
Share
|
||||||
|
</TopButton>
|
||||||
</Popover.Button>
|
</Popover.Button>
|
||||||
{open && <Popover.Panel className="absolute z-10 mt-4 right-0">
|
{open && (
|
||||||
<Tabs
|
<Popover.Panel className="absolute z-10 mt-4 right-0">
|
||||||
className="bg-gray-300 rounded-md shadow-md overflow-hidden text-gray-700"
|
<Tabs
|
||||||
selectedTabClassName="bg-gray-200"
|
className="bg-gray-300 rounded-md shadow-md overflow-hidden text-gray-700"
|
||||||
>
|
selectedTabClassName="bg-gray-200"
|
||||||
<TabPanel className="p-4">
|
>
|
||||||
<FullScriptEncoding />
|
<TabPanel className="p-4">
|
||||||
</TabPanel>
|
<FullScriptEncoding />
|
||||||
<TabPanel>
|
</TabPanel>
|
||||||
<p>blah</p>
|
<TabPanel>
|
||||||
<input onPaste={(e) => console.log(e)} />
|
<p>blah</p>
|
||||||
</TabPanel>
|
<input onPaste={(e) => console.log(e)} />
|
||||||
|
</TabPanel>
|
||||||
|
|
||||||
<TabList className="flex whitespace-nowrap text-gray-700 border-t border-gray-700">
|
<TabList className="flex whitespace-nowrap text-gray-700 border-t border-gray-700">
|
||||||
<Tab className="p-3 px-5">encoded script</Tab>
|
<Tab className="p-3 px-5">encoded script</Tab>
|
||||||
<Tab className="p-3 px-5">external script</Tab>
|
<Tab className="p-3 px-5">external script</Tab>
|
||||||
</TabList>
|
</TabList>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
</Popover.Panel>}
|
</Popover.Panel>
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -5,11 +5,15 @@ const IdeSideBar = () => {
|
|||||||
return (
|
return (
|
||||||
<div className="h-full flex flex-col justify-between">
|
<div className="h-full flex flex-col justify-between">
|
||||||
<div className="w-16 h-16 flex items-center justify-center bg-gray-900">
|
<div className="w-16 h-16 flex items-center justify-center bg-gray-900">
|
||||||
<Link to={routes.home()}>
|
<Link to={routes.home()}>
|
||||||
<Svg className="w-12" name="favicon" />
|
<Svg className="w-12" name="favicon" />
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
<button className="text-gray-300 p-2 pb-4 flex justify-center cursor-not-allowed" aria-label="IDE settings" disabled>
|
<button
|
||||||
|
className="text-gray-300 p-2 pb-4 flex justify-center cursor-not-allowed"
|
||||||
|
aria-label="IDE settings"
|
||||||
|
disabled
|
||||||
|
>
|
||||||
<Svg name="big-gear" />
|
<Svg name="big-gear" />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -8,7 +8,8 @@ import Svg from 'src/components/Svg/Svg'
|
|||||||
import { useIdeInit } from 'src/components/EncodedUrl/helpers'
|
import { useIdeInit } from 'src/components/EncodedUrl/helpers'
|
||||||
|
|
||||||
const IdeToolbarNew = ({ cadPackage }) => {
|
const IdeToolbarNew = ({ cadPackage }) => {
|
||||||
const [shouldShowConstructionMessage, setShouldShowConstructionMessage] = useState(true)
|
const [shouldShowConstructionMessage, setShouldShowConstructionMessage] =
|
||||||
|
useState(true)
|
||||||
const handleRender = useRender()
|
const handleRender = useRender()
|
||||||
useIdeInit(cadPackage)
|
useIdeInit(cadPackage)
|
||||||
|
|
||||||
@@ -21,21 +22,29 @@ const IdeToolbarNew = ({ cadPackage }) => {
|
|||||||
<nav className="flex">
|
<nav className="flex">
|
||||||
<IdeHeader handleRender={handleRender} />
|
<IdeHeader handleRender={handleRender} />
|
||||||
</nav>
|
</nav>
|
||||||
{shouldShowConstructionMessage && <div className="py-2 bg-pink-200 flex">
|
{shouldShowConstructionMessage && (
|
||||||
<div className="flex-grow text-center">
|
<div className="py-2 bg-pink-200 flex">
|
||||||
We're still working on this. Since you're here, have a look what{' '}
|
<div className="flex-grow text-center">
|
||||||
<OutBound
|
We're still working on this. Since you're here, have a look what{' '}
|
||||||
className="text-pink-700"
|
<OutBound
|
||||||
to="https://github.com/Irev-Dev/cadhub/discussions/212"
|
className="text-pink-700"
|
||||||
|
to="https://github.com/Irev-Dev/cadhub/discussions/212"
|
||||||
|
>
|
||||||
|
we've got planned
|
||||||
|
</OutBound>
|
||||||
|
.
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
className="flex"
|
||||||
|
onClick={() => setShouldShowConstructionMessage(false)}
|
||||||
>
|
>
|
||||||
we've got planned
|
<Svg
|
||||||
</OutBound>
|
className="h-4 w-6 text-gray-500 mr-3 items-center"
|
||||||
.
|
name="x"
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<button className="flex" onClick={() => setShouldShowConstructionMessage(false)}>
|
)}
|
||||||
<Svg className="h-4 w-6 text-gray-500 mr-3 items-center" name="x"/>
|
|
||||||
</button>
|
|
||||||
</div>}
|
|
||||||
<IdeContainer />
|
<IdeContainer />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -19,4 +19,3 @@ export const useRender = () => {
|
|||||||
localStorage.setItem(makeCodeStoreKey(state.ideType), state.code)
|
localStorage.setItem(makeCodeStoreKey(state.ideType), state.code)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,17 +2,21 @@ import { useContext } from 'react'
|
|||||||
import { MosaicWindowContext } from 'react-mosaic-component'
|
import { MosaicWindowContext } from 'react-mosaic-component'
|
||||||
import Svg from 'src/components/Svg/Svg'
|
import Svg from 'src/components/Svg/Svg'
|
||||||
|
|
||||||
const PanelToolbar = ({ panelName }: { panelName : string }) => {
|
const PanelToolbar = ({ panelName }: { panelName: string }) => {
|
||||||
const {mosaicWindowActions} = useContext(MosaicWindowContext)
|
const { mosaicWindowActions } = useContext(MosaicWindowContext)
|
||||||
return (
|
return (
|
||||||
<div className="absolute top-0 left-0 flex items-center h-9">
|
<div className="absolute top-0 left-0 flex items-center h-9">
|
||||||
{mosaicWindowActions.connectDragSource(
|
{mosaicWindowActions.connectDragSource(
|
||||||
<div className=" text-gray-500 bg-gray-300 cursor-grab px-2 h-full flex items-center">
|
<div className=" text-gray-500 bg-gray-300 cursor-grab px-2 h-full flex items-center">
|
||||||
<Svg name='drag-grid' className="w-4 p-px" />
|
<Svg name="drag-grid" className="w-4 p-px" />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<button className="bg-gray-500 text-gray-300 px-3 rounded-br-lg h-full cursor-not-allowed" aria-label={`${panelName} settings`} disabled>
|
<button
|
||||||
<Svg name='gear' className="w-7 p-px" />
|
className="bg-gray-500 text-gray-300 px-3 rounded-br-lg h-full cursor-not-allowed"
|
||||||
|
aria-label={`${panelName} settings`}
|
||||||
|
disabled
|
||||||
|
>
|
||||||
|
<Svg name="gear" className="w-7 p-px" />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,35 +1,40 @@
|
|||||||
type SvgNames = 'arrow-down' |
|
type SvgNames =
|
||||||
'arrow' |
|
| 'arrow-down'
|
||||||
'arrow-left' |
|
| 'arrow'
|
||||||
'big-gear' |
|
| 'arrow-left'
|
||||||
'camera' |
|
| 'big-gear'
|
||||||
'checkmark' |
|
| 'camera'
|
||||||
'chevron-down' |
|
| 'checkmark'
|
||||||
'dots-vertical' |
|
| 'chevron-down'
|
||||||
'drag-grid' |
|
| 'dots-vertical'
|
||||||
'exclamation-circle' |
|
| 'drag-grid'
|
||||||
'favicon' |
|
| 'exclamation-circle'
|
||||||
'flag' |
|
| 'favicon'
|
||||||
'fork' |
|
| 'flag'
|
||||||
'gear' |
|
| 'fork'
|
||||||
'lightbulb' |
|
| 'gear'
|
||||||
'logout' |
|
| 'lightbulb'
|
||||||
'mac-cmd-key' |
|
| 'logout'
|
||||||
'pencil' |
|
| 'mac-cmd-key'
|
||||||
'plus' |
|
| 'pencil'
|
||||||
'plus-circle' |
|
| 'plus'
|
||||||
'refresh' |
|
| 'plus-circle'
|
||||||
'save' |
|
| 'refresh'
|
||||||
'terminal' |
|
| 'save'
|
||||||
'trash' |
|
| 'terminal'
|
||||||
'x'
|
| 'trash'
|
||||||
|
| 'x'
|
||||||
|
|
||||||
const Svg = ({ name, className: className2 = '', strokeWidth = 2 }: {
|
const Svg = ({
|
||||||
|
name,
|
||||||
|
className: className2 = '',
|
||||||
|
strokeWidth = 2,
|
||||||
|
}: {
|
||||||
name: SvgNames
|
name: SvgNames
|
||||||
className?: string
|
className?: string
|
||||||
strokeWidth?: number
|
strokeWidth?: number
|
||||||
}) => {
|
}) => {
|
||||||
const svgs: {[name in SvgNames]: React.ReactElement} = {
|
const svgs: { [name in SvgNames]: React.ReactElement } = {
|
||||||
'arrow-down': (
|
'arrow-down': (
|
||||||
<svg
|
<svg
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
@@ -60,40 +65,33 @@ const Svg = ({ name, className: className2 = '', strokeWidth = 2 }: {
|
|||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
),
|
),
|
||||||
'camera': (
|
camera: (
|
||||||
<svg
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 21">
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
fill="none"
|
|
||||||
viewBox="0 0 24 21">
|
|
||||||
<path
|
<path
|
||||||
d="M6 5H4C2.34315 5 1 6.34315 1 8V17C1 18.6569 2.34315 20 4 20H20C21.6569 20 23 18.6569 23 17V8C23 6.34315 21.6569 5 20 5H18"
|
d="M6 5H4C2.34315 5 1 6.34315 1 8V17C1 18.6569 2.34315 20 4 20H20C21.6569 20 23 18.6569 23 17V8C23 6.34315 21.6569 5 20 5H18"
|
||||||
stroke="currentColor"
|
stroke="currentColor"
|
||||||
strokeWidth="2"
|
strokeWidth="2"
|
||||||
strokeLinecap="round"/>
|
strokeLinecap="round"
|
||||||
<circle
|
/>
|
||||||
cx="12"
|
<circle cx="12" cy="11" r="5" stroke="currentColor" strokeWidth="2" />
|
||||||
cy="11"
|
|
||||||
r="5"
|
|
||||||
stroke="currentColor"
|
|
||||||
strokeWidth="2"/>
|
|
||||||
<path
|
<path
|
||||||
d="M16 2.68641C14.8716 1.61443 13.5582 1 12.1563 1C10.6229 1 9.19532 1.7351 8 3"
|
d="M16 2.68641C14.8716 1.61443 13.5582 1 12.1563 1C10.6229 1 9.19532 1.7351 8 3"
|
||||||
stroke="currentColor"
|
stroke="currentColor"
|
||||||
strokeWidth="2"
|
strokeWidth="2"
|
||||||
strokeLinecap="round"/>
|
strokeLinecap="round"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
),
|
||||||
|
checkmark: (
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 21 20" fill="none">
|
||||||
|
<path
|
||||||
|
fillRule="evenodd"
|
||||||
|
clipRule="evenodd"
|
||||||
|
d="M10.3438 19.6875C15.7803 19.6875 20.1875 15.2803 20.1875 9.84375C20.1875 4.4072 15.7803 0 10.3438 0C4.9072 0 0.5 4.4072 0.5 9.84375C0.5 15.2803 4.9072 19.6875 10.3438 19.6875ZM15.3321 6.5547C15.6384 6.09517 15.5142 5.4743 15.0547 5.16795C14.5952 4.8616 13.9743 4.98577 13.6679 5.4453L9.34457 11.9304L7.20711 9.79289C6.81658 9.40237 6.18342 9.40237 5.79289 9.79289C5.40237 10.1834 5.40237 10.8166 5.79289 11.2071L8.79289 14.2071C9.00474 14.419 9.3004 14.5247 9.59854 14.4951C9.89667 14.4656 10.1659 14.304 10.3321 14.0547L15.3321 6.5547Z"
|
||||||
|
fill="currentColor"
|
||||||
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
),
|
),
|
||||||
'checkmark': (
|
|
||||||
<svg
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
viewBox="0 0 21 20"
|
|
||||||
fill="none">
|
|
||||||
<path
|
|
||||||
fillRule="evenodd"
|
|
||||||
clipRule="evenodd"
|
|
||||||
d="M10.3438 19.6875C15.7803 19.6875 20.1875 15.2803 20.1875 9.84375C20.1875 4.4072 15.7803 0 10.3438 0C4.9072 0 0.5 4.4072 0.5 9.84375C0.5 15.2803 4.9072 19.6875 10.3438 19.6875ZM15.3321 6.5547C15.6384 6.09517 15.5142 5.4743 15.0547 5.16795C14.5952 4.8616 13.9743 4.98577 13.6679 5.4453L9.34457 11.9304L7.20711 9.79289C6.81658 9.40237 6.18342 9.40237 5.79289 9.79289C5.40237 10.1834 5.40237 10.8166 5.79289 11.2071L8.79289 14.2071C9.00474 14.419 9.3004 14.5247 9.59854 14.4951C9.89667 14.4656 10.1659 14.304 10.3321 14.0547L15.3321 6.5547Z"
|
|
||||||
fill="currentColor"/>
|
|
||||||
</svg>),
|
|
||||||
'chevron-down': (
|
'chevron-down': (
|
||||||
<svg
|
<svg
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
@@ -325,12 +323,8 @@ const Svg = ({ name, className: className2 = '', strokeWidth = 2 }: {
|
|||||||
</svg>
|
</svg>
|
||||||
),
|
),
|
||||||
'mac-cmd-key': (
|
'mac-cmd-key': (
|
||||||
<svg
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 220 220">
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
<path
|
||||||
viewBox="0 0 220 220"
|
|
||||||
>
|
|
||||||
<
|
|
||||||
path
|
|
||||||
fill="currentColor"
|
fill="currentColor"
|
||||||
d="M57.254,121.064c-17.735,0-31.45,4.723-40.765,14.035c-19.39,19.391-19.39,50.943,0,70.334 C25.884,214.828,38.375,220,51.66,220c0,0,0,0,0,0c13.284,0,25.774-5.172,35.168-14.566c11.069-11.07,14.037-27.061,14.041-41.016 c0.001-3.531,0.017-10.09,0.017-10.09h18.227c0,0,0.016,6.559,0.018,10.09c0.004,13.955,2.971,29.945,14.041,41.016 C142.565,214.828,155.056,220,168.34,220c13.285,0,25.775-5.172,35.17-14.566c19.391-19.391,19.391-50.943,0-70.334 c-9.314-9.312-23.029-14.035-40.764-14.035c-3.602,0-10.346,0.02-10.346,0.02c0-0.932,0-22.178,0-22.178s6.744,0.029,10.346,0.029 c17.734,0,31.449-4.721,40.762-14.033c19.391-19.392,19.391-50.943,0.002-70.334C194.113,5.174,181.624,0,168.34,0 c-13.285,0-25.773,5.174-35.168,14.566c-10.67,10.672-13.812,25.914-14.029,39.498l-0.193,11.609H101.05l-0.192-11.609 c-0.217-13.584-3.359-28.826-14.03-39.498C77.434,5.174,64.944,0,51.66,0C38.376,0,25.886,5.174,16.49,14.568 C-2.899,33.959-2.899,65.51,16.491,84.902c9.314,9.313,23.028,14.033,40.762,14.033c3.601,0,10.346-0.029,10.346-0.029 s0,21.246,0,22.178C67.6,121.084,60.855,121.064,57.254,121.064z M154.328,35.587c3.727-3.726,8.683-5.779,13.954-5.779 s10.229,2.053,13.957,5.781c7.692,7.693,7.692,20.213-0.002,27.906c-3.384,3.385-10.327,5.248-19.549,5.248 c-4.6,0-14.107,0-14.107,0v-8.688C148.581,60.056,147.566,41.495,154.328,35.587z M148.581,159.945v-8.688c0,0,9.508,0,14.107,0 c9.222,0,16.165,1.863,19.549,5.248c7.694,7.693,7.694,20.213,0.002,27.906c-3.729,3.729-8.686,5.781-13.957,5.781 s-10.228-2.053-13.954-5.779C147.566,178.506,148.581,159.945,148.581,159.945z M93.75,93.75h32.5v32.5h-32.5V93.75z M57.312,68.743 c-9.222,0-16.165-1.863-19.549-5.248c-7.694-7.693-7.694-20.213-0.002-27.906c3.729-3.729,8.686-5.781,13.957-5.781 s10.228,2.053,13.954,5.779c6.762,5.908,5.747,24.469,5.747,24.469v8.688C71.419,68.743,61.911,68.743,57.312,68.743z M71.419,151.258v8.688c0,0,1.015,18.561-5.747,24.469c-3.727,3.727-8.683,5.779-13.954,5.779s-10.229-2.053-13.957-5.781 c-7.692-7.693-7.692-20.213,0.002-27.906c3.384-3.385,10.327-5.248,19.549-5.248C61.911,151.258,71.419,151.258,71.419,151.258z"
|
d="M57.254,121.064c-17.735,0-31.45,4.723-40.765,14.035c-19.39,19.391-19.39,50.943,0,70.334 C25.884,214.828,38.375,220,51.66,220c0,0,0,0,0,0c13.284,0,25.774-5.172,35.168-14.566c11.069-11.07,14.037-27.061,14.041-41.016 c0.001-3.531,0.017-10.09,0.017-10.09h18.227c0,0,0.016,6.559,0.018,10.09c0.004,13.955,2.971,29.945,14.041,41.016 C142.565,214.828,155.056,220,168.34,220c13.285,0,25.775-5.172,35.17-14.566c19.391-19.391,19.391-50.943,0-70.334 c-9.314-9.312-23.029-14.035-40.764-14.035c-3.602,0-10.346,0.02-10.346,0.02c0-0.932,0-22.178,0-22.178s6.744,0.029,10.346,0.029 c17.734,0,31.449-4.721,40.762-14.033c19.391-19.392,19.391-50.943,0.002-70.334C194.113,5.174,181.624,0,168.34,0 c-13.285,0-25.773,5.174-35.168,14.566c-10.67,10.672-13.812,25.914-14.029,39.498l-0.193,11.609H101.05l-0.192-11.609 c-0.217-13.584-3.359-28.826-14.03-39.498C77.434,5.174,64.944,0,51.66,0C38.376,0,25.886,5.174,16.49,14.568 C-2.899,33.959-2.899,65.51,16.491,84.902c9.314,9.313,23.028,14.033,40.762,14.033c3.601,0,10.346-0.029,10.346-0.029 s0,21.246,0,22.178C67.6,121.084,60.855,121.064,57.254,121.064z M154.328,35.587c3.727-3.726,8.683-5.779,13.954-5.779 s10.229,2.053,13.957,5.781c7.692,7.693,7.692,20.213-0.002,27.906c-3.384,3.385-10.327,5.248-19.549,5.248 c-4.6,0-14.107,0-14.107,0v-8.688C148.581,60.056,147.566,41.495,154.328,35.587z M148.581,159.945v-8.688c0,0,9.508,0,14.107,0 c9.222,0,16.165,1.863,19.549,5.248c7.694,7.693,7.694,20.213,0.002,27.906c-3.729,3.729-8.686,5.781-13.957,5.781 s-10.228-2.053-13.954-5.779C147.566,178.506,148.581,159.945,148.581,159.945z M93.75,93.75h32.5v32.5h-32.5V93.75z M57.312,68.743 c-9.222,0-16.165-1.863-19.549-5.248c-7.694-7.693-7.694-20.213-0.002-27.906c3.729-3.729,8.686-5.781,13.957-5.781 s10.228,2.053,13.954,5.779c6.762,5.908,5.747,24.469,5.747,24.469v8.688C71.419,68.743,61.911,68.743,57.312,68.743z M71.419,151.258v8.688c0,0,1.015,18.561-5.747,24.469c-3.727,3.727-8.683,5.779-13.954,5.779s-10.229-2.053-13.957-5.781 c-7.692-7.693-7.692-20.213,0.002-27.906c3.384-3.385,10.327-5.248,19.549-5.248C61.911,151.258,71.419,151.258,71.419,151.258z"
|
||||||
/>
|
/>
|
||||||
@@ -382,15 +376,13 @@ const Svg = ({ name, className: className2 = '', strokeWidth = 2 }: {
|
|||||||
</svg>
|
</svg>
|
||||||
),
|
),
|
||||||
refresh: (
|
refresh: (
|
||||||
<svg
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 14 17" fill="none">
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
viewBox="0 0 14 17"
|
|
||||||
fill="none">
|
|
||||||
<path
|
<path
|
||||||
d="M13 9.9271C13 13.189 10.3137 15.8333 7 15.8333C3.68629 15.8333 1 13.189 1 9.9271C1 6.66517 3.68629 4.02085 7 4.02085C9 4.02085 10.986 4.99917 12 5.77084M12 5.77084L8.33333 7.08334M12 5.77084L10.6667 1.83334"
|
d="M13 9.9271C13 13.189 10.3137 15.8333 7 15.8333C3.68629 15.8333 1 13.189 1 9.9271C1 6.66517 3.68629 4.02085 7 4.02085C9 4.02085 10.986 4.99917 12 5.77084M12 5.77084L8.33333 7.08334M12 5.77084L10.6667 1.83334"
|
||||||
stroke="currentColor"
|
stroke="currentColor"
|
||||||
strokeWidth="2"
|
strokeWidth="2"
|
||||||
strokeLinecap="round"/>
|
strokeLinecap="round"
|
||||||
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
),
|
),
|
||||||
save: (
|
save: (
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { toast } from '@redwoodjs/web/toast'
|
import { toast } from '@redwoodjs/web/toast'
|
||||||
|
|
||||||
function fallbackCopyTextToClipboard(text: string) {
|
function fallbackCopyTextToClipboard(text: string) {
|
||||||
var textArea = document.createElement('textarea')
|
const textArea = document.createElement('textarea')
|
||||||
textArea.value = text
|
textArea.value = text
|
||||||
|
|
||||||
// Avoid scrolling to bottom
|
// Avoid scrolling to bottom
|
||||||
@@ -14,8 +14,8 @@ function fallbackCopyTextToClipboard(text: string) {
|
|||||||
textArea.select()
|
textArea.select()
|
||||||
|
|
||||||
try {
|
try {
|
||||||
var successful = document.execCommand('copy')
|
const successful = document.execCommand('copy')
|
||||||
var msg = successful ? 'successful' : 'unsuccessful'
|
const msg = successful ? 'successful' : 'unsuccessful'
|
||||||
console.log('Fallback: Copying text command was ' + msg)
|
console.log('Fallback: Copying text command was ' + msg)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Fallback: Oops, unable to copy', err)
|
console.error('Fallback: Oops, unable to copy', err)
|
||||||
@@ -24,11 +24,12 @@ function fallbackCopyTextToClipboard(text: string) {
|
|||||||
document.body.removeChild(textArea)
|
document.body.removeChild(textArea)
|
||||||
}
|
}
|
||||||
|
|
||||||
const clipboardSuccessToast = (text: string) => toast.success(() => (
|
const clipboardSuccessToast = (text: string) =>
|
||||||
<div className="overflow-hidden">
|
toast.success(() => (
|
||||||
<p>link added to clipboard.</p>
|
<div className="overflow-hidden">
|
||||||
</div>
|
<p>link added to clipboard.</p>
|
||||||
))
|
</div>
|
||||||
|
))
|
||||||
|
|
||||||
const makeClipboardCopier = (success: Function) => (text: string) => {
|
const makeClipboardCopier = (success: Function) => (text: string) => {
|
||||||
if (!navigator.clipboard) {
|
if (!navigator.clipboard) {
|
||||||
|
|||||||
Reference in New Issue
Block a user