Compare commits

..

2 Commits

Author SHA1 Message Date
Siddharth Ganesan
34283b551f Fix dynimp 2026-03-14 18:54:37 -07:00
Siddharth Ganesan
e024f22aa8 Fix redis queuing and run 2026-03-14 18:43:19 -07:00
14 changed files with 43 additions and 66 deletions

View File

@@ -98,7 +98,7 @@ export function FileViewer({
file={file}
workspaceId={workspaceId}
canEdit={canEdit}
previewMode={previewMode ?? (showPreview ? 'preview' : 'editor')}
previewMode={previewMode ?? (showPreview ? 'split' : 'editor')}
autoFocus={autoFocus}
onDirtyChange={onDirtyChange}
onSaveStatusChange={onSaveStatusChange}

View File

@@ -1,3 +1,3 @@
export type { PreviewMode } from './file-viewer'
export { FileViewer, isPreviewable, isTextEditable } from './file-viewer'
export { RICH_PREVIEWABLE_EXTENSIONS } from './preview-panel'
export { PREVIEW_ONLY_EXTENSIONS, RICH_PREVIEWABLE_EXTENSIONS } from './preview-panel'

View File

@@ -23,6 +23,9 @@ const PREVIEWABLE_EXTENSIONS: Record<string, PreviewType> = {
svg: 'svg',
}
/** Extensions that should default to rendered preview (no raw editor). */
export const PREVIEW_ONLY_EXTENSIONS = new Set(['html', 'htm', 'svg'])
/** All extensions that have a rich preview renderer. */
export const RICH_PREVIEWABLE_EXTENSIONS = new Set(Object.keys(PREVIEWABLE_EXTENSIONS))

View File

@@ -476,11 +476,10 @@ export function Files() {
}, [closeListContextMenu])
useEffect(() => {
const isJustCreated = selectedFileId != null && justCreatedFileIdRef.current === selectedFileId
if (justCreatedFileIdRef.current && !isJustCreated) {
if (justCreatedFileIdRef.current && selectedFileId !== justCreatedFileIdRef.current) {
justCreatedFileIdRef.current = null
}
setShowPreview(!isJustCreated)
setShowPreview(true)
}, [selectedFileId])
useEffect(() => {
@@ -522,8 +521,8 @@ export function Files() {
...(canPreview
? [
{
label: showPreview ? 'Edit' : 'Preview',
icon: showPreview ? Pencil : Eye,
label: showPreview ? 'Hide Preview' : 'Preview',
icon: Eye,
onClick: handleTogglePreview,
},
]

View File

@@ -1,6 +1,6 @@
'use client'
import { lazy, memo, Suspense, useCallback, useEffect, useMemo } from 'react'
import { lazy, Suspense, useCallback, useEffect, useMemo } from 'react'
import { createLogger } from '@sim/logger'
import { Square } from 'lucide-react'
import { useRouter } from 'next/navigation'
@@ -51,11 +51,7 @@ interface ResourceContentProps {
* Handles table, file, and workflow resource types with appropriate
* embedded rendering for each.
*/
export const ResourceContent = memo(function ResourceContent({
workspaceId,
resource,
previewMode,
}: ResourceContentProps) {
export function ResourceContent({ workspaceId, resource, previewMode }: ResourceContentProps) {
switch (resource.type) {
case 'table':
return <Table key={resource.id} workspaceId={workspaceId} tableId={resource.id} embedded />
@@ -88,7 +84,7 @@ export const ResourceContent = memo(function ResourceContent({
default:
return null
}
})
}
interface ResourceActionsProps {
workspaceId: string
@@ -307,12 +303,10 @@ interface EmbeddedWorkflowProps {
function EmbeddedWorkflow({ workspaceId, workflowId }: EmbeddedWorkflowProps) {
const workflowExists = useWorkflowRegistry((state) => Boolean(state.workflows[workflowId]))
const isMetadataLoaded = useWorkflowRegistry(
(state) => state.hydration.phase !== 'idle' && state.hydration.phase !== 'metadata-loading'
)
const hasLoadError = useWorkflowRegistry(
(state) => state.hydration.phase === 'error' && state.hydration.workflowId === workflowId
)
const hydrationPhase = useWorkflowRegistry((state) => state.hydration.phase)
const hydrationWorkflowId = useWorkflowRegistry((state) => state.hydration.workflowId)
const isMetadataLoaded = hydrationPhase !== 'idle' && hydrationPhase !== 'metadata-loading'
const hasLoadError = hydrationPhase === 'error' && hydrationWorkflowId === workflowId
if (!isMetadataLoaded) return LOADING_SKELETON

View File

@@ -8,7 +8,7 @@ import {
useState,
} from 'react'
import { Button, Tooltip } from '@/components/emcn'
import { Columns3, Eye, PanelLeft, Pencil } from '@/components/emcn/icons'
import { Columns3, Eye, PanelLeft, Rows3 } from '@/components/emcn/icons'
import { cn } from '@/lib/core/utils/cn'
import type { PreviewMode } from '@/app/workspace/[workspaceId]/files/components/file-viewer'
import { AddResourceDropdown } from '@/app/workspace/[workspaceId]/home/components/mothership-view/components/add-resource-dropdown'
@@ -36,9 +36,9 @@ const EDGE_ZONE = 40
const SCROLL_SPEED = 8
const PREVIEW_MODE_ICONS = {
editor: Columns3,
split: Eye,
preview: Pencil,
editor: Rows3,
split: Columns3,
preview: Eye,
} satisfies Record<PreviewMode, (props: ComponentProps<typeof Eye>) => ReactNode>
/**

View File

@@ -1,10 +1,13 @@
'use client'
import { memo, useCallback, useEffect, useState } from 'react'
import { useCallback, useEffect, useState } from 'react'
import { cn } from '@/lib/core/utils/cn'
import { getFileExtension } from '@/lib/uploads/utils/file-utils'
import type { PreviewMode } from '@/app/workspace/[workspaceId]/files/components/file-viewer'
import { RICH_PREVIEWABLE_EXTENSIONS } from '@/app/workspace/[workspaceId]/files/components/file-viewer'
import {
PREVIEW_ONLY_EXTENSIONS,
RICH_PREVIEWABLE_EXTENSIONS,
} from '@/app/workspace/[workspaceId]/files/components/file-viewer'
import type {
MothershipResource,
MothershipResourceType,
@@ -31,7 +34,7 @@ interface MothershipViewProps {
className?: string
}
export const MothershipView = memo(function MothershipView({
export function MothershipView({
workspaceId,
chatId,
resources,
@@ -46,11 +49,12 @@ export const MothershipView = memo(function MothershipView({
}: MothershipViewProps) {
const active = resources.find((r) => r.id === activeResourceId) ?? resources[0] ?? null
const [previewMode, setPreviewMode] = useState<PreviewMode>('preview')
const [previewMode, setPreviewMode] = useState<PreviewMode>('split')
const handleCyclePreview = useCallback(() => setPreviewMode((m) => PREVIEW_CYCLE[m]), [])
useEffect(() => {
setPreviewMode('preview')
const ext = active?.type === 'file' ? getFileExtension(active.title) : ''
setPreviewMode(PREVIEW_ONLY_EXTENSIONS.has(ext) ? 'preview' : 'split')
}, [active?.id])
const isActivePreviewable =
@@ -95,4 +99,4 @@ export const MothershipView = memo(function MothershipView({
</div>
</div>
)
})
}

View File

@@ -167,6 +167,7 @@ export function Home({ chatId }: HomeProps = {}) {
const handleResourceEvent = useCallback(() => {
if (isResourceCollapsedRef.current) {
/** Auto-collapse sidebar to give resource panel maximum width for immersive experience */
const { isCollapsed, toggleCollapsed } = useSidebarStore.getState()
if (!isCollapsed) toggleCollapsed()
setIsResourceCollapsed(false)

View File

@@ -682,7 +682,8 @@ export function useChat(
readArgs?.path as string | undefined,
tc.result.output
)
if (resource && addResource(resource)) {
if (resource) {
addResource(resource)
onResourceEventRef.current?.()
}
}
@@ -693,21 +694,12 @@ export function useChat(
case 'resource_added': {
const resource = parsed.resource
if (resource?.type && resource?.id) {
const wasAdded = addResource(resource)
addResource(resource)
invalidateResourceQueries(queryClient, workspaceId, resource.type, resource.id)
if (!wasAdded && activeResourceIdRef.current !== resource.id) {
setActiveResourceId(resource.id)
}
onResourceEventRef.current?.()
if (resource.type === 'workflow') {
const wasRegistered = ensureWorkflowInRegistry(
resource.id,
resource.title,
workspaceId
)
if (wasAdded && wasRegistered) {
if (ensureWorkflowInRegistry(resource.id, resource.title, workspaceId)) {
useWorkflowRegistry.getState().setActiveWorkflow(resource.id)
} else {
useWorkflowRegistry.getState().loadWorkflowState(resource.id)

View File

@@ -57,7 +57,7 @@ export const STATUS_CONFIG: Record<
> = {
error: { variant: 'red', label: 'Error', color: 'var(--text-error)' },
pending: { variant: 'amber', label: 'Pending', color: '#f59e0b' },
running: { variant: 'amber', label: 'Running', color: '#f59e0b' },
running: { variant: 'green', label: 'Running', color: '#22c55e' },
cancelled: { variant: 'orange', label: 'Cancelled', color: '#f97316' },
info: { variant: 'gray', label: 'Info', color: 'var(--terminal-status-info-color)' },
}

View File

@@ -249,11 +249,7 @@ export function ScheduledTasks() {
>
Cancel
</Button>
<Button
variant='destructive'
onClick={handleDelete}
disabled={deleteSchedule.isPending}
>
<Button variant='default' onClick={handleDelete} disabled={deleteSchedule.isPending}>
{deleteSchedule.isPending ? 'Deleting...' : 'Delete'}
</Button>
</ModalFooter>

View File

@@ -207,7 +207,6 @@ const reactFlowStyles = [
'[&_.react-flow__node-subflowNode.selected]:!shadow-none',
].join(' ')
const reactFlowFitViewOptions = { padding: 0.6, maxZoom: 1.0 } as const
const embeddedFitViewOptions = { padding: 0.15, maxZoom: 0.85, minZoom: 0.35 } as const
const reactFlowProOptions = { hideAttribution: true } as const
/**
@@ -3852,11 +3851,11 @@ const WorkflowContent = React.memo(
onDragOver={effectivePermissions.canEdit ? onDragOver : undefined}
onInit={(instance) => {
requestAnimationFrame(() => {
instance.fitView(embedded ? embeddedFitViewOptions : reactFlowFitViewOptions)
instance.fitView(reactFlowFitViewOptions)
setIsCanvasReady(true)
})
}}
fitViewOptions={embedded ? embeddedFitViewOptions : reactFlowFitViewOptions}
fitViewOptions={reactFlowFitViewOptions}
minZoom={0.1}
maxZoom={1.3}
panOnScroll

View File

@@ -138,12 +138,9 @@ const SidebarTaskItem = memo(function SidebarTaskItem({
{task.id !== 'new' && (
<div className='relative flex h-[18px] w-[18px] flex-shrink-0 items-center justify-center'>
{isActive && !isCurrentRoute && (
<span className='absolute h-[7px] w-[7px] animate-ping rounded-full bg-amber-400 opacity-30 group-hover:hidden' />
<span className='absolute h-[7px] w-[7px] animate-ping rounded-full bg-[#33C482] opacity-30 group-hover:hidden' />
)}
{isActive && !isCurrentRoute && (
<span className='absolute h-[7px] w-[7px] rounded-full bg-amber-400 group-hover:hidden' />
)}
{!isActive && isUnread && !isCurrentRoute && (
{(isActive || isUnread) && !isCurrentRoute && (
<span className='absolute h-[7px] w-[7px] rounded-full bg-[#33C482] group-hover:hidden' />
)}
<button
@@ -1099,15 +1096,7 @@ export const Sidebar = memo(function Sidebar() {
tasks.map((task) => (
<DropdownMenuItem key={task.id} asChild>
<Link href={task.href}>
<span className='relative flex-shrink-0'>
<Blimp className='h-[16px] w-[16px]' />
{task.isActive && (
<span className='-bottom-[1px] -right-[1px] absolute h-[6px] w-[6px] rounded-full border border-[var(--surface-1)] bg-amber-400' />
)}
{!task.isActive && task.isUnread && (
<span className='-bottom-[1px] -right-[1px] absolute h-[6px] w-[6px] rounded-full border border-[var(--surface-1)] bg-[#33C482]' />
)}
</span>
<Blimp className='h-[16px] w-[16px]' />
<span>{task.name}</span>
</Link>
</DropdownMenuItem>

View File

@@ -40,7 +40,7 @@ export function PlayOutline(props: SVGProps<SVGSVGElement>) {
xmlns='http://www.w3.org/2000/svg'
{...props}
>
<path d='M7.5 3.5C7.5 2.672 8.452 2.18 9.128 2.66L18.128 9.16C18.72 9.58 18.72 10.46 18.128 10.88L9.128 17.38C8.452 17.86 7.5 17.368 7.5 16.54V3.5Z' />
<path d='M6.25 3.9C6.25 3.408 6.799 3.114 7.209 3.399L15.709 9.299C16.063 9.545 16.063 10.069 15.709 10.315L7.209 16.215C6.799 16.5 6.25 16.206 6.25 15.714V3.9Z' />
</svg>
)
}