@refraction-ui/react 0.20.1 → 0.22.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-ZF3OHCGV.js → chunk-2Q6HHFAN.js} +1 -1
- package/dist/chunk-2Q6HHFAN.js.map +7 -0
- package/dist/{chunk-533MCMSU.js → chunk-3I3DYDHR.js} +2 -2
- package/dist/{chunk-F7B64KT3.js → chunk-3Y2T26QD.js} +13 -7
- package/dist/chunk-3Y2T26QD.js.map +7 -0
- package/dist/{chunk-H54FLNAF.js → chunk-63N4PZUJ.js} +2 -2
- package/dist/{chunk-Y2FNNAIL.js → chunk-EGVPAMQU.js} +2 -2
- package/dist/chunk-EGVPAMQU.js.map +1 -0
- package/dist/{chunk-WK2IIUFO.js → chunk-QDMLIDYN.js} +2 -2
- package/dist/{chunk-BOLFYVI6.js → chunk-QPCKJ2YR.js} +2 -2
- package/dist/{chunk-TOZBR4GL.js → chunk-RSWOLRIC.js} +4 -4
- package/dist/{chunk-TOZBR4GL.js.map → chunk-RSWOLRIC.js.map} +1 -1
- package/dist/{chunk-UNEM2TRI.js → chunk-SDAJVXD4.js} +2 -2
- package/dist/{chunk-VK3EQ34F.js → chunk-V24GDHUX.js} +2 -2
- package/dist/{chunk-AMK4VCBG.js → chunk-WYKPPK3W.js} +2 -2
- package/dist/{chunk-ONACRMG4.js → chunk-XMFNBOPO.js} +2 -2
- package/dist/form.cjs.map +1 -1
- package/dist/form.js +1 -1
- package/dist/index.cjs +632 -47
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +4 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +4 -0
- package/dist/internal/action-list/index.d.ts +39 -0
- package/dist/internal/badge/index.d.cts +2 -2
- package/dist/internal/badge/index.d.ts +2 -2
- package/dist/internal/button/index.d.cts +4 -4
- package/dist/internal/button/index.d.ts +4 -4
- package/dist/internal/card/index.d.cts +14 -2
- package/dist/internal/card/index.d.ts +14 -2
- package/dist/internal/funnel-chart/index.d.ts +60 -0
- package/dist/internal/line-chart/index.d.ts +110 -0
- package/dist/internal/page-header/index.d.ts +35 -0
- package/dist/internal/react-action-list/index.d.ts +28 -0
- package/dist/internal/react-card/index.d.cts +12 -3
- package/dist/internal/react-card/index.d.ts +12 -3
- package/dist/internal/react-funnel-chart/index.d.ts +28 -0
- package/dist/internal/react-line-chart/index.d.ts +33 -0
- package/dist/internal/react-page-header/index.d.ts +23 -0
- package/dist/internal/react-select/index.d.cts +21 -3
- package/dist/internal/react-select/index.d.ts +21 -3
- package/dist/internal/react-separator/index.d.cts +6 -1
- package/dist/internal/react-separator/index.d.ts +6 -1
- package/dist/internal/select/index.d.cts +14 -1
- package/dist/internal/select/index.d.ts +14 -1
- package/dist/internal/separator/index.d.cts +6 -1
- package/dist/internal/separator/index.d.ts +6 -1
- package/dist/internal/shared/index.d.cts +6 -3
- package/dist/internal/shared/index.d.ts +6 -3
- package/dist/internal/tabs/index.d.cts +8 -1
- package/dist/internal/tabs/index.d.ts +8 -1
- package/dist/react-accordion.js +1 -1
- package/dist/react-action-list.js +74 -0
- package/dist/react-action-list.js.map +7 -0
- package/dist/react-ai.js +1 -1
- package/dist/react-analytics.js +1 -1
- package/dist/react-animated-text.js +1 -1
- package/dist/react-app-shell.js +1 -1
- package/dist/react-audience-feature-card.js +1 -1
- package/dist/react-audio-room.js +1 -1
- package/dist/react-auth.js +1 -1
- package/dist/react-avatar-group.js +1 -1
- package/dist/react-avatar.js +1 -1
- package/dist/react-badge.js +16 -4
- package/dist/react-badge.js.map +2 -2
- package/dist/react-bottom-nav.js +1 -1
- package/dist/react-brand-network-cell.js +1 -1
- package/dist/react-breadcrumbs.js +1 -1
- package/dist/react-browser-chrome-mock.js +1 -1
- package/dist/react-browser-chrome-mock.js.map +2 -2
- package/dist/react-button.js +3 -3
- package/dist/react-calendar.js +1 -1
- package/dist/react-call-controls.js +1 -1
- package/dist/react-callout.js +8 -3
- package/dist/react-callout.js.map +2 -2
- package/dist/react-card-grid.js +1 -1
- package/dist/react-card.js +15 -6
- package/dist/react-card.js.map +2 -2
- package/dist/react-carousel.js +1 -1
- package/dist/react-charts.js +1 -1
- package/dist/react-checkbox.js +1 -1
- package/dist/react-checklist.js +1 -1
- package/dist/react-code-block.js +1 -1
- package/dist/react-code-editor.js +1 -1
- package/dist/react-collapsible.js +1 -1
- package/dist/react-combobox.js +1 -1
- package/dist/react-command-input.js +1 -1
- package/dist/react-command.js +1 -1
- package/dist/react-composer.js +3 -3
- package/dist/react-content-protection.js +1 -1
- package/dist/react-conversation.js +5 -5
- package/dist/react-cookie-consent.js +1 -1
- package/dist/react-data-table.js +1 -1
- package/dist/react-date-picker.js +1 -1
- package/dist/react-device-frame.js +1 -1
- package/dist/react-dialog.js +2 -2
- package/dist/react-diff-viewer.js +4 -1
- package/dist/react-diff-viewer.js.map +2 -2
- package/dist/react-dropdown-menu.js +1 -1
- package/dist/react-editor-status-bar.js +1 -1
- package/dist/react-editor-tabs.js +1 -1
- package/dist/react-emoji-picker.js +2 -2
- package/dist/react-empty-state.js +1 -1
- package/dist/react-feedback-dialog.js +2 -2
- package/dist/react-file-tree.js +1 -1
- package/dist/react-file-upload.js +1 -1
- package/dist/react-floating-reactions.js +1 -1
- package/dist/react-flow-editor.js +2 -2
- package/dist/react-footer.js +1 -1
- package/dist/react-funnel-chart.js +105 -0
- package/dist/react-funnel-chart.js.map +7 -0
- package/dist/react-graph-view.js +2 -2
- package/dist/react-infinite-canvas.js +2 -2
- package/dist/react-inline-editor.js +1 -1
- package/dist/react-input-group.js +1 -1
- package/dist/react-input.js +2 -2
- package/dist/react-install-prompt.js +1 -1
- package/dist/react-kanban-board.js +1 -1
- package/dist/react-keyboard-shortcut.js +2 -2
- package/dist/react-language-selector.js +1 -1
- package/dist/react-line-chart.js +242 -0
- package/dist/react-line-chart.js.map +7 -0
- package/dist/react-link-card.js +1 -1
- package/dist/react-live-captions.js +1 -1
- package/dist/react-live-cursors.js +1 -1
- package/dist/react-live-transcript.js +1 -1
- package/dist/react-location-selector.js +1 -1
- package/dist/react-logger.js +1 -1
- package/dist/react-markdown-renderer.js +2 -2
- package/dist/react-marquee-strip.js +1 -1
- package/dist/react-mascot.js +1 -1
- package/dist/react-mastery-bar.js +1 -1
- package/dist/react-mini-map.js +1 -1
- package/dist/react-mobile-nav.js +1 -1
- package/dist/react-navbar.js +1 -1
- package/dist/react-numbered-steps.js +1 -1
- package/dist/react-otp-input.js +1 -1
- package/dist/react-page-header.js +40 -0
- package/dist/react-page-header.js.map +7 -0
- package/dist/react-pagination.js +1 -1
- package/dist/react-password-input.js +2 -2
- package/dist/react-payment.js +1 -1
- package/dist/react-popover.js +1 -1
- package/dist/react-pre-call-lobby.js +1 -1
- package/dist/react-presence-indicator.js +1 -1
- package/dist/react-pricing-card.js +1 -1
- package/dist/react-progress-display.js +2 -2
- package/dist/react-progress-display.js.map +1 -1
- package/dist/react-radial-gauge.js +1 -1
- package/dist/react-radio.js +1 -1
- package/dist/react-rating-scale.js +1 -1
- package/dist/react-reaction-bar.js +1 -1
- package/dist/react-resizable-layout.js +1 -1
- package/dist/react-search-bar.js +1 -1
- package/dist/react-section-head.js +1 -1
- package/dist/react-segmented-control.js +1 -1
- package/dist/react-select.js +94 -20
- package/dist/react-select.js.map +3 -3
- package/dist/react-separator.js +19 -7
- package/dist/react-separator.js.map +2 -2
- package/dist/react-sheet.js +1 -1
- package/dist/react-sidebar.js +1 -1
- package/dist/react-skeleton.js +1 -1
- package/dist/react-skip-to-content.js +1 -1
- package/dist/react-slide-viewer.js +3 -1
- package/dist/react-slide-viewer.js.map +2 -2
- package/dist/react-slider.js +1 -1
- package/dist/react-slot-picker.js +1 -1
- package/dist/react-social-auth-button.js +3 -3
- package/dist/react-sortable-list.js +1 -1
- package/dist/react-stat-grid.js +1 -1
- package/dist/react-status-indicator.js +1 -1
- package/dist/react-steps.js +1 -1
- package/dist/react-sticky-note.js +3 -1
- package/dist/react-sticky-note.js.map +2 -2
- package/dist/react-switch.js +1 -1
- package/dist/react-table-of-contents.js +1 -1
- package/dist/react-tabs.js +50 -2
- package/dist/react-tabs.js.map +2 -2
- package/dist/react-terminal.js +1 -1
- package/dist/react-test-results.js +1 -1
- package/dist/react-textarea.js +3 -3
- package/dist/react-textarea.js.map +1 -1
- package/dist/react-thread-view.js +2 -2
- package/dist/react-timeline.js +1 -1
- package/dist/react-toast.js +1 -1
- package/dist/react-tooltip.js +1 -1
- package/dist/react-version-selector.js +1 -1
- package/dist/react-video-grid.js +2 -2
- package/dist/react-video-player.js +1 -1
- package/dist/react-video-tile.js +2 -2
- package/dist/react-voice-pill.js +1 -1
- package/dist/react-waveform.js +1 -1
- package/dist/react-wizard.js +1 -1
- package/dist/styles.css +48 -0
- package/dist/theme.js +1 -1
- package/package.json +5 -1
- package/dist/chunk-F7B64KT3.js.map +0 -7
- package/dist/chunk-Y2FNNAIL.js.map +0 -1
- package/dist/chunk-ZF3OHCGV.js.map +0 -7
- /package/dist/{chunk-533MCMSU.js.map → chunk-3I3DYDHR.js.map} +0 -0
- /package/dist/{chunk-H54FLNAF.js.map → chunk-63N4PZUJ.js.map} +0 -0
- /package/dist/{chunk-WK2IIUFO.js.map → chunk-QDMLIDYN.js.map} +0 -0
- /package/dist/{chunk-BOLFYVI6.js.map → chunk-QPCKJ2YR.js.map} +0 -0
- /package/dist/{chunk-UNEM2TRI.js.map → chunk-SDAJVXD4.js.map} +0 -0
- /package/dist/{chunk-VK3EQ34F.js.map → chunk-V24GDHUX.js.map} +0 -0
- /package/dist/{chunk-AMK4VCBG.js.map → chunk-WYKPPK3W.js.map} +0 -0
- /package/dist/{chunk-ONACRMG4.js.map → chunk-XMFNBOPO.js.map} +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../diff-viewer/src/diff-viewer.ts", "../../diff-viewer/src/diff-viewer.styles.ts", "../../react-diff-viewer/src/DiffViewer.tsx"],
|
|
4
|
-
"sourcesContent": ["import type { AccessibilityProps } from '@refraction-ui/shared'\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport type DiffViewerTheme = 'light' | 'dark'\nexport type DiffViewMode = 'side-by-side' | 'inline'\n\nexport type DiffFileStatus = 'added' | 'modified' | 'deleted' | 'renamed'\n\nexport interface DiffFile {\n /** File path */\n path: string\n /** Change status */\n status: DiffFileStatus\n /** Lines added */\n additions: number\n /** Lines deleted */\n deletions: number\n /** Unified diff content for this file */\n diff?: string\n /** Old path for renamed files */\n oldPath?: string\n}\n\nexport interface DiffViewerProps {\n /** Files in this diff */\n files?: DiffFile[]\n /** Index of the active file */\n activeFileIndex?: number\n /** View mode */\n viewMode?: DiffViewMode\n /** Theme */\n theme?: DiffViewerTheme\n /** Language for syntax highlighting */\n language?: string\n /** Whether the sidebar is visible */\n sidebarOpen?: boolean\n /** Callback when active file changes */\n onFileSelect?: (index: number) => void\n /** Callback when view mode changes */\n onViewModeChange?: (mode: DiffViewMode) => void\n /** Callback when sidebar toggled */\n onSidebarToggle?: () => void\n}\n\nexport interface DiffViewerState {\n activeFileIndex: number\n viewMode: DiffViewMode\n theme: DiffViewerTheme\n language: string\n sidebarOpen: boolean\n files: DiffFile[]\n}\n\nexport interface DiffViewerAPI {\n /** Current viewer state */\n state: DiffViewerState\n /** ARIA attributes for the root element */\n ariaProps: Partial<AccessibilityProps> & Record<string, unknown>\n /** Data attributes for CSS styling hooks */\n dataAttributes: Record<string, string>\n /** Select a file by index */\n selectFile: (index: number) => void\n /** Move to next file */\n nextFile: () => void\n /** Move to previous file */\n prevFile: () => void\n /** Toggle view mode */\n toggleViewMode: () => void\n /** Toggle sidebar */\n toggleSidebar: () => void\n /** Get language label for a file path */\n getLanguageForFile: (path: string) => string\n /** Get human-readable file status icon */\n getFileStatusIcon: (status: DiffFileStatus) => string\n /** Get total additions across all files */\n totalAdditions: () => number\n /** Get total deletions across all files */\n totalDeletions: () => number\n}\n\n// ---------------------------------------------------------------------------\n// Language detection\n// ---------------------------------------------------------------------------\n\nconst extToLanguage: Record<string, string> = {\n ts: 'typescript',\n tsx: 'typescript',\n js: 'javascript',\n jsx: 'javascript',\n py: 'python',\n go: 'go',\n rs: 'rust',\n rb: 'ruby',\n java: 'java',\n swift: 'swift',\n kt: 'kotlin',\n dart: 'dart',\n php: 'php',\n lua: 'lua',\n r: 'r',\n scala: 'scala',\n json: 'json',\n yaml: 'yaml',\n yml: 'yaml',\n md: 'markdown',\n html: 'html',\n css: 'css',\n scss: 'scss',\n sql: 'sql',\n sh: 'shell',\n bash: 'shell',\n zsh: 'shell',\n xml: 'xml',\n toml: 'toml',\n proto: 'protobuf',\n dockerfile: 'dockerfile',\n cpp: 'cpp',\n c: 'c',\n}\n\nfunction getLanguageForFile(filePath: string): string {\n const ext = filePath.split('.').pop()?.toLowerCase() || ''\n const basename = filePath.split('/').pop()?.toLowerCase() || ''\n if (basename === 'dockerfile') return 'dockerfile'\n return extToLanguage[ext] || 'plaintext'\n}\n\n// ---------------------------------------------------------------------------\n// Status icons\n// ---------------------------------------------------------------------------\n\nconst statusIcons: Record<DiffFileStatus, string> = {\n added: '\\u{1F7E2}', // green circle\n modified: '\\u{1F7E1}', // yellow circle\n deleted: '\\u{1F534}', // red circle\n renamed: '\\u{1F535}', // blue circle\n}\n\n// ---------------------------------------------------------------------------\n// Factory\n// ---------------------------------------------------------------------------\n\n/**\n * Create a headless diff viewer API.\n *\n * Manages file selection, view mode, sidebar state, and produces ARIA/data\n * attributes. The actual rendering (Monaco DiffEditor, custom renderer, etc.)\n * is handled by framework-specific wrappers.\n */\nexport function createDiffViewer(props: DiffViewerProps = {}): DiffViewerAPI {\n const {\n files = [],\n activeFileIndex = 0,\n viewMode = 'side-by-side',\n theme = 'dark',\n language,\n sidebarOpen = true,\n onFileSelect,\n onViewModeChange,\n onSidebarToggle,\n } = props\n\n let currentIndex = Math.min(activeFileIndex, Math.max(0, files.length - 1))\n let currentViewMode = viewMode\n let currentSidebarOpen = sidebarOpen\n\n const state: DiffViewerState = {\n activeFileIndex: currentIndex,\n viewMode: currentViewMode,\n theme,\n language: language || (files[currentIndex] ? getLanguageForFile(files[currentIndex].path) : 'plaintext'),\n sidebarOpen: currentSidebarOpen,\n files,\n }\n\n const ariaProps: Partial<AccessibilityProps> & Record<string, unknown> = {\n role: 'region',\n 'aria-label': 'Diff viewer',\n }\n\n const dataAttributes: Record<string, string> = {\n 'data-view-mode': currentViewMode,\n 'data-theme': theme,\n 'data-file-count': String(files.length),\n }\n\n if (!currentSidebarOpen) {\n dataAttributes['data-sidebar-collapsed'] = ''\n }\n\n function selectFile(index: number) {\n if (index >= 0 && index < files.length) {\n currentIndex = index\n state.activeFileIndex = index\n state.language = language || getLanguageForFile(files[index].path)\n onFileSelect?.(index)\n }\n }\n\n function nextFile() {\n if (currentIndex < files.length - 1) {\n selectFile(currentIndex + 1)\n }\n }\n\n function prevFile() {\n if (currentIndex > 0) {\n selectFile(currentIndex - 1)\n }\n }\n\n function toggleViewMode() {\n currentViewMode = currentViewMode === 'side-by-side' ? 'inline' : 'side-by-side'\n state.viewMode = currentViewMode\n dataAttributes['data-view-mode'] = currentViewMode\n onViewModeChange?.(currentViewMode)\n }\n\n function toggleSidebar() {\n currentSidebarOpen = !currentSidebarOpen\n state.sidebarOpen = currentSidebarOpen\n if (currentSidebarOpen) {\n delete dataAttributes['data-sidebar-collapsed']\n } else {\n dataAttributes['data-sidebar-collapsed'] = ''\n }\n onSidebarToggle?.()\n }\n\n function totalAdditions(): number {\n return files.reduce((sum, f) => sum + f.additions, 0)\n }\n\n function totalDeletions(): number {\n return files.reduce((sum, f) => sum + f.deletions, 0)\n }\n\n return {\n state,\n ariaProps,\n dataAttributes,\n selectFile,\n nextFile,\n prevFile,\n toggleViewMode,\n toggleSidebar,\n getLanguageForFile,\n getFileStatusIcon: (status: DiffFileStatus) => statusIcons[status] || '\\u{26AA}',\n totalAdditions,\n totalDeletions,\n }\n}\n", "import { cva } from '@refraction-ui/shared'\nimport type { TokenContract } from '@refraction-ui/shared'\n\nexport const diffViewerTokens: TokenContract = {\n name: 'diff-viewer',\n tokens: {\n bg: { variable: '--rfr-diff-bg', fallback: 'hsl(var(--background))' },\n fg: { variable: '--rfr-diff-fg', fallback: 'hsl(var(--foreground))' },\n sidebarBg: { variable: '--rfr-diff-sidebar-bg', fallback: 'hsl(var(--muted))' },\n headerBg: { variable: '--rfr-diff-header-bg', fallback: 'hsl(var(--muted))' },\n border: { variable: '--rfr-diff-border', fallback: 'hsl(var(--border))' },\n addBg: { variable: '--rfr-diff-add-bg', fallback: 'rgba(46, 160, 67, 0.15)' },\n delBg: { variable: '--rfr-diff-del-bg', fallback: 'rgba(248, 81, 73, 0.15)' },\n addFg: { variable: '--rfr-diff-add-fg', fallback: '#3fb950' },\n delFg: { variable: '--rfr-diff-del-fg', fallback: '#f85149' },\n statusbar: { variable: '--rfr-diff-statusbar', fallback: 'hsl(var(--primary))' },\n statusbarFg: { variable: '--rfr-diff-statusbar-fg', fallback: 'hsl(var(--primary-foreground))' },\n },\n}\n\nexport const diffViewerVariants = cva({\n base: 'flex flex-col overflow-hidden font-sans text-sm',\n variants: {\n theme: {\n light: 'bg-white text-gray-900',\n dark: 'bg-gray-950 text-gray-100',\n },\n fullscreen: {\n 'true': 'h-screen',\n 'false': '',\n },\n },\n defaultVariants: {\n theme: 'dark',\n fullscreen: 'true',\n },\n})\n\nexport const sidebarVariants = cva({\n base: 'border-r overflow-auto flex-shrink-0',\n variants: {\n theme: {\n light: 'bg-gray-50 border-gray-200',\n dark: 'bg-gray-900 border-gray-800',\n },\n },\n defaultVariants: {\n theme: 'dark',\n },\n})\n\nexport const sidebarItemVariants = cva({\n base: 'px-2.5 py-1 cursor-pointer text-xs font-mono transition-colors',\n variants: {\n active: {\n 'true': 'border-l-2 border-blue-500',\n 'false': 'border-l-2 border-transparent',\n },\n theme: {\n light: '',\n dark: '',\n },\n },\n compoundVariants: [\n { active: 'true', theme: 'dark', class: 'bg-gray-800 text-gray-100' },\n { active: 'false', theme: 'dark', class: 'text-gray-400 hover:bg-gray-800/50' },\n { active: 'true', theme: 'light', class: 'bg-blue-50 text-gray-900' },\n { active: 'false', theme: 'light', class: 'text-gray-600 hover:bg-gray-100' },\n ],\n defaultVariants: {\n active: 'false',\n theme: 'dark',\n },\n})\n\nexport const tabBarVariants = cva({\n base: 'flex items-center border-b overflow-auto flex-shrink-0',\n variants: {\n theme: {\n light: 'bg-gray-50 border-gray-200',\n dark: 'bg-gray-900 border-gray-800',\n },\n },\n defaultVariants: {\n theme: 'dark',\n },\n})\n\nexport const tabVariants = cva({\n base: 'px-3 h-[35px] flex items-center gap-1 cursor-pointer text-xs font-mono border-r shrink-0',\n variants: {\n active: {\n 'true': '',\n 'false': '',\n },\n theme: {\n light: 'border-gray-200',\n dark: 'border-gray-800',\n },\n },\n compoundVariants: [\n { active: 'true', theme: 'dark', class: 'bg-gray-950 text-gray-100 border-b-gray-950 -mb-px' },\n { active: 'false', theme: 'dark', class: 'text-gray-400' },\n { active: 'true', theme: 'light', class: 'bg-white text-gray-900 border-b-white -mb-px' },\n { active: 'false', theme: 'light', class: 'text-gray-500' },\n ],\n defaultVariants: {\n active: 'false',\n theme: 'dark',\n },\n})\n\nexport const statusBarVariants = cva({\n base: 'h-[22px] flex items-center px-2.5 gap-3 text-[11px] font-mono flex-shrink-0',\n variants: {\n theme: {\n light: 'bg-blue-600 text-white',\n dark: 'bg-blue-700 text-white',\n },\n },\n defaultVariants: {\n theme: 'dark',\n },\n})\n", "import * as React from 'react'\nimport {\n createDiffViewer,\n diffViewerVariants,\n sidebarVariants,\n sidebarItemVariants,\n tabBarVariants,\n tabVariants,\n statusBarVariants,\n type DiffFile,\n type DiffViewerTheme,\n type DiffViewMode,\n} from '@refraction-ui/diff-viewer'\nimport { cn } from '@refraction-ui/shared'\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface DiffViewerProps {\n /** Files in this diff */\n files: DiffFile[]\n /** Original file content (left side) */\n original?: string\n /** Modified file content (right side) */\n modified?: string\n /** Language for syntax highlighting (auto-detected from file if omitted) */\n language?: string\n /** Monaco theme name */\n monacoTheme?: string\n /** Color theme */\n theme?: DiffViewerTheme\n /** View mode */\n viewMode?: DiffViewMode\n /** Whether to show the file sidebar */\n showSidebar?: boolean\n /** Whether to show the tab bar */\n showTabs?: boolean\n /** Whether to show the status bar */\n showStatusBar?: boolean\n /** Sidebar width in pixels */\n sidebarWidth?: number\n /** Active file index */\n activeFileIndex?: number\n /** Called when the active file changes */\n onFileSelect?: (index: number) => void\n /** Called when view mode changes */\n onViewModeChange?: (mode: DiffViewMode) => void\n /** Title shown in the status bar */\n statusBarTitle?: string\n /** Status text shown in the status bar */\n statusBarStatus?: string\n /** Additional class name */\n className?: string\n /** Monaco DiffEditor options override */\n editorOptions?: Record<string, unknown>\n}\n\n// ---------------------------------------------------------------------------\n// Lazy Monaco import\n// ---------------------------------------------------------------------------\n\nconst MonacoDiffEditor = React.lazy(() =>\n import('@monaco-editor/react').then((m) => ({ default: m.DiffEditor })),\n)\n\n// ---------------------------------------------------------------------------\n// Component\n// ---------------------------------------------------------------------------\n\nexport const DiffViewer = React.forwardRef<HTMLDivElement, DiffViewerProps>(\n function DiffViewer(\n {\n files,\n original = '',\n modified = '',\n language,\n monacoTheme = 'vs-dark',\n theme = 'dark',\n viewMode: controlledViewMode = 'side-by-side',\n showSidebar = true,\n showTabs = true,\n showStatusBar = true,\n sidebarWidth = 220,\n activeFileIndex: controlledIndex = 0,\n onFileSelect,\n onViewModeChange,\n statusBarTitle,\n statusBarStatus,\n className,\n editorOptions,\n },\n ref,\n ) {\n const [activeIdx, setActiveIdx] = React.useState(controlledIndex)\n const [sidebarOpen, setSidebarOpen] = React.useState(showSidebar)\n const [viewMode, setViewMode] = React.useState<DiffViewMode>(controlledViewMode)\n\n // Sync controlled props\n React.useEffect(() => setActiveIdx(controlledIndex), [controlledIndex])\n React.useEffect(() => setViewMode(controlledViewMode), [controlledViewMode])\n\n const api = React.useMemo(\n () =>\n createDiffViewer({\n files,\n activeFileIndex: activeIdx,\n viewMode,\n theme,\n language,\n sidebarOpen,\n onFileSelect: (i: number) => {\n setActiveIdx(i)\n onFileSelect?.(i)\n },\n onViewModeChange: (m: DiffViewMode) => {\n setViewMode(m)\n onViewModeChange?.(m)\n },\n onSidebarToggle: () => setSidebarOpen((s) => !s),\n }),\n [files, activeIdx, viewMode, theme, language, sidebarOpen, onFileSelect, onViewModeChange],\n )\n\n const activeFile = files[activeIdx]\n const detectedLang = language || (activeFile ? api.getLanguageForFile(activeFile.path) : 'plaintext')\n\n // Keyboard navigation\n React.useEffect(() => {\n function onKey(e: KeyboardEvent) {\n if (e.target instanceof HTMLInputElement || e.target instanceof HTMLTextAreaElement) return\n if (e.key === 'j') {\n setActiveIdx((i) => {\n const next = Math.min(i + 1, files.length - 1)\n onFileSelect?.(next)\n return next\n })\n } else if (e.key === 'k') {\n setActiveIdx((i) => {\n const prev = Math.max(i - 1, 0)\n onFileSelect?.(prev)\n return prev\n })\n } else if (e.key === 'b') {\n setSidebarOpen((s) => !s)\n }\n }\n window.addEventListener('keydown', onKey)\n return () => window.removeEventListener('keydown', onKey)\n }, [files.length, onFileSelect])\n\n return (\n <div\n ref={ref}\n className={cn(diffViewerVariants({ theme, fullscreen: 'true' }), className)}\n {...api.ariaProps}\n {...api.dataAttributes}\n >\n {/* Main area: sidebar + editor */}\n <div style={{ flex: 1, display: 'flex', overflow: 'hidden' }}>\n {/* File sidebar */}\n {sidebarOpen && (\n <div className={sidebarVariants({ theme })} style={{ width: sidebarWidth }}>\n <div className=\"px-2.5 py-2 text-[11px] font-semibold uppercase tracking-wider text-muted-foreground\">\n {`Files (${files.length})`}\n </div>\n {files.map((f, i) => {\n const active = i === activeIdx\n const fname = f.path.split('/').pop() || f.path\n const dir = f.path.split('/').slice(0, -1).join('/')\n return (\n <div\n key={f.path}\n onClick={() => {\n setActiveIdx(i)\n onFileSelect?.(i)\n }}\n className={sidebarItemVariants({ active: active ? 'true' : 'false', theme })}\n >\n <div className=\"flex items-center gap-1\">\n <span className=\"text-[10px]\">{api.getFileStatusIcon(f.status)}</span>\n <span className={active ? '' : 'opacity-70'}>{fname}</span>\n <span className=\"ml-auto text-[10px]\">\n <span className=\"text-green-500\">{`+${f.additions}`}</span>\n {f.deletions > 0 && <span className=\"text-red-500\">{`-${f.deletions}`}</span>}\n </span>\n </div>\n {dir && (\n <div className=\"text-[10px] opacity-50 mt-0.5 pl-4\">{dir}</div>\n )}\n </div>\n )\n })}\n </div>\n )}\n\n {/* Editor area */}\n <div style={{ flex: 1, display: 'flex', flexDirection: 'column', overflow: 'hidden' }}>\n {/* Tab bar */}\n {showTabs && activeFile && (\n <div className={tabBarVariants({ theme })}>\n {files.map((f, i) => {\n const active = i === activeIdx\n const fname = f.path.split('/').pop() || f.path\n return (\n <div\n key={f.path}\n onClick={() => {\n setActiveIdx(i)\n onFileSelect?.(i)\n }}\n className={tabVariants({ active: active ? 'true' : 'false', theme })}\n >\n <span className=\"text-[9px]\">{api.getFileStatusIcon(f.status)}</span>\n {fname}\n </div>\n )\n })}\n </div>\n )}\n\n {/* Monaco DiffEditor */}\n <div style={{ flex: 1 }}>\n <React.Suspense\n fallback={\n <div className=\"flex items-center justify-center h-full text-xs opacity-50\">\n Loading editor...\n </div>\n }\n >\n <MonacoDiffEditor\n original={original}\n modified={modified}\n language={detectedLang}\n theme={monacoTheme}\n options={{\n readOnly: true,\n renderSideBySide: viewMode === 'side-by-side',\n minimap: { enabled: true, scale: 1, showSlider: 'mouseover' },\n fontSize: 13,\n lineHeight: 20,\n fontFamily: \"'JetBrains Mono', 'Fira Code', 'SF Mono', Menlo, monospace\",\n fontLigatures: true,\n scrollBeyondLastLine: false,\n renderOverviewRuler: true,\n scrollbar: { verticalScrollbarSize: 10, horizontalScrollbarSize: 10 },\n padding: { top: 8, bottom: 8 },\n renderWhitespace: 'selection',\n bracketPairColorization: { enabled: true },\n guides: { indentation: true },\n smoothScrolling: true,\n cursorBlinking: 'smooth',\n ...editorOptions,\n }}\n />\n </React.Suspense>\n </div>\n </div>\n </div>\n\n {/* Status bar */}\n {showStatusBar && (\n <div className={statusBarVariants({ theme })}>\n {statusBarTitle && <span>{statusBarTitle}</span>}\n {activeFile && <span>{activeFile.path}</span>}\n <span style={{ marginLeft: 'auto' }}>\n {`+${api.totalAdditions()} -${api.totalDeletions()}`}\n </span>\n {statusBarStatus && <span>{statusBarStatus}</span>}\n <span>\n {`${files.length} ${files.length === 1 ? 'file' : 'files'}`}\n </span>\n </div>\n )}\n </div>\n )\n },\n)\n"],
|
|
5
|
-
"mappings": "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
|
|
4
|
+
"sourcesContent": ["import type { AccessibilityProps } from '@refraction-ui/shared'\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport type DiffViewerTheme = 'light' | 'dark'\nexport type DiffViewMode = 'side-by-side' | 'inline'\n\nexport type DiffFileStatus = 'added' | 'modified' | 'deleted' | 'renamed'\n\nexport interface DiffFile {\n /** File path */\n path: string\n /** Change status */\n status: DiffFileStatus\n /** Lines added */\n additions: number\n /** Lines deleted */\n deletions: number\n /** Unified diff content for this file */\n diff?: string\n /** Old path for renamed files */\n oldPath?: string\n}\n\nexport interface DiffViewerProps {\n /** Files in this diff */\n files?: DiffFile[]\n /** Index of the active file */\n activeFileIndex?: number\n /** View mode */\n viewMode?: DiffViewMode\n /** Theme */\n theme?: DiffViewerTheme\n /** Language for syntax highlighting */\n language?: string\n /** Whether the sidebar is visible */\n sidebarOpen?: boolean\n /** Callback when active file changes */\n onFileSelect?: (index: number) => void\n /** Callback when view mode changes */\n onViewModeChange?: (mode: DiffViewMode) => void\n /** Callback when sidebar toggled */\n onSidebarToggle?: () => void\n}\n\nexport interface DiffViewerState {\n activeFileIndex: number\n viewMode: DiffViewMode\n theme: DiffViewerTheme\n language: string\n sidebarOpen: boolean\n files: DiffFile[]\n}\n\nexport interface DiffViewerAPI {\n /** Current viewer state */\n state: DiffViewerState\n /** ARIA attributes for the root element */\n ariaProps: Partial<AccessibilityProps> & Record<string, unknown>\n /** Data attributes for CSS styling hooks */\n dataAttributes: Record<string, string>\n /** Select a file by index */\n selectFile: (index: number) => void\n /** Move to next file */\n nextFile: () => void\n /** Move to previous file */\n prevFile: () => void\n /** Toggle view mode */\n toggleViewMode: () => void\n /** Toggle sidebar */\n toggleSidebar: () => void\n /** Get language label for a file path */\n getLanguageForFile: (path: string) => string\n /** Get human-readable file status icon */\n getFileStatusIcon: (status: DiffFileStatus) => string\n /** Get total additions across all files */\n totalAdditions: () => number\n /** Get total deletions across all files */\n totalDeletions: () => number\n}\n\n// ---------------------------------------------------------------------------\n// Language detection\n// ---------------------------------------------------------------------------\n\nconst extToLanguage: Record<string, string> = {\n ts: 'typescript',\n tsx: 'typescript',\n js: 'javascript',\n jsx: 'javascript',\n py: 'python',\n go: 'go',\n rs: 'rust',\n rb: 'ruby',\n java: 'java',\n swift: 'swift',\n kt: 'kotlin',\n dart: 'dart',\n php: 'php',\n lua: 'lua',\n r: 'r',\n scala: 'scala',\n json: 'json',\n yaml: 'yaml',\n yml: 'yaml',\n md: 'markdown',\n html: 'html',\n css: 'css',\n scss: 'scss',\n sql: 'sql',\n sh: 'shell',\n bash: 'shell',\n zsh: 'shell',\n xml: 'xml',\n toml: 'toml',\n proto: 'protobuf',\n dockerfile: 'dockerfile',\n cpp: 'cpp',\n c: 'c',\n}\n\nfunction getLanguageForFile(filePath: string): string {\n const ext = filePath.split('.').pop()?.toLowerCase() || ''\n const basename = filePath.split('/').pop()?.toLowerCase() || ''\n if (basename === 'dockerfile') return 'dockerfile'\n return extToLanguage[ext] || 'plaintext'\n}\n\n// ---------------------------------------------------------------------------\n// Status icons\n// ---------------------------------------------------------------------------\n\nconst statusIcons: Record<DiffFileStatus, string> = {\n added: '\\u{1F7E2}', // green circle\n modified: '\\u{1F7E1}', // yellow circle\n deleted: '\\u{1F534}', // red circle\n renamed: '\\u{1F535}', // blue circle\n}\n\n// ---------------------------------------------------------------------------\n// Factory\n// ---------------------------------------------------------------------------\n\n/**\n * Create a headless diff viewer API.\n *\n * Manages file selection, view mode, sidebar state, and produces ARIA/data\n * attributes. The actual rendering (Monaco DiffEditor, custom renderer, etc.)\n * is handled by framework-specific wrappers.\n */\nexport function createDiffViewer(props: DiffViewerProps = {}): DiffViewerAPI {\n const {\n files = [],\n activeFileIndex = 0,\n viewMode = 'side-by-side',\n theme = 'dark',\n language,\n sidebarOpen = true,\n onFileSelect,\n onViewModeChange,\n onSidebarToggle,\n } = props\n\n let currentIndex = Math.min(activeFileIndex, Math.max(0, files.length - 1))\n let currentViewMode = viewMode\n let currentSidebarOpen = sidebarOpen\n\n const state: DiffViewerState = {\n activeFileIndex: currentIndex,\n viewMode: currentViewMode,\n theme,\n language: language || (files[currentIndex] ? getLanguageForFile(files[currentIndex].path) : 'plaintext'),\n sidebarOpen: currentSidebarOpen,\n files,\n }\n\n const ariaProps: Partial<AccessibilityProps> & Record<string, unknown> = {\n role: 'region',\n 'aria-label': 'Diff viewer',\n }\n\n const dataAttributes: Record<string, string> = {\n 'data-view-mode': currentViewMode,\n 'data-theme': theme,\n 'data-file-count': String(files.length),\n }\n\n if (!currentSidebarOpen) {\n dataAttributes['data-sidebar-collapsed'] = ''\n }\n\n function selectFile(index: number) {\n if (index >= 0 && index < files.length) {\n currentIndex = index\n state.activeFileIndex = index\n state.language = language || getLanguageForFile(files[index].path)\n onFileSelect?.(index)\n }\n }\n\n function nextFile() {\n if (currentIndex < files.length - 1) {\n selectFile(currentIndex + 1)\n }\n }\n\n function prevFile() {\n if (currentIndex > 0) {\n selectFile(currentIndex - 1)\n }\n }\n\n function toggleViewMode() {\n currentViewMode = currentViewMode === 'side-by-side' ? 'inline' : 'side-by-side'\n state.viewMode = currentViewMode\n dataAttributes['data-view-mode'] = currentViewMode\n onViewModeChange?.(currentViewMode)\n }\n\n function toggleSidebar() {\n currentSidebarOpen = !currentSidebarOpen\n state.sidebarOpen = currentSidebarOpen\n if (currentSidebarOpen) {\n delete dataAttributes['data-sidebar-collapsed']\n } else {\n dataAttributes['data-sidebar-collapsed'] = ''\n }\n onSidebarToggle?.()\n }\n\n function totalAdditions(): number {\n return files.reduce((sum, f) => sum + f.additions, 0)\n }\n\n function totalDeletions(): number {\n return files.reduce((sum, f) => sum + f.deletions, 0)\n }\n\n return {\n state,\n ariaProps,\n dataAttributes,\n selectFile,\n nextFile,\n prevFile,\n toggleViewMode,\n toggleSidebar,\n getLanguageForFile,\n getFileStatusIcon: (status: DiffFileStatus) => statusIcons[status] || '\\u{26AA}',\n totalAdditions,\n totalDeletions,\n }\n}\n", "import { cva } from '@refraction-ui/shared'\nimport type { TokenContract } from '@refraction-ui/shared'\n\nexport const diffViewerTokens: TokenContract = {\n name: 'diff-viewer',\n tokens: {\n bg: { variable: '--rfr-diff-bg', fallback: 'hsl(var(--background))' },\n fg: { variable: '--rfr-diff-fg', fallback: 'hsl(var(--foreground))' },\n sidebarBg: { variable: '--rfr-diff-sidebar-bg', fallback: 'hsl(var(--muted))' },\n headerBg: { variable: '--rfr-diff-header-bg', fallback: 'hsl(var(--muted))' },\n border: { variable: '--rfr-diff-border', fallback: 'hsl(var(--border))' },\n // Diff add/del colors are syntax semantics (like code highlighting), not\n // theme status tones — deliberately NOT routed through the success/\n // destructive tokens. The --rfr-diff-* variables are the theming seam.\n addBg: { variable: '--rfr-diff-add-bg', fallback: 'rgba(46, 160, 67, 0.15)' },\n delBg: { variable: '--rfr-diff-del-bg', fallback: 'rgba(248, 81, 73, 0.15)' },\n addFg: { variable: '--rfr-diff-add-fg', fallback: '#3fb950' },\n delFg: { variable: '--rfr-diff-del-fg', fallback: '#f85149' },\n statusbar: { variable: '--rfr-diff-statusbar', fallback: 'hsl(var(--primary))' },\n statusbarFg: { variable: '--rfr-diff-statusbar-fg', fallback: 'hsl(var(--primary-foreground))' },\n },\n}\n\nexport const diffViewerVariants = cva({\n base: 'flex flex-col overflow-hidden font-sans text-sm',\n variants: {\n theme: {\n light: 'bg-white text-gray-900',\n dark: 'bg-gray-950 text-gray-100',\n },\n fullscreen: {\n 'true': 'h-screen',\n 'false': '',\n },\n },\n defaultVariants: {\n theme: 'dark',\n fullscreen: 'true',\n },\n})\n\nexport const sidebarVariants = cva({\n base: 'border-r overflow-auto flex-shrink-0',\n variants: {\n theme: {\n light: 'bg-gray-50 border-gray-200',\n dark: 'bg-gray-900 border-gray-800',\n },\n },\n defaultVariants: {\n theme: 'dark',\n },\n})\n\nexport const sidebarItemVariants = cva({\n base: 'px-2.5 py-1 cursor-pointer text-xs font-mono transition-colors',\n variants: {\n active: {\n 'true': 'border-l-2 border-blue-500',\n 'false': 'border-l-2 border-transparent',\n },\n theme: {\n light: '',\n dark: '',\n },\n },\n compoundVariants: [\n { active: 'true', theme: 'dark', class: 'bg-gray-800 text-gray-100' },\n { active: 'false', theme: 'dark', class: 'text-gray-400 hover:bg-gray-800/50' },\n { active: 'true', theme: 'light', class: 'bg-blue-50 text-gray-900' },\n { active: 'false', theme: 'light', class: 'text-gray-600 hover:bg-gray-100' },\n ],\n defaultVariants: {\n active: 'false',\n theme: 'dark',\n },\n})\n\nexport const tabBarVariants = cva({\n base: 'flex items-center border-b overflow-auto flex-shrink-0',\n variants: {\n theme: {\n light: 'bg-gray-50 border-gray-200',\n dark: 'bg-gray-900 border-gray-800',\n },\n },\n defaultVariants: {\n theme: 'dark',\n },\n})\n\nexport const tabVariants = cva({\n base: 'px-3 h-[35px] flex items-center gap-1 cursor-pointer text-xs font-mono border-r shrink-0',\n variants: {\n active: {\n 'true': '',\n 'false': '',\n },\n theme: {\n light: 'border-gray-200',\n dark: 'border-gray-800',\n },\n },\n compoundVariants: [\n { active: 'true', theme: 'dark', class: 'bg-gray-950 text-gray-100 border-b-gray-950 -mb-px' },\n { active: 'false', theme: 'dark', class: 'text-gray-400' },\n { active: 'true', theme: 'light', class: 'bg-white text-gray-900 border-b-white -mb-px' },\n { active: 'false', theme: 'light', class: 'text-gray-500' },\n ],\n defaultVariants: {\n active: 'false',\n theme: 'dark',\n },\n})\n\nexport const statusBarVariants = cva({\n base: 'h-[22px] flex items-center px-2.5 gap-3 text-[11px] font-mono flex-shrink-0',\n variants: {\n theme: {\n light: 'bg-blue-600 text-white',\n dark: 'bg-blue-700 text-white',\n },\n },\n defaultVariants: {\n theme: 'dark',\n },\n})\n", "import * as React from 'react'\nimport {\n createDiffViewer,\n diffViewerVariants,\n sidebarVariants,\n sidebarItemVariants,\n tabBarVariants,\n tabVariants,\n statusBarVariants,\n type DiffFile,\n type DiffViewerTheme,\n type DiffViewMode,\n} from '@refraction-ui/diff-viewer'\nimport { cn } from '@refraction-ui/shared'\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface DiffViewerProps {\n /** Files in this diff */\n files: DiffFile[]\n /** Original file content (left side) */\n original?: string\n /** Modified file content (right side) */\n modified?: string\n /** Language for syntax highlighting (auto-detected from file if omitted) */\n language?: string\n /** Monaco theme name */\n monacoTheme?: string\n /** Color theme */\n theme?: DiffViewerTheme\n /** View mode */\n viewMode?: DiffViewMode\n /** Whether to show the file sidebar */\n showSidebar?: boolean\n /** Whether to show the tab bar */\n showTabs?: boolean\n /** Whether to show the status bar */\n showStatusBar?: boolean\n /** Sidebar width in pixels */\n sidebarWidth?: number\n /** Active file index */\n activeFileIndex?: number\n /** Called when the active file changes */\n onFileSelect?: (index: number) => void\n /** Called when view mode changes */\n onViewModeChange?: (mode: DiffViewMode) => void\n /** Title shown in the status bar */\n statusBarTitle?: string\n /** Status text shown in the status bar */\n statusBarStatus?: string\n /** Additional class name */\n className?: string\n /** Monaco DiffEditor options override */\n editorOptions?: Record<string, unknown>\n}\n\n// ---------------------------------------------------------------------------\n// Lazy Monaco import\n// ---------------------------------------------------------------------------\n\nconst MonacoDiffEditor = React.lazy(() =>\n import('@monaco-editor/react').then((m) => ({ default: m.DiffEditor })),\n)\n\n// ---------------------------------------------------------------------------\n// Component\n// ---------------------------------------------------------------------------\n\nexport const DiffViewer = React.forwardRef<HTMLDivElement, DiffViewerProps>(\n function DiffViewer(\n {\n files,\n original = '',\n modified = '',\n language,\n monacoTheme = 'vs-dark',\n theme = 'dark',\n viewMode: controlledViewMode = 'side-by-side',\n showSidebar = true,\n showTabs = true,\n showStatusBar = true,\n sidebarWidth = 220,\n activeFileIndex: controlledIndex = 0,\n onFileSelect,\n onViewModeChange,\n statusBarTitle,\n statusBarStatus,\n className,\n editorOptions,\n },\n ref,\n ) {\n const [activeIdx, setActiveIdx] = React.useState(controlledIndex)\n const [sidebarOpen, setSidebarOpen] = React.useState(showSidebar)\n const [viewMode, setViewMode] = React.useState<DiffViewMode>(controlledViewMode)\n\n // Sync controlled props\n React.useEffect(() => setActiveIdx(controlledIndex), [controlledIndex])\n React.useEffect(() => setViewMode(controlledViewMode), [controlledViewMode])\n\n const api = React.useMemo(\n () =>\n createDiffViewer({\n files,\n activeFileIndex: activeIdx,\n viewMode,\n theme,\n language,\n sidebarOpen,\n onFileSelect: (i: number) => {\n setActiveIdx(i)\n onFileSelect?.(i)\n },\n onViewModeChange: (m: DiffViewMode) => {\n setViewMode(m)\n onViewModeChange?.(m)\n },\n onSidebarToggle: () => setSidebarOpen((s) => !s),\n }),\n [files, activeIdx, viewMode, theme, language, sidebarOpen, onFileSelect, onViewModeChange],\n )\n\n const activeFile = files[activeIdx]\n const detectedLang = language || (activeFile ? api.getLanguageForFile(activeFile.path) : 'plaintext')\n\n // Keyboard navigation\n React.useEffect(() => {\n function onKey(e: KeyboardEvent) {\n if (e.target instanceof HTMLInputElement || e.target instanceof HTMLTextAreaElement) return\n if (e.key === 'j') {\n setActiveIdx((i) => {\n const next = Math.min(i + 1, files.length - 1)\n onFileSelect?.(next)\n return next\n })\n } else if (e.key === 'k') {\n setActiveIdx((i) => {\n const prev = Math.max(i - 1, 0)\n onFileSelect?.(prev)\n return prev\n })\n } else if (e.key === 'b') {\n setSidebarOpen((s) => !s)\n }\n }\n window.addEventListener('keydown', onKey)\n return () => window.removeEventListener('keydown', onKey)\n }, [files.length, onFileSelect])\n\n return (\n <div\n ref={ref}\n className={cn(diffViewerVariants({ theme, fullscreen: 'true' }), className)}\n {...api.ariaProps}\n {...api.dataAttributes}\n >\n {/* Main area: sidebar + editor */}\n <div style={{ flex: 1, display: 'flex', overflow: 'hidden' }}>\n {/* File sidebar */}\n {sidebarOpen && (\n <div className={sidebarVariants({ theme })} style={{ width: sidebarWidth }}>\n <div className=\"px-2.5 py-2 text-[11px] font-semibold uppercase tracking-wider text-muted-foreground\">\n {`Files (${files.length})`}\n </div>\n {files.map((f, i) => {\n const active = i === activeIdx\n const fname = f.path.split('/').pop() || f.path\n const dir = f.path.split('/').slice(0, -1).join('/')\n return (\n <div\n key={f.path}\n onClick={() => {\n setActiveIdx(i)\n onFileSelect?.(i)\n }}\n className={sidebarItemVariants({ active: active ? 'true' : 'false', theme })}\n >\n <div className=\"flex items-center gap-1\">\n <span className=\"text-[10px]\">{api.getFileStatusIcon(f.status)}</span>\n <span className={active ? '' : 'opacity-70'}>{fname}</span>\n <span className=\"ml-auto text-[10px]\">\n <span className=\"text-green-500\">{`+${f.additions}`}</span>\n {f.deletions > 0 && <span className=\"text-red-500\">{`-${f.deletions}`}</span>}\n </span>\n </div>\n {dir && (\n <div className=\"text-[10px] opacity-50 mt-0.5 pl-4\">{dir}</div>\n )}\n </div>\n )\n })}\n </div>\n )}\n\n {/* Editor area */}\n <div style={{ flex: 1, display: 'flex', flexDirection: 'column', overflow: 'hidden' }}>\n {/* Tab bar */}\n {showTabs && activeFile && (\n <div className={tabBarVariants({ theme })}>\n {files.map((f, i) => {\n const active = i === activeIdx\n const fname = f.path.split('/').pop() || f.path\n return (\n <div\n key={f.path}\n onClick={() => {\n setActiveIdx(i)\n onFileSelect?.(i)\n }}\n className={tabVariants({ active: active ? 'true' : 'false', theme })}\n >\n <span className=\"text-[9px]\">{api.getFileStatusIcon(f.status)}</span>\n {fname}\n </div>\n )\n })}\n </div>\n )}\n\n {/* Monaco DiffEditor */}\n <div style={{ flex: 1 }}>\n <React.Suspense\n fallback={\n <div className=\"flex items-center justify-center h-full text-xs opacity-50\">\n Loading editor...\n </div>\n }\n >\n <MonacoDiffEditor\n original={original}\n modified={modified}\n language={detectedLang}\n theme={monacoTheme}\n options={{\n readOnly: true,\n renderSideBySide: viewMode === 'side-by-side',\n minimap: { enabled: true, scale: 1, showSlider: 'mouseover' },\n fontSize: 13,\n lineHeight: 20,\n fontFamily: \"'JetBrains Mono', 'Fira Code', 'SF Mono', Menlo, monospace\",\n fontLigatures: true,\n scrollBeyondLastLine: false,\n renderOverviewRuler: true,\n scrollbar: { verticalScrollbarSize: 10, horizontalScrollbarSize: 10 },\n padding: { top: 8, bottom: 8 },\n renderWhitespace: 'selection',\n bracketPairColorization: { enabled: true },\n guides: { indentation: true },\n smoothScrolling: true,\n cursorBlinking: 'smooth',\n ...editorOptions,\n }}\n />\n </React.Suspense>\n </div>\n </div>\n </div>\n\n {/* Status bar */}\n {showStatusBar && (\n <div className={statusBarVariants({ theme })}>\n {statusBarTitle && <span>{statusBarTitle}</span>}\n {activeFile && <span>{activeFile.path}</span>}\n <span style={{ marginLeft: 'auto' }}>\n {`+${api.totalAdditions()} -${api.totalDeletions()}`}\n </span>\n {statusBarStatus && <span>{statusBarStatus}</span>}\n <span>\n {`${files.length} ${files.length === 1 ? 'file' : 'files'}`}\n </span>\n </div>\n )}\n </div>\n )\n },\n)\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["DiffViewer"]
|
|
7
7
|
}
|
|
@@ -22,10 +22,10 @@ import {
|
|
|
22
22
|
searchEmojis,
|
|
23
23
|
twemojiAssetUrl,
|
|
24
24
|
twemojiFilename
|
|
25
|
-
} from "./chunk-
|
|
25
|
+
} from "./chunk-3I3DYDHR.js";
|
|
26
26
|
import {
|
|
27
27
|
cn
|
|
28
|
-
} from "./chunk-
|
|
28
|
+
} from "./chunk-2Q6HHFAN.js";
|
|
29
29
|
|
|
30
30
|
// ../react-emoji-picker/dist/index.js
|
|
31
31
|
import * as React from "react";
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import {
|
|
2
2
|
createDialog
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-V24GDHUX.js";
|
|
4
4
|
import {
|
|
5
5
|
cn,
|
|
6
6
|
cva,
|
|
7
7
|
generateId
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-2Q6HHFAN.js";
|
|
9
9
|
|
|
10
10
|
// ../react-feedback-dialog/dist/index.js
|
|
11
11
|
import * as React from "react";
|
package/dist/react-file-tree.js
CHANGED
package/dist/react-footer.js
CHANGED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import {
|
|
2
|
+
cn
|
|
3
|
+
} from "./chunk-2Q6HHFAN.js";
|
|
4
|
+
|
|
5
|
+
// ../react-funnel-chart/dist/index.js
|
|
6
|
+
import * as React from "react";
|
|
7
|
+
|
|
8
|
+
// ../funnel-chart/dist/index.js
|
|
9
|
+
var DEFAULT_FUNNEL_MIN_BAR_PERCENT = 6;
|
|
10
|
+
var percentOf = (part, whole) => whole ? part / whole * 100 : 0;
|
|
11
|
+
function computeFunnel(steps, minBarPercent = DEFAULT_FUNNEL_MIN_BAR_PERCENT) {
|
|
12
|
+
const top = steps.length ? steps[0].value : 0;
|
|
13
|
+
return steps.map((step, i) => {
|
|
14
|
+
const shareOfTop = percentOf(step.value, top);
|
|
15
|
+
const following = steps[i + 1];
|
|
16
|
+
return {
|
|
17
|
+
step,
|
|
18
|
+
shareOfTop,
|
|
19
|
+
barPercent: Math.min(100, Math.max(shareOfTop, minBarPercent)),
|
|
20
|
+
next: following ? {
|
|
21
|
+
conversion: percentOf(following.value, step.value),
|
|
22
|
+
dropped: step.value - following.value
|
|
23
|
+
} : void 0
|
|
24
|
+
};
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
var funnelChartDefaults = {
|
|
28
|
+
formatValue: (value) => value.toLocaleString("en-US"),
|
|
29
|
+
formatPercent: (percent) => `${percent.toFixed(1)}%`,
|
|
30
|
+
shareLabel: (share) => `${share} of top`,
|
|
31
|
+
conversionLabel: (percent) => `${percent} continue`,
|
|
32
|
+
droppedLabel: (count) => `\u2212${count} dropped`
|
|
33
|
+
};
|
|
34
|
+
var funnelChartClasses = {
|
|
35
|
+
root: "m-0 grid list-none p-0",
|
|
36
|
+
bar: "relative flex items-center gap-3 overflow-hidden rounded-lg border border-border bg-background px-3.5 py-3",
|
|
37
|
+
fill: "absolute inset-y-0 left-0 border-r-2 border-primary bg-accent",
|
|
38
|
+
chevron: "relative shrink-0 text-primary",
|
|
39
|
+
text: "relative grid min-w-0 flex-1",
|
|
40
|
+
label: "text-[13px] font-bold text-foreground",
|
|
41
|
+
description: "text-[11px] text-muted-foreground",
|
|
42
|
+
figure: "relative grid text-right",
|
|
43
|
+
value: "text-[15px] font-bold tabular-nums text-foreground",
|
|
44
|
+
share: "text-[10.5px] text-muted-foreground",
|
|
45
|
+
transition: "flex items-center gap-2 py-1.5 pl-[18px] text-[11px] text-muted-foreground",
|
|
46
|
+
conversion: "font-semibold text-success",
|
|
47
|
+
dropped: "font-semibold text-destructive"
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
// ../react-funnel-chart/dist/index.js
|
|
51
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
52
|
+
function Chevron() {
|
|
53
|
+
return /* @__PURE__ */ jsx(
|
|
54
|
+
"svg",
|
|
55
|
+
{
|
|
56
|
+
className: funnelChartClasses.chevron,
|
|
57
|
+
width: "15",
|
|
58
|
+
height: "15",
|
|
59
|
+
viewBox: "0 0 24 24",
|
|
60
|
+
fill: "none",
|
|
61
|
+
stroke: "currentColor",
|
|
62
|
+
strokeWidth: "2",
|
|
63
|
+
strokeLinecap: "round",
|
|
64
|
+
strokeLinejoin: "round",
|
|
65
|
+
"aria-hidden": "true",
|
|
66
|
+
children: /* @__PURE__ */ jsx("path", { d: "m9 18 6-6-6-6" })
|
|
67
|
+
}
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
var FunnelChart = React.forwardRef(function FunnelChart2({
|
|
71
|
+
steps,
|
|
72
|
+
formatValue = funnelChartDefaults.formatValue,
|
|
73
|
+
formatPercent = funnelChartDefaults.formatPercent,
|
|
74
|
+
minBarPercent = DEFAULT_FUNNEL_MIN_BAR_PERCENT,
|
|
75
|
+
shareLabel = funnelChartDefaults.shareLabel,
|
|
76
|
+
conversionLabel = funnelChartDefaults.conversionLabel,
|
|
77
|
+
droppedLabel = funnelChartDefaults.droppedLabel,
|
|
78
|
+
className,
|
|
79
|
+
...rest
|
|
80
|
+
}, ref) {
|
|
81
|
+
const rows = React.useMemo(() => computeFunnel(steps, minBarPercent), [steps, minBarPercent]);
|
|
82
|
+
return /* @__PURE__ */ jsx("ol", { ref, className: cn(funnelChartClasses.root, className), "data-slot": "funnel-chart", ...rest, children: rows.map((row) => /* @__PURE__ */ jsxs("li", { children: [
|
|
83
|
+
/* @__PURE__ */ jsxs("div", { className: funnelChartClasses.bar, children: [
|
|
84
|
+
/* @__PURE__ */ jsx("div", { className: funnelChartClasses.fill, style: { width: `${row.barPercent}%` }, "aria-hidden": "true" }),
|
|
85
|
+
/* @__PURE__ */ jsx(Chevron, {}),
|
|
86
|
+
/* @__PURE__ */ jsxs("div", { className: funnelChartClasses.text, children: [
|
|
87
|
+
/* @__PURE__ */ jsx("b", { className: funnelChartClasses.label, children: row.step.label }),
|
|
88
|
+
row.step.description && /* @__PURE__ */ jsx("span", { className: funnelChartClasses.description, children: row.step.description })
|
|
89
|
+
] }),
|
|
90
|
+
/* @__PURE__ */ jsxs("div", { className: funnelChartClasses.figure, children: [
|
|
91
|
+
/* @__PURE__ */ jsx("b", { className: funnelChartClasses.value, children: formatValue(row.step.value) }),
|
|
92
|
+
/* @__PURE__ */ jsx("span", { className: funnelChartClasses.share, children: shareLabel(formatPercent(row.shareOfTop)) })
|
|
93
|
+
] })
|
|
94
|
+
] }),
|
|
95
|
+
row.next && /* @__PURE__ */ jsxs("div", { className: funnelChartClasses.transition, children: [
|
|
96
|
+
/* @__PURE__ */ jsx("span", { className: funnelChartClasses.conversion, children: conversionLabel(formatPercent(row.next.conversion)) }),
|
|
97
|
+
/* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: "\xB7" }),
|
|
98
|
+
/* @__PURE__ */ jsx("span", { className: funnelChartClasses.dropped, children: droppedLabel(formatValue(row.next.dropped)) })
|
|
99
|
+
] })
|
|
100
|
+
] }, row.step.id)) });
|
|
101
|
+
});
|
|
102
|
+
export {
|
|
103
|
+
FunnelChart
|
|
104
|
+
};
|
|
105
|
+
//# sourceMappingURL=react-funnel-chart.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../funnel-chart/src/funnel-chart.ts", "../../funnel-chart/src/funnel-chart.styles.ts", "../../react-funnel-chart/src/funnel-chart.tsx"],
|
|
4
|
+
"sourcesContent": ["export interface FunnelChartStep {\n /** Stable key. */\n id: string\n /** Step name. */\n label: string\n /** Secondary line under the label. */\n description?: string\n /** Count at this step. */\n value: number\n}\n\nexport interface FunnelChartTransition {\n /** Percent (0–100) of this step that reached the next one. */\n conversion: number\n /** Absolute count lost between this step and the next. */\n dropped: number\n}\n\nexport interface FunnelChartRow {\n step: FunnelChartStep\n /** Percent (0–100) of the first step's value. */\n shareOfTop: number\n /** Width the bar draws at, in percent — never below `minBarPercent`. */\n barPercent: number\n /** Transition to the following step; absent on the last step. */\n next?: FunnelChartTransition\n}\n\n/** Smallest bar width in percent, so a tiny step stays visible. */\nexport const DEFAULT_FUNNEL_MIN_BAR_PERCENT = 6\n\nconst percentOf = (part: number, whole: number) => (whole ? (part / whole) * 100 : 0)\n\n/**\n * Derive every row of a stepped conversion funnel: each step's share of the top\n * step, the bar width it draws at, and between two steps the continue-rate and\n * how many were lost. Pure and deterministic.\n */\nexport function computeFunnel(\n steps: FunnelChartStep[],\n minBarPercent: number = DEFAULT_FUNNEL_MIN_BAR_PERCENT,\n): FunnelChartRow[] {\n const top = steps.length ? steps[0].value : 0\n return steps.map((step, i) => {\n const shareOfTop = percentOf(step.value, top)\n const following = steps[i + 1]\n return {\n step,\n shareOfTop,\n barPercent: Math.min(100, Math.max(shareOfTop, minBarPercent)),\n next: following\n ? {\n conversion: percentOf(following.value, step.value),\n dropped: step.value - following.value,\n }\n : undefined,\n }\n })\n}\n\n/** Default copy + number formatting; every one is overridable by a prop. */\nexport const funnelChartDefaults = {\n formatValue: (value: number) => value.toLocaleString('en-US'),\n formatPercent: (percent: number) => `${percent.toFixed(1)}%`,\n shareLabel: (share: string) => `${share} of top`,\n conversionLabel: (percent: string) => `${percent} continue`,\n droppedLabel: (count: string) => `−${count} dropped`,\n}\n", "/** Token-class styles for FunnelChart; no colour literals. */\nexport const funnelChartClasses = {\n root: 'm-0 grid list-none p-0',\n bar: 'relative flex items-center gap-3 overflow-hidden rounded-lg border border-border bg-background px-3.5 py-3',\n fill: 'absolute inset-y-0 left-0 border-r-2 border-primary bg-accent',\n chevron: 'relative shrink-0 text-primary',\n text: 'relative grid min-w-0 flex-1',\n label: 'text-[13px] font-bold text-foreground',\n description: 'text-[11px] text-muted-foreground',\n figure: 'relative grid text-right',\n value: 'text-[15px] font-bold tabular-nums text-foreground',\n share: 'text-[10.5px] text-muted-foreground',\n transition: 'flex items-center gap-2 py-1.5 pl-[18px] text-[11px] text-muted-foreground',\n conversion: 'font-semibold text-success',\n dropped: 'font-semibold text-destructive',\n} as const\n", "import * as React from 'react'\nimport {\n computeFunnel,\n funnelChartClasses as c,\n funnelChartDefaults,\n DEFAULT_FUNNEL_MIN_BAR_PERCENT,\n type FunnelChartStep,\n} from '@refraction-ui/funnel-chart'\nimport { cn } from '@refraction-ui/shared'\n\nexport type { FunnelChartStep }\n\nexport interface FunnelChartProps extends Omit<React.HTMLAttributes<HTMLOListElement>, 'children'> {\n /** The funnel steps, top first. */\n steps: FunnelChartStep[]\n /** Formats a count. */\n formatValue?: (value: number) => string\n /** Formats a percent (already 0–100). */\n formatPercent?: (percent: number) => string\n /** Smallest bar width in percent, so a tiny step stays visible. Defaults to 6. */\n minBarPercent?: number\n /** Copy — a step's share of the top step. */\n shareLabel?: (share: string) => string\n /** Copy — continue-rate between two steps. */\n conversionLabel?: (percent: string) => string\n /** Copy — count lost between two steps. */\n droppedLabel?: (count: string) => string\n}\n\nfunction Chevron() {\n return (\n <svg\n className={c.chevron}\n width=\"15\"\n height=\"15\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"m9 18 6-6-6-6\" />\n </svg>\n )\n}\n\n/**\n * FunnelChart — a vertical conversion funnel: one bar per step, filled to its\n * share of the first step, with the continue-rate and drop-off between steps.\n * Renders an ordered list, so the figures are real text for assistive tech.\n */\nexport const FunnelChart = React.forwardRef<HTMLOListElement, FunnelChartProps>(function FunnelChart(\n {\n steps,\n formatValue = funnelChartDefaults.formatValue,\n formatPercent = funnelChartDefaults.formatPercent,\n minBarPercent = DEFAULT_FUNNEL_MIN_BAR_PERCENT,\n shareLabel = funnelChartDefaults.shareLabel,\n conversionLabel = funnelChartDefaults.conversionLabel,\n droppedLabel = funnelChartDefaults.droppedLabel,\n className,\n ...rest\n },\n ref,\n) {\n const rows = React.useMemo(() => computeFunnel(steps, minBarPercent), [steps, minBarPercent])\n return (\n <ol ref={ref} className={cn(c.root, className)} data-slot=\"funnel-chart\" {...rest}>\n {rows.map((row) => (\n <li key={row.step.id}>\n <div className={c.bar}>\n <div className={c.fill} style={{ width: `${row.barPercent}%` }} aria-hidden=\"true\" />\n <Chevron />\n <div className={c.text}>\n <b className={c.label}>{row.step.label}</b>\n {row.step.description && <span className={c.description}>{row.step.description}</span>}\n </div>\n <div className={c.figure}>\n <b className={c.value}>{formatValue(row.step.value)}</b>\n <span className={c.share}>{shareLabel(formatPercent(row.shareOfTop))}</span>\n </div>\n </div>\n {row.next && (\n <div className={c.transition}>\n <span className={c.conversion}>{conversionLabel(formatPercent(row.next.conversion))}</span>\n <span aria-hidden=\"true\">·</span>\n <span className={c.dropped}>{droppedLabel(formatValue(row.next.dropped))}</span>\n </div>\n )}\n </li>\n ))}\n </ol>\n )\n})\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["c", "FunnelChart"]
|
|
7
|
+
}
|
package/dist/react-graph-view.js
CHANGED
package/dist/react-input.js
CHANGED