Skip to content

Commit

Permalink
Various quality of life improvements (#112)
Browse files Browse the repository at this point in the history
  • Loading branch information
mariusandra authored Dec 30, 2024
1 parent 151621a commit f109ec0
Show file tree
Hide file tree
Showing 14 changed files with 255 additions and 153 deletions.
14 changes: 3 additions & 11 deletions backend/app/codegen/scene_nim.py
Original file line number Diff line number Diff line change
Expand Up @@ -360,17 +360,9 @@ def process_app_run(self, node):
app_config_pairs: list[list[str]] = []
for key, value in app_config.items():
if key not in field_types_for_node:
message = f'- ERROR: When generating scene {self.scene_id}. Config key "{key}" not found for app "{name}", node "{node_id}"'
try:
from app.models.log import new_log as log
log(
self.frame.id,
"stderr",
message,
)
continue
except Exception:
raise ValueError(message)
# message = f'- ERROR: When generating scene {self.scene_id}. Config key "{key}" not found for app "{name}", node "{node_id}"'
# raise ValueError(message)
continue
type = field_types_for_node[key]

app_config_pairs.append(
Expand Down
15 changes: 15 additions & 0 deletions frontend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
"kea": "^3.1.6",
"kea-forms": "^3.2.0",
"kea-loaders": "^3.0.1",
"kea-localstorage": "^3.1.0",
"kea-router": "^3.1.4",
"kea-subscriptions": "^3.0.0",
"react": "^18.2.0",
Expand Down
3 changes: 2 additions & 1 deletion frontend/src/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@ import { App } from './scenes/App'
import './index.css'
import { resetContext } from 'kea'
import { subscriptionsPlugin } from 'kea-subscriptions'
import { localStoragePlugin } from 'kea-localstorage'

resetContext({
plugins: [subscriptionsPlugin],
plugins: [subscriptionsPlugin, localStoragePlugin()],
})

ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(<App />)
10 changes: 7 additions & 3 deletions frontend/src/models/framesModel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ export const framesModel = kea<framesModelType>([
actions({
addFrame: (frame: FrameType) => ({ frame }),
loadFrame: (id: number) => ({ id }),
redeployFrame: (id: number) => ({ id }),
deployFrame: (id: number) => ({ id }),
stopFrame: (id: number) => ({ id }),
restartFrame: (id: number) => ({ id }),
renderFrame: (id: number) => ({ id }),
deleteFrame: (id: number) => ({ id }),
Expand Down Expand Up @@ -104,8 +105,11 @@ export const framesModel = kea<framesModelType>([
renderFrame: async ({ id }) => {
await apiFetch(`/api/frames/${id}/event/render`, { method: 'POST' })
},
redeployFrame: async ({ id }) => {
await apiFetch(`/api/frames/${id}/redeploy`, { method: 'POST' })
deployFrame: async ({ id }) => {
await apiFetch(`/api/frames/${id}/deploy`, { method: 'POST' })
},
stopFrame: async ({ id }) => {
await apiFetch(`/api/frames/${id}/stop`, { method: 'POST' })
},
restartFrame: async ({ id }) => {
await apiFetch(`/api/frames/${id}/restart`, { method: 'POST' })
Expand Down
23 changes: 12 additions & 11 deletions frontend/src/scenes/frame/Frame.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { Spinner } from '../../components/Spinner'
import { Button } from '../../components/Button'
import { Header } from '../../components/Header'
import { Panels } from './panels/Panels'
import { DropdownMenu } from '../../components/DropdownMenu'

interface FrameSceneProps {
id: string // taken straight from the URL, thus a string
Expand All @@ -24,21 +25,21 @@ export function Frame(props: FrameSceneProps) {
title={!frame ? `Loading frame ${props.id}...` : frame.name || frameHost(frame)}
buttons={
<div className="flex divide-x divide-gray-700 space-x-2">
<Button color="secondary" type="button" onClick={() => renderFrame()}>
Re-Render
</Button>
<Button color="secondary" type="button" onClick={() => restartFrame()}>
Restart
</Button>
<Button color="secondary" type="button" onClick={() => stopFrame()}>
Stop
</Button>
<DropdownMenu
buttonColor="secondary"
className="items-center"
items={[
{ label: 'Re-Render', onClick: () => renderFrame() },
{ label: 'Restart', onClick: () => restartFrame() },
{ label: 'Stop', onClick: () => stopFrame() },
]}
/>
<div className="flex pl-2 space-x-2">
<Button color={frameChanged ? 'primary' : 'secondary'} type="button" onClick={() => saveFrame()}>
Save
</Button>
<Button color={frameChanged ? 'primary' : 'secondary'} type="button" onClick={() => deployFrame()}>
Save&nbsp;&&nbsp;Redeploy
<Button color={'secondary'} type="button" onClick={() => deployFrame()}>
Redeploy
</Button>
</div>
</div>
Expand Down
12 changes: 4 additions & 8 deletions frontend/src/scenes/frame/frameLogic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -217,15 +217,11 @@ export const frameLogic = kea<frameLogicType>([
},
})),
listeners(({ actions, values, props }) => ({
renderFrame: () => framesModel.actions.renderFrame(props.frameId),
saveFrame: () => actions.submitFrameForm(),
deployFrame: () => actions.submitFrameForm(),
restartFrame: async () => {
await apiFetch(`/api/frames/${values.frameId}/restart`, { method: 'POST' })
},
stopFrame: async () => {
await apiFetch(`/api/frames/${values.frameId}/stop`, { method: 'POST' })
},
renderFrame: () => framesModel.actions.renderFrame(props.frameId),
restartFrame: () => framesModel.actions.restartFrame(props.frameId),
stopFrame: () => framesModel.actions.stopFrame(props.frameId),
deployFrame: () => framesModel.actions.deployFrame(props.frameId),
updateScene: ({ sceneId, scene }) => {
const { frameForm } = values
const hasScene = frameForm.scenes?.some(({ id }) => id === sceneId)
Expand Down
120 changes: 71 additions & 49 deletions frontend/src/scenes/frame/panels/Assets/Assets.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import { useActions, useValues } from 'kea'
import { frameLogic } from '../../frameLogic'
import { assetsLogic } from './assetsLogic'
import { panelsLogic } from '../panelsLogic'
import { Code } from '../../../../components/Code'
import { CloudArrowDownIcon } from '@heroicons/react/24/outline'
import { useState } from 'react'

function humaniseSize(size: number) {
const units = ['B', 'KB', 'MB', 'GB', 'TB']
Expand All @@ -15,62 +15,84 @@ function humaniseSize(size: number) {
return `${size.toFixed(2)} ${units[unitIndex]}`
}

// Define the shape of the node we get back from buildAssetTree
interface AssetNode {
name: string
path: string
isFolder: boolean
size?: number
mtime?: number
children: Record<string, AssetNode>
}

/** A recursive component that renders a folder or a file */
function TreeNode({
node,
frameId,
openAsset,
}: {
node: AssetNode
frameId: number
openAsset: (path: string) => void
}): JSX.Element {
const [expanded, setExpanded] = useState(node.path === '')

// If this node is a folder, display a collapsible section
if (node.isFolder) {
return (
<div className="ml-1">
<div className="cursor-pointer" onClick={() => setExpanded(!expanded)}>
{expanded ? '📂' : '📁'} <span className="hover:underline text-blue-400">{node.name || '/'}</span>
</div>
{expanded && (
<div className="ml-2 border-l border-gray-600 pl-2">
{Object.values(node.children).map((child) => (
<TreeNode key={child.path} node={child} frameId={frameId} openAsset={openAsset} />
))}
</div>
)}
</div>
)
} else {
// This is a file
return (
<div className="ml-1 flex items-center space-x-2">
<div className="flex-1 cursor-pointer hover:underline text-white" onClick={() => openAsset(node.path)}>
{node.name}
</div>
{node.size != null && <span className="text-xs text-gray-400">{humaniseSize(node.size)}</span>}
{node.mtime && (
<span className="text-xs text-gray-500" title={new Date(node.mtime * 1000).toLocaleString()}>
{new Date(node.mtime * 1000).toLocaleString()}
</span>
)}

{/* Download link */}
<a
href={`/api/frames/${frameId}/asset?path=${encodeURIComponent(node.path)}`}
download
className="text-gray-300 hover:text-white"
>
<CloudArrowDownIcon className="w-4 h-4 inline-block" />
</a>
</div>
)
}
}

export function Assets(): JSX.Element {
const { frame } = useValues(frameLogic)
const { assetsLoading, cleanedAssets, sortKey } = useValues(assetsLogic({ frameId: frame.id }))
const { setSortKey } = useActions(assetsLogic({ frameId: frame.id }))
const { assetsLoading, assetTree } = useValues(assetsLogic({ frameId: frame.id }))
const { openAsset } = useActions(panelsLogic({ frameId: frame.id }))

return (
<div className="space-y-2">
{assetsLoading ? (
<div>Loading assets...</div>
) : (
<table className="w-full">
<thead>
<tr className="bg-gray-900">
<th
onClick={() => setSortKey(sortKey === 'path' ? '-path' : 'path')}
className="cursor-pointer hover:underline"
>
Path
{sortKey === 'path' ? ' ▲' : sortKey === '-path' ? ' ▼' : ''}
</th>
<th
onClick={() => setSortKey(sortKey === 'size' ? '-size' : 'size')}
className="cursor-pointer hover:underline"
>
Size
{sortKey === 'size' ? ' ▲' : sortKey === '-size' ? ' ▼' : ''}
</th>
<th
onClick={() => setSortKey(sortKey === 'mtime' ? '-mtime' : 'mtime')}
className="cursor-pointer hover:underline"
>
Date
{sortKey === 'mtime' ? ' ▲' : sortKey === '-mtime' ? ' ▼' : ''}
</th>
<th>&nbsp;</th>
</tr>
</thead>
<tbody>
{cleanedAssets.map((asset) => (
<tr key={asset.path} className="even:bg-gray-700 hover:bg-gray-900">
<td onClick={() => openAsset(asset.path)} className="hover:underline cursor-pointer">
{asset.path}
</td>
<td className="text-nowrap">{humaniseSize(asset.size)}</td>
<td title={new Date(asset.mtime * 1000).toLocaleString()}>
{new Date(asset.mtime * 1000).toLocaleString()}
</td>
<td>
<a href={`/api/frames/${frame.id}/asset?path=${encodeURIComponent(asset.path)}`} download>
<CloudArrowDownIcon className="w-5 h-5" />
</a>
</td>
</tr>
))}
</tbody>
</table>
<div>
<TreeNode node={assetTree} frameId={frame.id} openAsset={openAsset} />
</div>
)}
</div>
)
Expand Down
Loading

0 comments on commit f109ec0

Please sign in to comment.