diff --git a/apps/studio/components/interfaces/Explorer/MarkdownCell.tsx b/apps/studio/components/interfaces/Explorer/MarkdownCell.tsx index 42319c09ea4..f9dc2cad062 100644 --- a/apps/studio/components/interfaces/Explorer/MarkdownCell.tsx +++ b/apps/studio/components/interfaces/Explorer/MarkdownCell.tsx @@ -53,14 +53,13 @@ export const MarkdownCell = ({ cell, onEdit }: MarkdownCellProps) => { return ( } gripDropdownContent={} gripClassName="mt-1.5 sm:opacity-0 group-hover:opacity-100 has-[[data-state=open]]:opacity-100 transition" > {isEditing ? ( -
+
{
(function return ( } gripDropdownContent={} gripClassName="mt-2 sm:opacity-0 group-hover:opacity-100 has-[[data-state=open]]:opacity-100 transition" diff --git a/apps/studio/components/interfaces/Explorer/QueryEditor/index.tsx b/apps/studio/components/interfaces/Explorer/QueryEditor/index.tsx index f4a4f56d764..1255b081ae2 100644 --- a/apps/studio/components/interfaces/Explorer/QueryEditor/index.tsx +++ b/apps/studio/components/interfaces/Explorer/QueryEditor/index.tsx @@ -534,7 +534,7 @@ export const QueryEditor = forwardRef(funct return ( <> - + diff --git a/apps/studio/components/interfaces/Explorer/__tests__/ExplorerNotebookTab.test.tsx b/apps/studio/components/interfaces/Explorer/__tests__/ExplorerNotebookTab.test.tsx index b114f5dbb43..003458f2df5 100644 --- a/apps/studio/components/interfaces/Explorer/__tests__/ExplorerNotebookTab.test.tsx +++ b/apps/studio/components/interfaces/Explorer/__tests__/ExplorerNotebookTab.test.tsx @@ -131,12 +131,15 @@ afterEach(() => { }) describe('ExplorerNotebookTab', () => { - it('hides SQL by default for saved notebooks and caps query cells at 6xl', () => { + it('hides SQL by default for saved notebooks and caps query cells from their sortable row', () => { renderNotebookTab() const queryCells = Array.from(document.querySelectorAll('[data-slot="explorer-query"]')) expect(queryCells).toHaveLength(2) - queryCells.forEach((cell) => expect(cell).toHaveClass('max-w-6xl')) + queryCells.forEach((cell) => { + expect(cell).not.toHaveClass('max-w-6xl') + expect(cell.closest('[style*="max-width"]')).not.toBeNull() + }) expect(screen.queryByRole('textbox', { name: 'SQL editor' })).not.toBeInTheDocument() }) diff --git a/apps/studio/components/layouts/ExplorerLayout/ExplorerLayout.constants.tsx b/apps/studio/components/layouts/ExplorerLayout/ExplorerLayout.constants.tsx index 9efa4538449..bfac9c16336 100644 --- a/apps/studio/components/layouts/ExplorerLayout/ExplorerLayout.constants.tsx +++ b/apps/studio/components/layouts/ExplorerLayout/ExplorerLayout.constants.tsx @@ -26,7 +26,7 @@ export const EXPLORER_SECTIONS: Array<{ export const rowClassName = (isActive: boolean) => cn( - 'group relative flex h-7 w-full items-center gap-2 rounded-md pl-3 pr-2 text-sm', + 'group relative flex h-7 w-full items-center gap-2 rounded-md pl-2 pr-2 text-sm', isActive ? 'bg-selection text-foreground' : 'text-foreground-light hover:bg-surface-200 hover:text-foreground' @@ -58,7 +58,7 @@ export const ExplorerNavResourceWrapper = ({ transition={LEVEL_TRANSITION} className={cn('absolute inset-0 flex flex-col', className)} > -
+
{searchPlaceholder} diff --git a/apps/studio/components/layouts/ExplorerLayout/ExplorerNavChats.tsx b/apps/studio/components/layouts/ExplorerLayout/ExplorerNavChats.tsx index 15e1e76582e..f6473b6038b 100644 --- a/apps/studio/components/layouts/ExplorerLayout/ExplorerNavChats.tsx +++ b/apps/studio/components/layouts/ExplorerLayout/ExplorerNavChats.tsx @@ -29,7 +29,7 @@ export const ExplorerNavChats = () => { return ( -
+
{chats.length === 0 ? (

{search ? 'No chats found' : 'No chats created yet'} diff --git a/apps/studio/components/layouts/ExplorerLayout/ExplorerNavHome.tsx b/apps/studio/components/layouts/ExplorerLayout/ExplorerNavHome.tsx index 547e3d3470f..4b969b9f64b 100644 --- a/apps/studio/components/layouts/ExplorerLayout/ExplorerNavHome.tsx +++ b/apps/studio/components/layouts/ExplorerLayout/ExplorerNavHome.tsx @@ -77,11 +77,11 @@ export const ExplorerNavHome = ({

-

+

Recently updated

{recentItems.length === 0 ? ( -

Nothing edited yet

+

Nothing edited yet

) : ( recentItems.map((item) => { const Icon = EXPLORER_SECTIONS.find((section) => section.type === item.type)?.icon diff --git a/apps/studio/components/layouts/ExplorerLayout/ExplorerNavNotebooks.tsx b/apps/studio/components/layouts/ExplorerLayout/ExplorerNavNotebooks.tsx index e605504e443..27861712c45 100644 --- a/apps/studio/components/layouts/ExplorerLayout/ExplorerNavNotebooks.tsx +++ b/apps/studio/components/layouts/ExplorerLayout/ExplorerNavNotebooks.tsx @@ -75,7 +75,7 @@ export const ExplorerNavNotebooks = () => { return ( -
+
{isPending ? ( ) : notebooks.length === 0 ? ( diff --git a/apps/studio/components/ui/AIAssistantPanel/AssistantChat.tsx b/apps/studio/components/ui/AIAssistantPanel/AssistantChat.tsx index a1f5a2b0b62..636122430ac 100644 --- a/apps/studio/components/ui/AIAssistantPanel/AssistantChat.tsx +++ b/apps/studio/components/ui/AIAssistantPanel/AssistantChat.tsx @@ -564,7 +564,7 @@ export const AssistantChat = ({ })} {hasMessages ? ( - + {renderedMessages}
{error && ( diff --git a/apps/studio/components/ui/AIAssistantPanel/AssistantChatForm.tsx b/apps/studio/components/ui/AIAssistantPanel/AssistantChatForm.tsx index 1f8b9edd767..e5a6c8a4c9f 100644 --- a/apps/studio/components/ui/AIAssistantPanel/AssistantChatForm.tsx +++ b/apps/studio/components/ui/AIAssistantPanel/AssistantChatForm.tsx @@ -108,7 +108,7 @@ const AssistantChatFormComponent = forwardRef( ref={formRef} {...props} onSubmit={handleSubmit} - className={cn('relative', className)} + className={cn('relative rounded-lg', className)} > {sqlSnippets && sqlSnippets.length > 0 && ( { expect(screen.getByRole('toolbar', { name: 'Notebook toolbar' })).toBeInTheDocument() expect(screen.getByText('2 cells')).toBeInTheDocument() expect(screen.getByText('New notebook')).toBeInTheDocument() - expect(container.firstElementChild).toHaveClass('max-w-6xl') + expect(container.firstElementChild).toHaveClass('max-w-3xl') }) it('surfaces a metadata-only database change after resolving the target', async () => { diff --git a/apps/studio/components/ui/AIAssistantPanel/AssistantNotebookPreview.tsx b/apps/studio/components/ui/AIAssistantPanel/AssistantNotebookPreview.tsx index 75c793a11c4..aac3e2e589d 100644 --- a/apps/studio/components/ui/AIAssistantPanel/AssistantNotebookPreview.tsx +++ b/apps/studio/components/ui/AIAssistantPanel/AssistantNotebookPreview.tsx @@ -83,7 +83,7 @@ export const AssistantNotebookPreview = ({ expandedOverrides[getEntryKey(entry)] === true return ( -
+
diff --git a/apps/studio/components/ui/AIAssistantPanel/AssistantQueryCell.tsx b/apps/studio/components/ui/AIAssistantPanel/AssistantQueryCell.tsx index 9f4cbb9e7b0..44e847305ce 100644 --- a/apps/studio/components/ui/AIAssistantPanel/AssistantQueryCell.tsx +++ b/apps/studio/components/ui/AIAssistantPanel/AssistantQueryCell.tsx @@ -150,7 +150,7 @@ export const AssistantQueryCell = ({ return ( {children}
+ return ( +
+ {children} +
+ ) } const isWideMessagePart = (part: NonNullable[number]) => diff --git a/apps/studio/components/ui/AIAssistantPanel/elements/Conversation.tsx b/apps/studio/components/ui/AIAssistantPanel/elements/Conversation.tsx index 5260059ad25..74b430795ed 100644 --- a/apps/studio/components/ui/AIAssistantPanel/elements/Conversation.tsx +++ b/apps/studio/components/ui/AIAssistantPanel/elements/Conversation.tsx @@ -10,6 +10,14 @@ type ConversationProps = Omit, 'children'> type ConversationContentProps = ComponentProps type ConversationScrollButtonProps = ComponentProps +/** + * Horizontal gutter the conversation reserves around its content. The fades overlay the scroll + * container, whose scrollbar runs down the right edge, so they stop at this gutter instead of + * spanning the full width. + */ +const CONTENT_GUTTER = 'px-7' +const FADE_GUTTER = 'inset-x-7' + export const Conversation = ({ className, children, ...props }: ConversationProps) => (
{children} ) export const ConversationContent = ({ className, ...props }: ConversationContentProps) => ( - + ) export const ConversationScrollButton = ({ diff --git a/apps/studio/components/ui/SortableSection.tsx b/apps/studio/components/ui/SortableSection.tsx index 1c31fcaa49f..d1822680921 100644 --- a/apps/studio/components/ui/SortableSection.tsx +++ b/apps/studio/components/ui/SortableSection.tsx @@ -5,14 +5,21 @@ import type { CSSProperties, PropsWithChildren, ReactNode } from 'react' import { useEffect, useRef, useState } from 'react' import { Button, cn, DropdownMenu, DropdownMenuTrigger } from 'ui' +const GRIP_WIDTH_REM = 1.5 +const ACTIONS_WIDTH_REM = 1.75 +const CONTROL_GAP_REM = 1 + export const SortableSection = ({ id, children, actions, + sectionWidth, gripClassName, gripDropdownContent, }: PropsWithChildren<{ id: string + /** Caps the section at this width and centres it, with the controls alongside it. */ + sectionWidth?: string gripClassName?: string actions?: ReactNode gripDropdownContent?: ReactNode @@ -43,18 +50,26 @@ export const SortableSection = ({ useEffect(() => () => clearTimeout(openTimeoutRef.current), []) + // Carried twice — once as the controls, once as padding — so the section stays centred. + const gutterRem = (actions ? ACTIONS_WIDTH_REM : 0) + GRIP_WIDTH_REM + CONTROL_GAP_REM + const style: CSSProperties = { transform: transform ? `translate3d(${Math.round(transform.x)}px, ${Math.round(transform.y)}px, 0)` : undefined, transition, + ...(sectionWidth && { + marginInline: 'auto', + maxWidth: `calc(${sectionWidth} + ${2 * gutterRem}rem)`, + paddingRight: `${gutterRem}rem`, + }), } return (
{actions}