diff --git a/src/components/command/CommandDialog.vue b/src/components/command/CommandDialog.vue
index 026a1f6..54d6192 100644
--- a/src/components/command/CommandDialog.vue
+++ b/src/components/command/CommandDialog.vue
@@ -1,19 +1,23 @@
@@ -30,16 +34,19 @@ const forwarded = useForwardPropsEmits(props, emits)
menu-shell, card vs popover, shadow-2xl vs dropdown). The backdrop scrim AND the
open/close motion (100ms fade + 2% zoom) are inherited from DialogContent itself —
we ONLY strip chrome here, so the palette shares the one modal scrim + motion
- language instead of re-declaring its own. The close X is dropped
- (show-close-button=false): a palette dismisses on Esc / outside-click / selection,
- and a corner X both clashed with the search row and skipped the icon-button
- contract. Width is held to a palette-friendly max-w-md.
+ language instead of re-declaring its own. A palette dismisses on Esc,
+ outside-click or selection. The compact picker instead owns a visible
+ title and the shared close button, with one narrower width rung.
-->
-
+ {{ title }}{{ description }}
@@ -49,7 +56,22 @@ const forwarded = useForwardPropsEmits(props, emits)
hairline in dark mode, and NONE in light mode — a white palette already
separates from the dark scrim by luminance, so a dark hairline there would
just muddy the edge instead of sharpening it. -->
-
+
+
+
+ {{ title }}
+
+
+ {{ description }}
+
+
+
diff --git a/src/components/dropdown-menu/DropdownMenuSubContent.vue b/src/components/dropdown-menu/DropdownMenuSubContent.vue
index 7b08df1..3dc459f 100644
--- a/src/components/dropdown-menu/DropdownMenuSubContent.vue
+++ b/src/components/dropdown-menu/DropdownMenuSubContent.vue
@@ -14,19 +14,21 @@ import { cn } from '#/lib/utils'
const props = withDefaults(defineProps(), { scrollable: true })
const emits = defineEmits()
-const delegatedProps = reactiveOmit(props, 'class', 'scrollable')
+const delegatedProps = reactiveOmit(props, 'class', 'scrollable', 'alignment')
const forwarded = useForwardPropsEmits(delegatedProps, emits)
const menuArea = ref>()
const customBody = ref()
const currentElement = computed(() => (menuArea.value?.viewportElement ?? customBody.value)
?.closest('[data-slot="dropdown-menu-sub-content"]') ?? undefined)
-const { alignOffset, ready } = useSubmenuAlignment(currentElement)
+const { alignOffset, ready } = useSubmenuAlignment(currentElement, () => props.alignment ?? 'first-item')
// Start the entrance on a fresh frame after mounting and placement have settled.
// Otherwise a long list can consume the animation before its first visible paint.
const motionReady = ref(false)
diff --git a/src/components/dropdown-menu/DropdownMenuSummary.vue b/src/components/dropdown-menu/DropdownMenuSummary.vue
new file mode 100644
index 0000000..0f3cd45
--- /dev/null
+++ b/src/components/dropdown-menu/DropdownMenuSummary.vue
@@ -0,0 +1,31 @@
+
+
+
+
+
+ {{ title }}
+ {{ value }}
+
+
+ {{ description }}
+
+
+
diff --git a/src/components/dropdown-menu/index.ts b/src/components/dropdown-menu/index.ts
index 7565776..eb6b38d 100644
--- a/src/components/dropdown-menu/index.ts
+++ b/src/components/dropdown-menu/index.ts
@@ -4,6 +4,7 @@ export { default as DropdownMenuCheckboxItem } from './DropdownMenuCheckboxItem.
export { default as DropdownMenuContent } from './DropdownMenuContent.vue'
export { default as DropdownMenuGroup } from './DropdownMenuGroup.vue'
export { default as DropdownMenuItem } from './DropdownMenuItem.vue'
+export { default as DropdownMenuSummary } from './DropdownMenuSummary.vue'
export { default as DropdownMenuLabel } from './DropdownMenuLabel.vue'
export { default as DropdownMenuRadioGroup } from './DropdownMenuRadioGroup.vue'
export { default as DropdownMenuRadioItem } from './DropdownMenuRadioItem.vue'
diff --git a/src/lib/useSubmenuAlignment.ts b/src/lib/useSubmenuAlignment.ts
index 739eb26..8779d4f 100644
--- a/src/lib/useSubmenuAlignment.ts
+++ b/src/lib/useSubmenuAlignment.ts
@@ -1,7 +1,10 @@
import { ref, watchPostEffect, type Ref } from 'vue'
/** Align the first row with its trigger, leaving collision handling to Reka. */
-export function useSubmenuAlignment(content: Readonly>) {
+export function useSubmenuAlignment(
+ content: Readonly>,
+ alignment: () => 'first-item' | 'parent-start' = () => 'first-item',
+) {
const alignOffset = ref(0)
const ready = ref(false)
watchPostEffect((cleanup) => {
@@ -10,7 +13,16 @@ export function useSubmenuAlignment(content: Readonly {
+ if (mode === 'parent-start') {
+ const parent = trigger.closest('[role="menu"]')
+ alignOffset.value = parent
+ ? parent.getBoundingClientRect().top - trigger.getBoundingClientRect().top
+ : 0
+ ready.value = true
+ return
+ }
// Virtual listboxes may unmount their first option while scrolling. Only
// anchor to logical row one, never to the first currently mounted row.
const first = element.querySelector(