Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,6 +1,17 @@
'use client'

import { createContext, memo, useContext, useEffect, useMemo, useRef, useState } from 'react'
import {
Children,
cloneElement,
createContext,
isValidElement,
memo,
useContext,
useEffect,
useMemo,
useRef,
useState,
} from 'react'
import matter from 'gray-matter'
import { useRouter } from 'next/navigation'
import rehypeSlug from 'rehype-slug'
Expand All @@ -10,7 +21,7 @@ import { Streamdown } from 'streamdown'
import 'streamdown/styles.css'
import { toError } from '@sim/utils/errors'
import { generateShortId } from '@sim/utils/id'
import { Checkbox, CopyCodeButton, highlight, languages, Skeleton } from '@/components/emcn'
import { Checkbox, highlight, languages, Skeleton } from '@/components/emcn'
import '@/components/emcn/components/code/code.css'
import 'prismjs/components/prism-bash'
import 'prismjs/components/prism-css'
Expand Down Expand Up @@ -473,7 +484,15 @@ function resolveSimFileUrl(src: string | undefined): string | undefined {
}

const STATIC_MARKDOWN_COMPONENTS = {
pre: ({ children }: { children?: React.ReactNode }) => <>{children}</>,
pre: ({ children }: { children?: React.ReactNode }) => (
<>
{Children.map(children, (child) =>
isValidElement<Record<string, unknown>>(child)
? cloneElement(child, { 'data-block': 'true' })
: child
) ?? children}
</>
),
'mermaid-diagram': ({ definition }: { definition?: string }) => {
const isStreaming = useContext(MermaidStreamingCtx)
return <MermaidDiagram definition={definition ?? ''} isStreaming={isStreaming} />
Expand Down Expand Up @@ -531,20 +550,11 @@ const STATIC_MARKDOWN_COMPONENTS = {
{children}
</h6>
),
inlineCode: ({ children }: { children?: React.ReactNode }) => {
if (typeof children === 'string' && children.includes('\n')) {
return (
<code className='my-4 block overflow-x-auto whitespace-pre-wrap break-words rounded-lg bg-[var(--surface-5)] p-4 font-mono text-[var(--text-primary)] leading-[1.6]'>
{children}
</code>
)
}
return (
<code className='whitespace-normal rounded bg-[var(--surface-5)] px-1.5 py-0.5 font-mono text-[var(--caution)]'>
{children}
</code>
)
},
inlineCode: ({ children }: { children?: React.ReactNode }) => (
<code className='whitespace-normal rounded bg-[var(--surface-5)] px-1.5 py-0.5 font-mono text-[var(--caution)] not-italic'>
{children}
</code>
),
Comment thread
waleedlatif1 marked this conversation as resolved.
code: ({ children, className }: { children?: React.ReactNode; className?: string }) => {
const langMatch = className?.match(/language-(\w+)/)
const langRaw = langMatch?.[1] ?? ''
Expand All @@ -564,22 +574,18 @@ const STATIC_MARKDOWN_COMPONENTS = {

return (
<div className='my-4 overflow-hidden rounded-lg border border-[var(--border)]'>
<div className='flex items-center justify-between border-[var(--border)] border-b bg-[var(--surface-3)] px-3 py-1.5'>
<div className='border-[var(--border)] border-b bg-[var(--surface-3)] px-3 py-1.5'>
<span className='text-[11px] text-[var(--text-tertiary)]'>{langRaw || 'code'}</span>
<CopyCodeButton
code={codeString}
className='-mr-1 text-[var(--text-tertiary)] hover:text-[var(--text-secondary)]'
/>
</div>
<div className='code-editor-theme bg-[var(--surface-5)]'>
{html ? (
<pre
className='m-0 overflow-x-auto whitespace-pre p-4 font-mono text-[13px] leading-[1.6]'
className='m-0 overflow-x-auto whitespace-pre p-4 font-mono text-[13px] not-italic leading-[1.6]'
dangerouslySetInnerHTML={{ __html: html }}
/>
) : (
<pre className='m-0 overflow-x-auto whitespace-pre p-4 font-mono text-[13px] text-[var(--text-primary)] leading-[1.6]'>
<code>{codeString.trimEnd()}</code>
<pre className='m-0 overflow-x-auto whitespace-pre p-4 font-mono text-[13px] text-[var(--text-primary)] not-italic leading-[1.6]'>
<code className='not-italic'>{codeString.trimEnd()}</code>
</pre>
)}
</div>
Expand Down Expand Up @@ -634,7 +640,7 @@ const STATIC_MARKDOWN_COMPONENTS = {
<tr className='border-[var(--border)] border-b last:border-b-0'>{children}</tr>
),
th: ({ children }: { children?: React.ReactNode }) => (
<th className='px-3 py-2 text-left font-semibold text-[12px] text-[var(--text-primary)]'>
<th className='px-3 py-2 text-left font-semibold text-[13px] text-[var(--text-primary)]'>
{children}
</th>
),
Expand Down Expand Up @@ -684,20 +690,36 @@ function LiRenderer({
const isTaskItem = typeof className === 'string' && className.includes('task-list-item')

if (isTaskItem) {
const [checkboxChild, ...contentChildren] = Children.toArray(children)
const content = <span className='min-w-0 flex-1'>{contentChildren}</span>

if (ctx) {
const offset = node?.position?.start?.offset
if (offset === undefined) {
return <li className='flex items-start gap-2 break-words leading-[1.6]'>{children}</li>
return (
<li className='flex items-start gap-2 break-words leading-[1.6]'>
{checkboxChild}
{content}
</li>
)
}
const before = ctx.contentRef.current.slice(0, offset)
const prior = before.match(/^(\s*(?:[-*+]|\d+[.)]) +)\[([ xX])\]/gm)
return (
<CheckboxIndexCtx.Provider value={prior ? prior.length : 0}>
<li className='flex items-start gap-2 break-words leading-[1.6]'>{children}</li>
<li className='flex items-start gap-2 break-words leading-[1.6]'>
{checkboxChild}
{content}
</li>
</CheckboxIndexCtx.Provider>
)
}
return <li className='flex items-start gap-2 break-words leading-[1.6]'>{children}</li>
return (
<li className='flex items-start gap-2 break-words leading-[1.6]'>
{checkboxChild}
{content}
</li>
)
}

return <li className='break-words leading-[1.6]'>{children}</li>
Expand Down
2 changes: 1 addition & 1 deletion apps/sim/app/workspace/[workspaceId]/files/files.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -622,7 +622,7 @@ export function Files() {
const mimeType = getMimeTypeFromExtension('md')
const blob = new Blob([''], { type: mimeType })
const file = new File([blob], name, { type: mimeType })
const result = await uploadFile.mutateAsync({ workspaceId, file })
const result = await uploadFile.mutateAsync({ workspaceId, file, skipToast: true })
const fileId = result.file?.id
if (fileId) {
justCreatedFileIdRef.current = fileId
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ import {
} from '@/hooks/queries/tables'
import { useWorkflowState, workflowKeys } from '@/hooks/queries/workflows'
import type { WorkflowMetadata } from '@/stores/workflows/registry/types'
import { COLUMN_SIDEBAR_WIDTH_CSS } from '../table/constants'
import { COLUMN_TYPE_OPTIONS, type SidebarColumnType } from './column-types'

export type ColumnConfigState =
Expand Down Expand Up @@ -1017,9 +1018,10 @@ export function ColumnSidebar({
role='dialog'
aria-label='Configure column'
className={cn(
'absolute top-0 right-0 bottom-0 z-[var(--z-modal)] flex w-[400px] flex-col overflow-hidden border-[var(--border)] border-l bg-[var(--bg)] shadow-overlay transition-transform duration-200 ease-out',
'absolute top-0 right-0 bottom-0 z-[var(--z-modal)] flex flex-col overflow-hidden border-[var(--border)] border-l bg-[var(--bg)] shadow-md transition-transform duration-200 ease-out',
open ? 'translate-x-0' : 'translate-x-full'
)}
style={{ width: COLUMN_SIDEBAR_WIDTH_CSS }}
>
<div className='flex h-full flex-col'>
<div className='flex items-center justify-between border-[var(--border)] border-b px-3 py-2'>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,9 @@ export function CellContent({
<div
className={cn('flex min-h-[20px] items-center justify-center', isEditing && 'invisible')}
>
<Checkbox size='sm' checked={Boolean(value)} className='pointer-events-none' />
<span data-boolean-cell-toggle className='inline-flex'>
<Checkbox size='sm' checked={Boolean(value)} className='pointer-events-none' />
</span>
</div>
)
} else if (!isNull && column.type === 'json') {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,6 @@ export const SELECTION_TINT_BG = 'bg-[rgba(37,99,235,0.06)]'
/** Default column width in pixels. Used as a fallback when a column hasn't
* been measured yet and as the initial width for newly-added columns. */
export const COL_WIDTH = 160

/** Column config sidebar width: roomy by default, bounded on narrow screens. */
export const COLUMN_SIDEBAR_WIDTH_CSS = 'min(480px, calc(100vw - 48px))'
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ import { RowModal } from '../row-modal'
import { TableFilter } from '../table-filter'
import { CellContent } from './cells/cell-content'
import { ExpandedCellPopover } from './cells/expanded-cell-popover'
import { COL_WIDTH, SELECTION_TINT_BG } from './constants'
import { COL_WIDTH, COLUMN_SIDEBAR_WIDTH_CSS, SELECTION_TINT_BG } from './constants'
import { ColumnHeaderMenu } from './headers/column-header-menu'
import { COLUMN_TYPE_ICONS } from './headers/column-type-icon'
import { WorkflowGroupMetaCell } from './headers/workflow-group-meta-cell'
Expand Down Expand Up @@ -94,8 +94,6 @@ const COL_WIDTH_AUTO_FIT_MAX = 1000
// row-by-row.
const CHECKBOX_COL_WIDTH = 56
const ADD_COL_WIDTH = 120
/** Width of the column-config slideout (matches `column-sidebar.tsx`'s `w-[400px]`). */
const COLUMN_SIDEBAR_WIDTH = 400
const SKELETON_COL_COUNT = 4
const SKELETON_ROW_COUNT = 10
const ROW_HEIGHT_ESTIMATE = 35
Expand Down Expand Up @@ -1218,29 +1216,35 @@ export function Table({
return () => cancelAnimationFrame(rafId)
}, [selectionAnchor, selectionFocus, isColumnSelection])

const handleCellClick = useCallback((rowId: string, columnName: string) => {
const column = columnsRef.current.find((c) => c.name === columnName)
if (column?.type === 'boolean') {
if (!canEditRef.current) return
const row = rowsRef.current.find((r) => r.id === rowId)
if (row) {
toggleBooleanCell(rowId, columnName, row.data[columnName])
const handleCellClick = useCallback(
(rowId: string, columnName: string, options?: { toggleBoolean?: boolean }) => {
const column = columnsRef.current.find((c) => c.name === columnName)
if (column?.type === 'boolean') {
if (!options?.toggleBoolean || !canEditRef.current) return
const row = rowsRef.current.find((r) => r.id === rowId)
if (row) {
toggleBooleanCell(rowId, columnName, row.data[columnName])
}
return
}
return
}

const current = editingCellRef.current
if (current && current.rowId === rowId && current.columnName === columnName) return
setEditingCell(null)
setInitialCharacter(null)
}, [])
const current = editingCellRef.current
if (current && current.rowId === rowId && current.columnName === columnName) return
setEditingCell(null)
setInitialCharacter(null)
},
[]
)

// The cell has `select-none` which suppresses programmatic selection, so we
// override `user-select` on the inner element until the next click. The popover
// only opens when the leaf's scroll dimensions exceed its client dimensions
// (workflow cells nest text inside a span with its own `overflow-clip`).
const handleCellDoubleClick = useCallback(
(rowId: string, columnName: string, columnKey: string) => {
const column = columnsRef.current.find((c) => c.key === columnKey)
if (column?.type === 'boolean') return

setSelectionFocus(null)
setIsColumnSelection(false)

Expand Down Expand Up @@ -2080,11 +2084,11 @@ export function Table({
* The two panels are mutually exclusive (each opener closes the other).
*/
const logPanelWidth = useLogDetailsUIStore((state) => state.panelWidth)
const sidebarReservedPx = configState
? COLUMN_SIDEBAR_WIDTH
const sidebarReservedWidth = configState
? COLUMN_SIDEBAR_WIDTH_CSS
: executionDetailsId
? logPanelWidth
: 0
? `${logPanelWidth}px`
: '0px'

const handleConfigureColumn = useCallback((columnName: string) => {
setExecutionDetailsId(null)
Expand Down Expand Up @@ -2578,8 +2582,8 @@ export function Table({
<div
className='relative h-fit'
style={{
width: `${tableWidth + sidebarReservedPx}px`,
paddingRight: sidebarReservedPx,
width: `calc(${tableWidth}px + ${sidebarReservedWidth})`,
paddingRight: sidebarReservedWidth,
}}
>
<table
Expand Down Expand Up @@ -2989,7 +2993,7 @@ interface DataRowProps {
initialCharacter: string | null
pendingCellValue: Record<string, unknown> | null
normalizedSelection: NormalizedSelection | null
onClick: (rowId: string, columnName: string) => void
onClick: (rowId: string, columnName: string, options?: { toggleBoolean?: boolean }) => void
onDoubleClick: (rowId: string, columnName: string, columnKey: string) => void
onSave: (rowId: string, columnName: string, value: unknown, reason: SaveReason) => void
onCancel: () => void
Expand Down Expand Up @@ -3115,15 +3119,15 @@ const DataRow = React.memo(function DataRow({

return (
<tr onContextMenu={(e) => onContextMenu(e, row)}>
<td className={cn(CELL_CHECKBOX, 'cursor-pointer')}>
<td
className={cn(CELL_CHECKBOX, 'cursor-pointer')}
onMouseDown={(e) => {
if (e.button !== 0) return
onRowToggle(rowIndex, e.shiftKey)
}}
>
<div className='flex items-center justify-center gap-1'>
<div
className='group/checkbox flex h-[20px] w-[24px] shrink-0 items-center justify-center'
onMouseDown={(e) => {
if (e.button !== 0) return
onRowToggle(rowIndex, e.shiftKey)
}}
>
<div className='group/checkbox flex h-[20px] w-[24px] shrink-0 items-center justify-center'>
<span
className={cn(
'text-[var(--text-tertiary)] text-xs tabular-nums',
Expand All @@ -3147,6 +3151,9 @@ const DataRow = React.memo(function DataRow({
aria-label={runningCount > 0 ? `Stop ${runningCount} running` : 'Run row'}
title={runningCount > 0 ? `Stop ${runningCount} running` : 'Run row'}
className='ml-auto flex h-[20px] w-[20px] shrink-0 items-center justify-center rounded text-[var(--text-primary)] transition-colors hover-hover:bg-[var(--surface-2)]'
onMouseDown={(e) => {
e.stopPropagation()
}}
onClick={() => {
if (runningCount > 0) {
onStopRow(row.id)
Expand Down Expand Up @@ -3192,7 +3199,13 @@ const DataRow = React.memo(function DataRow({
onCellMouseDown(rowIndex, colIndex, e.shiftKey)
}}
onMouseEnter={() => onCellMouseEnter(rowIndex, colIndex)}
onClick={() => onClick(row.id, column.name)}
onClick={(e) =>
onClick(row.id, column.name, {
toggleBoolean: Boolean(
(e.target as HTMLElement).closest('[data-boolean-cell-toggle]')
),
})
}
onDoubleClick={() => onDoubleClick(row.id, column.name, column.key)}
>
{isHighlighted && (isMultiCell || isRowChecked) && (
Expand Down Expand Up @@ -3307,9 +3320,23 @@ const SelectAllCheckbox = React.memo(function SelectAllCheckbox({
onCheckedChange: () => void
}) {
return (
<th className={CELL_HEADER_CHECKBOX}>
<th
className={cn(CELL_HEADER_CHECKBOX, 'cursor-pointer')}
role='checkbox'
aria-checked={checked}
tabIndex={0}
onMouseDown={(e) => {
if (e.button !== 0) return
onCheckedChange()
}}
onKeyDown={(e) => {
if (e.key !== ' ' && e.key !== 'Enter') return
e.preventDefault()
onCheckedChange()
}}
>
<div className='flex items-center justify-center'>
<Checkbox size='sm' checked={checked} onCheckedChange={onCheckedChange} />
<Checkbox size='sm' checked={checked} className='pointer-events-none' />
</div>
</th>
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -345,7 +345,7 @@ export function ImportCsvDialog({
<ModalBody>
{!parsed ? (
<div className='flex flex-col gap-2'>
<Label>Upload CSV</Label>
<Label>Import CSV</Label>
<Button
type='button'
variant='default'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ export function TablesListContextMenu({
{onUploadCsv && (
<DropdownMenuItem disabled={disableUpload} onSelect={onUploadCsv}>
<Upload />
Upload CSV
Import CSV
</DropdownMenuItem>
)}
</DropdownMenuContent>
Expand Down
2 changes: 1 addition & 1 deletion apps/sim/app/workspace/[workspaceId]/tables/tables.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -450,7 +450,7 @@ export function Tables() {
? `${uploadProgress.completed}/${uploadProgress.total}`
: uploading
? 'Uploading...'
: 'Upload CSV'
: 'Import CSV'

const handleCreateTable = useCallback(async () => {
const existingNames = tables.map((t) => t.name)
Expand Down
Loading
Loading