@open-mercato/ui 0.6.6 → 0.6.7-develop.6552.1.5208a19369

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 (29) hide show
  1. package/AGENTS.md +1 -1
  2. package/dist/ai/ChatPaneTabs.js +2 -2
  3. package/dist/ai/ChatPaneTabs.js.map +2 -2
  4. package/dist/backend/AppShell.js +2 -2
  5. package/dist/backend/AppShell.js.map +1 -1
  6. package/dist/backend/custom-fields/FieldDefinitionsEditor.js +37 -20
  7. package/dist/backend/custom-fields/FieldDefinitionsEditor.js.map +2 -2
  8. package/dist/backend/detail/AttachmentMetadataDialog.js +9 -17
  9. package/dist/backend/detail/AttachmentMetadataDialog.js.map +2 -2
  10. package/dist/backend/filters/AdvancedFilterBuilder.js +1 -1
  11. package/dist/backend/filters/AdvancedFilterBuilder.js.map +1 -1
  12. package/dist/backend/messages/useMessageCompose.js +1 -0
  13. package/dist/backend/messages/useMessageCompose.js.map +2 -2
  14. package/dist/primitives/drawer.js +6 -3
  15. package/dist/primitives/drawer.js.map +2 -2
  16. package/dist/primitives/tabs.js +2 -1
  17. package/dist/primitives/tabs.js.map +2 -2
  18. package/package.json +5 -4
  19. package/src/ai/ChatPaneTabs.tsx +2 -2
  20. package/src/backend/AppShell.tsx +2 -2
  21. package/src/backend/__tests__/DataTable.extensions.test.tsx +26 -10
  22. package/src/backend/custom-fields/FieldDefinitionsEditor.tsx +52 -24
  23. package/src/backend/detail/AttachmentMetadataDialog.tsx +16 -23
  24. package/src/backend/filters/AdvancedFilterBuilder.tsx +1 -1
  25. package/src/backend/messages/__tests__/useMessageCompose.test.tsx +60 -0
  26. package/src/backend/messages/useMessageCompose.ts +4 -0
  27. package/src/primitives/__tests__/drawer.test.tsx +2 -1
  28. package/src/primitives/drawer.tsx +4 -3
  29. package/src/primitives/tabs.tsx +6 -1
package/AGENTS.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  UI usage patterns based on customers, sales, and staff modules. Use these defaults when building new UI in `packages/ui` or consuming from other modules.
4
4
 
5
- > **DS reference:** [`.ai/ds-rules.md`](../../.ai/ds-rules.md) — color tokens, typography, spacing, decision trees. **Component reference (variants/sizes/props/examples/MUST rules):** [`.ai/ui-components.md`](../../.ai/ui-components.md).
5
+ > **DS reference:** [`.ai/ds-rules.md`](../../.ai/ds-rules.md) — color tokens, typography, spacing, decision trees. **Component reference (variants/sizes/props/examples/MUST rules):** [`.ai/ui-components.md`](../../.ai/ui-components.md). **Backend component families (charts, filters, detail sections, schedule, messages, page scaffolding):** [`.ai/ui-backend-components.md`](../../.ai/ui-backend-components.md) — check it before building any of these from scratch.
6
6
 
7
7
  ## Always
8
8
 
@@ -64,7 +64,7 @@ function ChatPaneTabs({ agentId, className }) {
64
64
  "data-active": isActive ? "true" : "false",
65
65
  className: cn(
66
66
  "group flex max-w-[12rem] shrink-0 items-center gap-1 rounded-t-md border-b-2 px-2 py-1",
67
- isActive ? "border-primary bg-background text-foreground" : "border-transparent text-muted-foreground hover:text-foreground"
67
+ isActive ? "border-accent-indigo bg-background text-foreground" : "border-transparent text-muted-foreground hover:text-foreground"
68
68
  ),
69
69
  children: [
70
70
  isRenaming ? /* @__PURE__ */ jsx(
@@ -94,7 +94,7 @@ function ChatPaneTabs({ agentId, className }) {
94
94
  onClick: () => sessions.setActiveSession(session.id),
95
95
  onDoubleClick: () => startRename(session),
96
96
  title: label,
97
- className: "truncate text-xs font-medium",
97
+ className: "truncate rounded-sm text-xs font-medium outline-none focus-visible:shadow-focus",
98
98
  children: label
99
99
  }
100
100
  ),
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/ai/ChatPaneTabs.tsx"],
4
- "sourcesContent": ["\"use client\"\n\n/**\n * Tab strip rendered above an `<AiChat>` surface (dock panel, dialog sheet).\n *\n * Each tab is a session for the same agent. The strip provides:\n * - tab switching (click)\n * - inline rename (double-click on the active tab title or pencil icon)\n * - close (X on hover)\n * - new session (`+`)\n * - history dropdown (clock icon \u2192 recent closed sessions; click reopens)\n *\n * The component is purely UI \u2014 state lives in `AiChatSessionsProvider`.\n */\n\nimport * as React from 'react'\nimport { Clock, Pencil, Plus, X } from 'lucide-react'\nimport { IconButton } from '../primitives/icon-button'\nimport { cn } from '@open-mercato/shared/lib/utils'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport {\n defaultSessionLabel,\n useAiChatSessions,\n type AiChatSession,\n} from './AiChatSessions'\n\nexport interface ChatPaneTabsProps {\n agentId: string\n className?: string\n}\n\nexport function ChatPaneTabs({ agentId, className }: ChatPaneTabsProps) {\n const t = useT()\n const sessions = useAiChatSessions()\n const open = sessions.getOpenSessions(agentId)\n const closed = sessions.getClosedSessions(agentId)\n const active = sessions.getActiveSession(agentId)\n\n const [historyOpen, setHistoryOpen] = React.useState(false)\n const [renamingId, setRenamingId] = React.useState<string | null>(null)\n const [draftName, setDraftName] = React.useState('')\n\n const startRename = (session: AiChatSession) => {\n setRenamingId(session.id)\n // Pre-fill with the current name only \u2014 never the date fallback. If\n // we pre-filled with the formatted date, blurring without typing\n // would persist that date string as the session name, and creating\n // a new tab a minute later would \"leak\" the old tab's date label\n // onto every other unnamed tab.\n setDraftName(session.name ?? '')\n }\n\n const commitRename = () => {\n if (!renamingId) return\n sessions.renameSession(renamingId, draftName)\n setRenamingId(null)\n }\n\n const cancelRename = () => {\n setRenamingId(null)\n setDraftName('')\n }\n\n return (\n // Outer wrapper does NOT scroll; only the inner tabs row does. The\n // `+` button and the history dropdown live OUTSIDE that scroll area\n // so the dropdown's absolute positioning isn't clipped by the\n // strip's `overflow-x-auto` (which CSS resolves to `overflow-y:auto`\n // too once one axis is non-`visible`, making any absolutely-\n // positioned child get cut off at the strip's bottom edge).\n <div\n className={cn('flex items-center gap-1 px-2 pt-2 text-sm', className)}\n data-ai-chat-tabs=\"\"\n role=\"tablist\"\n aria-label=\"Chat sessions\"\n >\n <div\n className=\"flex min-w-0 flex-1 items-center gap-1 overflow-x-auto\"\n data-ai-chat-tabs-scroll=\"\"\n >\n {open.length === 0 ? (\n <span className=\"px-2 py-1 text-xs text-muted-foreground\" data-ai-chat-tabs-empty=\"\">\n {t('ai_assistant.chat.tabs.noSessions', 'No sessions')}\n </span>\n ) : (\n open.map((session) => {\n const isActive = active?.id === session.id\n const isRenaming = renamingId === session.id\n const label = defaultSessionLabel(session)\n return (\n <div\n key={session.id}\n role=\"tab\"\n aria-selected={isActive}\n data-ai-chat-tab-id={session.id}\n data-active={isActive ? 'true' : 'false'}\n className={cn(\n 'group flex max-w-[12rem] shrink-0 items-center gap-1 rounded-t-md border-b-2 px-2 py-1',\n isActive\n ? 'border-primary bg-background text-foreground'\n : 'border-transparent text-muted-foreground hover:text-foreground',\n )}\n >\n {isRenaming ? (\n <input\n autoFocus\n type=\"text\"\n value={draftName}\n onChange={(event) => setDraftName(event.target.value)}\n onBlur={commitRename}\n onKeyDown={(event) => {\n if (event.key === 'Enter') {\n event.preventDefault()\n commitRename()\n } else if (event.key === 'Escape') {\n event.preventDefault()\n cancelRename()\n }\n }}\n className=\"h-6 max-w-[10rem] rounded border border-input bg-background px-1 text-xs outline-none focus:ring-2 focus:ring-ring/40\"\n data-ai-chat-tab-rename-input=\"\"\n />\n ) : (\n <button\n type=\"button\"\n onClick={() => sessions.setActiveSession(session.id)}\n onDoubleClick={() => startRename(session)}\n title={label}\n className=\"truncate text-xs font-medium\"\n >\n {label}\n </button>\n )}\n {!isRenaming && isActive ? (\n <IconButton\n type=\"button\"\n variant=\"ghost\"\n size=\"xs\"\n aria-label={t('ai_assistant.chat.tabs.rename', 'Rename')}\n title={t('ai_assistant.chat.tabs.rename', 'Rename')}\n className=\"opacity-60 hover:opacity-100\"\n onClick={() => startRename(session)}\n data-ai-chat-tab-rename=\"\"\n >\n <Pencil className=\"size-3\" />\n </IconButton>\n ) : null}\n <IconButton\n type=\"button\"\n variant=\"ghost\"\n size=\"xs\"\n aria-label={t('ai_assistant.chat.tabs.close', 'Close')}\n title={t('ai_assistant.chat.tabs.close', 'Close')}\n // Always rendered visible (a previous opacity-0 default\n // hid the X on non-hover and made the active-tab close\n // button look unreachable). Closing the very last open\n // tab is fine \u2014 `ensureSession` in the chat body's\n // effect immediately mints a fresh empty tab so the user\n // never sees an empty pane.\n className={cn(\n 'transition-opacity',\n isActive ? 'opacity-60 hover:opacity-100' : 'opacity-50 hover:opacity-100',\n )}\n data-active={isActive ? 'true' : 'false'}\n onMouseDown={(event) => {\n // Prevent the parent tab button's blur logic / focus\n // shift from racing the close click on the active tab.\n event.stopPropagation()\n }}\n onClick={(event) => {\n event.preventDefault()\n event.stopPropagation()\n sessions.closeSession(session.id)\n }}\n data-ai-chat-tab-close=\"\"\n >\n <X className=\"size-3\" />\n </IconButton>\n </div>\n )\n })\n )}\n </div>\n <IconButton\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n aria-label={t('ai_assistant.chat.tabs.newSession', 'New session')}\n title={t('ai_assistant.chat.tabs.newSession', 'New session')}\n onClick={() => sessions.createSession(agentId)}\n data-ai-chat-new-session=\"\"\n className=\"shrink-0\"\n >\n <Plus className=\"size-4\" />\n </IconButton>\n <HistoryDropdown\n open={historyOpen}\n onOpenChange={setHistoryOpen}\n closed={closed}\n onPick={(sessionId) => {\n sessions.reopenSession(sessionId)\n setHistoryOpen(false)\n }}\n />\n </div>\n )\n}\n\ninterface HistoryDropdownProps {\n open: boolean\n onOpenChange: (next: boolean) => void\n closed: AiChatSession[]\n onPick: (sessionId: string) => void\n}\n\n/**\n * Plain absolutely-positioned dropdown for the recent-sessions list.\n * Bypasses the Radix Popover primitive on purpose \u2014 every chat surface\n * (dock panel, customers/catalog/launcher dialog) creates its own stacking\n * context, and the Radix Portal'd PopoverContent kept ending up either\n * behind the dialog or pushed off the visible area on tall sheets. A\n * direct `position: absolute` child of the trigger button anchors the\n * dropdown to the icon, inherits the surface's stacking context, and is\n * predictable across the dock + every dialog host without z-index hacks.\n */\nfunction HistoryDropdown({ open, onOpenChange, closed, onPick }: HistoryDropdownProps) {\n const t = useT()\n const containerRef = React.useRef<HTMLDivElement | null>(null)\n\n React.useEffect(() => {\n if (!open) return\n const onDown = (event: MouseEvent | TouchEvent) => {\n const root = containerRef.current\n if (!root) return\n if (event.target instanceof Node && root.contains(event.target)) return\n onOpenChange(false)\n }\n const onKey = (event: KeyboardEvent) => {\n if (event.key === 'Escape') onOpenChange(false)\n }\n window.addEventListener('mousedown', onDown)\n window.addEventListener('touchstart', onDown, { passive: true })\n window.addEventListener('keydown', onKey)\n return () => {\n window.removeEventListener('mousedown', onDown)\n window.removeEventListener('touchstart', onDown)\n window.removeEventListener('keydown', onKey)\n }\n }, [open, onOpenChange])\n\n return (\n <div ref={containerRef} className=\"relative shrink-0\">\n <IconButton\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n aria-label={t('ai_assistant.chat.tabs.recentSessions', 'Recent sessions')}\n title={t('ai_assistant.chat.tabs.recentSessions', 'Recent sessions')}\n data-ai-chat-history-trigger=\"\"\n aria-expanded={open}\n onClick={() => onOpenChange(!open)}\n >\n <Clock className=\"size-4\" />\n </IconButton>\n {open ? (\n <div\n className=\"absolute right-0 top-full mt-2 w-72 max-h-[60vh] overflow-y-auto rounded-md border bg-popover p-1 text-popover-foreground shadow-md\"\n // Inline z-index so the dropdown sits above any host surface\n // (chat dialog at z-[70], dock panel, modal overlays). Inline\n // beats Tailwind JIT for arbitrary high values.\n style={{ zIndex: 2147483000 }}\n data-ai-chat-history-panel=\"\"\n role=\"menu\"\n >\n <div className=\"px-3 pt-2 pb-1 text-xs font-medium uppercase tracking-wide text-muted-foreground\">\n {t('ai_assistant.chat.tabs.recentSessions', 'Recent sessions')}\n </div>\n {closed.length === 0 ? (\n <div className=\"px-3 py-3 text-xs text-muted-foreground\" data-ai-chat-history-empty=\"\">\n {t('ai_assistant.chat.tabs.noPreviousSessions', 'No previous sessions yet.')}\n </div>\n ) : (\n <div className=\"flex flex-col gap-0.5\">\n {closed.map((session) => (\n <button\n key={session.id}\n type=\"button\"\n role=\"menuitem\"\n onClick={() => onPick(session.id)}\n className=\"flex w-full items-center justify-between gap-2 rounded-sm px-2 py-2 text-left text-sm hover:bg-accent hover:text-accent-foreground focus-visible:bg-accent focus-visible:text-accent-foreground focus-visible:outline-none\"\n data-ai-chat-history-item={session.id}\n >\n <span className=\"min-w-0 flex-1 truncate\">\n {defaultSessionLabel(session)}\n </span>\n <span className=\"shrink-0 text-[10px] text-muted-foreground\">\n {new Date(session.lastUsedAt).toLocaleDateString(undefined, {\n month: 'short',\n day: 'numeric',\n })}\n </span>\n </button>\n ))}\n </div>\n )}\n </div>\n ) : null}\n </div>\n )\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["\"use client\"\n\n/**\n * Tab strip rendered above an `<AiChat>` surface (dock panel, dialog sheet).\n *\n * Each tab is a session for the same agent. The strip provides:\n * - tab switching (click)\n * - inline rename (double-click on the active tab title or pencil icon)\n * - close (X on hover)\n * - new session (`+`)\n * - history dropdown (clock icon \u2192 recent closed sessions; click reopens)\n *\n * The component is purely UI \u2014 state lives in `AiChatSessionsProvider`.\n */\n\nimport * as React from 'react'\nimport { Clock, Pencil, Plus, X } from 'lucide-react'\nimport { IconButton } from '../primitives/icon-button'\nimport { cn } from '@open-mercato/shared/lib/utils'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport {\n defaultSessionLabel,\n useAiChatSessions,\n type AiChatSession,\n} from './AiChatSessions'\n\nexport interface ChatPaneTabsProps {\n agentId: string\n className?: string\n}\n\nexport function ChatPaneTabs({ agentId, className }: ChatPaneTabsProps) {\n const t = useT()\n const sessions = useAiChatSessions()\n const open = sessions.getOpenSessions(agentId)\n const closed = sessions.getClosedSessions(agentId)\n const active = sessions.getActiveSession(agentId)\n\n const [historyOpen, setHistoryOpen] = React.useState(false)\n const [renamingId, setRenamingId] = React.useState<string | null>(null)\n const [draftName, setDraftName] = React.useState('')\n\n const startRename = (session: AiChatSession) => {\n setRenamingId(session.id)\n // Pre-fill with the current name only \u2014 never the date fallback. If\n // we pre-filled with the formatted date, blurring without typing\n // would persist that date string as the session name, and creating\n // a new tab a minute later would \"leak\" the old tab's date label\n // onto every other unnamed tab.\n setDraftName(session.name ?? '')\n }\n\n const commitRename = () => {\n if (!renamingId) return\n sessions.renameSession(renamingId, draftName)\n setRenamingId(null)\n }\n\n const cancelRename = () => {\n setRenamingId(null)\n setDraftName('')\n }\n\n return (\n // Outer wrapper does NOT scroll; only the inner tabs row does. The\n // `+` button and the history dropdown live OUTSIDE that scroll area\n // so the dropdown's absolute positioning isn't clipped by the\n // strip's `overflow-x-auto` (which CSS resolves to `overflow-y:auto`\n // too once one axis is non-`visible`, making any absolutely-\n // positioned child get cut off at the strip's bottom edge).\n <div\n className={cn('flex items-center gap-1 px-2 pt-2 text-sm', className)}\n data-ai-chat-tabs=\"\"\n role=\"tablist\"\n aria-label=\"Chat sessions\"\n >\n <div\n className=\"flex min-w-0 flex-1 items-center gap-1 overflow-x-auto\"\n data-ai-chat-tabs-scroll=\"\"\n >\n {open.length === 0 ? (\n <span className=\"px-2 py-1 text-xs text-muted-foreground\" data-ai-chat-tabs-empty=\"\">\n {t('ai_assistant.chat.tabs.noSessions', 'No sessions')}\n </span>\n ) : (\n open.map((session) => {\n const isActive = active?.id === session.id\n const isRenaming = renamingId === session.id\n const label = defaultSessionLabel(session)\n return (\n <div\n key={session.id}\n role=\"tab\"\n aria-selected={isActive}\n data-ai-chat-tab-id={session.id}\n data-active={isActive ? 'true' : 'false'}\n className={cn(\n 'group flex max-w-[12rem] shrink-0 items-center gap-1 rounded-t-md border-b-2 px-2 py-1',\n isActive\n ? 'border-accent-indigo bg-background text-foreground'\n : 'border-transparent text-muted-foreground hover:text-foreground',\n )}\n >\n {isRenaming ? (\n <input\n autoFocus\n type=\"text\"\n value={draftName}\n onChange={(event) => setDraftName(event.target.value)}\n onBlur={commitRename}\n onKeyDown={(event) => {\n if (event.key === 'Enter') {\n event.preventDefault()\n commitRename()\n } else if (event.key === 'Escape') {\n event.preventDefault()\n cancelRename()\n }\n }}\n className=\"h-6 max-w-[10rem] rounded border border-input bg-background px-1 text-xs outline-none focus:ring-2 focus:ring-ring/40\"\n data-ai-chat-tab-rename-input=\"\"\n />\n ) : (\n <button\n type=\"button\"\n onClick={() => sessions.setActiveSession(session.id)}\n onDoubleClick={() => startRename(session)}\n title={label}\n className=\"truncate rounded-sm text-xs font-medium outline-none focus-visible:shadow-focus\"\n >\n {label}\n </button>\n )}\n {!isRenaming && isActive ? (\n <IconButton\n type=\"button\"\n variant=\"ghost\"\n size=\"xs\"\n aria-label={t('ai_assistant.chat.tabs.rename', 'Rename')}\n title={t('ai_assistant.chat.tabs.rename', 'Rename')}\n className=\"opacity-60 hover:opacity-100\"\n onClick={() => startRename(session)}\n data-ai-chat-tab-rename=\"\"\n >\n <Pencil className=\"size-3\" />\n </IconButton>\n ) : null}\n <IconButton\n type=\"button\"\n variant=\"ghost\"\n size=\"xs\"\n aria-label={t('ai_assistant.chat.tabs.close', 'Close')}\n title={t('ai_assistant.chat.tabs.close', 'Close')}\n // Always rendered visible (a previous opacity-0 default\n // hid the X on non-hover and made the active-tab close\n // button look unreachable). Closing the very last open\n // tab is fine \u2014 `ensureSession` in the chat body's\n // effect immediately mints a fresh empty tab so the user\n // never sees an empty pane.\n className={cn(\n 'transition-opacity',\n isActive ? 'opacity-60 hover:opacity-100' : 'opacity-50 hover:opacity-100',\n )}\n data-active={isActive ? 'true' : 'false'}\n onMouseDown={(event) => {\n // Prevent the parent tab button's blur logic / focus\n // shift from racing the close click on the active tab.\n event.stopPropagation()\n }}\n onClick={(event) => {\n event.preventDefault()\n event.stopPropagation()\n sessions.closeSession(session.id)\n }}\n data-ai-chat-tab-close=\"\"\n >\n <X className=\"size-3\" />\n </IconButton>\n </div>\n )\n })\n )}\n </div>\n <IconButton\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n aria-label={t('ai_assistant.chat.tabs.newSession', 'New session')}\n title={t('ai_assistant.chat.tabs.newSession', 'New session')}\n onClick={() => sessions.createSession(agentId)}\n data-ai-chat-new-session=\"\"\n className=\"shrink-0\"\n >\n <Plus className=\"size-4\" />\n </IconButton>\n <HistoryDropdown\n open={historyOpen}\n onOpenChange={setHistoryOpen}\n closed={closed}\n onPick={(sessionId) => {\n sessions.reopenSession(sessionId)\n setHistoryOpen(false)\n }}\n />\n </div>\n )\n}\n\ninterface HistoryDropdownProps {\n open: boolean\n onOpenChange: (next: boolean) => void\n closed: AiChatSession[]\n onPick: (sessionId: string) => void\n}\n\n/**\n * Plain absolutely-positioned dropdown for the recent-sessions list.\n * Bypasses the Radix Popover primitive on purpose \u2014 every chat surface\n * (dock panel, customers/catalog/launcher dialog) creates its own stacking\n * context, and the Radix Portal'd PopoverContent kept ending up either\n * behind the dialog or pushed off the visible area on tall sheets. A\n * direct `position: absolute` child of the trigger button anchors the\n * dropdown to the icon, inherits the surface's stacking context, and is\n * predictable across the dock + every dialog host without z-index hacks.\n */\nfunction HistoryDropdown({ open, onOpenChange, closed, onPick }: HistoryDropdownProps) {\n const t = useT()\n const containerRef = React.useRef<HTMLDivElement | null>(null)\n\n React.useEffect(() => {\n if (!open) return\n const onDown = (event: MouseEvent | TouchEvent) => {\n const root = containerRef.current\n if (!root) return\n if (event.target instanceof Node && root.contains(event.target)) return\n onOpenChange(false)\n }\n const onKey = (event: KeyboardEvent) => {\n if (event.key === 'Escape') onOpenChange(false)\n }\n window.addEventListener('mousedown', onDown)\n window.addEventListener('touchstart', onDown, { passive: true })\n window.addEventListener('keydown', onKey)\n return () => {\n window.removeEventListener('mousedown', onDown)\n window.removeEventListener('touchstart', onDown)\n window.removeEventListener('keydown', onKey)\n }\n }, [open, onOpenChange])\n\n return (\n <div ref={containerRef} className=\"relative shrink-0\">\n <IconButton\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n aria-label={t('ai_assistant.chat.tabs.recentSessions', 'Recent sessions')}\n title={t('ai_assistant.chat.tabs.recentSessions', 'Recent sessions')}\n data-ai-chat-history-trigger=\"\"\n aria-expanded={open}\n onClick={() => onOpenChange(!open)}\n >\n <Clock className=\"size-4\" />\n </IconButton>\n {open ? (\n <div\n className=\"absolute right-0 top-full mt-2 w-72 max-h-[60vh] overflow-y-auto rounded-md border bg-popover p-1 text-popover-foreground shadow-md\"\n // Inline z-index so the dropdown sits above any host surface\n // (chat dialog at z-[70], dock panel, modal overlays). Inline\n // beats Tailwind JIT for arbitrary high values.\n style={{ zIndex: 2147483000 }}\n data-ai-chat-history-panel=\"\"\n role=\"menu\"\n >\n <div className=\"px-3 pt-2 pb-1 text-xs font-medium uppercase tracking-wide text-muted-foreground\">\n {t('ai_assistant.chat.tabs.recentSessions', 'Recent sessions')}\n </div>\n {closed.length === 0 ? (\n <div className=\"px-3 py-3 text-xs text-muted-foreground\" data-ai-chat-history-empty=\"\">\n {t('ai_assistant.chat.tabs.noPreviousSessions', 'No previous sessions yet.')}\n </div>\n ) : (\n <div className=\"flex flex-col gap-0.5\">\n {closed.map((session) => (\n <button\n key={session.id}\n type=\"button\"\n role=\"menuitem\"\n onClick={() => onPick(session.id)}\n className=\"flex w-full items-center justify-between gap-2 rounded-sm px-2 py-2 text-left text-sm hover:bg-accent hover:text-accent-foreground focus-visible:bg-accent focus-visible:text-accent-foreground focus-visible:outline-none\"\n data-ai-chat-history-item={session.id}\n >\n <span className=\"min-w-0 flex-1 truncate\">\n {defaultSessionLabel(session)}\n </span>\n <span className=\"shrink-0 text-[10px] text-muted-foreground\">\n {new Date(session.lastUsedAt).toLocaleDateString(undefined, {\n month: 'short',\n day: 'numeric',\n })}\n </span>\n </button>\n ))}\n </div>\n )}\n </div>\n ) : null}\n </div>\n )\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -1104,14 +1104,14 @@ function AppShellBody({ productName, logo, email, canManageUpgradeActions = fals
1104
1104
  "aria-selected": isActive,
1105
1105
  "aria-controls": "mobile-drawer-tabpanel",
1106
1106
  onClick: () => setMobileDrawerView(tab.id === "main" ? "main" : "auto"),
1107
- className: "relative inline-flex items-center pb-2 text-sm font-medium leading-5 tracking-tight transition-colors focus:outline-none data-[active=true]:text-foreground data-[active=false]:text-muted-foreground hover:text-foreground",
1107
+ className: "relative inline-flex items-center pb-2 text-sm font-medium leading-5 tracking-tight transition-colors outline-none focus-visible:shadow-focus data-[active=true]:text-foreground data-[active=false]:text-muted-foreground hover:text-foreground",
1108
1108
  "data-active": isActive,
1109
1109
  children: [
1110
1110
  /* @__PURE__ */ jsx("span", { children: tab.label }),
1111
1111
  isActive ? /* @__PURE__ */ jsx(
1112
1112
  "span",
1113
1113
  {
1114
- className: "absolute -bottom-px left-0 right-0 h-0.5 bg-foreground",
1114
+ className: "absolute -bottom-px left-0 right-0 h-0.5 bg-accent-indigo",
1115
1115
  "aria-hidden": "true"
1116
1116
  }
1117
1117
  ) : null