dishui 0.0.106 → 0.0.110
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/.release-verified.json +3 -2
- package/dist/components/Accordion/index.js.map +1 -1
- package/dist/components/Avatar/index.js.map +1 -1
- package/dist/components/Badge/index.js.map +1 -1
- package/dist/components/Button/index.js.map +1 -1
- package/dist/components/ButtonMenu/index.js.map +1 -1
- package/dist/components/Card/Metric2.js.map +1 -1
- package/dist/components/Card/Ticket2.js.map +1 -1
- package/dist/components/Card/index.js.map +1 -1
- package/dist/components/Chart/Chart.js.map +1 -1
- package/dist/components/Checkbox/index.js.map +1 -1
- package/dist/components/ChoicePicker/index.js.map +1 -1
- package/dist/components/Column/index.js.map +1 -1
- package/dist/components/ConfirmDialog/index.js.map +1 -1
- package/dist/components/DateTimeInput/index.js.map +1 -1
- package/dist/components/Demo/index.js.map +1 -1
- package/dist/components/Demo/source.js.map +1 -1
- package/dist/components/Divider/index.js.map +1 -1
- package/dist/components/EmptyState/index.js.map +1 -1
- package/dist/components/Field/index.js.map +1 -1
- package/dist/components/FileUploader/DropZone.js.map +1 -1
- package/dist/components/FileUploader/FileItem.js.map +1 -1
- package/dist/components/FileUploader/FileUploader.js.map +1 -1
- package/dist/components/FileUploader/UploadQueue.js.map +1 -1
- package/dist/components/GroupButton/index.js.map +1 -1
- package/dist/components/GroupedTable/index.js.map +1 -1
- package/dist/components/Icon/index.js.map +1 -1
- package/dist/components/Image/index.js.map +1 -1
- package/dist/components/Input/index.js.map +1 -1
- package/dist/components/Label/index.js.map +1 -1
- package/dist/components/Layout/index.js.map +1 -1
- package/dist/components/LayoutSuite/DashboardSuite.js.map +1 -1
- package/dist/components/LayoutSuite/index.js.map +1 -1
- package/dist/components/List/index.js.map +1 -1
- package/dist/components/Markdown/Markdown.js.map +1 -1
- package/dist/components/Markdown/codeThemes.js.map +1 -1
- package/dist/components/Markdown/markdownComponents.js.map +1 -1
- package/dist/components/Markdown/parseCodeBlockMeta.js.map +1 -1
- package/dist/components/Markdown/useMarkdownSource.js.map +1 -1
- package/dist/components/Markdown/useMarkmapRenderer.js.map +1 -1
- package/dist/components/Markdown/useMermaidRenderer.js.map +1 -1
- package/dist/components/Modal/focusScope.js.map +1 -1
- package/dist/components/Modal/index.js.map +1 -1
- package/dist/components/MultipleChoice/index.js.map +1 -1
- package/dist/components/NavigationList/index.js.map +1 -1
- package/dist/components/Page/Float2.js.map +1 -1
- package/dist/components/Page/index.js.map +1 -1
- package/dist/components/Page/resize2.js.map +1 -1
- package/dist/components/PageHeader/index.js.map +1 -1
- package/dist/components/PagePanel/index.js.map +1 -1
- package/dist/components/ProtectedRoute/index.js.map +1 -1
- package/dist/components/Resizable/ResizeGrip.js.map +1 -1
- package/dist/components/Resizable/index.js.map +1 -1
- package/dist/components/Resizable/useResize.js.map +1 -1
- package/dist/components/Row/index.js.map +1 -1
- package/dist/components/Section/index.js.map +1 -1
- package/dist/components/Select/index.js.map +1 -1
- package/dist/components/SidebarToggleButton/index.js.map +1 -1
- package/dist/components/Skeleton/index.js.map +1 -1
- package/dist/components/Slider/index.js.map +1 -1
- package/dist/components/Switch/index.js.map +1 -1
- package/dist/components/Tab/index.js.map +1 -1
- package/dist/components/Table/index.js.map +1 -1
- package/dist/components/Tabs/index.js.map +1 -1
- package/dist/components/Tabs/tabNavigation.js.map +1 -1
- package/dist/components/Text/index.js.map +1 -1
- package/dist/components/Textarea/index.js.map +1 -1
- package/dist/components/Tooltip/index.js.map +1 -1
- package/dist/components/Tree/Tree.js.map +1 -1
- package/dist/components/ai/AgentChat/index.js.map +1 -1
- package/dist/components/ai/AgentRunCard/index.js.map +1 -1
- package/dist/components/ai/AgentScreen/index.js.map +1 -1
- package/dist/components/ai/ApprovalCard/index.js.map +1 -1
- package/dist/components/ai/CodeView/index.js.map +1 -1
- package/dist/components/ai/CommandSearch/index.js.map +1 -1
- package/dist/components/ai/ContextCards/index.js.map +1 -1
- package/dist/components/ai/DiffTable/index.js.map +1 -1
- package/dist/components/ai/FilterTable/index.js.map +1 -1
- package/dist/components/ai/FineTuneCard/index.js.map +1 -1
- package/dist/components/ai/InsightCards/index.js.map +1 -1
- package/dist/components/ai/IssueCard/index.js.map +1 -1
- package/dist/components/ai/LoadingState/index.js.map +1 -1
- package/dist/components/ai/PromptBar/index.js.map +1 -1
- package/dist/components/ai/RecommendationCard/index.js.map +1 -1
- package/dist/components/ai/RecordsTable/index.js.map +1 -1
- package/dist/components/ai/SelectionActions/index.js.map +1 -1
- package/dist/components/ai/StateFlow/index.js.map +1 -1
- package/dist/components/ai/StreamingText/index.js.map +1 -1
- package/dist/components/ai/TaskRows/index.js.map +1 -1
- package/dist/components/ai/Thinking/index.js.map +1 -1
- package/dist/components/ai/ToolChips/index.js.map +1 -1
- package/dist/components/ai/shared.js.map +1 -1
- package/dist/components/pro/ArticleContextMenu/index.js.map +1 -1
- package/dist/components/pro/ArticleControl/ArticleControl2.js.map +1 -1
- package/dist/components/pro/ArticleControl/buildHeadingTree.js.map +1 -1
- package/dist/components/pro/ArticleControl/useArticleToc.js.map +1 -1
- package/dist/components/pro/Calendar/Calendar.js +165 -160
- package/dist/components/pro/Calendar/Calendar.js.map +1 -1
- package/dist/components/pro/Calendar/hooks/useCalendarDrag.js.map +1 -1
- package/dist/components/pro/Calendar/hooks/useCalendarState.js +31 -31
- package/dist/components/pro/Calendar/hooks/useCalendarState.js.map +1 -1
- package/dist/components/pro/Calendar/utils.js.map +1 -1
- package/dist/components/pro/CodeEditor/editors/CodeEditor.js.map +1 -1
- package/dist/components/pro/CodeEditor/editors/MarkdownEditor.js.map +1 -1
- package/dist/components/pro/CodeEditor/editors/markdownExtensions.js.map +1 -1
- package/dist/components/pro/CodeEditor/extensions/contextMenu.js.map +1 -1
- package/dist/components/pro/CodeEditor/extensions/index.js.map +1 -1
- package/dist/components/pro/CodeEditor/langs/index.js.map +1 -1
- package/dist/components/pro/CodeEditor/langs/javascript.js.map +1 -1
- package/dist/components/pro/CodeEditor/langs/python.js.map +1 -1
- package/dist/components/pro/CodeEditor/langs/snippets.js.map +1 -1
- package/dist/components/pro/CodeEditor/panels/FootnotePanel.js.map +1 -1
- package/dist/components/pro/CodeEditor/panels/HistoryPanel.js +65 -59
- package/dist/components/pro/CodeEditor/panels/HistoryPanel.js.map +1 -1
- package/dist/components/pro/CodeEditor/panels/OutlinePanel.js.map +1 -1
- package/dist/components/pro/CodeEditor/panels/TableOfContentsPanel.js.map +1 -1
- package/dist/components/pro/CodeEditor/panels/VariablePanel.js.map +1 -1
- package/dist/components/pro/CodeEditor/themes.js.map +1 -1
- package/dist/components/pro/CodeEditor/utils.js.map +1 -1
- package/dist/components/pro/DataTable/DataTable.js.map +1 -1
- package/dist/components/pro/DataTable/Filters.js.map +1 -1
- package/dist/components/pro/DataTable/PivotPanel.js.map +1 -1
- package/dist/components/pro/DataTable/PivotView.js.map +1 -1
- package/dist/components/pro/DataTable/components/BulkActions.js.map +1 -1
- package/dist/components/pro/DataTable/components/ColumnSelector.js.map +1 -1
- package/dist/components/pro/DataTable/components/FilterBar.js.map +1 -1
- package/dist/components/pro/DataTable/components/TableBody.js.map +1 -1
- package/dist/components/pro/DataTable/components/TableHeader.js.map +1 -1
- package/dist/components/pro/DataTable/components/TablePagination.js.map +1 -1
- package/dist/components/pro/DataTable/components/TableToolbar.js.map +1 -1
- package/dist/components/pro/DataTable/extension.js.map +1 -1
- package/dist/components/pro/DataTable/pivotEngine.js.map +1 -1
- package/dist/components/pro/DataTable/pivotExtension.js.map +1 -1
- package/dist/components/pro/DataTable/rowDetailExtension.js.map +1 -1
- package/dist/components/pro/DataTable/tanstack-table.js.map +1 -1
- package/dist/components/pro/DataTable/treeDataUtils.js.map +1 -1
- package/dist/components/pro/DataTable/useColumnDragReorder.js.map +1 -1
- package/dist/components/pro/DataTable/useDataTableModel.js +119 -104
- package/dist/components/pro/DataTable/useDataTableModel.js.map +1 -1
- package/dist/components/pro/DataTable/utils.js.map +1 -1
- package/dist/components/pro/FileExplorer/Breadcrumb.js.map +1 -1
- package/dist/components/pro/FileExplorer/CreateDialog.js.map +1 -1
- package/dist/components/pro/FileExplorer/FileExplorer.js.map +1 -1
- package/dist/components/pro/FileExplorer/FileIcon.js.map +1 -1
- package/dist/components/pro/FileExplorer/GridView.js.map +1 -1
- package/dist/components/pro/FileExplorer/ListView.js.map +1 -1
- package/dist/components/pro/FileExplorer/Toolbar.js.map +1 -1
- package/dist/components/pro/Flow/Flow.js.map +1 -1
- package/dist/components/pro/Flow/FlowCanvas.js.map +1 -1
- package/dist/components/pro/Flow/archify/build.js.map +1 -1
- package/dist/components/pro/Flow/archify/convert.js.map +1 -1
- package/dist/components/pro/Flow/archify/palette.js.map +1 -1
- package/dist/components/pro/Flow/archify/presets.js.map +1 -1
- package/dist/components/pro/Flow/archify/spec.js.map +1 -1
- package/dist/components/pro/Flow/canvas/AlignmentGuides.js.map +1 -1
- package/dist/components/pro/Flow/canvas/GridRenderer.js.map +1 -1
- package/dist/components/pro/Flow/canvas/MiniMapRenderer.js.map +1 -1
- package/dist/components/pro/Flow/canvas/PixiApp.js.map +1 -1
- package/dist/components/pro/Flow/canvas/SelectionOverlay.js.map +1 -1
- package/dist/components/pro/Flow/canvas/ViewportContainer.js.map +1 -1
- package/dist/components/pro/Flow/constants.js.map +1 -1
- package/dist/components/pro/Flow/context/FlowAIContext.js.map +1 -1
- package/dist/components/pro/Flow/context/FlowContext.js.map +1 -1
- package/dist/components/pro/Flow/context/FlowUIContext.js.map +1 -1
- package/dist/components/pro/Flow/edges/EdgeRenderer.js.map +1 -1
- package/dist/components/pro/Flow/edges/edgeMarkers.js.map +1 -1
- package/dist/components/pro/Flow/edges/edgePaths.js.map +1 -1
- package/dist/components/pro/Flow/hooks/useAlignmentGuides.js.map +1 -1
- package/dist/components/pro/Flow/hooks/useClipboard.js.map +1 -1
- package/dist/components/pro/Flow/hooks/useCompactMode.js.map +1 -1
- package/dist/components/pro/Flow/hooks/useDeleteSelection.js.map +1 -1
- package/dist/components/pro/Flow/hooks/useDraggablePanel.js.map +1 -1
- package/dist/components/pro/Flow/hooks/useEdgeMutations.js.map +1 -1
- package/dist/components/pro/Flow/hooks/useFlowImportExport.js.map +1 -1
- package/dist/components/pro/Flow/hooks/useKeyboardShortcuts.js.map +1 -1
- package/dist/components/pro/Flow/hooks/useLayoutActions.js.map +1 -1
- package/dist/components/pro/Flow/hooks/useLineMutations.js.map +1 -1
- package/dist/components/pro/Flow/hooks/useMindMap.js.map +1 -1
- package/dist/components/pro/Flow/hooks/useNodeMutations.js.map +1 -1
- package/dist/components/pro/Flow/hooks/usePdfExport.js.map +1 -1
- package/dist/components/pro/Flow/hooks/usePixiApp.js.map +1 -1
- package/dist/components/pro/Flow/hooks/useResizablePanel.js.map +1 -1
- package/dist/components/pro/Flow/hooks/useSelection.js.map +1 -1
- package/dist/components/pro/Flow/hooks/useUndoRedo.js.map +1 -1
- package/dist/components/pro/Flow/hooks/useViewport.js.map +1 -1
- package/dist/components/pro/Flow/lines/FreehandTool.js.map +1 -1
- package/dist/components/pro/Flow/lines/GeometricLineTool.js.map +1 -1
- package/dist/components/pro/Flow/lines/LineRenderer.js.map +1 -1
- package/dist/components/pro/Flow/lines/lineSmoothing.js.map +1 -1
- package/dist/components/pro/Flow/nodes/DataNodeView.js.map +1 -1
- package/dist/components/pro/Flow/nodes/MediaContent.js.map +1 -1
- package/dist/components/pro/Flow/nodes/MediaOverlay.js.map +1 -1
- package/dist/components/pro/Flow/nodes/Model3DView.js.map +1 -1
- package/dist/components/pro/Flow/nodes/NodeHandles.js.map +1 -1
- package/dist/components/pro/Flow/nodes/NodeRenderer.js.map +1 -1
- package/dist/components/pro/Flow/nodes/NodeText.js.map +1 -1
- package/dist/components/pro/Flow/nodes/aiClient.js.map +1 -1
- package/dist/components/pro/Flow/nodes/animAnchors.js.map +1 -1
- package/dist/components/pro/Flow/nodes/chartGeometryBudget.js.map +1 -1
- package/dist/components/pro/Flow/nodes/dataUtils.js.map +1 -1
- package/dist/components/pro/Flow/nodes/demandRender.js.map +1 -1
- package/dist/components/pro/Flow/nodes/nodeUtils.js.map +1 -1
- package/dist/components/pro/Flow/nodes/shapes.js.map +1 -1
- package/dist/components/pro/Flow/nodes/svgIconTexture.js.map +1 -1
- package/dist/components/pro/Flow/nodes/svgIcons.js.map +1 -1
- package/dist/components/pro/Flow/panels/AIPanel.js.map +1 -1
- package/dist/components/pro/Flow/panels/Breadcrumb.js.map +1 -1
- package/dist/components/pro/Flow/panels/ContextMenu.js.map +1 -1
- package/dist/components/pro/Flow/panels/DataInspectorSection.js.map +1 -1
- package/dist/components/pro/Flow/panels/ExpandableTextarea.js.map +1 -1
- package/dist/components/pro/Flow/panels/FlowPanelGroup.js.map +1 -1
- package/dist/components/pro/Flow/panels/FlowToolbar.js.map +1 -1
- package/dist/components/pro/Flow/panels/InspectorPanel.js.map +1 -1
- package/dist/components/pro/Flow/panels/OutlinePanel.js.map +1 -1
- package/dist/components/pro/Flow/panels/PresentationMode.js.map +1 -1
- package/dist/components/pro/Flow/panels/ProjectTitle.js.map +1 -1
- package/dist/components/pro/Flow/panels/ShapePalette.js.map +1 -1
- package/dist/components/pro/Flow/panels/toolbarIcons.js.map +1 -1
- package/dist/components/pro/Flow/types.js.map +1 -1
- package/dist/components/pro/Flow/utils/dashedLine.js.map +1 -1
- package/dist/components/pro/Flow/utils/gradient.js.map +1 -1
- package/dist/components/pro/Flow/utils/pdfExport.js.map +1 -1
- package/dist/components/pro/Flow/utils/themeColor.js.map +1 -1
- package/dist/components/pro/Gantt/Gantt.js.map +1 -1
- package/dist/components/pro/Gantt/hooks/useGanttDrag.js.map +1 -1
- package/dist/components/pro/Gantt/hooks/useGanttState.js +79 -71
- package/dist/components/pro/Gantt/hooks/useGanttState.js.map +1 -1
- package/dist/components/pro/Gantt/utils.js.map +1 -1
- package/dist/components/pro/Kanban/Kanban.js.map +1 -1
- package/dist/components/pro/Kanban/Swimlanes.js.map +1 -1
- package/dist/components/pro/Kanban/useKanbanDrag.js.map +1 -1
- package/dist/components/pro/Kanban/utils.js.map +1 -1
- package/dist/components/pro/RichTextEditor/AIRewrite.d.ts +1 -0
- package/dist/components/pro/RichTextEditor/AIRewrite.js +109 -88
- package/dist/components/pro/RichTextEditor/AIRewrite.js.map +1 -1
- package/dist/components/pro/RichTextEditor/BlockHandle.js.map +1 -1
- package/dist/components/pro/RichTextEditor/BubbleToolbar.js.map +1 -1
- package/dist/components/pro/RichTextEditor/CommentDialog.js.map +1 -1
- package/dist/components/pro/RichTextEditor/CommentPanel.js.map +1 -1
- package/dist/components/pro/RichTextEditor/CommentPopover.js.map +1 -1
- package/dist/components/pro/RichTextEditor/ContextMenu.js.map +1 -1
- package/dist/components/pro/RichTextEditor/RichTextEditor.js +133 -132
- package/dist/components/pro/RichTextEditor/RichTextEditor.js.map +1 -1
- package/dist/components/pro/RichTextEditor/SlashMenu.js.map +1 -1
- package/dist/components/pro/RichTextEditor/Toolbar/ColorPicker.js.map +1 -1
- package/dist/components/pro/RichTextEditor/Toolbar/FloatingDialogMenu.js.map +1 -1
- package/dist/components/pro/RichTextEditor/Toolbar/MarkdownTemplates.js.map +1 -1
- package/dist/components/pro/RichTextEditor/Toolbar/SelectDropdown.js.map +1 -1
- package/dist/components/pro/RichTextEditor/Toolbar/TableOpsMenu.js.map +1 -1
- package/dist/components/pro/RichTextEditor/Toolbar/Toolbar.js.map +1 -1
- package/dist/components/pro/RichTextEditor/Toolbar/ToolbarButton.js.map +1 -1
- package/dist/components/pro/RichTextEditor/Toolbar/useAnchoredDropdown.js.map +1 -1
- package/dist/components/pro/RichTextEditor/extensions/CodeBlockView.js.map +1 -1
- package/dist/components/pro/RichTextEditor/extensions/CommentMark.js.map +1 -1
- package/dist/components/pro/RichTextEditor/extensions/FloatingDialog.js.map +1 -1
- package/dist/components/pro/RichTextEditor/extensions/FontSize.js.map +1 -1
- package/dist/components/pro/RichTextEditor/extensions/MarkdownBlock.js.map +1 -1
- package/dist/components/pro/RichTextEditor/extensions/ResizableImage.js.map +1 -1
- package/dist/components/pro/RichTextEditor/extensions/legacyMermaid.js.map +1 -1
- package/dist/components/pro/RichTextEditor/types.d.ts +2 -0
- package/dist/components/pro/RichTextEditor/types.js.map +1 -1
- package/dist/components/pro/Sprite/Molly/animations.d.ts +6 -0
- package/dist/components/pro/Sprite/Molly/animations.js +52 -0
- package/dist/components/pro/Sprite/Molly/animations.js.map +1 -0
- package/dist/components/pro/Sprite/Molly/events.d.ts +2 -0
- package/dist/components/pro/Sprite/Molly/events.js +46 -0
- package/dist/components/pro/Sprite/Molly/events.js.map +1 -0
- package/dist/components/pro/Sprite/Molly/index.d.ts +1 -0
- package/dist/components/pro/Sprite/Molly/index.js +12 -0
- package/dist/components/pro/Sprite/Molly/index.js.map +1 -0
- package/dist/components/pro/Sprite/Molly/legFlexion.d.ts +3 -0
- package/dist/components/pro/Sprite/Molly/legFlexion.js +48 -0
- package/dist/components/pro/Sprite/Molly/legFlexion.js.map +1 -0
- package/dist/components/pro/Sprite/Molly/locomotion.d.ts +4 -0
- package/dist/components/pro/Sprite/Molly/locomotion.js +51 -0
- package/dist/components/pro/Sprite/Molly/locomotion.js.map +1 -0
- package/dist/components/pro/Sprite/Molly/model.d.ts +23 -0
- package/dist/components/pro/Sprite/Molly/model.js +614 -0
- package/dist/components/pro/Sprite/Molly/model.js.map +1 -0
- package/dist/components/pro/Sprite/Molly/rig.d.ts +25 -0
- package/dist/components/pro/Sprite/Molly/rig.js +102 -0
- package/dist/components/pro/Sprite/Molly/rig.js.map +1 -0
- package/dist/components/pro/Sprite/Ruby/animations.d.ts +6 -0
- package/dist/components/pro/Sprite/Ruby/animations.js +58 -0
- package/dist/components/pro/Sprite/Ruby/animations.js.map +1 -0
- package/dist/components/pro/Sprite/Ruby/events.d.ts +2 -0
- package/dist/components/pro/Sprite/Ruby/events.js +46 -0
- package/dist/components/pro/Sprite/Ruby/events.js.map +1 -0
- package/dist/components/pro/Sprite/Ruby/index.d.ts +1 -0
- package/dist/components/pro/Sprite/Ruby/index.js +12 -0
- package/dist/components/pro/Sprite/Ruby/index.js.map +1 -0
- package/dist/components/pro/Sprite/Ruby/locomotion.d.ts +4 -0
- package/dist/components/pro/Sprite/Ruby/locomotion.js +41 -0
- package/dist/components/pro/Sprite/Ruby/locomotion.js.map +1 -0
- package/dist/components/pro/Sprite/Ruby/model.d.ts +23 -0
- package/dist/components/pro/Sprite/Ruby/model.js +605 -0
- package/dist/components/pro/Sprite/Ruby/model.js.map +1 -0
- package/dist/components/pro/Sprite/Ruby/rig.d.ts +26 -0
- package/dist/components/pro/Sprite/Ruby/rig.js +112 -0
- package/dist/components/pro/Sprite/Ruby/rig.js.map +1 -0
- package/dist/components/pro/Sprite/Sprite.d.ts +23 -0
- package/dist/components/pro/Sprite/Sprite2.js +224 -0
- package/dist/components/pro/Sprite/Sprite2.js.map +1 -0
- package/dist/components/pro/Sprite/actions.d.ts +3 -0
- package/dist/components/pro/Sprite/actions.js +16 -0
- package/dist/components/pro/Sprite/actions.js.map +1 -0
- package/dist/components/pro/Sprite/createSpriteScene.d.ts +4 -0
- package/dist/components/pro/Sprite/createSpriteScene.js +14 -0
- package/dist/components/pro/Sprite/createSpriteScene.js.map +1 -0
- package/dist/components/pro/Sprite/index.d.ts +4 -0
- package/dist/components/pro/Sprite/shared/animation.d.ts +33 -0
- package/dist/components/pro/Sprite/shared/animation.js +39 -0
- package/dist/components/pro/Sprite/shared/animation.js.map +1 -0
- package/dist/components/pro/Sprite/shared/createSpriteStage.d.ts +14 -0
- package/dist/components/pro/Sprite/shared/createSpriteStage.js +127 -0
- package/dist/components/pro/Sprite/shared/createSpriteStage.js.map +1 -0
- package/dist/components/pro/Sprite/shared/events.d.ts +5 -0
- package/dist/components/pro/Sprite/shared/events.js +21 -0
- package/dist/components/pro/Sprite/shared/events.js.map +1 -0
- package/dist/components/pro/Sprite/shared/face.d.ts +15 -0
- package/dist/components/pro/Sprite/shared/face.js +33 -0
- package/dist/components/pro/Sprite/shared/face.js.map +1 -0
- package/dist/components/pro/Sprite/shared/faceSurface.d.ts +6 -0
- package/dist/components/pro/Sprite/shared/faceSurface.js +66 -0
- package/dist/components/pro/Sprite/shared/faceSurface.js.map +1 -0
- package/dist/components/pro/Sprite/shared/gait.d.ts +5 -0
- package/dist/components/pro/Sprite/shared/gait.js +19 -0
- package/dist/components/pro/Sprite/shared/gait.js.map +1 -0
- package/dist/components/pro/Sprite/shared/geometry.d.ts +8 -0
- package/dist/components/pro/Sprite/shared/geometry.js +59 -0
- package/dist/components/pro/Sprite/shared/geometry.js.map +1 -0
- package/dist/components/pro/Sprite/shared/magicEffect.d.ts +27 -0
- package/dist/components/pro/Sprite/shared/magicEffect.js +61 -0
- package/dist/components/pro/Sprite/shared/magicEffect.js.map +1 -0
- package/dist/components/pro/Sprite/shared/playback.d.ts +8 -0
- package/dist/components/pro/Sprite/shared/playback.js +74 -0
- package/dist/components/pro/Sprite/shared/playback.js.map +1 -0
- package/dist/components/pro/Sprite/shared/rig.d.ts +6 -0
- package/dist/components/pro/Sprite/shared/rig.js +48 -0
- package/dist/components/pro/Sprite/shared/rig.js.map +1 -0
- package/dist/components/pro/Sprite/shared/scene.d.ts +9 -0
- package/dist/components/pro/Sprite/shared/scene.js +21 -0
- package/dist/components/pro/Sprite/shared/scene.js.map +1 -0
- package/dist/components/pro/Sprite/shared/sculptor.d.ts +14 -0
- package/dist/components/pro/Sprite/shared/sculptor.js +78 -0
- package/dist/components/pro/Sprite/shared/sculptor.js.map +1 -0
- package/dist/components/pro/Sprite/shared/speechBubble.d.ts +15 -0
- package/dist/components/pro/Sprite/shared/speechBubble.js +129 -0
- package/dist/components/pro/Sprite/shared/speechBubble.js.map +1 -0
- package/dist/components/pro/Sprite/types.d.ts +40 -0
- package/dist/contexts/AppContext.js.map +1 -1
- package/dist/contexts/authentication.js.map +1 -1
- package/dist/data-table.js +3 -3
- package/dist/data-table.js.map +1 -1
- package/dist/dishui.css +1 -1
- package/dist/hooks/useClickOutside.js.map +1 -1
- package/dist/hooks/useContainerElement.js.map +1 -1
- package/dist/hooks/useControllableState.js.map +1 -1
- package/dist/hooks/useCopyToClipboard.js.map +1 -1
- package/dist/hooks/useDebounce.js.map +1 -1
- package/dist/hooks/useDraggable.js.map +1 -1
- package/dist/hooks/useTableColumns.js.map +1 -1
- package/dist/hooks/useTableData.js.map +1 -1
- package/dist/index-nostyled.d.ts +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +116 -115
- package/dist/layouts/MainLayout.js.map +1 -1
- package/dist/layouts/Navbar.js.map +1 -1
- package/dist/layouts/Sidebar.js.map +1 -1
- package/dist/layouts/headbar/Headbar.js.map +1 -1
- package/dist/layouts/headbar/HeadbarLangSwitcher.js.map +1 -1
- package/dist/layouts/headbar/HeadbarSearch.js.map +1 -1
- package/dist/layouts/headbar/HeadbarThemeSwitcher.js.map +1 -1
- package/dist/layouts/headbar/HeadbarTitle.js.map +1 -1
- package/dist/sprite.js +2 -0
- package/dist/styles/themeStyles.js.map +1 -1
- package/dist/utils/cn.js.map +1 -1
- package/dist/utils/flexLayout.js.map +1 -1
- package/dist/utils/i18n.js.map +1 -1
- package/dist/utils/menu.js.map +1 -1
- package/dist/utils/rehype-code-meta.js.map +1 -1
- package/dist/utils/remark-code-meta.js.map +1 -1
- package/dist/utils/routerBuilder.js.map +1 -1
- package/dist/utils/safeStorage.js.map +1 -1
- package/dist/utils/selection.js +29 -20
- package/dist/utils/selection.js.map +1 -1
- package/dist/utils/themes.js.map +1 -1
- package/package.json +15 -11
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../../../src/components/ai/DiffTable/index.tsx"],"sourcesContent":["import React, { useEffect, useMemo, useState, useSyncExternalStore } from 'react';\nimport { cn } from '../../../utils/cn';\nimport Button from '../../Button';\nimport './DiffTable.css';\n\nexport type DiffTableColumn = {\n key: string;\n label: React.ReactNode;\n align?: 'left' | 'right';\n /** Render the cell in the mono font (IDs, SKUs). */\n mono?: boolean;\n};\n\nexport type DiffCellValue = string | number | null;\n\n/** A plain value is unchanged; an object describes a `before → after` edit. */\nexport type DiffCell = DiffCellValue | { before?: DiffCellValue; after?: DiffCellValue };\n\nexport type DiffRowChange = 'added' | 'removed' | 'modified';\n\nexport type DiffRow = {\n id: string;\n cells: Record<string, DiffCell>;\n /** Row-level change. Omitted rows are inferred as `modified` when any cell is an edit. */\n change?: DiffRowChange;\n};\n\nexport type DiffDecision = 'accepted' | 'rejected';\n\nexport type DiffTableProps = Omit<React.HTMLAttributes<HTMLDivElement>, 'children' | 'title' | 'summary'> & {\n columns: DiffTableColumn[];\n rows: DiffRow[];\n title?: React.ReactNode;\n /** Summary line under the title. Auto-computed (`3 changes across 2 rows`) when omitted. */\n summary?: React.ReactNode;\n /** Reveal changed rows one by one, top to bottom. */\n sweeping?: boolean;\n /** Milliseconds between each revealed row while sweeping. */\n sweepIntervalMs?: number;\n onAcceptAll?: () => void;\n onRejectAll?: () => void;\n onAcceptRow?: (row: DiffRow) => void;\n onRejectRow?: (row: DiffRow) => void;\n /** Controlled per-row decisions, keyed by row id. */\n decisions?: Record<string, DiffDecision>;\n dense?: boolean;\n /** Text overrides. */\n labels?: Partial<typeof DEFAULT_LABELS>;\n};\n\nconst DEFAULT_LABELS = {\n added: 'Added',\n removed: 'Removed',\n modified: 'Changed',\n unchanged: '—',\n acceptAll: 'Accept all',\n rejectAll: 'Reject all',\n accept: 'Accept',\n reject: 'Reject',\n accepted: 'Accepted',\n rejected: 'Rejected',\n status: 'Status',\n actions: 'Actions',\n empty: '∅',\n};\n\nexport function isDiffCellChange(cell: DiffCell): cell is { before?: DiffCellValue; after?: DiffCellValue } {\n return typeof cell === 'object' && cell !== null;\n}\n\n/** Infer the row's change kind from its explicit flag or its cells. */\nexport function diffRowChange(row: DiffRow): DiffRowChange | undefined {\n if (row.change) return row.change;\n return Object.values(row.cells).some(isDiffCellChange) ? 'modified' : undefined;\n}\n\n/** Number of changed cells in a row (whole row counts as one for added/removed). */\nfunction countRowChanges(row: DiffRow): number {\n const change = row.change;\n if (change === 'added' || change === 'removed') return 1;\n return Object.values(row.cells).filter(isDiffCellChange).length;\n}\n\nconst plural = (n: number, word: string) => `${n} ${word}${n === 1 ? '' : 's'}`;\n\nconst REDUCED_MOTION = '(prefers-reduced-motion: reduce)';\nconst subscribeReducedMotion = (onChange: () => void) => {\n if (typeof window === 'undefined' || !window.matchMedia) return () => {};\n const mq = window.matchMedia(REDUCED_MOTION);\n mq.addEventListener('change', onChange);\n return () => mq.removeEventListener('change', onChange);\n};\nconst getReducedMotion = () => typeof window !== 'undefined' && !!window.matchMedia && window.matchMedia(REDUCED_MOTION).matches;\nconst usePrefersReducedMotion = () => useSyncExternalStore(subscribeReducedMotion, getReducedMotion, () => false);\n\nconst CheckIcon = () => (\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden=\"true\">\n <path d=\"M20 6 9 17l-5-5\" />\n </svg>\n);\nconst XIcon = () => (\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden=\"true\">\n <path d=\"M18 6 6 18M6 6l12 12\" />\n </svg>\n);\n\nconst renderValue = (value: DiffCellValue | undefined, empty: string) =>\n value === null || value === undefined || value === '' ? <span className=\"dishui-ai-diff-table__empty\">{empty}</span> : String(value);\n\n/**\n * AI-proposed edits sweeping through tabular data. Changed cells show the\n * old value struck through next to the new one; added/removed rows are\n * tinted and always labelled in the leading status column.\n */\nexport default function DiffTable({\n columns,\n rows,\n title,\n summary,\n sweeping: sweepingProp = false,\n sweepIntervalMs = 220,\n onAcceptAll,\n onRejectAll,\n onAcceptRow,\n onRejectRow,\n decisions,\n dense = false,\n labels: labelOverrides,\n className,\n ...rest\n}: DiffTableProps) {\n const labels = { ...DEFAULT_LABELS, ...labelOverrides };\n\n const changedIds = useMemo(() => rows.filter(r => diffRowChange(r)).map(r => r.id), [rows]);\n const changeCount = useMemo(() => rows.reduce((n, r) => n + countRowChanges(r), 0), [rows]);\n\n // Reduced motion: skip the sweep and show every change at once.\n const reducedMotion = usePrefersReducedMotion();\n const sweeping = sweepingProp && !reducedMotion;\n\n // Sweep: reveal one changed row per interval. `revealed` counts changed rows (top to bottom).\n const [revealed, setRevealed] = useState(0);\n useEffect(() => {\n if (!sweeping) return;\n const total = changedIds.length;\n const id = window.setInterval(() => {\n setRevealed(n => {\n if (n + 1 >= total) window.clearInterval(id);\n return Math.min(n + 1, total);\n });\n }, Math.max(16, sweepIntervalMs));\n return () => {\n window.clearInterval(id);\n // Restart from the top the next time a sweep begins.\n setRevealed(0);\n };\n }, [sweeping, sweepIntervalMs, changedIds.length]);\n\n const revealedIds = useMemo(() => {\n if (!sweeping) return null;\n return new Set(changedIds.slice(0, revealed));\n }, [sweeping, changedIds, revealed]);\n\n const done = !sweeping || revealed >= changedIds.length;\n const hasRowActions = Boolean(onAcceptRow || onRejectRow);\n const hasFooter = Boolean(onAcceptAll || onRejectAll);\n\n const autoSummary = `${plural(changeCount, 'change')} across ${plural(changedIds.length, 'row')}`;\n\n return (\n <div\n {...rest}\n className={cn('dishui-ai-diff-table', dense && 'dishui-ai-diff-table--dense', sweeping && !done && 'dishui-ai-diff-table--sweeping', className)}\n >\n {(title || summary !== null) && (\n <div className=\"dishui-ai-diff-table__header\">\n {title && <div className=\"dishui-ai-diff-table__title\">{title}</div>}\n <div className=\"dishui-ai-diff-table__summary\" aria-live=\"polite\">\n {summary ?? autoSummary}\n </div>\n </div>\n )}\n\n <div className=\"dishui-ai-diff-table__scroll\">\n <table className=\"dishui-ai-diff-table__table\">\n <thead>\n <tr>\n <th scope=\"col\" className=\"dishui-ai-diff-table__th dishui-ai-diff-table__th--status\">{labels.status}</th>\n {columns.map(col => (\n <th\n key={col.key}\n scope=\"col\"\n className={cn('dishui-ai-diff-table__th', col.align === 'right' && 'dishui-ai-diff-table__th--right')}\n >\n {col.label}\n </th>\n ))}\n {hasRowActions && (\n <th scope=\"col\" className=\"dishui-ai-diff-table__th dishui-ai-diff-table__th--actions\">\n <span className=\"dishui-ai-diff-table__sr\">{labels.actions}</span>\n </th>\n )}\n </tr>\n </thead>\n <tbody>\n {rows.map(row => {\n const change = diffRowChange(row);\n const visible = !change || !revealedIds || revealedIds.has(row.id);\n const decision = decisions?.[row.id];\n return (\n <tr\n key={row.id}\n className={cn(\n 'dishui-ai-diff-table__row',\n change && `dishui-ai-diff-table__row--${change}`,\n change && visible && 'dishui-ai-diff-table__row--revealed',\n decision && `dishui-ai-diff-table__row--${decision}`,\n )}\n data-change={change}\n >\n <td className=\"dishui-ai-diff-table__td dishui-ai-diff-table__td--status\">\n {change && visible ? (\n <span className={cn('dishui-ai-diff-table__badge', `dishui-ai-diff-table__badge--${change}`)}>\n {labels[change]}\n </span>\n ) : (\n <span className=\"dishui-ai-diff-table__badge dishui-ai-diff-table__badge--none\" aria-hidden=\"true\">\n {labels.unchanged}\n </span>\n )}\n </td>\n {columns.map(col => {\n const cell = row.cells[col.key];\n const changed = visible && isDiffCellChange(cell);\n const tdClass = cn(\n 'dishui-ai-diff-table__td',\n col.align === 'right' && 'dishui-ai-diff-table__td--right',\n col.mono && 'dishui-ai-diff-table__td--mono',\n changed && 'dishui-ai-diff-table__td--changed',\n );\n if (isDiffCellChange(cell)) {\n if (!visible) {\n return <td key={col.key} className={tdClass}>{renderValue(cell.before, labels.empty)}</td>;\n }\n return (\n <td key={col.key} className={tdClass}>\n <span className=\"dishui-ai-diff-table__change\">\n <del className=\"dishui-ai-diff-table__before\">{renderValue(cell.before, labels.empty)}</del>\n <span className=\"dishui-ai-diff-table__arrow\" aria-hidden=\"true\">→</span>\n <ins className=\"dishui-ai-diff-table__after\">{renderValue(cell.after, labels.empty)}</ins>\n </span>\n </td>\n );\n }\n return (\n <td key={col.key} className={tdClass}>\n {change === 'removed' && visible ? (\n <del className=\"dishui-ai-diff-table__before\">{renderValue(cell, labels.empty)}</del>\n ) : change === 'added' && visible ? (\n <ins className=\"dishui-ai-diff-table__after\">{renderValue(cell, labels.empty)}</ins>\n ) : (\n renderValue(cell, labels.empty)\n )}\n </td>\n );\n })}\n {hasRowActions && (\n <td className=\"dishui-ai-diff-table__td dishui-ai-diff-table__td--actions\">\n {change && visible && (\n decision ? (\n <span className={cn('dishui-ai-diff-table__decision', `dishui-ai-diff-table__decision--${decision}`)}>\n {decision === 'accepted' ? <CheckIcon /> : <XIcon />}\n {labels[decision]}\n </span>\n ) : (\n <span className=\"dishui-ai-diff-table__row-actions\">\n {onAcceptRow && (\n <button type=\"button\" className=\"dishui-ai-diff-table__icon-btn\" aria-label={labels.accept} title={labels.accept} onClick={() => onAcceptRow(row)}>\n <CheckIcon />\n </button>\n )}\n {onRejectRow && (\n <button type=\"button\" className=\"dishui-ai-diff-table__icon-btn\" aria-label={labels.reject} title={labels.reject} onClick={() => onRejectRow(row)}>\n <XIcon />\n </button>\n )}\n </span>\n )\n )}\n </td>\n )}\n </tr>\n );\n })}\n </tbody>\n </table>\n </div>\n\n {hasFooter && (\n <div className=\"dishui-ai-diff-table__footer\">\n <span className=\"dishui-ai-diff-table__footer-meta\">{done ? autoSummary : 'Reviewing…'}</span>\n <span className=\"dishui-ai-diff-table__footer-actions\">\n {onRejectAll && (\n <Button type=\"button\" variant=\"secondary\" size=\"sm\" onClick={onRejectAll} disabled={!done}>\n {labels.rejectAll}\n </Button>\n )}\n {onAcceptAll && (\n <Button type=\"button\" variant=\"primary\" size=\"sm\" onClick={onAcceptAll} disabled={!done}>\n {labels.acceptAll}\n </Button>\n )}\n </span>\n </div>\n )}\n </div>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":["Button","ButtonWithCompounds","DEFAULT_LABELS","isDiffCellChange","cell","diffRowChange","row","countRowChanges","change","plural","n","word","REDUCED_MOTION","subscribeReducedMotion","mq","onChange","getReducedMotion","usePrefersReducedMotion","useSyncExternalStore","CheckIcon","XIcon","renderValue","value","empty","DiffTable","sweepingProp","labelOverrides","rest","labels","changedIds","useMemo","rows","r","changeCount","reducedMotion","sweeping","revealed","setRevealed","useState","total","id","sweepIntervalMs","revealedIds","done","hasRowActions","onAcceptRow","onRejectRow","hasFooter","onAcceptAll","onRejectAll","autoSummary","cn","dense","className","title","summary","columns","col","visible","decision","decisions","changed","tdClass","jsx"],"sources":["../../../../src/components/ai/DiffTable/index.tsx"],"sourcesContent":["import React, { useEffect, useMemo, useState, useSyncExternalStore } from 'react';\nimport { cn } from '../../../utils/cn';\nimport Button from '../../Button';\nimport './DiffTable.css';\n\nexport type DiffTableColumn = {\n key: string;\n label: React.ReactNode;\n align?: 'left' | 'right';\n /** Render the cell in the mono font (IDs, SKUs). */\n mono?: boolean;\n};\n\nexport type DiffCellValue = string | number | null;\n\n/** A plain value is unchanged; an object describes a `before → after` edit. */\nexport type DiffCell = DiffCellValue | { before?: DiffCellValue; after?: DiffCellValue };\n\nexport type DiffRowChange = 'added' | 'removed' | 'modified';\n\nexport type DiffRow = {\n id: string;\n cells: Record<string, DiffCell>;\n /** Row-level change. Omitted rows are inferred as `modified` when any cell is an edit. */\n change?: DiffRowChange;\n};\n\nexport type DiffDecision = 'accepted' | 'rejected';\n\nexport type DiffTableProps = Omit<React.HTMLAttributes<HTMLDivElement>, 'children' | 'title' | 'summary'> & {\n columns: DiffTableColumn[];\n rows: DiffRow[];\n title?: React.ReactNode;\n /** Summary line under the title. Auto-computed (`3 changes across 2 rows`) when omitted. */\n summary?: React.ReactNode;\n /** Reveal changed rows one by one, top to bottom. */\n sweeping?: boolean;\n /** Milliseconds between each revealed row while sweeping. */\n sweepIntervalMs?: number;\n onAcceptAll?: () => void;\n onRejectAll?: () => void;\n onAcceptRow?: (row: DiffRow) => void;\n onRejectRow?: (row: DiffRow) => void;\n /** Controlled per-row decisions, keyed by row id. */\n decisions?: Record<string, DiffDecision>;\n dense?: boolean;\n /** Text overrides. */\n labels?: Partial<typeof DEFAULT_LABELS>;\n};\n\nconst DEFAULT_LABELS = {\n added: 'Added',\n removed: 'Removed',\n modified: 'Changed',\n unchanged: '—',\n acceptAll: 'Accept all',\n rejectAll: 'Reject all',\n accept: 'Accept',\n reject: 'Reject',\n accepted: 'Accepted',\n rejected: 'Rejected',\n status: 'Status',\n actions: 'Actions',\n empty: '∅',\n};\n\nexport function isDiffCellChange(cell: DiffCell): cell is { before?: DiffCellValue; after?: DiffCellValue } {\n return typeof cell === 'object' && cell !== null;\n}\n\n/** Infer the row's change kind from its explicit flag or its cells. */\nexport function diffRowChange(row: DiffRow): DiffRowChange | undefined {\n if (row.change) return row.change;\n return Object.values(row.cells).some(isDiffCellChange) ? 'modified' : undefined;\n}\n\n/** Number of changed cells in a row (whole row counts as one for added/removed). */\nfunction countRowChanges(row: DiffRow): number {\n const change = row.change;\n if (change === 'added' || change === 'removed') return 1;\n return Object.values(row.cells).filter(isDiffCellChange).length;\n}\n\nconst plural = (n: number, word: string) => `${n} ${word}${n === 1 ? '' : 's'}`;\n\nconst REDUCED_MOTION = '(prefers-reduced-motion: reduce)';\nconst subscribeReducedMotion = (onChange: () => void) => {\n if (typeof window === 'undefined' || !window.matchMedia) return () => {};\n const mq = window.matchMedia(REDUCED_MOTION);\n mq.addEventListener('change', onChange);\n return () => mq.removeEventListener('change', onChange);\n};\nconst getReducedMotion = () => typeof window !== 'undefined' && !!window.matchMedia && window.matchMedia(REDUCED_MOTION).matches;\nconst usePrefersReducedMotion = () => useSyncExternalStore(subscribeReducedMotion, getReducedMotion, () => false);\n\nconst CheckIcon = () => (\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden=\"true\">\n <path d=\"M20 6 9 17l-5-5\" />\n </svg>\n);\nconst XIcon = () => (\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden=\"true\">\n <path d=\"M18 6 6 18M6 6l12 12\" />\n </svg>\n);\n\nconst renderValue = (value: DiffCellValue | undefined, empty: string) =>\n value === null || value === undefined || value === '' ? <span className=\"dishui-ai-diff-table__empty\">{empty}</span> : String(value);\n\n/**\n * AI-proposed edits sweeping through tabular data. Changed cells show the\n * old value struck through next to the new one; added/removed rows are\n * tinted and always labelled in the leading status column.\n */\nexport default function DiffTable({\n columns,\n rows,\n title,\n summary,\n sweeping: sweepingProp = false,\n sweepIntervalMs = 220,\n onAcceptAll,\n onRejectAll,\n onAcceptRow,\n onRejectRow,\n decisions,\n dense = false,\n labels: labelOverrides,\n className,\n ...rest\n}: DiffTableProps) {\n const labels = { ...DEFAULT_LABELS, ...labelOverrides };\n\n const changedIds = useMemo(() => rows.filter(r => diffRowChange(r)).map(r => r.id), [rows]);\n const changeCount = useMemo(() => rows.reduce((n, r) => n + countRowChanges(r), 0), [rows]);\n\n // Reduced motion: skip the sweep and show every change at once.\n const reducedMotion = usePrefersReducedMotion();\n const sweeping = sweepingProp && !reducedMotion;\n\n // Sweep: reveal one changed row per interval. `revealed` counts changed rows (top to bottom).\n const [revealed, setRevealed] = useState(0);\n useEffect(() => {\n if (!sweeping) return;\n const total = changedIds.length;\n const id = window.setInterval(() => {\n setRevealed(n => {\n if (n + 1 >= total) window.clearInterval(id);\n return Math.min(n + 1, total);\n });\n }, Math.max(16, sweepIntervalMs));\n return () => {\n window.clearInterval(id);\n // Restart from the top the next time a sweep begins.\n setRevealed(0);\n };\n }, [sweeping, sweepIntervalMs, changedIds.length]);\n\n const revealedIds = useMemo(() => {\n if (!sweeping) return null;\n return new Set(changedIds.slice(0, revealed));\n }, [sweeping, changedIds, revealed]);\n\n const done = !sweeping || revealed >= changedIds.length;\n const hasRowActions = Boolean(onAcceptRow || onRejectRow);\n const hasFooter = Boolean(onAcceptAll || onRejectAll);\n\n const autoSummary = `${plural(changeCount, 'change')} across ${plural(changedIds.length, 'row')}`;\n\n return (\n <div\n {...rest}\n className={cn('dishui-ai-diff-table', dense && 'dishui-ai-diff-table--dense', sweeping && !done && 'dishui-ai-diff-table--sweeping', className)}\n >\n {(title || summary !== null) && (\n <div className=\"dishui-ai-diff-table__header\">\n {title && <div className=\"dishui-ai-diff-table__title\">{title}</div>}\n <div className=\"dishui-ai-diff-table__summary\" aria-live=\"polite\">\n {summary ?? autoSummary}\n </div>\n </div>\n )}\n\n <div className=\"dishui-ai-diff-table__scroll\">\n <table className=\"dishui-ai-diff-table__table\">\n <thead>\n <tr>\n <th scope=\"col\" className=\"dishui-ai-diff-table__th dishui-ai-diff-table__th--status\">{labels.status}</th>\n {columns.map(col => (\n <th\n key={col.key}\n scope=\"col\"\n className={cn('dishui-ai-diff-table__th', col.align === 'right' && 'dishui-ai-diff-table__th--right')}\n >\n {col.label}\n </th>\n ))}\n {hasRowActions && (\n <th scope=\"col\" className=\"dishui-ai-diff-table__th dishui-ai-diff-table__th--actions\">\n <span className=\"dishui-ai-diff-table__sr\">{labels.actions}</span>\n </th>\n )}\n </tr>\n </thead>\n <tbody>\n {rows.map(row => {\n const change = diffRowChange(row);\n const visible = !change || !revealedIds || revealedIds.has(row.id);\n const decision = decisions?.[row.id];\n return (\n <tr\n key={row.id}\n className={cn(\n 'dishui-ai-diff-table__row',\n change && `dishui-ai-diff-table__row--${change}`,\n change && visible && 'dishui-ai-diff-table__row--revealed',\n decision && `dishui-ai-diff-table__row--${decision}`,\n )}\n data-change={change}\n >\n <td className=\"dishui-ai-diff-table__td dishui-ai-diff-table__td--status\">\n {change && visible ? (\n <span className={cn('dishui-ai-diff-table__badge', `dishui-ai-diff-table__badge--${change}`)}>\n {labels[change]}\n </span>\n ) : (\n <span className=\"dishui-ai-diff-table__badge dishui-ai-diff-table__badge--none\" aria-hidden=\"true\">\n {labels.unchanged}\n </span>\n )}\n </td>\n {columns.map(col => {\n const cell = row.cells[col.key];\n const changed = visible && isDiffCellChange(cell);\n const tdClass = cn(\n 'dishui-ai-diff-table__td',\n col.align === 'right' && 'dishui-ai-diff-table__td--right',\n col.mono && 'dishui-ai-diff-table__td--mono',\n changed && 'dishui-ai-diff-table__td--changed',\n );\n if (isDiffCellChange(cell)) {\n if (!visible) {\n return <td key={col.key} className={tdClass}>{renderValue(cell.before, labels.empty)}</td>;\n }\n return (\n <td key={col.key} className={tdClass}>\n <span className=\"dishui-ai-diff-table__change\">\n <del className=\"dishui-ai-diff-table__before\">{renderValue(cell.before, labels.empty)}</del>\n <span className=\"dishui-ai-diff-table__arrow\" aria-hidden=\"true\">→</span>\n <ins className=\"dishui-ai-diff-table__after\">{renderValue(cell.after, labels.empty)}</ins>\n </span>\n </td>\n );\n }\n return (\n <td key={col.key} className={tdClass}>\n {change === 'removed' && visible ? (\n <del className=\"dishui-ai-diff-table__before\">{renderValue(cell, labels.empty)}</del>\n ) : change === 'added' && visible ? (\n <ins className=\"dishui-ai-diff-table__after\">{renderValue(cell, labels.empty)}</ins>\n ) : (\n renderValue(cell, labels.empty)\n )}\n </td>\n );\n })}\n {hasRowActions && (\n <td className=\"dishui-ai-diff-table__td dishui-ai-diff-table__td--actions\">\n {change && visible && (\n decision ? (\n <span className={cn('dishui-ai-diff-table__decision', `dishui-ai-diff-table__decision--${decision}`)}>\n {decision === 'accepted' ? <CheckIcon /> : <XIcon />}\n {labels[decision]}\n </span>\n ) : (\n <span className=\"dishui-ai-diff-table__row-actions\">\n {onAcceptRow && (\n <button type=\"button\" className=\"dishui-ai-diff-table__icon-btn\" aria-label={labels.accept} title={labels.accept} onClick={() => onAcceptRow(row)}>\n <CheckIcon />\n </button>\n )}\n {onRejectRow && (\n <button type=\"button\" className=\"dishui-ai-diff-table__icon-btn\" aria-label={labels.reject} title={labels.reject} onClick={() => onRejectRow(row)}>\n <XIcon />\n </button>\n )}\n </span>\n )\n )}\n </td>\n )}\n </tr>\n );\n })}\n </tbody>\n </table>\n </div>\n\n {hasFooter && (\n <div className=\"dishui-ai-diff-table__footer\">\n <span className=\"dishui-ai-diff-table__footer-meta\">{done ? autoSummary : 'Reviewing…'}</span>\n <span className=\"dishui-ai-diff-table__footer-actions\">\n {onRejectAll && (\n <Button type=\"button\" variant=\"secondary\" size=\"sm\" onClick={onRejectAll} disabled={!done}>\n {labels.rejectAll}\n </Button>\n )}\n {onAcceptAll && (\n <Button type=\"button\" variant=\"primary\" size=\"sm\" onClick={onAcceptAll} disabled={!done}>\n {labels.acceptAll}\n </Button>\n )}\n </span>\n </div>\n )}\n </div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../../../src/components/ai/FilterTable/index.tsx"],"sourcesContent":["import React, { useMemo, useState } from 'react';\nimport { cn } from '../../../utils/cn';\nimport Table from '../../Table';\nimport Skeleton from '../../Skeleton';\nimport './FilterTable.css';\n\nconst EMPTY_FILTERS: FilterTableFilter<never>[] = [];\n\nexport type FilterTableColumn<Row> = { key: string; label: React.ReactNode; render?: (row: Row) => React.ReactNode; value?: (row: Row) => string | number; align?: 'left' | 'right'; mono?: boolean };\nexport type FilterTableFilter<Row> = { id: string; label: string; predicate: (row: Row) => boolean };\nexport type FilterTableProps<Row extends { id: string }> = Omit<React.HTMLAttributes<HTMLDivElement>, 'children'> & { rows: Row[]; columns: FilterTableColumn<Row>[]; filters?: FilterTableFilter<Row>[]; value?: string; defaultValue?: string; onChange?: (id: string) => void; search?: string; defaultSearch?: string; onSearchChange?: (query: string) => void; searchPlaceholder?: string; emptyText?: React.ReactNode; noResultsText?: React.ReactNode; caption?: React.ReactNode; loading?: boolean };\n\nexport default function FilterTable<Row extends { id: string }>({ rows, columns, filters = EMPTY_FILTERS as FilterTableFilter<Row>[], value, defaultValue = 'all', onChange, search, defaultSearch = '', onSearchChange, searchPlaceholder = 'Filter records…', emptyText = 'No records yet', noResultsText = 'No matching records', caption, loading = false, className, ...rest }: FilterTableProps<Row>) {\n const [internalFilter, setInternalFilter] = useState(defaultValue); const [internalSearch, setInternalSearch] = useState(defaultSearch); const active = value ?? internalFilter; const query = search ?? internalSearch;\n const shown = useMemo(() => rows.filter(row => { const filter = filters.find(x => x.id === active); if (filter && !filter.predicate(row)) return false; if (!query.trim()) return true; return columns.some(col => String(col.value?.(row) ?? (row as Record<string, unknown>)[col.key] ?? '').toLowerCase().includes(query.trim().toLowerCase())); }), [rows, columns, filters, active, query]);\n const setFilter = (id: string) => { if (value === undefined) setInternalFilter(id); onChange?.(id); };\n const setSearch = (next: string) => { if (search === undefined) setInternalSearch(next); onSearchChange?.(next); };\n return <div {...rest} className={cn('dishui-ai-filter-table', className)} aria-busy={loading || undefined}><div className=\"dishui-ai-filter-table__toolbar\"><div className=\"dishui-ai-filter-table__filters\" role=\"group\" aria-label=\"Table filters\"><button type=\"button\" aria-pressed={active === 'all'} onClick={() => setFilter('all')}>All <span>{rows.length}</span></button>{filters.map(filter => <button key={filter.id} type=\"button\" aria-pressed={active === filter.id} onClick={() => setFilter(filter.id)}>{filter.label} <span>{rows.filter(filter.predicate).length}</span></button>)}</div><input type=\"search\" aria-label={searchPlaceholder} placeholder={searchPlaceholder} value={query} onChange={e => setSearch(e.target.value)}/></div><div className=\"dishui-ai-filter-table__scroll\"><Table>{caption && <caption>{caption}</caption>}<Table.Head><Table.Row>{columns.map(col => <Table.HeaderCell key={col.key} className={col.align === 'right' ? 'dishui-ai-filter-table__right' : undefined}>{col.label}</Table.HeaderCell>)}</Table.Row></Table.Head><Table.Body>{loading ? ['one', 'two', 'three'].map(rowKey => <Table.Row key={rowKey} aria-hidden=\"true\">{columns.map(col => <Table.Cell key={col.key}><Skeleton className=\"dishui-ai-filter-table__skeleton\" /></Table.Cell>)}</Table.Row>) : shown.map(row => <Table.Row key={row.id}>{columns.map(col => <Table.Cell key={col.key} className={cn(col.align === 'right' && 'dishui-ai-filter-table__right', col.mono && 'dishui-ai-filter-table__mono')}>{col.render ? col.render(row) : String(col.value?.(row) ?? (row as Record<string, unknown>)[col.key] ?? '—')}</Table.Cell>)}</Table.Row>)}{!loading && shown.length === 0 && <Table.Row><Table.Cell className=\"dishui-ai-filter-table__empty\" colSpan={columns.length}>{rows.length ? noResultsText : emptyText}</Table.Cell></Table.Row>}</Table.Body></Table></div></div>;\n}\n"],"mappings":";;;;;;;AAMA,
|
|
1
|
+
{"version":3,"file":"index.js","names":["Table","TableWithCompounds","Skeleton","EMPTY_FILTERS","FilterTable","rest","internalFilter","setInternalFilter","useState","defaultValue","internalSearch","setInternalSearch","defaultSearch","active","value","query","search","shown","useMemo","rows","filter","filters","x","row","columns","col","setFilter","id","onChange","setSearch","next","onSearchChange","cn","className","loading","searchPlaceholder","caption","rowKey","noResultsText","emptyText"],"sources":["../../../../src/components/ai/FilterTable/index.tsx"],"sourcesContent":["import React, { useMemo, useState } from 'react';\nimport { cn } from '../../../utils/cn';\nimport Table from '../../Table';\nimport Skeleton from '../../Skeleton';\nimport './FilterTable.css';\n\nconst EMPTY_FILTERS: FilterTableFilter<never>[] = [];\n\nexport type FilterTableColumn<Row> = { key: string; label: React.ReactNode; render?: (row: Row) => React.ReactNode; value?: (row: Row) => string | number; align?: 'left' | 'right'; mono?: boolean };\nexport type FilterTableFilter<Row> = { id: string; label: string; predicate: (row: Row) => boolean };\nexport type FilterTableProps<Row extends { id: string }> = Omit<React.HTMLAttributes<HTMLDivElement>, 'children'> & { rows: Row[]; columns: FilterTableColumn<Row>[]; filters?: FilterTableFilter<Row>[]; value?: string; defaultValue?: string; onChange?: (id: string) => void; search?: string; defaultSearch?: string; onSearchChange?: (query: string) => void; searchPlaceholder?: string; emptyText?: React.ReactNode; noResultsText?: React.ReactNode; caption?: React.ReactNode; loading?: boolean };\n\nexport default function FilterTable<Row extends { id: string }>({ rows, columns, filters = EMPTY_FILTERS as FilterTableFilter<Row>[], value, defaultValue = 'all', onChange, search, defaultSearch = '', onSearchChange, searchPlaceholder = 'Filter records…', emptyText = 'No records yet', noResultsText = 'No matching records', caption, loading = false, className, ...rest }: FilterTableProps<Row>) {\n const [internalFilter, setInternalFilter] = useState(defaultValue); const [internalSearch, setInternalSearch] = useState(defaultSearch); const active = value ?? internalFilter; const query = search ?? internalSearch;\n const shown = useMemo(() => rows.filter(row => { const filter = filters.find(x => x.id === active); if (filter && !filter.predicate(row)) return false; if (!query.trim()) return true; return columns.some(col => String(col.value?.(row) ?? (row as Record<string, unknown>)[col.key] ?? '').toLowerCase().includes(query.trim().toLowerCase())); }), [rows, columns, filters, active, query]);\n const setFilter = (id: string) => { if (value === undefined) setInternalFilter(id); onChange?.(id); };\n const setSearch = (next: string) => { if (search === undefined) setInternalSearch(next); onSearchChange?.(next); };\n return <div {...rest} className={cn('dishui-ai-filter-table', className)} aria-busy={loading || undefined}><div className=\"dishui-ai-filter-table__toolbar\"><div className=\"dishui-ai-filter-table__filters\" role=\"group\" aria-label=\"Table filters\"><button type=\"button\" aria-pressed={active === 'all'} onClick={() => setFilter('all')}>All <span>{rows.length}</span></button>{filters.map(filter => <button key={filter.id} type=\"button\" aria-pressed={active === filter.id} onClick={() => setFilter(filter.id)}>{filter.label} <span>{rows.filter(filter.predicate).length}</span></button>)}</div><input type=\"search\" aria-label={searchPlaceholder} placeholder={searchPlaceholder} value={query} onChange={e => setSearch(e.target.value)}/></div><div className=\"dishui-ai-filter-table__scroll\"><Table>{caption && <caption>{caption}</caption>}<Table.Head><Table.Row>{columns.map(col => <Table.HeaderCell key={col.key} className={col.align === 'right' ? 'dishui-ai-filter-table__right' : undefined}>{col.label}</Table.HeaderCell>)}</Table.Row></Table.Head><Table.Body>{loading ? ['one', 'two', 'three'].map(rowKey => <Table.Row key={rowKey} aria-hidden=\"true\">{columns.map(col => <Table.Cell key={col.key}><Skeleton className=\"dishui-ai-filter-table__skeleton\" /></Table.Cell>)}</Table.Row>) : shown.map(row => <Table.Row key={row.id}>{columns.map(col => <Table.Cell key={col.key} className={cn(col.align === 'right' && 'dishui-ai-filter-table__right', col.mono && 'dishui-ai-filter-table__mono')}>{col.render ? col.render(row) : String(col.value?.(row) ?? (row as Record<string, unknown>)[col.key] ?? '—')}</Table.Cell>)}</Table.Row>)}{!loading && shown.length === 0 && <Table.Row><Table.Cell className=\"dishui-ai-filter-table__empty\" colSpan={columns.length}>{rows.length ? noResultsText : emptyText}</Table.Cell></Table.Row>}</Table.Body></Table></div></div>;\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../../../src/components/ai/FineTuneCard/index.tsx"],"sourcesContent":["import React from 'react';\nimport { cn } from '../../../utils/cn';\nimport Button from '../../Button';\nimport { clamp } from '../shared';\nimport './FineTuneCard.css';\n\nexport type FineTuneMetric = {\n label: React.ReactNode;\n value: React.ReactNode;\n};\n\nexport type FineTuneCardProps = Omit<React.HTMLAttributes<HTMLDivElement>, 'title'> & {\n title: React.ReactNode;\n model?: string;\n description?: React.ReactNode;\n status?: 'draft' | 'queued' | 'training' | 'ready' | 'failed';\n progress?: number;\n metrics?: FineTuneMetric[];\n error?: React.ReactNode;\n onStart?: () => void;\n onCancel?: () => void;\n onRetry?: () => void;\n disabled?: boolean;\n labels?: { start?: string; cancel?: string; retry?: string };\n};\n\nconst STATUS = {\n draft: 'Draft',\n queued: 'Queued',\n training: 'Training',\n ready: 'Ready',\n failed: 'Failed',\n} as const;\nconst EMPTY_METRICS: FineTuneMetric[] = [];\n\nexport default function FineTuneCard({\n title,\n model,\n description,\n status = 'draft',\n progress = 0,\n metrics = EMPTY_METRICS,\n error,\n onStart,\n onCancel,\n onRetry,\n disabled = false,\n labels,\n className,\n ...rest\n}: FineTuneCardProps) {\n const pct = Math.round(clamp(progress, 0, 1) * 100);\n const active = status === 'queued' || status === 'training';\n\n return (\n <article {...rest} className={cn('dishui-ai-fine-tune-card', `dishui-ai-fine-tune-card--${status}`, className)}>\n <header>\n <div>\n <h3>{title}</h3>\n {model && <code>{model}</code>}\n </div>\n <span className=\"dishui-ai-fine-tune-card__status\">{STATUS[status]}</span>\n </header>\n\n {description && <div className=\"dishui-ai-fine-tune-card__description\">{description}</div>}\n\n {active && (\n <div className=\"dishui-ai-fine-tune-card__progress\">\n <div><span>Training progress</span><strong>{pct}%</strong></div>\n <progress max={100} value={pct}>{pct}%</progress>\n </div>\n )}\n\n {metrics.length > 0 && (\n <dl>\n {metrics.map(metric => (\n <div key={`${String(metric.label)}-${String(metric.value)}`}>\n <dt>{metric.label}</dt>\n <dd>{metric.value}</dd>\n </div>\n ))}\n </dl>\n )}\n\n {status === 'failed' && error && (\n <div role=\"alert\" className=\"dishui-ai-fine-tune-card__error\">{error}</div>\n )}\n\n <footer>\n {status === 'draft' && onStart && (\n <Button type=\"button\" variant=\"primary\" size=\"sm\" disabled={disabled} onClick={onStart}>\n {labels?.start ?? 'Start training'}\n </Button>\n )}\n {active && onCancel && (\n <Button type=\"button\" variant=\"secondary\" size=\"sm\" disabled={disabled} onClick={onCancel}>\n {labels?.cancel ?? 'Cancel'}\n </Button>\n )}\n {status === 'failed' && onRetry && (\n <Button type=\"button\" variant=\"primary\" size=\"sm\" disabled={disabled} onClick={onRetry}>\n {labels?.retry ?? 'Retry'}\n </Button>\n )}\n </footer>\n </article>\n );\n}\n"],"mappings":";;;;;;;AA0BA,
|
|
1
|
+
{"version":3,"file":"index.js","names":["Button","ButtonWithCompounds","STATUS","EMPTY_METRICS","FineTuneCard","rest","pct","clamp","progress","active","status","cn","className","title","model","description","metrics","metric","error","onStart","labels","onCancel","onRetry"],"sources":["../../../../src/components/ai/FineTuneCard/index.tsx"],"sourcesContent":["import React from 'react';\nimport { cn } from '../../../utils/cn';\nimport Button from '../../Button';\nimport { clamp } from '../shared';\nimport './FineTuneCard.css';\n\nexport type FineTuneMetric = {\n label: React.ReactNode;\n value: React.ReactNode;\n};\n\nexport type FineTuneCardProps = Omit<React.HTMLAttributes<HTMLDivElement>, 'title'> & {\n title: React.ReactNode;\n model?: string;\n description?: React.ReactNode;\n status?: 'draft' | 'queued' | 'training' | 'ready' | 'failed';\n progress?: number;\n metrics?: FineTuneMetric[];\n error?: React.ReactNode;\n onStart?: () => void;\n onCancel?: () => void;\n onRetry?: () => void;\n disabled?: boolean;\n labels?: { start?: string; cancel?: string; retry?: string };\n};\n\nconst STATUS = {\n draft: 'Draft',\n queued: 'Queued',\n training: 'Training',\n ready: 'Ready',\n failed: 'Failed',\n} as const;\nconst EMPTY_METRICS: FineTuneMetric[] = [];\n\nexport default function FineTuneCard({\n title,\n model,\n description,\n status = 'draft',\n progress = 0,\n metrics = EMPTY_METRICS,\n error,\n onStart,\n onCancel,\n onRetry,\n disabled = false,\n labels,\n className,\n ...rest\n}: FineTuneCardProps) {\n const pct = Math.round(clamp(progress, 0, 1) * 100);\n const active = status === 'queued' || status === 'training';\n\n return (\n <article {...rest} className={cn('dishui-ai-fine-tune-card', `dishui-ai-fine-tune-card--${status}`, className)}>\n <header>\n <div>\n <h3>{title}</h3>\n {model && <code>{model}</code>}\n </div>\n <span className=\"dishui-ai-fine-tune-card__status\">{STATUS[status]}</span>\n </header>\n\n {description && <div className=\"dishui-ai-fine-tune-card__description\">{description}</div>}\n\n {active && (\n <div className=\"dishui-ai-fine-tune-card__progress\">\n <div><span>Training progress</span><strong>{pct}%</strong></div>\n <progress max={100} value={pct}>{pct}%</progress>\n </div>\n )}\n\n {metrics.length > 0 && (\n <dl>\n {metrics.map(metric => (\n <div key={`${String(metric.label)}-${String(metric.value)}`}>\n <dt>{metric.label}</dt>\n <dd>{metric.value}</dd>\n </div>\n ))}\n </dl>\n )}\n\n {status === 'failed' && error && (\n <div role=\"alert\" className=\"dishui-ai-fine-tune-card__error\">{error}</div>\n )}\n\n <footer>\n {status === 'draft' && onStart && (\n <Button type=\"button\" variant=\"primary\" size=\"sm\" disabled={disabled} onClick={onStart}>\n {labels?.start ?? 'Start training'}\n </Button>\n )}\n {active && onCancel && (\n <Button type=\"button\" variant=\"secondary\" size=\"sm\" disabled={disabled} onClick={onCancel}>\n {labels?.cancel ?? 'Cancel'}\n </Button>\n )}\n {status === 'failed' && onRetry && (\n <Button type=\"button\" variant=\"primary\" size=\"sm\" disabled={disabled} onClick={onRetry}>\n {labels?.retry ?? 'Retry'}\n </Button>\n )}\n </footer>\n </article>\n );\n}\n"],"mappings":";;;;;;;AA0BA,IAAME,IAAS;CACb,OAAO;CACP,QAAQ;CACR,UAAU;CACV,OAAO;CACP,QAAQ;AACV,GACMC,IAAkC,CAAC;AAEzC,SAAwBC,EAAa,EACnC,UACA,UACA,gBACA,YAAS,SACT,cAAW,GACX,aAAUD,GACV,UACA,YACA,aACA,YACA,cAAW,IACX,WACA,cACA,GAAGE,KACiB;CACpB,IAAMC,IAAM,KAAK,MAAMC,EAAMC,GAAU,GAAG,CAAC,IAAI,GAAG,GAC5CC,IAASC,MAAW,YAAYA,MAAW;CAEjD,OACE,kBAAC,WAAD;EAAS,GAAIL;EAAM,WAAWM,EAAG,4BAA4B,6BAA6BD,KAAUE,CAAS;EAA7G,UAAA;GACE,kBAAC,UAAD,EAAA,UAAA,CACE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,MAAD,EAAA,UAAKC,EAAU,CAAA,GACdC,KAAS,kBAAC,QAAD,EAAA,UAAOA,EAAY,CAAA,CAC1B,EAAA,CAAA,GACL,kBAAC,QAAD;IAAM,WAAU;IAAoC,UAAAZ,EAAOQ;GAAc,CAAA,CACnE,EAAA,CAAA;GAEPK,KAAe,kBAAC,OAAD;IAAK,WAAU;IAAyC,UAAAA;GAAiB,CAAA;GAExFN,KACC,kBAAC,OAAD;IAAK,WAAU;IAAf,UAAA,CACE,kBAAC,OAAD,EAAA,UAAA,CAAK,kBAAC,QAAD,EAAA,UAAM,oBAAuB,CAAA,GAAC,kBAAC,UAAD,EAAA,UAAA,CAASH,GAAI,GAAS,EAAA,CAAA,CAAM,EAAA,CAAA,GAC/D,kBAAC,YAAD;KAAU,KAAK;KAAK,OAAOA;KAA3B,UAAA,CAAiCA,GAAI,GAAW;IAC7C,CAAA,CAAA;;GAGNU,EAAQ,SAAS,KAChB,kBAAC,MAAD,EAAA,UACGA,EAAQ,KAAI,MACX,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,MAAD,EAAA,UAAKC,EAAO,MAAU,CAAA,GACtB,kBAAC,MAAD,EAAA,UAAKA,EAAO,MAAU,CAAA,CACnB,EAAA,GAHK,GAAG,OAAOA,EAAO,KAAK,EAAE,GAAG,OAAOA,EAAO,KAAK,GAGnD,CACN,EACC,CAAA;GAGLP,MAAW,YAAYQ,KACtB,kBAAC,OAAD;IAAK,MAAK;IAAQ,WAAU;IAAmC,UAAAA;GAAW,CAAA;GAG5E,kBAAC,UAAD,EAAA,UAAA;IACGR,MAAW,WAAWS,KACrB,kBAACnB,GAAD;KAAQ,MAAK;KAAS,SAAQ;KAAU,MAAK;KAAe;KAAU,SAASmB;KAC5E,UAAAC,GAAQ,SAAS;IACZ,CAAA;IAETX,KAAUY,KACT,kBAACrB,GAAD;KAAQ,MAAK;KAAS,SAAQ;KAAY,MAAK;KAAe;KAAU,SAASqB;KAC9E,UAAAD,GAAQ,UAAU;IACb,CAAA;IAETV,MAAW,YAAYY,KACtB,kBAACtB,GAAD;KAAQ,MAAK;KAAS,SAAQ;KAAU,MAAK;KAAe;KAAU,SAASsB;KAC5E,UAAAF,GAAQ,SAAS;IACZ,CAAA;GAEJ,EAAA,CAAA;EACD;;AAEb"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../../../src/components/ai/InsightCards/index.tsx"],"sourcesContent":["import React, { useState } from 'react';\nimport { cn } from '../../../utils/cn';\nimport './InsightCards.css';\n\nexport type InsightCardItem = { id: string; label: React.ReactNode; value: React.ReactNode; detail?: React.ReactNode; trend?: { value: string; direction: 'up' | 'down' | 'flat'; label?: string }; status?: 'neutral' | 'success' | 'warning' | 'danger' | 'info' };\nexport type InsightCardsProps = Omit<React.HTMLAttributes<HTMLDivElement>, 'children' | 'onSelect'> & { items: InsightCardItem[]; columns?: 2 | 3 | 4; selectedId?: string; defaultSelectedId?: string; onSelect?: (item: InsightCardItem) => void; loading?: boolean; emptyText?: React.ReactNode };\n\nexport default function InsightCards({ items, columns = 3, selectedId, defaultSelectedId, onSelect, loading = false, emptyText = 'No insights available', className, ...rest }: InsightCardsProps) {\n const [internal, setInternal] = useState(defaultSelectedId); const selected = selectedId ?? internal;\n const select = (item: InsightCardItem) => { if (selectedId === undefined) setInternal(item.id); onSelect?.(item); };\n if (!loading && !items.length) return <div {...rest} className={cn('dishui-ai-insight-cards dishui-ai-insight-cards--empty', className)}>{emptyText}</div>;\n const content = loading ? Array.from({ length: columns }, (_, i) => ({ id: `loading-${i}`, label: '', value: '' } as InsightCardItem)) : items;\n return <div {...rest} className={cn('dishui-ai-insight-cards', `dishui-ai-insight-cards--${columns}`, className)}>{content.map(item => { const body = <><span className=\"dishui-ai-insight-cards__label\">{item.label}</span><strong className=\"dishui-ai-insight-cards__value\">{item.value}</strong>{item.trend && <span className={cn('dishui-ai-insight-cards__trend', `dishui-ai-insight-cards__trend--${item.trend.direction}`)}>{item.trend.direction === 'up' ? '↑' : item.trend.direction === 'down' ? '↓' : '→'} {item.trend.value}{item.trend.label && <span> {item.trend.label}</span>}</span>}{item.detail && <span className=\"dishui-ai-insight-cards__detail\">{item.detail}</span>}{loading && <><span className=\"dishui-ai-insight-cards__skeleton\"/><span className=\"dishui-ai-insight-cards__skeleton dishui-ai-insight-cards__skeleton--short\"/></>}</>; return onSelect ? <button type=\"button\" key={item.id} className={cn('dishui-ai-insight-cards__card', item.status && `dishui-ai-insight-cards__card--${item.status}`, selected === item.id && 'dishui-ai-insight-cards__card--selected')} aria-pressed={selected === item.id} onClick={() => select(item)}>{body}</button> : <article key={item.id} className={cn('dishui-ai-insight-cards__card', item.status && `dishui-ai-insight-cards__card--${item.status}`)}>{body}</article>; })}</div>;\n}\n"],"mappings":";;;;;AAOA,
|
|
1
|
+
{"version":3,"file":"index.js","names":["_Fragment","InsightCards","rest","internal","setInternal","useState","defaultSelectedId","selected","selectedId","select","item","onSelect","loading","items","cn","className","emptyText","content","columns","i","body","Fragment"],"sources":["../../../../src/components/ai/InsightCards/index.tsx"],"sourcesContent":["import React, { useState } from 'react';\nimport { cn } from '../../../utils/cn';\nimport './InsightCards.css';\n\nexport type InsightCardItem = { id: string; label: React.ReactNode; value: React.ReactNode; detail?: React.ReactNode; trend?: { value: string; direction: 'up' | 'down' | 'flat'; label?: string }; status?: 'neutral' | 'success' | 'warning' | 'danger' | 'info' };\nexport type InsightCardsProps = Omit<React.HTMLAttributes<HTMLDivElement>, 'children' | 'onSelect'> & { items: InsightCardItem[]; columns?: 2 | 3 | 4; selectedId?: string; defaultSelectedId?: string; onSelect?: (item: InsightCardItem) => void; loading?: boolean; emptyText?: React.ReactNode };\n\nexport default function InsightCards({ items, columns = 3, selectedId, defaultSelectedId, onSelect, loading = false, emptyText = 'No insights available', className, ...rest }: InsightCardsProps) {\n const [internal, setInternal] = useState(defaultSelectedId); const selected = selectedId ?? internal;\n const select = (item: InsightCardItem) => { if (selectedId === undefined) setInternal(item.id); onSelect?.(item); };\n if (!loading && !items.length) return <div {...rest} className={cn('dishui-ai-insight-cards dishui-ai-insight-cards--empty', className)}>{emptyText}</div>;\n const content = loading ? Array.from({ length: columns }, (_, i) => ({ id: `loading-${i}`, label: '', value: '' } as InsightCardItem)) : items;\n return <div {...rest} className={cn('dishui-ai-insight-cards', `dishui-ai-insight-cards--${columns}`, className)}>{content.map(item => { const body = <><span className=\"dishui-ai-insight-cards__label\">{item.label}</span><strong className=\"dishui-ai-insight-cards__value\">{item.value}</strong>{item.trend && <span className={cn('dishui-ai-insight-cards__trend', `dishui-ai-insight-cards__trend--${item.trend.direction}`)}>{item.trend.direction === 'up' ? '↑' : item.trend.direction === 'down' ? '↓' : '→'} {item.trend.value}{item.trend.label && <span> {item.trend.label}</span>}</span>}{item.detail && <span className=\"dishui-ai-insight-cards__detail\">{item.detail}</span>}{loading && <><span className=\"dishui-ai-insight-cards__skeleton\"/><span className=\"dishui-ai-insight-cards__skeleton dishui-ai-insight-cards__skeleton--short\"/></>}</>; return onSelect ? <button type=\"button\" key={item.id} className={cn('dishui-ai-insight-cards__card', item.status && `dishui-ai-insight-cards__card--${item.status}`, selected === item.id && 'dishui-ai-insight-cards__card--selected')} aria-pressed={selected === item.id} onClick={() => select(item)}>{body}</button> : <article key={item.id} className={cn('dishui-ai-insight-cards__card', item.status && `dishui-ai-insight-cards__card--${item.status}`)}>{body}</article>; })}</div>;\n}\n"],"mappings":";;;;;AAOA,SAAwBC,EAAa,EAAE,UAAO,aAAU,GAAG,eAAY,sBAAmB,aAAU,aAAU,IAAO,eAAY,yBAAyB,cAAW,GAAGC,KAA2B;CACjM,IAAM,CAACC,GAAUC,KAAeC,EAASC,CAAiB,GAASC,IAAWC,KAAcL,GACtFM,KAAU,MAA0B;EAAsD,AAAhDD,MAAe,KAAA,KAAWJ,EAAYM,EAAK,EAAE,GAAGC,IAAWD,CAAI;CAAG;CAClH,IAAI,CAACE,KAAW,CAACC,EAAM,QAAQ,OAAO,kBAAC,OAAD;EAAK,GAAIX;EAAM,WAAWY,EAAG,0DAA0DC,CAAS;EAAI,UAAAC;CAAe,CAAA;CACzJ,IAAMC,IAAUL,IAAU,MAAM,KAAK,EAAE,QAAQM,EAAQ,IAAI,GAAG,OAAO;EAAE,IAAI,WAAWC;EAAK,OAAO;EAAI,OAAO;CAAG,EAAqB,IAAIN;CACzI,OAAO,kBAAC,OAAD;EAAK,GAAIX;EAAM,WAAWY,EAAG,2BAA2B,4BAA4BI,KAAWH,CAAS;EAAI,UAAAE,EAAQ,KAAI,MAAQ;GAAE,IAAMG,IAAO,kBAAApB,GAAA,EAAA,UAAA;IAAE,kBAAC,QAAD;KAAM,WAAU;KAAkC,UAAAU,EAAK;IAAY,CAAA;IAAC,kBAAC,UAAD;KAAQ,WAAU;KAAkC,UAAAA,EAAK;IAAc,CAAA;IAAEA,EAAK,SAAS,kBAAC,QAAD;KAAM,WAAWI,EAAG,kCAAkC,mCAAmCJ,EAAK,MAAM,WAAW;KAA/G,UAAA;MAAmHA,EAAK,MAAM,cAAc,OAAO,MAAMA,EAAK,MAAM,cAAc,SAAS,MAAM;MAAI;MAAEA,EAAK,MAAM;MAAOA,EAAK,MAAM,SAAS,kBAAC,QAAD,EAAA,UAAA,CAAM,KAAEA,EAAK,MAAM,KAAY,EAAA,CAAA;KAAQ;;IAAGA,EAAK,UAAU,kBAAC,QAAD;KAAM,WAAU;KAAmC,UAAAA,EAAK;IAAa,CAAA;IAAGE,KAAW,kBAAAZ,GAAA,EAAA,UAAA,CAAE,kBAAC,QAAD,EAAM,WAAU,oCAAoC,CAAA,GAAC,kBAAC,QAAD,EAAM,WAAU,6EAA6E,CAAA,CAAG,EAAA,CAAA;GAAI,EAAA,CAAA;GAAG,OAAOW,IAAW,kBAAC,UAAD;IAAQ,MAAK;IAAuB,WAAWG,EAAG,iCAAiCJ,EAAK,UAAU,kCAAkCA,EAAK,UAAUH,MAAaG,EAAK,MAAM,yCAAyC;IAAG,gBAAcH,MAAaG,EAAK;IAAI,eAAeD,EAAOC,CAAI;IAAI,UAAAU;GAAa,GAA3QV,EAAK,EAAsQ,IAAI,kBAAC,WAAD;IAAuB,WAAWI,EAAG,iCAAiCJ,EAAK,UAAU,kCAAkCA,EAAK,QAAQ;IAAI,UAAAU;GAAc,GAAxIV,EAAK,EAAmI;EAAG,CAAC;CAAO,CAAA;AACzyC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../../../src/components/ai/IssueCard/index.tsx"],"sourcesContent":["import React from 'react';\nimport { cn } from '../../../utils/cn';\nimport './IssueCard.css';\n\n/** Priority tone drives the small trailing dot beside the identifier. */\nexport type IssuePriority = 'none' | 'low' | 'medium' | 'high' | 'urgent';\n\n/** Accent stripe on the leading edge of the card. */\nexport type IssueAccent = 'none' | 'neutral' | 'info' | 'success' | 'warning' | 'danger';\n\nexport type IssueLabel = {\n id?: string;\n text: React.ReactNode;\n};\n\nexport type IssueCardProps = Omit<React.HTMLAttributes<HTMLDivElement>, 'title' | 'onSelect'> & {\n /** Primary issue title. */\n title: React.ReactNode;\n /** Short labels/tags shown below the title (e.g. `ui`, `infra`). */\n labels?: Array<IssueLabel | string>;\n /** Leading icon slot (e.g. issue type glyph). Style the box via `iconTone`. */\n icon?: React.ReactNode;\n /** Colour of the leading icon box. */\n iconTone?: 'neutral' | 'info' | 'success' | 'warning' | 'danger';\n /** Human-facing identifier, rendered in a monospace, tabular style (e.g. `FCE-38`). */\n identifier?: React.ReactNode;\n /** Priority drives the coloured dot next to the identifier. */\n priority?: IssuePriority;\n /** Numeric badge on the trailing edge (e.g. estimate/points). */\n count?: React.ReactNode;\n /** Assignee slot, typically an `Avatar`. */\n assignee?: React.ReactNode;\n /** Coloured stripe on the leading edge. Defaults to `none`. */\n accent?: IssueAccent;\n /** Selected state (raises border + subtle surface shift). */\n selected?: boolean;\n /** Click handler turns the card into a button for keyboard users. */\n onSelect?: () => void;\n};\n\nconst PRIORITY_LABEL: Record<IssuePriority, string> = {\n none: 'No priority',\n low: 'Low priority',\n medium: 'Medium priority',\n high: 'High priority',\n urgent: 'Urgent priority',\n};\n\nconst EMPTY_LABELS: Array<IssueLabel | string> = [];\n\nconst toLabel = (label: IssueLabel | string, index: number): IssueLabel & { key: string } => {\n if (typeof label === 'string') return { text: label, key: `${label}-${index}` };\n return { ...label, key: label.id ?? `${String(label.text)}-${index}` };\n};\n\n/**\n * Linear-style issue card: an optional accent stripe, a title, small labels,\n * and a footer that pairs a type icon, identifier, a priority dot, an estimate\n * and an assignee. Status is always carried by text/icon, never colour alone.\n */\nexport default function IssueCard({\n title,\n labels = EMPTY_LABELS,\n icon,\n iconTone = 'neutral',\n identifier,\n priority = 'none',\n count,\n assignee,\n accent = 'none',\n selected = false,\n onSelect,\n className,\n ...rest\n}: IssueCardProps) {\n const interactive = !!onSelect;\n const items = labels.map(toLabel);\n\n return (\n <div\n {...rest}\n className={cn(\n 'dishui-ai-issue-card',\n accent !== 'none' && `dishui-ai-issue-card--accent-${accent}`,\n selected && 'dishui-ai-issue-card--selected',\n interactive && 'dishui-ai-issue-card--interactive',\n className,\n )}\n role={interactive ? 'button' : undefined}\n tabIndex={interactive ? 0 : undefined}\n aria-pressed={interactive ? selected : undefined}\n onClick={interactive ? onSelect : rest.onClick}\n onKeyDown={interactive ? (event) => {\n if (event.target !== event.currentTarget) return;\n if (event.key === 'Enter' || event.key === ' ') {\n event.preventDefault();\n if (!event.repeat) onSelect?.();\n }\n } : undefined}\n >\n <h3 className=\"dishui-ai-issue-card__title\">{title}</h3>\n\n {items.length > 0 && (\n <div className=\"dishui-ai-issue-card__labels\">\n {items.map(label => (\n <span key={label.key} className=\"dishui-ai-issue-card__label\">{label.text}</span>\n ))}\n </div>\n )}\n\n <div className=\"dishui-ai-issue-card__footer\">\n {icon !== null && icon !== undefined && (\n <span className={cn('dishui-ai-issue-card__icon', `dishui-ai-issue-card__icon--${iconTone}`)} aria-hidden=\"true\">\n {icon}\n </span>\n )}\n {identifier !== null && identifier !== undefined && (\n <span className=\"dishui-ai-issue-card__id\">{identifier}</span>\n )}\n {priority !== 'none' && (\n <span\n className={cn('dishui-ai-issue-card__priority', `dishui-ai-issue-card__priority--${priority}`)}\n role=\"img\"\n aria-label={PRIORITY_LABEL[priority]}\n title={PRIORITY_LABEL[priority]}\n />\n )}\n <span className=\"dishui-ai-issue-card__spacer\" />\n {count !== null && count !== undefined && (\n <span className=\"dishui-ai-issue-card__count\">{count}</span>\n )}\n {assignee !== null && assignee !== undefined && (\n <span className=\"dishui-ai-issue-card__assignee\">{assignee}</span>\n )}\n </div>\n </div>\n );\n}\n"],"mappings":";;;;;AAwCA,
|
|
1
|
+
{"version":3,"file":"index.js","names":["PRIORITY_LABEL","EMPTY_LABELS","toLabel","label","index","IssueCard","rest","interactive","onSelect","items","labels","cn","accent","selected","className","event","title","icon","iconTone","identifier","priority","count","assignee"],"sources":["../../../../src/components/ai/IssueCard/index.tsx"],"sourcesContent":["import React from 'react';\nimport { cn } from '../../../utils/cn';\nimport './IssueCard.css';\n\n/** Priority tone drives the small trailing dot beside the identifier. */\nexport type IssuePriority = 'none' | 'low' | 'medium' | 'high' | 'urgent';\n\n/** Accent stripe on the leading edge of the card. */\nexport type IssueAccent = 'none' | 'neutral' | 'info' | 'success' | 'warning' | 'danger';\n\nexport type IssueLabel = {\n id?: string;\n text: React.ReactNode;\n};\n\nexport type IssueCardProps = Omit<React.HTMLAttributes<HTMLDivElement>, 'title' | 'onSelect'> & {\n /** Primary issue title. */\n title: React.ReactNode;\n /** Short labels/tags shown below the title (e.g. `ui`, `infra`). */\n labels?: Array<IssueLabel | string>;\n /** Leading icon slot (e.g. issue type glyph). Style the box via `iconTone`. */\n icon?: React.ReactNode;\n /** Colour of the leading icon box. */\n iconTone?: 'neutral' | 'info' | 'success' | 'warning' | 'danger';\n /** Human-facing identifier, rendered in a monospace, tabular style (e.g. `FCE-38`). */\n identifier?: React.ReactNode;\n /** Priority drives the coloured dot next to the identifier. */\n priority?: IssuePriority;\n /** Numeric badge on the trailing edge (e.g. estimate/points). */\n count?: React.ReactNode;\n /** Assignee slot, typically an `Avatar`. */\n assignee?: React.ReactNode;\n /** Coloured stripe on the leading edge. Defaults to `none`. */\n accent?: IssueAccent;\n /** Selected state (raises border + subtle surface shift). */\n selected?: boolean;\n /** Click handler turns the card into a button for keyboard users. */\n onSelect?: () => void;\n};\n\nconst PRIORITY_LABEL: Record<IssuePriority, string> = {\n none: 'No priority',\n low: 'Low priority',\n medium: 'Medium priority',\n high: 'High priority',\n urgent: 'Urgent priority',\n};\n\nconst EMPTY_LABELS: Array<IssueLabel | string> = [];\n\nconst toLabel = (label: IssueLabel | string, index: number): IssueLabel & { key: string } => {\n if (typeof label === 'string') return { text: label, key: `${label}-${index}` };\n return { ...label, key: label.id ?? `${String(label.text)}-${index}` };\n};\n\n/**\n * Linear-style issue card: an optional accent stripe, a title, small labels,\n * and a footer that pairs a type icon, identifier, a priority dot, an estimate\n * and an assignee. Status is always carried by text/icon, never colour alone.\n */\nexport default function IssueCard({\n title,\n labels = EMPTY_LABELS,\n icon,\n iconTone = 'neutral',\n identifier,\n priority = 'none',\n count,\n assignee,\n accent = 'none',\n selected = false,\n onSelect,\n className,\n ...rest\n}: IssueCardProps) {\n const interactive = !!onSelect;\n const items = labels.map(toLabel);\n\n return (\n <div\n {...rest}\n className={cn(\n 'dishui-ai-issue-card',\n accent !== 'none' && `dishui-ai-issue-card--accent-${accent}`,\n selected && 'dishui-ai-issue-card--selected',\n interactive && 'dishui-ai-issue-card--interactive',\n className,\n )}\n role={interactive ? 'button' : undefined}\n tabIndex={interactive ? 0 : undefined}\n aria-pressed={interactive ? selected : undefined}\n onClick={interactive ? onSelect : rest.onClick}\n onKeyDown={interactive ? (event) => {\n if (event.target !== event.currentTarget) return;\n if (event.key === 'Enter' || event.key === ' ') {\n event.preventDefault();\n if (!event.repeat) onSelect?.();\n }\n } : undefined}\n >\n <h3 className=\"dishui-ai-issue-card__title\">{title}</h3>\n\n {items.length > 0 && (\n <div className=\"dishui-ai-issue-card__labels\">\n {items.map(label => (\n <span key={label.key} className=\"dishui-ai-issue-card__label\">{label.text}</span>\n ))}\n </div>\n )}\n\n <div className=\"dishui-ai-issue-card__footer\">\n {icon !== null && icon !== undefined && (\n <span className={cn('dishui-ai-issue-card__icon', `dishui-ai-issue-card__icon--${iconTone}`)} aria-hidden=\"true\">\n {icon}\n </span>\n )}\n {identifier !== null && identifier !== undefined && (\n <span className=\"dishui-ai-issue-card__id\">{identifier}</span>\n )}\n {priority !== 'none' && (\n <span\n className={cn('dishui-ai-issue-card__priority', `dishui-ai-issue-card__priority--${priority}`)}\n role=\"img\"\n aria-label={PRIORITY_LABEL[priority]}\n title={PRIORITY_LABEL[priority]}\n />\n )}\n <span className=\"dishui-ai-issue-card__spacer\" />\n {count !== null && count !== undefined && (\n <span className=\"dishui-ai-issue-card__count\">{count}</span>\n )}\n {assignee !== null && assignee !== undefined && (\n <span className=\"dishui-ai-issue-card__assignee\">{assignee}</span>\n )}\n </div>\n </div>\n );\n}\n"],"mappings":";;;;;AAwCA,IAAMA,IAAgD;CACpD,MAAM;CACN,KAAK;CACL,QAAQ;CACR,MAAM;CACN,QAAQ;AACV,GAEMC,IAA2C,CAAC,GAE5CC,KAAW,GAA4B,MACvC,OAAOC,KAAU,WAAiB;CAAE,MAAMA;CAAO,KAAK,GAAGA,EAAM,GAAGC;AAAQ,IACvE;CAAE,GAAGD;CAAO,KAAKA,EAAM,MAAM,GAAG,OAAOA,EAAM,IAAI,EAAE,GAAGC;AAAQ;AAQvE,SAAwBC,EAAU,EAChC,UACA,YAASJ,GACT,SACA,cAAW,WACX,eACA,cAAW,QACX,UACA,aACA,YAAS,QACT,cAAW,IACX,aACA,cACA,GAAGK,KACc;CACjB,IAAMC,IAAc,CAAC,CAACC,GAChBC,IAAQC,EAAO,IAAIR,CAAO;CAEhC,OACE,kBAAC,OAAD;EACE,GAAII;EACJ,WAAWK,EACT,wBACAC,MAAW,UAAU,gCAAgCA,KACrDC,KAAY,kCACZN,KAAe,qCACfO,CACF;EACA,MAAMP,IAAc,WAAW,KAAA;EAC/B,UAAUA,IAAc,IAAI,KAAA;EAC5B,gBAAcA,IAAcM,IAAW,KAAA;EACvC,SAASN,IAAcC,IAAWF,EAAK;EACvC,WAAWC,KAAe,MAAU;GAClC,AAAIQ,EAAM,WAAWA,EAAM,kBACvBA,EAAM,QAAQ,WAAWA,EAAM,QAAQ,SACzCA,EAAM,eAAe,GAChBA,EAAM,UAAQP,IAAW;EAElC,IAAI,KAAA;EAnBN,UAAA;GAqBE,kBAAC,MAAD;IAAI,WAAU;IAA+B,UAAAQ;GAAU,CAAA;GAEtDP,EAAM,SAAS,KACd,kBAAC,OAAD;IAAK,WAAU;IACZ,UAAAA,EAAM,KAAI,MACT,kBAAC,QAAD;KAAsB,WAAU;KAA+B,UAAAN,EAAM;IAAW,GAArEA,EAAM,GAA+D,CACjF;GACE,CAAA;GAGP,kBAAC,OAAD;IAAK,WAAU;IAAf,UAAA;KACGc,KAAS,QACR,kBAAC,QAAD;MAAM,WAAWN,EAAG,8BAA8B,+BAA+BO,GAAU;MAAG,eAAY;MACvG,UAAAD;KACG,CAAA;KAEPE,KAAe,QACd,kBAAC,QAAD;MAAM,WAAU;MAA4B,UAAAA;KAAiB,CAAA;KAE9DC,MAAa,UACZ,kBAAC,QAAD;MACE,WAAWT,EAAG,kCAAkC,mCAAmCS,GAAU;MAC7F,MAAK;MACL,cAAYpB,EAAeoB;MAC3B,OAAOpB,EAAeoB;KACvB,CAAA;KAEH,kBAAC,QAAD,EAAM,WAAU,+BAAgC,CAAA;KAC/CC,KAAU,QACT,kBAAC,QAAD;MAAM,WAAU;MAA+B,UAAAA;KAAY,CAAA;KAE5DC,KAAa,QACZ,kBAAC,QAAD;MAAM,WAAU;MAAkC,UAAAA;KAAe,CAAA;IAEhE;;EACF;;AAET"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../../../src/components/ai/LoadingState/index.tsx"],"sourcesContent":["import React, { useEffect, useState } from 'react';\nimport { cn } from '../../../utils/cn';\nimport { formatElapsed } from '../shared';\nimport './LoadingState.css';\n\nexport type LoadingStateVariant = 'grid' | 'drive' | 'dots' | 'orbit' | 'surfer';\n\nexport type LoadingStateProps = Omit<React.HTMLAttributes<HTMLDivElement>, 'children'> & {\n /** Short present-tense verb shown next to the glyph, e.g. `Churning`, `Searching`. */\n label?: React.ReactNode;\n /** Glyph animation. All variants are indeterminate and respect reduced motion. */\n variant?: LoadingStateVariant;\n /** Show the elapsed time next to the label. */\n showElapsed?: boolean;\n /** Epoch milliseconds the work started; defaults to mount time. */\n startedAt?: number;\n /** Controlled elapsed time in milliseconds. When set, the internal timer is disabled. */\n elapsedMs?: number;\n /** `inline` sits in a sentence; `block` renders a bordered row. */\n layout?: 'inline' | 'block';\n size?: 'sm' | 'md';\n};\n\nconst index = (i: number) => ({ '--i': i } as React.CSSProperties);\n\nconst Glyph: React.FC<{ variant: LoadingStateVariant }> = ({ variant }) => {\n switch (variant) {\n case 'drive':\n return (\n <span className=\"dishui-ai-loading__drive\">\n {Array.from({ length: 4 }, (_, i) => <span key={i} className=\"dishui-ai-loading__cell\" style={index(i)} />)}\n </span>\n );\n case 'dots':\n return (\n <span className=\"dishui-ai-loading__dots\">\n {Array.from({ length: 3 }, (_, i) => <span key={i} className=\"dishui-ai-loading__dot\" style={index(i)} />)}\n </span>\n );\n case 'orbit':\n return (\n <>\n <span className=\"dishui-ai-loading__orbit\" />\n <span className=\"dishui-ai-loading__satellite\" />\n </>\n );\n case 'surfer':\n return (\n <span className=\"dishui-ai-loading__surfer\">\n {Array.from({ length: 5 }, (_, i) => <span key={i} className=\"dishui-ai-loading__bar\" style={index(i)} />)}\n </span>\n );\n default:\n return (\n <span className=\"dishui-ai-loading__grid\">\n {Array.from({ length: 16 }, (_, i) => {\n // Diagonal sweep: delay grows with row + column.\n const delay = Math.floor(i / 4) + (i % 4);\n return <span key={i} className=\"dishui-ai-loading__cell\" style={index(delay)} />;\n })}\n </span>\n );\n }\n};\n\n/**\n * Indeterminate agent progress: pixel-grid (or sibling) glyph, a short label\n * and the elapsed time. Announces as a polite status region.\n */\nexport default function LoadingState({\n label = 'Thinking',\n variant = 'grid',\n showElapsed = true,\n startedAt,\n elapsedMs,\n layout = 'inline',\n size = 'md',\n className,\n ...rest\n}: LoadingStateProps) {\n const [internalElapsed, setInternalElapsed] = useState(0);\n const controlled = elapsedMs !== undefined;\n\n useEffect(() => {\n if (controlled || !showElapsed) return;\n const start = startedAt ?? Date.now();\n const id = window.setInterval(() => setInternalElapsed(Date.now() - start), 100);\n return () => window.clearInterval(id);\n }, [controlled, showElapsed, startedAt]);\n\n const elapsed = controlled ? elapsedMs : internalElapsed;\n\n return (\n <div\n role=\"status\"\n aria-live=\"polite\"\n {...rest}\n className={cn(\n 'dishui-ai-loading',\n `dishui-ai-loading--${variant}`,\n size === 'sm' && 'dishui-ai-loading--sm',\n layout === 'block' && 'dishui-ai-loading--block',\n className,\n )}\n >\n <span className=\"dishui-ai-loading__glyph\" aria-hidden=\"true\">\n <Glyph variant={variant} />\n </span>\n <span className=\"dishui-ai-loading__label\">{label}</span>\n {showElapsed && <span className=\"dishui-ai-loading__elapsed\">{formatElapsed(elapsed)}</span>}\n </div>\n );\n}\n"],"mappings":";;;;;;AAuBA,
|
|
1
|
+
{"version":3,"file":"index.js","names":["_Fragment","index","i","Glyph","variant","Fragment","delay","LoadingState","rest","internalElapsed","setInternalElapsed","useState","controlled","elapsedMs","showElapsed","start","startedAt","id","elapsed","cn","size","layout","className","label","formatElapsed"],"sources":["../../../../src/components/ai/LoadingState/index.tsx"],"sourcesContent":["import React, { useEffect, useState } from 'react';\nimport { cn } from '../../../utils/cn';\nimport { formatElapsed } from '../shared';\nimport './LoadingState.css';\n\nexport type LoadingStateVariant = 'grid' | 'drive' | 'dots' | 'orbit' | 'surfer';\n\nexport type LoadingStateProps = Omit<React.HTMLAttributes<HTMLDivElement>, 'children'> & {\n /** Short present-tense verb shown next to the glyph, e.g. `Churning`, `Searching`. */\n label?: React.ReactNode;\n /** Glyph animation. All variants are indeterminate and respect reduced motion. */\n variant?: LoadingStateVariant;\n /** Show the elapsed time next to the label. */\n showElapsed?: boolean;\n /** Epoch milliseconds the work started; defaults to mount time. */\n startedAt?: number;\n /** Controlled elapsed time in milliseconds. When set, the internal timer is disabled. */\n elapsedMs?: number;\n /** `inline` sits in a sentence; `block` renders a bordered row. */\n layout?: 'inline' | 'block';\n size?: 'sm' | 'md';\n};\n\nconst index = (i: number) => ({ '--i': i } as React.CSSProperties);\n\nconst Glyph: React.FC<{ variant: LoadingStateVariant }> = ({ variant }) => {\n switch (variant) {\n case 'drive':\n return (\n <span className=\"dishui-ai-loading__drive\">\n {Array.from({ length: 4 }, (_, i) => <span key={i} className=\"dishui-ai-loading__cell\" style={index(i)} />)}\n </span>\n );\n case 'dots':\n return (\n <span className=\"dishui-ai-loading__dots\">\n {Array.from({ length: 3 }, (_, i) => <span key={i} className=\"dishui-ai-loading__dot\" style={index(i)} />)}\n </span>\n );\n case 'orbit':\n return (\n <>\n <span className=\"dishui-ai-loading__orbit\" />\n <span className=\"dishui-ai-loading__satellite\" />\n </>\n );\n case 'surfer':\n return (\n <span className=\"dishui-ai-loading__surfer\">\n {Array.from({ length: 5 }, (_, i) => <span key={i} className=\"dishui-ai-loading__bar\" style={index(i)} />)}\n </span>\n );\n default:\n return (\n <span className=\"dishui-ai-loading__grid\">\n {Array.from({ length: 16 }, (_, i) => {\n // Diagonal sweep: delay grows with row + column.\n const delay = Math.floor(i / 4) + (i % 4);\n return <span key={i} className=\"dishui-ai-loading__cell\" style={index(delay)} />;\n })}\n </span>\n );\n }\n};\n\n/**\n * Indeterminate agent progress: pixel-grid (or sibling) glyph, a short label\n * and the elapsed time. Announces as a polite status region.\n */\nexport default function LoadingState({\n label = 'Thinking',\n variant = 'grid',\n showElapsed = true,\n startedAt,\n elapsedMs,\n layout = 'inline',\n size = 'md',\n className,\n ...rest\n}: LoadingStateProps) {\n const [internalElapsed, setInternalElapsed] = useState(0);\n const controlled = elapsedMs !== undefined;\n\n useEffect(() => {\n if (controlled || !showElapsed) return;\n const start = startedAt ?? Date.now();\n const id = window.setInterval(() => setInternalElapsed(Date.now() - start), 100);\n return () => window.clearInterval(id);\n }, [controlled, showElapsed, startedAt]);\n\n const elapsed = controlled ? elapsedMs : internalElapsed;\n\n return (\n <div\n role=\"status\"\n aria-live=\"polite\"\n {...rest}\n className={cn(\n 'dishui-ai-loading',\n `dishui-ai-loading--${variant}`,\n size === 'sm' && 'dishui-ai-loading--sm',\n layout === 'block' && 'dishui-ai-loading--block',\n className,\n )}\n >\n <span className=\"dishui-ai-loading__glyph\" aria-hidden=\"true\">\n <Glyph variant={variant} />\n </span>\n <span className=\"dishui-ai-loading__label\">{label}</span>\n {showElapsed && <span className=\"dishui-ai-loading__elapsed\">{formatElapsed(elapsed)}</span>}\n </div>\n );\n}\n"],"mappings":";;;;;;AAuBA,IAAMC,KAAS,OAAe,EAAE,OAAOC,EAAE,IAEnCC,KAAqD,EAAE,iBAAc;CACzE,QAAQC,GAAR;EACE,KAAK,SACH,OACE,kBAAC,QAAD;GAAM,WAAU;GACb,UAAA,MAAM,KAAK,EAAE,QAAQ,EAAE,IAAI,GAAG,MAAM,kBAAC,QAAD;IAAc,WAAU;IAA0B,OAAOH,EAAMC,CAAC;GAAI,GAAzDA,CAAyD,CAAC;EACtG,CAAA;EAEV,KAAK,QACH,OACE,kBAAC,QAAD;GAAM,WAAU;GACb,UAAA,MAAM,KAAK,EAAE,QAAQ,EAAE,IAAI,GAAG,MAAM,kBAAC,QAAD;IAAc,WAAU;IAAyB,OAAOD,EAAMC,CAAC;GAAI,GAAxDA,CAAwD,CAAC;EACrG,CAAA;EAEV,KAAK,SACH,OACE,kBAAAF,GAAA,EAAA,UAAA,CACE,kBAAC,QAAD,EAAM,WAAU,2BAA4B,CAAA,GAC5C,kBAAC,QAAD,EAAM,WAAU,+BAAgC,CAAA,CAChD,EAAA,CAAA;EAEN,KAAK,UACH,OACE,kBAAC,QAAD;GAAM,WAAU;GACb,UAAA,MAAM,KAAK,EAAE,QAAQ,EAAE,IAAI,GAAG,MAAM,kBAAC,QAAD;IAAc,WAAU;IAAyB,OAAOC,EAAMC,CAAC;GAAI,GAAxDA,CAAwD,CAAC;EACrG,CAAA;EAEV,SACE,OACE,kBAAC,QAAD;GAAM,WAAU;GACb,UAAA,MAAM,KAAK,EAAE,QAAQ,GAAG,IAAI,GAAG,MAAM;IAEpC,IAAMI,IAAQ,KAAK,MAAMJ,IAAI,CAAC,IAAKA,IAAI;IACvC,OAAO,kBAAC,QAAD;KAAc,WAAU;KAA0B,OAAOD,EAAMK,CAAK;IAAI,GAA7DJ,CAA6D;GACjF,CAAC;EACG,CAAA;CAEZ;AACF;AAMA,SAAwBK,EAAa,EACnC,WAAQ,YACR,aAAU,QACV,iBAAc,IACd,cACA,cACA,YAAS,UACT,UAAO,MACP,cACA,GAAGC,KACiB;CACpB,IAAM,CAACC,GAAiBC,KAAsBC,EAAS,CAAC,GAClDC,IAAaC,MAAc,KAAA;CAEjC,QAAgB;EACd,IAAID,KAAc,CAACE,GAAa;EAChC,IAAMC,IAAQC,KAAa,KAAK,IAAI,GAC9BC,IAAK,OAAO,kBAAkBP,EAAmB,KAAK,IAAI,IAAIK,CAAK,GAAG,GAAG;EAC/E,aAAa,OAAO,cAAcE,CAAE;CACtC,GAAG;EAACL;EAAYE;EAAaE;CAAS,CAAC;CAEvC,IAAME,IAAUN,IAAaC,IAAYJ;CAEzC,OACE,kBAAC,OAAD;EACE,MAAK;EACL,aAAU;EACV,GAAID;EACJ,WAAWW,EACT,qBACA,sBAAsBf,KACtBgB,MAAS,QAAQ,yBACjBC,MAAW,WAAW,4BACtBC,CACF;EAVF,UAAA;GAYE,kBAAC,QAAD;IAAM,WAAU;IAA2B,eAAY;IACrD,UAAA,kBAACnB,GAAD,EAAgB,WAAU,CAAA;GACtB,CAAA;GACN,kBAAC,QAAD;IAAM,WAAU;IAA4B,UAAAoB;GAAY,CAAA;GACvDT,KAAe,kBAAC,QAAD;IAAM,WAAU;IAA8B,UAAAU,EAAcN,CAAO;GAAQ,CAAA;EACxF;;AAET"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../../../src/components/ai/PromptBar/index.tsx"],"sourcesContent":["import React, { useId, useRef, useState } from 'react';\nimport Button from '../../Button';\nimport Textarea from '../../Textarea';\nimport { cn } from '../../../utils/cn';\nimport './PromptBar.css';\n\nconst EMPTY_ATTACHMENTS: PromptAttachment[] = [];\n\nexport type PromptAttachment = { id: string; name: string; meta?: string };\nexport type PromptBarProps = Omit<React.FormHTMLAttributes<HTMLFormElement>, 'onSubmit' | 'onChange'> & {\n value?: string;\n defaultValue?: string;\n onChange?: (value: string) => void;\n onSubmit?: (value: string) => void;\n placeholder?: string;\n label?: string;\n busy?: boolean;\n disabled?: boolean;\n maxLength?: number;\n attachments?: PromptAttachment[];\n onRemoveAttachment?: (attachment: PromptAttachment) => void;\n /** Controls rendered at the left side of the bottom toolbar. */\n leading?: React.ReactNode;\n /** Controls rendered before the character count and send button. */\n actions?: React.ReactNode;\n submitLabel?: string;\n};\n\nconst SendIcon = ({ busy }: { busy: boolean }) => busy ? (\n <span className=\"dishui-ai-prompt-bar__busy\" aria-hidden=\"true\">•••</span>\n) : (\n <svg aria-hidden=\"true\" width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <path d=\"m5 12 7-7 7 7\" /><path d=\"M12 19V5\" />\n </svg>\n);\n\n/** Composer assembled from DishUI Textarea and Button primitives. */\nexport default function PromptBar({\n value,\n defaultValue = '',\n onChange,\n onSubmit,\n placeholder = 'Ask the agent…',\n label = 'Prompt',\n busy = false,\n disabled = false,\n maxLength,\n attachments = EMPTY_ATTACHMENTS,\n onRemoveAttachment,\n leading,\n actions,\n submitLabel = 'Send',\n className,\n ...rest\n}: PromptBarProps) {\n const id = useId();\n const textareaRef = useRef<HTMLTextAreaElement>(null);\n const [internal, setInternal] = useState(defaultValue);\n const text = value ?? internal;\n\n const update = (next: string) => {\n if (value === undefined) setInternal(next);\n onChange?.(next);\n const textarea = textareaRef.current;\n if (textarea) {\n textarea.style.height = 'auto';\n textarea.style.height = `${Math.min(textarea.scrollHeight, 160)}px`;\n }\n };\n const submit = () => {\n const next = text.trim();\n if (next && !busy && !disabled) onSubmit?.(next);\n };\n\n return (\n <form {...rest} className={cn('dishui-ai-prompt-bar', className)} onSubmit={event => { event.preventDefault(); submit(); }} aria-busy={busy || undefined}>\n {attachments.length > 0 && (\n <ul className=\"dishui-ai-prompt-bar__attachments\" aria-label=\"Attachments\">\n {attachments.map(file => (\n <li key={file.id}>\n <span title={file.name}>{file.name}</span>\n {file.meta && <small>{file.meta}</small>}\n {onRemoveAttachment && (\n <Button type=\"button\" variant=\"ghost\" size=\"xs\" square aria-label={`Remove ${file.name}`} disabled={disabled || busy} onClick={() => onRemoveAttachment(file)}>×</Button>\n )}\n </li>\n ))}\n </ul>\n )}\n <div className=\"dishui-ai-prompt-bar__main\">\n {leading && <div className=\"dishui-ai-prompt-bar__leading\">{leading}</div>}\n <div className=\"dishui-ai-prompt-bar__editor\">\n <Textarea\n ref={textareaRef}\n id={id}\n aria-label={label}\n rows={1}\n value={text}\n maxLength={maxLength}\n disabled={disabled}\n placeholder={placeholder}\n wrapperClassName=\"dishui-ai-prompt-bar__textarea-wrap\"\n className=\"dishui-ai-prompt-bar__textarea\"\n onChange={event => update(event.target.value)}\n onKeyDown={event => {\n if (event.key === 'Enter' && !event.shiftKey && !event.nativeEvent.isComposing) {\n event.preventDefault();\n submit();\n }\n }}\n />\n </div>\n <div className=\"dishui-ai-prompt-bar__actions\">\n {actions}\n {maxLength !== undefined && <span className=\"dishui-ai-prompt-bar__count\">{text.length}/{maxLength}</span>}\n <Button\n type=\"submit\"\n variant=\"primary\"\n size=\"sm\"\n square\n className=\"dishui-ai-prompt-bar__submit\"\n disabled={disabled || busy || !text.trim()}\n aria-label={submitLabel}\n >\n <SendIcon busy={busy} />\n </Button>\n </div>\n </div>\n </form>\n );\n}\n"],"mappings":";;;;;;;AAMA,
|
|
1
|
+
{"version":3,"file":"index.js","names":["Button","ButtonWithCompounds","Textarea","EMPTY_ATTACHMENTS","SendIcon","busy","PromptBar","rest","id","useId","textareaRef","useRef","internal","setInternal","useState","defaultValue","text","value","update","next","onChange","textarea","submit","disabled","onSubmit","cn","className","event","attachments","file","onRemoveAttachment","leading","label","actions","maxLength","submitLabel"],"sources":["../../../../src/components/ai/PromptBar/index.tsx"],"sourcesContent":["import React, { useId, useRef, useState } from 'react';\nimport Button from '../../Button';\nimport Textarea from '../../Textarea';\nimport { cn } from '../../../utils/cn';\nimport './PromptBar.css';\n\nconst EMPTY_ATTACHMENTS: PromptAttachment[] = [];\n\nexport type PromptAttachment = { id: string; name: string; meta?: string };\nexport type PromptBarProps = Omit<React.FormHTMLAttributes<HTMLFormElement>, 'onSubmit' | 'onChange'> & {\n value?: string;\n defaultValue?: string;\n onChange?: (value: string) => void;\n onSubmit?: (value: string) => void;\n placeholder?: string;\n label?: string;\n busy?: boolean;\n disabled?: boolean;\n maxLength?: number;\n attachments?: PromptAttachment[];\n onRemoveAttachment?: (attachment: PromptAttachment) => void;\n /** Controls rendered at the left side of the bottom toolbar. */\n leading?: React.ReactNode;\n /** Controls rendered before the character count and send button. */\n actions?: React.ReactNode;\n submitLabel?: string;\n};\n\nconst SendIcon = ({ busy }: { busy: boolean }) => busy ? (\n <span className=\"dishui-ai-prompt-bar__busy\" aria-hidden=\"true\">•••</span>\n) : (\n <svg aria-hidden=\"true\" width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <path d=\"m5 12 7-7 7 7\" /><path d=\"M12 19V5\" />\n </svg>\n);\n\n/** Composer assembled from DishUI Textarea and Button primitives. */\nexport default function PromptBar({\n value,\n defaultValue = '',\n onChange,\n onSubmit,\n placeholder = 'Ask the agent…',\n label = 'Prompt',\n busy = false,\n disabled = false,\n maxLength,\n attachments = EMPTY_ATTACHMENTS,\n onRemoveAttachment,\n leading,\n actions,\n submitLabel = 'Send',\n className,\n ...rest\n}: PromptBarProps) {\n const id = useId();\n const textareaRef = useRef<HTMLTextAreaElement>(null);\n const [internal, setInternal] = useState(defaultValue);\n const text = value ?? internal;\n\n const update = (next: string) => {\n if (value === undefined) setInternal(next);\n onChange?.(next);\n const textarea = textareaRef.current;\n if (textarea) {\n textarea.style.height = 'auto';\n textarea.style.height = `${Math.min(textarea.scrollHeight, 160)}px`;\n }\n };\n const submit = () => {\n const next = text.trim();\n if (next && !busy && !disabled) onSubmit?.(next);\n };\n\n return (\n <form {...rest} className={cn('dishui-ai-prompt-bar', className)} onSubmit={event => { event.preventDefault(); submit(); }} aria-busy={busy || undefined}>\n {attachments.length > 0 && (\n <ul className=\"dishui-ai-prompt-bar__attachments\" aria-label=\"Attachments\">\n {attachments.map(file => (\n <li key={file.id}>\n <span title={file.name}>{file.name}</span>\n {file.meta && <small>{file.meta}</small>}\n {onRemoveAttachment && (\n <Button type=\"button\" variant=\"ghost\" size=\"xs\" square aria-label={`Remove ${file.name}`} disabled={disabled || busy} onClick={() => onRemoveAttachment(file)}>×</Button>\n )}\n </li>\n ))}\n </ul>\n )}\n <div className=\"dishui-ai-prompt-bar__main\">\n {leading && <div className=\"dishui-ai-prompt-bar__leading\">{leading}</div>}\n <div className=\"dishui-ai-prompt-bar__editor\">\n <Textarea\n ref={textareaRef}\n id={id}\n aria-label={label}\n rows={1}\n value={text}\n maxLength={maxLength}\n disabled={disabled}\n placeholder={placeholder}\n wrapperClassName=\"dishui-ai-prompt-bar__textarea-wrap\"\n className=\"dishui-ai-prompt-bar__textarea\"\n onChange={event => update(event.target.value)}\n onKeyDown={event => {\n if (event.key === 'Enter' && !event.shiftKey && !event.nativeEvent.isComposing) {\n event.preventDefault();\n submit();\n }\n }}\n />\n </div>\n <div className=\"dishui-ai-prompt-bar__actions\">\n {actions}\n {maxLength !== undefined && <span className=\"dishui-ai-prompt-bar__count\">{text.length}/{maxLength}</span>}\n <Button\n type=\"submit\"\n variant=\"primary\"\n size=\"sm\"\n square\n className=\"dishui-ai-prompt-bar__submit\"\n disabled={disabled || busy || !text.trim()}\n aria-label={submitLabel}\n >\n <SendIcon busy={busy} />\n </Button>\n </div>\n </div>\n </form>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../../../src/components/ai/RecommendationCard/index.tsx"],"sourcesContent":["import React, { useId, useState } from 'react';\nimport { cn } from '../../../utils/cn';\nimport Button from '../../Button';\nimport { clamp } from '../shared';\nimport './RecommendationCard.css';\n\nexport type ConfidenceLevel = 'high' | 'medium' | 'low';\n\nexport type Recommendation = {\n title: React.ReactNode;\n detail?: React.ReactNode;\n /** 0–1. */\n confidence: number;\n};\n\nexport type RecommendationAlternative = {\n id: string;\n title: React.ReactNode;\n /** Short judgement shown on the right, e.g. `Needs review`, `No signal`. */\n verdict?: React.ReactNode;\n /** 0–1; when present a small meter is shown next to the verdict. */\n confidence?: number;\n};\n\nexport type RecommendationCardLabels = {\n accept?: string;\n alternatives?: string;\n otherOptions?: string;\n};\n\nexport type RecommendationCardProps = Omit<React.HTMLAttributes<HTMLDivElement>, 'children'> & {\n /** The agent's question, e.g. `Want me to place this restock order?` */\n question: React.ReactNode;\n recommendation: Recommendation;\n alternatives?: RecommendationAlternative[];\n /** Map a 0–1 confidence to its label. */\n confidenceLabel?: (value: number) => string;\n onAccept?: () => void;\n /** When omitted, the button toggles the built-in alternatives list. */\n onAlternatives?: () => void;\n onSelectAlternative?: (alternative: RecommendationAlternative) => void;\n labels?: RecommendationCardLabels;\n /** Accept is in flight: both buttons disabled, Accept marked `aria-busy`. */\n busy?: boolean;\n disabled?: boolean;\n};\n\nconst DEFAULT_LABELS: Required<RecommendationCardLabels> = {\n accept: 'Accept',\n alternatives: 'Alternatives',\n otherOptions: 'Other options',\n};\n\nconst SEGMENTS = 5;\n\nexport function confidenceLevel(value: number): ConfidenceLevel {\n if (value >= 0.75) return 'high';\n if (value >= 0.45) return 'medium';\n return 'low';\n}\n\nconst defaultConfidenceLabel = (value: number) => {\n switch (confidenceLevel(value)) {\n case 'high': return 'High confidence';\n case 'medium': return 'Medium confidence';\n default: return 'Low confidence';\n }\n};\n\nconst ConfidenceMeter: React.FC<{ value: number; label: string; compact?: boolean }> = ({ value, label, compact }) => {\n const v = clamp(Number.isFinite(value) ? value : 0, 0, 1);\n const filled = v === 0 ? 0 : Math.max(1, Math.round(v * SEGMENTS));\n const level = confidenceLevel(v);\n return (\n <div\n className={cn('dishui-ai-recommendation-card__meter', `dishui-ai-recommendation-card__meter--${level}`, compact && 'dishui-ai-recommendation-card__meter--compact')}\n role=\"meter\"\n aria-valuemin={0}\n aria-valuemax={1}\n aria-valuenow={v}\n aria-valuetext={label}\n aria-label=\"Confidence\"\n >\n <span className=\"dishui-ai-recommendation-card__segments\" aria-hidden=\"true\">\n {Array.from({ length: SEGMENTS }, (_, i) => (\n <span key={i} className={cn('dishui-ai-recommendation-card__segment', i < filled && 'dishui-ai-recommendation-card__segment--on')} />\n ))}\n </span>\n {!compact && <span className=\"dishui-ai-recommendation-card__meter-label\">{label}</span>}\n </div>\n );\n};\n\n/**\n * An agent suggestion: the question it is answering, the recommended\n * action with a segmented confidence meter, optional alternatives and a\n * secondary/primary action pair.\n */\nexport default function RecommendationCard({\n question,\n recommendation,\n alternatives,\n confidenceLabel = defaultConfidenceLabel,\n onAccept,\n onAlternatives,\n onSelectAlternative,\n labels,\n busy = false,\n disabled = false,\n className,\n ...rest\n}: RecommendationCardProps) {\n const baseId = useId();\n const text = { ...DEFAULT_LABELS, ...labels };\n const [showAlternatives, setShowAlternatives] = useState(false);\n const hasAlternatives = !!alternatives?.length;\n const listId = `${baseId}-alternatives`;\n const inert = busy || disabled;\n\n const handleAlternatives = () => {\n if (onAlternatives) onAlternatives();\n else setShowAlternatives(v => !v);\n };\n\n return (\n <div {...rest} className={cn('dishui-ai-recommendation-card', className)}>\n <div className=\"dishui-ai-recommendation-card__question\">{question}</div>\n\n <div className=\"dishui-ai-recommendation-card__body\">\n <div className=\"dishui-ai-recommendation-card__title\">{recommendation.title}</div>\n {recommendation.detail !== undefined && (\n <div className=\"dishui-ai-recommendation-card__detail\">{recommendation.detail}</div>\n )}\n <ConfidenceMeter value={recommendation.confidence} label={confidenceLabel(recommendation.confidence)} />\n </div>\n\n {hasAlternatives && (\n <div\n id={listId}\n className=\"dishui-ai-recommendation-card__alternatives\"\n hidden={!showAlternatives}\n >\n <div className=\"dishui-ai-recommendation-card__alternatives-title\">{text.otherOptions}</div>\n <ul className=\"dishui-ai-recommendation-card__list\">\n {alternatives!.map(alt => {\n const inner = (\n <>\n <span className=\"dishui-ai-recommendation-card__alt-title\">{alt.title}</span>\n {alt.confidence !== undefined && (\n <ConfidenceMeter value={alt.confidence} label={confidenceLabel(alt.confidence)} compact />\n )}\n {alt.verdict !== undefined && <span className=\"dishui-ai-recommendation-card__verdict\">{alt.verdict}</span>}\n </>\n );\n return (\n <li key={alt.id} className=\"dishui-ai-recommendation-card__alt\">\n {onSelectAlternative ? (\n <button\n type=\"button\"\n className=\"dishui-ai-recommendation-card__alt-button\"\n disabled={inert}\n onClick={() => onSelectAlternative(alt)}\n >\n {inner}\n </button>\n ) : (\n <div className=\"dishui-ai-recommendation-card__alt-button dishui-ai-recommendation-card__alt-button--static\">{inner}</div>\n )}\n </li>\n );\n })}\n </ul>\n </div>\n )}\n\n {(onAccept || onAlternatives || hasAlternatives) && (\n <div className=\"dishui-ai-recommendation-card__actions\">\n {(onAlternatives || hasAlternatives) && (\n <Button\n type=\"button\"\n variant=\"secondary\"\n size=\"sm\"\n disabled={inert}\n aria-expanded={onAlternatives ? undefined : showAlternatives}\n aria-controls={onAlternatives || !hasAlternatives ? undefined : listId}\n onClick={handleAlternatives}\n >\n {text.alternatives}\n </Button>\n )}\n {onAccept && (\n <Button type=\"button\" variant=\"primary\" size=\"sm\" disabled={inert} aria-busy={busy || undefined} onClick={onAccept}>\n {text.accept}\n </Button>\n )}\n </div>\n )}\n </div>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":["_Fragment","Button","ButtonWithCompounds","DEFAULT_LABELS","SEGMENTS","confidenceLevel","value","defaultConfidenceLabel","ConfidenceMeter","v","clamp","filled","level","cn","compact","label","i","RecommendationCard","rest","baseId","useId","text","labels","showAlternatives","setShowAlternatives","useState","hasAlternatives","alternatives","listId","inert","busy","disabled","handleAlternatives","onAlternatives","className","question","recommendation","confidenceLabel","inner","Fragment","alt","onSelectAlternative","onAccept"],"sources":["../../../../src/components/ai/RecommendationCard/index.tsx"],"sourcesContent":["import React, { useId, useState } from 'react';\nimport { cn } from '../../../utils/cn';\nimport Button from '../../Button';\nimport { clamp } from '../shared';\nimport './RecommendationCard.css';\n\nexport type ConfidenceLevel = 'high' | 'medium' | 'low';\n\nexport type Recommendation = {\n title: React.ReactNode;\n detail?: React.ReactNode;\n /** 0–1. */\n confidence: number;\n};\n\nexport type RecommendationAlternative = {\n id: string;\n title: React.ReactNode;\n /** Short judgement shown on the right, e.g. `Needs review`, `No signal`. */\n verdict?: React.ReactNode;\n /** 0–1; when present a small meter is shown next to the verdict. */\n confidence?: number;\n};\n\nexport type RecommendationCardLabels = {\n accept?: string;\n alternatives?: string;\n otherOptions?: string;\n};\n\nexport type RecommendationCardProps = Omit<React.HTMLAttributes<HTMLDivElement>, 'children'> & {\n /** The agent's question, e.g. `Want me to place this restock order?` */\n question: React.ReactNode;\n recommendation: Recommendation;\n alternatives?: RecommendationAlternative[];\n /** Map a 0–1 confidence to its label. */\n confidenceLabel?: (value: number) => string;\n onAccept?: () => void;\n /** When omitted, the button toggles the built-in alternatives list. */\n onAlternatives?: () => void;\n onSelectAlternative?: (alternative: RecommendationAlternative) => void;\n labels?: RecommendationCardLabels;\n /** Accept is in flight: both buttons disabled, Accept marked `aria-busy`. */\n busy?: boolean;\n disabled?: boolean;\n};\n\nconst DEFAULT_LABELS: Required<RecommendationCardLabels> = {\n accept: 'Accept',\n alternatives: 'Alternatives',\n otherOptions: 'Other options',\n};\n\nconst SEGMENTS = 5;\n\nexport function confidenceLevel(value: number): ConfidenceLevel {\n if (value >= 0.75) return 'high';\n if (value >= 0.45) return 'medium';\n return 'low';\n}\n\nconst defaultConfidenceLabel = (value: number) => {\n switch (confidenceLevel(value)) {\n case 'high': return 'High confidence';\n case 'medium': return 'Medium confidence';\n default: return 'Low confidence';\n }\n};\n\nconst ConfidenceMeter: React.FC<{ value: number; label: string; compact?: boolean }> = ({ value, label, compact }) => {\n const v = clamp(Number.isFinite(value) ? value : 0, 0, 1);\n const filled = v === 0 ? 0 : Math.max(1, Math.round(v * SEGMENTS));\n const level = confidenceLevel(v);\n return (\n <div\n className={cn('dishui-ai-recommendation-card__meter', `dishui-ai-recommendation-card__meter--${level}`, compact && 'dishui-ai-recommendation-card__meter--compact')}\n role=\"meter\"\n aria-valuemin={0}\n aria-valuemax={1}\n aria-valuenow={v}\n aria-valuetext={label}\n aria-label=\"Confidence\"\n >\n <span className=\"dishui-ai-recommendation-card__segments\" aria-hidden=\"true\">\n {Array.from({ length: SEGMENTS }, (_, i) => (\n <span key={i} className={cn('dishui-ai-recommendation-card__segment', i < filled && 'dishui-ai-recommendation-card__segment--on')} />\n ))}\n </span>\n {!compact && <span className=\"dishui-ai-recommendation-card__meter-label\">{label}</span>}\n </div>\n );\n};\n\n/**\n * An agent suggestion: the question it is answering, the recommended\n * action with a segmented confidence meter, optional alternatives and a\n * secondary/primary action pair.\n */\nexport default function RecommendationCard({\n question,\n recommendation,\n alternatives,\n confidenceLabel = defaultConfidenceLabel,\n onAccept,\n onAlternatives,\n onSelectAlternative,\n labels,\n busy = false,\n disabled = false,\n className,\n ...rest\n}: RecommendationCardProps) {\n const baseId = useId();\n const text = { ...DEFAULT_LABELS, ...labels };\n const [showAlternatives, setShowAlternatives] = useState(false);\n const hasAlternatives = !!alternatives?.length;\n const listId = `${baseId}-alternatives`;\n const inert = busy || disabled;\n\n const handleAlternatives = () => {\n if (onAlternatives) onAlternatives();\n else setShowAlternatives(v => !v);\n };\n\n return (\n <div {...rest} className={cn('dishui-ai-recommendation-card', className)}>\n <div className=\"dishui-ai-recommendation-card__question\">{question}</div>\n\n <div className=\"dishui-ai-recommendation-card__body\">\n <div className=\"dishui-ai-recommendation-card__title\">{recommendation.title}</div>\n {recommendation.detail !== undefined && (\n <div className=\"dishui-ai-recommendation-card__detail\">{recommendation.detail}</div>\n )}\n <ConfidenceMeter value={recommendation.confidence} label={confidenceLabel(recommendation.confidence)} />\n </div>\n\n {hasAlternatives && (\n <div\n id={listId}\n className=\"dishui-ai-recommendation-card__alternatives\"\n hidden={!showAlternatives}\n >\n <div className=\"dishui-ai-recommendation-card__alternatives-title\">{text.otherOptions}</div>\n <ul className=\"dishui-ai-recommendation-card__list\">\n {alternatives!.map(alt => {\n const inner = (\n <>\n <span className=\"dishui-ai-recommendation-card__alt-title\">{alt.title}</span>\n {alt.confidence !== undefined && (\n <ConfidenceMeter value={alt.confidence} label={confidenceLabel(alt.confidence)} compact />\n )}\n {alt.verdict !== undefined && <span className=\"dishui-ai-recommendation-card__verdict\">{alt.verdict}</span>}\n </>\n );\n return (\n <li key={alt.id} className=\"dishui-ai-recommendation-card__alt\">\n {onSelectAlternative ? (\n <button\n type=\"button\"\n className=\"dishui-ai-recommendation-card__alt-button\"\n disabled={inert}\n onClick={() => onSelectAlternative(alt)}\n >\n {inner}\n </button>\n ) : (\n <div className=\"dishui-ai-recommendation-card__alt-button dishui-ai-recommendation-card__alt-button--static\">{inner}</div>\n )}\n </li>\n );\n })}\n </ul>\n </div>\n )}\n\n {(onAccept || onAlternatives || hasAlternatives) && (\n <div className=\"dishui-ai-recommendation-card__actions\">\n {(onAlternatives || hasAlternatives) && (\n <Button\n type=\"button\"\n variant=\"secondary\"\n size=\"sm\"\n disabled={inert}\n aria-expanded={onAlternatives ? undefined : showAlternatives}\n aria-controls={onAlternatives || !hasAlternatives ? undefined : listId}\n onClick={handleAlternatives}\n >\n {text.alternatives}\n </Button>\n )}\n {onAccept && (\n <Button type=\"button\" variant=\"primary\" size=\"sm\" disabled={inert} aria-busy={busy || undefined} onClick={onAccept}>\n {text.accept}\n </Button>\n )}\n </div>\n )}\n </div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../../../src/components/ai/RecordsTable/index.tsx"],"sourcesContent":["import React, { useEffect, useMemo, useState } from 'react';\nimport { cn } from '../../../utils/cn';\nimport './RecordsTable.css';\n\nexport type RecordStrength = 'very-strong' | 'strong' | 'weak' | 'very-weak' | 'none';\n\nexport type RecordRow = {\n id: string;\n name: string;\n location?: string;\n avatar?: React.ReactNode;\n tags: string[];\n /** Epoch ms or ISO string; `null`/undefined = no contact yet. */\n lastInteractionAt?: number | string | null;\n strength: RecordStrength;\n link?: string | null;\n};\n\nexport type RecordsSortKey = 'name' | 'lastInteraction' | 'strength' | 'tags';\nexport type RecordsSort = { key: RecordsSortKey; direction: 'asc' | 'desc' };\n\nexport type RecordsTableProps = Omit<React.HTMLAttributes<HTMLDivElement>, 'children'> & {\n records: RecordRow[];\n sort?: RecordsSort | null;\n defaultSort?: RecordsSort | null;\n onSortChange?: (sort: RecordsSort | null) => void;\n /** Tags shown before collapsing into a `+n` chip. */\n maxTags?: number;\n showIndex?: boolean;\n /** `true` computes count / average strength / link count; a node replaces it. */\n footer?: boolean | React.ReactNode;\n onRowClick?: (record: RecordRow) => void;\n selectedId?: string | null;\n stickyHeader?: boolean;\n maxHeight?: string | number;\n /** Reference time for relative dates. Defaults to mount time (set in an effect). */\n now?: number;\n emptyText?: React.ReactNode;\n labels?: Partial<typeof DEFAULT_LABELS>;\n};\n\nconst DEFAULT_LABELS = {\n index: '#',\n company: 'Company',\n categories: 'Categories',\n lastInteraction: 'Last interaction',\n strength: 'Connection strength',\n links: 'Links',\n noContact: 'No contact',\n count: 'count',\n average: 'average',\n linksCount: 'links',\n addCalculation: 'Add calculation',\n empty: 'No records',\n};\n\nexport const STRENGTH_LABEL: Record<RecordStrength, string> = {\n 'very-strong': 'Very strong',\n strong: 'Strong',\n weak: 'Weak',\n 'very-weak': 'Very weak',\n none: 'No communication',\n};\n\nconst STRENGTH_BARS: Record<RecordStrength, number> = { none: 0, 'very-weak': 1, weak: 2, strong: 3, 'very-strong': 4 };\n\n/** none 0 / very-weak 25 / weak 50 / strong 75 / very-strong 100. */\nexport function strengthPercent(strength: RecordStrength): number {\n return STRENGTH_BARS[strength] * 25;\n}\n\nconst MINUTE = 60_000;\nconst HOUR = 60 * MINUTE;\nconst DAY = 24 * HOUR;\nconst WEEK = 7 * DAY;\nconst MONTH = 30.44 * DAY;\nconst YEAR = 365.25 * DAY;\nconst yearLabel = (n: number) => `${n} year${n === 1 ? '' : 's'} ago`;\n\n/**\n * date-fns-like relative buckets: `just now`, `4 days ago`, `5 weeks ago`,\n * `11 months ago`, `about 1 year ago`, `over 1 year ago`, `almost 2 years ago`.\n */\nexport function formatRelativeTime(from: number, now: number): string {\n const diff = Math.max(0, now - from);\n if (diff < MINUTE) return 'just now';\n if (diff < HOUR) {\n const m = Math.round(diff / MINUTE);\n return `${m} minute${m === 1 ? '' : 's'} ago`;\n }\n if (diff < DAY) {\n const h = Math.round(diff / HOUR);\n return `${h} hour${h === 1 ? '' : 's'} ago`;\n }\n if (diff < WEEK) {\n const d = Math.round(diff / DAY);\n return d <= 1 ? 'yesterday' : `${d} days ago`;\n }\n if (diff < 2 * MONTH) {\n const w = Math.round(diff / WEEK);\n return `${w} week${w === 1 ? '' : 's'} ago`;\n }\n if (diff < YEAR) {\n const mo = Math.round(diff / MONTH);\n return `${mo} months ago`;\n }\n const years = diff / YEAR;\n const whole = Math.floor(years);\n const frac = years - whole;\n if (frac < 0.25) return `about ${yearLabel(whole)}`;\n if (frac < 0.75) return `over ${yearLabel(whole)}`;\n return `almost ${yearLabel(whole + 1)}`;\n}\n\nconst toEpoch = (value: number | string | null | undefined): number | null => {\n if (value === null || value === undefined) return null;\n const t = typeof value === 'number' ? value : Date.parse(value);\n return Number.isFinite(t) ? t : null;\n};\n\nconst formatAbsolute = (t: number) => new Date(t).toISOString().slice(0, 10);\n\nconst hostOf = (url: string) => {\n try {\n return new URL(url.includes('://') ? url : `https://${url}`).hostname.replace(/^www\\./, '');\n } catch {\n return url;\n }\n};\n\nconst compare = (a: RecordRow, b: RecordRow, key: RecordsSortKey): number => {\n switch (key) {\n case 'name':\n return a.name.localeCompare(b.name);\n case 'strength':\n return STRENGTH_BARS[a.strength] - STRENGTH_BARS[b.strength];\n case 'tags':\n return a.tags.length - b.tags.length;\n case 'lastInteraction': {\n const ta = toEpoch(a.lastInteractionAt) ?? Number.NEGATIVE_INFINITY;\n const tb = toEpoch(b.lastInteractionAt) ?? Number.NEGATIVE_INFINITY;\n return ta - tb;\n }\n default:\n return 0;\n }\n};\n\nconst SortIcon: React.FC<{ direction?: 'asc' | 'desc' }> = ({ direction }) => (\n <svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden=\"true\">\n {direction !== 'desc' && <path d=\"m7 10 5-5 5 5\" opacity={direction === 'asc' ? 1 : 0.4} />}\n {direction !== 'asc' && <path d=\"m7 14 5 5 5-5\" opacity={direction === 'desc' ? 1 : 0.4} />}\n </svg>\n);\n\nconst StrengthGlyph: React.FC<{ strength: RecordStrength }> = ({ strength }) => {\n const bars = STRENGTH_BARS[strength];\n return (\n <span className=\"dishui-ai-records-table__signal\" aria-hidden=\"true\">\n {Array.from({ length: 4 }, (_, i) => (\n <span key={i} className={cn('dishui-ai-records-table__bar', i < bars && 'dishui-ai-records-table__bar--on')} />\n ))}\n </span>\n );\n};\n\ntype SortableHeaderProps = {\n label: React.ReactNode;\n sortKey: RecordsSortKey;\n sort: RecordsSort | null;\n onToggle: (key: RecordsSortKey) => void;\n className?: string;\n};\n\nconst SortableHeader: React.FC<SortableHeaderProps> = ({ label, sortKey, sort, onToggle, className }) => {\n const active = sort?.key === sortKey ? sort.direction : undefined;\n return (\n <th\n scope=\"col\"\n className={cn('dishui-ai-records-table__th', className)}\n aria-sort={active ? (active === 'asc' ? 'ascending' : 'descending') : 'none'}\n >\n <button\n type=\"button\"\n className={cn('dishui-ai-records-table__sort', active && 'dishui-ai-records-table__sort--active')}\n onClick={() => onToggle(sortKey)}\n >\n <span>{label}</span>\n <SortIcon direction={active} />\n </button>\n </th>\n );\n};\n\n/**\n * CRM-style grid with tag chips, sortable columns, relative interaction\n * dates and a 4-bar connection-strength glyph paired with its label.\n */\nexport default function RecordsTable({\n records,\n sort: sortProp,\n defaultSort = null,\n onSortChange,\n maxTags = 3,\n showIndex = true,\n footer = true,\n onRowClick,\n selectedId,\n stickyHeader = false,\n maxHeight,\n now: nowProp,\n emptyText,\n labels: labelOverrides,\n className,\n style,\n ...rest\n}: RecordsTableProps) {\n const labels = { ...DEFAULT_LABELS, ...labelOverrides };\n\n const [internalSort, setInternalSort] = useState<RecordsSort | null>(defaultSort);\n const sort = sortProp !== undefined ? sortProp : internalSort;\n const toggleSort = (key: RecordsSortKey) => {\n const next: RecordsSort | null =\n sort?.key !== key ? { key, direction: 'asc' } : sort.direction === 'asc' ? { key, direction: 'desc' } : null;\n if (sortProp === undefined) setInternalSort(next);\n onSortChange?.(next);\n };\n\n // Relative times need a reference \"now\"; never read the clock during render.\n const [mountNow, setMountNow] = useState<number | null>(null);\n useEffect(() => {\n if (nowProp !== undefined) return;\n const id = window.setTimeout(() => setMountNow(Date.now()), 0);\n return () => window.clearTimeout(id);\n }, [nowProp]);\n const now = nowProp ?? mountNow;\n\n const sorted = useMemo(() => {\n if (!sort) return records;\n const dir = sort.direction === 'asc' ? 1 : -1;\n const indexed = records.map((r, i) => ({ r, i }));\n // The copied array is intentionally sorted in place; `records` is never mutated.\n // eslint-disable-next-line unicorn/no-array-sort\n indexed.sort((a, b) => compare(a.r, b.r, sort.key) * dir || a.i - b.i);\n return indexed.map(x => x.r);\n }, [records, sort]);\n\n const stats = useMemo(() => {\n const count = records.length;\n const avg = count ? Math.round(records.reduce((n, r) => n + strengthPercent(r.strength), 0) / count) : 0;\n const links = records.filter(r => r.link).length;\n return { count, avg, links };\n }, [records]);\n\n const interactive = Boolean(onRowClick);\n const scrollStyle: React.CSSProperties | undefined = maxHeight !== undefined ? { maxHeight } : undefined;\n\n return (\n <div {...rest} style={style} className={cn('dishui-ai-records-table', stickyHeader && 'dishui-ai-records-table--sticky', className)}>\n <div className=\"dishui-ai-records-table__scroll\" style={scrollStyle}>\n <table className=\"dishui-ai-records-table__table\">\n <thead>\n <tr>\n {showIndex && <th scope=\"col\" className=\"dishui-ai-records-table__th dishui-ai-records-table__th--index\">{labels.index}</th>}\n <SortableHeader label={labels.company} sortKey=\"name\" sort={sort} onToggle={toggleSort} />\n <SortableHeader label={labels.categories} sortKey=\"tags\" sort={sort} onToggle={toggleSort} />\n <SortableHeader label={labels.lastInteraction} sortKey=\"lastInteraction\" sort={sort} onToggle={toggleSort} />\n <SortableHeader label={labels.strength} sortKey=\"strength\" sort={sort} onToggle={toggleSort} />\n <th scope=\"col\" className=\"dishui-ai-records-table__th\">{labels.links}</th>\n </tr>\n </thead>\n <tbody>\n {sorted.length === 0 && (\n <tr>\n <td className=\"dishui-ai-records-table__td dishui-ai-records-table__td--empty\" colSpan={showIndex ? 6 : 5}>\n {emptyText ?? labels.empty}\n </td>\n </tr>\n )}\n {sorted.map((record, i) => {\n const t = toEpoch(record.lastInteractionAt);\n const shown = record.tags.slice(0, maxTags);\n const hiddenTags = record.tags.slice(maxTags);\n const selected = selectedId === record.id;\n return (\n <tr\n key={record.id}\n className={cn(\n 'dishui-ai-records-table__row',\n interactive && 'dishui-ai-records-table__row--interactive',\n selected && 'dishui-ai-records-table__row--selected',\n )}\n data-selected={selected || undefined}\n tabIndex={interactive ? 0 : undefined}\n onClick={interactive ? () => onRowClick?.(record) : undefined}\n onKeyDown={\n interactive\n ? e => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n onRowClick?.(record);\n }\n }\n : undefined\n }\n >\n {showIndex && <td className=\"dishui-ai-records-table__td dishui-ai-records-table__td--index\">{i + 1}</td>}\n <td className=\"dishui-ai-records-table__td dishui-ai-records-table__td--company\">\n <span className=\"dishui-ai-records-table__avatar\" aria-hidden=\"true\">\n {record.avatar ?? record.name.trim().charAt(0).toUpperCase()}\n </span>\n <span className=\"dishui-ai-records-table__name\">\n {record.name}\n {record.location && <span className=\"dishui-ai-records-table__location\"> — {record.location}</span>}\n </span>\n </td>\n <td className=\"dishui-ai-records-table__td\">\n <span className=\"dishui-ai-records-table__tags\">\n {shown.map(tag => (\n <span key={tag} className=\"dishui-ai-records-table__tag\">{tag}</span>\n ))}\n {hiddenTags.length > 0 && (\n <span className=\"dishui-ai-records-table__tag dishui-ai-records-table__tag--more\" title={hiddenTags.join(', ')}>\n +{hiddenTags.length}\n </span>\n )}\n {record.tags.length === 0 && <span className=\"dishui-ai-records-table__muted\">—</span>}\n </span>\n </td>\n <td className={cn('dishui-ai-records-table__td dishui-ai-records-table__td--time', t === null && 'dishui-ai-records-table__td--muted')}>\n {t === null ? labels.noContact : now === null ? formatAbsolute(t) : <time dateTime={new Date(t).toISOString()} title={formatAbsolute(t)}>{formatRelativeTime(t, now)}</time>}\n </td>\n <td className={cn('dishui-ai-records-table__td dishui-ai-records-table__td--strength', `dishui-ai-records-table__td--${record.strength}`)}>\n <StrengthGlyph strength={record.strength} />\n <span>{STRENGTH_LABEL[record.strength]}</span>\n </td>\n <td className=\"dishui-ai-records-table__td\">\n {record.link ? (\n <a\n className=\"dishui-ai-records-table__link\"\n href={record.link.includes('://') ? record.link : `https://${record.link}`}\n target=\"_blank\"\n rel=\"noreferrer\"\n onClick={e => e.stopPropagation()}\n >\n {hostOf(record.link)}\n </a>\n ) : (\n <span className=\"dishui-ai-records-table__muted\">—</span>\n )}\n </td>\n </tr>\n );\n })}\n </tbody>\n {footer && (\n <tfoot>\n <tr>\n {footer === true ? (\n <>\n {showIndex && <td className=\"dishui-ai-records-table__tf dishui-ai-records-table__td--index\" />}\n <td className=\"dishui-ai-records-table__tf\"><span className=\"dishui-ai-records-table__num\">{stats.count}</span> {labels.count}</td>\n <td className=\"dishui-ai-records-table__tf dishui-ai-records-table__tf--muted\">{labels.addCalculation}</td>\n <td className=\"dishui-ai-records-table__tf dishui-ai-records-table__tf--muted\">—</td>\n <td className=\"dishui-ai-records-table__tf\"><span className=\"dishui-ai-records-table__num\">{stats.avg}%</span> {labels.average}</td>\n <td className=\"dishui-ai-records-table__tf\"><span className=\"dishui-ai-records-table__num\">{stats.links}</span> {labels.linksCount}</td>\n </>\n ) : (\n <td className=\"dishui-ai-records-table__tf\" colSpan={showIndex ? 6 : 5}>{footer}</td>\n )}\n </tr>\n </tfoot>\n )}\n </table>\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":["_Fragment","DEFAULT_LABELS","STRENGTH_LABEL","STRENGTH_BARS","strengthPercent","strength","MINUTE","HOUR","DAY","WEEK","MONTH","YEAR","yearLabel","n","formatRelativeTime","diff","now","from","m","h","d","w","years","whole","frac","toEpoch","value","formatAbsolute","t","hostOf","url","compare","key","a","b","SortIcon","direction","StrengthGlyph","bars","cn","i","SortableHeader","active","sort","sortKey","className","onToggle","label","RecordsTable","sortProp","nowProp","labelOverrides","rest","labels","internalSort","setInternalSort","useState","defaultSort","toggleSort","next","onSortChange","mountNow","setMountNow","id","sorted","useMemo","records","dir","indexed","x","stats","count","r","interactive","onRowClick","scrollStyle","maxHeight","stickyHeader","showIndex","emptyText","record","shown","maxTags","hiddenTags","selected","selectedId","tag","footer","Fragment"],"sources":["../../../../src/components/ai/RecordsTable/index.tsx"],"sourcesContent":["import React, { useEffect, useMemo, useState } from 'react';\nimport { cn } from '../../../utils/cn';\nimport './RecordsTable.css';\n\nexport type RecordStrength = 'very-strong' | 'strong' | 'weak' | 'very-weak' | 'none';\n\nexport type RecordRow = {\n id: string;\n name: string;\n location?: string;\n avatar?: React.ReactNode;\n tags: string[];\n /** Epoch ms or ISO string; `null`/undefined = no contact yet. */\n lastInteractionAt?: number | string | null;\n strength: RecordStrength;\n link?: string | null;\n};\n\nexport type RecordsSortKey = 'name' | 'lastInteraction' | 'strength' | 'tags';\nexport type RecordsSort = { key: RecordsSortKey; direction: 'asc' | 'desc' };\n\nexport type RecordsTableProps = Omit<React.HTMLAttributes<HTMLDivElement>, 'children'> & {\n records: RecordRow[];\n sort?: RecordsSort | null;\n defaultSort?: RecordsSort | null;\n onSortChange?: (sort: RecordsSort | null) => void;\n /** Tags shown before collapsing into a `+n` chip. */\n maxTags?: number;\n showIndex?: boolean;\n /** `true` computes count / average strength / link count; a node replaces it. */\n footer?: boolean | React.ReactNode;\n onRowClick?: (record: RecordRow) => void;\n selectedId?: string | null;\n stickyHeader?: boolean;\n maxHeight?: string | number;\n /** Reference time for relative dates. Defaults to mount time (set in an effect). */\n now?: number;\n emptyText?: React.ReactNode;\n labels?: Partial<typeof DEFAULT_LABELS>;\n};\n\nconst DEFAULT_LABELS = {\n index: '#',\n company: 'Company',\n categories: 'Categories',\n lastInteraction: 'Last interaction',\n strength: 'Connection strength',\n links: 'Links',\n noContact: 'No contact',\n count: 'count',\n average: 'average',\n linksCount: 'links',\n addCalculation: 'Add calculation',\n empty: 'No records',\n};\n\nexport const STRENGTH_LABEL: Record<RecordStrength, string> = {\n 'very-strong': 'Very strong',\n strong: 'Strong',\n weak: 'Weak',\n 'very-weak': 'Very weak',\n none: 'No communication',\n};\n\nconst STRENGTH_BARS: Record<RecordStrength, number> = { none: 0, 'very-weak': 1, weak: 2, strong: 3, 'very-strong': 4 };\n\n/** none 0 / very-weak 25 / weak 50 / strong 75 / very-strong 100. */\nexport function strengthPercent(strength: RecordStrength): number {\n return STRENGTH_BARS[strength] * 25;\n}\n\nconst MINUTE = 60_000;\nconst HOUR = 60 * MINUTE;\nconst DAY = 24 * HOUR;\nconst WEEK = 7 * DAY;\nconst MONTH = 30.44 * DAY;\nconst YEAR = 365.25 * DAY;\nconst yearLabel = (n: number) => `${n} year${n === 1 ? '' : 's'} ago`;\n\n/**\n * date-fns-like relative buckets: `just now`, `4 days ago`, `5 weeks ago`,\n * `11 months ago`, `about 1 year ago`, `over 1 year ago`, `almost 2 years ago`.\n */\nexport function formatRelativeTime(from: number, now: number): string {\n const diff = Math.max(0, now - from);\n if (diff < MINUTE) return 'just now';\n if (diff < HOUR) {\n const m = Math.round(diff / MINUTE);\n return `${m} minute${m === 1 ? '' : 's'} ago`;\n }\n if (diff < DAY) {\n const h = Math.round(diff / HOUR);\n return `${h} hour${h === 1 ? '' : 's'} ago`;\n }\n if (diff < WEEK) {\n const d = Math.round(diff / DAY);\n return d <= 1 ? 'yesterday' : `${d} days ago`;\n }\n if (diff < 2 * MONTH) {\n const w = Math.round(diff / WEEK);\n return `${w} week${w === 1 ? '' : 's'} ago`;\n }\n if (diff < YEAR) {\n const mo = Math.round(diff / MONTH);\n return `${mo} months ago`;\n }\n const years = diff / YEAR;\n const whole = Math.floor(years);\n const frac = years - whole;\n if (frac < 0.25) return `about ${yearLabel(whole)}`;\n if (frac < 0.75) return `over ${yearLabel(whole)}`;\n return `almost ${yearLabel(whole + 1)}`;\n}\n\nconst toEpoch = (value: number | string | null | undefined): number | null => {\n if (value === null || value === undefined) return null;\n const t = typeof value === 'number' ? value : Date.parse(value);\n return Number.isFinite(t) ? t : null;\n};\n\nconst formatAbsolute = (t: number) => new Date(t).toISOString().slice(0, 10);\n\nconst hostOf = (url: string) => {\n try {\n return new URL(url.includes('://') ? url : `https://${url}`).hostname.replace(/^www\\./, '');\n } catch {\n return url;\n }\n};\n\nconst compare = (a: RecordRow, b: RecordRow, key: RecordsSortKey): number => {\n switch (key) {\n case 'name':\n return a.name.localeCompare(b.name);\n case 'strength':\n return STRENGTH_BARS[a.strength] - STRENGTH_BARS[b.strength];\n case 'tags':\n return a.tags.length - b.tags.length;\n case 'lastInteraction': {\n const ta = toEpoch(a.lastInteractionAt) ?? Number.NEGATIVE_INFINITY;\n const tb = toEpoch(b.lastInteractionAt) ?? Number.NEGATIVE_INFINITY;\n return ta - tb;\n }\n default:\n return 0;\n }\n};\n\nconst SortIcon: React.FC<{ direction?: 'asc' | 'desc' }> = ({ direction }) => (\n <svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden=\"true\">\n {direction !== 'desc' && <path d=\"m7 10 5-5 5 5\" opacity={direction === 'asc' ? 1 : 0.4} />}\n {direction !== 'asc' && <path d=\"m7 14 5 5 5-5\" opacity={direction === 'desc' ? 1 : 0.4} />}\n </svg>\n);\n\nconst StrengthGlyph: React.FC<{ strength: RecordStrength }> = ({ strength }) => {\n const bars = STRENGTH_BARS[strength];\n return (\n <span className=\"dishui-ai-records-table__signal\" aria-hidden=\"true\">\n {Array.from({ length: 4 }, (_, i) => (\n <span key={i} className={cn('dishui-ai-records-table__bar', i < bars && 'dishui-ai-records-table__bar--on')} />\n ))}\n </span>\n );\n};\n\ntype SortableHeaderProps = {\n label: React.ReactNode;\n sortKey: RecordsSortKey;\n sort: RecordsSort | null;\n onToggle: (key: RecordsSortKey) => void;\n className?: string;\n};\n\nconst SortableHeader: React.FC<SortableHeaderProps> = ({ label, sortKey, sort, onToggle, className }) => {\n const active = sort?.key === sortKey ? sort.direction : undefined;\n return (\n <th\n scope=\"col\"\n className={cn('dishui-ai-records-table__th', className)}\n aria-sort={active ? (active === 'asc' ? 'ascending' : 'descending') : 'none'}\n >\n <button\n type=\"button\"\n className={cn('dishui-ai-records-table__sort', active && 'dishui-ai-records-table__sort--active')}\n onClick={() => onToggle(sortKey)}\n >\n <span>{label}</span>\n <SortIcon direction={active} />\n </button>\n </th>\n );\n};\n\n/**\n * CRM-style grid with tag chips, sortable columns, relative interaction\n * dates and a 4-bar connection-strength glyph paired with its label.\n */\nexport default function RecordsTable({\n records,\n sort: sortProp,\n defaultSort = null,\n onSortChange,\n maxTags = 3,\n showIndex = true,\n footer = true,\n onRowClick,\n selectedId,\n stickyHeader = false,\n maxHeight,\n now: nowProp,\n emptyText,\n labels: labelOverrides,\n className,\n style,\n ...rest\n}: RecordsTableProps) {\n const labels = { ...DEFAULT_LABELS, ...labelOverrides };\n\n const [internalSort, setInternalSort] = useState<RecordsSort | null>(defaultSort);\n const sort = sortProp !== undefined ? sortProp : internalSort;\n const toggleSort = (key: RecordsSortKey) => {\n const next: RecordsSort | null =\n sort?.key !== key ? { key, direction: 'asc' } : sort.direction === 'asc' ? { key, direction: 'desc' } : null;\n if (sortProp === undefined) setInternalSort(next);\n onSortChange?.(next);\n };\n\n // Relative times need a reference \"now\"; never read the clock during render.\n const [mountNow, setMountNow] = useState<number | null>(null);\n useEffect(() => {\n if (nowProp !== undefined) return;\n const id = window.setTimeout(() => setMountNow(Date.now()), 0);\n return () => window.clearTimeout(id);\n }, [nowProp]);\n const now = nowProp ?? mountNow;\n\n const sorted = useMemo(() => {\n if (!sort) return records;\n const dir = sort.direction === 'asc' ? 1 : -1;\n const indexed = records.map((r, i) => ({ r, i }));\n // The copied array is intentionally sorted in place; `records` is never mutated.\n // eslint-disable-next-line unicorn/no-array-sort\n indexed.sort((a, b) => compare(a.r, b.r, sort.key) * dir || a.i - b.i);\n return indexed.map(x => x.r);\n }, [records, sort]);\n\n const stats = useMemo(() => {\n const count = records.length;\n const avg = count ? Math.round(records.reduce((n, r) => n + strengthPercent(r.strength), 0) / count) : 0;\n const links = records.filter(r => r.link).length;\n return { count, avg, links };\n }, [records]);\n\n const interactive = Boolean(onRowClick);\n const scrollStyle: React.CSSProperties | undefined = maxHeight !== undefined ? { maxHeight } : undefined;\n\n return (\n <div {...rest} style={style} className={cn('dishui-ai-records-table', stickyHeader && 'dishui-ai-records-table--sticky', className)}>\n <div className=\"dishui-ai-records-table__scroll\" style={scrollStyle}>\n <table className=\"dishui-ai-records-table__table\">\n <thead>\n <tr>\n {showIndex && <th scope=\"col\" className=\"dishui-ai-records-table__th dishui-ai-records-table__th--index\">{labels.index}</th>}\n <SortableHeader label={labels.company} sortKey=\"name\" sort={sort} onToggle={toggleSort} />\n <SortableHeader label={labels.categories} sortKey=\"tags\" sort={sort} onToggle={toggleSort} />\n <SortableHeader label={labels.lastInteraction} sortKey=\"lastInteraction\" sort={sort} onToggle={toggleSort} />\n <SortableHeader label={labels.strength} sortKey=\"strength\" sort={sort} onToggle={toggleSort} />\n <th scope=\"col\" className=\"dishui-ai-records-table__th\">{labels.links}</th>\n </tr>\n </thead>\n <tbody>\n {sorted.length === 0 && (\n <tr>\n <td className=\"dishui-ai-records-table__td dishui-ai-records-table__td--empty\" colSpan={showIndex ? 6 : 5}>\n {emptyText ?? labels.empty}\n </td>\n </tr>\n )}\n {sorted.map((record, i) => {\n const t = toEpoch(record.lastInteractionAt);\n const shown = record.tags.slice(0, maxTags);\n const hiddenTags = record.tags.slice(maxTags);\n const selected = selectedId === record.id;\n return (\n <tr\n key={record.id}\n className={cn(\n 'dishui-ai-records-table__row',\n interactive && 'dishui-ai-records-table__row--interactive',\n selected && 'dishui-ai-records-table__row--selected',\n )}\n data-selected={selected || undefined}\n tabIndex={interactive ? 0 : undefined}\n onClick={interactive ? () => onRowClick?.(record) : undefined}\n onKeyDown={\n interactive\n ? e => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n onRowClick?.(record);\n }\n }\n : undefined\n }\n >\n {showIndex && <td className=\"dishui-ai-records-table__td dishui-ai-records-table__td--index\">{i + 1}</td>}\n <td className=\"dishui-ai-records-table__td dishui-ai-records-table__td--company\">\n <span className=\"dishui-ai-records-table__avatar\" aria-hidden=\"true\">\n {record.avatar ?? record.name.trim().charAt(0).toUpperCase()}\n </span>\n <span className=\"dishui-ai-records-table__name\">\n {record.name}\n {record.location && <span className=\"dishui-ai-records-table__location\"> — {record.location}</span>}\n </span>\n </td>\n <td className=\"dishui-ai-records-table__td\">\n <span className=\"dishui-ai-records-table__tags\">\n {shown.map(tag => (\n <span key={tag} className=\"dishui-ai-records-table__tag\">{tag}</span>\n ))}\n {hiddenTags.length > 0 && (\n <span className=\"dishui-ai-records-table__tag dishui-ai-records-table__tag--more\" title={hiddenTags.join(', ')}>\n +{hiddenTags.length}\n </span>\n )}\n {record.tags.length === 0 && <span className=\"dishui-ai-records-table__muted\">—</span>}\n </span>\n </td>\n <td className={cn('dishui-ai-records-table__td dishui-ai-records-table__td--time', t === null && 'dishui-ai-records-table__td--muted')}>\n {t === null ? labels.noContact : now === null ? formatAbsolute(t) : <time dateTime={new Date(t).toISOString()} title={formatAbsolute(t)}>{formatRelativeTime(t, now)}</time>}\n </td>\n <td className={cn('dishui-ai-records-table__td dishui-ai-records-table__td--strength', `dishui-ai-records-table__td--${record.strength}`)}>\n <StrengthGlyph strength={record.strength} />\n <span>{STRENGTH_LABEL[record.strength]}</span>\n </td>\n <td className=\"dishui-ai-records-table__td\">\n {record.link ? (\n <a\n className=\"dishui-ai-records-table__link\"\n href={record.link.includes('://') ? record.link : `https://${record.link}`}\n target=\"_blank\"\n rel=\"noreferrer\"\n onClick={e => e.stopPropagation()}\n >\n {hostOf(record.link)}\n </a>\n ) : (\n <span className=\"dishui-ai-records-table__muted\">—</span>\n )}\n </td>\n </tr>\n );\n })}\n </tbody>\n {footer && (\n <tfoot>\n <tr>\n {footer === true ? (\n <>\n {showIndex && <td className=\"dishui-ai-records-table__tf dishui-ai-records-table__td--index\" />}\n <td className=\"dishui-ai-records-table__tf\"><span className=\"dishui-ai-records-table__num\">{stats.count}</span> {labels.count}</td>\n <td className=\"dishui-ai-records-table__tf dishui-ai-records-table__tf--muted\">{labels.addCalculation}</td>\n <td className=\"dishui-ai-records-table__tf dishui-ai-records-table__tf--muted\">—</td>\n <td className=\"dishui-ai-records-table__tf\"><span className=\"dishui-ai-records-table__num\">{stats.avg}%</span> {labels.average}</td>\n <td className=\"dishui-ai-records-table__tf\"><span className=\"dishui-ai-records-table__num\">{stats.links}</span> {labels.linksCount}</td>\n </>\n ) : (\n <td className=\"dishui-ai-records-table__tf\" colSpan={showIndex ? 6 : 5}>{footer}</td>\n )}\n </tr>\n </tfoot>\n )}\n </table>\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../../../src/components/ai/SelectionActions/index.tsx"],"sourcesContent":["import React from 'react';\nimport { cn } from '../../../utils/cn';\nimport './SelectionActions.css';\n\nexport type SelectionAction = { id: string; label: React.ReactNode; icon?: React.ReactNode; onSelect: () => void; disabled?: boolean; disabledReason?: string; destructive?: boolean };\nexport type SelectionActionsProps = Omit<React.HTMLAttributes<HTMLDivElement>, 'children'> & { count: number; actions: SelectionAction[]; onClear?: () => void; selectionLabel?: (count: number) => React.ReactNode; clearLabel?: string; sticky?: boolean };\nconst defaultSelectionLabel = (count: number) => `${count} selected`;\nexport default function SelectionActions({ count, actions, onClear, selectionLabel = defaultSelectionLabel, clearLabel = 'Clear selection', sticky = false, className, ...rest }: SelectionActionsProps) {\n if (count <= 0) return null;\n return <div {...rest} className={cn('dishui-ai-selection-actions', sticky && 'dishui-ai-selection-actions--sticky', className)} role=\"toolbar\" aria-label=\"Selection actions\"><strong>{selectionLabel(count)}</strong><span className=\"dishui-ai-selection-actions__divider\" aria-hidden=\"true\"/>{actions.map(action => <button key={action.id} type=\"button\" className={cn(action.destructive && 'dishui-ai-selection-actions__action--destructive')} disabled={action.disabled} title={action.disabledReason} onClick={action.onSelect}>{action.icon}<span>{action.label}</span></button>)}{onClear && <button type=\"button\" className=\"dishui-ai-selection-actions__clear\" onClick={onClear} aria-label={clearLabel}>×</button>}</div>;\n}\n"],"mappings":";;;;;AAMA,
|
|
1
|
+
{"version":3,"file":"index.js","names":["defaultSelectionLabel","count","SelectionActions","rest","cn","sticky","className","selectionLabel","actions","action","onClear","clearLabel"],"sources":["../../../../src/components/ai/SelectionActions/index.tsx"],"sourcesContent":["import React from 'react';\nimport { cn } from '../../../utils/cn';\nimport './SelectionActions.css';\n\nexport type SelectionAction = { id: string; label: React.ReactNode; icon?: React.ReactNode; onSelect: () => void; disabled?: boolean; disabledReason?: string; destructive?: boolean };\nexport type SelectionActionsProps = Omit<React.HTMLAttributes<HTMLDivElement>, 'children'> & { count: number; actions: SelectionAction[]; onClear?: () => void; selectionLabel?: (count: number) => React.ReactNode; clearLabel?: string; sticky?: boolean };\nconst defaultSelectionLabel = (count: number) => `${count} selected`;\nexport default function SelectionActions({ count, actions, onClear, selectionLabel = defaultSelectionLabel, clearLabel = 'Clear selection', sticky = false, className, ...rest }: SelectionActionsProps) {\n if (count <= 0) return null;\n return <div {...rest} className={cn('dishui-ai-selection-actions', sticky && 'dishui-ai-selection-actions--sticky', className)} role=\"toolbar\" aria-label=\"Selection actions\"><strong>{selectionLabel(count)}</strong><span className=\"dishui-ai-selection-actions__divider\" aria-hidden=\"true\"/>{actions.map(action => <button key={action.id} type=\"button\" className={cn(action.destructive && 'dishui-ai-selection-actions__action--destructive')} disabled={action.disabled} title={action.disabledReason} onClick={action.onSelect}>{action.icon}<span>{action.label}</span></button>)}{onClear && <button type=\"button\" className=\"dishui-ai-selection-actions__clear\" onClick={onClear} aria-label={clearLabel}>×</button>}</div>;\n}\n"],"mappings":";;;;;AAMA,IAAMA,KAAyB,MAAkB,GAAGC,EAAM;AAC1D,SAAwBC,EAAiB,EAAE,UAAO,YAAS,YAAS,oBAAiBF,GAAuB,gBAAa,mBAAmB,YAAS,IAAO,cAAW,GAAGG,KAA+B;CAEvM,OADIF,KAAS,IAAU,OAChB,kBAAC,OAAD;EAAK,GAAIE;EAAM,WAAWC,EAAG,+BAA+BC,KAAU,uCAAuCC,CAAS;EAAG,MAAK;EAAU,cAAW;EAAnJ,UAAA;GAAuK,kBAAC,UAAD,EAAA,UAASC,EAAeN,CAAK,EAAU,CAAA;GAAC,kBAAC,QAAD;IAAM,WAAU;IAAuC,eAAY;GAAO,CAAA;GAAEO,EAAQ,KAAI,MAAU,kBAAC,UAAD;IAAwB,MAAK;IAAS,WAAWJ,EAAGK,EAAO,eAAe,kDAAkD;IAAG,UAAUA,EAAO;IAAU,OAAOA,EAAO;IAAgB,SAASA,EAAO;IAAxM,UAAA,CAAmNA,EAAO,MAAK,kBAAC,QAAD,EAAA,UAAOA,EAAO,MAAY,CAAA,CAAS;GAArP,GAAAA,EAAO,EAA8O,CAAC;GAAGC,KAAW,kBAAC,UAAD;IAAQ,MAAK;IAAS,WAAU;IAAqC,SAASA;IAAS,cAAYC;IAAY,UAAA;GAAS,CAAA;EAAO;;AAC1sB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../../../src/components/ai/StateFlow/index.tsx"],"sourcesContent":["import React, { useId, useMemo, useState } from 'react';\nimport { cn } from '../../../utils/cn';\nimport './StateFlow.css';\n\nexport type StateFlowNode = { id: string; label: React.ReactNode; description?: React.ReactNode; status?: 'pending' | 'running' | 'completed' | 'failed'; shape?: 'process' | 'decision' | 'terminal'; disabled?: boolean };\nexport type StateFlowEdge = { from: string; to: string; label?: React.ReactNode };\nexport type StateFlowProps = Omit<React.HTMLAttributes<HTMLDivElement>, 'children' | 'onSelect'> & { nodes: StateFlowNode[]; edges: StateFlowEdge[]; selectedId?: string; defaultSelectedId?: string; onSelect?: (node: StateFlowNode) => void; direction?: 'vertical' | 'horizontal'; emptyText?: React.ReactNode };\n\nexport default function StateFlow({ nodes, edges, selectedId, defaultSelectedId, onSelect, direction = 'vertical', emptyText = 'No steps in this flow', className, ...rest }: StateFlowProps) {\n const uid = useId(); const [internal, setInternal] = useState(defaultSelectedId); const selected = selectedId ?? internal;\n const incoming = useMemo(() => { const map = new Map<string, StateFlowEdge[]>(); edges.forEach(edge => map.set(edge.to, [...(map.get(edge.to) ?? []), edge])); return map; }, [edges]);\n if (!nodes.length) return <div {...rest} className={cn('dishui-ai-state-flow dishui-ai-state-flow--empty', className)}>{emptyText}</div>;\n const select = (node: StateFlowNode) => { if (node.disabled) return; if (selectedId === undefined) setInternal(node.id); onSelect?.(node); };\n return <div {...rest} className={cn('dishui-ai-state-flow', `dishui-ai-state-flow--${direction}`, className)} role=\"list\" aria-label=\"Workflow\">{nodes.map((node, index) => <React.Fragment key={node.id}>{index > 0 && <div className=\"dishui-ai-state-flow__connector\" aria-hidden=\"true\"><span>↓</span>{(incoming.get(node.id) ?? []).map(edge => edge.label && <small key={`${edge.from}-${edge.to}`}>{edge.label}</small>)}</div>}<button type=\"button\" id={`${uid}-${node.id}`} role=\"listitem\" disabled={node.disabled} aria-current={selected === node.id ? 'step' : undefined} className={cn('dishui-ai-state-flow__node', `dishui-ai-state-flow__node--${node.shape ?? 'process'}`, node.status && `dishui-ai-state-flow__node--${node.status}`, selected === node.id && 'dishui-ai-state-flow__node--selected')} onClick={() => select(node)}><span className=\"dishui-ai-state-flow__status\">{node.status ?? 'step'}</span><strong>{node.label}</strong>{node.description && <small>{node.description}</small>}</button></React.Fragment>)}</div>;\n}\n"],"mappings":";;;;;AAQA,
|
|
1
|
+
{"version":3,"file":"index.js","names":["React","StateFlow","rest","uid","useId","internal","setInternal","useState","defaultSelectedId","selected","selectedId","incoming","useMemo","map","edges","edge","nodes","cn","className","emptyText","select","node","onSelect","direction","index"],"sources":["../../../../src/components/ai/StateFlow/index.tsx"],"sourcesContent":["import React, { useId, useMemo, useState } from 'react';\nimport { cn } from '../../../utils/cn';\nimport './StateFlow.css';\n\nexport type StateFlowNode = { id: string; label: React.ReactNode; description?: React.ReactNode; status?: 'pending' | 'running' | 'completed' | 'failed'; shape?: 'process' | 'decision' | 'terminal'; disabled?: boolean };\nexport type StateFlowEdge = { from: string; to: string; label?: React.ReactNode };\nexport type StateFlowProps = Omit<React.HTMLAttributes<HTMLDivElement>, 'children' | 'onSelect'> & { nodes: StateFlowNode[]; edges: StateFlowEdge[]; selectedId?: string; defaultSelectedId?: string; onSelect?: (node: StateFlowNode) => void; direction?: 'vertical' | 'horizontal'; emptyText?: React.ReactNode };\n\nexport default function StateFlow({ nodes, edges, selectedId, defaultSelectedId, onSelect, direction = 'vertical', emptyText = 'No steps in this flow', className, ...rest }: StateFlowProps) {\n const uid = useId(); const [internal, setInternal] = useState(defaultSelectedId); const selected = selectedId ?? internal;\n const incoming = useMemo(() => { const map = new Map<string, StateFlowEdge[]>(); edges.forEach(edge => map.set(edge.to, [...(map.get(edge.to) ?? []), edge])); return map; }, [edges]);\n if (!nodes.length) return <div {...rest} className={cn('dishui-ai-state-flow dishui-ai-state-flow--empty', className)}>{emptyText}</div>;\n const select = (node: StateFlowNode) => { if (node.disabled) return; if (selectedId === undefined) setInternal(node.id); onSelect?.(node); };\n return <div {...rest} className={cn('dishui-ai-state-flow', `dishui-ai-state-flow--${direction}`, className)} role=\"list\" aria-label=\"Workflow\">{nodes.map((node, index) => <React.Fragment key={node.id}>{index > 0 && <div className=\"dishui-ai-state-flow__connector\" aria-hidden=\"true\"><span>↓</span>{(incoming.get(node.id) ?? []).map(edge => edge.label && <small key={`${edge.from}-${edge.to}`}>{edge.label}</small>)}</div>}<button type=\"button\" id={`${uid}-${node.id}`} role=\"listitem\" disabled={node.disabled} aria-current={selected === node.id ? 'step' : undefined} className={cn('dishui-ai-state-flow__node', `dishui-ai-state-flow__node--${node.shape ?? 'process'}`, node.status && `dishui-ai-state-flow__node--${node.status}`, selected === node.id && 'dishui-ai-state-flow__node--selected')} onClick={() => select(node)}><span className=\"dishui-ai-state-flow__status\">{node.status ?? 'step'}</span><strong>{node.label}</strong>{node.description && <small>{node.description}</small>}</button></React.Fragment>)}</div>;\n}\n"],"mappings":";;;;;AAQA,SAAwBC,EAAU,EAAE,UAAO,UAAO,eAAY,sBAAmB,aAAU,eAAY,YAAY,eAAY,yBAAyB,cAAW,GAAGC,KAAwB;CAC5L,IAAMC,IAAMC,EAAM,GAAS,CAACC,GAAUC,KAAeC,EAASC,CAAiB,GAASC,IAAWC,KAAcL,GAC3GM,IAAWC,QAAc;EAAE,IAAMC,oBAAM,IAAI,IAA6B;EAAiF,OAA9EC,EAAM,SAAQ,MAAQD,EAAI,IAAIE,EAAK,IAAI,CAAC,GAAIF,EAAI,IAAIE,EAAK,EAAE,KAAK,CAAC,GAAIA,CAAI,CAAC,CAAC,GAAUF;CAAK,GAAG,CAACC,CAAK,CAAC;CACrL,IAAI,CAACE,EAAM,QAAQ,OAAO,kBAAC,OAAD;EAAK,GAAId;EAAM,WAAWe,EAAG,oDAAoDC,CAAS;EAAI,UAAAC;CAAe,CAAA;CACvI,IAAMC,KAAU,MAAwB;EAAE,AAAIC,EAAK,aAAsBX,MAAe,KAAA,KAAWJ,EAAYe,EAAK,EAAE,GAAGC,IAAWD,CAAI;CAAG;CAC3I,OAAO,kBAAC,OAAD;EAAK,GAAInB;EAAM,WAAWe,EAAG,wBAAwB,yBAAyBM,KAAaL,CAAS;EAAG,MAAK;EAAO,cAAW;EAAY,UAAAF,EAAM,KAAK,GAAM,MAAU,kBAAChB,EAAM,UAAP,EAAA,UAAA,CAA+BwB,IAAQ,KAAK,kBAAC,OAAD;GAAK,WAAU;GAAkC,eAAY;GAA7D,UAAA,CAAoE,kBAAC,QAAD,EAAA,UAAM,IAAO,CAAA,IAAGb,EAAS,IAAIU,EAAK,EAAE,KAAK,CAAC,EAAA,CAAG,KAAI,MAAQN,EAAK,SAAS,kBAAC,SAAD,EAAA,UAAwCA,EAAK,MAAa,GAA9C,GAAGA,EAAK,KAAK,GAAGA,EAAK,IAAyB,CAAC,CAAO;EAAE,CAAA,GAAA,kBAAC,UAAD;GAAQ,MAAK;GAAS,IAAI,GAAGZ,EAAI,GAAGkB,EAAK;GAAM,MAAK;GAAW,UAAUA,EAAK;GAAU,gBAAcZ,MAAaY,EAAK,KAAK,SAAS,KAAA;GAAW,WAAWJ,EAAG,8BAA8B,+BAA+BI,EAAK,SAAS,aAAaA,EAAK,UAAU,+BAA+BA,EAAK,UAAUZ,MAAaY,EAAK,MAAM,sCAAsC;GAAG,eAAeD,EAAOC,CAAI;GAA/Y,UAAA;IAAkZ,kBAAC,QAAD;KAAM,WAAU;KAAgC,UAAAA,EAAK,UAAU;IAAa,CAAA;IAAC,kBAAC,UAAD,EAAA,UAASA,EAAK,MAAc,CAAA;IAAEA,EAAK,eAAe,kBAAC,SAAD,EAAA,UAAQA,EAAK,YAAmB,CAAA;GAAU;EAAiB,CAAA,CAAA,EAAA,GAAlzBA,EAAK,EAA6yB,CAAC;CAAO,CAAA;AAC7/B"}
|