From 0d10a01880fa7319c9fc1d3e1de9c5a36fb2543a Mon Sep 17 00:00:00 2001
From: 0xzion <0xzion.penx@gmail.com>
Date: Sun, 26 May 2024 18:09:56 +0800
Subject: [PATCH] feat: can render database
---
apps/desktop/package.json | 1 +
.../src/common/installBuiltinExtension.ts | 15 +--
.../CommandPalette/CommandApp/CommandApp.tsx | 2 +-
.../CommandPalette/CommandApp/DatabaseApp.tsx | 11 --
.../CommandApp/DatabaseApp/DatabaseApp.tsx | 22 +++
.../CommandApp/DatabaseApp/DatabaseDetail.tsx | 125 ++++++++++++++++++
.../CommandApp/DatabaseApp/FieldIcon.tsx | 44 ++++++
.../CommandApp/DatabaseApp/RowForm.tsx | 68 ++++++++++
.../CommandPalette/CommandComponents.ts | 2 +
.../CommandPalette/CommandPaletteFooter.tsx | 5 +-
.../CommandPalette/DatabaseName.tsx | 21 +++
.../CommandPalette/ListItemIcon.tsx | 48 ++++++-
.../components/CommandPalette/ListItemUI.tsx | 13 +-
.../components/CommandPalette/SearchBar.tsx | 32 ++++-
apps/desktop/src/hooks/useCommandPosition.ts | 8 ++
apps/desktop/src/hooks/useCurrentDatabase.ts | 11 ++
apps/desktop/src/hooks/useHandleSelect.ts | 10 +-
apps/desktop/src/hooks/useItems.ts | 32 ++++-
packages/app/src/Workbench/PanelItem.tsx | 2 -
.../AccountSettings/AccountSettings.tsx | 2 +-
.../app/src/Workbench/Sidebar/Sidebar.tsx | 4 +-
packages/app/src/Workbench/Workbench.tsx | 2 +-
.../cell-fields/src/fields/SingleSelect.tsx | 2 +-
packages/local-db/src/libs/getRandomColor.ts | 2 +-
packages/model/src/Node.ts | 6 +
packages/penx/src/components/ListBuilder.ts | 18 ++-
pnpm-lock.yaml | 4 +
27 files changed, 457 insertions(+), 55 deletions(-)
delete mode 100644 apps/desktop/src/components/CommandPalette/CommandApp/DatabaseApp.tsx
create mode 100644 apps/desktop/src/components/CommandPalette/CommandApp/DatabaseApp/DatabaseApp.tsx
create mode 100644 apps/desktop/src/components/CommandPalette/CommandApp/DatabaseApp/DatabaseDetail.tsx
create mode 100644 apps/desktop/src/components/CommandPalette/CommandApp/DatabaseApp/FieldIcon.tsx
create mode 100644 apps/desktop/src/components/CommandPalette/CommandApp/DatabaseApp/RowForm.tsx
create mode 100644 apps/desktop/src/components/CommandPalette/DatabaseName.tsx
create mode 100644 apps/desktop/src/hooks/useCurrentDatabase.ts
diff --git a/apps/desktop/package.json b/apps/desktop/package.json
index 1dfe74de2..740ea624f 100644
--- a/apps/desktop/package.json
+++ b/apps/desktop/package.json
@@ -22,6 +22,7 @@
"@penx/api": "workspace:*",
"@penx/app": "workspace:*",
"@penx/cmdk": "workspace:*",
+ "@penx/cell-fields": "workspace:*",
"@penx/constants": "workspace:*",
"@penx/encryption": "workspace:*",
"@penx/event": "workspace:*",
diff --git a/apps/desktop/src/common/installBuiltinExtension.ts b/apps/desktop/src/common/installBuiltinExtension.ts
index dc5878bef..f4af007c7 100644
--- a/apps/desktop/src/common/installBuiltinExtension.ts
+++ b/apps/desktop/src/common/installBuiltinExtension.ts
@@ -18,9 +18,9 @@ export async function installBuiltinExtension() {
isDeveloping: false,
commands: [
{
- name: 'database',
- title: 'Database',
- icon: '/icons/copy.svg',
+ name: 'marketplace',
+ title: 'marketplace',
+ icon: '/icons/marketplace.svg',
subtitle: '',
description: '',
code: '',
@@ -35,15 +35,6 @@ export async function installBuiltinExtension() {
code: '',
isBuiltIn: true,
},
- {
- name: 'marketplace',
- title: 'marketplace',
- icon: '/icons/marketplace.svg',
- subtitle: '',
- description: '',
- code: '',
- isBuiltIn: true,
- },
],
createdAt: new Date(),
updatedAt: new Date(),
diff --git a/apps/desktop/src/components/CommandPalette/CommandApp/CommandApp.tsx b/apps/desktop/src/components/CommandPalette/CommandApp/CommandApp.tsx
index d4d2accf1..776ec9d45 100644
--- a/apps/desktop/src/components/CommandPalette/CommandApp/CommandApp.tsx
+++ b/apps/desktop/src/components/CommandPalette/CommandApp/CommandApp.tsx
@@ -6,7 +6,7 @@ import { Spinner } from 'uikit'
import { Markdown } from '~/components/Markdown'
import { CommandAppUI } from '~/hooks/useCommandAppUI'
import { ClipboardHistoryApp } from './ClipboardHistoryApp'
-import { DatabaseApp } from './DatabaseApp'
+import { DatabaseApp } from './DatabaseApp/DatabaseApp'
import { ListApp } from './ListApp'
import { MarketplaceApp } from './MarketplaceApp/MarketplaceApp'
import { TodayApp } from './TodayApp'
diff --git a/apps/desktop/src/components/CommandPalette/CommandApp/DatabaseApp.tsx b/apps/desktop/src/components/CommandPalette/CommandApp/DatabaseApp.tsx
deleted file mode 100644
index 6fd491020..000000000
--- a/apps/desktop/src/components/CommandPalette/CommandApp/DatabaseApp.tsx
+++ /dev/null
@@ -1,11 +0,0 @@
-import { Box } from '@fower/react'
-
-export function DatabaseApp() {
- return (
-
-
- Database List
-
-
- )
-}
diff --git a/apps/desktop/src/components/CommandPalette/CommandApp/DatabaseApp/DatabaseApp.tsx b/apps/desktop/src/components/CommandPalette/CommandApp/DatabaseApp/DatabaseApp.tsx
new file mode 100644
index 000000000..bebc97dbe
--- /dev/null
+++ b/apps/desktop/src/components/CommandPalette/CommandApp/DatabaseApp/DatabaseApp.tsx
@@ -0,0 +1,22 @@
+import { useQuery } from '@tanstack/react-query'
+import { db } from '@penx/local-db'
+import { useCurrentDatabase } from '~/hooks/useCurrentDatabase'
+import { useSearch } from '~/hooks/useSearch'
+import { StyledCommandGroup } from '../../CommandComponents'
+import { DatabaseDetail } from './DatabaseDetail'
+
+export function DatabaseApp() {
+ const { database } = useCurrentDatabase()
+ const { search } = useSearch()
+
+ const { data, isLoading } = useQuery({
+ queryKey: ['database', database.id],
+ queryFn: () => db.getDatabase(database.id),
+ })
+
+ if (isLoading || !data) {
+ return
+ }
+
+ return
+}
diff --git a/apps/desktop/src/components/CommandPalette/CommandApp/DatabaseApp/DatabaseDetail.tsx b/apps/desktop/src/components/CommandPalette/CommandApp/DatabaseApp/DatabaseDetail.tsx
new file mode 100644
index 000000000..60faf5a12
--- /dev/null
+++ b/apps/desktop/src/components/CommandPalette/CommandApp/DatabaseApp/DatabaseDetail.tsx
@@ -0,0 +1,125 @@
+import { useEffect, useMemo } from 'react'
+import { Box } from '@fower/react'
+import { Divider } from 'uikit'
+import {
+ ICellNode,
+ IColumnNode,
+ IDatabaseNode,
+ IOptionNode,
+ IRowNode,
+ IViewNode,
+} from '@penx/model-types'
+import { mappedByKey } from '@penx/shared'
+import { useValue } from '~/hooks/useValue'
+import { StyledCommandEmpty, StyledCommandGroup } from '../../CommandComponents'
+import { ListItemUI } from '../../ListItemUI'
+import { RowForm } from './RowForm'
+
+interface Props {
+ text: string
+ database: IDatabaseNode
+ views: IViewNode[]
+ columns: IColumnNode[]
+ rows: IRowNode[]
+ cells: ICellNode[]
+ options: IOptionNode[]
+}
+
+interface Item {
+ row: IRowNode
+ cell: ICellNode
+ rowCells: ICellNode[]
+}
+
+export function DatabaseDetail(props: Props) {
+ const { value, setValue } = useValue()
+ const { text, ...rest } = props
+ const { columns, rows, views, cells } = rest
+ const currentView = views[0]
+ const { viewColumns = [] } = currentView.props
+ const columnMap = mappedByKey(columns, 'id')
+ const sortedColumns = viewColumns.map(({ columnId }) => columnMap[columnId])
+
+ const filteredRows: Item[] = useMemo(() => {
+ const items = rows
+ .map((row) => {
+ const rowCells = cells.filter((cell) => cell.props.rowId === row.id)
+
+ if (!text) {
+ const cell = rowCells.find(
+ (cell) => cell.props.columnId === sortedColumns[0].id,
+ )!
+ return { row, rowCells, cell }
+ }
+
+ const cell = rowCells.find((cell) => {
+ // console.log('cell-----:', cell.props.data)
+ const data = String(cell.props?.data || '').toLowerCase()
+ return data.includes(text.toLowerCase())
+ })!
+ return { row, rowCells, cell }
+ })
+ .filter((item) => !!item.cell)
+ .slice(0, 20)
+ return items
+ }, [rows, cells, text, sortedColumns])
+
+ useEffect(() => {
+ if (!isUuidV4(value) && filteredRows.length) {
+ setValue(filteredRows[0].row.id)
+ }
+ }, [filteredRows, value, setValue])
+
+ // console.log('=======filteredRows:', filteredRows, 'value:', value)
+ const currentItem = filteredRows.find((item) => item.row.id === value)
+
+ if (!filteredRows.length)
+ return (
+
+ No results found.
+
+ )
+
+ return (
+
+
+ {filteredRows.map((item, index) => {
+ // console.log('=======item:', item)
+
+ const listItem = {
+ title: dataToString(item.cell.props.data),
+ }
+ return (
+
+ )
+ })}
+
+
+
+
+
+ {currentItem && }
+
+
+ )
+}
+
+function dataToString(data: any) {
+ if (!data) return 'Untitled'
+ if (typeof data === 'string') return data
+ if (typeof data === 'number') return data.toString()
+ if (Array.isArray(data)) return data.join(',')
+ return JSON.stringify(data, null)
+}
+
+function isUuidV4(uuid: string): boolean {
+ const uuidV4Regex =
+ /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i
+ return uuidV4Regex.test(uuid)
+}
diff --git a/apps/desktop/src/components/CommandPalette/CommandApp/DatabaseApp/FieldIcon.tsx b/apps/desktop/src/components/CommandPalette/CommandApp/DatabaseApp/FieldIcon.tsx
new file mode 100644
index 000000000..60be63437
--- /dev/null
+++ b/apps/desktop/src/components/CommandPalette/CommandApp/DatabaseApp/FieldIcon.tsx
@@ -0,0 +1,44 @@
+import { Box } from '@fower/react'
+import {
+ CalendarDays,
+ CheckCircle2,
+ Hash,
+ Home,
+ Key,
+ Link,
+ ListChecks,
+ Text,
+} from 'lucide-react'
+import { FieldType } from '@penx/model-types'
+
+interface Props {
+ index?: number
+ fieldType: `${FieldType}`
+ size?: number
+}
+
+export const FieldIcon = ({ fieldType, size = 16, index }: Props) => {
+ const iconsMap: Record = {
+ [FieldType.TEXT]: Text,
+ [FieldType.NUMBER]: Hash,
+ [FieldType.URL]: Link,
+ [FieldType.PASSWORD]: Key,
+ [FieldType.SINGLE_SELECT]: CheckCircle2,
+ [FieldType.MULTIPLE_SELECT]: ListChecks,
+ [FieldType.MARKDOWN]: Text,
+ [FieldType.DATE]: CalendarDays,
+ [FieldType.CREATED_AT]: CalendarDays,
+ [FieldType.UPDATED_AT]: CalendarDays,
+ }
+ let Icon = iconsMap[fieldType]
+
+ if (index === 0) Icon = Home
+
+ if (Icon)
+ return (
+
+
+
+ )
+ return null
+}
diff --git a/apps/desktop/src/components/CommandPalette/CommandApp/DatabaseApp/RowForm.tsx b/apps/desktop/src/components/CommandPalette/CommandApp/DatabaseApp/RowForm.tsx
new file mode 100644
index 000000000..620262970
--- /dev/null
+++ b/apps/desktop/src/components/CommandPalette/CommandApp/DatabaseApp/RowForm.tsx
@@ -0,0 +1,68 @@
+import { forwardRef } from 'react'
+import { Box } from '@fower/react'
+import { CellField } from '@penx/cell-fields'
+import {
+ ICellNode,
+ IColumnNode,
+ IDatabaseNode,
+ IOptionNode,
+ IRowNode,
+ IViewNode,
+} from '@penx/model-types'
+import { mappedByKey } from '@penx/shared'
+import { FieldIcon } from './FieldIcon'
+
+interface Props {
+ rowId: string
+ database: IDatabaseNode
+ views: IViewNode[]
+ columns: IColumnNode[]
+ rows: IRowNode[]
+ cells: ICellNode[]
+ options: IOptionNode[]
+}
+
+export const RowForm = forwardRef(
+ function TagForm(props, ref) {
+ const { columns, views, cells, rowId } = props
+
+ // console.log('========cells;', cells, 'rowId:', rowId)
+
+ const currentView = views[0]
+
+ const columnMap = mappedByKey(columns, 'id')
+ const { viewColumns = [] } = currentView.props
+ const sortedColumns = viewColumns.map(({ columnId }) => columnMap[columnId])
+
+ const rowCells = sortedColumns.map((column) => {
+ return cells.find(
+ (cell) =>
+ cell.props.rowId === rowId && cell.props.columnId === column.id,
+ )!
+ })
+
+ // console.log('========rowCells:', rowCells)
+
+ return (
+
+ {rowCells.map((cell, index) => {
+ // console.log('=====cell:', cell)
+
+ const column = columns.find((col) => col.id === cell.props.columnId)!
+
+ if (!column) return null
+
+ return (
+
+
+
+ {column.props.displayName}
+
+
+
+ )
+ })}
+
+ )
+ },
+)
diff --git a/apps/desktop/src/components/CommandPalette/CommandComponents.ts b/apps/desktop/src/components/CommandPalette/CommandComponents.ts
index a34cf5c71..d57f96673 100644
--- a/apps/desktop/src/components/CommandPalette/CommandComponents.ts
+++ b/apps/desktop/src/components/CommandPalette/CommandComponents.ts
@@ -10,3 +10,5 @@ export const StyledCommandInput = styled(Command.Input)
export const StyledCommandList = styled(Command.List)
export const StyledCommandGroup = styled(Command.Group)
+
+export const StyledCommandEmpty = styled(Command.Empty)
diff --git a/apps/desktop/src/components/CommandPalette/CommandPaletteFooter.tsx b/apps/desktop/src/components/CommandPalette/CommandPaletteFooter.tsx
index 8c2e001b4..d9f8c23c8 100644
--- a/apps/desktop/src/components/CommandPalette/CommandPaletteFooter.tsx
+++ b/apps/desktop/src/components/CommandPalette/CommandPaletteFooter.tsx
@@ -26,10 +26,9 @@ export const CommandPaletteFooter = ({ footerHeight }: Props) => {
px4
toBetween
>
- {currentCommand && (
+ {currentCommand && currentCommand.data.extensionIcon ? (
- )}
- {!currentCommand && (
+ ) : (
{
+ const { database } = useCurrentDatabase()
+ return (
+
+ # {database.props.name}
+
+ )
+}
diff --git a/apps/desktop/src/components/CommandPalette/ListItemIcon.tsx b/apps/desktop/src/components/CommandPalette/ListItemIcon.tsx
index 64db241b1..c407f4aba 100644
--- a/apps/desktop/src/components/CommandPalette/ListItemIcon.tsx
+++ b/apps/desktop/src/components/CommandPalette/ListItemIcon.tsx
@@ -1,13 +1,21 @@
import SVG from 'react-inlinesvg'
import { Box, css, FowerHTMLProps } from '@fower/react'
import Image from 'next/image'
+import { isObjectIcon } from 'penx'
+import { getRandomColor } from '@penx/local-db'
interface ListItemIconProps extends FowerHTMLProps<'div'> {
icon?: string | number
size?: number
+ bg?: string
}
-export function ListItemIcon({ icon, size = 20, ...rest }: ListItemIconProps) {
+export function ListItemIcon({
+ icon,
+ bg,
+ size = 20,
+ ...rest
+}: ListItemIconProps) {
if (!icon) {
return (
@@ -15,13 +23,49 @@ export function ListItemIcon({ icon, size = 20, ...rest }: ListItemIconProps) {
}
if (typeof icon === 'number') {
+ const colorName = bg || getRandomColor('500')
+
+ const arr = [
+ colorName.replace('500', '400'),
+ colorName,
+ colorName.replace('500', '600'),
+ ]
+
return (
-
+
{icon}
)
}
+ // TODO: handle other icon value
+ if (isObjectIcon(icon)) {
+ if (icon.value === '#') {
+ return (
+
+ {icon.value}
+
+ )
+ }
+ }
+
if (icon.startsWith('/')) {
return (
, 'onSelect'> {
index: number
+ value?: any
item: IListItem
isListApp?: boolean
titleLayout?: 'column' | 'row'
+ showIcon?: boolean
onSelect?: (item: IListItem) => void
}
@@ -20,6 +22,8 @@ export const ListItemUI = ({
index,
titleLayout = 'row',
isListApp = false,
+ showIcon = true,
+ value,
...rest
}: ListItemUIProps) => {
const { currentCommand } = useCurrentCommand()
@@ -53,7 +57,8 @@ export const ListItemUI = ({
gap4
roundedLG
black
- value={title}
+ value={value || title}
+ // keywords={[title]}
onSelect={() => {
onSelect?.(item)
}}
@@ -63,7 +68,7 @@ export const ListItemUI = ({
{...rest}
>
-
+ {showIcon && }
{title}
@@ -71,9 +76,9 @@ export const ListItemUI = ({
- {!!item.data?.commandName && (
+ {!!item.data?.type && (
- Command
+ {item.data?.type}
)}
{item?.extra && (
diff --git a/apps/desktop/src/components/CommandPalette/SearchBar.tsx b/apps/desktop/src/components/CommandPalette/SearchBar.tsx
index f6bacbc9f..061e752ea 100644
--- a/apps/desktop/src/components/CommandPalette/SearchBar.tsx
+++ b/apps/desktop/src/components/CommandPalette/SearchBar.tsx
@@ -6,8 +6,10 @@ import { useCommandPosition } from '~/hooks/useCommandPosition'
import { useCurrentCommand } from '~/hooks/useCurrentCommand'
import { useCommands, useItems } from '~/hooks/useItems'
import { useSearch } from '~/hooks/useSearch'
+import { useValue } from '~/hooks/useValue'
import { ToggleModeButton } from '../ToggleModeButton'
import { StyledCommandInput } from './CommandComponents'
+import { DatabaseName } from './DatabaseName'
import { SearchBarFilter } from './SearchBarFilter'
interface Props {
@@ -18,10 +20,22 @@ export const SearchBar = ({ searchBarHeight }: Props) => {
const { setItems } = useItems()
const { commands } = useCommands()
const ref = useRef()
- const { isCommandApp, isCommandAppDetail, backToRoot, setPosition } =
- useCommandPosition()
+ const {
+ isCommandApp,
+ isCommandAppDetail,
+ backToRoot,
+ backToCommandApp,
+ setPosition,
+ } = useCommandPosition()
const { currentCommand } = useCurrentCommand()
+ const currentCommandName = currentCommand?.data?.commandName
+ const isMarketplaceDetail =
+ currentCommandName === 'marketplace' && isCommandAppDetail
+
+ const isDatabaseDetail =
+ currentCommandName === 'database' && isCommandAppDetail
+
return (
{
cursorPointer
onClick={() => {
if (isCommandAppDetail) {
- setPosition('COMMAND_APP')
+ backToCommandApp()
} else {
backToRoot()
+ setSearch('')
}
}}
>
)}
- {!isCommandAppDetail && (
+
+ {isDatabaseDetail && }
+
+ {!isMarketplaceDetail && (
{
onKeyDown={(e) => {
if (e.key === 'Backspace' || e.key === 'delete') {
if (!search && isCommandApp) {
- backToRoot()
+ if (isCommandAppDetail) {
+ backToCommandApp()
+ } else {
+ backToRoot()
+ }
}
}
if (e.key === 'Enter') {
diff --git a/apps/desktop/src/hooks/useCommandPosition.ts b/apps/desktop/src/hooks/useCommandPosition.ts
index 7f5958ff0..1fe13083f 100644
--- a/apps/desktop/src/hooks/useCommandPosition.ts
+++ b/apps/desktop/src/hooks/useCommandPosition.ts
@@ -20,6 +20,13 @@ export function useCommandPosition() {
workerStore.currentWorker.postMessage('BACK_TO_ROOT')
}
}
+
+ function backToCommandApp() {
+ console.log('name....dd COMMAND_APP')
+
+ setPosition('COMMAND_APP')
+ }
+
return {
isRoot: position === 'ROOT',
isCommandApp:
@@ -27,6 +34,7 @@ export function useCommandPosition() {
isCommandAppDetail: position === 'COMMAND_APP_DETAIL',
position,
backToRoot,
+ backToCommandApp,
setPosition,
}
}
diff --git a/apps/desktop/src/hooks/useCurrentDatabase.ts b/apps/desktop/src/hooks/useCurrentDatabase.ts
new file mode 100644
index 000000000..d3122b1b3
--- /dev/null
+++ b/apps/desktop/src/hooks/useCurrentDatabase.ts
@@ -0,0 +1,11 @@
+import { atom, useAtom } from 'jotai'
+import { IDatabaseNode } from '@penx/model-types'
+
+export const currentDatabaseAtom = atom(
+ null as any as IDatabaseNode,
+)
+
+export function useCurrentDatabase() {
+ const [database, setDatabase] = useAtom(currentDatabaseAtom)
+ return { database, setDatabase }
+}
diff --git a/apps/desktop/src/hooks/useHandleSelect.ts b/apps/desktop/src/hooks/useHandleSelect.ts
index 0fe41e5c9..d9e4d9433 100644
--- a/apps/desktop/src/hooks/useHandleSelect.ts
+++ b/apps/desktop/src/hooks/useHandleSelect.ts
@@ -9,12 +9,14 @@ import { useCommandAppLoading } from './useCommandAppLoading'
import { useCommandAppUI } from './useCommandAppUI'
import { useCommandPosition } from './useCommandPosition'
import { useCurrentCommand } from './useCurrentCommand'
+import { useCurrentDatabase } from './useCurrentDatabase'
import { useSearch } from './useSearch'
export function useHandleSelect() {
const { setUI } = useCommandAppUI()
const { setPosition } = useCommandPosition()
const { setCurrentCommand } = useCurrentCommand()
+ const { setDatabase } = useCurrentDatabase()
const { setLoading } = useCommandAppLoading()
const { setSearch } = useSearch()
@@ -26,6 +28,12 @@ export function useHandleSelect() {
setPosition('COMMAND_APP')
+ if (item.data?.type === 'Database') {
+ setDatabase(item.data.database)
+ setUI({ type: 'database' })
+ return
+ }
+
const ext = await db.getExtensionBySlug(item.data.extensionSlug)
if (!ext) return
@@ -134,7 +142,7 @@ export function useHandleSelect() {
}
if (
- ['marketplace', 'today', 'database', 'clipboard-history'].includes(
+ ['marketplace', 'today', 'clipboard-history'].includes(
event.data?.type,
)
) {
diff --git a/apps/desktop/src/hooks/useItems.ts b/apps/desktop/src/hooks/useItems.ts
index 1489768a3..4a5112d1d 100644
--- a/apps/desktop/src/hooks/useItems.ts
+++ b/apps/desktop/src/hooks/useItems.ts
@@ -3,6 +3,7 @@ import { useQuery } from '@tanstack/react-query'
import { atom, useAtom, useSetAtom } from 'jotai'
import { IListItem } from 'penx'
import { db } from '@penx/local-db'
+import { Node } from '@penx/model'
import { useSearch } from './useSearch'
export const itemsAtom = atom([])
@@ -39,9 +40,12 @@ export function useCommands() {
export function useLoadCommands() {
return useQuery(['commands'], async () => {
- const extensions = await db.listExtensions()
+ const [extensions, databases] = await Promise.all([
+ db.listExtensions(),
+ db.listDatabases(),
+ ])
- return extensions.reduce((acc, cur) => {
+ const commands = extensions.reduce((acc, cur) => {
return [
...acc,
...cur.commands.map((item) => {
@@ -61,6 +65,7 @@ export function useLoadCommands() {
subtitle: cur.name,
icon: getIcon(),
data: {
+ type: 'Command',
assets: cur.assets,
filters: item.filters,
runtime: item.runtime,
@@ -73,6 +78,29 @@ export function useLoadCommands() {
}),
]
}, [] as IListItem[])
+
+ const databaseItems = databases.reduce((acc, item) => {
+ const node = new Node(item)
+ if (node.isSpecialDatabase) return acc
+ return [
+ ...acc,
+ {
+ type: 'command',
+ title: node.tagName,
+ subtitle: '',
+ icon: {
+ value: '#',
+ bg: node.tagColor,
+ },
+ data: {
+ type: 'Database',
+ database: item,
+ },
+ } as IListItem,
+ ]
+ }, [] as IListItem[])
+
+ return [...commands, ...databaseItems]
})
}
diff --git a/packages/app/src/Workbench/PanelItem.tsx b/packages/app/src/Workbench/PanelItem.tsx
index c09666c9e..a46aa43e1 100644
--- a/packages/app/src/Workbench/PanelItem.tsx
+++ b/packages/app/src/Workbench/PanelItem.tsx
@@ -87,8 +87,6 @@ export function PanelItem({ node, index }: Props) {
const plugins = [withBulletPlugin]
- const { isOpen, setIsOpen } = useQuickAdd()
-
if (!activeSpace.isOutliner) {
plugins.push(withAutoNodeId)
}
diff --git a/packages/app/src/Workbench/Settings/AccountSettings/AccountSettings.tsx b/packages/app/src/Workbench/Settings/AccountSettings/AccountSettings.tsx
index 5e302e4ac..bedec24d3 100644
--- a/packages/app/src/Workbench/Settings/AccountSettings/AccountSettings.tsx
+++ b/packages/app/src/Workbench/Settings/AccountSettings/AccountSettings.tsx
@@ -16,7 +16,7 @@ export function AccountSettings() {
{data.userId}
diff --git a/packages/app/src/Workbench/Sidebar/Sidebar.tsx b/packages/app/src/Workbench/Sidebar/Sidebar.tsx
index 3a0c8c30e..a5771872c 100644
--- a/packages/app/src/Workbench/Sidebar/Sidebar.tsx
+++ b/packages/app/src/Workbench/Sidebar/Sidebar.tsx
@@ -152,7 +152,7 @@ export const Sidebar = memo(
}}
/>
- {
store.router.routeTo('MARKETPLACE')
}}
- />
+ /> */}
{/* {
- {!isBackedUp && session && name === 'NODE' && }
+ {/* {!isBackedUp && session && name === 'NODE' && } */}
= memo(
function SingleSelectCell(props) {
const { cell } = props
- const { options, deleteCellOption } = useDatabaseContext()
+ const { options = [], deleteCellOption } = useDatabaseContext()
const [value, setValue] = useState(
Array.isArray(cell.props.data) ? cell.props.data : [],
)
diff --git a/packages/local-db/src/libs/getRandomColor.ts b/packages/local-db/src/libs/getRandomColor.ts
index d79bc7feb..b16f9eff7 100644
--- a/packages/local-db/src/libs/getRandomColor.ts
+++ b/packages/local-db/src/libs/getRandomColor.ts
@@ -12,7 +12,7 @@ export function getColorNames(postfix = '500') {
}
export function getRandomColor(postfix = '500'): string {
- const keys = getColorNames()
+ const keys = getColorNames(postfix)
const index = Math.floor(Math.random() * keys.length)
return keys[index]!
diff --git a/packages/model/src/Node.ts b/packages/model/src/Node.ts
index b0aa9f939..47fc73c77 100644
--- a/packages/model/src/Node.ts
+++ b/packages/model/src/Node.ts
@@ -226,6 +226,12 @@ export class Node {
return today === this.date
}
+ get isSpecialDatabase() {
+ if (this.tagName === '__FILE__' || this.tagName === '__TODO__') return true
+ if (this.tagName.startsWith('$template__')) return true
+ return false
+ }
+
get fileHash() {
const element = this.element as any
try {
diff --git a/packages/penx/src/components/ListBuilder.ts b/packages/penx/src/components/ListBuilder.ts
index 4dbe572e4..061243f4d 100644
--- a/packages/penx/src/components/ListBuilder.ts
+++ b/packages/penx/src/components/ListBuilder.ts
@@ -26,6 +26,13 @@ export interface ListHeading {
title: string
}
+export interface ObjectIcon {
+ value: ImageLike | undefined | null
+ tooltip?: string
+ color?: string
+ bg?: string
+}
+
export interface IListItem {
id?: string
@@ -45,12 +52,7 @@ export interface IListItem {
tooltip?: string | null
}
- icon?:
- | ImageLike
- | {
- value: ImageLike | undefined | null
- tooltip: string
- }
+ icon?: ImageLike | ObjectIcon
actions?: ListItemAction[]
@@ -78,6 +80,10 @@ export function isListJSON(json: any): json is ListJSON {
return json.type === 'list'
}
+export function isObjectIcon(icon: any): icon is ObjectIcon {
+ return typeof icon === 'object' && icon?.value !== undefined
+}
+
export class ListBuilder {
isShowingDetail = false
isLoading = false
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 105ee7ab6..c4a3be463 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -812,6 +812,9 @@ importers:
'@penx/app':
specifier: workspace:*
version: link:../../packages/app
+ '@penx/cell-fields':
+ specifier: workspace:*
+ version: link:../../packages/cell-fields
'@penx/cmdk':
specifier: workspace:*
version: link:../../packages/cmdk
@@ -21842,6 +21845,7 @@ packages:
rimraf@2.6.3:
resolution: {integrity: sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==}
+ deprecated: Rimraf versions prior to v4 are no longer supported
hasBin: true
rimraf@2.7.1: