Skip to content

🎉 trial of CommandPalette feature #1748

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 26 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
7b8f56d
install cmdk
tnyo43 May 10, 2025
9f93a37
add command palette base
tnyo43 May 10, 2025
0e0565c
show table lists
tnyo43 May 10, 2025
bfa9790
show ReactFlow (not working)
tnyo43 May 10, 2025
fe9545d
Merge branch 'main' into trial-command-palette
tnyo43 May 15, 2025
a8d061d
move CommandPalette component to erd-core
tnyo43 May 15, 2025
688157c
show TableNode of selected table in the command palette component
tnyo43 May 15, 2025
4080fd6
add button to move to the table node in the main content
tnyo43 May 15, 2025
190d475
fix useEffect deps
tnyo43 May 15, 2025
ba28cef
update input styles
tnyo43 May 15, 2025
e884d6b
update item styles
tnyo43 May 17, 2025
2a9a668
sample, show table preview with every cursor move
tnyo43 May 17, 2025
f6be316
tmp
tnyo43 May 17, 2025
d969438
fix by using onValueChange in Command.Dialog
tnyo43 May 17, 2025
9cec481
set command palette trigger button for mouse use cases
tnyo43 May 17, 2025
f58c6e4
make button prettier
tnyo43 May 17, 2025
cfeea9d
make table preview prettier
tnyo43 May 17, 2025
18241a0
fix color
tnyo43 May 17, 2025
5f16b26
Merge branch 'main' into trial-command-palette
tnyo43 May 22, 2025
ebb7cba
introduce `super-selected` status to allow user to focus on a specifi…
tnyo43 May 22, 2025
3e50b52
remove tableName when the dialog opens
tnyo43 May 22, 2025
e312520
fix icon property error
tnyo43 May 22, 2025
7138f9e
close TableDetails during CommandPalette component opens
tnyo43 May 24, 2025
86d0868
add title and description to avoid errors
tnyo43 May 24, 2025
7cfbd1a
remove table escape functions
tnyo43 May 27, 2025
c671ae2
pin @radix-ui/react-dialog version
tnyo43 May 30, 2025
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
2 changes: 2 additions & 0 deletions frontend/packages/erd-core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,10 @@
"dependencies": {
"@liam-hq/ui": "workspace:*",
"@radix-ui/react-toolbar": "1.1.9",
"@radix-ui/react-dialog": "1.1.13",
"@xyflow/react": "12.3.5",
"clsx": "2.1.1",
"cmdk": "^1.1.1",
"elkjs": "0.10.0",
"react": "18.3.1",
"ts-pattern": "5.7.0",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
TooltipTrigger,
} from '@liam-hq/ui'
import type { FC } from 'react'
import { CommandPaletteTriggerButton } from '../CommandPalette'
import styles from './AppBar.module.css'
import { CopyLinkButton } from './CopyLinkButton'
import { GithubButton } from './GithubButton'
Expand Down Expand Up @@ -43,6 +44,7 @@ export const AppBar: FC = () => {
<h1 className={styles.title}>Liam ERD</h1>

<div className={styles.rightSide}>
<CommandPaletteTriggerButton />
<div className={styles.iconButtonGroup}>
<GithubButton />
<ReleaseNoteButton />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
.content[cmdk-dialog] {
z-index: 10000;
background-color: #232526;
color: white;
border-radius: 6px;
box-shadow: var(--shadow-6);
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 638px;
height: 508px;
animation: contentShow 150ms cubic-bezier(0.16, 1, 0.3, 1);
border: 1px solid black;
}

[cmdk-overlay]:has(+ .content) {
z-index: 10000;
width: 100vw;
height: 100vh;
background-color: var(--black-a9);
position: fixed;
inset: 0;
animation: overlayShow 150ms cubic-bezier(0.16, 1, 0.3, 1);
backdrop-filter: blur(8px);
}

.searchContainer {
display: flex;
align-items: center;
padding: 8px 16px;
width: 100%;
border-bottom: 1px solid rgba(255, 255, 255, 0.05);
}

.searchContainer > [cmdk-input] {
margin: 0 8px;
height: 32px;
flex-grow: 1;
}

.escapeSign {
border: 1px solid #434536;
border-radius: var(--border-radius-smbase);
font-size: 10px;
width: 34px;
height: 21px;
text-align: center;
line-height: 21px;
}

.main {
display: flex;
padding: 8px;
}

.main > * {
width: 50%;
}

.main [cmdk-list] {
max-height: 444px;
overflow-x: hidden;
overflow-y: scroll;
}

.main [cmdk-group-heading] {
height: 40px;
line-height: 24px;
padding: 8px;
color: var(--global-mute-text);
font-size: var(--font-size-3);
}

.itemInner {
width: 100%;
height: 100%;
display: flex;
padding: 8px;
gap: 8px;
overflow: truncate;
}

.itemInner svg {
flex-shrink: 0;
color: white;
}

.main [cmdk-item][data-selected="true"] {
background-color: rgba(255, 255, 255, 0.05);
}

.main [cmdk-item][data-super-selected="true"] {
border: 1px solid white;
}

.previewContainer {
padding: 32px;
height: 444px;
}

.tableNodeContainer {
padding: 40px 20px;
background-color: black;
height: 100%;
overflow-x: hidden;
overflow-y: scroll;
}

.triggerButtonContainer {
display: flex;
height: 29px;
width: 240px;
padding: 0 8px;
border: 1px solid #383a3b;
background-color: #141616;
align-items: center;
color: var(--global-mute-text);
}

.triggerButtonContainer .rightSide {
display: flex;
gap: 4px;
margin-left: auto;
margin-right: 0;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
'use client'

import { useTableSelection } from '@/features/erd/hooks'
import {
updatePaletteOpen,
useCommandPaletteStore,
useSchemaStore,
} from '@/stores'
import { Search, Table2 } from '@liam-hq/ui'
import * as Dialog from '@radix-ui/react-dialog'
import { Command } from 'cmdk'
import { type FC, useCallback, useEffect, useState } from 'react'
import { TableNode } from '../../ERDContent/components/TableNode'
import styles from './CommandPalette.module.css'

export const CommandPalette: FC = () => {
const schema = useSchemaStore()
const { open } = useCommandPaletteStore()

useEffect(() => {
const down = (e: KeyboardEvent) => {
if (e.key === 'k' && (e.metaKey || e.ctrlKey)) {
e.preventDefault()
updatePaletteOpen(true)
setTableName(null)
setSuperSelected(false)
}
}

document.addEventListener('keydown', down)
return () => document.removeEventListener('keydown', down)
}, [])

const [tableName, setTableName] = useState<string | null>(null)
const { selectTable } = useTableSelection()
const [superSelected, setSuperSelected] = useState(false)
const superSelectedTableName = superSelected ? tableName : undefined

const table = schema.tables[tableName ?? '']

const jumpToERD = useCallback(
(tableName: string) => {
updatePaletteOpen(false)
setSuperSelected(false)
selectTable({ tableId: tableName, displayArea: 'main' })
},
[selectTable],
)

return (
<Command.Dialog
open={open}
onOpenChange={updatePaletteOpen}
contentClassName={styles.content}
value={tableName ?? ''}
onValueChange={(v) => {
if (!superSelected) setTableName(v)
}}
>
<Dialog.Title hidden>Command Palette</Dialog.Title>
<Dialog.Description hidden>find tables</Dialog.Description>
<div className={styles.searchContainer}>
<Search />
<Command.Input placeholder="Search" />
<span className={styles.escapeSign}>ESC</span>
</div>
<div className={styles.main}>
<Command.List>
<Command.Empty>No results found.</Command.Empty>

<Command.Group heading="Suggestions">
{Object.values(schema.tables).map((table) => (
<Command.Item
key={table.name}
value={table.name}
onSelect={() => jumpToERD(table.name)}
data-super-selected={
superSelectedTableName === table.name ? 'true' : undefined
}
>
{/* biome-ignore lint/a11y/useKeyWithClickEvents: <explanation> */}
<div
className={styles.itemInner}
onClick={(event) => {
event.stopPropagation()
if (superSelectedTableName === table.name)
setSuperSelected((state) => !state)
else {
setSuperSelected(true)
setTableName(table.name)
}
}}
onDoubleClick={(event) => {
event.stopPropagation()
jumpToERD(table.name)
}}
>
<Table2 />
{table.name}
</div>
</Command.Item>
))}
</Command.Group>
</Command.List>
<div>
<div className={styles.previewContainer}>
{table && (
<div className={styles.tableNodeContainer}>
<TableNode
id=""
type="table"
data={{
table: table,
showMode: 'ALL_FIELDS',
isActiveHighlighted: false,
isHighlighted: false,
sourceColumnName: undefined,
targetColumnCardinalities: undefined,
}}
dragging={false}
isConnectable={false}
positionAbsoluteX={0}
positionAbsoluteY={0}
zIndex={0}
/>
</div>
)}
</div>
</div>
</div>
</Command.Dialog>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { updatePaletteOpen } from '@/stores'
import { Search } from '@liam-hq/ui'
import type { FC } from 'react'
import styles from './CommandPalette.module.css'
import { CmdIcon, KIcon } from './Icons'

export const CommandPaletteTriggerButton: FC = () => {
return (
<button
type="button"
className={styles.triggerButtonContainer}
onClick={() => updatePaletteOpen(true)}
>
<Search />
Search
<span className={styles.rightSide}>
<CmdIcon />
<KIcon />
</span>
</button>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
export const KIcon = () => (
// biome-ignore lint/a11y/noSvgWithoutTitle: <explanation>
<svg
width="21"
height="22"
viewBox="0 0 21 22"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M0 3.5C0 1.84315 1.34315 0.5 3 0.5H18C19.6569 0.5 21 1.84315 21 3.5V18.5C21 20.1569 19.6569 21.5 18 21.5H3C1.34315 21.5 0 20.1569 0 18.5V3.5Z"
fill="white"
fillOpacity="0.05"
/>
<path
d="M10.1303 11.896L8.93034 13.312V16H7.92234V7.624H8.93034V11.992H8.96634L10.0703 10.6L12.5423 7.624H13.7543L10.8143 11.152L13.8383 16H12.6623L10.1303 11.896Z"
fill="#BEBFC1"
/>
</svg>
)

export const CmdIcon = () => (
// biome-ignore lint/a11y/noSvgWithoutTitle: <explanation>
<svg
width="21"
height="22"
viewBox="0 0 21 22"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M0 3.5C0 1.84315 1.34315 0.5 3 0.5H18C19.6569 0.5 21 1.84315 21 3.5V18.5C21 20.1569 19.6569 21.5 18 21.5H3C1.34315 21.5 0 20.1569 0 18.5V3.5Z"
fill="white"
fillOpacity="0.05"
/>
<path
d="M8.06128 16C7.50128 16 7.05328 15.836 6.71728 15.508C6.38928 15.18 6.22528 14.74 6.22528 14.188C6.22528 13.604 6.39728 13.152 6.74128 12.832C7.09328 12.504 7.60928 12.34 8.28928 12.34H8.99728V11.02H8.28928C7.60928 11.02 7.09328 10.86 6.74128 10.54C6.39728 10.212 6.22528 9.756 6.22528 9.172C6.22528 8.62 6.38928 8.18 6.71728 7.852C7.05328 7.524 7.50128 7.36 8.06128 7.36C8.50928 7.36 8.86128 7.444 9.11728 7.612C9.38128 7.78 9.56528 8.004 9.66928 8.284C9.78128 8.564 9.83728 8.872 9.83728 9.208V10.204H11.1573V9.208C11.1573 8.872 11.2093 8.564 11.3133 8.284C11.4253 8.004 11.6093 7.78 11.8653 7.612C12.1293 7.444 12.4853 7.36 12.9333 7.36C13.4933 7.36 13.9373 7.524 14.2653 7.852C14.6013 8.18 14.7693 8.62 14.7693 9.172C14.7693 9.756 14.5973 10.212 14.2533 10.54C13.9093 10.86 13.3933 11.02 12.7053 11.02H11.9973V12.34H12.7053C13.3933 12.34 13.9093 12.504 14.2533 12.832C14.5973 13.152 14.7693 13.604 14.7693 14.188C14.7693 14.74 14.6013 15.18 14.2653 15.508C13.9373 15.836 13.4933 16 12.9333 16C12.4853 16 12.1293 15.916 11.8653 15.748C11.6093 15.58 11.4253 15.356 11.3133 15.076C11.2093 14.796 11.1573 14.488 11.1573 14.152V13.156H9.83728V14.152C9.83728 14.488 9.78128 14.796 9.66928 15.076C9.56528 15.356 9.38128 15.58 9.11728 15.748C8.86128 15.916 8.50928 16 8.06128 16ZM11.9973 9.184V10.204H12.7053C13.1293 10.204 13.4373 10.12 13.6293 9.952C13.8213 9.776 13.9173 9.516 13.9173 9.172C13.9173 8.82 13.8213 8.568 13.6293 8.416C13.4453 8.264 13.2133 8.188 12.9333 8.188C12.6213 8.188 12.3853 8.28 12.2253 8.464C12.0733 8.64 11.9973 8.88 11.9973 9.184ZM8.28928 10.204H8.99728V9.184C8.99728 8.88 8.91728 8.64 8.75728 8.464C8.60528 8.28 8.37328 8.188 8.06128 8.188C7.78128 8.188 7.54528 8.264 7.35328 8.416C7.16928 8.568 7.07728 8.82 7.07728 9.172C7.07728 9.516 7.17328 9.776 7.36528 9.952C7.55728 10.12 7.86528 10.204 8.28928 10.204ZM9.83728 12.34H11.1573V11.02H9.83728V12.34ZM8.06128 15.172C8.37328 15.172 8.60528 15.084 8.75728 14.908C8.91728 14.724 8.99728 14.48 8.99728 14.176V13.156H8.28928C7.86528 13.156 7.55728 13.244 7.36528 13.42C7.17328 13.588 7.07728 13.844 7.07728 14.188C7.07728 14.54 7.16928 14.792 7.35328 14.944C7.54528 15.096 7.78128 15.172 8.06128 15.172ZM11.9973 14.176C11.9973 14.48 12.0733 14.724 12.2253 14.908C12.3853 15.084 12.6213 15.172 12.9333 15.172C13.2133 15.172 13.4453 15.096 13.6293 14.944C13.8213 14.792 13.9173 14.54 13.9173 14.188C13.9173 13.844 13.8213 13.588 13.6293 13.42C13.4373 13.244 13.1293 13.156 12.7053 13.156H11.9973V14.176Z"
fill="#BEBFC1"
/>
</svg>
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './CommandPalette'
export * from './CommandPaletteTriggerButton'
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import { useSchemaStore, useUserEditingStore } from '@/stores'
import { convertSchemaToNodes } from '../../utils'
import { ERDContent } from '../ERDContent'
import { CardinalityMarkers } from './CardinalityMarkers'
import { CommandPalette } from './CommandPalette'
import { ErrorDisplay } from './ErrorDisplay'
import { LeftPane } from './LeftPane'
import { RelationshipEdgeParticleMarker } from './RelationshipEdgeParticleMarker'
Expand Down Expand Up @@ -154,6 +155,7 @@ export const ERDRenderer: FC<Props> = ({
</div>
)}
</main>
<CommandPalette />
</ResizablePanel>
</ResizablePanelGroup>
</ReactFlowProvider>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { commandPaletteStore } from './store'

export const updatePaletteOpen = (open: boolean) => {
commandPaletteStore.open = open
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { useSnapshot } from 'valtio'
import { commandPaletteStore } from './store'
import type { CommandPalette } from './types'

export const useCommandPaletteStore = () =>
useSnapshot(commandPaletteStore) as CommandPalette
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export * from './store'
export * from './hooks'
export * from './actions'
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { proxy } from 'valtio'
import type { CommandPalette } from './types'

export const commandPaletteStore = proxy<CommandPalette>({
open: false,
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export type CommandPalette = {
open: boolean
}
Loading
Loading