@mastra/playground-ui 68.0.0-alpha.3 → 68.0.0
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.
- package/dist/{FilterBar-CQl04O6t.cjs → FilterBar-BSP2UTjc.cjs} +2 -2
- package/dist/{FilterBar-CQl04O6t.cjs.map → FilterBar-BSP2UTjc.cjs.map} +1 -1
- package/dist/{FilterBar-BgW2dqRQ.js → FilterBar-PSQ1OwqH.js} +2 -2
- package/dist/{FilterBar-BgW2dqRQ.js.map → FilterBar-PSQ1OwqH.js.map} +1 -1
- package/dist/{MetricsCard-CEB-f2sv.cjs → MetricsCard-BEwiS7Pe.cjs} +8 -8
- package/dist/MetricsCard-BEwiS7Pe.cjs.map +1 -0
- package/dist/{MetricsCard-M8awJ2B_.js → MetricsCard-ua6C6b8a.js} +10 -10
- package/dist/MetricsCard-ua6C6b8a.js.map +1 -0
- package/dist/{MetricsLineChart-BhHoduO0.js → MetricsLineChart-4SwxBbQx.js} +14 -3
- package/dist/MetricsLineChart-4SwxBbQx.js.map +1 -0
- package/dist/{MetricsLineChart-CjBm4IXu.cjs → MetricsLineChart-tZxsauau.cjs} +13 -2
- package/dist/MetricsLineChart-tZxsauau.cjs.map +1 -0
- package/dist/{MetricsStackedBarChart-BdMlKv6G.cjs → MetricsStackedBarChart-CDLE76IB.cjs} +2 -2
- package/dist/{MetricsStackedBarChart-BdMlKv6G.cjs.map → MetricsStackedBarChart-CDLE76IB.cjs.map} +1 -1
- package/dist/{MetricsStackedBarChart-BRuA6MTE.js → MetricsStackedBarChart-D2QyS4-z.js} +2 -2
- package/dist/{MetricsStackedBarChart-BRuA6MTE.js.map → MetricsStackedBarChart-D2QyS4-z.js.map} +1 -1
- package/dist/components/DateTimeRangePicker.cjs.js +1 -1
- package/dist/components/DateTimeRangePicker.es.js +1 -1
- package/dist/components/FilterBar.cjs.js +2 -2
- package/dist/components/FilterBar.es.js +2 -2
- package/dist/components/MetricsCard.cjs.js +1 -1
- package/dist/components/MetricsCard.es.js +1 -1
- package/dist/components/MetricsLineChart.cjs.js +1 -1
- package/dist/components/MetricsLineChart.es.js +1 -1
- package/dist/components/MetricsStackedBarChart.cjs.js +1 -1
- package/dist/components/MetricsStackedBarChart.es.js +1 -1
- package/dist/{date-time-range-picker-CTt7Od0P.cjs → date-time-range-picker-CWGjoR3j.cjs} +40 -13
- package/dist/date-time-range-picker-CWGjoR3j.cjs.map +1 -0
- package/dist/{date-time-range-picker-DKh1K9Iy.js → date-time-range-picker-CdSiXwB4.js} +40 -13
- package/dist/date-time-range-picker-CdSiXwB4.js.map +1 -0
- package/dist/domains/chat/messages/message-row.cjs.js +1 -1
- package/dist/domains/chat/messages/message-row.es.js +1 -1
- package/dist/domains/chat/tools/badges/agent-badge-wrapper.cjs.js +1 -1
- package/dist/domains/chat/tools/badges/agent-badge-wrapper.es.js +1 -1
- package/dist/domains/chat/tools/badges/agent-badge.cjs.js +1 -1
- package/dist/domains/chat/tools/badges/agent-badge.es.js +1 -1
- package/dist/domains/chat/tools/connect-prompt-tool.cjs.js +79 -4
- package/dist/domains/chat/tools/connect-prompt-tool.cjs.js.map +1 -1
- package/dist/domains/chat/tools/connect-prompt-tool.es.js +79 -4
- package/dist/domains/chat/tools/connect-prompt-tool.es.js.map +1 -1
- package/dist/domains/chat/tools/mcp-input-request-tool.cjs.js +10 -12
- package/dist/domains/chat/tools/mcp-input-request-tool.cjs.js.map +1 -1
- package/dist/domains/chat/tools/mcp-input-request-tool.es.js +10 -12
- package/dist/domains/chat/tools/mcp-input-request-tool.es.js.map +1 -1
- package/dist/domains/chat/tools/tool-card-kind.cjs.js +1 -1
- package/dist/domains/chat/tools/tool-card-kind.cjs.js.map +1 -1
- package/dist/domains/chat/tools/tool-card-kind.es.js +1 -1
- package/dist/domains/chat/tools/tool-card-kind.es.js.map +1 -1
- package/dist/domains/chat/tools/tool-card.cjs.js +1 -1
- package/dist/domains/chat/tools/tool-card.es.js +1 -1
- package/dist/domains/connect/types.cjs.js.map +1 -1
- package/dist/domains/connect/types.es.js.map +1 -1
- package/dist/domains/connect/use-user-connect.cjs.js +19 -2
- package/dist/domains/connect/use-user-connect.cjs.js.map +1 -1
- package/dist/domains/connect/use-user-connect.es.js +19 -2
- package/dist/domains/connect/use-user-connect.es.js.map +1 -1
- package/dist/domains/dashboards/components/dashboard-renderer.cjs.js +4 -3
- package/dist/domains/dashboards/components/dashboard-renderer.cjs.js.map +1 -1
- package/dist/domains/dashboards/components/dashboard-renderer.es.js +4 -3
- package/dist/domains/dashboards/components/dashboard-renderer.es.js.map +1 -1
- package/dist/domains/dashboards/components/widget-chrome.cjs.js +1 -1
- package/dist/domains/dashboards/components/widget-chrome.es.js +1 -1
- package/dist/domains/dashboards/components/widget-error.cjs.js +1 -1
- package/dist/domains/dashboards/components/widget-error.es.js +1 -1
- package/dist/domains/dashboards/hooks/use-widget-views.cjs.js +4 -3
- package/dist/domains/dashboards/hooks/use-widget-views.cjs.js.map +1 -1
- package/dist/domains/dashboards/hooks/use-widget-views.es.js +4 -3
- package/dist/domains/dashboards/hooks/use-widget-views.es.js.map +1 -1
- package/dist/domains/dashboards/kinds/scores/scores-widget.cjs.js +9 -6
- package/dist/domains/dashboards/kinds/scores/scores-widget.cjs.js.map +1 -1
- package/dist/domains/dashboards/kinds/scores/scores-widget.es.js +9 -6
- package/dist/domains/dashboards/kinds/scores/scores-widget.es.js.map +1 -1
- package/dist/domains/dashboards/kinds/table/table-tabs.cjs.js +2 -2
- package/dist/domains/dashboards/kinds/table/table-tabs.es.js +2 -2
- package/dist/domains/dashboards/kinds/table/table-widget.cjs.js +1 -1
- package/dist/domains/dashboards/kinds/table/table-widget.es.js +1 -1
- package/dist/domains/dashboards/kinds/timeseries/timeseries-widget.cjs.js +6 -5
- package/dist/domains/dashboards/kinds/timeseries/timeseries-widget.cjs.js.map +1 -1
- package/dist/domains/dashboards/kinds/timeseries/timeseries-widget.es.js +6 -5
- package/dist/domains/dashboards/kinds/timeseries/timeseries-widget.es.js.map +1 -1
- package/dist/domains/dashboards/lib/chrome.cjs.js +9 -2
- package/dist/domains/dashboards/lib/chrome.cjs.js.map +1 -1
- package/dist/domains/dashboards/lib/chrome.es.js +9 -2
- package/dist/domains/dashboards/lib/chrome.es.js.map +1 -1
- package/dist/domains/dashboards/widget-kind.cjs.js.map +1 -1
- package/dist/domains/dashboards/widget-kind.es.js.map +1 -1
- package/dist/domains/metrics/components/agent-runs-card.cjs.js +3 -3
- package/dist/domains/metrics/components/agent-runs-card.es.js +3 -3
- package/dist/domains/metrics/components/breakdown/breakdown-tab-body.cjs.js +1 -1
- package/dist/domains/metrics/components/breakdown/breakdown-tab-body.es.js +1 -1
- package/dist/domains/metrics/components/breakdown-card.cjs.js +1 -1
- package/dist/domains/metrics/components/breakdown-card.es.js +1 -1
- package/dist/domains/metrics/components/chart-area.cjs.js +1 -1
- package/dist/domains/metrics/components/chart-area.es.js +1 -1
- package/dist/domains/metrics/components/chart-card-error.cjs.js +1 -1
- package/dist/domains/metrics/components/chart-card-error.es.js +1 -1
- package/dist/domains/metrics/components/chart-card.cjs.js +1 -1
- package/dist/domains/metrics/components/chart-card.es.js +1 -1
- package/dist/domains/metrics/components/date-range-selector.cjs.js +1 -1
- package/dist/domains/metrics/components/date-range-selector.es.js +1 -1
- package/dist/domains/metrics/components/distinct-ids/distinct-ids-chart.cjs.js +3 -2
- package/dist/domains/metrics/components/distinct-ids/distinct-ids-chart.cjs.js.map +1 -1
- package/dist/domains/metrics/components/distinct-ids/distinct-ids-chart.es.js +3 -2
- package/dist/domains/metrics/components/distinct-ids/distinct-ids-chart.es.js.map +1 -1
- package/dist/domains/metrics/components/failure-rate-card.cjs.js +3 -2
- package/dist/domains/metrics/components/failure-rate-card.cjs.js.map +1 -1
- package/dist/domains/metrics/components/failure-rate-card.es.js +3 -2
- package/dist/domains/metrics/components/failure-rate-card.es.js.map +1 -1
- package/dist/domains/metrics/components/latency-card.cjs.js +3 -2
- package/dist/domains/metrics/components/latency-card.cjs.js.map +1 -1
- package/dist/domains/metrics/components/latency-card.es.js +3 -2
- package/dist/domains/metrics/components/latency-card.es.js.map +1 -1
- package/dist/domains/metrics/components/metrics-time-range-chip.cjs.js +1 -1
- package/dist/domains/metrics/components/metrics-time-range-chip.es.js +1 -1
- package/dist/domains/metrics/components/scores-card.cjs.js +2 -1
- package/dist/domains/metrics/components/scores-card.cjs.js.map +1 -1
- package/dist/domains/metrics/components/scores-card.es.js +2 -1
- package/dist/domains/metrics/components/scores-card.es.js.map +1 -1
- package/dist/domains/metrics/components/token-usage-card.cjs.js +3 -3
- package/dist/domains/metrics/components/token-usage-card.es.js +3 -3
- package/dist/domains/metrics/components/trace-aggregate-card.cjs.js +1 -1
- package/dist/domains/metrics/components/trace-aggregate-card.es.js +1 -1
- package/dist/domains/observability/components/observability-histogram.cjs.js +1 -1
- package/dist/domains/observability/components/observability-histogram.es.js +1 -1
- package/dist/domains/spans/components/spans-filter-bar.cjs.js +2 -2
- package/dist/domains/spans/components/spans-filter-bar.es.js +2 -2
- package/dist/domains/spans/hooks/use-spans-filter-bar.cjs.js +1 -1
- package/dist/domains/spans/hooks/use-spans-filter-bar.es.js +1 -1
- package/dist/domains/threads/components/threads-filter-bar.cjs.js +2 -2
- package/dist/domains/threads/components/threads-filter-bar.es.js +2 -2
- package/dist/domains/threads/hooks/use-threads-filter-bar.cjs.js +1 -1
- package/dist/domains/threads/hooks/use-threads-filter-bar.es.js +1 -1
- package/dist/domains/traces/components/traces-explorer/traces-explorer-filter-bar.cjs.js +2 -2
- package/dist/domains/traces/components/traces-explorer/traces-explorer-filter-bar.es.js +2 -2
- package/dist/domains/traces/components/traces-explorer/use-traces-filter-bar.cjs.js +1 -1
- package/dist/domains/traces/components/traces-explorer/use-traces-filter-bar.es.js +1 -1
- package/dist/{filter-bar-time-range-mg9JeYue.js → filter-bar-time-range-qlV8p7hp.js} +2 -2
- package/dist/{filter-bar-time-range-mg9JeYue.js.map → filter-bar-time-range-qlV8p7hp.js.map} +1 -1
- package/dist/{filter-bar-time-range-BA-pXMlo.cjs → filter-bar-time-range-zdM024JD.cjs} +2 -2
- package/dist/{filter-bar-time-range-BA-pXMlo.cjs.map → filter-bar-time-range-zdM024JD.cjs.map} +1 -1
- package/dist/src/domains/chat/tools/connect-prompt-tool.d.ts +10 -1
- package/dist/src/domains/connect/types.d.ts +16 -0
- package/dist/src/domains/dashboards/components/dashboard-renderer.d.ts +4 -2
- package/dist/src/domains/dashboards/hooks/use-widget-views.d.ts +4 -3
- package/dist/src/domains/dashboards/index.d.ts +1 -1
- package/dist/src/domains/dashboards/kinds/scores/scores-widget.d.ts +1 -1
- package/dist/src/domains/dashboards/lib/chrome.d.ts +6 -4
- package/dist/src/domains/dashboards/widget-kind.d.ts +12 -0
- package/dist/src/ds/components/DateTimeRangePicker/date-time-range-picker.d.ts +12 -1
- package/dist/src/ds/components/MetricsCard/metrics-card-headline.d.ts +1 -1
- package/dist/src/ds/components/MetricsLineChart/metrics-line-chart.d.ts +8 -0
- package/dist/style.css +4 -0
- package/dist/{tool-card-rxhTDpk_.cjs → tool-card-BOLXeKIp.cjs} +4 -1
- package/dist/{tool-card-rxhTDpk_.cjs.map → tool-card-BOLXeKIp.cjs.map} +1 -1
- package/dist/{tool-card-toDgoZzJ.js → tool-card-HZ5bm7ue.js} +4 -1
- package/dist/{tool-card-toDgoZzJ.js.map → tool-card-HZ5bm7ue.js.map} +1 -1
- package/package.json +9 -9
- package/dist/MetricsCard-CEB-f2sv.cjs.map +0 -1
- package/dist/MetricsCard-M8awJ2B_.js.map +0 -1
- package/dist/MetricsLineChart-BhHoduO0.js.map +0 -1
- package/dist/MetricsLineChart-CjBm4IXu.cjs.map +0 -1
- package/dist/date-time-range-picker-CTt7Od0P.cjs.map +0 -1
- package/dist/date-time-range-picker-DKh1K9Iy.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tool-card-toDgoZzJ.js","names":["_Fragment","_Fragment"],"sources":["../src/domains/chat/tools/badges/agent-badge.tsx","../src/domains/chat/tools/badges/agent-badge-wrapper.tsx","../src/domains/chat/tools/tool-card.tsx"],"sourcesContent":["import React from 'react';\nimport Markdown from 'react-markdown';\nimport { ToolCard } from '../tool-card';\nimport { BackgroundTaskMetadataDialogTrigger } from './background-task-metadata-dialog';\nimport type { MessageMetadata } from '@/domains/chat';\nimport { NetworkChoiceMetadataDialogTrigger } from '@/domains/chat/components/network-choice-metadata-dialog';\nimport { SectionLabel } from '@/domains/chat/components/section-label';\nimport { awaitsToolApproval } from '@/domains/chat/tools/badges/awaits-tool-approval';\nimport type { ToolApprovalRequest } from '@/domains/chat/tools/badges/tool-approval-badge';\nimport { ToolApprovalBadge } from '@/domains/chat/tools/badges/tool-approval-badge';\nimport type { ActivityStatus } from '@/ds/components/ai/activity';\nimport { ToolCallMono } from '@/ds/components/ai/tool-call';\nimport { Button } from '@/ds/components/Button';\nimport { CodeEditor } from '@/ds/components/CodeEditor';\nimport { AgentIcon } from '@/ds/icons/AgentIcon';\n\ntype TextMessage = {\n type: 'text';\n content: string;\n};\n\ntype ToolMessage = {\n type: 'tool';\n toolName: string;\n toolOutput?: any;\n args?: any;\n toolCallId: string;\n result?: any;\n};\n\nexport type AgentMessage = TextMessage | ToolMessage;\n\nexport interface AgentBadgeProps extends Omit<ToolApprovalRequest, 'toolCalled'> {\n agentId: string;\n messages: AgentMessage[];\n metadata?: MessageMetadata;\n suspendPayload?: any;\n toolCalled?: boolean;\n isComplete?: boolean;\n keepOpenForStreamingChildMessages?: boolean;\n status?: ActivityStatus;\n errorText?: string;\n onLoadPrevious?: () => void;\n isLoadingPrevious?: boolean;\n}\n\nexport const AgentBadge = ({\n agentId,\n messages = [],\n metadata,\n toolCallId,\n toolApprovalMetadata,\n toolName,\n isNetwork,\n suspendPayload,\n toolCalled: toolCalledProp,\n isComplete = false,\n keepOpenForStreamingChildMessages = false,\n status = 'idle',\n errorText,\n onLoadPrevious,\n isLoadingPrevious = false,\n}: AgentBadgeProps) => {\n const routingDecision = metadata?.mode === 'network' ? metadata.routingDecision : undefined;\n const selectionReason =\n metadata?.mode === 'network' ? (routingDecision?.selectionReason ?? metadata.selectionReason) : undefined;\n const agentNetworkInput = metadata?.mode === 'network' ? (routingDecision ?? metadata.agentInput) : undefined;\n\n const parentSuspendedTools =\n metadata?.mode === 'stream' || metadata?.mode === 'network' || metadata?.mode === 'generate'\n ? metadata?.suspendedTools\n : undefined;\n\n const bgEntry =\n (metadata?.mode === 'stream' || metadata?.mode === 'generate') && metadata?.backgroundTasks\n ? metadata.backgroundTasks[toolCallId]\n : undefined;\n\n const allChildToolsComplete =\n messages.length > 0 &&\n messages.every(message => {\n if (message.type === 'text') {\n return true;\n }\n return message.toolOutput !== undefined;\n });\n\n let toolCalled = isComplete && allChildToolsComplete;\n\n if (isNetwork) {\n toolCalled = toolCalledProp ?? allChildToolsComplete;\n }\n\n const isError = status === 'error';\n const shouldCollapseContent = isComplete && !isError && !toolApprovalMetadata && !keepOpenForStreamingChildMessages;\n const shownError = isError ? errorText : undefined;\n const hasBody =\n Boolean(onLoadPrevious) ||\n messages.length > 0 ||\n Boolean(shownError) ||\n Boolean(suspendPayload) ||\n awaitsToolApproval({ toolApprovalMetadata, toolCalled });\n\n let suspendPayloadSlot =\n typeof suspendPayload === 'string' ? (\n <ToolCallMono copyText={suspendPayload} className=\"text-muted-foreground\">\n {suspendPayload}\n </ToolCallMono>\n ) : (\n <CodeEditor data={suspendPayload} data-testid=\"tool-suspend-payload\" />\n );\n\n return (\n <ToolApprovalBadge\n approval={{\n toolCalled,\n toolCallId,\n toolApprovalMetadata,\n toolName,\n isNetwork,\n isGenerateMode: metadata?.mode === 'generate',\n }}\n data-testid=\"agent-badge\"\n icon={<AgentIcon className=\"text-span-agent\" />}\n title={agentId}\n status={status}\n initialCollapsed={shouldCollapseContent}\n extraInfo={\n metadata?.mode === 'network' ? (\n <NetworkChoiceMetadataDialogTrigger\n selectionReason={selectionReason ?? ''}\n input={agentNetworkInput as string | Record<string, unknown> | undefined}\n />\n ) : bgEntry?.taskId && bgEntry?.startedAt ? (\n <BackgroundTaskMetadataDialogTrigger backgroundTask={bgEntry} />\n ) : null\n }\n >\n {hasBody && (\n <>\n {onLoadPrevious && (\n <Button\n variant=\"ghost\"\n size=\"sm\"\n onClick={onLoadPrevious}\n disabled={isLoadingPrevious}\n data-testid=\"agent-badge-load-previous\"\n >\n {isLoadingPrevious ? 'Loading earlier messages…' : 'Load earlier messages'}\n </Button>\n )}\n\n {messages.map((message, index) => {\n if (message.type === 'text') {\n return <Markdown key={index}>{message.content}</Markdown>;\n }\n\n let result;\n\n try {\n result = typeof message.toolOutput === 'string' ? JSON.parse(message.toolOutput) : message.toolOutput;\n } catch {\n result = message.toolOutput;\n }\n\n return (\n <React.Fragment key={index}>\n <ToolCard\n toolName={message.toolName}\n input={message.args}\n output={result}\n state=\"output-available\"\n toolCallId={message.toolCallId}\n metadata={{\n mode: 'stream',\n requireApprovalMetadata: isNetwork ? metadata?.requireApprovalMetadata : undefined,\n suspendedTools: parentSuspendedTools,\n }}\n />\n </React.Fragment>\n );\n })}\n\n {shownError && (\n <ToolCallMono copyText={shownError} data-testid=\"agent-error\" className=\"text-destructive-foreground\">\n {shownError}\n </ToolCallMono>\n )}\n\n {suspendPayloadSlot !== undefined && suspendPayload && (\n <div>\n <SectionLabel>Agent suspend payload</SectionLabel>\n {suspendPayloadSlot}\n </div>\n )}\n </>\n )}\n </ToolApprovalBadge>\n );\n};\n","import { toAISdkV5Messages } from '@mastra/ai-sdk/ui';\nimport { useAgentMessages } from '@mastra/react/hooks/agents';\nimport type { AgentMessage } from './agent-badge';\nimport { AgentBadge } from './agent-badge';\nimport { resolveToChildMessages } from './resolve-child-messages';\nimport type { MessageMetadata } from '@/domains/chat';\nimport { LoadingBadge } from '@/domains/chat/components/loading-badge';\nimport type { ToolApprovalRequest } from '@/domains/chat/tools/badges/tool-approval-badge';\nimport type { ActivityStatus } from '@/ds/components/ai/activity';\n\ninterface SubAgentToolResult {\n toolName: string;\n toolCallId: string;\n result: any;\n args: any;\n}\n\ninterface AgentBadgeWrapperProps extends Omit<ToolApprovalRequest, 'toolCalled'> {\n agentId: string;\n result?: {\n childMessages?: AgentMessage[];\n subAgentResourceId?: string;\n subAgentThreadId?: string;\n subAgentToolResults?: SubAgentToolResult[];\n text?: string;\n };\n metadata?: MessageMetadata;\n suspendPayload?: any;\n toolCalled?: boolean;\n isComplete?: boolean;\n status?: ActivityStatus;\n errorText?: string;\n}\n\nexport const AgentBadgeWrapper = ({\n agentId,\n result,\n metadata,\n toolCallId,\n toolApprovalMetadata,\n toolName,\n isNetwork,\n suspendPayload,\n toolCalled,\n isComplete,\n status,\n errorText,\n}: AgentBadgeWrapperProps) => {\n const shouldFetchAgentMessages = Boolean(\n result?.subAgentThreadId && !result.text && !result.subAgentToolResults?.length,\n );\n const { data, isLoading, fetchNextPage, hasNextPage, isFetchingNextPage } = useAgentMessages({\n threadId: shouldFetchAgentMessages ? result?.subAgentThreadId : undefined,\n agentId,\n memory: true,\n });\n\n if (isLoading) {\n return <LoadingBadge />;\n }\n\n const convertedMessages = data?.messages ? toAISdkV5Messages(data.messages) : [];\n\n let childMessages = result?.childMessages?.length ? result.childMessages : undefined;\n\n if (!childMessages && result?.subAgentToolResults?.length) {\n const toolMessages: AgentMessage[] = result.subAgentToolResults.map(tr => ({\n type: 'tool' as const,\n toolName: tr.toolName,\n toolCallId: tr.toolCallId,\n args: tr.args,\n toolOutput: tr.result,\n }));\n if (result.text) {\n toolMessages.push({ type: 'text' as const, content: result.text });\n }\n childMessages = toolMessages;\n }\n\n if (!childMessages && result?.text) {\n childMessages = [{ type: 'text' as const, content: result.text }];\n }\n\n let onLoadPrevious: (() => void) | undefined;\n if (!childMessages) {\n childMessages = resolveToChildMessages(convertedMessages) as AgentMessage[];\n onLoadPrevious = hasNextPage ? fetchNextPage : undefined;\n }\n\n const hasStreamingChildMessages = Boolean(result && Object.prototype.hasOwnProperty.call(result, 'childMessages'));\n\n return (\n <AgentBadge\n agentId={agentId}\n messages={childMessages ?? []}\n keepOpenForStreamingChildMessages={hasStreamingChildMessages}\n metadata={metadata}\n toolCallId={toolCallId}\n toolApprovalMetadata={toolApprovalMetadata}\n toolName={toolName}\n isNetwork={isNetwork}\n suspendPayload={suspendPayload}\n toolCalled={toolCalled}\n isComplete={isComplete}\n status={status}\n errorText={errorText}\n onLoadPrevious={onLoadPrevious}\n isLoadingPrevious={isFetchingNextPage}\n />\n );\n};\n","import { useMcpAppTools } from '@mastra/react/hooks/mcps';\nimport { useCallback, useContext } from 'react';\nimport { AgentBadgeWrapper } from './badges/agent-badge-wrapper';\nimport { FileTreeBadge } from './badges/file-tree-badge';\nimport { KnowledgeToolBadge, knowledgeCard } from './badges/knowledge-tool-badge';\nimport { DeferredMcpInputRequest, ForegroundMcpInputRequest } from './badges/mcp-input-request-badge';\nimport { SandboxExecutionBadge } from './badges/sandbox-execution-badge';\nimport { ToolBadge } from './badges/tool-badge';\nimport { useDeferredInputRequest } from './badges/use-deferred-input-request';\nimport { useWorkflowStream, WorkflowBadge } from './badges/workflow-badge';\nimport { suspendedMcpInputRequest } from './mcp-input-request';\nimport { SubmitPlanTool } from './submit-plan-tool';\nimport { ToolResultMedia } from './tool-result-media';\nimport type { MessageMetadata } from '@/domains/chat';\nimport { ChatAgentContext, useChatRunning, useChatSend } from '@/domains/chat/context/chat-context';\nimport { AskUserTool } from '@/domains/chat/tools/ask-user-tool';\nimport { CodeModeBadge } from '@/domains/chat/tools/badges/code-mode-badge';\nimport { ObservationMarkerBadge } from '@/domains/chat/tools/badges/observation-marker-badge';\nimport { ConnectPromptTool } from '@/domains/chat/tools/connect-prompt-tool';\nimport {\n badgeStatus,\n codeModeCall,\n isAgentCall,\n isDeferredCall,\n isSettledState,\n isWorkflowCall,\n toolCardKind,\n toolInteraction,\n} from '@/domains/chat/tools/tool-card-kind';\nimport { McpAppToolResult } from '@/domains/mcps/components/mcp-app-tool-result';\nimport { PlaygroundWorkflowRunProvider } from '@/domains/workflows/context/playground-workflow-run-provider';\n\n/** A `data`-typed part the agent wrote via `writer.custom`, scoped to a call by `data.toolCallId`. */\nexport interface DataMessagePart {\n type: string;\n name?: string;\n data?: any;\n}\n\nexport interface ToolCardProps {\n toolName: string;\n input: any;\n output: any;\n modelOutput?: unknown;\n /** The Mastra background task running the call, when it was deferred. */\n backgroundTaskId?: string;\n /** The background task's status in the part's metadata: `running` until the part holds its result. */\n backgroundTaskStatus?: string;\n toolCallId: string;\n /** Part state: v5 `output-available`/`output-error`/`input-available`, or v4 `result`/`call`. */\n state?: string;\n /** Error message when `state` is `output-error`. */\n errorText?: string;\n metadata?: MessageMetadata;\n /** `data`-typed parts from the parent message, for badges that read live streaming metadata. */\n dataParts?: ReadonlyArray<DataMessagePart>;\n /** Historical rendering mode: preserve presentation while suppressing executable actions and side effects. */\n readOnly?: boolean;\n}\n\nconst stripPrefix = (toolName: string, prefix: string): string =>\n toolName.startsWith(prefix) ? toolName.slice(prefix.length) : toolName;\n\n/** Its own workflow run scope, so a streaming workflow result can drive a live graph inside the card. */\nexport const ToolCard = (props: ToolCardProps) => {\n return (\n <PlaygroundWorkflowRunProvider workflowId=\"\" withoutTimeTravel>\n <ToolCardInner {...props} />\n </PlaygroundWorkflowRunProvider>\n );\n};\n\nexport const ToolCardInner = ({\n toolName,\n input,\n output,\n modelOutput,\n backgroundTaskId,\n backgroundTaskStatus,\n toolCallId,\n state,\n errorText,\n metadata,\n dataParts,\n readOnly = false,\n}: ToolCardProps) => {\n const { data: mcpAppTools } = useMcpAppTools();\n const send = useChatSend();\n const chatAgent = useContext(ChatAgentContext);\n const { isRunning } = useChatRunning();\n const handleMcpAppSendMessage = useCallback(\n (content: string) => {\n void send({ message: content });\n },\n [send],\n );\n useWorkflowStream(output);\n\n const kind = toolCardKind(\n { toolName, toolCallId, input, output, state, backgroundTaskId, backgroundTaskStatus },\n { metadata, mcpAppTools },\n );\n const { approval: toolApprovalMetadata, suspended: suspendedToolMetadata } = toolInteraction(\n metadata,\n toolName,\n toolCallId,\n );\n const status = badgeStatus(state, isRunning);\n // Any deferred call, whatever its output looks like: live it holds the MCP task's progress records,\n // after a reload the \"Background task started\" placeholder.\n const deferredRequest = useDeferredInputRequest(\n backgroundTaskId ?? '',\n isDeferredCall(backgroundTaskId, backgroundTaskStatus) && !readOnly,\n );\n const isNetwork = metadata?.mode === 'network';\n const toolCalled = isNetwork && metadata?.hasMoreMessages ? true : undefined;\n const agentToolName = stripPrefix(toolName, 'agent-');\n const workflowToolName = stripPrefix(toolName, 'workflow-');\n const deferredCard = deferredRequest && backgroundTaskId && (\n <DeferredMcpInputRequest\n key={String(deferredRequest.suspendedAt)}\n backgroundTaskId={backgroundTaskId}\n request={deferredRequest}\n />\n );\n\n const body = renderKind();\n // A deferred call's question shows under whichever badge draws the call. One fragment either way, so\n // the badge (and its task progress) isn't remounted when the question comes and goes.\n return (\n <>\n {body}\n {body !== null && deferredCard}\n </>\n );\n\n function renderKind() {\n switch (kind) {\n case 'hidden':\n return null;\n case 'observation':\n return <ObservationMarkerBadge toolName={toolName} omData={output?.omData ?? input} />;\n case 'ask_user':\n if (!readOnly) {\n return <AskUserTool toolName={toolName} toolCallId={toolCallId} output={output} metadata={metadata} />;\n }\n break;\n case 'connect_prompt':\n return <ConnectPromptTool output={output} readOnly={readOnly} />;\n case 'submit_plan':\n return (\n <SubmitPlanTool\n agentId={chatAgent?.agentId}\n agentVersionId={chatAgent?.agentVersionId}\n requestContext={chatAgent?.requestContext}\n toolName={toolName}\n toolCallId={toolCallId}\n output={output}\n metadata={metadata}\n />\n );\n case 'mcp_input_request': {\n const payload = suspendedMcpInputRequest(metadata, toolCallId);\n if (!payload) break;\n return (\n <>\n <ToolBadge\n toolName={toolName}\n args={input}\n result={output}\n toolOutput={[]}\n metadata={metadata}\n toolCallId={toolCallId}\n toolApprovalMetadata={toolApprovalMetadata}\n isNetwork={isNetwork}\n toolCalled={toolCalled}\n status={status}\n mcpInputRequest={payload}\n />\n {/* The card stays mounted across a call's suspensions, so a new one must start with no answers. */}\n <ForegroundMcpInputRequest\n key={payload.requestedAt ?? payload.createdAt}\n toolCallId={toolCallId}\n payload={payload}\n readOnly={readOnly}\n isNetwork={isNetwork}\n />\n </>\n );\n }\n case 'background': {\n const isAgent = isAgentCall(metadata, toolName);\n const isWorkflow = isWorkflowCall(metadata, toolName);\n return (\n <>\n <ToolBadge\n toolName={isAgent ? agentToolName : isWorkflow ? workflowToolName : toolName}\n args={input}\n result={output}\n toolOutput={[]}\n metadata={metadata}\n toolCallId={toolCallId}\n toolApprovalMetadata={toolApprovalMetadata}\n suspendPayload={suspendedToolMetadata?.suspendPayload}\n isNetwork={isNetwork}\n toolCalled={toolCalled}\n withoutArgs={isAgent || isWorkflow}\n status={status}\n backgroundTaskId={backgroundTaskId}\n backgroundTaskStatus={backgroundTaskStatus}\n mcpInputRequest={deferredRequest?.payload}\n />\n </>\n );\n }\n case 'agent':\n return (\n <AgentBadgeWrapper\n agentId={agentToolName}\n result={output}\n metadata={metadata}\n toolCallId={toolCallId}\n toolApprovalMetadata={toolApprovalMetadata}\n toolName={toolName}\n isNetwork={isNetwork}\n suspendPayload={suspendedToolMetadata?.suspendPayload}\n toolCalled={toolCalled}\n isComplete={isSettledState(state)}\n status={status}\n errorText={errorText}\n />\n );\n case 'workflow':\n return (\n <WorkflowBadge\n workflowId={workflowToolName}\n isStreaming={metadata?.mode === 'stream' || isNetwork}\n result={output}\n metadata={metadata}\n toolCallId={toolCallId}\n toolApprovalMetadata={toolApprovalMetadata}\n suspendPayload={suspendedToolMetadata?.suspendPayload}\n toolName={toolName}\n isNetwork={isNetwork}\n toolCalled={toolCalled}\n />\n );\n case 'file_tree':\n return (\n <FileTreeBadge\n toolName={toolName}\n args={input}\n result={output}\n metadata={metadata}\n toolCallId={toolCallId}\n toolApprovalMetadata={toolApprovalMetadata}\n isNetwork={isNetwork}\n toolCalled={toolCalled}\n dataParts={dataParts}\n status={status}\n />\n );\n case 'sandbox':\n return (\n <SandboxExecutionBadge\n toolName={toolName}\n args={input}\n result={output}\n metadata={metadata}\n toolCallId={toolCallId}\n toolApprovalMetadata={toolApprovalMetadata}\n isNetwork={isNetwork}\n toolCalled={toolCalled}\n dataParts={dataParts}\n status={status}\n />\n );\n case 'code_mode': {\n const call = codeModeCall(input, output);\n if (call) {\n return (\n <CodeModeBadge\n toolName={toolName}\n code={call.code}\n result={call.result}\n metadata={metadata}\n toolCallId={toolCallId}\n toolApprovalMetadata={toolApprovalMetadata}\n isNetwork={isNetwork}\n toolCalled={toolCalled}\n />\n );\n }\n break;\n }\n case 'knowledge': {\n const card = knowledgeCard(toolName, input, output, state === 'output-error' ? (errorText ?? '') : undefined);\n if (card) {\n return (\n <KnowledgeToolBadge\n card={card}\n toolName={toolName}\n metadata={metadata}\n toolCallId={toolCallId}\n toolApprovalMetadata={toolApprovalMetadata}\n isNetwork={isNetwork}\n status={status}\n />\n );\n }\n break;\n }\n case 'mcp_app':\n case 'plain':\n break;\n }\n\n const mcpAppInfo = mcpAppTools?.[toolName];\n\n return (\n <>\n <ToolBadge\n toolName={toolName}\n args={input}\n result={output ?? errorText}\n toolOutput={output?.toolOutput || []}\n metadata={metadata}\n toolCallId={toolCallId}\n toolApprovalMetadata={toolApprovalMetadata}\n suspendPayload={suspendedToolMetadata?.suspendPayload}\n isNetwork={isNetwork}\n toolCalled={toolCalled}\n status={status}\n backgroundTaskId={backgroundTaskId}\n backgroundTaskStatus={backgroundTaskStatus}\n mcpInputRequest={deferredRequest?.payload}\n />\n <ToolResultMedia modelOutput={modelOutput} />\n {mcpAppInfo && output !== undefined && (\n <McpAppToolResult\n appInfo={mcpAppInfo}\n toolArgs={input}\n toolResult={output}\n onSendMessage={handleMcpAppSendMessage}\n readOnly={readOnly}\n />\n )}\n </>\n );\n }\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"tool-card-HZ5bm7ue.js","names":["_Fragment","_Fragment"],"sources":["../src/domains/chat/tools/badges/agent-badge.tsx","../src/domains/chat/tools/badges/agent-badge-wrapper.tsx","../src/domains/chat/tools/tool-card.tsx"],"sourcesContent":["import React from 'react';\nimport Markdown from 'react-markdown';\nimport { ToolCard } from '../tool-card';\nimport { BackgroundTaskMetadataDialogTrigger } from './background-task-metadata-dialog';\nimport type { MessageMetadata } from '@/domains/chat';\nimport { NetworkChoiceMetadataDialogTrigger } from '@/domains/chat/components/network-choice-metadata-dialog';\nimport { SectionLabel } from '@/domains/chat/components/section-label';\nimport { awaitsToolApproval } from '@/domains/chat/tools/badges/awaits-tool-approval';\nimport type { ToolApprovalRequest } from '@/domains/chat/tools/badges/tool-approval-badge';\nimport { ToolApprovalBadge } from '@/domains/chat/tools/badges/tool-approval-badge';\nimport type { ActivityStatus } from '@/ds/components/ai/activity';\nimport { ToolCallMono } from '@/ds/components/ai/tool-call';\nimport { Button } from '@/ds/components/Button';\nimport { CodeEditor } from '@/ds/components/CodeEditor';\nimport { AgentIcon } from '@/ds/icons/AgentIcon';\n\ntype TextMessage = {\n type: 'text';\n content: string;\n};\n\ntype ToolMessage = {\n type: 'tool';\n toolName: string;\n toolOutput?: any;\n args?: any;\n toolCallId: string;\n result?: any;\n};\n\nexport type AgentMessage = TextMessage | ToolMessage;\n\nexport interface AgentBadgeProps extends Omit<ToolApprovalRequest, 'toolCalled'> {\n agentId: string;\n messages: AgentMessage[];\n metadata?: MessageMetadata;\n suspendPayload?: any;\n toolCalled?: boolean;\n isComplete?: boolean;\n keepOpenForStreamingChildMessages?: boolean;\n status?: ActivityStatus;\n errorText?: string;\n onLoadPrevious?: () => void;\n isLoadingPrevious?: boolean;\n}\n\nexport const AgentBadge = ({\n agentId,\n messages = [],\n metadata,\n toolCallId,\n toolApprovalMetadata,\n toolName,\n isNetwork,\n suspendPayload,\n toolCalled: toolCalledProp,\n isComplete = false,\n keepOpenForStreamingChildMessages = false,\n status = 'idle',\n errorText,\n onLoadPrevious,\n isLoadingPrevious = false,\n}: AgentBadgeProps) => {\n const routingDecision = metadata?.mode === 'network' ? metadata.routingDecision : undefined;\n const selectionReason =\n metadata?.mode === 'network' ? (routingDecision?.selectionReason ?? metadata.selectionReason) : undefined;\n const agentNetworkInput = metadata?.mode === 'network' ? (routingDecision ?? metadata.agentInput) : undefined;\n\n const parentSuspendedTools =\n metadata?.mode === 'stream' || metadata?.mode === 'network' || metadata?.mode === 'generate'\n ? metadata?.suspendedTools\n : undefined;\n\n const bgEntry =\n (metadata?.mode === 'stream' || metadata?.mode === 'generate') && metadata?.backgroundTasks\n ? metadata.backgroundTasks[toolCallId]\n : undefined;\n\n const allChildToolsComplete =\n messages.length > 0 &&\n messages.every(message => {\n if (message.type === 'text') {\n return true;\n }\n return message.toolOutput !== undefined;\n });\n\n let toolCalled = isComplete && allChildToolsComplete;\n\n if (isNetwork) {\n toolCalled = toolCalledProp ?? allChildToolsComplete;\n }\n\n const isError = status === 'error';\n const shouldCollapseContent = isComplete && !isError && !toolApprovalMetadata && !keepOpenForStreamingChildMessages;\n const shownError = isError ? errorText : undefined;\n const hasBody =\n Boolean(onLoadPrevious) ||\n messages.length > 0 ||\n Boolean(shownError) ||\n Boolean(suspendPayload) ||\n awaitsToolApproval({ toolApprovalMetadata, toolCalled });\n\n let suspendPayloadSlot =\n typeof suspendPayload === 'string' ? (\n <ToolCallMono copyText={suspendPayload} className=\"text-muted-foreground\">\n {suspendPayload}\n </ToolCallMono>\n ) : (\n <CodeEditor data={suspendPayload} data-testid=\"tool-suspend-payload\" />\n );\n\n return (\n <ToolApprovalBadge\n approval={{\n toolCalled,\n toolCallId,\n toolApprovalMetadata,\n toolName,\n isNetwork,\n isGenerateMode: metadata?.mode === 'generate',\n }}\n data-testid=\"agent-badge\"\n icon={<AgentIcon className=\"text-span-agent\" />}\n title={agentId}\n status={status}\n initialCollapsed={shouldCollapseContent}\n extraInfo={\n metadata?.mode === 'network' ? (\n <NetworkChoiceMetadataDialogTrigger\n selectionReason={selectionReason ?? ''}\n input={agentNetworkInput as string | Record<string, unknown> | undefined}\n />\n ) : bgEntry?.taskId && bgEntry?.startedAt ? (\n <BackgroundTaskMetadataDialogTrigger backgroundTask={bgEntry} />\n ) : null\n }\n >\n {hasBody && (\n <>\n {onLoadPrevious && (\n <Button\n variant=\"ghost\"\n size=\"sm\"\n onClick={onLoadPrevious}\n disabled={isLoadingPrevious}\n data-testid=\"agent-badge-load-previous\"\n >\n {isLoadingPrevious ? 'Loading earlier messages…' : 'Load earlier messages'}\n </Button>\n )}\n\n {messages.map((message, index) => {\n if (message.type === 'text') {\n return <Markdown key={index}>{message.content}</Markdown>;\n }\n\n let result;\n\n try {\n result = typeof message.toolOutput === 'string' ? JSON.parse(message.toolOutput) : message.toolOutput;\n } catch {\n result = message.toolOutput;\n }\n\n return (\n <React.Fragment key={index}>\n <ToolCard\n toolName={message.toolName}\n input={message.args}\n output={result}\n state=\"output-available\"\n toolCallId={message.toolCallId}\n metadata={{\n mode: 'stream',\n requireApprovalMetadata: isNetwork ? metadata?.requireApprovalMetadata : undefined,\n suspendedTools: parentSuspendedTools,\n }}\n />\n </React.Fragment>\n );\n })}\n\n {shownError && (\n <ToolCallMono copyText={shownError} data-testid=\"agent-error\" className=\"text-destructive-foreground\">\n {shownError}\n </ToolCallMono>\n )}\n\n {suspendPayloadSlot !== undefined && suspendPayload && (\n <div>\n <SectionLabel>Agent suspend payload</SectionLabel>\n {suspendPayloadSlot}\n </div>\n )}\n </>\n )}\n </ToolApprovalBadge>\n );\n};\n","import { toAISdkV5Messages } from '@mastra/ai-sdk/ui';\nimport { useAgentMessages } from '@mastra/react/hooks/agents';\nimport type { AgentMessage } from './agent-badge';\nimport { AgentBadge } from './agent-badge';\nimport { resolveToChildMessages } from './resolve-child-messages';\nimport type { MessageMetadata } from '@/domains/chat';\nimport { LoadingBadge } from '@/domains/chat/components/loading-badge';\nimport type { ToolApprovalRequest } from '@/domains/chat/tools/badges/tool-approval-badge';\nimport type { ActivityStatus } from '@/ds/components/ai/activity';\n\ninterface SubAgentToolResult {\n toolName: string;\n toolCallId: string;\n result: any;\n args: any;\n}\n\ninterface AgentBadgeWrapperProps extends Omit<ToolApprovalRequest, 'toolCalled'> {\n agentId: string;\n result?: {\n childMessages?: AgentMessage[];\n subAgentResourceId?: string;\n subAgentThreadId?: string;\n subAgentToolResults?: SubAgentToolResult[];\n text?: string;\n };\n metadata?: MessageMetadata;\n suspendPayload?: any;\n toolCalled?: boolean;\n isComplete?: boolean;\n status?: ActivityStatus;\n errorText?: string;\n}\n\nexport const AgentBadgeWrapper = ({\n agentId,\n result,\n metadata,\n toolCallId,\n toolApprovalMetadata,\n toolName,\n isNetwork,\n suspendPayload,\n toolCalled,\n isComplete,\n status,\n errorText,\n}: AgentBadgeWrapperProps) => {\n const shouldFetchAgentMessages = Boolean(\n result?.subAgentThreadId && !result.text && !result.subAgentToolResults?.length,\n );\n const { data, isLoading, fetchNextPage, hasNextPage, isFetchingNextPage } = useAgentMessages({\n threadId: shouldFetchAgentMessages ? result?.subAgentThreadId : undefined,\n agentId,\n memory: true,\n });\n\n if (isLoading) {\n return <LoadingBadge />;\n }\n\n const convertedMessages = data?.messages ? toAISdkV5Messages(data.messages) : [];\n\n let childMessages = result?.childMessages?.length ? result.childMessages : undefined;\n\n if (!childMessages && result?.subAgentToolResults?.length) {\n const toolMessages: AgentMessage[] = result.subAgentToolResults.map(tr => ({\n type: 'tool' as const,\n toolName: tr.toolName,\n toolCallId: tr.toolCallId,\n args: tr.args,\n toolOutput: tr.result,\n }));\n if (result.text) {\n toolMessages.push({ type: 'text' as const, content: result.text });\n }\n childMessages = toolMessages;\n }\n\n if (!childMessages && result?.text) {\n childMessages = [{ type: 'text' as const, content: result.text }];\n }\n\n let onLoadPrevious: (() => void) | undefined;\n if (!childMessages) {\n childMessages = resolveToChildMessages(convertedMessages) as AgentMessage[];\n onLoadPrevious = hasNextPage ? fetchNextPage : undefined;\n }\n\n const hasStreamingChildMessages = Boolean(result && Object.prototype.hasOwnProperty.call(result, 'childMessages'));\n\n return (\n <AgentBadge\n agentId={agentId}\n messages={childMessages ?? []}\n keepOpenForStreamingChildMessages={hasStreamingChildMessages}\n metadata={metadata}\n toolCallId={toolCallId}\n toolApprovalMetadata={toolApprovalMetadata}\n toolName={toolName}\n isNetwork={isNetwork}\n suspendPayload={suspendPayload}\n toolCalled={toolCalled}\n isComplete={isComplete}\n status={status}\n errorText={errorText}\n onLoadPrevious={onLoadPrevious}\n isLoadingPrevious={isFetchingNextPage}\n />\n );\n};\n","import { useMcpAppTools } from '@mastra/react/hooks/mcps';\nimport { useCallback, useContext } from 'react';\nimport { AgentBadgeWrapper } from './badges/agent-badge-wrapper';\nimport { FileTreeBadge } from './badges/file-tree-badge';\nimport { KnowledgeToolBadge, knowledgeCard } from './badges/knowledge-tool-badge';\nimport { DeferredMcpInputRequest, ForegroundMcpInputRequest } from './badges/mcp-input-request-badge';\nimport { SandboxExecutionBadge } from './badges/sandbox-execution-badge';\nimport { ToolBadge } from './badges/tool-badge';\nimport { useDeferredInputRequest } from './badges/use-deferred-input-request';\nimport { useWorkflowStream, WorkflowBadge } from './badges/workflow-badge';\nimport { suspendedMcpInputRequest } from './mcp-input-request';\nimport { SubmitPlanTool } from './submit-plan-tool';\nimport { ToolResultMedia } from './tool-result-media';\nimport type { MessageMetadata } from '@/domains/chat';\nimport { ChatAgentContext, useChatRunning, useChatSend } from '@/domains/chat/context/chat-context';\nimport { AskUserTool } from '@/domains/chat/tools/ask-user-tool';\nimport { CodeModeBadge } from '@/domains/chat/tools/badges/code-mode-badge';\nimport { ObservationMarkerBadge } from '@/domains/chat/tools/badges/observation-marker-badge';\nimport { ConnectPromptTool } from '@/domains/chat/tools/connect-prompt-tool';\nimport {\n badgeStatus,\n codeModeCall,\n isAgentCall,\n isDeferredCall,\n isSettledState,\n isWorkflowCall,\n toolCardKind,\n toolInteraction,\n} from '@/domains/chat/tools/tool-card-kind';\nimport { McpAppToolResult } from '@/domains/mcps/components/mcp-app-tool-result';\nimport { PlaygroundWorkflowRunProvider } from '@/domains/workflows/context/playground-workflow-run-provider';\n\n/** A `data`-typed part the agent wrote via `writer.custom`, scoped to a call by `data.toolCallId`. */\nexport interface DataMessagePart {\n type: string;\n name?: string;\n data?: any;\n}\n\nexport interface ToolCardProps {\n toolName: string;\n input: any;\n output: any;\n modelOutput?: unknown;\n /** The Mastra background task running the call, when it was deferred. */\n backgroundTaskId?: string;\n /** The background task's status in the part's metadata: `running` until the part holds its result. */\n backgroundTaskStatus?: string;\n toolCallId: string;\n /** Part state: v5 `output-available`/`output-error`/`input-available`, or v4 `result`/`call`. */\n state?: string;\n /** Error message when `state` is `output-error`. */\n errorText?: string;\n metadata?: MessageMetadata;\n /** `data`-typed parts from the parent message, for badges that read live streaming metadata. */\n dataParts?: ReadonlyArray<DataMessagePart>;\n /** Historical rendering mode: preserve presentation while suppressing executable actions and side effects. */\n readOnly?: boolean;\n}\n\nconst stripPrefix = (toolName: string, prefix: string): string =>\n toolName.startsWith(prefix) ? toolName.slice(prefix.length) : toolName;\n\n/** Its own workflow run scope, so a streaming workflow result can drive a live graph inside the card. */\nexport const ToolCard = (props: ToolCardProps) => {\n return (\n <PlaygroundWorkflowRunProvider workflowId=\"\" withoutTimeTravel>\n <ToolCardInner {...props} />\n </PlaygroundWorkflowRunProvider>\n );\n};\n\nexport const ToolCardInner = ({\n toolName,\n input,\n output,\n modelOutput,\n backgroundTaskId,\n backgroundTaskStatus,\n toolCallId,\n state,\n errorText,\n metadata,\n dataParts,\n readOnly = false,\n}: ToolCardProps) => {\n const { data: mcpAppTools } = useMcpAppTools();\n const send = useChatSend();\n const chatAgent = useContext(ChatAgentContext);\n const { isRunning } = useChatRunning();\n const handleMcpAppSendMessage = useCallback(\n (content: string) => {\n void send({ message: content });\n },\n [send],\n );\n useWorkflowStream(output);\n\n const kind = toolCardKind(\n { toolName, toolCallId, input, output, state, backgroundTaskId, backgroundTaskStatus },\n { metadata, mcpAppTools },\n );\n const { approval: toolApprovalMetadata, suspended: suspendedToolMetadata } = toolInteraction(\n metadata,\n toolName,\n toolCallId,\n );\n const status = badgeStatus(state, isRunning);\n // Any deferred call, whatever its output looks like: live it holds the MCP task's progress records,\n // after a reload the \"Background task started\" placeholder.\n const deferredRequest = useDeferredInputRequest(\n backgroundTaskId ?? '',\n isDeferredCall(backgroundTaskId, backgroundTaskStatus) && !readOnly,\n );\n const isNetwork = metadata?.mode === 'network';\n const toolCalled = isNetwork && metadata?.hasMoreMessages ? true : undefined;\n const agentToolName = stripPrefix(toolName, 'agent-');\n const workflowToolName = stripPrefix(toolName, 'workflow-');\n const deferredCard = deferredRequest && backgroundTaskId && (\n <DeferredMcpInputRequest\n key={String(deferredRequest.suspendedAt)}\n backgroundTaskId={backgroundTaskId}\n request={deferredRequest}\n />\n );\n\n const body = renderKind();\n // A deferred call's question shows under whichever badge draws the call. One fragment either way, so\n // the badge (and its task progress) isn't remounted when the question comes and goes.\n return (\n <>\n {body}\n {body !== null && deferredCard}\n </>\n );\n\n function renderKind() {\n switch (kind) {\n case 'hidden':\n return null;\n case 'observation':\n return <ObservationMarkerBadge toolName={toolName} omData={output?.omData ?? input} />;\n case 'ask_user':\n if (!readOnly) {\n return <AskUserTool toolName={toolName} toolCallId={toolCallId} output={output} metadata={metadata} />;\n }\n break;\n case 'connect_prompt':\n return (\n <ConnectPromptTool\n toolName={toolName}\n toolCallId={toolCallId}\n output={output}\n metadata={metadata}\n readOnly={readOnly}\n />\n );\n case 'submit_plan':\n return (\n <SubmitPlanTool\n agentId={chatAgent?.agentId}\n agentVersionId={chatAgent?.agentVersionId}\n requestContext={chatAgent?.requestContext}\n toolName={toolName}\n toolCallId={toolCallId}\n output={output}\n metadata={metadata}\n />\n );\n case 'mcp_input_request': {\n const payload = suspendedMcpInputRequest(metadata, toolCallId);\n if (!payload) break;\n return (\n <>\n <ToolBadge\n toolName={toolName}\n args={input}\n result={output}\n toolOutput={[]}\n metadata={metadata}\n toolCallId={toolCallId}\n toolApprovalMetadata={toolApprovalMetadata}\n isNetwork={isNetwork}\n toolCalled={toolCalled}\n status={status}\n mcpInputRequest={payload}\n />\n {/* The card stays mounted across a call's suspensions, so a new one must start with no answers. */}\n <ForegroundMcpInputRequest\n key={payload.requestedAt ?? payload.createdAt}\n toolCallId={toolCallId}\n payload={payload}\n readOnly={readOnly}\n isNetwork={isNetwork}\n />\n </>\n );\n }\n case 'background': {\n const isAgent = isAgentCall(metadata, toolName);\n const isWorkflow = isWorkflowCall(metadata, toolName);\n return (\n <>\n <ToolBadge\n toolName={isAgent ? agentToolName : isWorkflow ? workflowToolName : toolName}\n args={input}\n result={output}\n toolOutput={[]}\n metadata={metadata}\n toolCallId={toolCallId}\n toolApprovalMetadata={toolApprovalMetadata}\n suspendPayload={suspendedToolMetadata?.suspendPayload}\n isNetwork={isNetwork}\n toolCalled={toolCalled}\n withoutArgs={isAgent || isWorkflow}\n status={status}\n backgroundTaskId={backgroundTaskId}\n backgroundTaskStatus={backgroundTaskStatus}\n mcpInputRequest={deferredRequest?.payload}\n />\n </>\n );\n }\n case 'agent':\n return (\n <AgentBadgeWrapper\n agentId={agentToolName}\n result={output}\n metadata={metadata}\n toolCallId={toolCallId}\n toolApprovalMetadata={toolApprovalMetadata}\n toolName={toolName}\n isNetwork={isNetwork}\n suspendPayload={suspendedToolMetadata?.suspendPayload}\n toolCalled={toolCalled}\n isComplete={isSettledState(state)}\n status={status}\n errorText={errorText}\n />\n );\n case 'workflow':\n return (\n <WorkflowBadge\n workflowId={workflowToolName}\n isStreaming={metadata?.mode === 'stream' || isNetwork}\n result={output}\n metadata={metadata}\n toolCallId={toolCallId}\n toolApprovalMetadata={toolApprovalMetadata}\n suspendPayload={suspendedToolMetadata?.suspendPayload}\n toolName={toolName}\n isNetwork={isNetwork}\n toolCalled={toolCalled}\n />\n );\n case 'file_tree':\n return (\n <FileTreeBadge\n toolName={toolName}\n args={input}\n result={output}\n metadata={metadata}\n toolCallId={toolCallId}\n toolApprovalMetadata={toolApprovalMetadata}\n isNetwork={isNetwork}\n toolCalled={toolCalled}\n dataParts={dataParts}\n status={status}\n />\n );\n case 'sandbox':\n return (\n <SandboxExecutionBadge\n toolName={toolName}\n args={input}\n result={output}\n metadata={metadata}\n toolCallId={toolCallId}\n toolApprovalMetadata={toolApprovalMetadata}\n isNetwork={isNetwork}\n toolCalled={toolCalled}\n dataParts={dataParts}\n status={status}\n />\n );\n case 'code_mode': {\n const call = codeModeCall(input, output);\n if (call) {\n return (\n <CodeModeBadge\n toolName={toolName}\n code={call.code}\n result={call.result}\n metadata={metadata}\n toolCallId={toolCallId}\n toolApprovalMetadata={toolApprovalMetadata}\n isNetwork={isNetwork}\n toolCalled={toolCalled}\n />\n );\n }\n break;\n }\n case 'knowledge': {\n const card = knowledgeCard(toolName, input, output, state === 'output-error' ? (errorText ?? '') : undefined);\n if (card) {\n return (\n <KnowledgeToolBadge\n card={card}\n toolName={toolName}\n metadata={metadata}\n toolCallId={toolCallId}\n toolApprovalMetadata={toolApprovalMetadata}\n isNetwork={isNetwork}\n status={status}\n />\n );\n }\n break;\n }\n case 'mcp_app':\n case 'plain':\n break;\n }\n\n const mcpAppInfo = mcpAppTools?.[toolName];\n\n return (\n <>\n <ToolBadge\n toolName={toolName}\n args={input}\n result={output ?? errorText}\n toolOutput={output?.toolOutput || []}\n metadata={metadata}\n toolCallId={toolCallId}\n toolApprovalMetadata={toolApprovalMetadata}\n suspendPayload={suspendedToolMetadata?.suspendPayload}\n isNetwork={isNetwork}\n toolCalled={toolCalled}\n status={status}\n backgroundTaskId={backgroundTaskId}\n backgroundTaskStatus={backgroundTaskStatus}\n mcpInputRequest={deferredRequest?.payload}\n />\n <ToolResultMedia modelOutput={modelOutput} />\n {mcpAppInfo && output !== undefined && (\n <McpAppToolResult\n appInfo={mcpAppInfo}\n toolArgs={input}\n toolResult={output}\n onSendMessage={handleMcpAppSendMessage}\n readOnly={readOnly}\n />\n )}\n </>\n );\n }\n};\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mastra/playground-ui",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "68.0.0
|
|
4
|
+
"version": "68.0.0",
|
|
5
5
|
"description": "Mastra Playground components",
|
|
6
6
|
"sideEffects": [
|
|
7
7
|
"**/*.css"
|
|
@@ -220,9 +220,9 @@
|
|
|
220
220
|
"react": ">=19.0.0",
|
|
221
221
|
"react-dom": ">=19.0.0",
|
|
222
222
|
"tailwindcss": "^4.0.0",
|
|
223
|
-
"@mastra/
|
|
224
|
-
"@mastra/
|
|
225
|
-
"@mastra/
|
|
223
|
+
"@mastra/client-js": "^1.59.0",
|
|
224
|
+
"@mastra/react": "1.14.1",
|
|
225
|
+
"@mastra/ai-sdk": "^1.11.3"
|
|
226
226
|
},
|
|
227
227
|
"devDependencies": {
|
|
228
228
|
"@modelcontextprotocol/sdk": "^1.32.2",
|
|
@@ -263,12 +263,12 @@
|
|
|
263
263
|
"vite-plugin-lib-inject-css": "^2.2.2",
|
|
264
264
|
"vitest": "5.1.0",
|
|
265
265
|
"@types/d3-force": "^3.0.10",
|
|
266
|
-
"@
|
|
266
|
+
"@mastra/core": "1.82.0",
|
|
267
267
|
"@mastra/ai-sdk": "1.11.3",
|
|
268
|
-
"@mastra/
|
|
269
|
-
"@mastra/
|
|
270
|
-
"@
|
|
271
|
-
"@mastra/react": "1.14.1
|
|
268
|
+
"@mastra/memory": "1.41.0",
|
|
269
|
+
"@mastra/client-js": "^1.59.0",
|
|
270
|
+
"@internal/lint": "0.0.148",
|
|
271
|
+
"@mastra/react": "1.14.1",
|
|
272
272
|
"@mastra/schema-compat": "1.3.17"
|
|
273
273
|
},
|
|
274
274
|
"homepage": "https://mastra.ai",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"MetricsCard-CEB-f2sv.cjs","names":["cn","cn","cn","Txt","cn","SkeletonText","cn","Spinner","cn","Txt","Card","cn","cn","DropdownMenu","cn","ChevronDownIcon","cn","cn","cn","SkeletonText","MetricsKpiCard"],"sources":["../src/ds/components/MetricsCard/metrics-card-actions.tsx","../src/ds/components/MetricsCard/metrics-card-content.tsx","../src/ds/components/MetricsCard/metrics-card-error.tsx","../src/ds/components/MetricsCard/metrics-card-headline.tsx","../src/ds/components/MetricsCard/metrics-card-loading.tsx","../src/ds/components/MetricsCard/metrics-card-no-data.tsx","../src/ds/components/MetricsCard/metrics-card-root.tsx","../src/ds/components/MetricsCard/metrics-card-title.tsx","../src/ds/components/MetricsCard/metrics-card-title-menu.tsx","../src/ds/components/MetricsCard/metrics-card-toolbar.tsx","../src/ds/components/MetricsCard/metrics-card-top-bar.tsx","../src/ds/components/MetricsCard/metrics-card-total.tsx","../src/ds/components/MetricsCard/metrics-card.tsx"],"sourcesContent":["import type { ReactNode } from 'react';\nimport { cn } from '@/lib/utils';\n\n/** Container for icon buttons / small actions in the top bar of a MetricsCard,\n * placed between the title and `Total` (which always renders last). Small (28px) buttons sit\n * centered on the 20px title line without making the top bar taller. */\nexport function MetricsCardActions({\n children,\n reveal = 'always',\n className,\n}: {\n children: ReactNode;\n /**\n * `hover`: the actions appear while the card is hovered or one of them has focus, so a page\n * of cards doesn't read as a page of buttons.\n */\n reveal?: 'always' | 'hover';\n className?: string;\n}) {\n return (\n <div\n className={cn(\n '-my-1 flex shrink-0 items-center gap-1 self-start',\n reveal === 'hover' &&\n 'opacity-0 transition-opacity duration-150 group-hover/metrics-card:opacity-100 focus-within:opacity-100',\n className,\n )}\n >\n {children}\n </div>\n );\n}\n","import type { ReactNode } from 'react';\nimport { cn } from '@/lib/utils';\n\n/**\n * Scrolls sideways only (wide tables). Vertical overflow is hidden: a chart filling a card with a\n * fractional height rounds 1px past it, which otherwise turns the content into a scroll box.\n * Tables inside a card drop the last row's border: the card edge already closes them.\n */\nexport function MetricsCardContent({ children, className }: { children: ReactNode; className?: string }) {\n return (\n <div className={cn('overflow-x-auto overflow-y-hidden [&_tbody>tr:last-child]:border-b-0', className)}>\n {children}\n </div>\n );\n}\n","import { Txt } from '@/ds/components/Txt';\nimport { cn } from '@/lib/utils';\n\nexport function MetricsCardError({\n message = 'Failed to load data',\n className,\n}: {\n message?: string;\n className?: string;\n}) {\n return (\n <div className={cn('flex flex-col items-center justify-center gap-3', className)}>\n <Txt variant=\"caption\" className=\"text-destructive-foreground\">\n {message}\n </Txt>\n </div>\n );\n}\n","import { SkeletonText } from '@/ds/components/Skeleton';\nimport { cn } from '@/lib/utils';\n\n/**\n * The header of a card with one large chart (an explorer, a detail page): the label over a large\n * value, the change against the previous period beside it. Cards in a grid use the compact title\n * and `MetricsCard.Total` instead. A skeleton while loading, in the value's own line box.\n */\nexport function MetricsCardHeadline({\n label,\n value,\n change,\n isLoading = false,\n className,\n}: {\n label: string;\n value: string;\n /** E.g. \"+71.4%\"; shown as \"+71.4% vs previous\". */\n change?: string;\n isLoading?: boolean;\n className?: string;\n}) {\n return (\n <div className={cn('grid gap-1.5', className)}>\n <h3 className=\"text-body-sm text-muted-foreground\">{label}</h3>\n <span className=\"flex items-baseline gap-2\">\n <span className=\"text-title text-foreground tabular-nums [text-box:trim-both_cap_alphabetic]\">\n {isLoading ? <SkeletonText line=\"text-title\" className=\"w-24\" /> : value}\n </span>\n {!isLoading && change && (\n <span className=\"text-body-sm text-muted-foreground tabular-nums\">{change} vs previous</span>\n )}\n </span>\n </div>\n );\n}\n","import { Spinner } from '@/ds/components/Spinner/spinner';\nimport { cn } from '@/lib/utils';\n\nexport function MetricsCardLoading({ className }: { className?: string }) {\n return (\n <div className={cn('flex items-center justify-center', className)}>\n <Spinner size=\"md\" variant=\"pulse\" className=\"text-placeholder\" />\n </div>\n );\n}\n","import { Txt } from '@/ds/components/Txt';\nimport { cn } from '@/lib/utils';\n\nexport function MetricsCardNoData({ message = 'No data yet', className }: { message?: string; className?: string }) {\n return (\n <div className={cn('flex h-full items-center justify-center', className)}>\n <Txt tone=\"faint\">{message}</Txt>\n </div>\n );\n}\n","import type { ReactNode } from 'react';\nimport { Card } from '@/ds/components/Card';\nimport { cn } from '@/lib/utils';\n\nexport function MetricsCardRoot({ children, className }: { children: ReactNode; className?: string }) {\n return (\n <Card\n className={cn(\n 'group/metrics-card grid min-h-72 min-w-80 flex-1 grid-rows-[auto_1fr] gap-4 px-4 py-3 2xl:min-w-120 md:min-w-88 lg:min-w-sm xl:min-w-104',\n className,\n )}\n >\n {children}\n </Card>\n );\n}\n","import { cn } from '@/lib/utils';\n\nexport function MetricsCardTitle({\n children,\n className,\n as: Tag = 'h3',\n}: {\n children: string;\n className?: string;\n as?: 'h2' | 'h3' | 'h4';\n}) {\n return <Tag className={cn('text-body font-medium text-foreground', className)}>{children}</Tag>;\n}\n","import { ChevronDownIcon } from 'lucide-react';\nimport { DropdownMenu } from '@/ds/components/DropdownMenu';\nimport type { DropdownMenuRadioOption } from '@/ds/components/DropdownMenu';\nimport { cn } from '@/lib/utils';\n\nexport type MetricsCardTitleMenuProps<V extends string> = {\n /** The card title for the picked view (e.g. \"Spend by model\"). */\n title: string;\n value: V;\n onValueChange: (value: V) => void;\n /** The views (e.g. \"By model\", \"By provider\"). */\n options: readonly DropdownMenuRadioOption<V>[];\n className?: string;\n};\n\n/**\n * A card title that switches the card's view, as on Vercel's usage cards: the title is the\n * trigger, with a chevron. The hover fill bleeds past the text so the title stays aligned.\n * Wrapped, so the top bar stretches the wrapper rather than the button, and the menu's open-state\n * nodes next to the trigger don't become the top bar's first child.\n */\nexport function MetricsCardTitleMenu<V extends string>({\n title,\n value,\n onValueChange,\n options,\n className,\n}: MetricsCardTitleMenuProps<V>) {\n return (\n <div className=\"flex min-w-0\">\n <DropdownMenu>\n <DropdownMenu.Trigger\n render={\n <button\n type=\"button\"\n className={cn(\n '-mx-1.5 -my-0.5 flex items-center gap-1 self-start rounded-md px-1.5 py-0.5 text-left text-body font-medium text-foreground hover:bg-fill-subtle',\n className,\n )}\n />\n }\n >\n {title}\n <ChevronDownIcon className=\"size-3.5 text-muted-foreground\" />\n </DropdownMenu.Trigger>\n <DropdownMenu.Content align=\"start\">\n <DropdownMenu.RadioOptions options={options} value={value} onValueChange={onValueChange} closeOnSelect />\n </DropdownMenu.Content>\n </DropdownMenu>\n </div>\n );\n}\n","import type { ReactNode } from 'react';\nimport { cn } from '@/lib/utils';\n\n/**\n * One row under the top bar for a card's controls: a legend, list column headers.\n * The first child sits on the left, the last on the right; either side may be omitted. When\n * both don't fit (a narrow card), the right side wraps to its own line, still on the right. At least 24px\n * tall, so toolbars line up across cards in the same row.\n */\nexport function MetricsCardToolbar({ children, className }: { children: ReactNode; className?: string }) {\n return (\n <div\n className={cn(\n 'flex min-h-6 min-w-0 flex-wrap items-center justify-between gap-x-4 gap-y-2 [&>*:first-child]:min-w-0 [&>*:last-child:not(:first-child)]:ml-auto',\n className,\n )}\n >\n {children}\n </div>\n );\n}\n","import type { ReactNode } from 'react';\nimport { cn } from '@/lib/utils';\n\nexport function MetricsCardTopBar({ children, className }: { children: ReactNode; className?: string }) {\n return (\n <div className={cn('flex items-start gap-4 [&>:first-child]:min-w-0 [&>:first-child]:flex-1', className)}>\n {children}\n </div>\n );\n}\n","import { SkeletonText } from '@/ds/components/Skeleton';\nimport { cn } from '@/lib/utils';\n\n/** One of several totals in a card that charts a few series, marked with its series' color. */\nexport type MetricsCardTotalPart = { label: string; value: string; color: string };\n\n/**\n * A card's total, right of the title in the same type (14px, medium). Always the top\n * bar's last item. A skeleton while loading, in the value's own line box. A card charting a\n * few series of different things (resources, threads) can give one total each, each after a\n * marker in its series' color, like the legend.\n */\nexport function MetricsCardTotal({\n value,\n isLoading = false,\n className,\n}: {\n value: string | readonly MetricsCardTotalPart[];\n isLoading?: boolean;\n className?: string;\n}) {\n return (\n <span className={cn('order-last shrink-0 text-body font-medium text-foreground tabular-nums', className)}>\n <TotalValue value={value} isLoading={isLoading} />\n </span>\n );\n}\n\nfunction TotalValue({ value, isLoading }: { value: string | readonly MetricsCardTotalPart[]; isLoading: boolean }) {\n if (isLoading) return <SkeletonText line=\"text-body\" className=\"h-4 w-14\" />;\n if (typeof value === 'string') return value;\n return (\n <span className=\"inline-flex items-center gap-3\">\n {value.map(part => (\n <span key={part.label} className=\"inline-flex items-center gap-1.5\">\n <span aria-hidden className=\"size-2 shrink-0 rounded-[2px]\" style={{ backgroundColor: part.color }} />\n {/* The marker is decorative; screen readers hear which total this is. */}\n <span className=\"sr-only\">{part.label} </span>\n {part.value}\n </span>\n ))}\n </span>\n );\n}\n","import { MetricsCardActions } from './metrics-card-actions';\nimport { MetricsCardContent } from './metrics-card-content';\nimport { MetricsCardError } from './metrics-card-error';\nimport { MetricsCardHeadline } from './metrics-card-headline';\nimport { MetricsCardLoading } from './metrics-card-loading';\nimport { MetricsCardNoData } from './metrics-card-no-data';\nimport { MetricsCardRoot } from './metrics-card-root';\nimport { MetricsCardTitle } from './metrics-card-title';\nimport { MetricsCardTitleMenu } from './metrics-card-title-menu';\nimport { MetricsCardToolbar } from './metrics-card-toolbar';\nimport { MetricsCardTopBar } from './metrics-card-top-bar';\nimport { MetricsCardTotal } from './metrics-card-total';\nimport { MetricsKpiCard } from '@/ds/components/MetricsKpiCard';\n\nexport const MetricsCard = Object.assign(MetricsCardRoot, {\n Root: MetricsCardRoot,\n Kpi: MetricsKpiCard,\n TopBar: MetricsCardTopBar,\n Toolbar: MetricsCardToolbar,\n Actions: MetricsCardActions,\n Title: MetricsCardTitle,\n TitleMenu: MetricsCardTitleMenu,\n Total: MetricsCardTotal,\n Headline: MetricsCardHeadline,\n Loading: MetricsCardLoading,\n Error: MetricsCardError,\n Content: MetricsCardContent,\n NoData: MetricsCardNoData,\n});\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"MetricsCard-M8awJ2B_.js","names":[],"sources":["../src/ds/components/MetricsCard/metrics-card-actions.tsx","../src/ds/components/MetricsCard/metrics-card-content.tsx","../src/ds/components/MetricsCard/metrics-card-error.tsx","../src/ds/components/MetricsCard/metrics-card-headline.tsx","../src/ds/components/MetricsCard/metrics-card-loading.tsx","../src/ds/components/MetricsCard/metrics-card-no-data.tsx","../src/ds/components/MetricsCard/metrics-card-root.tsx","../src/ds/components/MetricsCard/metrics-card-title.tsx","../src/ds/components/MetricsCard/metrics-card-title-menu.tsx","../src/ds/components/MetricsCard/metrics-card-toolbar.tsx","../src/ds/components/MetricsCard/metrics-card-top-bar.tsx","../src/ds/components/MetricsCard/metrics-card-total.tsx","../src/ds/components/MetricsCard/metrics-card.tsx"],"sourcesContent":["import type { ReactNode } from 'react';\nimport { cn } from '@/lib/utils';\n\n/** Container for icon buttons / small actions in the top bar of a MetricsCard,\n * placed between the title and `Total` (which always renders last). Small (28px) buttons sit\n * centered on the 20px title line without making the top bar taller. */\nexport function MetricsCardActions({\n children,\n reveal = 'always',\n className,\n}: {\n children: ReactNode;\n /**\n * `hover`: the actions appear while the card is hovered or one of them has focus, so a page\n * of cards doesn't read as a page of buttons.\n */\n reveal?: 'always' | 'hover';\n className?: string;\n}) {\n return (\n <div\n className={cn(\n '-my-1 flex shrink-0 items-center gap-1 self-start',\n reveal === 'hover' &&\n 'opacity-0 transition-opacity duration-150 group-hover/metrics-card:opacity-100 focus-within:opacity-100',\n className,\n )}\n >\n {children}\n </div>\n );\n}\n","import type { ReactNode } from 'react';\nimport { cn } from '@/lib/utils';\n\n/**\n * Scrolls sideways only (wide tables). Vertical overflow is hidden: a chart filling a card with a\n * fractional height rounds 1px past it, which otherwise turns the content into a scroll box.\n * Tables inside a card drop the last row's border: the card edge already closes them.\n */\nexport function MetricsCardContent({ children, className }: { children: ReactNode; className?: string }) {\n return (\n <div className={cn('overflow-x-auto overflow-y-hidden [&_tbody>tr:last-child]:border-b-0', className)}>\n {children}\n </div>\n );\n}\n","import { Txt } from '@/ds/components/Txt';\nimport { cn } from '@/lib/utils';\n\nexport function MetricsCardError({\n message = 'Failed to load data',\n className,\n}: {\n message?: string;\n className?: string;\n}) {\n return (\n <div className={cn('flex flex-col items-center justify-center gap-3', className)}>\n <Txt variant=\"caption\" className=\"text-destructive-foreground\">\n {message}\n </Txt>\n </div>\n );\n}\n","import { SkeletonText } from '@/ds/components/Skeleton';\nimport { cn } from '@/lib/utils';\n\n/**\n * The header of a card with one large chart (an explorer, a detail page): the label over a large\n * value, the change against the previous period beside it. Cards in a grid use the compact title\n * and `MetricsCard.Total` instead. A skeleton while loading, in the value's own line box.\n */\nexport function MetricsCardHeadline({\n label,\n value,\n change,\n isLoading = false,\n className,\n}: {\n label: string;\n value: string;\n /** E.g. \"+71.4%\"; shown as \"+71.4% vs previous\". */\n change?: string;\n isLoading?: boolean;\n className?: string;\n}) {\n return (\n <div className={cn('grid gap-1.5', className)}>\n <h3 className=\"text-body-sm text-muted-foreground\">{label}</h3>\n <span className=\"flex items-baseline gap-2\">\n <span className=\"text-title text-foreground tabular-nums [text-box:trim-both_cap_alphabetic]\">\n {isLoading ? <SkeletonText line=\"text-title\" className=\"w-24\" /> : value}\n </span>\n {!isLoading && change && (\n <span className=\"text-body-sm text-muted-foreground tabular-nums\">{change} vs previous</span>\n )}\n </span>\n </div>\n );\n}\n","import { Spinner } from '@/ds/components/Spinner/spinner';\nimport { cn } from '@/lib/utils';\n\nexport function MetricsCardLoading({ className }: { className?: string }) {\n return (\n <div className={cn('flex items-center justify-center', className)}>\n <Spinner size=\"md\" variant=\"pulse\" className=\"text-placeholder\" />\n </div>\n );\n}\n","import { Txt } from '@/ds/components/Txt';\nimport { cn } from '@/lib/utils';\n\nexport function MetricsCardNoData({ message = 'No data yet', className }: { message?: string; className?: string }) {\n return (\n <div className={cn('flex h-full items-center justify-center', className)}>\n <Txt tone=\"faint\">{message}</Txt>\n </div>\n );\n}\n","import type { ReactNode } from 'react';\nimport { Card } from '@/ds/components/Card';\nimport { cn } from '@/lib/utils';\n\nexport function MetricsCardRoot({ children, className }: { children: ReactNode; className?: string }) {\n return (\n <Card\n className={cn(\n 'group/metrics-card grid min-h-72 min-w-80 flex-1 grid-rows-[auto_1fr] gap-4 px-4 py-3 2xl:min-w-120 md:min-w-88 lg:min-w-sm xl:min-w-104',\n className,\n )}\n >\n {children}\n </Card>\n );\n}\n","import { cn } from '@/lib/utils';\n\nexport function MetricsCardTitle({\n children,\n className,\n as: Tag = 'h3',\n}: {\n children: string;\n className?: string;\n as?: 'h2' | 'h3' | 'h4';\n}) {\n return <Tag className={cn('text-body font-medium text-foreground', className)}>{children}</Tag>;\n}\n","import { ChevronDownIcon } from 'lucide-react';\nimport { DropdownMenu } from '@/ds/components/DropdownMenu';\nimport type { DropdownMenuRadioOption } from '@/ds/components/DropdownMenu';\nimport { cn } from '@/lib/utils';\n\nexport type MetricsCardTitleMenuProps<V extends string> = {\n /** The card title for the picked view (e.g. \"Spend by model\"). */\n title: string;\n value: V;\n onValueChange: (value: V) => void;\n /** The views (e.g. \"By model\", \"By provider\"). */\n options: readonly DropdownMenuRadioOption<V>[];\n className?: string;\n};\n\n/**\n * A card title that switches the card's view, as on Vercel's usage cards: the title is the\n * trigger, with a chevron. The hover fill bleeds past the text so the title stays aligned.\n * Wrapped, so the top bar stretches the wrapper rather than the button, and the menu's open-state\n * nodes next to the trigger don't become the top bar's first child.\n */\nexport function MetricsCardTitleMenu<V extends string>({\n title,\n value,\n onValueChange,\n options,\n className,\n}: MetricsCardTitleMenuProps<V>) {\n return (\n <div className=\"flex min-w-0\">\n <DropdownMenu>\n <DropdownMenu.Trigger\n render={\n <button\n type=\"button\"\n className={cn(\n '-mx-1.5 -my-0.5 flex items-center gap-1 self-start rounded-md px-1.5 py-0.5 text-left text-body font-medium text-foreground hover:bg-fill-subtle',\n className,\n )}\n />\n }\n >\n {title}\n <ChevronDownIcon className=\"size-3.5 text-muted-foreground\" />\n </DropdownMenu.Trigger>\n <DropdownMenu.Content align=\"start\">\n <DropdownMenu.RadioOptions options={options} value={value} onValueChange={onValueChange} closeOnSelect />\n </DropdownMenu.Content>\n </DropdownMenu>\n </div>\n );\n}\n","import type { ReactNode } from 'react';\nimport { cn } from '@/lib/utils';\n\n/**\n * One row under the top bar for a card's controls: a legend, list column headers.\n * The first child sits on the left, the last on the right; either side may be omitted. When\n * both don't fit (a narrow card), the right side wraps to its own line, still on the right. At least 24px\n * tall, so toolbars line up across cards in the same row.\n */\nexport function MetricsCardToolbar({ children, className }: { children: ReactNode; className?: string }) {\n return (\n <div\n className={cn(\n 'flex min-h-6 min-w-0 flex-wrap items-center justify-between gap-x-4 gap-y-2 [&>*:first-child]:min-w-0 [&>*:last-child:not(:first-child)]:ml-auto',\n className,\n )}\n >\n {children}\n </div>\n );\n}\n","import type { ReactNode } from 'react';\nimport { cn } from '@/lib/utils';\n\nexport function MetricsCardTopBar({ children, className }: { children: ReactNode; className?: string }) {\n return (\n <div className={cn('flex items-start gap-4 [&>:first-child]:min-w-0 [&>:first-child]:flex-1', className)}>\n {children}\n </div>\n );\n}\n","import { SkeletonText } from '@/ds/components/Skeleton';\nimport { cn } from '@/lib/utils';\n\n/** One of several totals in a card that charts a few series, marked with its series' color. */\nexport type MetricsCardTotalPart = { label: string; value: string; color: string };\n\n/**\n * A card's total, right of the title in the same type (14px, medium). Always the top\n * bar's last item. A skeleton while loading, in the value's own line box. A card charting a\n * few series of different things (resources, threads) can give one total each, each after a\n * marker in its series' color, like the legend.\n */\nexport function MetricsCardTotal({\n value,\n isLoading = false,\n className,\n}: {\n value: string | readonly MetricsCardTotalPart[];\n isLoading?: boolean;\n className?: string;\n}) {\n return (\n <span className={cn('order-last shrink-0 text-body font-medium text-foreground tabular-nums', className)}>\n <TotalValue value={value} isLoading={isLoading} />\n </span>\n );\n}\n\nfunction TotalValue({ value, isLoading }: { value: string | readonly MetricsCardTotalPart[]; isLoading: boolean }) {\n if (isLoading) return <SkeletonText line=\"text-body\" className=\"h-4 w-14\" />;\n if (typeof value === 'string') return value;\n return (\n <span className=\"inline-flex items-center gap-3\">\n {value.map(part => (\n <span key={part.label} className=\"inline-flex items-center gap-1.5\">\n <span aria-hidden className=\"size-2 shrink-0 rounded-[2px]\" style={{ backgroundColor: part.color }} />\n {/* The marker is decorative; screen readers hear which total this is. */}\n <span className=\"sr-only\">{part.label} </span>\n {part.value}\n </span>\n ))}\n </span>\n );\n}\n","import { MetricsCardActions } from './metrics-card-actions';\nimport { MetricsCardContent } from './metrics-card-content';\nimport { MetricsCardError } from './metrics-card-error';\nimport { MetricsCardHeadline } from './metrics-card-headline';\nimport { MetricsCardLoading } from './metrics-card-loading';\nimport { MetricsCardNoData } from './metrics-card-no-data';\nimport { MetricsCardRoot } from './metrics-card-root';\nimport { MetricsCardTitle } from './metrics-card-title';\nimport { MetricsCardTitleMenu } from './metrics-card-title-menu';\nimport { MetricsCardToolbar } from './metrics-card-toolbar';\nimport { MetricsCardTopBar } from './metrics-card-top-bar';\nimport { MetricsCardTotal } from './metrics-card-total';\nimport { MetricsKpiCard } from '@/ds/components/MetricsKpiCard';\n\nexport const MetricsCard = Object.assign(MetricsCardRoot, {\n Root: MetricsCardRoot,\n Kpi: MetricsKpiCard,\n TopBar: MetricsCardTopBar,\n Toolbar: MetricsCardToolbar,\n Actions: MetricsCardActions,\n Title: MetricsCardTitle,\n TitleMenu: MetricsCardTitleMenu,\n Total: MetricsCardTotal,\n Headline: MetricsCardHeadline,\n Loading: MetricsCardLoading,\n Error: MetricsCardError,\n Content: MetricsCardContent,\n NoData: MetricsCardNoData,\n});\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"MetricsLineChart-BhHoduO0.js","names":[],"sources":["../src/ds/components/MetricsLineChart/metrics-line-chart-legend.tsx","../src/ds/components/MetricsLineChart/metrics-line-chart-tooltip.tsx","../src/ds/components/MetricsLineChart/metrics-line-chart.tsx"],"sourcesContent":["import type { MetricsLineChartSeries } from './metrics-line-chart';\nimport { Txt } from '@/ds/components/Txt';\nimport { cn } from '@/lib/utils';\n\n/** A marker and a label per series. Totals belong in the card's header, not here. */\nexport function MetricsLineChartLegend({\n series,\n className,\n}: {\n series: Pick<MetricsLineChartSeries, 'dataKey' | 'label' | 'color' | 'dashed'>[];\n className?: string;\n}) {\n return (\n <div className={cn('flex flex-wrap items-center gap-x-4 gap-y-1', className)}>\n {series.map(s => (\n <div key={s.dataKey} className=\"inline-flex items-center gap-1.5\">\n {s.dashed ? (\n <span className=\"h-0 w-3 shrink-0 border-t-2 border-dashed\" style={{ borderColor: s.color }} />\n ) : (\n <div className=\"size-2 shrink-0 rounded-[2px]\" style={{ backgroundColor: s.color }} />\n )}\n <Txt as=\"span\" variant=\"body-sm\" tone=\"muted\" className=\"max-w-48 truncate\">\n {s.label}\n </Txt>\n </div>\n ))}\n </div>\n );\n}\n","import { ChartTooltip } from '@/ds/components/ChartTooltip';\nimport { Txt } from '@/ds/components/Txt';\n\nexport function MetricsLineChartTooltip({\n active,\n payload,\n label,\n suffix,\n formatValue = value => value.toLocaleString('en-US'),\n showTotal = false,\n labelKey,\n formatByKey,\n}: {\n active?: boolean;\n payload?: Array<{\n name: string;\n value: number;\n color: string;\n dataKey?: string | number;\n payload?: Record<string, unknown>;\n }>;\n label?: string;\n suffix?: string;\n formatValue?: (value: number) => string;\n /** Adds a \"Total\" row under a hairline: the sum of every row, e.g. a stacked column's height. */\n showTotal?: boolean;\n /** Read the heading from this field of the hovered row instead of the x-axis label. */\n labelKey?: string;\n /** Per-series formatters, by data key, for charts that mix units (counts and durations). */\n formatByKey?: Record<string, (value: number) => string>;\n}) {\n if (!active || !payload?.length) return null;\n // A missing reading (NaN, Infinity) shows as a dash and stays out of the total.\n const finite = (value: unknown): value is number => typeof value === 'number' && Number.isFinite(value);\n const total = payload.reduce((sum, entry) => sum + (finite(entry.value) ? entry.value : 0), 0);\n // A bucket with no readings at all totals to a dash, not 0.\n const hasReading = payload.some(entry => finite(entry.value));\n const heading = labelKey ? String(payload[0]?.payload?.[labelKey] ?? label ?? '') : label;\n const format = (entry: { dataKey?: string | number }) => formatByKey?.[String(entry.dataKey)] ?? formatValue;\n // A number through its formatter; a missing or non-finite one as a dash; text as it is.\n const reading = (entry: (typeof payload)[number]) => {\n if (finite(entry.value)) return format(entry)(entry.value);\n if (typeof entry.value === 'number' || entry.value == null) return '—';\n return entry.value;\n };\n return (\n // A floor like shadcn's chart tooltip, so short readouts don't size it to their own label.\n <ChartTooltip className=\"min-w-32\">\n <Txt variant=\"column\" tone=\"ink\" className=\"mb-1\">\n {heading}\n </Txt>\n <div className=\"grid grid-cols-[auto_1fr_auto] items-center gap-x-2 gap-y-1\">\n {payload.map(entry => (\n <div key={entry.name} className=\"contents\">\n <span className=\"size-2 rounded-[2px]\" style={{ backgroundColor: entry.color }} />\n <Txt as=\"span\" variant=\"caption\" tone=\"muted\" className=\"max-w-40 truncate\">\n {entry.name}\n </Txt>\n <span className=\"text-right font-mono text-foreground tabular-nums\">\n {reading(entry)}\n {finite(entry.value) && suffix}\n </span>\n </div>\n ))}\n {showTotal && (\n <div className=\"col-span-3 grid grid-cols-subgrid items-center border-t border-border pt-1\">\n <Txt as=\"span\" variant=\"caption\" tone=\"muted\" className=\"col-span-2\">\n Total\n </Txt>\n <span className=\"text-right font-mono text-foreground tabular-nums\">\n {hasReading ? formatValue(total) : '—'}\n {hasReading && suffix}\n </span>\n </div>\n )}\n </div>\n </ChartTooltip>\n );\n}\n","import { Area, CartesianGrid, ComposedChart, Line, Tooltip } from 'recharts';\nimport { MetricsLineChartLegend } from './metrics-line-chart-legend';\nimport { MetricsLineChartTooltip } from './metrics-line-chart-tooltip';\nimport { ChartSkeleton } from '@/ds/components/ChartSkeleton';\nimport { CHART_CURSOR_PROPS, CHART_GRID_PROPS, CHART_TOOLTIP_PROPS, useChartSize } from '@/ds/primitives/chart-axes';\nimport type { ChartXLabels } from '@/ds/primitives/chart-axes';\nimport { Y_AXIS_WIDTH, compactNumber, dataMax, isWholeData, timeXAxis, valueYAxis } from '@/ds/primitives/chart-frame';\nimport type { ChartHeight } from '@/ds/primitives/chart-frame';\nimport { CHART_MARGIN } from '@/ds/primitives/chart-layout';\nimport { ChartPlot } from '@/ds/primitives/chart-plot';\nimport { ChartTooltipPosition } from '@/ds/primitives/chart-tooltip-position';\nimport { useChartDefsId } from '@/ds/primitives/use-chart-defs-id';\nimport { cn } from '@/lib/utils';\n\nexport type MetricsLineChartSeries = {\n dataKey: string;\n label: string;\n color: string;\n /** A secondary reading (e.g. an average next to counts): dashed, no fill under it. */\n dashed?: boolean;\n /** The series to read first (e.g. P95 among percentiles): a slightly heavier line. */\n emphasis?: boolean;\n};\n\n/** Line weight; an emphasised series adds half a pixel. */\nconst LINE_WIDTH = 1.75;\n/** Opacity at the top of the fill under a line (it fades to nothing at the base). */\nconst AREA_OPACITY = 0.12;\n\n/**\n * How the series draw.\n * - `default`: 1.75px lines over a fill.\n * - `thin`: 1.5px lines (emphasis included) over a fainter fill (40% of the default), for small\n * cards in a grid.\n */\nexport type MetricsLineChartLineStyle = 'default' | 'thin';\n\nconst LINE_STYLES: Record<MetricsLineChartLineStyle, { width: number; emphasis: number; areaOpacity: number }> = {\n default: { width: LINE_WIDTH, emphasis: LINE_WIDTH + 0.5, areaOpacity: AREA_OPACITY },\n thin: { width: 1.5, emphasis: 1.5, areaOpacity: AREA_OPACITY * 0.4 },\n};\n\n/**\n * Fills fade out sideways over their first and last 24px, so they don't end in a hard vertical\n * edge where the line starts and stops. A mask sized to each fill's own box.\n */\nconst THIN_FILL_FADE = '[&_.recharts-area-area]:mask-x-from-[calc(100%-24px)]';\n/** A lone point draws no line at all, so it gets a dot; lines already bridge gaps (`connectNulls`). */\nconst SPARSE_POINTS = 1;\n\n// Jumps straight to the hovered point, with the dots and the tooltip.\nfunction HoverLine({ points }: { points?: { x: number; y: number }[] }) {\n const [top, bottom] = points ?? [];\n if (!top || !bottom) return null;\n return (\n <line x1={top.x} x2={top.x} y1={top.y} y2={bottom.y} {...CHART_CURSOR_PROPS} className=\"pointer-events-none\" />\n );\n}\n\nexport type MetricsLineChartPointClickHandler = (point: Record<string, unknown>, seriesKey: string) => void;\n\nexport type MetricsLineChartProps = {\n data: Record<string, unknown>[];\n series: MetricsLineChartSeries[];\n /** Pixels, or `fill` to grow with the parent (a flex column, e.g. a card's content). */\n height?: ChartHeight;\n yDomain?: [number, number];\n /** Click on a series' active dot. */\n onPointClick?: MetricsLineChartPointClickHandler;\n /** Click anywhere on a bucket (e.g. to open its traces); the plot shows a pointer cursor. */\n onBucketClick?: (row: Record<string, unknown>, index: number) => void;\n /** Row field printed under the x-axis. */\n xKey?: string;\n /** Row field with the bucket's start time (ms), which places labels on round clock times. */\n timestampKey?: string;\n /** `auto` fits as many labels as the width allows; `edges` prints only the first and last. */\n xLabels?: ChartXLabels;\n /** Row field for the tooltip heading (e.g. a longer date); defaults to the x-axis label. */\n tooltipLabelKey?: string;\n /** Render a visible dot on every point. A series with a single point gets one anyway. */\n showDots?: boolean;\n /**\n * Point markers. `auto`: on a series' lone point (or every point with `showDots`). `hover`:\n * none at rest, only the hovered point's.\n */\n dots?: 'auto' | 'hover';\n /** Line weight and fill. `thin` suits small cards in a grid. */\n lineStyle?: MetricsLineChartLineStyle;\n /** Set to `false` to render `MetricsLineChartLegend` elsewhere, e.g. next to tabs. */\n showLegend?: boolean;\n /** Formats tooltip values (e.g. `ms`, `%`), and y-axis ticks unless `axisFormatter` is set. */\n valueFormatter?: (value: number) => string;\n /** Formats y-axis ticks, when they need a shorter form than the tooltip. */\n axisFormatter?: (value: number) => string;\n /**\n * Set to `false` to drop the y-axis labels and let the plot span the card. The gridlines stay\n * for relative scale; exact values come from the tooltip and the card's summary.\n */\n showYAxis?: boolean;\n /** Show a ghost of the chart while data loads, in the chart's own footprint. */\n isLoading?: boolean;\n className?: string;\n};\n\nexport function MetricsLineChart(props: MetricsLineChartProps) {\n const { data, series, height = 210, showLegend = true, isLoading = false, className } = props;\n const root = cn(height === 'fill' && 'flex min-h-0 flex-1 flex-col', className);\n // The legend stays while loading: it comes from the series, not the data.\n return (\n <div className={root}>\n {showLegend && <MetricsLineChartLegend series={series} className=\"mb-4\" />}\n {isLoading ? <ChartSkeleton kind=\"line\" height={height} /> : <LinePlot {...props} />}\n </div>\n );\n}\n\n/** The plot itself, once the data is in. */\nfunction LinePlot({\n data,\n series,\n height = 210,\n yDomain,\n onPointClick,\n onBucketClick,\n xKey = 'time',\n timestampKey = 'tsMs',\n xLabels = 'auto',\n tooltipLabelKey,\n showDots = false,\n dots = 'auto',\n lineStyle = 'default',\n valueFormatter,\n axisFormatter,\n showYAxis = true,\n}: MetricsLineChartProps) {\n const id = useChartDefsId();\n const { size, onResize, ref } = useChartSize();\n\n const format = valueFormatter ?? compactNumber.format;\n const isSparse = (key: string) => data.filter(row => typeof row[key] === 'number').length <= SPARSE_POINTS;\n const plotWidth = size.width - (showYAxis ? Y_AXIS_WIDTH : 0);\n const line = LINE_STYLES[lineStyle];\n const restDot = (s: MetricsLineChartSeries) =>\n dots === 'auto' && (showDots || isSparse(s.dataKey)) ? { r: 3, fill: s.color, strokeWidth: 0 } : false;\n const activeDot = (s: MetricsLineChartSeries) => ({\n r: 3.5,\n fill: s.color,\n stroke: 'var(--background)',\n strokeWidth: 2,\n ...(onPointClick && {\n style: { cursor: 'pointer' },\n onClick: (_: unknown, payload: unknown) => {\n // Recharts passes the dot's props; the hovered row is under `payload`.\n if (isRecord(payload) && isRecord(payload.payload)) onPointClick(payload.payload, s.dataKey);\n },\n }),\n });\n\n return (\n <ChartPlot\n height={height}\n plotRef={ref}\n onResize={onResize}\n clickable={!!onBucketClick}\n className={cn(lineStyle === 'thin' && THIN_FILL_FADE)}\n >\n <ComposedChart\n data={data}\n margin={CHART_MARGIN}\n onClick={state => {\n const i = state?.activeTooltipIndex;\n const row = i === undefined || i === null ? undefined : data[Number(i)];\n if (onBucketClick && row) onBucketClick(row, Number(i));\n }}\n >\n <defs>\n {series.map((s, i) => (\n <linearGradient key={s.dataKey} id={`${id}-area-${i}`} x1=\"0\" y1=\"0\" x2=\"0\" y2=\"1\">\n <stop offset=\"0%\" stopColor={s.color} stopOpacity={line.areaOpacity} />\n <stop offset=\"95%\" stopColor={s.color} stopOpacity={0} />\n </linearGradient>\n ))}\n </defs>\n <CartesianGrid {...CHART_GRID_PROPS} />\n {timeXAxis({ data, xKey, timestampKey, xLabels, plotWidth })}\n {valueYAxis({\n show: showYAxis,\n size,\n formatter: axisFormatter ?? format,\n domain: yDomain,\n max: dataMax(\n data,\n series.map(s => s.dataKey),\n ),\n integer: isWholeData(\n data,\n series.map(s => s.dataKey),\n ),\n })}\n <Tooltip\n {...CHART_TOOLTIP_PROPS}\n content={\n <ChartTooltipPosition>\n <MetricsLineChartTooltip formatValue={format} labelKey={tooltipLabelKey} />\n </ChartTooltipPosition>\n }\n cursor={<HoverLine />}\n />\n {series.map((s, i) => {\n const common = {\n type: 'monotone' as const,\n dataKey: s.dataKey,\n name: s.label,\n stroke: s.color,\n strokeWidth: s.emphasis ? line.emphasis : line.width,\n strokeDasharray: s.dashed ? '4 3' : undefined,\n dot: restDot(s),\n activeDot: activeDot(s),\n isAnimationActive: false,\n connectNulls: true,\n };\n return s.dashed ? (\n <Line key={s.dataKey} {...common} />\n ) : (\n <Area key={s.dataKey} {...common} fill={`url(#${id}-area-${i})`} fillOpacity={1} />\n );\n })}\n </ComposedChart>\n </ChartPlot>\n );\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null;\n}\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"MetricsLineChart-CjBm4IXu.cjs","names":["cn","Txt","ChartTooltip","Txt","CHART_CURSOR_PROPS","cn","ChartSkeleton","useChartDefsId","useChartSize","compactNumber","ChartPlot","ComposedChart","CHART_MARGIN","CartesianGrid","CHART_GRID_PROPS","timeXAxis","valueYAxis","dataMax","isWholeData","Tooltip","CHART_TOOLTIP_PROPS","ChartTooltipPosition","Line","Area"],"sources":["../src/ds/components/MetricsLineChart/metrics-line-chart-legend.tsx","../src/ds/components/MetricsLineChart/metrics-line-chart-tooltip.tsx","../src/ds/components/MetricsLineChart/metrics-line-chart.tsx"],"sourcesContent":["import type { MetricsLineChartSeries } from './metrics-line-chart';\nimport { Txt } from '@/ds/components/Txt';\nimport { cn } from '@/lib/utils';\n\n/** A marker and a label per series. Totals belong in the card's header, not here. */\nexport function MetricsLineChartLegend({\n series,\n className,\n}: {\n series: Pick<MetricsLineChartSeries, 'dataKey' | 'label' | 'color' | 'dashed'>[];\n className?: string;\n}) {\n return (\n <div className={cn('flex flex-wrap items-center gap-x-4 gap-y-1', className)}>\n {series.map(s => (\n <div key={s.dataKey} className=\"inline-flex items-center gap-1.5\">\n {s.dashed ? (\n <span className=\"h-0 w-3 shrink-0 border-t-2 border-dashed\" style={{ borderColor: s.color }} />\n ) : (\n <div className=\"size-2 shrink-0 rounded-[2px]\" style={{ backgroundColor: s.color }} />\n )}\n <Txt as=\"span\" variant=\"body-sm\" tone=\"muted\" className=\"max-w-48 truncate\">\n {s.label}\n </Txt>\n </div>\n ))}\n </div>\n );\n}\n","import { ChartTooltip } from '@/ds/components/ChartTooltip';\nimport { Txt } from '@/ds/components/Txt';\n\nexport function MetricsLineChartTooltip({\n active,\n payload,\n label,\n suffix,\n formatValue = value => value.toLocaleString('en-US'),\n showTotal = false,\n labelKey,\n formatByKey,\n}: {\n active?: boolean;\n payload?: Array<{\n name: string;\n value: number;\n color: string;\n dataKey?: string | number;\n payload?: Record<string, unknown>;\n }>;\n label?: string;\n suffix?: string;\n formatValue?: (value: number) => string;\n /** Adds a \"Total\" row under a hairline: the sum of every row, e.g. a stacked column's height. */\n showTotal?: boolean;\n /** Read the heading from this field of the hovered row instead of the x-axis label. */\n labelKey?: string;\n /** Per-series formatters, by data key, for charts that mix units (counts and durations). */\n formatByKey?: Record<string, (value: number) => string>;\n}) {\n if (!active || !payload?.length) return null;\n // A missing reading (NaN, Infinity) shows as a dash and stays out of the total.\n const finite = (value: unknown): value is number => typeof value === 'number' && Number.isFinite(value);\n const total = payload.reduce((sum, entry) => sum + (finite(entry.value) ? entry.value : 0), 0);\n // A bucket with no readings at all totals to a dash, not 0.\n const hasReading = payload.some(entry => finite(entry.value));\n const heading = labelKey ? String(payload[0]?.payload?.[labelKey] ?? label ?? '') : label;\n const format = (entry: { dataKey?: string | number }) => formatByKey?.[String(entry.dataKey)] ?? formatValue;\n // A number through its formatter; a missing or non-finite one as a dash; text as it is.\n const reading = (entry: (typeof payload)[number]) => {\n if (finite(entry.value)) return format(entry)(entry.value);\n if (typeof entry.value === 'number' || entry.value == null) return '—';\n return entry.value;\n };\n return (\n // A floor like shadcn's chart tooltip, so short readouts don't size it to their own label.\n <ChartTooltip className=\"min-w-32\">\n <Txt variant=\"column\" tone=\"ink\" className=\"mb-1\">\n {heading}\n </Txt>\n <div className=\"grid grid-cols-[auto_1fr_auto] items-center gap-x-2 gap-y-1\">\n {payload.map(entry => (\n <div key={entry.name} className=\"contents\">\n <span className=\"size-2 rounded-[2px]\" style={{ backgroundColor: entry.color }} />\n <Txt as=\"span\" variant=\"caption\" tone=\"muted\" className=\"max-w-40 truncate\">\n {entry.name}\n </Txt>\n <span className=\"text-right font-mono text-foreground tabular-nums\">\n {reading(entry)}\n {finite(entry.value) && suffix}\n </span>\n </div>\n ))}\n {showTotal && (\n <div className=\"col-span-3 grid grid-cols-subgrid items-center border-t border-border pt-1\">\n <Txt as=\"span\" variant=\"caption\" tone=\"muted\" className=\"col-span-2\">\n Total\n </Txt>\n <span className=\"text-right font-mono text-foreground tabular-nums\">\n {hasReading ? formatValue(total) : '—'}\n {hasReading && suffix}\n </span>\n </div>\n )}\n </div>\n </ChartTooltip>\n );\n}\n","import { Area, CartesianGrid, ComposedChart, Line, Tooltip } from 'recharts';\nimport { MetricsLineChartLegend } from './metrics-line-chart-legend';\nimport { MetricsLineChartTooltip } from './metrics-line-chart-tooltip';\nimport { ChartSkeleton } from '@/ds/components/ChartSkeleton';\nimport { CHART_CURSOR_PROPS, CHART_GRID_PROPS, CHART_TOOLTIP_PROPS, useChartSize } from '@/ds/primitives/chart-axes';\nimport type { ChartXLabels } from '@/ds/primitives/chart-axes';\nimport { Y_AXIS_WIDTH, compactNumber, dataMax, isWholeData, timeXAxis, valueYAxis } from '@/ds/primitives/chart-frame';\nimport type { ChartHeight } from '@/ds/primitives/chart-frame';\nimport { CHART_MARGIN } from '@/ds/primitives/chart-layout';\nimport { ChartPlot } from '@/ds/primitives/chart-plot';\nimport { ChartTooltipPosition } from '@/ds/primitives/chart-tooltip-position';\nimport { useChartDefsId } from '@/ds/primitives/use-chart-defs-id';\nimport { cn } from '@/lib/utils';\n\nexport type MetricsLineChartSeries = {\n dataKey: string;\n label: string;\n color: string;\n /** A secondary reading (e.g. an average next to counts): dashed, no fill under it. */\n dashed?: boolean;\n /** The series to read first (e.g. P95 among percentiles): a slightly heavier line. */\n emphasis?: boolean;\n};\n\n/** Line weight; an emphasised series adds half a pixel. */\nconst LINE_WIDTH = 1.75;\n/** Opacity at the top of the fill under a line (it fades to nothing at the base). */\nconst AREA_OPACITY = 0.12;\n\n/**\n * How the series draw.\n * - `default`: 1.75px lines over a fill.\n * - `thin`: 1.5px lines (emphasis included) over a fainter fill (40% of the default), for small\n * cards in a grid.\n */\nexport type MetricsLineChartLineStyle = 'default' | 'thin';\n\nconst LINE_STYLES: Record<MetricsLineChartLineStyle, { width: number; emphasis: number; areaOpacity: number }> = {\n default: { width: LINE_WIDTH, emphasis: LINE_WIDTH + 0.5, areaOpacity: AREA_OPACITY },\n thin: { width: 1.5, emphasis: 1.5, areaOpacity: AREA_OPACITY * 0.4 },\n};\n\n/**\n * Fills fade out sideways over their first and last 24px, so they don't end in a hard vertical\n * edge where the line starts and stops. A mask sized to each fill's own box.\n */\nconst THIN_FILL_FADE = '[&_.recharts-area-area]:mask-x-from-[calc(100%-24px)]';\n/** A lone point draws no line at all, so it gets a dot; lines already bridge gaps (`connectNulls`). */\nconst SPARSE_POINTS = 1;\n\n// Jumps straight to the hovered point, with the dots and the tooltip.\nfunction HoverLine({ points }: { points?: { x: number; y: number }[] }) {\n const [top, bottom] = points ?? [];\n if (!top || !bottom) return null;\n return (\n <line x1={top.x} x2={top.x} y1={top.y} y2={bottom.y} {...CHART_CURSOR_PROPS} className=\"pointer-events-none\" />\n );\n}\n\nexport type MetricsLineChartPointClickHandler = (point: Record<string, unknown>, seriesKey: string) => void;\n\nexport type MetricsLineChartProps = {\n data: Record<string, unknown>[];\n series: MetricsLineChartSeries[];\n /** Pixels, or `fill` to grow with the parent (a flex column, e.g. a card's content). */\n height?: ChartHeight;\n yDomain?: [number, number];\n /** Click on a series' active dot. */\n onPointClick?: MetricsLineChartPointClickHandler;\n /** Click anywhere on a bucket (e.g. to open its traces); the plot shows a pointer cursor. */\n onBucketClick?: (row: Record<string, unknown>, index: number) => void;\n /** Row field printed under the x-axis. */\n xKey?: string;\n /** Row field with the bucket's start time (ms), which places labels on round clock times. */\n timestampKey?: string;\n /** `auto` fits as many labels as the width allows; `edges` prints only the first and last. */\n xLabels?: ChartXLabels;\n /** Row field for the tooltip heading (e.g. a longer date); defaults to the x-axis label. */\n tooltipLabelKey?: string;\n /** Render a visible dot on every point. A series with a single point gets one anyway. */\n showDots?: boolean;\n /**\n * Point markers. `auto`: on a series' lone point (or every point with `showDots`). `hover`:\n * none at rest, only the hovered point's.\n */\n dots?: 'auto' | 'hover';\n /** Line weight and fill. `thin` suits small cards in a grid. */\n lineStyle?: MetricsLineChartLineStyle;\n /** Set to `false` to render `MetricsLineChartLegend` elsewhere, e.g. next to tabs. */\n showLegend?: boolean;\n /** Formats tooltip values (e.g. `ms`, `%`), and y-axis ticks unless `axisFormatter` is set. */\n valueFormatter?: (value: number) => string;\n /** Formats y-axis ticks, when they need a shorter form than the tooltip. */\n axisFormatter?: (value: number) => string;\n /**\n * Set to `false` to drop the y-axis labels and let the plot span the card. The gridlines stay\n * for relative scale; exact values come from the tooltip and the card's summary.\n */\n showYAxis?: boolean;\n /** Show a ghost of the chart while data loads, in the chart's own footprint. */\n isLoading?: boolean;\n className?: string;\n};\n\nexport function MetricsLineChart(props: MetricsLineChartProps) {\n const { data, series, height = 210, showLegend = true, isLoading = false, className } = props;\n const root = cn(height === 'fill' && 'flex min-h-0 flex-1 flex-col', className);\n // The legend stays while loading: it comes from the series, not the data.\n return (\n <div className={root}>\n {showLegend && <MetricsLineChartLegend series={series} className=\"mb-4\" />}\n {isLoading ? <ChartSkeleton kind=\"line\" height={height} /> : <LinePlot {...props} />}\n </div>\n );\n}\n\n/** The plot itself, once the data is in. */\nfunction LinePlot({\n data,\n series,\n height = 210,\n yDomain,\n onPointClick,\n onBucketClick,\n xKey = 'time',\n timestampKey = 'tsMs',\n xLabels = 'auto',\n tooltipLabelKey,\n showDots = false,\n dots = 'auto',\n lineStyle = 'default',\n valueFormatter,\n axisFormatter,\n showYAxis = true,\n}: MetricsLineChartProps) {\n const id = useChartDefsId();\n const { size, onResize, ref } = useChartSize();\n\n const format = valueFormatter ?? compactNumber.format;\n const isSparse = (key: string) => data.filter(row => typeof row[key] === 'number').length <= SPARSE_POINTS;\n const plotWidth = size.width - (showYAxis ? Y_AXIS_WIDTH : 0);\n const line = LINE_STYLES[lineStyle];\n const restDot = (s: MetricsLineChartSeries) =>\n dots === 'auto' && (showDots || isSparse(s.dataKey)) ? { r: 3, fill: s.color, strokeWidth: 0 } : false;\n const activeDot = (s: MetricsLineChartSeries) => ({\n r: 3.5,\n fill: s.color,\n stroke: 'var(--background)',\n strokeWidth: 2,\n ...(onPointClick && {\n style: { cursor: 'pointer' },\n onClick: (_: unknown, payload: unknown) => {\n // Recharts passes the dot's props; the hovered row is under `payload`.\n if (isRecord(payload) && isRecord(payload.payload)) onPointClick(payload.payload, s.dataKey);\n },\n }),\n });\n\n return (\n <ChartPlot\n height={height}\n plotRef={ref}\n onResize={onResize}\n clickable={!!onBucketClick}\n className={cn(lineStyle === 'thin' && THIN_FILL_FADE)}\n >\n <ComposedChart\n data={data}\n margin={CHART_MARGIN}\n onClick={state => {\n const i = state?.activeTooltipIndex;\n const row = i === undefined || i === null ? undefined : data[Number(i)];\n if (onBucketClick && row) onBucketClick(row, Number(i));\n }}\n >\n <defs>\n {series.map((s, i) => (\n <linearGradient key={s.dataKey} id={`${id}-area-${i}`} x1=\"0\" y1=\"0\" x2=\"0\" y2=\"1\">\n <stop offset=\"0%\" stopColor={s.color} stopOpacity={line.areaOpacity} />\n <stop offset=\"95%\" stopColor={s.color} stopOpacity={0} />\n </linearGradient>\n ))}\n </defs>\n <CartesianGrid {...CHART_GRID_PROPS} />\n {timeXAxis({ data, xKey, timestampKey, xLabels, plotWidth })}\n {valueYAxis({\n show: showYAxis,\n size,\n formatter: axisFormatter ?? format,\n domain: yDomain,\n max: dataMax(\n data,\n series.map(s => s.dataKey),\n ),\n integer: isWholeData(\n data,\n series.map(s => s.dataKey),\n ),\n })}\n <Tooltip\n {...CHART_TOOLTIP_PROPS}\n content={\n <ChartTooltipPosition>\n <MetricsLineChartTooltip formatValue={format} labelKey={tooltipLabelKey} />\n </ChartTooltipPosition>\n }\n cursor={<HoverLine />}\n />\n {series.map((s, i) => {\n const common = {\n type: 'monotone' as const,\n dataKey: s.dataKey,\n name: s.label,\n stroke: s.color,\n strokeWidth: s.emphasis ? line.emphasis : line.width,\n strokeDasharray: s.dashed ? '4 3' : undefined,\n dot: restDot(s),\n activeDot: activeDot(s),\n isAnimationActive: false,\n connectNulls: true,\n };\n return s.dashed ? (\n <Line key={s.dataKey} {...common} />\n ) : (\n <Area key={s.dataKey} {...common} fill={`url(#${id}-area-${i})`} fillOpacity={1} />\n );\n })}\n </ComposedChart>\n </ChartPlot>\n );\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null;\n}\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"date-time-range-picker-CTt7Od0P.cjs","names":["parse","isValid","useState","useId","startOfDay","format","_Fragment","cn","Txt","DatePicker","TimePicker","Field","FieldError","Button","Check","useState","Button","CalendarIcon","TriggerChevron","Popover","PopoverTrigger","PopoverContent","cn","DropdownMenu","XIcon","ButtonsGroup"],"sources":["../src/ds/components/DateTimeRangePicker/custom-range-editor.tsx","../src/ds/components/DateTimeRangePicker/date-time-range-picker.tsx"],"sourcesContent":["import { format, isValid, parse, startOfDay } from 'date-fns';\nimport { Check } from 'lucide-react';\nimport { useId, useState } from 'react';\nimport { Button } from '@/ds/components/Button/Button';\nimport { DatePicker, TimePicker } from '@/ds/components/DateTimePicker';\nimport { Field, FieldError } from '@/ds/components/Field';\nimport { Txt } from '@/ds/components/Txt';\nimport { cn } from '@/lib/utils';\n\nfunction formatDays(ms: number): string {\n const days = Math.round(ms / (24 * 60 * 60 * 1000));\n return `${days} day${days === 1 ? '' : 's'}`;\n}\n\n/** The draft day at the draft time, or the bare day when the time doesn't parse. */\nfunction combineDateAndTime(date: Date | undefined, timeStr: string): Date | undefined {\n if (!date) return undefined;\n const dateOnly = new Date(date.getFullYear(), date.getMonth(), date.getDate());\n const combined = parse(timeStr, 'h:mm a', dateOnly);\n return isValid(combined) ? combined : date;\n}\n\nexport interface CustomRangeEditorProps {\n dateFrom?: Date;\n dateTo?: Date;\n disabled?: boolean;\n /** Widest range that can be applied, in milliseconds. */\n maxRangeMs?: number;\n /** Earliest day a range can start on. Days before it are disabled. */\n minDate?: Date;\n onApply: (from: Date | undefined, to: Date | undefined) => void;\n onShowPresets: () => void;\n}\n\n/**\n * The custom range popover body. It mounts each time the popover opens, so its drafts\n * start from the current range without the picker resetting them.\n */\nexport function CustomRangeEditor({\n dateFrom,\n dateTo,\n disabled,\n maxRangeMs,\n minDate,\n onApply,\n onShowPresets,\n}: CustomRangeEditorProps) {\n const [draftDateFrom, setDraftDateFrom] = useState<Date | undefined>(dateFrom);\n const [draftDateTo, setDraftDateTo] = useState<Date | undefined>(dateTo);\n const [draftTimeFrom, setDraftTimeFrom] = useState('12:00 AM');\n const [draftTimeTo, setDraftTimeTo] = useState('11:59 PM');\n const [applyAttempted, setApplyAttempted] = useState(false);\n const errorId = useId();\n\n const fromDate = combineDateAndTime(draftDateFrom, draftTimeFrom);\n const toDate = combineDateAndTime(draftDateTo, draftTimeTo);\n const rangeReversed = fromDate !== undefined && toDate !== undefined && fromDate.getTime() > toDate.getTime();\n const rangeTooWide =\n fromDate !== undefined && toDate !== undefined && toDate.getTime() - fromDate.getTime() > (maxRangeMs ?? Infinity);\n const firstDay = minDate ? startOfDay(minDate) : undefined;\n const endFirstDay = draftDateFrom && (!firstDay || draftDateFrom > firstDay) ? draftDateFrom : firstDay;\n const rangeTooEarly = fromDate !== undefined && firstDay !== undefined && fromDate < firstDay;\n const rangeInvalid = rangeReversed || rangeTooWide || rangeTooEarly;\n // Shown after a failed Apply, and gone as soon as the drafts form a valid range again.\n let error: string | undefined;\n if (applyAttempted && rangeReversed) error = 'Start date/time must be before end date/time';\n else if (applyAttempted && rangeTooWide) error = `Range cannot be longer than ${formatDays(maxRangeMs ?? 0)}`;\n else if (applyAttempted && firstDay && rangeTooEarly)\n error = `Range cannot start before ${format(firstDay, 'MMM d, yyyy')}`;\n\n const apply = () => {\n if (rangeInvalid) {\n setApplyAttempted(true);\n return;\n }\n onApply(fromDate, toDate);\n };\n\n return (\n <>\n <div\n role=\"group\"\n aria-label=\"Custom date range\"\n aria-invalid={error ? true : undefined}\n aria-describedby={error ? errorId : undefined}\n className=\"flex\"\n >\n <div className={cn('border-r border-border')}>\n <Txt as=\"span\" variant=\"column\" tone=\"muted\" className=\"block px-4 pt-3\">\n Start\n </Txt>\n <DatePicker\n mode=\"single\"\n selected={draftDateFrom}\n month={draftDateFrom}\n onSelect={setDraftDateFrom}\n disabled={disabled}\n fromDate={firstDay}\n toDate={draftDateTo}\n />\n <TimePicker\n label=\"Start time\"\n className=\"mx-4 mb-3 w-auto\"\n defaultValue={draftTimeFrom}\n onValueChange={v => {\n if (!disabled) setDraftTimeFrom(v);\n }}\n />\n </div>\n <div>\n <Txt as=\"span\" variant=\"column\" tone=\"muted\" className=\"block px-4 pt-3\">\n End\n </Txt>\n <DatePicker\n mode=\"single\"\n selected={draftDateTo}\n month={draftDateTo}\n onSelect={setDraftDateTo}\n disabled={disabled}\n fromDate={endFirstDay}\n />\n <TimePicker\n label=\"End time\"\n className=\"mx-4 mb-3 w-auto\"\n defaultValue={draftTimeTo}\n onValueChange={v => {\n if (!disabled) setDraftTimeTo(v);\n }}\n />\n </div>\n </div>\n <Field invalid={Boolean(error)}>\n <FieldError id={errorId} className=\"px-4 pb-1\">\n {error}\n </FieldError>\n </Field>\n <div className={cn('flex items-center justify-between px-4 pb-3')}>\n <Button variant=\"ghost\" size=\"sm\" disabled={disabled} onClick={onShowPresets}>\n ← Presets\n </Button>\n <Button icon={<Check />} variant=\"primary\" size=\"sm\" onClick={apply} disabled={disabled}>\n Apply\n </Button>\n </div>\n </>\n );\n}\n","import { CalendarIcon, XIcon } from 'lucide-react';\nimport { useState } from 'react';\nimport type { ReactNode, Ref } from 'react';\nimport { CustomRangeEditor } from './custom-range-editor';\nimport { Button, TriggerChevron } from '@/ds/components/Button/Button';\nimport type { TextButtonSize } from '@/ds/components/Button/Button';\nimport { ButtonsGroup } from '@/ds/components/ButtonsGroup/buttons-group';\nimport { DropdownMenu } from '@/ds/components/DropdownMenu/dropdown-menu';\nimport { Popover, PopoverTrigger, PopoverContent } from '@/ds/components/Popover/popover';\nimport { cn } from '@/lib/utils';\n\nexport type DateRangePreset = 'all' | 'last-24h' | 'last-3d' | 'last-7d' | 'last-14d' | 'last-30d' | 'custom';\n\nconst DATE_PRESETS: { value: DateRangePreset; label: string; ms?: number }[] = [\n { value: 'all', label: 'All' },\n { value: 'last-24h', label: 'Last 24 hours', ms: 24 * 60 * 60 * 1000 },\n { value: 'last-3d', label: 'Last 3 days', ms: 3 * 24 * 60 * 60 * 1000 },\n { value: 'last-7d', label: 'Last 7 days', ms: 7 * 24 * 60 * 60 * 1000 },\n { value: 'last-14d', label: 'Last 14 days', ms: 14 * 24 * 60 * 60 * 1000 },\n { value: 'last-30d', label: 'Last 30 days', ms: 30 * 24 * 60 * 60 * 1000 },\n { value: 'custom', label: 'Custom range...' },\n];\n\nconst fmtClock = (d: Date) =>\n d.toLocaleTimeString('en-US', d.getMinutes() === 0 ? { hour: 'numeric' } : { hour: 'numeric', minute: '2-digit' });\n\n/**\n * A custom range as day and time, kept short: `Oct 21, 2:05 PM – 4 PM` within one day,\n * `Oct 20, 12 AM – Oct 27, 9:30 AM` across days, with years only outside the current one.\n */\nfunction customRangeLabel(dateFrom: Date | undefined, dateTo: Date | undefined) {\n const thisYear = new Date().getFullYear();\n const withYear = [dateFrom, dateTo].some(d => d && d.getFullYear() !== thisYear);\n const fmtDay = (d: Date) =>\n d.toLocaleDateString('en-US', { month: 'short', day: 'numeric', ...(withYear && { year: 'numeric' }) });\n const fmtMoment = (d: Date) => `${fmtDay(d)}, ${fmtClock(d)}`;\n\n const start = dateFrom ? fmtMoment(dateFrom) : 'Start';\n if (!dateTo) return `${start} \\u2013 End`;\n const sameDay = dateFrom !== undefined && fmtDay(dateFrom) === fmtDay(dateTo);\n return `${start} \\u2013 ${sameDay ? fmtClock(dateTo) : fmtMoment(dateTo)}`;\n}\n\nexport interface DateTimeRangePickerProps {\n preset?: DateRangePreset;\n onPresetChange?: (preset: DateRangePreset) => void;\n dateFrom?: Date;\n dateTo?: Date;\n onDateChange?: (value: Date | undefined, type: 'from' | 'to') => void;\n /**\n * Called once with both ends when a custom range is applied. When provided it replaces\n * the two `onDateChange` calls for that step, so consumers can write the range atomically.\n */\n onDateRangeChange?: (from: Date | undefined, to: Date | undefined) => void;\n disabled?: boolean;\n /** Widest custom range that can be applied, in milliseconds. Presets are unaffected. */\n maxRangeMs?: number;\n /** Earliest day a custom range can start on, e.g. the oldest data still kept. Earlier days are disabled. */\n minDate?: Date;\n /** Subset of presets to show. If omitted, all presets are shown. */\n presets?: readonly DateRangePreset[];\n /** Size of the trigger Button. Defaults to 'sm', the toolbar rung. */\n size?: TextButtonSize;\n /**\n * The preset a custom range resets to. While a custom range is applied, a × joined to the\n * trigger takes the chevron's place and switches back to it.\n */\n defaultPreset?: DateRangePreset;\n /** Replaces what the reset × does, e.g. to undo a histogram zoom before falling back to `defaultPreset`. */\n onReset?: () => void;\n /** The trigger Button. */\n ref?: Ref<HTMLButtonElement>;\n}\n\nexport function DateTimeRangePicker({\n preset = 'all',\n onPresetChange,\n dateFrom,\n dateTo,\n onDateChange,\n onDateRangeChange,\n disabled,\n maxRangeMs,\n minDate,\n presets,\n size = 'sm',\n defaultPreset,\n onReset,\n ref,\n}: DateTimeRangePickerProps) {\n const visiblePresets = presets ? DATE_PRESETS.filter(p => presets.includes(p.value)) : DATE_PRESETS;\n const [customRangeOpen, setCustomRangeOpen] = useState(false);\n const [presetMenuOpen, setPresetMenuOpen] = useState(false);\n // The preset active before \"Custom range...\" was picked, until a range is applied.\n // \"← Presets\" restores it instead of jumping to the first preset in the list.\n const [presetBeforeCustom, setPresetBeforeCustom] = useState<DateRangePreset | undefined>();\n\n // \"← Presets\" restores the previous preset through the parent, which may apply it a\n // render later (e.g. a router URL update). Show it right away instead of \"custom\".\n const restoringPreset = presetMenuOpen && preset === 'custom' ? presetBeforeCustom : undefined;\n const shownPreset = restoringPreset ?? preset;\n const datePresetLabel = DATE_PRESETS.find(p => p.value === shownPreset)?.label ?? 'All';\n\n const applyPreset = (value: DateRangePreset) => {\n onPresetChange?.(value);\n const entry = DATE_PRESETS.find(p => p.value === value);\n if (entry?.ms) {\n onDateChange?.(new Date(Date.now() - entry.ms), 'from');\n onDateChange?.(undefined, 'to');\n } else {\n onDateChange?.(undefined, 'from');\n onDateChange?.(undefined, 'to');\n }\n };\n\n const handlePresetSelect = (value: DateRangePreset) => {\n setPresetMenuOpen(false);\n if (value === 'custom') {\n if (preset !== 'custom') setPresetBeforeCustom(preset);\n onPresetChange?.(value);\n setCustomRangeOpen(true);\n return;\n }\n setPresetBeforeCustom(undefined);\n applyPreset(value);\n };\n\n const applyCustomRange = (fromDate: Date | undefined, toDate: Date | undefined) => {\n if (onDateRangeChange) {\n onDateRangeChange(fromDate, toDate);\n } else {\n onDateChange?.(fromDate, 'from');\n onDateChange?.(toDate, 'to');\n }\n setPresetBeforeCustom(undefined);\n setCustomRangeOpen(false);\n };\n\n // Back to the preset list. Restores the preset the user came from; an applied custom\n // range matches no preset, so it stays and the list shows \"Custom range...\" as current.\n const showPresets = () => {\n setCustomRangeOpen(false);\n if (presetBeforeCustom) applyPreset(presetBeforeCustom);\n setPresetMenuOpen(true);\n };\n\n const canReset = defaultPreset !== undefined && shownPreset === 'custom';\n const editingCustomRange = shownPreset === 'custom' && !presetMenuOpen;\n const label = shownPreset === 'custom' ? customRangeLabel(dateFrom, dateTo) : datePresetLabel;\n const trigger = (\n <Button\n ref={ref}\n size={size}\n disabled={disabled}\n icon={<CalendarIcon />}\n iconEnd={canReset ? undefined : <TriggerChevron />}\n >\n {label}\n </Button>\n );\n\n return (\n <JoinReset\n size={size}\n reset={\n canReset && (\n <ResetRangeButton size={size} disabled={disabled} onReset={onReset ?? (() => applyPreset(defaultPreset))} />\n )\n }\n >\n {editingCustomRange && (\n <Popover open={customRangeOpen} onOpenChange={setCustomRangeOpen}>\n <PopoverTrigger render={trigger} />\n <PopoverContent align=\"start\" className={cn('w-auto p-0')}>\n <CustomRangeEditor\n dateFrom={dateFrom}\n dateTo={dateTo}\n disabled={disabled}\n maxRangeMs={maxRangeMs}\n minDate={minDate}\n onApply={applyCustomRange}\n onShowPresets={showPresets}\n />\n </PopoverContent>\n </Popover>\n )}\n {!editingCustomRange && (\n <DropdownMenu open={presetMenuOpen} onOpenChange={setPresetMenuOpen}>\n <DropdownMenu.Trigger render={trigger} />\n <DropdownMenu.Content align=\"start\">\n {/* Items select on click (not onValueChange) so re-picking the checked\n \"Custom range...\" still reopens the range editor. */}\n <DropdownMenu.RadioGroup value={shownPreset}>\n {visiblePresets.map(p => (\n <DropdownMenu.RadioItem\n key={p.value}\n value={p.value}\n closeOnClick\n onClick={() => handlePresetSelect(p.value)}\n >\n {p.label}\n </DropdownMenu.RadioItem>\n ))}\n </DropdownMenu.RadioGroup>\n </DropdownMenu.Content>\n </DropdownMenu>\n )}\n </JoinReset>\n );\n}\n\n/** Switches a custom range back to the picker's default preset. */\nfunction ResetRangeButton({\n size,\n disabled,\n onReset,\n}: {\n size: TextButtonSize;\n disabled?: boolean;\n onReset: () => void;\n}) {\n return (\n <Button\n type=\"button\"\n size={`icon-${size}` as const}\n tooltip=\"Reset time range\"\n disabled={disabled}\n onClick={onReset}\n >\n <XIcon />\n </Button>\n );\n}\n\n/** The trigger alone, or joined with the reset × as one pill. */\nfunction JoinReset({ reset, size, children }: { reset: ReactNode; size: TextButtonSize; children: ReactNode }) {\n if (!reset) return children;\n return (\n <ButtonsGroup size={size}>\n {children}\n {reset}\n </ButtonsGroup>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"date-time-range-picker-DKh1K9Iy.js","names":["_Fragment"],"sources":["../src/ds/components/DateTimeRangePicker/custom-range-editor.tsx","../src/ds/components/DateTimeRangePicker/date-time-range-picker.tsx"],"sourcesContent":["import { format, isValid, parse, startOfDay } from 'date-fns';\nimport { Check } from 'lucide-react';\nimport { useId, useState } from 'react';\nimport { Button } from '@/ds/components/Button/Button';\nimport { DatePicker, TimePicker } from '@/ds/components/DateTimePicker';\nimport { Field, FieldError } from '@/ds/components/Field';\nimport { Txt } from '@/ds/components/Txt';\nimport { cn } from '@/lib/utils';\n\nfunction formatDays(ms: number): string {\n const days = Math.round(ms / (24 * 60 * 60 * 1000));\n return `${days} day${days === 1 ? '' : 's'}`;\n}\n\n/** The draft day at the draft time, or the bare day when the time doesn't parse. */\nfunction combineDateAndTime(date: Date | undefined, timeStr: string): Date | undefined {\n if (!date) return undefined;\n const dateOnly = new Date(date.getFullYear(), date.getMonth(), date.getDate());\n const combined = parse(timeStr, 'h:mm a', dateOnly);\n return isValid(combined) ? combined : date;\n}\n\nexport interface CustomRangeEditorProps {\n dateFrom?: Date;\n dateTo?: Date;\n disabled?: boolean;\n /** Widest range that can be applied, in milliseconds. */\n maxRangeMs?: number;\n /** Earliest day a range can start on. Days before it are disabled. */\n minDate?: Date;\n onApply: (from: Date | undefined, to: Date | undefined) => void;\n onShowPresets: () => void;\n}\n\n/**\n * The custom range popover body. It mounts each time the popover opens, so its drafts\n * start from the current range without the picker resetting them.\n */\nexport function CustomRangeEditor({\n dateFrom,\n dateTo,\n disabled,\n maxRangeMs,\n minDate,\n onApply,\n onShowPresets,\n}: CustomRangeEditorProps) {\n const [draftDateFrom, setDraftDateFrom] = useState<Date | undefined>(dateFrom);\n const [draftDateTo, setDraftDateTo] = useState<Date | undefined>(dateTo);\n const [draftTimeFrom, setDraftTimeFrom] = useState('12:00 AM');\n const [draftTimeTo, setDraftTimeTo] = useState('11:59 PM');\n const [applyAttempted, setApplyAttempted] = useState(false);\n const errorId = useId();\n\n const fromDate = combineDateAndTime(draftDateFrom, draftTimeFrom);\n const toDate = combineDateAndTime(draftDateTo, draftTimeTo);\n const rangeReversed = fromDate !== undefined && toDate !== undefined && fromDate.getTime() > toDate.getTime();\n const rangeTooWide =\n fromDate !== undefined && toDate !== undefined && toDate.getTime() - fromDate.getTime() > (maxRangeMs ?? Infinity);\n const firstDay = minDate ? startOfDay(minDate) : undefined;\n const endFirstDay = draftDateFrom && (!firstDay || draftDateFrom > firstDay) ? draftDateFrom : firstDay;\n const rangeTooEarly = fromDate !== undefined && firstDay !== undefined && fromDate < firstDay;\n const rangeInvalid = rangeReversed || rangeTooWide || rangeTooEarly;\n // Shown after a failed Apply, and gone as soon as the drafts form a valid range again.\n let error: string | undefined;\n if (applyAttempted && rangeReversed) error = 'Start date/time must be before end date/time';\n else if (applyAttempted && rangeTooWide) error = `Range cannot be longer than ${formatDays(maxRangeMs ?? 0)}`;\n else if (applyAttempted && firstDay && rangeTooEarly)\n error = `Range cannot start before ${format(firstDay, 'MMM d, yyyy')}`;\n\n const apply = () => {\n if (rangeInvalid) {\n setApplyAttempted(true);\n return;\n }\n onApply(fromDate, toDate);\n };\n\n return (\n <>\n <div\n role=\"group\"\n aria-label=\"Custom date range\"\n aria-invalid={error ? true : undefined}\n aria-describedby={error ? errorId : undefined}\n className=\"flex\"\n >\n <div className={cn('border-r border-border')}>\n <Txt as=\"span\" variant=\"column\" tone=\"muted\" className=\"block px-4 pt-3\">\n Start\n </Txt>\n <DatePicker\n mode=\"single\"\n selected={draftDateFrom}\n month={draftDateFrom}\n onSelect={setDraftDateFrom}\n disabled={disabled}\n fromDate={firstDay}\n toDate={draftDateTo}\n />\n <TimePicker\n label=\"Start time\"\n className=\"mx-4 mb-3 w-auto\"\n defaultValue={draftTimeFrom}\n onValueChange={v => {\n if (!disabled) setDraftTimeFrom(v);\n }}\n />\n </div>\n <div>\n <Txt as=\"span\" variant=\"column\" tone=\"muted\" className=\"block px-4 pt-3\">\n End\n </Txt>\n <DatePicker\n mode=\"single\"\n selected={draftDateTo}\n month={draftDateTo}\n onSelect={setDraftDateTo}\n disabled={disabled}\n fromDate={endFirstDay}\n />\n <TimePicker\n label=\"End time\"\n className=\"mx-4 mb-3 w-auto\"\n defaultValue={draftTimeTo}\n onValueChange={v => {\n if (!disabled) setDraftTimeTo(v);\n }}\n />\n </div>\n </div>\n <Field invalid={Boolean(error)}>\n <FieldError id={errorId} className=\"px-4 pb-1\">\n {error}\n </FieldError>\n </Field>\n <div className={cn('flex items-center justify-between px-4 pb-3')}>\n <Button variant=\"ghost\" size=\"sm\" disabled={disabled} onClick={onShowPresets}>\n ← Presets\n </Button>\n <Button icon={<Check />} variant=\"primary\" size=\"sm\" onClick={apply} disabled={disabled}>\n Apply\n </Button>\n </div>\n </>\n );\n}\n","import { CalendarIcon, XIcon } from 'lucide-react';\nimport { useState } from 'react';\nimport type { ReactNode, Ref } from 'react';\nimport { CustomRangeEditor } from './custom-range-editor';\nimport { Button, TriggerChevron } from '@/ds/components/Button/Button';\nimport type { TextButtonSize } from '@/ds/components/Button/Button';\nimport { ButtonsGroup } from '@/ds/components/ButtonsGroup/buttons-group';\nimport { DropdownMenu } from '@/ds/components/DropdownMenu/dropdown-menu';\nimport { Popover, PopoverTrigger, PopoverContent } from '@/ds/components/Popover/popover';\nimport { cn } from '@/lib/utils';\n\nexport type DateRangePreset = 'all' | 'last-24h' | 'last-3d' | 'last-7d' | 'last-14d' | 'last-30d' | 'custom';\n\nconst DATE_PRESETS: { value: DateRangePreset; label: string; ms?: number }[] = [\n { value: 'all', label: 'All' },\n { value: 'last-24h', label: 'Last 24 hours', ms: 24 * 60 * 60 * 1000 },\n { value: 'last-3d', label: 'Last 3 days', ms: 3 * 24 * 60 * 60 * 1000 },\n { value: 'last-7d', label: 'Last 7 days', ms: 7 * 24 * 60 * 60 * 1000 },\n { value: 'last-14d', label: 'Last 14 days', ms: 14 * 24 * 60 * 60 * 1000 },\n { value: 'last-30d', label: 'Last 30 days', ms: 30 * 24 * 60 * 60 * 1000 },\n { value: 'custom', label: 'Custom range...' },\n];\n\nconst fmtClock = (d: Date) =>\n d.toLocaleTimeString('en-US', d.getMinutes() === 0 ? { hour: 'numeric' } : { hour: 'numeric', minute: '2-digit' });\n\n/**\n * A custom range as day and time, kept short: `Oct 21, 2:05 PM – 4 PM` within one day,\n * `Oct 20, 12 AM – Oct 27, 9:30 AM` across days, with years only outside the current one.\n */\nfunction customRangeLabel(dateFrom: Date | undefined, dateTo: Date | undefined) {\n const thisYear = new Date().getFullYear();\n const withYear = [dateFrom, dateTo].some(d => d && d.getFullYear() !== thisYear);\n const fmtDay = (d: Date) =>\n d.toLocaleDateString('en-US', { month: 'short', day: 'numeric', ...(withYear && { year: 'numeric' }) });\n const fmtMoment = (d: Date) => `${fmtDay(d)}, ${fmtClock(d)}`;\n\n const start = dateFrom ? fmtMoment(dateFrom) : 'Start';\n if (!dateTo) return `${start} \\u2013 End`;\n const sameDay = dateFrom !== undefined && fmtDay(dateFrom) === fmtDay(dateTo);\n return `${start} \\u2013 ${sameDay ? fmtClock(dateTo) : fmtMoment(dateTo)}`;\n}\n\nexport interface DateTimeRangePickerProps {\n preset?: DateRangePreset;\n onPresetChange?: (preset: DateRangePreset) => void;\n dateFrom?: Date;\n dateTo?: Date;\n onDateChange?: (value: Date | undefined, type: 'from' | 'to') => void;\n /**\n * Called once with both ends when a custom range is applied. When provided it replaces\n * the two `onDateChange` calls for that step, so consumers can write the range atomically.\n */\n onDateRangeChange?: (from: Date | undefined, to: Date | undefined) => void;\n disabled?: boolean;\n /** Widest custom range that can be applied, in milliseconds. Presets are unaffected. */\n maxRangeMs?: number;\n /** Earliest day a custom range can start on, e.g. the oldest data still kept. Earlier days are disabled. */\n minDate?: Date;\n /** Subset of presets to show. If omitted, all presets are shown. */\n presets?: readonly DateRangePreset[];\n /** Size of the trigger Button. Defaults to 'sm', the toolbar rung. */\n size?: TextButtonSize;\n /**\n * The preset a custom range resets to. While a custom range is applied, a × joined to the\n * trigger takes the chevron's place and switches back to it.\n */\n defaultPreset?: DateRangePreset;\n /** Replaces what the reset × does, e.g. to undo a histogram zoom before falling back to `defaultPreset`. */\n onReset?: () => void;\n /** The trigger Button. */\n ref?: Ref<HTMLButtonElement>;\n}\n\nexport function DateTimeRangePicker({\n preset = 'all',\n onPresetChange,\n dateFrom,\n dateTo,\n onDateChange,\n onDateRangeChange,\n disabled,\n maxRangeMs,\n minDate,\n presets,\n size = 'sm',\n defaultPreset,\n onReset,\n ref,\n}: DateTimeRangePickerProps) {\n const visiblePresets = presets ? DATE_PRESETS.filter(p => presets.includes(p.value)) : DATE_PRESETS;\n const [customRangeOpen, setCustomRangeOpen] = useState(false);\n const [presetMenuOpen, setPresetMenuOpen] = useState(false);\n // The preset active before \"Custom range...\" was picked, until a range is applied.\n // \"← Presets\" restores it instead of jumping to the first preset in the list.\n const [presetBeforeCustom, setPresetBeforeCustom] = useState<DateRangePreset | undefined>();\n\n // \"← Presets\" restores the previous preset through the parent, which may apply it a\n // render later (e.g. a router URL update). Show it right away instead of \"custom\".\n const restoringPreset = presetMenuOpen && preset === 'custom' ? presetBeforeCustom : undefined;\n const shownPreset = restoringPreset ?? preset;\n const datePresetLabel = DATE_PRESETS.find(p => p.value === shownPreset)?.label ?? 'All';\n\n const applyPreset = (value: DateRangePreset) => {\n onPresetChange?.(value);\n const entry = DATE_PRESETS.find(p => p.value === value);\n if (entry?.ms) {\n onDateChange?.(new Date(Date.now() - entry.ms), 'from');\n onDateChange?.(undefined, 'to');\n } else {\n onDateChange?.(undefined, 'from');\n onDateChange?.(undefined, 'to');\n }\n };\n\n const handlePresetSelect = (value: DateRangePreset) => {\n setPresetMenuOpen(false);\n if (value === 'custom') {\n if (preset !== 'custom') setPresetBeforeCustom(preset);\n onPresetChange?.(value);\n setCustomRangeOpen(true);\n return;\n }\n setPresetBeforeCustom(undefined);\n applyPreset(value);\n };\n\n const applyCustomRange = (fromDate: Date | undefined, toDate: Date | undefined) => {\n if (onDateRangeChange) {\n onDateRangeChange(fromDate, toDate);\n } else {\n onDateChange?.(fromDate, 'from');\n onDateChange?.(toDate, 'to');\n }\n setPresetBeforeCustom(undefined);\n setCustomRangeOpen(false);\n };\n\n // Back to the preset list. Restores the preset the user came from; an applied custom\n // range matches no preset, so it stays and the list shows \"Custom range...\" as current.\n const showPresets = () => {\n setCustomRangeOpen(false);\n if (presetBeforeCustom) applyPreset(presetBeforeCustom);\n setPresetMenuOpen(true);\n };\n\n const canReset = defaultPreset !== undefined && shownPreset === 'custom';\n const editingCustomRange = shownPreset === 'custom' && !presetMenuOpen;\n const label = shownPreset === 'custom' ? customRangeLabel(dateFrom, dateTo) : datePresetLabel;\n const trigger = (\n <Button\n ref={ref}\n size={size}\n disabled={disabled}\n icon={<CalendarIcon />}\n iconEnd={canReset ? undefined : <TriggerChevron />}\n >\n {label}\n </Button>\n );\n\n return (\n <JoinReset\n size={size}\n reset={\n canReset && (\n <ResetRangeButton size={size} disabled={disabled} onReset={onReset ?? (() => applyPreset(defaultPreset))} />\n )\n }\n >\n {editingCustomRange && (\n <Popover open={customRangeOpen} onOpenChange={setCustomRangeOpen}>\n <PopoverTrigger render={trigger} />\n <PopoverContent align=\"start\" className={cn('w-auto p-0')}>\n <CustomRangeEditor\n dateFrom={dateFrom}\n dateTo={dateTo}\n disabled={disabled}\n maxRangeMs={maxRangeMs}\n minDate={minDate}\n onApply={applyCustomRange}\n onShowPresets={showPresets}\n />\n </PopoverContent>\n </Popover>\n )}\n {!editingCustomRange && (\n <DropdownMenu open={presetMenuOpen} onOpenChange={setPresetMenuOpen}>\n <DropdownMenu.Trigger render={trigger} />\n <DropdownMenu.Content align=\"start\">\n {/* Items select on click (not onValueChange) so re-picking the checked\n \"Custom range...\" still reopens the range editor. */}\n <DropdownMenu.RadioGroup value={shownPreset}>\n {visiblePresets.map(p => (\n <DropdownMenu.RadioItem\n key={p.value}\n value={p.value}\n closeOnClick\n onClick={() => handlePresetSelect(p.value)}\n >\n {p.label}\n </DropdownMenu.RadioItem>\n ))}\n </DropdownMenu.RadioGroup>\n </DropdownMenu.Content>\n </DropdownMenu>\n )}\n </JoinReset>\n );\n}\n\n/** Switches a custom range back to the picker's default preset. */\nfunction ResetRangeButton({\n size,\n disabled,\n onReset,\n}: {\n size: TextButtonSize;\n disabled?: boolean;\n onReset: () => void;\n}) {\n return (\n <Button\n type=\"button\"\n size={`icon-${size}` as const}\n tooltip=\"Reset time range\"\n disabled={disabled}\n onClick={onReset}\n >\n <XIcon />\n </Button>\n );\n}\n\n/** The trigger alone, or joined with the reset × as one pill. */\nfunction JoinReset({ reset, size, children }: { reset: ReactNode; size: TextButtonSize; children: ReactNode }) {\n if (!reset) return children;\n return (\n <ButtonsGroup size={size}>\n {children}\n {reset}\n </ButtonsGroup>\n );\n}\n"],"mappings":"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"}
|