Merge origin/main

This commit is contained in:
Frank Johnson
2021-09-11 06:05:17 -04:00
15 changed files with 2894 additions and 2593 deletions

View File

@@ -20,8 +20,12 @@ const makeRequest = (route, port) => [
})
res.status(data.statusCode)
res.setHeader('Content-Type', 'application/javascript')
res.setHeader('Content-Encoding', 'gzip')
res.send(Buffer.from(data.body, 'base64'))
if (data.headers && data.headers['Content-Encoding'] === 'gzip') {
res.setHeader('Content-Encoding', 'gzip')
res.send(Buffer.from(data.body, 'base64'))
} else {
res.send(Buffer.from(data.body, 'base64'))
}
} catch (e) {
res.status(500)
res.send()

View File

@@ -19,9 +19,9 @@ export const runCQ = async ({
`--outputopts "deflection:${deflection};angularDeflection:${deflection};"`,
].join(' ')
console.log('command', command)
let consoleMessage = ''
try {
const consoleMessage = await runCommand(command, 30000)
consoleMessage = await runCommand(command, 30000)
await writeFiles(
[
{
@@ -36,10 +36,11 @@ export const runCQ = async ({
)
await runCommand(
`cat ${stlPath} /var/task/cadhub-concat-split /tmp/${tempFile}/metadata.json | gzip > ${fullPath}`,
15000
15000,
true
)
return { consoleMessage, fullPath }
} catch (error) {
return { error, fullPath }
return { error: consoleMessage, fullPath }
}
}

View File

@@ -23,7 +23,11 @@ export async function writeFiles(
return tempFile
}
export async function runCommand(command, timeout = 5000): Promise<string> {
export async function runCommand(
command,
timeout = 5000,
shouldRejectStdErr = false
): Promise<string> {
return new Promise((resolve, reject) => {
exec(command, (error, stdout, stderr) => {
if (error) {
@@ -35,6 +39,10 @@ export async function runCommand(command, timeout = 5000): Promise<string> {
}
if (stderr) {
console.log(`stderr: ${stderr}`)
if (shouldRejectStdErr) {
reject(stderr)
return
}
resolve(stderr)
return
}
@@ -105,7 +113,8 @@ export async function storeAssetAndReturnUrl({
if (error) {
const response = {
statusCode: 400,
body: JSON.stringify({ error, fullPath }),
body: Buffer.from(JSON.stringify({ error, fullPath })).toString('base64'),
isBase64Encoded: true,
}
callback(null, response)
return
@@ -119,7 +128,10 @@ export async function storeAssetAndReturnUrl({
console.log('read file error', e)
const response = {
statusCode: 400,
body: JSON.stringify({ error: consoleMessage, fullPath }),
body: Buffer.from(
JSON.stringify({ error: consoleMessage, fullPath })
).toString('base64'),
isBase64Encoded: true,
}
callback(null, response)
return

View File

@@ -33,4 +33,4 @@
"node": ">=14",
"yarn": ">=1.15"
}
}
}

View File

@@ -30,6 +30,7 @@
"cloudinary-react": "^1.6.7",
"get-active-classes": "^0.0.11",
"gotrue-js": "^0.9.27",
"hotkeys-js": "^3.8.7",
"html-to-image": "^1.7.0",
"lodash": "^4.17.21",
"netlify-identity-widget": "^1.9.1",
@@ -40,6 +41,7 @@
"react-dropzone": "^11.2.1",
"react-ga": "^3.3.0",
"react-helmet": "^6.1.0",
"react-hotkeys-hook": "^3.4.0",
"react-image-crop": "^8.6.6",
"react-mosaic-component": "^5.0.0",
"react-tabs": "^3.2.2",

View File

@@ -0,0 +1,68 @@
import { createContext, useContext } from 'react'
import { makeStyles } from '@material-ui/core/styles'
import Dialog from '@material-ui/core/Dialog'
import { editorMenuConfig } from './menuConfig'
const useStyles = makeStyles({
root: {
transform: `translate3d(0,0,50px)`,
},
})
interface ShortcutsModalContextType {
open: boolean
toggleOpen: () => any
}
export const ShortcutsModalContext = createContext<ShortcutsModalContextType>({
open: false,
toggleOpen: () => {},
})
export function useShortcutsModalContext() {
return useContext(ShortcutsModalContext)
}
const AllShortcutsModal = () => {
const classes = useStyles()
const { open, toggleOpen } = useShortcutsModalContext()
return (
<>
<Dialog
open={open}
onClose={() => toggleOpen()}
className={classes.root + ' bg-transparent'}
PaperProps={{
style: {
backgroundColor: 'transparent',
},
}}
>
<div className="bg-ch-gray-700 font-fira-sans shadow-lg text-ch-gray-300 p-4">
<h2 className="text-2xl mb-4">All Shortcuts</h2>
{editorMenuConfig
.filter((menu) => menu.items.length)
.map((menu) => (
<section key={'allshortcuts-' + menu.name} className="my-6">
<h3 className="text-xl border-b-2 pb-2 mb-2">{menu.label}</h3>
{menu.items.map((item) => (
<div
className="flex gap-16 justify-between"
key={'allshortcuts-' + menu.name + '-' + item.label}
>
<p>{item.label}</p>
<span className="text-right font-fira-code text-ch-gray-400">
{item.shortcutLabel}
</span>
</div>
))}
</section>
))}
</div>
</Dialog>
</>
)
}
export default AllShortcutsModal

View File

@@ -0,0 +1,71 @@
import { Menu } from '@headlessui/react'
import { useHotkeys } from 'react-hotkeys-hook'
export function DropdownItem({ config, state, thunkDispatch }) {
useHotkeys(config.shortcut, handleClick)
function handleClick(e) {
e.preventDefault()
config.callback(e, { state, thunkDispatch })
}
return (
<Menu.Item>
{({ active }) => (
<button
className={`${
active && 'bg-gray-600'
} px-2 py-1 flex justify-between`}
onClick={handleClick}
>
{config.label}
{config.shortcutLabel && (
<span className="text-gray-400 pl-6 text-right">
{config.shortcutLabel}
</span>
)}
</button>
)}
</Menu.Item>
)
}
export function Dropdown({
label,
disabled,
children,
}: {
label: string
disabled: boolean
children: React.ReactNode
}) {
return (
<div className="relative">
<Menu>
{({ open }) => (
<>
<Menu.Button
className={
'text-gray-100' +
(disabled ? ' text-gray-400 cursor-not-allowed' : '')
}
disabled={disabled}
>
{label}
</Menu.Button>
{children && (
<Menu.Items
static
className={
(open ? '' : 'hidden ') +
'absolute flex flex-col mt-4 bg-ch-gray-760 rounded text-gray-100 overflow-hidden whitespace-nowrap border border-ch-gray-700'
}
>
{children}
</Menu.Items>
)}
</>
)}
</Menu>
</div>
)
}

View File

@@ -1,133 +1,51 @@
import { Menu } from '@headlessui/react'
import { useIdeContext } from 'src/helpers/hooks/useIdeContext'
import Svg from 'src/components/Svg/Svg'
import { useRender } from 'src/components/IdeWrapper/useRender'
import { makeStlDownloadHandler, PullTitleFromFirstLine } from './helpers'
import { useSaveCode } from 'src/components/IdeWrapper/useSaveCode'
import CadPackage from 'src/components/CadPackage/CadPackage'
import { editorMenuConfig } from './menuConfig'
import AllShortcutsModal from './AllShortcutsModal'
import { Dropdown } from './Dropdowns'
const EditorMenu = () => {
const handleRender = useRender()
const saveCode = useSaveCode()
const { state, thunkDispatch } = useIdeContext()
const onRender = () => {
handleRender()
saveCode({ code: state.code })
}
const handleStlDownload = makeStlDownloadHandler({
type: state.objectData?.type,
ideType: state.ideType,
geometry: state.objectData?.data,
quality: state.objectData?.quality,
fileName: PullTitleFromFirstLine(state.code || ''),
thunkDispatch,
})
return (
<div className="flex justify-between bg-ch-gray-760 text-gray-100">
<div className="flex items-center h-9 w-full cursor-grab">
<div className=" text-ch-gray-760 bg-ch-gray-300 cursor-grab px-1.5 h-full flex items-center">
<Svg name="drag-grid" className="w-4 p-px" />
</div>
<div className="grid grid-flow-col-dense gap-6 px-5">
<FileDropdown
handleRender={onRender}
handleStlDownload={handleStlDownload}
/>
<button className="cursor-not-allowed" disabled>
Edit
<>
<div className="flex justify-between bg-ch-gray-760 text-gray-100">
<div className="flex items-center h-9 w-full cursor-grab">
<div className=" text-ch-gray-760 bg-ch-gray-300 cursor-grab px-2 h-full flex items-center">
<Svg name="drag-grid" className="w-4 p-px" />
</div>
<div className="grid grid-flow-col-dense gap-6 px-5">
{editorMenuConfig.map((menu) => (
<Dropdown
label={menu.label}
disabled={menu.disabled}
key={menu.label + '-dropdown'}
>
{menu.items.map((itemConfig) => (
<itemConfig.component
state={state}
thunkDispatch={thunkDispatch}
config={itemConfig}
key={menu.label + '-' + itemConfig.label}
/>
))}
</Dropdown>
))}
</div>
<button
className="text-ch-gray-300 h-full cursor-not-allowed"
aria-label="editor settings"
disabled
>
<Svg name="gear" className="w-6 p-px" />
</button>
<ViewDropdown
handleLayoutReset={() => thunkDispatch({ type: 'resetLayout' })}
/>
</div>
<button
className="text-ch-gray-300 h-full cursor-not-allowed"
aria-label="editor settings"
disabled
>
<Svg name="gear" className="w-6 p-px" />
</button>
<CadPackage cadPackage={state.ideType} className="px-3" />
</div>
<CadPackage cadPackage={state.ideType} className="px-3" />
</div>
<AllShortcutsModal />
</>
)
}
export default EditorMenu
function FileDropdown({ handleRender, handleStlDownload }) {
return (
<Dropdown name="File">
<Menu.Item>
{({ active }) => (
<button
className={`${active && 'bg-gray-600'} px-2 py-1`}
onClick={handleRender}
>
Save &amp; Render{' '}
<span className="text-gray-400 pl-4">
{/(Mac|iPhone|iPod|iPad)/i.test(navigator.platform) ? (
<>
<Svg
name="mac-cmd-key"
className="h-3 w-3 inline-block text-left"
/>
S
</>
) : (
'Ctrl S'
)}
</span>
</button>
)}
</Menu.Item>
<Menu.Item>
{({ active }) => (
<button
className={`${active && 'bg-gray-600'} px-2 py-1 text-left`}
onClick={handleStlDownload}
>
Download STL
</button>
)}
</Menu.Item>
</Dropdown>
)
}
function ViewDropdown({ handleLayoutReset }) {
return (
<Dropdown name="View">
<Menu.Item>
{({ active }) => (
<button
className={`${active && 'bg-gray-600'} px-2 py-1`}
onClick={handleLayoutReset}
>
Reset layout
</button>
)}
</Menu.Item>
</Dropdown>
)
}
function Dropdown({
name,
children,
}: {
name: string
children: React.ReactNode
}) {
return (
<div className="relative">
<Menu>
<Menu.Button className="text-gray-100">{name}</Menu.Button>
<Menu.Items className="absolute flex flex-col mt-4 bg-ch-gray-760 rounded text-gray-100 overflow-hidden whitespace-nowrap border border-ch-gray-700">
{children}
</Menu.Items>
</Menu>
</div>
)
}

View File

@@ -0,0 +1,109 @@
import React from 'react'
import { useRender } from 'src/components/IdeWrapper/useRender'
import { makeStlDownloadHandler, PullTitleFromFirstLine } from './helpers'
import { useSaveCode } from 'src/components/IdeWrapper/useSaveCode'
import { DropdownItem } from './Dropdowns'
import { useShortcutsModalContext } from './AllShortcutsModal'
export function cmdOrCtrl() {
return /(Mac|iPhone|iPod|iPad)/i.test(navigator.platform) ? '⌘' : 'Ctrl'
}
const fileMenuConfig: EditorMenuConfig = {
name: 'file',
label: 'File',
disabled: false,
items: [
{
label: 'Save & Render',
shortcut: 'ctrl+s, command+s',
shortcutLabel: cmdOrCtrl() + ' S',
component: (props) => {
const { state, config } = props
const handleRender = useRender()
const saveCode = useSaveCode()
function onRender(e) {
e.preventDefault()
handleRender()
saveCode({ code: state.code })
}
config.callback = onRender
return <DropdownItem {...props} />
},
},
{
label: 'Download STL',
shortcut: 'ctrl+shift+d, command+shift+d',
shortcutLabel: cmdOrCtrl() + ' Shift D',
component: (props) => {
const { state, thunkDispatch, config } = props
const handleStlDownload = makeStlDownloadHandler({
type: state.objectData?.type,
ideType: state.ideType,
geometry: state.objectData?.data,
quality: state.objectData?.quality,
fileName: PullTitleFromFirstLine(state.code || ''),
thunkDispatch,
})
config.callback = handleStlDownload
return <DropdownItem {...props} />
},
},
],
}
const editMenuConfig = {
name: 'edit',
label: 'Edit',
disabled: true,
items: [],
}
const viewMenuConfig = {
name: 'view',
label: 'View',
disabled: false,
items: [
{
label: 'Reset layout',
shortcut: 'ctrl+shift+r',
shortcutLabel: 'Ctrl Shift R',
component: (props) => {
const { config, thunkDispatch } = props
config.callback = () => thunkDispatch({ type: 'resetLayout' })
return <DropdownItem {...props} />
},
},
{
label: 'All shortcuts',
shortcut: 'ctrl+shift+/',
shortcutLabel: 'Ctrl Shift /',
component: (props) => {
const { config } = props
const { toggleOpen } = useShortcutsModalContext()
config.callback = toggleOpen
return <DropdownItem {...props} />
},
},
],
}
export const editorMenuConfig = [fileMenuConfig, editMenuConfig, viewMenuConfig]
export interface EditorMenuItemConfig {
label: string
shortcut: string
shortcutLabel: React.ReactElement | string
component: (props: any) => React.ReactElement
}
export interface EditorMenuConfig {
name: string
label: string
disabled: boolean
items: Array<EditorMenuItemConfig>
}

View File

@@ -0,0 +1,98 @@
import { ReactFragment, ReactNode, useEffect, useReducer, useState } from 'react'
import { SvgNames } from 'src/components/Svg/Svg'
interface SidebarConfigType {
name: string,
icon: SvgNames,
disabled: boolean,
panel: ReactFragment | null,
}
export const sidebarTopConfig : SidebarConfigType[] = [
{
name: 'Files',
icon: 'files',
disabled: false,
panel: <article className="px-2 py-4">
<p><em>Coming Soon</em></p>
<hr className="my-4"/>
<p>
We're working on multi-file support in tandem with the GitHub integration.
</p>
</article>,
},
{
name: 'GitHub',
icon: 'github',
disabled: false,
panel: <article className="px-2 py-4">
<p><em>Coming Soon</em></p>
<hr className="my-4"/>
<p>
This integration will allow you to sync a project with a GitHub repo and push changes back to it as a commit!
</p>
</article>,
},
{
name: 'Visibility',
icon: 'eye',
disabled: true,
panel: null,
},
]
const settingsConfig = [
{
title: "Editor",
name: "editor",
open: false,
content: <p className="p-2">This text will go in a details element!</p>,
},
{
title: "Viewer",
name: "viewer",
open: false,
content: <p className="p-2">This text will go in a details element!</p>
},
{
title: "Console",
name: "console",
open: false,
content: <p className="p-2">This text will go in a details element!</p>
},
]
export const sidebarBottomConfig : SidebarConfigType[] = [
{
name: 'Settings',
icon: 'gear',
disabled: false,
panel: <article className="">
{ settingsConfig.map(item => (
<details key={'settings-tray-'+item.name}>
<summary className="px-2 py-1 bg-ch-pink-800 bg-opacity-20 my-px">{ item.title }</summary>
{ item.content }
</details>
))}
</article>,
},
]
export const sidebarCombinedConfig = [
...sidebarTopConfig,
...sidebarBottomConfig,
]
function SettingsMenu() {
console.log('hello?', settingsConfig)
return (
<article className="px-2 py-4">
{ settingsConfig.map(item => (
<details key={'settings-tray-'+item.name}>
<summary>{ item.title }</summary>
{ item.content }
</details>
))}
</article>
)
}

View File

@@ -8,6 +8,7 @@ import Svg from 'src/components/Svg/Svg'
import { useIdeInit } from 'src/components/EncodedUrl/helpers'
import { useIdeContext } from 'src/helpers/hooks/useIdeContext'
import { useSaveCode } from 'src/components/IdeWrapper/useSaveCode'
import { ShortcutsModalContext } from 'src/components/EditorMenu/AllShortcutsModal'
interface Props {
cadPackage: string
@@ -22,9 +23,15 @@ const IdeWrapper = ({ cadPackage }: Props) => {
saveCode({ code: state.code })
}
useIdeInit(cadPackage, project?.code || state?.code)
const [shortcutModalOpen, setShortcutModalOpen] = useState(false)
const shortcutModalContextValues = {
open: shortcutModalOpen,
toggleOpen: () => setShortcutModalOpen(!shortcutModalOpen),
}
return (
<div className="h-full flex flex-col">
<ShortcutsModalContext.Provider value={shortcutModalContextValues}>
<nav className="flex">
<IdeHeader handleRender={onRender} />
</nav>
@@ -36,6 +43,7 @@ const IdeWrapper = ({ cadPackage }: Props) => {
<IdeContainer />
</div>
</div>
</ShortcutsModalContext.Provider>
</div>
)
}

View File

@@ -350,7 +350,8 @@ const Svg = ({
fillRule="evenodd"
clipRule="evenodd"
fill="currentColor"
d="M18.5596 32.6494L14.4414 32.6493L13.3126 27.3566L11.1836 26.4752L6.6429 29.4195L3.73084 26.5076L6.75991 22.0513L5.79279 19.838L0.500103 18.7091L0.5 14.591L5.79292 13.5818L6.75988 11.2476L3.73082 6.79131L6.64287 3.8794L11.1836 6.82369L13.3115 5.9422L14.4403 0.649517L18.5585 0.649414L19.5677 5.94233L21.9012 6.90846L26.3575 3.87939L29.2694 6.79145L26.3251 11.3321L27.2071 13.5807L32.5 14.5899L32.4999 18.708L27.2072 19.8369L26.3251 21.9667L29.2694 26.5074L26.3575 29.4195L21.9012 26.3904L19.5688 27.3565L18.5596 32.6494ZM16.5001 22.2987C13.3792 22.2987 10.8492 19.7687 10.8492 16.6478C10.8492 13.5269 13.3792 10.997 16.5001 10.997C19.621 10.997 22.151 13.5269 22.151 16.6478C22.151 19.7687 19.621 22.2987 16.5001 22.2987Z"/>
d="M18.5596 32.6494L14.4414 32.6493L13.3126 27.3566L11.1836 26.4752L6.6429 29.4195L3.73084 26.5076L6.75991 22.0513L5.79279 19.838L0.500103 18.7091L0.5 14.591L5.79292 13.5818L6.75988 11.2476L3.73082 6.79131L6.64287 3.8794L11.1836 6.82369L13.3115 5.9422L14.4403 0.649517L18.5585 0.649414L19.5677 5.94233L21.9012 6.90846L26.3575 3.87939L29.2694 6.79145L26.3251 11.3321L27.2071 13.5807L32.5 14.5899L32.4999 18.708L27.2072 19.8369L26.3251 21.9667L29.2694 26.5074L26.3575 29.4195L21.9012 26.3904L19.5688 27.3565L18.5596 32.6494ZM16.5001 22.2987C13.3792 22.2987 10.8492 19.7687 10.8492 16.6478C10.8492 13.5269 13.3792 10.997 16.5001 10.997C19.621 10.997 22.151 13.5269 22.151 16.6478C22.151 19.7687 19.621 22.2987 16.5001 22.2987Z"
/>
</svg>
),
github: (

View File

@@ -43,13 +43,10 @@ export const render: DefaultKernelExport['render'] = async ({
}
const blob = await response.blob()
const text = await new Response(blob).text()
const { consoleMessage, customizerParams, type } = splitGziped(text)
const { consoleMessage } = splitGziped(text)
return createHealthyResponse({
type: type !== 'stl' ? 'png' : 'geometry',
data:
type !== 'stl'
? blob
: await stlToGeometry(window.URL.createObjectURL(blob)),
type: 'geometry',
data: await stlToGeometry(window.URL.createObjectURL(blob)),
consoleMessage,
date: new Date(),
})

View File

@@ -4,7 +4,7 @@ import { CadhubParams } from 'src/components/Customizer/customizerConverter'
export const lambdaBaseURL =
process.env.CAD_LAMBDA_BASE_URL ||
'https://2inlbple1b.execute-api.us-east-1.amazonaws.com/prod2'
'https://oxt2p7ddgj.execute-api.us-east-1.amazonaws.com/prod'
export const stlToGeometry = (url) =>
new Promise((resolve, reject) => {

File diff suppressed because it is too large Load Diff