Skip to content

Commit 39c6aef

Browse files
fix(files): unstick monaco find widget tooltips and surface logs in mothership add-resource (#4395)
1 parent cc28ba8 commit 39c6aef

7 files changed

Lines changed: 132 additions & 9 deletions

File tree

apps/sim/app/workspace/[workspaceId]/files/components/file-viewer/editor-context-menu.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import {
99
DropdownMenuShortcut,
1010
DropdownMenuTrigger,
1111
} from '@/components/emcn'
12-
import { Clipboard, Copy, Search } from '@/components/emcn/icons'
12+
import { Clipboard, Copy, Search, SelectAll } from '@/components/emcn/icons'
1313

1414
interface EditorContextMenuProps {
1515
isOpen: boolean
@@ -88,6 +88,7 @@ export function EditorContextMenu({
8888
)}
8989
<DropdownMenuSeparator />
9090
<DropdownMenuItem onSelect={onSelectAll}>
91+
<SelectAll />
9192
Select all
9293
<DropdownMenuShortcut>⌘A</DropdownMenuShortcut>
9394
</DropdownMenuItem>

apps/sim/app/workspace/[workspaceId]/files/components/file-viewer/text-editor.tsx

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,27 @@ const SPLIT_MIN_PCT = 20
219219
const SPLIT_MAX_PCT = 80
220220
const SPLIT_DEFAULT_PCT = 50
221221

222+
/**
223+
* Monaco's find-widget button hover tooltips render above the button by default. Because the find
224+
* widget sits at the very top of the editor, the tooltip overlaps the button itself —
225+
* `document.elementFromPoint` at the button's center returns `.hover-contents` instead of the
226+
* button, which fires `mouseleave` on the button. Monaco then disposes the tooltip, the cursor
227+
* lands back on the button, and the cycle repeats every ~210ms, making the buttons unclickable.
228+
*
229+
* Translate the tooltip's `.context-view` below the button so the cursor stays on the button
230+
* while the tooltip is shown, and flip the carat to point up at the button.
231+
*/
232+
const FIND_TOOLTIP_FIX_CSS = `
233+
[data-find-tooltip-fix] .context-view.top.left {
234+
transform: translateY(56px);
235+
}
236+
[data-find-tooltip-fix] .context-view.top.left .workbench-hover-pointer.bottom {
237+
top: -3px;
238+
bottom: auto;
239+
transform: rotate(225deg);
240+
}
241+
`
242+
222243
/** Maps file extensions to Monaco editor language IDs. */
223244
const MONACO_LANGUAGE_BY_EXTENSION: Partial<Record<string, string>> = {
224245
js: 'javascript',
@@ -663,7 +684,8 @@ export const TextEditor = memo(function TextEditor({
663684
const closeContextMenu = () => setContextMenu(null)
664685

665686
return (
666-
<div ref={containerRef} className='relative flex flex-1 overflow-hidden'>
687+
<div ref={containerRef} data-find-tooltip-fix className='relative flex flex-1 overflow-hidden'>
688+
<style>{FIND_TOOLTIP_FIX_CSS}</style>
667689
{showEditor && (
668690
<div
669691
style={showPreviewPane ? { width: `${splitPct}%`, flexShrink: 0 } : undefined}

apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/add-resource-dropdown/add-resource-dropdown.tsx

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,10 @@ import type {
2424
MothershipResource,
2525
MothershipResourceType,
2626
} from '@/app/workspace/[workspaceId]/home/types'
27+
import { formatDate } from '@/app/workspace/[workspaceId]/logs/utils'
2728
import { useFolders } from '@/hooks/queries/folders'
2829
import { useKnowledgeBasesQuery } from '@/hooks/queries/kb/knowledge'
30+
import { useLogsList } from '@/hooks/queries/logs'
2931
import { useTablesList } from '@/hooks/queries/tables'
3032
import { useTasks } from '@/hooks/queries/tasks'
3133
import { useWorkflows } from '@/hooks/queries/workflows'
@@ -47,6 +49,18 @@ interface AvailableItemsByType {
4749
items: AvailableItem[]
4850
}
4951

52+
const LOG_DROPDOWN_LIMIT = 50
53+
54+
const LOG_DROPDOWN_FILTERS = {
55+
timeRange: 'All time' as const,
56+
level: 'all',
57+
workflowIds: [] as string[],
58+
folderIds: [] as string[],
59+
triggers: [] as string[],
60+
searchQuery: '',
61+
limit: LOG_DROPDOWN_LIMIT,
62+
}
63+
5064
export function useAvailableResources(
5165
workspaceId: string,
5266
existingKeys: Set<string>,
@@ -58,6 +72,13 @@ export function useAvailableResources(
5872
const { data: knowledgeBases } = useKnowledgeBasesQuery(workspaceId)
5973
const { data: folders = [] } = useFolders(workspaceId)
6074
const { data: tasks = [] } = useTasks(workspaceId)
75+
const { data: logsData } = useLogsList(workspaceId, LOG_DROPDOWN_FILTERS)
76+
const logs = useMemo(() => (logsData?.pages ?? []).flatMap((page) => page.logs), [logsData])
77+
const workflowColorById = useMemo(() => {
78+
const map = new Map<string, string>()
79+
for (const w of workflows) map.set(w.id, w.color)
80+
return map
81+
}, [workflows])
6182

6283
return useMemo(() => {
6384
const excluded = new Set<MothershipResourceType>(excludeTypes ?? [])
@@ -115,9 +136,39 @@ export function useAvailableResources(
115136
isOpen: existingKeys.has(`task:${t.id}`),
116137
})),
117138
},
139+
{
140+
type: 'log' as const,
141+
items: logs.map((log) => {
142+
const workflowName = log.workflow?.name ?? log.workflowId ?? 'Unknown'
143+
const color =
144+
log.workflow?.color ??
145+
(log.workflowId ? workflowColorById.get(log.workflowId) : undefined) ??
146+
'#888'
147+
const time = formatDate(log.createdAt).compact
148+
return {
149+
id: log.id,
150+
name: `${workflowName} · ${time}`,
151+
workflowName,
152+
color,
153+
time,
154+
isOpen: existingKeys.has(`log:${log.id}`),
155+
}
156+
}),
157+
},
118158
]
119159
return groups.filter((g) => !excluded.has(g.type))
120-
}, [workflows, folders, tables, files, knowledgeBases, tasks, existingKeys, excludeTypes])
160+
}, [
161+
workflows,
162+
folders,
163+
tables,
164+
files,
165+
knowledgeBases,
166+
tasks,
167+
logs,
168+
workflowColorById,
169+
existingKeys,
170+
excludeTypes,
171+
])
121172
}
122173

123174
export type WorkflowTreeNode =

apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/resource-registry/resource-registry.tsx

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,30 @@ function IconDropdownItem({ item, icon: Icon }: DropdownItemRenderProps & { icon
100100
)
101101
}
102102

103+
function LogDropdownItem({ item }: DropdownItemRenderProps) {
104+
const color = (item.color as string) ?? '#888'
105+
const workflowName = (item.workflowName as string) ?? item.name
106+
const time = (item.time as string) ?? ''
107+
return (
108+
<>
109+
<div
110+
className='h-[14px] w-[14px] flex-shrink-0 rounded-[3px] border-[2px]'
111+
style={{
112+
backgroundColor: color,
113+
borderColor: workflowBorderColor(color),
114+
backgroundClip: 'padding-box',
115+
}}
116+
/>
117+
<span className='truncate'>{workflowName}</span>
118+
{time && (
119+
<span className='ml-auto flex-shrink-0 text-[var(--text-tertiary)] text-caption'>
120+
{time}
121+
</span>
122+
)}
123+
</>
124+
)
125+
}
126+
103127
export const RESOURCE_REGISTRY: Record<MothershipResourceType, ResourceTypeConfig> = {
104128
generic: {
105129
type: 'generic',
@@ -172,7 +196,7 @@ export const RESOURCE_REGISTRY: Record<MothershipResourceType, ResourceTypeConfi
172196
renderTabIcon: (_resource, className) => (
173197
<Library className={cn(className, 'text-[var(--text-icon)]')} />
174198
),
175-
renderDropdownItem: (props) => <IconDropdownItem {...props} icon={Library} />,
199+
renderDropdownItem: (props) => <LogDropdownItem {...props} />,
176200
},
177201
} as const
178202

apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/resource-tabs/resource-tabs.tsx

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -41,11 +41,7 @@ import { useWorkspaceFiles } from '@/hooks/queries/workspace-files'
4141
const EDGE_ZONE = 40
4242
const SCROLL_SPEED = 8
4343

44-
const ADD_RESOURCE_EXCLUDED_TYPES: readonly MothershipResourceType[] = [
45-
'folder',
46-
'task',
47-
'log',
48-
] as const
44+
const ADD_RESOURCE_EXCLUDED_TYPES: readonly MothershipResourceType[] = ['folder', 'task'] as const
4945

5046
/**
5147
* Returns the id of the nearest resource to `idx` that is in `filter`

apps/sim/components/emcn/icons/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ export { RefreshCw } from './refresh-cw'
6565
export { Rocket } from './rocket'
6666
export { Rows3 } from './rows3'
6767
export { Search } from './search'
68+
export { SelectAll } from './select-all'
6869
export { Send } from './send'
6970
export { Server } from './server'
7071
export { Settings } from './settings'
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import type { SVGProps } from 'react'
2+
3+
/**
4+
* SelectAll icon component - four L-shaped corner brackets indicating a selection region
5+
* @param props - SVG properties including className, fill, etc.
6+
*/
7+
export function SelectAll(props: SVGProps<SVGSVGElement>) {
8+
return (
9+
<svg
10+
width='24'
11+
height='24'
12+
viewBox='0 0 24 24'
13+
fill='none'
14+
stroke='currentColor'
15+
strokeWidth='2'
16+
strokeLinecap='round'
17+
strokeLinejoin='round'
18+
xmlns='http://www.w3.org/2000/svg'
19+
aria-hidden='true'
20+
{...props}
21+
>
22+
<path d='M8 4H4V8' />
23+
<path d='M16 4H20V8' />
24+
<path d='M20 16V20H16' />
25+
<path d='M8 20H4V16' />
26+
</svg>
27+
)
28+
}

0 commit comments

Comments
 (0)