betterstart-cli 0.0.12 → 0.0.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (117) hide show
  1. package/dist/assets/adapters/next/plugins/blog/plugin.ts +1 -1
  2. package/dist/assets/adapters/next/plugins/blog/schemas/menus.json +115 -0
  3. package/dist/assets/adapters/next/plugins/blog/schemas/posts.json +2 -7
  4. package/dist/assets/adapters/next/templates/init/components/layouts/admin-sidebar.tsx +2 -2
  5. package/dist/assets/adapters/next/templates/init/components/shared/dev-mode/lifecycle-hooks-tab.tsx +2 -2
  6. package/dist/assets/adapters/next/templates/init/components/shared/dev-mode/snippets-tab.tsx +1 -1
  7. package/dist/assets/adapters/next/templates/init/components/shared/dev-mode-integrate.tsx +1 -1
  8. package/dist/assets/adapters/next/templates/init/components/shared/entity-versions/entity-versions-drawer.tsx +1 -1
  9. package/dist/assets/adapters/next/templates/init/components/shared/media/edit-media-dialog-content.tsx +2 -2
  10. package/dist/assets/adapters/next/templates/init/components/shared/media/media-url-importer.tsx +1 -1
  11. package/dist/assets/adapters/next/templates/init/components/shared/page-header.tsx +20 -17
  12. package/dist/assets/adapters/next/templates/init/data/navigation.ts +2 -1
  13. package/dist/assets/adapters/next/templates/init/hooks/content-editor/use-content-editor-slash-menu.ts +28 -2
  14. package/dist/assets/adapters/next/templates/init/hooks/content-editor/use-content-editor.ts +22 -1
  15. package/dist/assets/adapters/next/templates/init/hooks/use-audit-log.ts +23 -0
  16. package/dist/assets/adapters/next/templates/init/lib/actions/audit/cleanup-audit-logs.ts +63 -0
  17. package/dist/assets/adapters/next/templates/init/lib/actions/audit/get-audit-logs.ts +97 -0
  18. package/dist/assets/adapters/next/templates/init/lib/actions/audit/index.ts +17 -0
  19. package/dist/assets/adapters/next/templates/init/lib/actions/audit/record-audit-event.ts +37 -0
  20. package/dist/assets/adapters/next/templates/init/lib/actions/audit/register.ts +13 -0
  21. package/dist/assets/adapters/next/templates/init/lib/actions/audit/request-metadata.ts +37 -0
  22. package/dist/assets/adapters/next/templates/init/lib/actions/audit/types.ts +72 -0
  23. package/dist/assets/adapters/next/templates/init/lib/actions/forms/upsert-form-settings.ts +3 -0
  24. package/dist/assets/adapters/next/templates/init/lib/actions/media/create-media.ts +19 -0
  25. package/dist/assets/adapters/next/templates/init/lib/actions/media/delete-media-bulk.ts +23 -0
  26. package/dist/assets/adapters/next/templates/init/lib/actions/media/delete-media.ts +21 -0
  27. package/dist/assets/adapters/next/templates/init/lib/actions/media/update-media.ts +23 -1
  28. package/dist/assets/adapters/next/templates/init/lib/actions/profile/update-email.ts +17 -0
  29. package/dist/assets/adapters/next/templates/init/lib/actions/upload/upload-files.ts +3 -0
  30. package/dist/assets/adapters/next/templates/init/lib/actions/upload/upload-media-from-url.ts +3 -0
  31. package/dist/assets/adapters/next/templates/init/lib/actions/users/create-user.ts +19 -1
  32. package/dist/assets/adapters/next/templates/init/lib/actions/users/delete-user.ts +23 -1
  33. package/dist/assets/adapters/next/templates/init/lib/actions/users/update-user-role.ts +23 -1
  34. package/dist/assets/adapters/next/templates/init/lib/db/core/schema.ts +49 -0
  35. package/dist/assets/adapters/next/templates/init/lib/lifecycle-hooks/index.ts +6 -1
  36. package/dist/assets/adapters/next/templates/init/lib/lifecycle-hooks/register.ts +3 -0
  37. package/dist/assets/adapters/next/templates/init/lib/lifecycle-hooks/registry.ts +36 -1
  38. package/dist/assets/adapters/next/templates/init/lib/lifecycle-hooks/runner.ts +116 -12
  39. package/dist/assets/adapters/next/templates/init/lib/lifecycle-hooks/types.ts +33 -0
  40. package/dist/assets/adapters/next/templates/init/pages/audit-log/audit-log-detail-drawer.tsx +96 -0
  41. package/dist/assets/adapters/next/templates/init/pages/audit-log/audit-log-detail-row.tsx +17 -0
  42. package/dist/assets/adapters/next/templates/init/pages/audit-log/audit-log-filters.tsx +152 -0
  43. package/dist/assets/adapters/next/templates/init/pages/audit-log/audit-log-page-content.tsx +279 -0
  44. package/dist/assets/adapters/next/templates/init/pages/audit-log/audit-log-page-skeleton.tsx +19 -0
  45. package/dist/assets/adapters/next/templates/init/pages/audit-log/audit-log-page.tsx +14 -0
  46. package/dist/assets/adapters/next/templates/init/pages/dashboard-page.tsx +1 -1
  47. package/dist/assets/adapters/next/templates/init/pages/forgot-password-page-skeleton.tsx +1 -1
  48. package/dist/assets/adapters/next/templates/init/pages/login-page-skeleton.tsx +1 -1
  49. package/dist/assets/adapters/next/templates/init/pages/reset-password-page-skeleton.tsx +1 -1
  50. package/dist/assets/adapters/next/templates/init/utils/audit/audit.ts +54 -0
  51. package/dist/assets/adapters/next/templates/init/utils/editor/tiptap.ts +0 -24
  52. package/dist/assets/shared-assets/react-admin/custom/content-editor/horizontal-rule-button.tsx +1 -1
  53. package/dist/assets/shared-assets/react-admin/custom/content-editor/index.tsx +6 -0
  54. package/dist/assets/shared-assets/react-admin/custom/content-editor/main-toolbar-content.tsx +1 -1
  55. package/dist/assets/shared-assets/react-admin/custom/content-editor/math-bubble-menu.tsx +1 -1
  56. package/dist/assets/shared-assets/react-admin/custom/content-editor/math-button.tsx +1 -1
  57. package/dist/assets/shared-assets/react-admin/custom/content-editor/math-editor-controls.tsx +27 -40
  58. package/dist/assets/shared-assets/react-admin/custom/content-editor/math-popover-button.tsx +0 -1
  59. package/dist/assets/shared-assets/react-admin/custom/content-editor/mobile-toolbar-content.tsx +4 -4
  60. package/dist/assets/shared-assets/react-admin/custom/content-editor/mode-toggle-button.tsx +1 -1
  61. package/dist/assets/shared-assets/react-admin/custom/content-editor/selection-bubble-menu.tsx +45 -8
  62. package/dist/assets/shared-assets/react-admin/custom/content-editor/slash-command-menu.tsx +153 -27
  63. package/dist/assets/shared-assets/react-admin/custom/content-editor/source-mode-dropdown-button.tsx +1 -1
  64. package/dist/assets/shared-assets/react-admin/custom/content-editor/source-mode.tsx +9 -9
  65. package/dist/assets/shared-assets/react-admin/custom/content-editor/table-bubble-menu.tsx +1 -1
  66. package/dist/assets/shared-assets/react-admin/custom/content-editor/table-button.tsx +9 -4
  67. package/dist/assets/shared-assets/react-admin/custom/content-editor/tiptap-ui/blockquote-button/blockquote-button.tsx +1 -1
  68. package/dist/assets/shared-assets/react-admin/custom/content-editor/tiptap-ui/code-block-button/code-block-button.tsx +1 -1
  69. package/dist/assets/shared-assets/react-admin/custom/content-editor/tiptap-ui/color-highlight-popover/color-highlight-popover-button.tsx +1 -1
  70. package/dist/assets/shared-assets/react-admin/custom/content-editor/tiptap-ui/color-highlight-popover/color-highlight-popover-content.tsx +1 -1
  71. package/dist/assets/shared-assets/react-admin/custom/content-editor/tiptap-ui/color-highlight-popover/color-highlight-popover.tsx +11 -2
  72. package/dist/assets/shared-assets/react-admin/custom/content-editor/tiptap-ui/color-highlight-popover/source-color-highlight-popover.tsx +1 -1
  73. package/dist/assets/shared-assets/react-admin/custom/content-editor/tiptap-ui/heading-dropdown-menu/heading-dropdown-menu-item.tsx +1 -1
  74. package/dist/assets/shared-assets/react-admin/custom/content-editor/tiptap-ui/heading-dropdown-menu/heading-dropdown-menu.tsx +9 -4
  75. package/dist/assets/shared-assets/react-admin/custom/content-editor/tiptap-ui/link-popover/link-button.tsx +1 -1
  76. package/dist/assets/shared-assets/react-admin/custom/content-editor/tiptap-ui/link-popover/link-control-popover.tsx +2 -4
  77. package/dist/assets/shared-assets/react-admin/custom/content-editor/tiptap-ui/link-popover/link-main.tsx +19 -45
  78. package/dist/assets/shared-assets/react-admin/custom/content-editor/tiptap-ui/link-popover/link-popover.tsx +1 -1
  79. package/dist/assets/shared-assets/react-admin/custom/content-editor/tiptap-ui/list-dropdown-menu/list-dropdown-menu-item.tsx +1 -1
  80. package/dist/assets/shared-assets/react-admin/custom/content-editor/tiptap-ui/list-dropdown-menu/list-dropdown-menu.tsx +9 -4
  81. package/dist/assets/shared-assets/react-admin/custom/content-editor/tiptap-ui/mark-button/mark-button.tsx +1 -1
  82. package/dist/assets/shared-assets/react-admin/custom/content-editor/tiptap-ui/undo-redo-button/undo-redo-button.tsx +1 -1
  83. package/dist/assets/shared-assets/react-admin/custom/date-range-picker.tsx +3 -3
  84. package/dist/assets/shared-assets/react-admin/custom/gallery-field.tsx +2 -2
  85. package/dist/assets/shared-assets/react-admin/custom/media-gallery-field.tsx +2 -2
  86. package/dist/assets/shared-assets/react-admin/custom/placeholder-card.tsx +1 -1
  87. package/dist/assets/shared-assets/react-admin/custom/placeholder.tsx +1 -1
  88. package/dist/assets/shared-assets/react-admin/custom/sortable-gallery-item.tsx +1 -1
  89. package/dist/assets/shared-assets/react-admin/schema.json +0 -23
  90. package/dist/assets/shared-assets/react-admin/ui/accordion.tsx +1 -1
  91. package/dist/assets/shared-assets/react-admin/ui/alert.tsx +1 -1
  92. package/dist/assets/shared-assets/react-admin/ui/button.tsx +18 -18
  93. package/dist/assets/shared-assets/react-admin/ui/card.tsx +3 -3
  94. package/dist/assets/shared-assets/react-admin/ui/chart.tsx +1 -1
  95. package/dist/assets/shared-assets/react-admin/ui/combobox.tsx +29 -5
  96. package/dist/assets/shared-assets/react-admin/ui/command.tsx +1 -1
  97. package/dist/assets/shared-assets/react-admin/ui/context-menu.tsx +6 -6
  98. package/dist/assets/shared-assets/react-admin/ui/drawer.tsx +7 -3
  99. package/dist/assets/shared-assets/react-admin/ui/dropdown-menu.tsx +32 -17
  100. package/dist/assets/shared-assets/react-admin/ui/empty.tsx +2 -2
  101. package/dist/assets/shared-assets/react-admin/ui/field.tsx +1 -1
  102. package/dist/assets/shared-assets/react-admin/ui/hover-card.tsx +1 -1
  103. package/dist/assets/shared-assets/react-admin/ui/input-group.tsx +1 -1
  104. package/dist/assets/shared-assets/react-admin/ui/item.tsx +2 -2
  105. package/dist/assets/shared-assets/react-admin/ui/menubar.tsx +7 -7
  106. package/dist/assets/shared-assets/react-admin/ui/navigation-menu.tsx +4 -4
  107. package/dist/assets/shared-assets/react-admin/ui/popover.tsx +1 -1
  108. package/dist/assets/shared-assets/react-admin/ui/resizable.tsx +1 -1
  109. package/dist/assets/shared-assets/react-admin/ui/select.tsx +2 -2
  110. package/dist/assets/shared-assets/react-admin/ui/sidebar.tsx +3 -3
  111. package/dist/assets/shared-assets/react-admin/ui/skeleton.tsx +1 -1
  112. package/dist/assets/shared-assets/react-admin/ui/tabs.tsx +2 -2
  113. package/dist/assets/shared-assets/react-admin/ui/textarea.tsx +1 -1
  114. package/dist/assets/shared-assets/react-admin/ui/tooltip.tsx +1 -1
  115. package/dist/cli.js +2769 -802
  116. package/dist/cli.js.map +1 -1
  117. package/package.json +1 -1
@@ -10,6 +10,7 @@ import { Separator } from '@admin/components/ui/separator'
10
10
  import { cn } from '@admin/utils/shared/cn'
11
11
  import type { Editor } from '@tiptap/core'
12
12
  import { BubbleMenu } from '@tiptap/react/menus'
13
+ import * as React from 'react'
13
14
 
14
15
  const HIDDEN_SELECTION_TOOLBAR_TYPES = [
15
16
  'mediaGalleryPlaceholder',
@@ -26,6 +27,31 @@ const HIDDEN_SELECTION_TOOLBAR_TYPES = [
26
27
  ] as const
27
28
 
28
29
  export function SelectionBubbleMenu({ editor }: { editor: Editor }) {
30
+ const [hasOpenSelectionMenuChild, setHasOpenSelectionMenuChild] = React.useState(false)
31
+ const hasOpenSelectionMenuChildRef = React.useRef(false)
32
+ const setSelectionMenuChildOpen = React.useCallback((isOpen: boolean) => {
33
+ hasOpenSelectionMenuChildRef.current = isOpen
34
+ setHasOpenSelectionMenuChild(isOpen)
35
+ }, [])
36
+ const handleSelectionMenuChildOpenChange = React.useCallback(
37
+ (isOpen: boolean) => {
38
+ setSelectionMenuChildOpen(isOpen)
39
+ },
40
+ [setSelectionMenuChildOpen]
41
+ )
42
+ const handleSelectionMenuPointerDown = React.useCallback(
43
+ (event: React.PointerEvent<HTMLDivElement>) => {
44
+ const target = event.target
45
+ if (!(target instanceof Element)) return
46
+
47
+ const menuTrigger = target.closest('button[aria-haspopup], button[aria-expanded]')
48
+ if (menuTrigger) {
49
+ setSelectionMenuChildOpen(true)
50
+ }
51
+ },
52
+ [setSelectionMenuChildOpen]
53
+ )
54
+
29
55
  return (
30
56
  <>
31
57
  <BubbleMenu
@@ -46,8 +72,8 @@ export function SelectionBubbleMenu({ editor }: { editor: Editor }) {
46
72
  role="toolbar"
47
73
  aria-label="Link formatting toolbar"
48
74
  className={cn(
49
- 'flex items-center gap-1 overflow-x-auto border-border rounded-full border bg-popover p-1 text-popover-foreground shadow-menu [scrollbar-width:none] [&::-webkit-scrollbar]:hidden',
50
- 'relative z-50 p-0'
75
+ 'flex items-center gap-1 overflow-x-auto border-border border bg-popover p-4 text-foreground shadow-2xl [scrollbar-width:none] [&::-webkit-scrollbar]:hidden rounded-xl',
76
+ 'relative z-10'
51
77
  )}
52
78
  >
53
79
  <LinkContent editor={editor} />
@@ -60,7 +86,7 @@ export function SelectionBubbleMenu({ editor }: { editor: Editor }) {
60
86
  updateDelay={0}
61
87
  shouldShow={({ editor: currentEditor, from, to }) =>
62
88
  currentEditor.isEditable &&
63
- from !== to &&
89
+ (from !== to || hasOpenSelectionMenuChild || hasOpenSelectionMenuChildRef.current) &&
64
90
  HIDDEN_SELECTION_TOOLBAR_TYPES.every((type) => !currentEditor.isActive(type))
65
91
  }
66
92
  options={{
@@ -73,14 +99,22 @@ export function SelectionBubbleMenu({ editor }: { editor: Editor }) {
73
99
  <div
74
100
  role="toolbar"
75
101
  aria-label="Selection formatting toolbar"
102
+ onPointerDownCapture={handleSelectionMenuPointerDown}
76
103
  className={cn(
77
104
  'flex items-center gap-1 overflow-x-auto border-border rounded-full border bg-popover p-1 text-popover-foreground shadow-menu [scrollbar-width:none] [&::-webkit-scrollbar]:hidden',
78
- 'relative z-50'
105
+ 'relative z-10'
79
106
  )}
80
107
  >
81
108
  <div className="flex items-center gap-0.5">
82
- <HeadingDropdownMenu editor={editor} />
83
- <ListDropdownMenu editor={editor} types={['bulletList', 'orderedList', 'taskList']} />
109
+ <HeadingDropdownMenu
110
+ editor={editor}
111
+ onOpenChange={handleSelectionMenuChildOpenChange}
112
+ />
113
+ <ListDropdownMenu
114
+ editor={editor}
115
+ types={['bulletList', 'orderedList', 'taskList']}
116
+ onOpenChange={handleSelectionMenuChildOpenChange}
117
+ />
84
118
  </div>
85
119
 
86
120
  <Separator
@@ -95,8 +129,11 @@ export function SelectionBubbleMenu({ editor }: { editor: Editor }) {
95
129
  <MarkButton editor={editor} type="strike" />
96
130
  <MarkButton editor={editor} type="code" />
97
131
  <MarkButton editor={editor} type="underline" />
98
- <ColorHighlightPopover editor={editor} />
99
- <LinkPopover editor={editor} />
132
+ <ColorHighlightPopover
133
+ editor={editor}
134
+ onOpenChange={handleSelectionMenuChildOpenChange}
135
+ />
136
+ <LinkPopover editor={editor} onOpenChange={handleSelectionMenuChildOpenChange} />
100
137
  </div>
101
138
  </div>
102
139
  </BubbleMenu>
@@ -1,65 +1,191 @@
1
+ 'use client'
2
+
3
+ import {
4
+ Combobox,
5
+ ComboboxContent,
6
+ ComboboxEmpty,
7
+ ComboboxInput,
8
+ ComboboxItem,
9
+ ComboboxList
10
+ } from '@admin/components/ui/combobox'
1
11
  import type { ContentEditorSlashCommand } from '@admin/utils/editor/slash-commands'
2
- import { getSlashMenuStyle, type SlashMenuState } from '@admin/utils/editor/tiptap'
12
+ import type { SlashMenuState } from '@admin/utils/editor/tiptap'
3
13
  import { cn } from '@admin/utils/shared/cn'
14
+ import * as React from 'react'
4
15
 
5
16
  export function SlashCommandMenu({
6
17
  activeIndex,
7
18
  commands,
8
19
  onActiveIndexChange,
20
+ onClose,
9
21
  onCommandSelect,
10
- slashMenu
22
+ onSearchChange,
23
+ slashMenu,
24
+ searchQuery
11
25
  }: {
12
26
  activeIndex: number
13
27
  commands: ContentEditorSlashCommand[]
14
28
  onActiveIndexChange: (index: number) => void
29
+ onClose: () => void
15
30
  onCommandSelect: (command: ContentEditorSlashCommand) => void
31
+ onSearchChange: (query: string) => void
16
32
  slashMenu: SlashMenuState | null
33
+ searchQuery: string
17
34
  }) {
35
+ const activeItemRef = React.useRef<HTMLDivElement | null>(null)
36
+ const searchInputRef = React.useRef<HTMLInputElement | null>(null)
37
+ const activeCommandId = commands[activeIndex]?.id
38
+
39
+ const handleSearchKeyDown = (event: React.KeyboardEvent<HTMLInputElement>) => {
40
+ const stopComboboxKeyHandling = () => {
41
+ const baseUIEvent = event as React.KeyboardEvent<HTMLInputElement> & {
42
+ preventBaseUIHandler?: () => void
43
+ }
44
+
45
+ event.preventDefault()
46
+ event.stopPropagation()
47
+ baseUIEvent.preventBaseUIHandler?.()
48
+ }
49
+
50
+ if (event.key === 'ArrowDown') {
51
+ stopComboboxKeyHandling()
52
+ onActiveIndexChange(commands.length > 0 ? (activeIndex + 1) % commands.length : 0)
53
+ return
54
+ }
55
+
56
+ if (event.key === 'ArrowUp') {
57
+ stopComboboxKeyHandling()
58
+ onActiveIndexChange(
59
+ commands.length > 0 ? (activeIndex - 1 + commands.length) % commands.length : 0
60
+ )
61
+ return
62
+ }
63
+
64
+ if (event.key === 'Escape') {
65
+ stopComboboxKeyHandling()
66
+ onClose()
67
+ return
68
+ }
69
+
70
+ if (event.key === 'Enter' || event.key === 'Tab') {
71
+ const command = commands[activeIndex]
72
+ if (!command) return
73
+
74
+ stopComboboxKeyHandling()
75
+ onCommandSelect(command)
76
+ }
77
+ }
78
+
79
+ React.useEffect(() => {
80
+ if (!slashMenu) return
81
+
82
+ const focusSearchInput = () => {
83
+ searchInputRef.current?.focus({ preventScroll: true })
84
+ searchInputRef.current?.select()
85
+ }
86
+
87
+ focusSearchInput()
88
+ const frame = requestAnimationFrame(focusSearchInput)
89
+
90
+ return () => cancelAnimationFrame(frame)
91
+ }, [slashMenu])
92
+
93
+ React.useEffect(() => {
94
+ if (!activeCommandId) return
95
+
96
+ activeItemRef.current?.scrollIntoView({ block: 'nearest' })
97
+ }, [activeCommandId])
98
+
18
99
  if (!slashMenu) return null
19
100
 
101
+ const slashAnchor = {
102
+ getBoundingClientRect: () => slashMenu.rect
103
+ }
104
+
20
105
  return (
21
- <div
22
- className="z-50 w-72 max-w-[calc(100vw-2rem)] rounded-md border bg-popover p-1 text-popover-foreground shadow-md"
23
- style={getSlashMenuStyle(slashMenu.rect)}
106
+ <Combobox<ContentEditorSlashCommand>
107
+ autoHighlight
108
+ filter={null}
109
+ filteredItems={commands}
110
+ inputValue={searchQuery}
111
+ itemToStringLabel={(command) => command.title}
112
+ itemToStringValue={(command) => command.id}
113
+ items={commands}
114
+ onInputValueChange={onSearchChange}
115
+ onItemHighlighted={(command) => {
116
+ if (!command) return
117
+
118
+ const nextIndex = commands.findIndex((item) => item.id === command.id)
119
+ if (nextIndex >= 0) onActiveIndexChange(nextIndex)
120
+ }}
121
+ onOpenChange={(isOpen) => {
122
+ if (!isOpen) onClose()
123
+ }}
124
+ onValueChange={(command) => {
125
+ if (command) onCommandSelect(command)
126
+ }}
127
+ open
128
+ value={null}
24
129
  >
25
- {commands.length > 0 ? (
26
- <div className="max-h-80 overflow-y-auto">
130
+ <ComboboxContent
131
+ anchor={slashAnchor}
132
+ align="start"
133
+ className="w-72 min-w-72 max-w-[calc(100vw-2rem)] p-2 gap-2 flex flex-col"
134
+ collisionPadding={8}
135
+ finalFocus={false}
136
+ initialFocus={false}
137
+ positionMethod="fixed"
138
+ side="bottom"
139
+ sideOffset={8}
140
+ data-content-editor-slash-menu
141
+ >
142
+ <ComboboxInput
143
+ aria-label="Search commands"
144
+ ref={searchInputRef}
145
+ onKeyDown={handleSearchKeyDown}
146
+ placeholder="Search commands..."
147
+ showClear
148
+ showTrigger={false}
149
+ className="border border-border"
150
+ />
151
+ <ComboboxEmpty className="py-6">No commands found</ComboboxEmpty>
152
+ <ComboboxList className="max-h-80 p-0">
27
153
  {commands.map((command, index) => {
28
154
  const Icon = command.Icon
29
155
  const isActive = index === activeIndex
30
156
 
31
157
  return (
32
- <button
158
+ <ComboboxItem
159
+ index={index}
33
160
  key={command.id}
34
- type="button"
161
+ data-active={isActive ? 'true' : undefined}
162
+ ref={(node) => {
163
+ if (isActive) activeItemRef.current = node
164
+ }}
165
+ onMouseDown={(event) => event.preventDefault()}
166
+ onMouseEnter={() => onActiveIndexChange(index)}
167
+ value={command}
35
168
  className={cn(
36
- 'flex w-full items-start gap-3 rounded-sm px-2.5 py-2 text-left text-sm outline-none transition-colors',
169
+ 'border border-transparent p-2 data-highlighted:bg-card! data-highlighted:border-border!',
37
170
  {
38
- 'bg-accent text-accent-foreground': isActive,
39
- 'text-popover-foreground hover:bg-accent hover:text-accent-foreground':
40
- !isActive
171
+ 'bg-card border-border shadow-input text-foreground': isActive
41
172
  }
42
173
  )}
43
- onMouseDown={(event) => event.preventDefault()}
44
- onMouseEnter={() => onActiveIndexChange(index)}
45
- onClick={() => onCommandSelect(command)}
46
174
  >
47
- <span className="mt-0.5 flex size-7 shrink-0 items-center justify-center rounded-sm bg-muted">
48
- <Icon className="size-4" />
175
+ <span className="flex size-8 shrink-0 items-center justify-center rounded-sm bg-card border border-border shadow-input">
176
+ <Icon />
49
177
  </span>
50
- <span className="min-w-0">
51
- <span className="block font-medium leading-none">{command.title}</span>
52
- <span className="mt-1 block truncate text-xs text-muted-foreground">
178
+ <span className="min-w-0 space-y-1 flex flex-col w-full">
179
+ <span className="font-medium leading-none">{command.title}</span>
180
+ <span className="truncate text-xs text-muted-foreground">
53
181
  {command.description}
54
182
  </span>
55
183
  </span>
56
- </button>
184
+ </ComboboxItem>
57
185
  )
58
186
  })}
59
- </div>
60
- ) : (
61
- <div className="px-3 py-6 text-center text-sm text-muted-foreground">No commands found</div>
62
- )}
63
- </div>
187
+ </ComboboxList>
188
+ </ComboboxContent>
189
+ </Combobox>
64
190
  )
65
191
  }
@@ -40,7 +40,7 @@ export function SourceModeDropdownButton({
40
40
  onPointerDown={(event) => event.preventDefault()}
41
41
  onClick={() => setIsOpen((current) => !current)}
42
42
  >
43
- <Icon className="tiptap-button-icon" />
43
+ <Icon />
44
44
  <ChevronDownIcon className="tiptap-button-dropdown-small" />
45
45
  </Button>
46
46
  </DropdownMenuTrigger>
@@ -140,7 +140,7 @@ export function SourceMode({
140
140
  aria-label="Undo"
141
141
  title="Undo"
142
142
  >
143
- <Undo2Icon className="tiptap-button-icon" />
143
+ <Undo2Icon />
144
144
  </Button>
145
145
  <Button
146
146
  type="button"
@@ -151,7 +151,7 @@ export function SourceMode({
151
151
  aria-label="Redo"
152
152
  title="Redo"
153
153
  >
154
- <Redo2Icon className="tiptap-button-icon" />
154
+ <Redo2Icon />
155
155
  </Button>
156
156
  </div>
157
157
 
@@ -178,7 +178,7 @@ export function SourceMode({
178
178
  title={option.label}
179
179
  onSelect={() => applyHeading(option.level)}
180
180
  >
181
- <Icon className="tiptap-button-icon" />
181
+ <Icon />
182
182
  <span className="tiptap-button-text">{option.label}</span>
183
183
  </DropdownMenuItem>
184
184
  )
@@ -196,7 +196,7 @@ export function SourceMode({
196
196
  title={option.label}
197
197
  onSelect={() => applyList(option.type)}
198
198
  >
199
- <Icon className="tiptap-button-icon" />
199
+ <Icon />
200
200
  <span className="tiptap-button-text">{option.label}</span>
201
201
  </DropdownMenuItem>
202
202
  )
@@ -211,7 +211,7 @@ export function SourceMode({
211
211
  aria-label="Blockquote"
212
212
  title="Blockquote"
213
213
  >
214
- <BlockquoteIcon className="tiptap-button-icon" />
214
+ <BlockquoteIcon />
215
215
  </Button>
216
216
  <Button
217
217
  type="button"
@@ -222,7 +222,7 @@ export function SourceMode({
222
222
  aria-label="Code Block"
223
223
  title="Code Block"
224
224
  >
225
- <CodeBlockIcon className="tiptap-button-icon" />
225
+ <CodeBlockIcon />
226
226
  </Button>
227
227
  <Button
228
228
  type="button"
@@ -233,7 +233,7 @@ export function SourceMode({
233
233
  aria-label="Insert divider"
234
234
  title="Divider"
235
235
  >
236
- <Minus className="tiptap-button-icon" />
236
+ <Minus />
237
237
  </Button>
238
238
  </div>
239
239
 
@@ -281,7 +281,7 @@ export function SourceMode({
281
281
  aria-label={mark.label}
282
282
  title={mark.label}
283
283
  >
284
- <mark.Icon className="tiptap-button-icon" />
284
+ <mark.Icon />
285
285
  </Button>
286
286
  ))}
287
287
  <SourceColorHighlightPopover
@@ -322,7 +322,7 @@ export function SourceMode({
322
322
  aria-label="Add media"
323
323
  title="Add media"
324
324
  >
325
- <ImagePlusIcon className="tiptap-button-icon" />
325
+ <ImagePlusIcon />
326
326
  <span className="tiptap-button-text">Media</span>
327
327
  </Button>
328
328
  </div>
@@ -227,7 +227,7 @@ export function TableBubbleMenu({ editor }: { editor: Editor }) {
227
227
  }}
228
228
  title={label}
229
229
  >
230
- <Icon className="tiptap-button-icon" />
230
+ <Icon />
231
231
  </Button>
232
232
  )
233
233
  })}
@@ -76,11 +76,11 @@ export function TableButton({ disabled, editor, onInsertTable }: TableButtonProp
76
76
  onPointerDown={(event) => event.preventDefault()}
77
77
  onClick={() => setIsOpen((current) => !current)}
78
78
  >
79
- <TableIcon className="tiptap-button-icon" />
79
+ <TableIcon />
80
80
  <span className="tiptap-button-text">Table</span>
81
81
  </Button>
82
82
  </PopoverTrigger>
83
- <PopoverContent align="start" className="w-56 p-3">
83
+ <PopoverContent align="start" className="w-72 p-4">
84
84
  <div className="flex flex-col gap-3" onKeyDownCapture={handleKeyDown}>
85
85
  <div className="grid grid-cols-2 gap-2">
86
86
  <div className="flex flex-col gap-1.5">
@@ -106,8 +106,13 @@ export function TableButton({ disabled, editor, onInsertTable }: TableButtonProp
106
106
  />
107
107
  </div>
108
108
  </div>
109
- <Button type="button" size="sm" disabled={disabled || !canInsert} onClick={insertTable}>
110
- Enter
109
+ <Button
110
+ type="button"
111
+ variant="default"
112
+ disabled={disabled || !canInsert}
113
+ onClick={insertTable}
114
+ >
115
+ Insert
111
116
  </Button>
112
117
  </div>
113
118
  </PopoverContent>
@@ -102,7 +102,7 @@ export function BlockquoteButton({
102
102
  >
103
103
  {children ?? (
104
104
  <>
105
- <Icon className="tiptap-button-icon" />
105
+ <Icon />
106
106
  {text && <span className="tiptap-button-text">{text}</span>}
107
107
  {shouldShowControlShortcut(shortcut) && (
108
108
  <BlockquoteShortcutBadge shortcutKeys={shortcutKeys} />
@@ -100,7 +100,7 @@ export function CodeBlockButton({
100
100
  >
101
101
  {children ?? (
102
102
  <>
103
- <Icon className="tiptap-button-icon" />
103
+ <Icon />
104
104
  {text && <span className="tiptap-button-text">{text}</span>}
105
105
  {shouldShowControlShortcut(shortcut) && (
106
106
  <CodeBlockShortcutBadge shortcutKeys={shortcutKeys} />
@@ -34,7 +34,7 @@ export function ColorHighlightPopoverButton({
34
34
  ref={ref}
35
35
  {...props}
36
36
  >
37
- {children ?? <HighlighterIcon className="tiptap-button-icon" />}
37
+ {children ?? <HighlighterIcon />}
38
38
  </Button>
39
39
  )
40
40
  }
@@ -123,7 +123,7 @@ export function ColorHighlightPopoverContent(props: ColorHighlightPopoverContent
123
123
  disabled={disabled}
124
124
  data-disabled={disabled}
125
125
  >
126
- <BanIcon className="tiptap-button-icon" />
126
+ <BanIcon />
127
127
  </Button>
128
128
  </div>
129
129
  )
@@ -33,6 +33,7 @@ export interface ColorHighlightPopoverProps
33
33
  * If not provided, defaults to a predefined set of colors.
34
34
  */
35
35
  colors?: HighlightColor[]
36
+ onOpenChange?: (isOpen: boolean) => void
36
37
  tooltip?: ControlTooltip
37
38
  useColorValue?: boolean
38
39
  }
@@ -43,6 +44,7 @@ export function ColorHighlightPopover({
43
44
  colors = DEFAULT_HIGHLIGHT_COLORS,
44
45
  useColorValue = false,
45
46
  onApplied,
47
+ onOpenChange,
46
48
  tooltip,
47
49
  disabled,
48
50
  ref,
@@ -60,11 +62,18 @@ export function ColorHighlightPopover({
60
62
  const isActive = highlightState.isActive
61
63
  const label = highlightState.label
62
64
  const Icon = highlightState.Icon
65
+ const handleOpenChange = React.useCallback(
66
+ (open: boolean) => {
67
+ setIsOpen(open)
68
+ onOpenChange?.(open)
69
+ },
70
+ [onOpenChange]
71
+ )
63
72
 
64
73
  if (!isVisible) return null
65
74
 
66
75
  return (
67
- <Popover open={isOpen} onOpenChange={setIsOpen}>
76
+ <Popover open={isOpen} onOpenChange={handleOpenChange}>
68
77
  <PopoverTrigger asChild>
69
78
  <ColorHighlightPopoverButton
70
79
  disabled={disabled || !canColorHighlight}
@@ -77,7 +86,7 @@ export function ColorHighlightPopover({
77
86
  {...props}
78
87
  ref={ref}
79
88
  >
80
- <Icon className="tiptap-button-icon" />
89
+ <Icon />
81
90
  </ColorHighlightPopoverButton>
82
91
  </PopoverTrigger>
83
92
  <PopoverContent
@@ -45,7 +45,7 @@ export function SourceColorHighlightPopover({
45
45
  tooltip={tooltip}
46
46
  {...props}
47
47
  >
48
- <HighlighterIcon className="tiptap-button-icon" />
48
+ <HighlighterIcon />
49
49
  </ColorHighlightPopoverButton>
50
50
  </PopoverTrigger>
51
51
  <PopoverContent
@@ -28,7 +28,7 @@ export function HeadingDropdownMenuItem({
28
28
  title={label}
29
29
  onSelect={() => handleToggle()}
30
30
  >
31
- <Icon className="tiptap-button-icon" />
31
+ <Icon />
32
32
  <span className="tiptap-button-text">{label}</span>
33
33
  </DropdownMenuItem>
34
34
  )
@@ -74,7 +74,7 @@ export function HeadingDropdownMenu({
74
74
  }
75
75
 
76
76
  return (
77
- <DropdownMenu open={isOpen} onOpenChange={handleOpenChange}>
77
+ <DropdownMenu modal={false} open={isOpen} onOpenChange={handleOpenChange}>
78
78
  <DropdownMenuTrigger asChild>
79
79
  <Button
80
80
  type="button"
@@ -91,21 +91,26 @@ export function HeadingDropdownMenu({
91
91
  className="w-auto px-2"
92
92
  {...buttonProps}
93
93
  ref={ref}
94
- onPointerDown={(e) => e.preventDefault()}
94
+ onPointerDown={(event) => event.preventDefault()}
95
95
  onClick={() => handleOpenChange(!isOpen)}
96
96
  >
97
97
  {children ? (
98
98
  children
99
99
  ) : (
100
100
  <>
101
- <Icon className="tiptap-button-icon" />
101
+ <Icon />
102
102
  <ChevronDownIcon className="tiptap-button-dropdown-small" />
103
103
  </>
104
104
  )}
105
105
  </Button>
106
106
  </DropdownMenuTrigger>
107
107
 
108
- <DropdownMenuContent align="start" className="w-auto min-w-40">
108
+ <DropdownMenuContent
109
+ align="start"
110
+ className="w-auto min-w-40"
111
+ onCloseAutoFocus={(event) => event.preventDefault()}
112
+ portalled={false}
113
+ >
109
114
  <DropdownMenuGroup>
110
115
  {levels.map((level) => (
111
116
  <HeadingDropdownMenuItem key={`heading-${level}`} editor={editor} level={level} />
@@ -33,7 +33,7 @@ export function LinkButton({
33
33
  ref={ref}
34
34
  {...props}
35
35
  >
36
- {children || <LinkIcon className="tiptap-button-icon" />}
36
+ {children || <LinkIcon />}
37
37
  </Button>
38
38
  )
39
39
  }
@@ -22,7 +22,6 @@ export function LinkControlPopover({
22
22
  onClick,
23
23
  onOpenChange,
24
24
  open,
25
- openLink,
26
25
  ref,
27
26
  removeLink,
28
27
  setLink,
@@ -71,17 +70,16 @@ export function LinkControlPopover({
71
70
  onClick={handleClick}
72
71
  ref={ref}
73
72
  >
74
- {children ?? <LinkIcon className="tiptap-button-icon" />}
73
+ {children ?? <LinkIcon />}
75
74
  </LinkButton>
76
75
  </PopoverTrigger>
77
76
 
78
- <PopoverContent className="w-auto max-w-[calc(100vw-2rem)] gap-0 p-0 rounded-full">
77
+ <PopoverContent className="w-auto max-w-[calc(100vw-2rem)]">
79
78
  <LinkMain
80
79
  url={url}
81
80
  setUrl={setUrl}
82
81
  setLink={handleSetLink}
83
82
  removeLink={removeLink}
84
- openLink={openLink}
85
83
  isActive={isActive}
86
84
  />
87
85
  </PopoverContent>