@intlayer/design-system 9.4.0 → 9.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (185) hide show
  1. package/README.md +1 -1
  2. package/dist/esm/api/index.mjs +2 -2
  3. package/dist/esm/api/useUser/index.mjs +1 -1
  4. package/dist/esm/components/Avatar/index.mjs +1 -1
  5. package/dist/esm/components/Avatar/index.mjs.map +1 -1
  6. package/dist/esm/components/Badge/index.mjs +3 -3
  7. package/dist/esm/components/Badge/index.mjs.map +1 -1
  8. package/dist/esm/components/Breadcrumb/index.mjs +5 -5
  9. package/dist/esm/components/Breadcrumb/index.mjs.map +1 -1
  10. package/dist/esm/components/Browser/Browser.mjs +4 -4
  11. package/dist/esm/components/Browser/Browser.mjs.map +1 -1
  12. package/dist/esm/components/Button/Button.mjs +1 -0
  13. package/dist/esm/components/Button/Button.mjs.map +1 -1
  14. package/dist/esm/components/Carousel/index.mjs +5 -5
  15. package/dist/esm/components/Carousel/index.mjs.map +1 -1
  16. package/dist/esm/components/CollapsibleTable/CollapsibleTable.mjs +5 -5
  17. package/dist/esm/components/CollapsibleTable/CollapsibleTable.mjs.map +1 -1
  18. package/dist/esm/components/Container/index.mjs +2 -2
  19. package/dist/esm/components/Container/index.mjs.map +1 -1
  20. package/dist/esm/components/ContentEditor/ContentEditorTextArea.mjs +1 -1
  21. package/dist/esm/components/DictionaryEditor/ItemLayout.mjs +1 -1
  22. package/dist/esm/components/DictionaryEditor/ItemLayout.mjs.map +1 -1
  23. package/dist/esm/components/DictionaryFieldEditor/ContentEditorView/TextEditor.mjs +7 -7
  24. package/dist/esm/components/DictionaryFieldEditor/ContentEditorView/TextEditor.mjs.map +1 -1
  25. package/dist/esm/components/DictionaryFieldEditor/DictionaryCreationForm/DictionaryCreationForm.mjs +1 -1
  26. package/dist/esm/components/DictionaryFieldEditor/DictionaryDetails/DictionaryDetailsForm.mjs +9 -9
  27. package/dist/esm/components/DictionaryFieldEditor/DictionaryDetails/DictionaryDetailsForm.mjs.map +1 -1
  28. package/dist/esm/components/DictionaryFieldEditor/DictionaryFieldEditor.mjs +4 -4
  29. package/dist/esm/components/DictionaryFieldEditor/DictionaryFieldEditor.mjs.map +1 -1
  30. package/dist/esm/components/DictionaryFieldEditor/NavigationView/NavigationViewNode.mjs +1 -1
  31. package/dist/esm/components/DictionaryFieldEditor/{ContentEditor.mjs → NodeEditor.mjs} +9 -9
  32. package/dist/esm/components/DictionaryFieldEditor/NodeEditor.mjs.map +1 -0
  33. package/dist/esm/components/DictionaryFieldEditor/SaveForm/SaveForm.mjs +3 -3
  34. package/dist/esm/components/DictionaryFieldEditor/SaveForm/SaveForm.mjs.map +1 -1
  35. package/dist/esm/components/DictionaryFieldEditor/StructureView/StructureView.mjs +2 -2
  36. package/dist/esm/components/DictionaryFieldEditor/StructureView/StructureView.mjs.map +1 -1
  37. package/dist/esm/components/DictionaryFieldEditor/VersionSwitcherDropDown/VersionSwitcher.mjs +2 -2
  38. package/dist/esm/components/DictionaryFieldEditor/VersionSwitcherDropDown/VersionSwitcher.mjs.map +1 -1
  39. package/dist/esm/components/DictionaryFieldEditor/index.mjs +3 -3
  40. package/dist/esm/components/Footer/index.mjs +5 -3
  41. package/dist/esm/components/Footer/index.mjs.map +1 -1
  42. package/dist/esm/components/Form/elements/OTPElement.mjs +1 -1
  43. package/dist/esm/components/HeightResizer/index.mjs +1 -1
  44. package/dist/esm/components/HeightResizer/index.mjs.map +1 -1
  45. package/dist/esm/components/IDE/Code.mjs +1 -1
  46. package/dist/esm/components/IDE/Code.mjs.map +1 -1
  47. package/dist/esm/components/IDE/FileList.mjs +1 -1
  48. package/dist/esm/components/IDE/FileList.mjs.map +1 -1
  49. package/dist/esm/components/IDE/FileTree.mjs +1 -1
  50. package/dist/esm/components/IDE/FileTree.mjs.map +1 -1
  51. package/dist/esm/components/IDE/IDE.mjs +10 -7
  52. package/dist/esm/components/IDE/IDE.mjs.map +1 -1
  53. package/dist/esm/components/IDE/MarkDownRender.mjs +4 -3
  54. package/dist/esm/components/IDE/MarkDownRender.mjs.map +1 -1
  55. package/dist/esm/components/Input/Checkbox.mjs +4 -4
  56. package/dist/esm/components/Input/Checkbox.mjs.map +1 -1
  57. package/dist/esm/components/Input/Input.mjs +2 -2
  58. package/dist/esm/components/Input/Input.mjs.map +1 -1
  59. package/dist/esm/components/Input/OTPInput.mjs +1 -1
  60. package/dist/esm/components/Input/OTPInput.mjs.map +1 -1
  61. package/dist/esm/components/KeyboardShortcut/KeyboardShortcut.mjs +2 -2
  62. package/dist/esm/components/KeyboardShortcut/KeyboardShortcut.mjs.map +1 -1
  63. package/dist/esm/components/LanguageBackground/LanguageSection.mjs +6 -8
  64. package/dist/esm/components/LanguageBackground/LanguageSection.mjs.map +1 -1
  65. package/dist/esm/components/LanguageBackground/index.mjs +2 -2
  66. package/dist/esm/components/LocaleSwitcherContentDropDown/LocaleSwitcherContent.mjs +3 -3
  67. package/dist/esm/components/LocaleSwitcherContentDropDown/LocaleSwitcherContent.mjs.map +1 -1
  68. package/dist/esm/components/MarkDownRender/MarkDownIframe.mjs +1 -1
  69. package/dist/esm/components/MarkDownRender/MarkDownIframe.mjs.map +1 -1
  70. package/dist/esm/components/MarkDownRender/MarkDownRender.mjs +9 -8
  71. package/dist/esm/components/MarkDownRender/MarkDownRender.mjs.map +1 -1
  72. package/dist/esm/components/MarkdownEditor/MarkdownEditor.mjs +2 -2
  73. package/dist/esm/components/MarkdownEditor/MarkdownEditor.mjs.map +1 -1
  74. package/dist/esm/components/MarkdownEditor/generative/AISelector.mjs +3 -3
  75. package/dist/esm/components/MarkdownEditor/generative/AISelector.mjs.map +1 -1
  76. package/dist/esm/components/MarkdownEditor/generative/AISelectorCommands.mjs +2 -2
  77. package/dist/esm/components/MarkdownEditor/generative/AISelectorCommands.mjs.map +1 -1
  78. package/dist/esm/components/MarkdownEditor/generative/GenerativeMenuSwitch.mjs +2 -2
  79. package/dist/esm/components/MarkdownEditor/generative/GenerativeMenuSwitch.mjs.map +1 -1
  80. package/dist/esm/components/Modal/Modal.mjs +2 -3
  81. package/dist/esm/components/Modal/Modal.mjs.map +1 -1
  82. package/dist/esm/components/Navbar/DesktopNavbar.mjs +3 -3
  83. package/dist/esm/components/Navbar/DesktopNavbar.mjs.map +1 -1
  84. package/dist/esm/components/Navbar/MobileNavbar.mjs +2 -2
  85. package/dist/esm/components/Navbar/MobileNavbar.mjs.map +1 -1
  86. package/dist/esm/components/Pagination/Pagination.mjs +1 -1
  87. package/dist/esm/components/Pattern/GridDistortionPattern.mjs +2 -2
  88. package/dist/esm/components/Pattern/GridDistortionPattern.mjs.map +1 -1
  89. package/dist/esm/components/Pattern/GridPattern.mjs +1 -1
  90. package/dist/esm/components/Pattern/GridPattern.mjs.map +1 -1
  91. package/dist/esm/components/RightDrawer/RightDrawer.mjs +5 -5
  92. package/dist/esm/components/RightDrawer/RightDrawer.mjs.map +1 -1
  93. package/dist/esm/components/Select/Multiselect.mjs +2 -2
  94. package/dist/esm/components/Select/Multiselect.mjs.map +1 -1
  95. package/dist/esm/components/Select/Select.mjs +2 -2
  96. package/dist/esm/components/Select/Select.mjs.map +1 -1
  97. package/dist/esm/components/Status/index.mjs +28 -0
  98. package/dist/esm/components/Status/index.mjs.map +1 -0
  99. package/dist/esm/components/Steps/index.mjs +3 -3
  100. package/dist/esm/components/Steps/index.mjs.map +1 -1
  101. package/dist/esm/components/SwitchSelector/SwitchSelector.mjs +12 -11
  102. package/dist/esm/components/SwitchSelector/SwitchSelector.mjs.map +1 -1
  103. package/dist/esm/components/SwitchSelector/VerticalSwitchSelector.mjs +6 -6
  104. package/dist/esm/components/SwitchSelector/VerticalSwitchSelector.mjs.map +1 -1
  105. package/dist/esm/components/Tab/Tab.mjs +2 -2
  106. package/dist/esm/components/Tab/Tab.mjs.map +1 -1
  107. package/dist/esm/components/TabSelector/TabSelector.mjs +5 -5
  108. package/dist/esm/components/TabSelector/TabSelector.mjs.map +1 -1
  109. package/dist/esm/components/Table/SmartTable.mjs +2 -2
  110. package/dist/esm/components/Table/SmartTable.mjs.map +1 -1
  111. package/dist/esm/components/Table/Table.mjs +1 -1
  112. package/dist/esm/components/Table/Table.mjs.map +1 -1
  113. package/dist/esm/components/Table/TableElements.mjs +2 -2
  114. package/dist/esm/components/Table/TableElements.mjs.map +1 -1
  115. package/dist/esm/components/Tag/index.mjs +1 -1
  116. package/dist/esm/components/Tag/index.mjs.map +1 -1
  117. package/dist/esm/components/TechLogo/TechLogo.mjs +2 -0
  118. package/dist/esm/components/TechLogo/TechLogo.mjs.map +1 -1
  119. package/dist/esm/components/TechLogo/logos/HTMX.mjs +31 -0
  120. package/dist/esm/components/TechLogo/logos/HTMX.mjs.map +1 -0
  121. package/dist/esm/components/TechLogo/logos/Remix.mjs +22 -0
  122. package/dist/esm/components/TechLogo/logos/Remix.mjs.map +1 -0
  123. package/dist/esm/components/TechLogo/techLogos.mjs +5 -1
  124. package/dist/esm/components/TechLogo/techLogos.mjs.map +1 -1
  125. package/dist/esm/components/Terminal/Terminal.mjs +4 -4
  126. package/dist/esm/components/Terminal/Terminal.mjs.map +1 -1
  127. package/dist/esm/components/TextArea/TextArea.mjs.map +1 -1
  128. package/dist/esm/components/Toaster/Toast.mjs +4 -4
  129. package/dist/esm/components/Toaster/Toast.mjs.map +1 -1
  130. package/dist/esm/components/index.mjs +8 -7
  131. package/dist/esm/hooks/index.mjs +7 -7
  132. package/dist/esm/hooks/usePersistedStore.mjs +6 -18
  133. package/dist/esm/hooks/usePersistedStore.mjs.map +1 -1
  134. package/dist/esm/providers/ReactQueryProvider.mjs +10 -2
  135. package/dist/esm/providers/ReactQueryProvider.mjs.map +1 -1
  136. package/dist/esm/providers/ThemeProvider.mjs +27 -15
  137. package/dist/esm/providers/ThemeProvider.mjs.map +1 -1
  138. package/dist/esm/routes.mjs +3 -1
  139. package/dist/esm/routes.mjs.map +1 -1
  140. package/dist/esm/structured-data/buildItemListJsonLd.mjs +27 -0
  141. package/dist/esm/structured-data/buildItemListJsonLd.mjs.map +1 -0
  142. package/dist/esm/structured-data/index.mjs +2 -1
  143. package/dist/types/components/Badge/index.d.ts.map +1 -1
  144. package/dist/types/components/Button/Button.d.ts +2 -2
  145. package/dist/types/components/Button/Button.d.ts.map +1 -1
  146. package/dist/types/components/Container/index.d.ts.map +1 -1
  147. package/dist/types/components/DictionaryFieldEditor/ContentEditorView/TextEditor.d.ts.map +1 -1
  148. package/dist/types/components/DictionaryFieldEditor/NodeEditor.d.ts +10 -0
  149. package/dist/types/components/DictionaryFieldEditor/NodeEditor.d.ts.map +1 -0
  150. package/dist/types/components/DictionaryFieldEditor/index.d.ts +2 -2
  151. package/dist/types/components/Footer/index.d.ts.map +1 -1
  152. package/dist/types/components/Form/FormBase.d.ts +1 -1
  153. package/dist/types/components/IDE/MarkDownRender.d.ts +1 -0
  154. package/dist/types/components/IDE/MarkDownRender.d.ts.map +1 -1
  155. package/dist/types/components/LanguageBackground/LanguageSection.d.ts +5 -1
  156. package/dist/types/components/LanguageBackground/LanguageSection.d.ts.map +1 -1
  157. package/dist/types/components/LanguageBackground/index.d.ts +2 -2
  158. package/dist/types/components/MarkDownRender/MarkDownRender.d.ts.map +1 -1
  159. package/dist/types/components/Status/index.d.ts +8 -0
  160. package/dist/types/components/Status/index.d.ts.map +1 -0
  161. package/dist/types/components/SwitchSelector/SwitchSelector.d.ts +3 -3
  162. package/dist/types/components/SwitchSelector/SwitchSelector.d.ts.map +1 -1
  163. package/dist/types/components/Table/TableElements.d.ts.map +1 -1
  164. package/dist/types/components/TechLogo/TechLogo.d.ts.map +1 -1
  165. package/dist/types/components/TechLogo/logos/HTMX.d.ts +6 -0
  166. package/dist/types/components/TechLogo/logos/HTMX.d.ts.map +1 -0
  167. package/dist/types/components/TechLogo/logos/Remix.d.ts +6 -0
  168. package/dist/types/components/TechLogo/logos/Remix.d.ts.map +1 -0
  169. package/dist/types/components/TechLogo/techLogos.d.ts +4 -2
  170. package/dist/types/components/TechLogo/types.d.ts +1 -1
  171. package/dist/types/components/TextArea/TextArea.d.ts +1 -1
  172. package/dist/types/components/index.d.ts +3 -2
  173. package/dist/types/providers/ThemeProvider.d.ts.map +1 -1
  174. package/dist/types/routes.d.ts +3 -1
  175. package/dist/types/routes.d.ts.map +1 -1
  176. package/dist/types/structured-data/buildItemListJsonLd.d.ts +36 -0
  177. package/dist/types/structured-data/buildItemListJsonLd.d.ts.map +1 -0
  178. package/dist/types/structured-data/index.d.ts +2 -1
  179. package/package.json +53 -53
  180. package/src/components/MarkdownEditor/markdown-editor.css +16 -16
  181. package/styles/globals.css +71 -0
  182. package/dist/esm/components/DictionaryFieldEditor/ContentEditor.mjs.map +0 -1
  183. package/dist/types/components/DictionaryFieldEditor/ContentEditor.d.ts +0 -10
  184. package/dist/types/components/DictionaryFieldEditor/ContentEditor.d.ts.map +0 -1
  185. package/tailwind.css +0 -269
@@ -1 +1 @@
1
- {"version":3,"file":"KeyboardShortcut.mjs","names":[],"sources":["../../../../src/components/KeyboardShortcut/KeyboardShortcut.tsx"],"sourcesContent":["'use client';\n\nimport { useDevice } from '@hooks/useDevice';\nimport { cn } from '@utils/cn';\nimport { type FC, useEffect, useState } from 'react';\n\n/**\n * Enum for available keyboard keys\n */\nexport type KeyList =\n | 'Ctrl'\n | 'Alt'\n | 'Shift'\n | 'Meta'\n | 'F'\n | 'K'\n | 'L'\n | 'P'\n | 'S'\n | 'A'\n | 'B'\n | 'C'\n | 'D'\n | 'E'\n | 'G'\n | 'H'\n | 'I'\n | 'J'\n | 'M'\n | 'N'\n | 'O'\n | 'Q'\n | 'R'\n | 'T'\n | 'U'\n | 'V'\n | 'W'\n | 'X'\n | 'Y'\n | 'Z'\n | 'Enter'\n | 'Escape'\n | 'Backspace'\n | 'Tab'\n | 'Space'\n | 'ArrowUp'\n | 'ArrowDown'\n | 'ArrowLeft'\n | 'ArrowRight';\n\n/**\n * Type-safe keyboard shortcut combinations\n * Note: Using string type to avoid union type complexity issues\n * Expected format: \"Key + Key\" (e.g., \"⌘ + F\", \"Ctrl + Shift + K\")\n */\nexport type KeyboardShortcutType = string;\n\nexport type KeyboardShortcutProps = {\n /** The keyboard shortcut combination (e.g., \"⌘ + F\" or \"Ctrl + K\") */\n shortcut: KeyboardShortcutType;\n /** Callback function triggered when the shortcut is pressed */\n onTriggered?: () => void;\n /** Whether to display the shortcut visually (default: true) */\n display?: boolean;\n /** Whether to disable the shortcut trigger (default: false) */\n disabled?: boolean;\n /** Additional CSS classes */\n className?: string;\n /** Size of the keyboard shortcut display */\n size?: 'sm' | 'md' | 'lg';\n};\n\n/**\n * Parse keyboard shortcut string into individual keys\n */\nconst parseShortcut = (shortcut: string): string[] => {\n return shortcut.split(' + ').map((key) => key.trim());\n};\n\n/**\n * Normalize key name for event comparison\n */\nconst normalizeKey = (key: string): string => {\n const keyMap: Record<string, string> = {\n '⌘': 'Meta',\n Ctrl: 'Control',\n Control: 'Control',\n Alt: 'Alt',\n '⌥': 'Alt',\n Shift: 'Shift',\n Meta: 'Meta',\n '↑': 'ArrowUp',\n '↓': 'ArrowDown',\n '←': 'ArrowLeft',\n '→': 'ArrowRight',\n ArrowUp: 'ArrowUp',\n ArrowDown: 'ArrowDown',\n ArrowLeft: 'ArrowLeft',\n ArrowRight: 'ArrowRight',\n };\n\n return keyMap[key] || key;\n};\n\n/**\n * Check if the keyboard event matches the shortcut\n */\nconst matchesShortcut = (event: KeyboardEvent, keys: string[]): boolean => {\n const normalizedKeys = keys.map(normalizeKey);\n const hasModifiers = {\n Meta: normalizedKeys.includes('Meta'),\n Control: normalizedKeys.includes('Control'),\n Alt: normalizedKeys.includes('Alt'),\n Shift: normalizedKeys.includes('Shift'),\n };\n\n // Check if all required modifiers are pressed\n if (\n hasModifiers.Meta !== event.metaKey ||\n hasModifiers.Control !== event.ctrlKey ||\n hasModifiers.Alt !== event.altKey ||\n hasModifiers.Shift !== event.shiftKey\n ) {\n return false;\n }\n\n // Find the non-modifier key\n const nonModifierKey = keys.find(\n (key) =>\n !['⌘', 'Ctrl', 'Control', 'Alt', '⌥', 'Shift', 'Meta'].includes(\n normalizeKey(key)\n )\n );\n\n if (!nonModifierKey) return false;\n\n // Normalize the key for comparison\n const normalizedNonModifierKey = normalizeKey(nonModifierKey);\n\n // Compare the main key\n // For arrow keys, compare directly with event.key\n if (normalizedNonModifierKey.startsWith('Arrow')) {\n return event.key === normalizedNonModifierKey;\n }\n\n // For other keys, compare case-insensitive\n return event.key.toLowerCase() === normalizedNonModifierKey.toLowerCase();\n};\n\n/**\n * Get display key symbol for better visual representation\n */\nconst getDisplayKey = (key: string): string => {\n const displayMap: Record<string, string> = {\n ArrowUp: '↑',\n ArrowDown: '↓',\n ArrowLeft: '←',\n ArrowRight: '→',\n };\n\n return displayMap[key] || key;\n};\n\n/**\n * Get display shortcut based on OS (Mac uses ⌘ and ⌥, others use Ctrl and Alt)\n */\nconst getDisplayShortcut = (shortcut: string, isMac: boolean): string => {\n let result = shortcut;\n\n if (isMac) {\n result = result.replace(/Ctrl/g, '⌘');\n result = result.replace(/Alt/g, '⌥');\n } else {\n result = result.replace(/⌘/g, 'Ctrl');\n result = result.replace(/⌥/g, 'Alt');\n }\n\n // Replace arrow key names with symbols\n result = result.replace(/ArrowUp/g, '↑');\n result = result.replace(/ArrowDown/g, '↓');\n result = result.replace(/ArrowLeft/g, '←');\n result = result.replace(/ArrowRight/g, '→');\n\n return result;\n};\n\n/**\n * KeyboardShortcut Component\n *\n * A reusable component that displays keyboard shortcuts and listens for key combinations.\n * Automatically adapts to Mac (⌘, ⌥) and Windows/Linux (Ctrl, Alt) conventions.\n *\n * @example\n * ```tsx\n * <KeyboardShortcut\n * shortcut=\"⌘ + F\"\n * onTriggered={() => setShowSearch(true)}\n * />\n * ```\n */\nexport const KeyboardShortcut: FC<KeyboardShortcutProps> = ({\n shortcut,\n onTriggered,\n display = true,\n disabled = false,\n className,\n size = 'md',\n}) => {\n const { isMac } = useDevice();\n const displayShortcut = getDisplayShortcut(shortcut, isMac ?? false);\n const keys = parseShortcut(displayShortcut);\n const [pressedKeys, setPressedKeys] = useState<Set<string>>(new Set());\n\n useEffect(() => {\n const handleKeyDown = (event: KeyboardEvent) => {\n // 1. Identify input fields\n const target = event.target as HTMLElement;\n const isInputField =\n target.tagName === 'INPUT' ||\n target.tagName === 'TEXTAREA' ||\n target.isContentEditable;\n\n // ... (Your existing key visualization logic here) ...\n // Note: Copied your key tracking logic for context\n const currentKey = event.key;\n const normalizedEventKeys = new Set<string>();\n if (event.metaKey) normalizedEventKeys.add('⌘');\n if (event.ctrlKey) normalizedEventKeys.add('Ctrl');\n if (event.altKey) normalizedEventKeys.add(isMac ? '⌥' : 'Alt');\n if (event.shiftKey) normalizedEventKeys.add('Shift');\n\n if (currentKey.startsWith('Arrow')) {\n normalizedEventKeys.add(currentKey);\n normalizedEventKeys.add(getDisplayKey(currentKey));\n } else {\n normalizedEventKeys.add(currentKey.toUpperCase());\n }\n setPressedKeys(normalizedEventKeys);\n\n // 2. Trigger callback if shortcut matches\n if (!disabled && onTriggered && matchesShortcut(event, keys)) {\n // FIX: Check if the required shortcut is \"Escape\"\n const isEscapeShortcut = keys.includes('Escape');\n\n // Only block if it is an input field AND the shortcut is NOT Escape\n if (isInputField && !isEscapeShortcut) {\n return;\n }\n\n event.preventDefault();\n onTriggered();\n }\n };\n\n const handleKeyUp = () => {\n setPressedKeys(new Set());\n };\n\n window.addEventListener('keydown', handleKeyDown);\n window.addEventListener('keyup', handleKeyUp);\n window.addEventListener('blur', handleKeyUp);\n\n return () => {\n window.removeEventListener('keydown', handleKeyDown);\n window.removeEventListener('keyup', handleKeyUp);\n window.removeEventListener('blur', handleKeyUp);\n };\n }, [keys, onTriggered, isMac, disabled]);\n if (!display) return null;\n\n /**\n * Check if a key is currently pressed\n */\n const isKeyPressed = (key: string): boolean => {\n const upperKey = key.toUpperCase();\n const normalizedKey = normalizeKey(key);\n\n return (\n pressedKeys.has(key) ||\n pressedKeys.has(upperKey) ||\n pressedKeys.has(normalizedKey) ||\n // Check for modifier key matches\n (key === '⌘' && pressedKeys.has('Meta')) ||\n (key === 'Ctrl' && pressedKeys.has('Control')) ||\n (key === '⌥' && pressedKeys.has('Alt')) ||\n (key === 'Alt' && pressedKeys.has('Alt')) ||\n // Check for arrow key symbols\n (key === '←' && pressedKeys.has('ArrowLeft')) ||\n (key === '→' && pressedKeys.has('ArrowRight')) ||\n (key === '↑' && pressedKeys.has('ArrowUp')) ||\n (key === '↓' && pressedKeys.has('ArrowDown'))\n );\n };\n\n return (\n <kbd\n className={cn(\n 'inline-flex items-center justify-center gap-0.5 p-0.5',\n 'rounded-lg [corner-shape:squircle] supports-[corner-shape:squircle]:rounded-xl',\n 'font-medium font-sans',\n 'border-1 border-neutral/20 text-neutral',\n size === 'sm' && 'text-xs',\n size === 'md' && 'text-sm',\n size === 'lg' && 'text-base',\n className\n )}\n >\n {keys.map((key, index) => {\n const keyId = `${key}-${index}-${shortcut}`;\n const displayKey = getDisplayKey(key);\n return (\n <span key={keyId} className=\"inline-flex items-center\">\n {index > 0 && <span className=\"text-neutral/50\">+</span>}\n <span\n className={cn(\n 'min-w-4 px-0.5 text-center',\n isKeyPressed(key) && 'scale-120 font-bold text-text'\n )}\n suppressHydrationWarning\n >\n {displayKey}\n </span>\n </span>\n );\n })}\n </kbd>\n );\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"KeyboardShortcut.mjs","names":[],"sources":["../../../../src/components/KeyboardShortcut/KeyboardShortcut.tsx"],"sourcesContent":["'use client';\n\nimport { useDevice } from '@hooks/useDevice';\nimport { cn } from '@utils/cn';\nimport { type FC, useEffect, useState } from 'react';\n\n/**\n * Enum for available keyboard keys\n */\nexport type KeyList =\n | 'Ctrl'\n | 'Alt'\n | 'Shift'\n | 'Meta'\n | 'F'\n | 'K'\n | 'L'\n | 'P'\n | 'S'\n | 'A'\n | 'B'\n | 'C'\n | 'D'\n | 'E'\n | 'G'\n | 'H'\n | 'I'\n | 'J'\n | 'M'\n | 'N'\n | 'O'\n | 'Q'\n | 'R'\n | 'T'\n | 'U'\n | 'V'\n | 'W'\n | 'X'\n | 'Y'\n | 'Z'\n | 'Enter'\n | 'Escape'\n | 'Backspace'\n | 'Tab'\n | 'Space'\n | 'ArrowUp'\n | 'ArrowDown'\n | 'ArrowLeft'\n | 'ArrowRight';\n\n/**\n * Type-safe keyboard shortcut combinations\n * Note: Using string type to avoid union type complexity issues\n * Expected format: \"Key + Key\" (e.g., \"⌘ + F\", \"Ctrl + Shift + K\")\n */\nexport type KeyboardShortcutType = string;\n\nexport type KeyboardShortcutProps = {\n /** The keyboard shortcut combination (e.g., \"⌘ + F\" or \"Ctrl + K\") */\n shortcut: KeyboardShortcutType;\n /** Callback function triggered when the shortcut is pressed */\n onTriggered?: () => void;\n /** Whether to display the shortcut visually (default: true) */\n display?: boolean;\n /** Whether to disable the shortcut trigger (default: false) */\n disabled?: boolean;\n /** Additional CSS classes */\n className?: string;\n /** Size of the keyboard shortcut display */\n size?: 'sm' | 'md' | 'lg';\n};\n\n/**\n * Parse keyboard shortcut string into individual keys\n */\nconst parseShortcut = (shortcut: string): string[] => {\n return shortcut.split(' + ').map((key) => key.trim());\n};\n\n/**\n * Normalize key name for event comparison\n */\nconst normalizeKey = (key: string): string => {\n const keyMap: Record<string, string> = {\n '⌘': 'Meta',\n Ctrl: 'Control',\n Control: 'Control',\n Alt: 'Alt',\n '⌥': 'Alt',\n Shift: 'Shift',\n Meta: 'Meta',\n '↑': 'ArrowUp',\n '↓': 'ArrowDown',\n '←': 'ArrowLeft',\n '→': 'ArrowRight',\n ArrowUp: 'ArrowUp',\n ArrowDown: 'ArrowDown',\n ArrowLeft: 'ArrowLeft',\n ArrowRight: 'ArrowRight',\n };\n\n return keyMap[key] || key;\n};\n\n/**\n * Check if the keyboard event matches the shortcut\n */\nconst matchesShortcut = (event: KeyboardEvent, keys: string[]): boolean => {\n const normalizedKeys = keys.map(normalizeKey);\n const hasModifiers = {\n Meta: normalizedKeys.includes('Meta'),\n Control: normalizedKeys.includes('Control'),\n Alt: normalizedKeys.includes('Alt'),\n Shift: normalizedKeys.includes('Shift'),\n };\n\n // Check if all required modifiers are pressed\n if (\n hasModifiers.Meta !== event.metaKey ||\n hasModifiers.Control !== event.ctrlKey ||\n hasModifiers.Alt !== event.altKey ||\n hasModifiers.Shift !== event.shiftKey\n ) {\n return false;\n }\n\n // Find the non-modifier key\n const nonModifierKey = keys.find(\n (key) =>\n !['⌘', 'Ctrl', 'Control', 'Alt', '⌥', 'Shift', 'Meta'].includes(\n normalizeKey(key)\n )\n );\n\n if (!nonModifierKey) return false;\n\n // Normalize the key for comparison\n const normalizedNonModifierKey = normalizeKey(nonModifierKey);\n\n // Compare the main key\n // For arrow keys, compare directly with event.key\n if (normalizedNonModifierKey.startsWith('Arrow')) {\n return event.key === normalizedNonModifierKey;\n }\n\n // For other keys, compare case-insensitive\n return event.key.toLowerCase() === normalizedNonModifierKey.toLowerCase();\n};\n\n/**\n * Get display key symbol for better visual representation\n */\nconst getDisplayKey = (key: string): string => {\n const displayMap: Record<string, string> = {\n ArrowUp: '↑',\n ArrowDown: '↓',\n ArrowLeft: '←',\n ArrowRight: '→',\n };\n\n return displayMap[key] || key;\n};\n\n/**\n * Get display shortcut based on OS (Mac uses ⌘ and ⌥, others use Ctrl and Alt)\n */\nconst getDisplayShortcut = (shortcut: string, isMac: boolean): string => {\n let result = shortcut;\n\n if (isMac) {\n result = result.replace(/Ctrl/g, '⌘');\n result = result.replace(/Alt/g, '⌥');\n } else {\n result = result.replace(/⌘/g, 'Ctrl');\n result = result.replace(/⌥/g, 'Alt');\n }\n\n // Replace arrow key names with symbols\n result = result.replace(/ArrowUp/g, '↑');\n result = result.replace(/ArrowDown/g, '↓');\n result = result.replace(/ArrowLeft/g, '←');\n result = result.replace(/ArrowRight/g, '→');\n\n return result;\n};\n\n/**\n * KeyboardShortcut Component\n *\n * A reusable component that displays keyboard shortcuts and listens for key combinations.\n * Automatically adapts to Mac (⌘, ⌥) and Windows/Linux (Ctrl, Alt) conventions.\n *\n * @example\n * ```tsx\n * <KeyboardShortcut\n * shortcut=\"⌘ + F\"\n * onTriggered={() => setShowSearch(true)}\n * />\n * ```\n */\nexport const KeyboardShortcut: FC<KeyboardShortcutProps> = ({\n shortcut,\n onTriggered,\n display = true,\n disabled = false,\n className,\n size = 'md',\n}) => {\n const { isMac } = useDevice();\n const displayShortcut = getDisplayShortcut(shortcut, isMac ?? false);\n const keys = parseShortcut(displayShortcut);\n const [pressedKeys, setPressedKeys] = useState<Set<string>>(new Set());\n\n useEffect(() => {\n const handleKeyDown = (event: KeyboardEvent) => {\n // 1. Identify input fields\n const target = event.target as HTMLElement;\n const isInputField =\n target.tagName === 'INPUT' ||\n target.tagName === 'TEXTAREA' ||\n target.isContentEditable;\n\n // ... (Your existing key visualization logic here) ...\n // Note: Copied your key tracking logic for context\n const currentKey = event.key;\n const normalizedEventKeys = new Set<string>();\n if (event.metaKey) normalizedEventKeys.add('⌘');\n if (event.ctrlKey) normalizedEventKeys.add('Ctrl');\n if (event.altKey) normalizedEventKeys.add(isMac ? '⌥' : 'Alt');\n if (event.shiftKey) normalizedEventKeys.add('Shift');\n\n if (currentKey.startsWith('Arrow')) {\n normalizedEventKeys.add(currentKey);\n normalizedEventKeys.add(getDisplayKey(currentKey));\n } else {\n normalizedEventKeys.add(currentKey.toUpperCase());\n }\n setPressedKeys(normalizedEventKeys);\n\n // 2. Trigger callback if shortcut matches\n if (!disabled && onTriggered && matchesShortcut(event, keys)) {\n // FIX: Check if the required shortcut is \"Escape\"\n const isEscapeShortcut = keys.includes('Escape');\n\n // Only block if it is an input field AND the shortcut is NOT Escape\n if (isInputField && !isEscapeShortcut) {\n return;\n }\n\n event.preventDefault();\n onTriggered();\n }\n };\n\n const handleKeyUp = () => {\n setPressedKeys(new Set());\n };\n\n window.addEventListener('keydown', handleKeyDown);\n window.addEventListener('keyup', handleKeyUp);\n window.addEventListener('blur', handleKeyUp);\n\n return () => {\n window.removeEventListener('keydown', handleKeyDown);\n window.removeEventListener('keyup', handleKeyUp);\n window.removeEventListener('blur', handleKeyUp);\n };\n }, [keys, onTriggered, isMac, disabled]);\n if (!display) return null;\n\n /**\n * Check if a key is currently pressed\n */\n const isKeyPressed = (key: string): boolean => {\n const upperKey = key.toUpperCase();\n const normalizedKey = normalizeKey(key);\n\n return (\n pressedKeys.has(key) ||\n pressedKeys.has(upperKey) ||\n pressedKeys.has(normalizedKey) ||\n // Check for modifier key matches\n (key === '⌘' && pressedKeys.has('Meta')) ||\n (key === 'Ctrl' && pressedKeys.has('Control')) ||\n (key === '⌥' && pressedKeys.has('Alt')) ||\n (key === 'Alt' && pressedKeys.has('Alt')) ||\n // Check for arrow key symbols\n (key === '←' && pressedKeys.has('ArrowLeft')) ||\n (key === '→' && pressedKeys.has('ArrowRight')) ||\n (key === '↑' && pressedKeys.has('ArrowUp')) ||\n (key === '↓' && pressedKeys.has('ArrowDown'))\n );\n };\n\n return (\n <kbd\n className={cn(\n 'inline-flex items-center justify-center gap-0.5 p-0.5',\n 'rounded-lg [corner-shape:squircle] supports-[corner-shape:squircle]:rounded-xl',\n 'font-medium font-sans',\n 'border-1 text-neutral',\n size === 'sm' && 'text-xs',\n size === 'md' && 'text-sm',\n size === 'lg' && 'text-base',\n className\n )}\n >\n {keys.map((key, index) => {\n const keyId = `${key}-${index}-${shortcut}`;\n const displayKey = getDisplayKey(key);\n return (\n <span key={keyId} className=\"inline-flex items-center\">\n {index > 0 && <span className=\"text-neutral/50\">+</span>}\n <span\n className={cn(\n 'min-w-4 px-0.5 text-center',\n isKeyPressed(key) && 'scale-120 font-bold text-foreground'\n )}\n suppressHydrationWarning\n >\n {displayKey}\n </span>\n </span>\n );\n })}\n </kbd>\n );\n};\n"],"mappings":"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"}
@@ -1,7 +1,6 @@
1
1
  'use client';
2
2
 
3
3
  import { cn } from "../../utils/cn.mjs";
4
- import { Container } from "../Container/index.mjs";
5
4
  import { Flag } from "../Flags/Flag.mjs";
6
5
  import { startTransition, useEffect, useState } from "react";
7
6
  import { jsx, jsxs } from "react/jsx-runtime";
@@ -17,14 +16,13 @@ const shuffleArray = (array, limit) => {
17
16
  return limit ? shuffled.slice(0, limit) : shuffled;
18
17
  };
19
18
  const LocalCard = ({ locale, ...props }) => /* @__PURE__ */ jsx("div", {
20
- className: "group z-10 mx-8 inline-flex shrink-0 transition-transform duration-300 hover:scale-105",
19
+ className: "group z-10 mx-8 inline-flex shrink-0 scale-95 rounded-lg border transition-transform duration-300 hover:scale-100",
21
20
  ...props,
22
- children: /* @__PURE__ */ jsxs(Container, {
23
- roundedSize: "xl",
24
- className: "flex flex-row items-center gap-5 p-3",
21
+ children: /* @__PURE__ */ jsxs("div", {
22
+ className: "flex flex-row items-center gap-5 px-3 py-1",
25
23
  children: [/* @__PURE__ */ jsx(Flag, {
26
24
  locale,
27
- className: "max-h-5 max-w-5 rounded-sm grayscale-80 transition duration-300 group-hover:grayscale-0",
25
+ className: "size-5 rounded-sm grayscale-50 transition duration-300 group-hover:grayscale-0",
28
26
  width: 640,
29
27
  height: 480,
30
28
  loading: "lazy"
@@ -56,7 +54,7 @@ const LanguageSection = ({ className, ...props }) => {
56
54
  });
57
55
  }, []);
58
56
  return /* @__PURE__ */ jsx("section", {
59
- className: cn("mask-[linear-gradient(to_right,transparent_0,black_128px,black_calc(100%-128px),transparent_100%)] my-10 w-full overflow-hidden", className),
57
+ className: cn("w-full overflow-hidden py-3", className),
60
58
  ...props,
61
59
  children: /* @__PURE__ */ jsxs("div", {
62
60
  className: "relative flex w-full flex-col gap-5 py-3",
@@ -83,5 +81,5 @@ const LanguageSection = ({ className, ...props }) => {
83
81
  };
84
82
 
85
83
  //#endregion
86
- export { LanguageSection };
84
+ export { LanguageSection, LocalCardList };
87
85
  //# sourceMappingURL=LanguageSection.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"LanguageSection.mjs","names":[],"sources":["../../../../src/components/LanguageBackground/LanguageSection.tsx"],"sourcesContent":["'use client';\n\nimport { cn } from '@utils/cn';\nimport {\n ALL_LOCALES,\n getHTMLTextDir,\n getLocaleName,\n type Locale,\n} from 'intlayer';\nimport {\n type FC,\n type HTMLAttributes,\n startTransition,\n useEffect,\n useState,\n} from 'react';\nimport { Container } from '../Container';\nimport { Flag } from '../Flags';\n\nconst shuffleArray = (array: string[], limit?: number) => {\n const shuffled = [...array];\n\n for (let i = shuffled.length - 1; i > 0; i--) {\n const randomIndex = Math.floor(Math.random() * (i + 1));\n\n [shuffled[i], shuffled[randomIndex]] = [shuffled[randomIndex], shuffled[i]];\n }\n\n return limit ? shuffled.slice(0, limit) : shuffled;\n};\n\nconst LocalCard: FC<{ locale: string }> = ({ locale, ...props }) => (\n <div\n className=\"group z-10 mx-8 inline-flex shrink-0 transition-transform duration-300 hover:scale-105\"\n {...props}\n >\n <Container\n roundedSize=\"xl\"\n className=\"flex flex-row items-center gap-5 p-3\"\n >\n <Flag\n locale={locale as Locale}\n className=\"max-h-5 max-w-5 rounded-sm grayscale-80 transition duration-300 group-hover:grayscale-0\"\n width={640}\n height={480}\n loading=\"lazy\"\n />\n <span\n dir={getHTMLTextDir(locale as Locale)}\n lang={locale as Locale}\n className=\"flex text-nowrap\"\n >\n {getLocaleName(locale as Locale)}\n </span>\n </Container>\n </div>\n);\n\nconst LocalCardList: FC<{ localeList: string[]; className?: string }> = ({\n localeList,\n className,\n ...props\n}) => (\n <div className=\"relative flex w-full overflow-hidden\" {...props}>\n <div\n className={cn('inline-flex shrink-0 will-change-transform', className)}\n >\n {/* First set of cards */}\n {localeList.map((locale) => (\n <LocalCard key={`${locale}-first`} locale={locale} />\n ))}\n {/* Duplicate set for seamless loop */}\n {localeList.map((locale) => (\n <LocalCard key={`${locale}-second`} locale={locale} />\n ))}\n </div>\n </div>\n);\n\nconst NUM_OF_LOCALES = 15;\n\nconst emptyArrayOfLocale: string[][] = new Array(4).fill(0).map(() => []);\nconst arrayOfLocale: string[][] = new Array(4)\n .fill(0)\n .map(() => shuffleArray(Object.values(ALL_LOCALES), NUM_OF_LOCALES));\n\nexport const LanguageSection: FC<HTMLAttributes<HTMLElement>> = ({\n className,\n ...props\n}) => {\n const [localeList, setLocaleList] = useState(emptyArrayOfLocale);\n const [firstPart, secondPart, thirdPart, fourthPart] = localeList;\n\n useEffect(() => {\n startTransition(() => {\n setLocaleList(arrayOfLocale);\n });\n }, []);\n\n return (\n <section\n className={cn(\n 'mask-[linear-gradient(to_right,transparent_0,black_128px,black_calc(100%-128px),transparent_100%)] my-10 w-full overflow-hidden',\n className\n )}\n {...props}\n >\n <div className=\"relative flex w-full flex-col gap-5 py-3\">\n <LocalCardList localeList={firstPart} className=\"horizontal-loop-1\" />\n <LocalCardList localeList={secondPart} className=\"horizontal-loop-2\" />\n <LocalCardList localeList={thirdPart} className=\"horizontal-loop-1\" />\n <LocalCardList localeList={fourthPart} className=\"horizontal-loop-2\" />\n </div>\n </section>\n );\n};\n"],"mappings":";;;;;;;;;;AAmBA,MAAM,gBAAgB,OAAiB,UAAmB;CACxD,MAAM,WAAW,CAAC,GAAG,KAAK;CAE1B,KAAK,IAAI,IAAI,SAAS,SAAS,GAAG,IAAI,GAAG,KAAK;EAC5C,MAAM,cAAc,KAAK,MAAM,KAAK,OAAO,KAAK,IAAI,EAAE;EAEtD,CAAC,SAAS,IAAI,SAAS,gBAAgB,CAAC,SAAS,cAAc,SAAS,EAAE;CAC5E;CAEA,OAAO,QAAQ,SAAS,MAAM,GAAG,KAAK,IAAI;AAC5C;AAEA,MAAM,aAAqC,EAAE,QAAQ,GAAG,YACtD,oBAAC,OAAD;CACE,WAAU;CACV,GAAI;WAEJ,qBAAC,WAAD;EACE,aAAY;EACZ,WAAU;YAFZ,CAIE,oBAAC,MAAD;GACU;GACR,WAAU;GACV,OAAO;GACP,QAAQ;GACR,SAAQ;EACT,IACD,oBAAC,QAAD;GACE,KAAK,eAAe,MAAgB;GACpC,MAAM;GACN,WAAU;aAET,cAAc,MAAgB;EAC3B,EACG;;AACR;AAGP,MAAM,iBAAmE,EACvE,YACA,WACA,GAAG,YAEH,oBAAC,OAAD;CAAK,WAAU;CAAuC,GAAI;WACxD,qBAAC,OAAD;EACE,WAAW,GAAG,8CAA8C,SAAS;YADvE,CAIG,WAAW,KAAK,WACf,oBAAC,WAAD,EAA2C,OAAS,GAApC,GAAG,OAAO,OAA0B,CACrD,GAEA,WAAW,KAAK,WACf,oBAAC,WAAD,EAA4C,OAAS,GAArC,GAAG,OAAO,QAA2B,CACtD,CACE;;AACF;AAGP,MAAM,iBAAiB;AAEvB,MAAM,qBAAiC,IAAI,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC;AACxE,MAAM,gBAA4B,IAAI,MAAM,CAAC,CAAC,CAC3C,KAAK,CAAC,CAAC,CACP,UAAU,aAAa,OAAO,OAAO,WAAW,GAAG,cAAc,CAAC;AAErE,MAAa,mBAAoD,EAC/D,WACA,GAAG,YACC;CACJ,MAAM,CAAC,YAAY,iBAAiB,SAAS,kBAAkB;CAC/D,MAAM,CAAC,WAAW,YAAY,WAAW,cAAc;CAEvD,gBAAgB;EACd,sBAAsB;GACpB,cAAc,aAAa;EAC7B,CAAC;CACH,GAAG,CAAC,CAAC;CAEL,OACE,oBAAC,WAAD;EACE,WAAW,GACT,mIACA,SACF;EACA,GAAI;YAEJ,qBAAC,OAAD;GAAK,WAAU;aAAf;IACE,oBAAC,eAAD;KAAe,YAAY;KAAW,WAAU;IAAqB;IACrE,oBAAC,eAAD;KAAe,YAAY;KAAY,WAAU;IAAqB;IACtE,oBAAC,eAAD;KAAe,YAAY;KAAW,WAAU;IAAqB;IACrE,oBAAC,eAAD;KAAe,YAAY;KAAY,WAAU;IAAqB;GACnE;;CACE;AAEb"}
1
+ {"version":3,"file":"LanguageSection.mjs","names":[],"sources":["../../../../src/components/LanguageBackground/LanguageSection.tsx"],"sourcesContent":["'use client';\n\nimport { cn } from '@utils/cn';\nimport {\n ALL_LOCALES,\n getHTMLTextDir,\n getLocaleName,\n type Locale,\n} from 'intlayer';\nimport {\n type FC,\n type HTMLAttributes,\n startTransition,\n useEffect,\n useState,\n} from 'react';\nimport { Flag } from '../Flags';\n\nconst shuffleArray = (array: string[], limit?: number) => {\n const shuffled = [...array];\n\n for (let i = shuffled.length - 1; i > 0; i--) {\n const randomIndex = Math.floor(Math.random() * (i + 1));\n\n [shuffled[i], shuffled[randomIndex]] = [shuffled[randomIndex], shuffled[i]];\n }\n\n return limit ? shuffled.slice(0, limit) : shuffled;\n};\n\nconst LocalCard: FC<{ locale: string }> = ({ locale, ...props }) => (\n <div\n className=\"group z-10 mx-8 inline-flex shrink-0 scale-95 rounded-lg border transition-transform duration-300 hover:scale-100\"\n {...props}\n >\n <div className=\"flex flex-row items-center gap-5 px-3 py-1\">\n <Flag\n locale={locale as Locale}\n className=\"size-5 rounded-sm grayscale-50 transition duration-300 group-hover:grayscale-0\"\n width={640}\n height={480}\n loading=\"lazy\"\n />\n <span\n dir={getHTMLTextDir(locale as Locale)}\n lang={locale as Locale}\n className=\"flex text-nowrap\"\n >\n {getLocaleName(locale as Locale)}\n </span>\n </div>\n </div>\n);\n\nexport const LocalCardList: FC<{\n localeList: string[];\n className?: string;\n}> = ({ localeList, className, ...props }) => (\n <div className=\"relative flex w-full overflow-hidden\" {...props}>\n <div\n className={cn('inline-flex shrink-0 will-change-transform', className)}\n >\n {/* First set of cards */}\n {localeList.map((locale) => (\n <LocalCard key={`${locale}-first`} locale={locale} />\n ))}\n {/* Duplicate set for seamless loop */}\n {localeList.map((locale) => (\n <LocalCard key={`${locale}-second`} locale={locale} />\n ))}\n </div>\n </div>\n);\n\nconst NUM_OF_LOCALES = 15;\n\nconst emptyArrayOfLocale: string[][] = new Array(4).fill(0).map(() => []);\nconst arrayOfLocale: string[][] = new Array(4)\n .fill(0)\n .map(() => shuffleArray(Object.values(ALL_LOCALES), NUM_OF_LOCALES));\n\nexport const LanguageSection: FC<HTMLAttributes<HTMLElement>> = ({\n className,\n ...props\n}) => {\n const [localeList, setLocaleList] = useState(emptyArrayOfLocale);\n const [firstPart, secondPart, thirdPart, fourthPart] = localeList;\n\n useEffect(() => {\n startTransition(() => {\n setLocaleList(arrayOfLocale);\n });\n }, []);\n\n return (\n <section\n className={cn('w-full overflow-hidden py-3', className)}\n {...props}\n >\n <div className=\"relative flex w-full flex-col gap-5 py-3\">\n <LocalCardList localeList={firstPart!} className=\"horizontal-loop-1\" />\n <LocalCardList localeList={secondPart!} className=\"horizontal-loop-2\" />\n <LocalCardList localeList={thirdPart!} className=\"horizontal-loop-1\" />\n <LocalCardList localeList={fourthPart!} className=\"horizontal-loop-2\" />\n </div>\n </section>\n );\n};\n"],"mappings":";;;;;;;;;AAkBA,MAAM,gBAAgB,OAAiB,UAAmB;CACxD,MAAM,WAAW,CAAC,GAAG,KAAK;CAE1B,KAAK,IAAI,IAAI,SAAS,SAAS,GAAG,IAAI,GAAG,KAAK;EAC5C,MAAM,cAAc,KAAK,MAAM,KAAK,OAAO,KAAK,IAAI,EAAE;EAEtD,CAAC,SAAS,IAAI,SAAS,gBAAgB,CAAC,SAAS,cAAc,SAAS,EAAE;CAC5E;CAEA,OAAO,QAAQ,SAAS,MAAM,GAAG,KAAK,IAAI;AAC5C;AAEA,MAAM,aAAqC,EAAE,QAAQ,GAAG,YACtD,oBAAC,OAAD;CACE,WAAU;CACV,GAAI;WAEJ,qBAAC,OAAD;EAAK,WAAU;YAAf,CACE,oBAAC,MAAD;GACU;GACR,WAAU;GACV,OAAO;GACP,QAAQ;GACR,SAAQ;EACT,IACD,oBAAC,QAAD;GACE,KAAK,eAAe,MAAgB;GACpC,MAAM;GACN,WAAU;aAET,cAAc,MAAgB;EAC3B,EACH;;AACF;AAGP,MAAa,iBAGP,EAAE,YAAY,WAAW,GAAG,YAChC,oBAAC,OAAD;CAAK,WAAU;CAAuC,GAAI;WACxD,qBAAC,OAAD;EACE,WAAW,GAAG,8CAA8C,SAAS;YADvE,CAIG,WAAW,KAAK,WACf,oBAAC,WAAD,EAA2C,OAAS,GAApC,GAAG,OAAO,OAA0B,CACrD,GAEA,WAAW,KAAK,WACf,oBAAC,WAAD,EAA4C,OAAS,GAArC,GAAG,OAAO,QAA2B,CACtD,CACE;;AACF;AAGP,MAAM,iBAAiB;AAEvB,MAAM,qBAAiC,IAAI,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC;AACxE,MAAM,gBAA4B,IAAI,MAAM,CAAC,CAAC,CAC3C,KAAK,CAAC,CAAC,CACP,UAAU,aAAa,OAAO,OAAO,WAAW,GAAG,cAAc,CAAC;AAErE,MAAa,mBAAoD,EAC/D,WACA,GAAG,YACC;CACJ,MAAM,CAAC,YAAY,iBAAiB,SAAS,kBAAkB;CAC/D,MAAM,CAAC,WAAW,YAAY,WAAW,cAAc;CAEvD,gBAAgB;EACd,sBAAsB;GACpB,cAAc,aAAa;EAC7B,CAAC;CACH,GAAG,CAAC,CAAC;CAEL,OACE,oBAAC,WAAD;EACE,WAAW,GAAG,+BAA+B,SAAS;EACtD,GAAI;YAEJ,qBAAC,OAAD;GAAK,WAAU;aAAf;IACE,oBAAC,eAAD;KAAe,YAAY;KAAY,WAAU;IAAqB;IACtE,oBAAC,eAAD;KAAe,YAAY;KAAa,WAAU;IAAqB;IACvE,oBAAC,eAAD;KAAe,YAAY;KAAY,WAAU;IAAqB;IACtE,oBAAC,eAAD;KAAe,YAAY;KAAa,WAAU;IAAqB;GACpE;;CACE;AAEb"}
@@ -1,4 +1,4 @@
1
1
  import { LanguageBackground } from "./LanguageBackground.mjs";
2
- import { LanguageSection } from "./LanguageSection.mjs";
2
+ import { LanguageSection, LocalCardList } from "./LanguageSection.mjs";
3
3
 
4
- export { LanguageBackground, LanguageSection };
4
+ export { LanguageBackground, LanguageSection, LocalCardList };
@@ -1,11 +1,11 @@
1
1
  'use client';
2
2
 
3
+ import { usePersistedStore } from "../../hooks/usePersistedStore.mjs";
3
4
  import { Button } from "../Button/Button.mjs";
4
5
  import { Container } from "../Container/index.mjs";
5
6
  import { DropDown } from "../DropDown/index.mjs";
6
7
  import { Input } from "../Input/Input.mjs";
7
8
  import { SwitchSelector } from "../SwitchSelector/SwitchSelector.mjs";
8
- import { usePersistedStore } from "../../hooks/usePersistedStore.mjs";
9
9
  import { useLocaleSwitcherContent } from "./LocaleSwitcherContentContext.mjs";
10
10
  import { useMemo, useRef, useState } from "react";
11
11
  import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
@@ -76,7 +76,7 @@ const LocaleSwitcherContent = ({ panelProps, isMultilingual = true }) => {
76
76
  else setSelectedLocales([locale]);
77
77
  };
78
78
  return /* @__PURE__ */ jsx("div", {
79
- className: "rounded-xl border border-text text-text transition-colors",
79
+ className: "rounded-xl border border-text text-foreground transition-colors",
80
80
  children: /* @__PURE__ */ jsxs(DropDown, {
81
81
  identifier: DROPDOWN_IDENTIFIER,
82
82
  children: [/* @__PURE__ */ jsx(DropDown.Trigger, {
@@ -87,7 +87,7 @@ const LocaleSwitcherContent = ({ panelProps, isMultilingual = true }) => {
87
87
  color: "text",
88
88
  variant: "hoverable",
89
89
  children: /* @__PURE__ */ jsxs("div", {
90
- className: "flex w-full items-center justify-between text-text",
90
+ className: "flex w-full items-center justify-between text-foreground",
91
91
  children: [/* @__PURE__ */ jsx("div", {
92
92
  className: "px-2 py-1",
93
93
  children: /* @__PURE__ */ jsx(Globe, { size: 16 })
@@ -1 +1 @@
1
- {"version":3,"file":"LocaleSwitcherContent.mjs","names":[],"sources":["../../../../src/components/LocaleSwitcherContentDropDown/LocaleSwitcherContent.tsx"],"sourcesContent":["'use client';\n\nimport { usePersistedStore } from '@hooks/usePersistedStore';\nimport { getHTMLTextDir, getLocaleName } from '@intlayer/core/localization';\nimport { ENGLISH } from '@intlayer/types/locales';\nimport type { LocalesValues } from '@intlayer/types/module_augmentation';\nimport Fuse, { type IFuseOptions } from 'fuse.js';\nimport { Check, Globe, MoveVertical } from 'lucide-react';\nimport { type FC, useMemo, useRef, useState } from 'react';\nimport { useIntlayer, useLocale } from 'react-intlayer';\nimport { Button } from '../Button';\nimport { Container } from '../Container';\nimport { DropDown, type PanelProps } from '../DropDown';\nimport { Input } from '../Input';\nimport { SwitchSelector } from '../SwitchSelector';\nimport { useLocaleSwitcherContent } from './LocaleSwitcherContentContext';\n\nexport type LocaleSwitcherContentProps = {\n panelProps?: Omit<PanelProps, 'identifier'>;\n isMultilingual?: boolean;\n};\n\nconst DROPDOWN_IDENTIFIER = 'locale-switcher-content';\n\ntype MultilingualAvailableLocales = {\n locale: LocalesValues;\n englishName: string;\n currentLocaleName: string;\n ownLocaleName: string;\n};\n\nexport const LocaleSwitcherContent: FC<LocaleSwitcherContentProps> = ({\n panelProps,\n isMultilingual = true,\n}) => {\n const {\n switchTo,\n searchInput,\n localeSwitcherLabel,\n languageListLabel,\n seeAllLocalesSwitch,\n } = useIntlayer('locale-switcher-content');\n const inputRef = useRef<HTMLInputElement>(null);\n const { locale } = useLocale();\n const { availableLocales, selectedLocales, setSelectedLocales } =\n useLocaleSwitcherContent();\n\n // 1. Memoize the list construction so it doesn't rebuild every render\n const multilingualAvailableLocales: MultilingualAvailableLocales[] = useMemo(\n () =>\n availableLocales.map((localeEl) => {\n const englishName = getLocaleName(localeEl, ENGLISH);\n const currentLocaleName = getLocaleName(localeEl, locale);\n const ownLocaleName = getLocaleName(localeEl);\n return {\n locale: localeEl,\n englishName,\n currentLocaleName,\n ownLocaleName,\n };\n }),\n [availableLocales, locale]\n );\n\n // 2. State for Search Query only (Source of Truth)\n const [searchQuery, setSearchQuery] = useState('');\n\n const [seeAllLocales, setSeeAllLocales] = usePersistedStore(\n 'locale-content-selector-see-all-locales',\n false\n );\n\n // 3. Memoize Fuse instance\n const fuse = useMemo(() => {\n const fuseOptions: IFuseOptions<MultilingualAvailableLocales> = {\n keys: [\n { name: 'ownLocaleName', weight: 0.4 },\n { name: 'englishName', weight: 0.2 },\n { name: 'currentLocaleName', weight: 0.2 },\n { name: 'locale', weight: 0.2 },\n ],\n threshold: 0.02,\n };\n return new Fuse(multilingualAvailableLocales, fuseOptions);\n }, [multilingualAvailableLocales]);\n\n // 4. Derive results from Search Query\n const results = useMemo(() => {\n if (!searchQuery) {\n return multilingualAvailableLocales;\n }\n return fuse.search(searchQuery).map((result) => result.item);\n }, [searchQuery, multilingualAvailableLocales, fuse]);\n\n const handleClickLocale = (localeItem: LocalesValues) => {\n if (isMultilingual) {\n if (selectedLocales.includes(localeItem)) {\n if (selectedLocales.length > 1) {\n setSelectedLocales((prev) => prev.filter((el) => el !== localeItem));\n }\n } else {\n setSelectedLocales((prev) => [...prev, localeItem]);\n }\n } else {\n setSelectedLocales([localeItem]);\n }\n };\n\n const handleSeeAllLocales = (value: boolean) => {\n setSeeAllLocales(value);\n\n if (value) {\n setSelectedLocales(availableLocales);\n } else {\n setSelectedLocales([locale]);\n }\n };\n\n return (\n <div className=\"rounded-xl border border-text text-text transition-colors\">\n <DropDown identifier={DROPDOWN_IDENTIFIER}>\n <DropDown.Trigger\n identifier={DROPDOWN_IDENTIFIER}\n label={localeSwitcherLabel.value}\n className=\"p-0!\"\n roundedSize=\"3xl\"\n color=\"text\"\n variant=\"hoverable\"\n >\n <div className=\"flex w-full items-center justify-between text-text\">\n <div className=\"px-2 py-1\">\n <Globe size={16} />\n </div>\n <MoveVertical className=\"self-center\" size={16} />\n </div>\n </DropDown.Trigger>\n\n <DropDown.Panel\n identifier={DROPDOWN_IDENTIFIER}\n isOverable\n isFocusable\n align=\"end\"\n {...panelProps}\n >\n <Container\n className=\"max-h-[60vh] min-w-28\"\n separator=\"y\"\n role=\"listbox\"\n transparency=\"xs\"\n border\n roundedSize=\"3xl\"\n borderColor=\"text\"\n aria-label={languageListLabel.value}\n >\n {isMultilingual && (\n <div className=\"m-auto p-2\">\n <SwitchSelector\n defaultValue={seeAllLocales} // Ensure this uses the persisted state\n onChange={handleSeeAllLocales}\n color=\"text\"\n size=\"sm\"\n className=\"!w-60\"\n choices={[\n {\n content: seeAllLocalesSwitch.true.value,\n value: true,\n },\n {\n content: seeAllLocalesSwitch.false.value,\n value: false,\n },\n ]}\n />\n </div>\n )}\n\n {!(isMultilingual && seeAllLocales) && (\n <>\n <div className=\"p-3\">\n <Input\n type=\"search\"\n aria-label={searchInput.ariaLabel.value}\n placeholder={searchInput.placeholder.value}\n // Update search query state directly\n onChange={(e) => setSearchQuery(e.target.value)}\n ref={inputRef}\n />\n </div>\n <ol className=\"divide-y divide-dashed divide-text/20 overflow-y-auto p-1\">\n {results.map(\n ({\n locale: localeItem,\n currentLocaleName,\n ownLocaleName,\n }) => (\n <li className=\"px-1.5 py-1\" key={localeItem}>\n <Button\n onClick={() => handleClickLocale(localeItem)}\n label={`${switchTo} ${currentLocaleName}`}\n disabled={\n !(availableLocales ?? availableLocales).includes(\n localeItem\n )\n }\n isActive={selectedLocales.includes(localeItem)}\n variant=\"hoverable\"\n color=\"text\"\n isFullWidth\n textAlign=\"left\"\n size=\"sm\"\n >\n <div className=\"flex flex-row items-center justify-between gap-3 px-2 py-1\">\n {isMultilingual && (\n <div className=\"w-4\">\n {selectedLocales.includes(localeItem) && (\n <Check className=\"size-full\" />\n )}\n </div>\n )}\n <div className=\"flex flex-1 flex-row items-center justify-between gap-3 px-2 py-1\">\n <div className=\"flex flex-col text-nowrap\">\n <span\n dir={getHTMLTextDir(localeItem)}\n lang={localeItem}\n >\n {ownLocaleName}\n </span>\n <span className=\"text-neutral text-xs\">\n {currentLocaleName}\n </span>\n </div>\n <span className=\"text-neutral text-sm\">\n {localeItem.toUpperCase()}\n </span>\n </div>\n </div>\n </Button>\n </li>\n )\n )}\n </ol>\n </>\n )}\n </Container>\n </DropDown.Panel>\n </DropDown>\n </div>\n );\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"LocaleSwitcherContent.mjs","names":[],"sources":["../../../../src/components/LocaleSwitcherContentDropDown/LocaleSwitcherContent.tsx"],"sourcesContent":["'use client';\n\nimport { usePersistedStore } from '@hooks/usePersistedStore';\nimport { getHTMLTextDir, getLocaleName } from '@intlayer/core/localization';\nimport { ENGLISH } from '@intlayer/types/locales';\nimport type { LocalesValues } from '@intlayer/types/module_augmentation';\nimport Fuse, { type IFuseOptions } from 'fuse.js';\nimport { Check, Globe, MoveVertical } from 'lucide-react';\nimport { type FC, useMemo, useRef, useState } from 'react';\nimport { useIntlayer, useLocale } from 'react-intlayer';\nimport { Button } from '../Button';\nimport { Container } from '../Container';\nimport { DropDown, type PanelProps } from '../DropDown';\nimport { Input } from '../Input';\nimport { SwitchSelector } from '../SwitchSelector';\nimport { useLocaleSwitcherContent } from './LocaleSwitcherContentContext';\n\nexport type LocaleSwitcherContentProps = {\n panelProps?: Omit<PanelProps, 'identifier'>;\n isMultilingual?: boolean;\n};\n\nconst DROPDOWN_IDENTIFIER = 'locale-switcher-content';\n\ntype MultilingualAvailableLocales = {\n locale: LocalesValues;\n englishName: string;\n currentLocaleName: string;\n ownLocaleName: string;\n};\n\nexport const LocaleSwitcherContent: FC<LocaleSwitcherContentProps> = ({\n panelProps,\n isMultilingual = true,\n}) => {\n const {\n switchTo,\n searchInput,\n localeSwitcherLabel,\n languageListLabel,\n seeAllLocalesSwitch,\n } = useIntlayer('locale-switcher-content');\n const inputRef = useRef<HTMLInputElement>(null);\n const { locale } = useLocale();\n const { availableLocales, selectedLocales, setSelectedLocales } =\n useLocaleSwitcherContent();\n\n // 1. Memoize the list construction so it doesn't rebuild every render\n const multilingualAvailableLocales: MultilingualAvailableLocales[] = useMemo(\n () =>\n availableLocales.map((localeEl) => {\n const englishName = getLocaleName(localeEl, ENGLISH);\n const currentLocaleName = getLocaleName(localeEl, locale);\n const ownLocaleName = getLocaleName(localeEl);\n return {\n locale: localeEl,\n englishName,\n currentLocaleName,\n ownLocaleName,\n };\n }),\n [availableLocales, locale]\n );\n\n // 2. State for Search Query only (Source of Truth)\n const [searchQuery, setSearchQuery] = useState('');\n\n const [seeAllLocales, setSeeAllLocales] = usePersistedStore(\n 'locale-content-selector-see-all-locales',\n false\n );\n\n // 3. Memoize Fuse instance\n const fuse = useMemo(() => {\n const fuseOptions: IFuseOptions<MultilingualAvailableLocales> = {\n keys: [\n { name: 'ownLocaleName', weight: 0.4 },\n { name: 'englishName', weight: 0.2 },\n { name: 'currentLocaleName', weight: 0.2 },\n { name: 'locale', weight: 0.2 },\n ],\n threshold: 0.02,\n };\n return new Fuse(multilingualAvailableLocales, fuseOptions);\n }, [multilingualAvailableLocales]);\n\n // 4. Derive results from Search Query\n const results = useMemo(() => {\n if (!searchQuery) {\n return multilingualAvailableLocales;\n }\n return fuse.search(searchQuery).map((result) => result.item);\n }, [searchQuery, multilingualAvailableLocales, fuse]);\n\n const handleClickLocale = (localeItem: LocalesValues) => {\n if (isMultilingual) {\n if (selectedLocales.includes(localeItem)) {\n if (selectedLocales.length > 1) {\n setSelectedLocales((prev) => prev.filter((el) => el !== localeItem));\n }\n } else {\n setSelectedLocales((prev) => [...prev, localeItem]);\n }\n } else {\n setSelectedLocales([localeItem]);\n }\n };\n\n const handleSeeAllLocales = (value: boolean) => {\n setSeeAllLocales(value);\n\n if (value) {\n setSelectedLocales(availableLocales);\n } else {\n setSelectedLocales([locale]);\n }\n };\n\n return (\n <div className=\"rounded-xl border border-text text-foreground transition-colors\">\n <DropDown identifier={DROPDOWN_IDENTIFIER}>\n <DropDown.Trigger\n identifier={DROPDOWN_IDENTIFIER}\n label={localeSwitcherLabel.value}\n className=\"p-0!\"\n roundedSize=\"3xl\"\n color=\"text\"\n variant=\"hoverable\"\n >\n <div className=\"flex w-full items-center justify-between text-foreground\">\n <div className=\"px-2 py-1\">\n <Globe size={16} />\n </div>\n <MoveVertical className=\"self-center\" size={16} />\n </div>\n </DropDown.Trigger>\n\n <DropDown.Panel\n identifier={DROPDOWN_IDENTIFIER}\n isOverable\n isFocusable\n align=\"end\"\n {...panelProps}\n >\n <Container\n className=\"max-h-[60vh] min-w-28\"\n separator=\"y\"\n role=\"listbox\"\n transparency=\"xs\"\n border\n roundedSize=\"3xl\"\n borderColor=\"text\"\n aria-label={languageListLabel.value}\n >\n {isMultilingual && (\n <div className=\"m-auto p-2\">\n <SwitchSelector\n defaultValue={seeAllLocales} // Ensure this uses the persisted state\n onChange={handleSeeAllLocales}\n color=\"text\"\n size=\"sm\"\n className=\"!w-60\"\n choices={[\n {\n content: seeAllLocalesSwitch.true.value,\n value: true,\n },\n {\n content: seeAllLocalesSwitch.false.value,\n value: false,\n },\n ]}\n />\n </div>\n )}\n\n {!(isMultilingual && seeAllLocales) && (\n <>\n <div className=\"p-3\">\n <Input\n type=\"search\"\n aria-label={searchInput.ariaLabel.value}\n placeholder={searchInput.placeholder.value}\n // Update search query state directly\n onChange={(e) => setSearchQuery(e.target.value)}\n ref={inputRef}\n />\n </div>\n <ol className=\"divide-y divide-dashed divide-text/20 overflow-y-auto p-1\">\n {results.map(\n ({\n locale: localeItem,\n currentLocaleName,\n ownLocaleName,\n }) => (\n <li className=\"px-1.5 py-1\" key={localeItem}>\n <Button\n onClick={() => handleClickLocale(localeItem)}\n label={`${switchTo} ${currentLocaleName}`}\n disabled={\n !(availableLocales ?? availableLocales).includes(\n localeItem\n )\n }\n isActive={selectedLocales.includes(localeItem)}\n variant=\"hoverable\"\n color=\"text\"\n isFullWidth\n textAlign=\"left\"\n size=\"sm\"\n >\n <div className=\"flex flex-row items-center justify-between gap-3 px-2 py-1\">\n {isMultilingual && (\n <div className=\"w-4\">\n {selectedLocales.includes(localeItem) && (\n <Check className=\"size-full\" />\n )}\n </div>\n )}\n <div className=\"flex flex-1 flex-row items-center justify-between gap-3 px-2 py-1\">\n <div className=\"flex flex-col text-nowrap\">\n <span\n dir={getHTMLTextDir(localeItem)}\n lang={localeItem}\n >\n {ownLocaleName}\n </span>\n <span className=\"text-neutral text-xs\">\n {currentLocaleName}\n </span>\n </div>\n <span className=\"text-neutral text-sm\">\n {localeItem.toUpperCase()}\n </span>\n </div>\n </div>\n </Button>\n </li>\n )\n )}\n </ol>\n </>\n )}\n </Container>\n </DropDown.Panel>\n </DropDown>\n </div>\n );\n};\n"],"mappings":"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"}
@@ -158,7 +158,7 @@ const MarkDownIframe = (props) => {
158
158
  rel: "noopener noreferrer",
159
159
  label: "",
160
160
  color: "neutral",
161
- className: "inline-flex min-w-0 max-w-[calc(100%-3rem)] items-center gap-2 text-neutral text-xs underline-offset-2 hover:text-text hover:underline",
161
+ className: "inline-flex min-w-0 max-w-[calc(100%-3rem)] items-center gap-2 text-muted-foreground text-xs underline-offset-2 hover:text-foreground hover:underline",
162
162
  children: label
163
163
  }) : /* @__PURE__ */ jsx("span", {
164
164
  className: "text-neutral text-sm",
@@ -1 +1 @@
1
- {"version":3,"file":"MarkDownIframe.mjs","names":[],"sources":["../../../../src/components/MarkDownRender/MarkDownIframe.tsx"],"sourcesContent":["'use client';\n\nimport { Button } from '@components/Button';\nimport { Container } from '@components/Container';\nimport { Link } from '@components/Link';\nimport { Modal } from '@components/Modal';\nimport { cn } from '@utils/cn';\nimport { MoveDiagonal, Play } from 'lucide-react';\nimport {\n type ComponentProps,\n type FC,\n useEffect,\n useRef,\n useState,\n} from 'react';\n\nfunction embedLinkMeta(src: string | undefined): {\n href: string;\n label: string;\n} {\n if (!src) return { href: '', label: '' };\n if (/^https?:\\/\\//i.test(src)) {\n try {\n const url = new URL(src);\n return { href: url.href, label: url.host };\n } catch {\n return { href: src, label: src };\n }\n }\n return { href: src, label: src };\n}\n\n/**\n * Reads the video id out of a YouTube embed address.\n *\n * @returns The video id, or `null` when the address is not a YouTube embed.\n */\nconst getYoutubeVideoId = (src: string | undefined): string | null => {\n if (!src || !/^https?:\\/\\//i.test(src)) return null;\n\n try {\n const url = new URL(src);\n if (!/(^|\\.)youtube(-nocookie)?\\.com$/.test(url.hostname)) return null;\n\n const videoId = url.pathname.replace(/^\\/embed\\//, '').split('/')[0];\n\n return videoId || null;\n } catch {\n return null;\n }\n};\n\n/**\n * Tracks whether an element has come close enough to the viewport to be worth\n * loading.\n *\n * `loading=\"lazy\"` is not enough for embeds inside tabs or carousels: the\n * browser only defers frames that are far down the page, so a frame parked\n * off-screen by a transform — an inactive tab panel — still loads immediately.\n * A live-app embed costs upwards of a megabyte and seconds of main thread,\n * which is the whole page's performance budget spent on something nobody is\n * looking at.\n *\n * @returns A ref to attach to the placeholder, and whether it became visible.\n */\nconst useHasBecomeVisible = <ElementType extends HTMLElement>() => {\n const elementRef = useRef<ElementType>(null);\n const [hasBecomeVisible, setHasBecomeVisible] = useState(false);\n\n useEffect(() => {\n const element = elementRef.current;\n if (!element || hasBecomeVisible) return;\n\n if (typeof IntersectionObserver === 'undefined') {\n setHasBecomeVisible(true);\n return;\n }\n\n const observer = new IntersectionObserver(\n (entries) => {\n if (entries.some((entry) => entry.isIntersecting)) {\n setHasBecomeVisible(true);\n observer.disconnect();\n }\n },\n { rootMargin: '200px' }\n );\n\n observer.observe(element);\n\n return () => observer.disconnect();\n }, [hasBecomeVisible]);\n\n return { elementRef, hasBecomeVisible };\n};\n\ntype EmbeddedFrameProps = {\n src: string | undefined;\n title: string | undefined;\n className: string;\n frameProps: Omit<ComponentProps<'iframe'>, 'src' | 'title' | 'className'>;\n};\n\n/**\n * The embed itself: a poster for YouTube, deferred loading for anything else.\n *\n * A YouTube player pulls well over a megabyte of script before it can show a\n * single frame. Standing in for it with its own thumbnail until the reader\n * presses play keeps that cost on the readers who actually watch the video.\n */\nconst EmbeddedFrame: FC<EmbeddedFrameProps> = ({\n src,\n title,\n className,\n frameProps,\n}) => {\n const [isPlayRequested, setIsPlayRequested] = useState(false);\n const { elementRef, hasBecomeVisible } =\n useHasBecomeVisible<HTMLDivElement>();\n const youtubeVideoId = getYoutubeVideoId(src);\n\n if (youtubeVideoId && !isPlayRequested) {\n return (\n <button\n type=\"button\"\n aria-label={title ?? 'Play video'}\n onClick={() => setIsPlayRequested(true)}\n className={cn(\n 'group relative block cursor-pointer border-0 bg-card p-0',\n className\n )}\n >\n {/*\n * The poster stands in for the player, so on a page that opens with a\n * video it is the largest element painted — deferring it would defer\n * the page's LCP. It is the one image here worth fetching eagerly.\n */}\n <img\n src={`https://i.ytimg.com/vi/${youtubeVideoId}/hqdefault.jpg`}\n alt=\"\"\n fetchPriority=\"high\"\n decoding=\"async\"\n className=\"size-full object-cover\"\n />\n <span className=\"absolute inset-0 flex items-center justify-center\">\n <span className=\"flex size-14 items-center justify-center rounded-full bg-text/70 text-text-opposite transition-transform group-hover:scale-110\">\n <Play className=\"size-6 translate-x-0.5 fill-current\" />\n </span>\n </span>\n </button>\n );\n }\n\n if (!hasBecomeVisible && !isPlayRequested) {\n // Same box as the frame it stands in for, so nothing shifts once it loads.\n return <div ref={elementRef} aria-hidden className={className} />;\n }\n\n const frameSrc = (() => {\n if (!isPlayRequested || !youtubeVideoId || !src) return src;\n\n const playbackSrc = new URL(src);\n playbackSrc.searchParams.set('autoplay', '1');\n\n return playbackSrc.href;\n })();\n\n return (\n <iframe\n {...frameProps}\n src={frameSrc}\n title={title}\n loading=\"lazy\"\n className={className}\n />\n );\n};\n\nexport const MarkDownIframe: FC<ComponentProps<'iframe'>> = (props) => {\n const { src, className, title, ...rest } = props;\n const [isModalOpen, setIsModalOpen] = useState(false);\n const { href, label } = embedLinkMeta(src);\n\n return (\n <Container\n roundedSize=\"2xl\"\n border\n borderColor=\"neutral\"\n className=\"overflow-hidden p-0\"\n gap=\"none\"\n >\n <EmbeddedFrame\n src={src}\n title={title}\n frameProps={rest}\n className={cn(\n 'block max-h-[80vh] min-h-[12rem] w-full border-0',\n className\n )}\n />\n <div className=\"flex items-center justify-between gap-3 px-3 py-1\">\n {href ? (\n <Link\n href={href}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n label=\"\"\n color=\"neutral\"\n className=\"inline-flex min-w-0 max-w-[calc(100%-3rem)] items-center gap-2 text-neutral text-xs underline-offset-2 hover:text-text hover:underline\"\n >\n {label}\n </Link>\n ) : (\n <span className=\"text-neutral text-sm\">Embedded frame</span>\n )}\n <Button\n variant=\"hoverable\"\n size=\"icon-md\"\n onClick={() => setIsModalOpen(true)}\n label=\"Open embedded page in fullscreen\"\n Icon={MoveDiagonal}\n />\n </div>\n\n <Modal\n isOpen={isModalOpen}\n onClose={() => setIsModalOpen(false)}\n size=\"unset\"\n hasCloseButton\n isScrollable\n padding=\"sm\"\n >\n {isModalOpen && src ? (\n <Container\n roundedSize=\"2xl\"\n border\n borderColor=\"neutral\"\n className=\"overflow-hidden p-0\"\n gap=\"none\"\n >\n <iframe\n {...rest}\n src={src}\n title={title ?? 'Embedded content'}\n allowFullScreen\n className=\"block min-h-[82vh] w-full border-0\"\n />\n </Container>\n ) : null}\n </Modal>\n </Container>\n );\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"MarkDownIframe.mjs","names":[],"sources":["../../../../src/components/MarkDownRender/MarkDownIframe.tsx"],"sourcesContent":["'use client';\n\nimport { Button } from '@components/Button';\nimport { Container } from '@components/Container';\nimport { Link } from '@components/Link';\nimport { Modal } from '@components/Modal';\nimport { cn } from '@utils/cn';\nimport { MoveDiagonal, Play } from 'lucide-react';\nimport {\n type ComponentProps,\n type FC,\n useEffect,\n useRef,\n useState,\n} from 'react';\n\nfunction embedLinkMeta(src: string | undefined): {\n href: string;\n label: string;\n} {\n if (!src) return { href: '', label: '' };\n if (/^https?:\\/\\//i.test(src)) {\n try {\n const url = new URL(src);\n return { href: url.href, label: url.host };\n } catch {\n return { href: src, label: src };\n }\n }\n return { href: src, label: src };\n}\n\n/**\n * Reads the video id out of a YouTube embed address.\n *\n * @returns The video id, or `null` when the address is not a YouTube embed.\n */\nconst getYoutubeVideoId = (src: string | undefined): string | null => {\n if (!src || !/^https?:\\/\\//i.test(src)) return null;\n\n try {\n const url = new URL(src);\n if (!/(^|\\.)youtube(-nocookie)?\\.com$/.test(url.hostname)) return null;\n\n const videoId = url.pathname.replace(/^\\/embed\\//, '').split('/')[0];\n\n return videoId || null;\n } catch {\n return null;\n }\n};\n\n/**\n * Tracks whether an element has come close enough to the viewport to be worth\n * loading.\n *\n * `loading=\"lazy\"` is not enough for embeds inside tabs or carousels: the\n * browser only defers frames that are far down the page, so a frame parked\n * off-screen by a transform — an inactive tab panel — still loads immediately.\n * A live-app embed costs upwards of a megabyte and seconds of main thread,\n * which is the whole page's performance budget spent on something nobody is\n * looking at.\n *\n * @returns A ref to attach to the placeholder, and whether it became visible.\n */\nconst useHasBecomeVisible = <ElementType extends HTMLElement>() => {\n const elementRef = useRef<ElementType>(null);\n const [hasBecomeVisible, setHasBecomeVisible] = useState(false);\n\n useEffect(() => {\n const element = elementRef.current;\n if (!element || hasBecomeVisible) return;\n\n if (typeof IntersectionObserver === 'undefined') {\n setHasBecomeVisible(true);\n return;\n }\n\n const observer = new IntersectionObserver(\n (entries) => {\n if (entries.some((entry) => entry.isIntersecting)) {\n setHasBecomeVisible(true);\n observer.disconnect();\n }\n },\n { rootMargin: '200px' }\n );\n\n observer.observe(element);\n\n return () => observer.disconnect();\n }, [hasBecomeVisible]);\n\n return { elementRef, hasBecomeVisible };\n};\n\ntype EmbeddedFrameProps = {\n src: string | undefined;\n title: string | undefined;\n className: string;\n frameProps: Omit<ComponentProps<'iframe'>, 'src' | 'title' | 'className'>;\n};\n\n/**\n * The embed itself: a poster for YouTube, deferred loading for anything else.\n *\n * A YouTube player pulls well over a megabyte of script before it can show a\n * single frame. Standing in for it with its own thumbnail until the reader\n * presses play keeps that cost on the readers who actually watch the video.\n */\nconst EmbeddedFrame: FC<EmbeddedFrameProps> = ({\n src,\n title,\n className,\n frameProps,\n}) => {\n const [isPlayRequested, setIsPlayRequested] = useState(false);\n const { elementRef, hasBecomeVisible } =\n useHasBecomeVisible<HTMLDivElement>();\n const youtubeVideoId = getYoutubeVideoId(src);\n\n if (youtubeVideoId && !isPlayRequested) {\n return (\n <button\n type=\"button\"\n aria-label={title ?? 'Play video'}\n onClick={() => setIsPlayRequested(true)}\n className={cn(\n 'group relative block cursor-pointer border-0 bg-card p-0',\n className\n )}\n >\n {/*\n * The poster stands in for the player, so on a page that opens with a\n * video it is the largest element painted — deferring it would defer\n * the page's LCP. It is the one image here worth fetching eagerly.\n */}\n <img\n src={`https://i.ytimg.com/vi/${youtubeVideoId}/hqdefault.jpg`}\n alt=\"\"\n fetchPriority=\"high\"\n decoding=\"async\"\n className=\"size-full object-cover\"\n />\n <span className=\"absolute inset-0 flex items-center justify-center\">\n <span className=\"flex size-14 items-center justify-center rounded-full bg-text/70 text-text-opposite transition-transform group-hover:scale-110\">\n <Play className=\"size-6 translate-x-0.5 fill-current\" />\n </span>\n </span>\n </button>\n );\n }\n\n if (!hasBecomeVisible && !isPlayRequested) {\n // Same box as the frame it stands in for, so nothing shifts once it loads.\n return <div ref={elementRef} aria-hidden className={className} />;\n }\n\n const frameSrc = (() => {\n if (!isPlayRequested || !youtubeVideoId || !src) return src;\n\n const playbackSrc = new URL(src);\n playbackSrc.searchParams.set('autoplay', '1');\n\n return playbackSrc.href;\n })();\n\n return (\n <iframe\n {...frameProps}\n src={frameSrc}\n title={title}\n loading=\"lazy\"\n className={className}\n />\n );\n};\n\nexport const MarkDownIframe: FC<ComponentProps<'iframe'>> = (props) => {\n const { src, className, title, ...rest } = props;\n const [isModalOpen, setIsModalOpen] = useState(false);\n const { href, label } = embedLinkMeta(src);\n\n return (\n <Container\n roundedSize=\"2xl\"\n border\n borderColor=\"neutral\"\n className=\"overflow-hidden p-0\"\n gap=\"none\"\n >\n <EmbeddedFrame\n src={src}\n title={title}\n frameProps={rest}\n className={cn(\n 'block max-h-[80vh] min-h-[12rem] w-full border-0',\n className\n )}\n />\n <div className=\"flex items-center justify-between gap-3 px-3 py-1\">\n {href ? (\n <Link\n href={href}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n label=\"\"\n color=\"neutral\"\n className=\"inline-flex min-w-0 max-w-[calc(100%-3rem)] items-center gap-2 text-muted-foreground text-xs underline-offset-2 hover:text-foreground hover:underline\"\n >\n {label}\n </Link>\n ) : (\n <span className=\"text-neutral text-sm\">Embedded frame</span>\n )}\n <Button\n variant=\"hoverable\"\n size=\"icon-md\"\n onClick={() => setIsModalOpen(true)}\n label=\"Open embedded page in fullscreen\"\n Icon={MoveDiagonal}\n />\n </div>\n\n <Modal\n isOpen={isModalOpen}\n onClose={() => setIsModalOpen(false)}\n size=\"unset\"\n hasCloseButton\n isScrollable\n padding=\"sm\"\n >\n {isModalOpen && src ? (\n <Container\n roundedSize=\"2xl\"\n border\n borderColor=\"neutral\"\n className=\"overflow-hidden p-0\"\n gap=\"none\"\n >\n <iframe\n {...rest}\n src={src}\n title={title ?? 'Embedded content'}\n allowFullScreen\n className=\"block min-h-[82vh] w-full border-0\"\n />\n </Container>\n ) : null}\n </Modal>\n </Container>\n );\n};\n"],"mappings":"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"}
@@ -15,36 +15,36 @@ import { renderMarkdown } from "react-intlayer/markdown";
15
15
  //#region src/components/MarkDownRender/MarkDownRender.tsx
16
16
  const H1Renderer = (props) => /* @__PURE__ */ jsx(H1, {
17
17
  isClickable: true,
18
- className: "mb-16 mb-8 text-text",
18
+ className: "mb-16 mb-8 text-foreground",
19
19
  ...props
20
20
  });
21
21
  const H2Renderer = (props) => /* @__PURE__ */ jsx(H2, {
22
22
  isClickable: true,
23
- className: "mt-16 text-text",
23
+ className: "mt-16 text-foreground",
24
24
  ...props
25
25
  });
26
26
  const H3Renderer = (props) => /* @__PURE__ */ jsx(H3, {
27
27
  isClickable: true,
28
- className: "mt-5 text-text",
28
+ className: "mt-5 text-foreground",
29
29
  ...props
30
30
  });
31
31
  const H4Renderer = (props) => /* @__PURE__ */ jsx(H4, {
32
32
  isClickable: true,
33
- className: "mt-3 text-text",
33
+ className: "mt-3 text-foreground",
34
34
  ...props
35
35
  });
36
36
  const H5Renderer = (props) => /* @__PURE__ */ jsx(H5, {
37
37
  isClickable: true,
38
- className: "mt-3 text-text",
38
+ className: "mt-3 text-foreground",
39
39
  ...props
40
40
  });
41
41
  const H6Renderer = (props) => /* @__PURE__ */ jsx(H6, {
42
42
  isClickable: true,
43
- className: "mt-3 text-text",
43
+ className: "mt-3 text-foreground",
44
44
  ...props
45
45
  });
46
46
  const StrongRenderer = (props) => /* @__PURE__ */ jsx("strong", {
47
- className: "text-text",
47
+ className: "text-foreground",
48
48
  ...props
49
49
  });
50
50
  const MemoizedCodeBlock = memo(({ className, children, highlightedHtml, lang, ...rest }) => {
@@ -62,6 +62,7 @@ const MemoizedCodeBlock = memo(({ className, children, highlightedHtml, lang, ..
62
62
  language,
63
63
  showHeader: true,
64
64
  highlightedHtml,
65
+ className: "border",
65
66
  children: content
66
67
  });
67
68
  }, (prevProps, nextProps) => prevProps.children === nextProps.children && prevProps.className === nextProps.className && prevProps.highlightedHtml === nextProps.highlightedHtml);
@@ -74,7 +75,7 @@ const MemoizedCodeBlock = memo(({ className, children, highlightedHtml, lang, ..
74
75
  */
75
76
  const CodeRenderer = (props) => /* @__PURE__ */ jsx(MemoizedCodeBlock, { ...props });
76
77
  const BlockquoteRenderer = ({ className, ...props }) => /* @__PURE__ */ jsx("blockquote", {
77
- className: cn("mt-5 gap-3 border-card border-l-4 pl-5 text-neutral [&_strong]:text-neutral", className),
78
+ className: cn("mt-5 gap-3 border-card border-l-4 pl-5 text-muted-foreground [&_strong]:text-neutral", className),
78
79
  ...props
79
80
  });
80
81
  const UlRenderer = ({ className, ...props }) => /* @__PURE__ */ jsx("ul", {
@@ -1 +1 @@
1
- {"version":3,"file":"MarkDownRender.mjs","names":[],"sources":["../../../../src/components/MarkDownRender/MarkDownRender.tsx"],"sourcesContent":["import type { LocalesValues } from '@intlayer/types/module_augmentation';\nimport { cn } from '@utils/cn';\nimport type { ComponentProps, ComponentPropsWithoutRef, FC } from 'react';\nimport { memo } from 'react';\nimport {\n type MarkdownRenderer as MarkdownRendererIntlayer,\n type ParsedMarkdown,\n renderMarkdown,\n} from 'react-intlayer/markdown';\nimport { H1, H2, H3, H4, H5, H6 } from '../Headers';\nimport { Code } from '../IDE/Code';\nimport { CodeProvider } from '../IDE/CodeContext';\nimport type { CodeLanguage } from '../IDE/shikiLanguages';\nimport { Link } from '../Link';\nimport { Tab } from '../Tab';\nimport { TabProvider } from '../Tab/TabContext';\nimport { Hr, SmartTable, Td, Th, Tr } from '../Table';\nimport { MarkDownIframe } from './MarkDownIframe';\n\nexport type { ParsedMarkdown };\n\n// Extracted, stable component renderers\nconst H1Renderer = (props: ComponentProps<'h1'>) => (\n <H1 isClickable className=\"mb-16 mb-8 text-text\" {...props} />\n);\nconst H2Renderer = (props: ComponentProps<'h2'>) => (\n <H2 isClickable className=\"mt-16 text-text\" {...props} />\n);\nconst H3Renderer = (props: ComponentProps<'h3'>) => (\n <H3 isClickable className=\"mt-5 text-text\" {...props} />\n);\nconst H4Renderer = (props: ComponentProps<'h4'>) => (\n <H4 isClickable className=\"mt-3 text-text\" {...props} />\n);\nconst H5Renderer = (props: ComponentProps<'h5'>) => (\n <H5 isClickable className=\"mt-3 text-text\" {...props} />\n);\nconst H6Renderer = (props: ComponentProps<'h6'>) => (\n <H6 isClickable className=\"mt-3 text-text\" {...props} />\n);\nconst StrongRenderer = (props: ComponentProps<'strong'>) => (\n <strong className=\"text-text\" {...props} />\n);\n\nconst MemoizedCodeBlock = memo(\n ({\n className,\n children,\n highlightedHtml,\n lang, // Destructure html lang prop to prevent passing invalid BCP-47 language tag to Code component\n ...rest\n }: ComponentProps<'code'> & { highlightedHtml?: string }) => {\n const content = String(children ?? '').replace(/\\n$/, '');\n const isBlock = !!className;\n\n if (!isBlock) {\n const decodedContent = content.replace(\n /&(?:amp;)?#(\\d+);/g,\n (_, code: string) => String.fromCharCode(parseInt(code, 10))\n );\n return (\n <code className=\"rounded-md border border-neutral/30 bg-card/60 box-decoration-clone px-1.5 py-0.5 font-mono text-[0.875em] leading-[inherit]\">\n {decodedContent}\n </code>\n );\n }\n\n const language = (className?.replace(/lang(?:uage)?-/, '') ||\n 'plaintext') as CodeLanguage;\n\n return (\n <Code\n {...rest}\n language={language}\n showHeader\n highlightedHtml={highlightedHtml}\n >\n {content}\n </Code>\n );\n },\n (prevProps, nextProps) =>\n prevProps.children === nextProps.children &&\n prevProps.className === nextProps.className &&\n prevProps.highlightedHtml === nextProps.highlightedHtml\n);\n\n/**\n * Renders a fenced code block.\n *\n * `highlightedHtml` reaches this renderer through the code node's attributes:\n * a pipeline that highlights the markdown ahead of time writes it there, and\n * the block then paints without loading the highlighter in the browser.\n */\nconst CodeRenderer = (props: ComponentProps<'code'>) => (\n <MemoizedCodeBlock {...props} />\n);\n\nconst BlockquoteRenderer = ({\n className,\n ...props\n}: ComponentProps<'blockquote'>) => (\n <blockquote\n className={cn(\n 'mt-5 gap-3 border-card border-l-4 pl-5 text-neutral [&_strong]:text-neutral',\n className\n )}\n {...props}\n />\n);\n\nconst UlRenderer = ({ className, ...props }: ComponentProps<'ul'>) => (\n <ul\n className={cn(\n 'mt-5 flex list-disc flex-col gap-3 pl-5 marker:text-neutral/80',\n className\n )}\n {...props}\n />\n);\n\nconst OlRenderer = ({ className, ...props }: ComponentProps<'ol'>) => (\n <ol\n className={cn(\n 'mt-5 flex list-decimal flex-col gap-3 pl-5 marker:text-neutral/80',\n className\n )}\n {...props}\n />\n);\n\nconst ImgRenderer = ({\n className,\n alt,\n src,\n ...props\n}: ComponentProps<'img'>) => (\n <img\n {...props}\n alt={alt ?? ''}\n loading=\"lazy\"\n className={cn('max-h-[80vh] max-w-full rounded-md', className)}\n src={\n src?.includes('github.com')\n ? src\n ?.replace('github.com', 'raw.githubusercontent.com')\n .replace('/blob/', '/') // GitHub raw URLs do not use /blob/\n : src\n }\n />\n);\n\nconst createLinkRenderer = (locale?: LocalesValues) => {\n return (props: ComponentProps<'a'>) => (\n <Link\n isExternalLink={props.href?.startsWith('http')}\n underlined\n locale={locale}\n label=\"\"\n color=\"text\"\n {...(props as any)}\n />\n );\n};\n\nconst PreRenderer = (props: ComponentProps<'pre'>) => <>{props.children}</>;\nconst TableRenderer = (props: ComponentProps<typeof SmartTable>) => (\n <SmartTable isRollable displayModal {...props} />\n);\n\nconst TabsRenderer = (props: ComponentProps<typeof Tab>) => (\n <Tab\n {...props}\n className=\"rounded-xl border border-card\"\n headerClassName=\"sticky rounded-xl top-24 z-5 bg-background/70 backdrop-blur overflow-x-auto\"\n />\n);\nconst ColumnsRenderer = ({\n className,\n ...props\n}: ComponentPropsWithoutRef<'div'>) => (\n <div className={cn('flex gap-4 max-md:flex-col', className)} {...props} />\n);\nconst ColumnRenderer = ({\n className,\n ...props\n}: ComponentPropsWithoutRef<'div'>) => (\n <div className={cn('flex-1', className)} {...props} />\n);\n\nconst Iframe = (props: ComponentProps<'iframe'>) => (\n <MarkDownIframe {...props} />\n);\n\n// Static configuration object for static renderers\nconst staticMarkdownComponents = {\n h1: H1Renderer,\n h2: H2Renderer,\n h3: H3Renderer,\n h4: H4Renderer,\n h5: H5Renderer,\n h6: H6Renderer,\n strong: StrongRenderer,\n blockquote: BlockquoteRenderer,\n ul: UlRenderer,\n ol: OlRenderer,\n img: ImgRenderer,\n pre: PreRenderer,\n table: TableRenderer,\n th: Th,\n tr: Tr,\n td: Td,\n hr: Hr,\n Tabs: TabsRenderer,\n Tab: Tab.Item,\n Columns: ColumnsRenderer,\n Column: ColumnRenderer,\n iframe: Iframe,\n};\n\n// Factory function to create components with dynamic props\nconst createMarkdownComponents = (locale?: LocalesValues) => ({\n ...staticMarkdownComponents,\n code: CodeRenderer,\n a: createLinkRenderer(locale),\n});\n\n// Export static renderers for backward compatibility\nexport const baseMarkdownComponents = staticMarkdownComponents;\n\ntype MarkdownRendererProps = {\n children: string | ParsedMarkdown;\n locale?: LocalesValues;\n forceBlock?: boolean;\n preserveFrontmatter?: boolean;\n tagfilter?: boolean;\n components?: ComponentProps<typeof MarkdownRendererIntlayer>['components'];\n wrapper?: ComponentProps<typeof MarkdownRendererIntlayer>['wrapper'];\n};\n\nexport const getIntlayerMarkdownOptions = () => ({\n components: baseMarkdownComponents,\n});\n\nexport const MarkdownRenderer: FC<MarkdownRendererProps> = ({\n children,\n locale,\n forceBlock,\n preserveFrontmatter,\n tagfilter,\n components: componentsProp,\n wrapper,\n}) => {\n const markdownComponents = createMarkdownComponents(locale);\n\n const markdownContent = renderMarkdown(children, {\n components: {\n ...markdownComponents,\n ...componentsProp,\n },\n wrapper,\n forceBlock,\n preserveFrontmatter,\n tagfilter,\n });\n\n return (\n <CodeProvider>\n <TabProvider>{markdownContent}</TabProvider>\n </CodeProvider>\n );\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"MarkDownRender.mjs","names":[],"sources":["../../../../src/components/MarkDownRender/MarkDownRender.tsx"],"sourcesContent":["import type { LocalesValues } from '@intlayer/types/module_augmentation';\nimport { cn } from '@utils/cn';\nimport type { ComponentProps, ComponentPropsWithoutRef, FC } from 'react';\nimport { memo } from 'react';\nimport {\n type MarkdownRenderer as MarkdownRendererIntlayer,\n type ParsedMarkdown,\n renderMarkdown,\n} from 'react-intlayer/markdown';\nimport { H1, H2, H3, H4, H5, H6 } from '../Headers';\nimport { Code } from '../IDE/Code';\nimport { CodeProvider } from '../IDE/CodeContext';\nimport type { CodeLanguage } from '../IDE/shikiLanguages';\nimport { Link } from '../Link';\nimport { Tab } from '../Tab';\nimport { TabProvider } from '../Tab/TabContext';\nimport { Hr, SmartTable, Td, Th, Tr } from '../Table';\nimport { MarkDownIframe } from './MarkDownIframe';\n\nexport type { ParsedMarkdown };\n\n// Extracted, stable component renderers\nconst H1Renderer = (props: ComponentProps<'h1'>) => (\n <H1 isClickable className=\"mb-16 mb-8 text-foreground\" {...props} />\n);\nconst H2Renderer = (props: ComponentProps<'h2'>) => (\n <H2 isClickable className=\"mt-16 text-foreground\" {...props} />\n);\nconst H3Renderer = (props: ComponentProps<'h3'>) => (\n <H3 isClickable className=\"mt-5 text-foreground\" {...props} />\n);\nconst H4Renderer = (props: ComponentProps<'h4'>) => (\n <H4 isClickable className=\"mt-3 text-foreground\" {...props} />\n);\nconst H5Renderer = (props: ComponentProps<'h5'>) => (\n <H5 isClickable className=\"mt-3 text-foreground\" {...props} />\n);\nconst H6Renderer = (props: ComponentProps<'h6'>) => (\n <H6 isClickable className=\"mt-3 text-foreground\" {...props} />\n);\nconst StrongRenderer = (props: ComponentProps<'strong'>) => (\n <strong className=\"text-foreground\" {...props} />\n);\n\nconst MemoizedCodeBlock = memo(\n ({\n className,\n children,\n highlightedHtml,\n lang, // Destructure html lang prop to prevent passing invalid BCP-47 language tag to Code component\n ...rest\n }: ComponentProps<'code'> & { highlightedHtml?: string }) => {\n const content = String(children ?? '').replace(/\\n$/, '');\n const isBlock = !!className;\n\n if (!isBlock) {\n const decodedContent = content.replace(\n /&(?:amp;)?#(\\d+);/g,\n (_, code: string) => String.fromCharCode(parseInt(code, 10))\n );\n return (\n <code className=\"rounded-md border border-neutral/30 bg-card/60 box-decoration-clone px-1.5 py-0.5 font-mono text-[0.875em] leading-[inherit]\">\n {decodedContent}\n </code>\n );\n }\n\n const language = (className?.replace(/lang(?:uage)?-/, '') ||\n 'plaintext') as CodeLanguage;\n\n return (\n <Code\n {...rest}\n language={language}\n showHeader\n highlightedHtml={highlightedHtml}\n className=\"border\"\n >\n {content}\n </Code>\n );\n },\n (prevProps, nextProps) =>\n prevProps.children === nextProps.children &&\n prevProps.className === nextProps.className &&\n prevProps.highlightedHtml === nextProps.highlightedHtml\n);\n\n/**\n * Renders a fenced code block.\n *\n * `highlightedHtml` reaches this renderer through the code node's attributes:\n * a pipeline that highlights the markdown ahead of time writes it there, and\n * the block then paints without loading the highlighter in the browser.\n */\nconst CodeRenderer = (props: ComponentProps<'code'>) => (\n <MemoizedCodeBlock {...props} />\n);\n\nconst BlockquoteRenderer = ({\n className,\n ...props\n}: ComponentProps<'blockquote'>) => (\n <blockquote\n className={cn(\n 'mt-5 gap-3 border-card border-l-4 pl-5 text-muted-foreground [&_strong]:text-neutral',\n className\n )}\n {...props}\n />\n);\n\nconst UlRenderer = ({ className, ...props }: ComponentProps<'ul'>) => (\n <ul\n className={cn(\n 'mt-5 flex list-disc flex-col gap-3 pl-5 marker:text-neutral/80',\n className\n )}\n {...props}\n />\n);\n\nconst OlRenderer = ({ className, ...props }: ComponentProps<'ol'>) => (\n <ol\n className={cn(\n 'mt-5 flex list-decimal flex-col gap-3 pl-5 marker:text-neutral/80',\n className\n )}\n {...props}\n />\n);\n\nconst ImgRenderer = ({\n className,\n alt,\n src,\n ...props\n}: ComponentProps<'img'>) => (\n <img\n {...props}\n alt={alt ?? ''}\n loading=\"lazy\"\n className={cn('max-h-[80vh] max-w-full rounded-md', className)}\n src={\n src?.includes('github.com')\n ? src\n ?.replace('github.com', 'raw.githubusercontent.com')\n .replace('/blob/', '/') // GitHub raw URLs do not use /blob/\n : src\n }\n />\n);\n\nconst createLinkRenderer = (locale?: LocalesValues) => {\n return (props: ComponentProps<'a'>) => (\n <Link\n isExternalLink={props.href?.startsWith('http')}\n underlined\n locale={locale}\n label=\"\"\n color=\"text\"\n {...(props as any)}\n />\n );\n};\n\nconst PreRenderer = (props: ComponentProps<'pre'>) => <>{props.children}</>;\nconst TableRenderer = (props: ComponentProps<typeof SmartTable>) => (\n <SmartTable isRollable displayModal {...props} />\n);\n\nconst TabsRenderer = (props: ComponentProps<typeof Tab>) => (\n <Tab\n {...props}\n className=\"rounded-xl border border-card\"\n headerClassName=\"sticky rounded-xl top-24 z-5 bg-background/70 backdrop-blur overflow-x-auto\"\n />\n);\nconst ColumnsRenderer = ({\n className,\n ...props\n}: ComponentPropsWithoutRef<'div'>) => (\n <div className={cn('flex gap-4 max-md:flex-col', className)} {...props} />\n);\nconst ColumnRenderer = ({\n className,\n ...props\n}: ComponentPropsWithoutRef<'div'>) => (\n <div className={cn('flex-1', className)} {...props} />\n);\n\nconst Iframe = (props: ComponentProps<'iframe'>) => (\n <MarkDownIframe {...props} />\n);\n\n// Static configuration object for static renderers\nconst staticMarkdownComponents = {\n h1: H1Renderer,\n h2: H2Renderer,\n h3: H3Renderer,\n h4: H4Renderer,\n h5: H5Renderer,\n h6: H6Renderer,\n strong: StrongRenderer,\n blockquote: BlockquoteRenderer,\n ul: UlRenderer,\n ol: OlRenderer,\n img: ImgRenderer,\n pre: PreRenderer,\n table: TableRenderer,\n th: Th,\n tr: Tr,\n td: Td,\n hr: Hr,\n Tabs: TabsRenderer,\n Tab: Tab.Item,\n Columns: ColumnsRenderer,\n Column: ColumnRenderer,\n iframe: Iframe,\n};\n\n// Factory function to create components with dynamic props\nconst createMarkdownComponents = (locale?: LocalesValues) => ({\n ...staticMarkdownComponents,\n code: CodeRenderer,\n a: createLinkRenderer(locale),\n});\n\n// Export static renderers for backward compatibility\nexport const baseMarkdownComponents = staticMarkdownComponents;\n\ntype MarkdownRendererProps = {\n children: string | ParsedMarkdown;\n locale?: LocalesValues;\n forceBlock?: boolean;\n preserveFrontmatter?: boolean;\n tagfilter?: boolean;\n components?: ComponentProps<typeof MarkdownRendererIntlayer>['components'];\n wrapper?: ComponentProps<typeof MarkdownRendererIntlayer>['wrapper'];\n};\n\nexport const getIntlayerMarkdownOptions = () => ({\n components: baseMarkdownComponents,\n});\n\nexport const MarkdownRenderer: FC<MarkdownRendererProps> = ({\n children,\n locale,\n forceBlock,\n preserveFrontmatter,\n tagfilter,\n components: componentsProp,\n wrapper,\n}) => {\n const markdownComponents = createMarkdownComponents(locale);\n\n const markdownContent = renderMarkdown(children, {\n components: {\n ...markdownComponents,\n ...componentsProp,\n },\n wrapper,\n forceBlock,\n preserveFrontmatter,\n tagfilter,\n });\n\n return (\n <CodeProvider>\n <TabProvider>{markdownContent}</TabProvider>\n </CodeProvider>\n );\n};\n"],"mappings":"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"}
@@ -75,7 +75,7 @@ const MarkdownEditor = ({ defaultValue, onChange, editable = true, enableAI = tr
75
75
  onUpdate: handleUpdate,
76
76
  slotAfter: /* @__PURE__ */ jsx(ImageResizer, {}),
77
77
  children: [/* @__PURE__ */ jsxs(EditorCommand, {
78
- className: "z-50 h-auto max-h-[330px] overflow-y-auto rounded-lg border border-neutral/20 bg-card px-1 py-2 text-text shadow-xl transition-all",
78
+ className: "z-50 h-auto max-h-[330px] overflow-y-auto rounded-lg border bg-card px-1 py-2 text-foreground shadow-xl transition-all",
79
79
  children: [/* @__PURE__ */ jsx(EditorCommandEmpty, {
80
80
  className: "px-2 text-muted-foreground",
81
81
  children: content.noResults.value
@@ -100,7 +100,7 @@ const MarkdownEditor = ({ defaultValue, onChange, editable = true, enableAI = tr
100
100
  children: toolbar
101
101
  }) : /* @__PURE__ */ jsx(EditorBubble, {
102
102
  options: { placement: "top" },
103
- className: "flex w-fit max-w-[90vw] rounded-lg border-[1.3px] border-neutral/20 bg-card/95 text-text shadow-xl backdrop-blur [corner-shape:squircle] supports-[corner-shape:squircle]:rounded-xl",
103
+ className: "flex w-fit max-w-[90vw] rounded-lg border-[1.3px] bg-card/95 text-foreground shadow-xl backdrop-blur [corner-shape:squircle] supports-[corner-shape:squircle]:rounded-xl",
104
104
  children: toolbar
105
105
  })]
106
106
  }) })
@@ -1 +1 @@
1
- {"version":3,"file":"MarkdownEditor.mjs","names":[],"sources":["../../../../src/components/MarkdownEditor/MarkdownEditor.tsx"],"sourcesContent":["'use client';\n\nimport { cn } from '@utils/cn';\nimport { type FC, useMemo, useState } from 'react';\nimport { useIntlayer } from 'react-intlayer';\nimport { defaultExtensions } from './extensions';\nimport { GenerativeMenuSwitch } from './generative/GenerativeMenuSwitch';\nimport {\n type CreateEditorUploadFnOptions,\n createEditorUploadFn,\n} from './imageUpload';\nimport {\n EditorBubble,\n EditorCommand,\n EditorCommandEmpty,\n EditorCommandItem,\n EditorCommandList,\n EditorContent,\n type EditorInstance,\n EditorRoot,\n handleCommandNavigation,\n handleImageDrop,\n handleImagePaste,\n ImageResizer,\n} from './novel';\nimport { ColorSelector } from './selectors/ColorSelector';\nimport { LinkSelector } from './selectors/LinkSelector';\nimport { NodeSelector } from './selectors/NodeSelector';\nimport { TextButtons } from './selectors/TextButtons';\nimport { createSlashCommand } from './slashCommand';\n\nconst Divider = () => <div className=\"h-full w-px shrink-0 bg-border\" />;\n\nexport type MarkdownEditorProps = {\n /** Initial markdown value. */\n defaultValue?: string;\n /** Called with the serialized markdown on every change. */\n onChange?: (markdown: string) => void;\n /** Whether the document can be edited. Defaults to `true`. */\n editable?: boolean;\n /** Enables the AI bubble menu (\"Ask AI\"). Defaults to `true`. */\n enableAI?: boolean;\n /**\n * Custom image uploader. When omitted, images are embedded inline as base64\n * data URLs (no backend required).\n */\n uploadImage?: CreateEditorUploadFnOptions['uploadImage'];\n /** Maximum allowed image size in megabytes. Defaults to `20`. */\n maxImageSizeMb?: number;\n /** Class applied to the editor surface. */\n className?: string;\n};\n\n/**\n * Notion-style WYSIWYG markdown editor built on Novel / Tiptap.\n *\n * Features a slash command menu, a formatting bubble menu (node / link / color\n * / inline marks), image upload (drop / paste / slash), and an optional\n * AI assistant wired to the Intlayer AI backend.\n *\n * The value contract is markdown: pass `defaultValue` as a markdown string and\n * read changes from `onChange`.\n */\nexport const MarkdownEditor: FC<MarkdownEditorProps> = ({\n defaultValue,\n onChange,\n editable = true,\n enableAI = true,\n uploadImage,\n maxImageSizeMb,\n className,\n}) => {\n const [openAI, setOpenAI] = useState(false);\n const content = useIntlayer('markdown-editor');\n\n const uploadFn = useMemo(\n () => createEditorUploadFn({ uploadImage, maxSizeMb: maxImageSizeMb }),\n [uploadImage, maxImageSizeMb]\n );\n\n const { extensions, suggestionItems } = useMemo(() => {\n const { slashCommand, suggestionItems: items } = createSlashCommand(\n uploadFn,\n content\n );\n\n return {\n extensions: [...defaultExtensions, slashCommand],\n suggestionItems: items,\n };\n }, [uploadFn, content]);\n\n const handleUpdate = ({ editor }: { editor: EditorInstance }) => {\n onChange?.(editor.storage.markdown.getMarkdown() as string);\n };\n\n const toolbar = (\n <>\n <NodeSelector />\n <Divider />\n <LinkSelector />\n <Divider />\n <TextButtons />\n <Divider />\n <ColorSelector />\n </>\n );\n\n return (\n <div className=\"relative w-full\">\n <EditorRoot>\n <EditorContent\n immediatelyRender={false}\n editable={editable}\n initialContent={defaultValue ?? ''}\n extensions={extensions}\n className={cn('min-h-[350px] w-full max-w-full', className)}\n editorProps={{\n handleDOMEvents: {\n keydown: (_view, event) => handleCommandNavigation(event),\n },\n handlePaste: (view, event) =>\n handleImagePaste(view, event, uploadFn),\n handleDrop: (view, event, _slice, moved) =>\n handleImageDrop(view, event, moved, uploadFn),\n attributes: {\n // Content typography (headings, lists, spacing) is defined in\n // `markdown-editor.css`, scoped to `.ProseMirror` — this project\n // has no Tailwind `prose` plugin.\n class: 'max-w-full pl-6 focus:outline-none',\n },\n }}\n onUpdate={handleUpdate}\n slotAfter={<ImageResizer />}\n >\n <EditorCommand className=\"z-50 h-auto max-h-[330px] overflow-y-auto rounded-lg border border-neutral/20 bg-card px-1 py-2 text-text shadow-xl transition-all\">\n <EditorCommandEmpty className=\"px-2 text-muted-foreground\">\n {content.noResults.value}\n </EditorCommandEmpty>\n <EditorCommandList>\n {suggestionItems.map((item) => (\n <EditorCommandItem\n value={item.title}\n onCommand={(val) => item.command?.(val)}\n className=\"flex w-full items-center space-x-2 rounded-md px-2 py-1 text-left text-sm hover:bg-accent aria-selected:bg-accent\"\n key={item.title}\n >\n <div className=\"flex size-10 items-center justify-center rounded-md border border-card bg-background\">\n {item.icon}\n </div>\n <div>\n <p className=\"font-medium\">{item.title}</p>\n <p className=\"text-muted-foreground text-xs\">\n {item.description}\n </p>\n </div>\n </EditorCommandItem>\n ))}\n </EditorCommandList>\n </EditorCommand>\n\n {enableAI ? (\n <GenerativeMenuSwitch open={openAI} onOpenChange={setOpenAI}>\n {toolbar}\n </GenerativeMenuSwitch>\n ) : (\n <EditorBubble\n options={{ placement: 'top' }}\n className=\"flex w-fit max-w-[90vw] rounded-lg border-[1.3px] border-neutral/20 bg-card/95 text-text shadow-xl backdrop-blur [corner-shape:squircle] supports-[corner-shape:squircle]:rounded-xl\"\n >\n {toolbar}\n </EditorBubble>\n )}\n </EditorContent>\n </EditorRoot>\n </div>\n );\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AA+BA,MAAM,gBAAgB,oBAAC,OAAD,EAAK,WAAU,iCAAkC;;;;;;;;;;;AAgCvE,MAAa,kBAA2C,EACtD,cACA,UACA,WAAW,MACX,WAAW,MACX,aACA,gBACA,gBACI;CACJ,MAAM,CAAC,QAAQ,aAAa,SAAS,KAAK;CAC1C,MAAM,UAAU,YAAY,iBAAiB;CAE7C,MAAM,WAAW,cACT,qBAAqB;EAAE;EAAa,WAAW;CAAe,CAAC,GACrE,CAAC,aAAa,cAAc,CAC9B;CAEA,MAAM,EAAE,YAAY,oBAAoB,cAAc;EACpD,MAAM,EAAE,cAAc,iBAAiB,UAAU,mBAC/C,UACA,OACF;EAEA,OAAO;GACL,YAAY,CAAC,GAAG,mBAAmB,YAAY;GAC/C,iBAAiB;EACnB;CACF,GAAG,CAAC,UAAU,OAAO,CAAC;CAEtB,MAAM,gBAAgB,EAAE,aAAyC;EAC/D,WAAW,OAAO,QAAQ,SAAS,YAAY,CAAW;CAC5D;CAEA,MAAM,UACJ;EACE,oBAAC,cAAD,CAAe;EACf,oBAAC,SAAD,CAAU;EACV,oBAAC,cAAD,CAAe;EACf,oBAAC,SAAD,CAAU;EACV,oBAAC,aAAD,CAAc;EACd,oBAAC,SAAD,CAAU;EACV,oBAAC,eAAD,CAAgB;CAChB;CAGJ,OACE,oBAAC,OAAD;EAAK,WAAU;YACb,oBAAC,YAAD,YACE,qBAAC,eAAD;GACE,mBAAmB;GACT;GACV,gBAAgB,gBAAgB;GACpB;GACZ,WAAW,GAAG,mCAAmC,SAAS;GAC1D,aAAa;IACX,iBAAiB,EACf,UAAU,OAAO,UAAU,wBAAwB,KAAK,EAC1D;IACA,cAAc,MAAM,UAClB,iBAAiB,MAAM,OAAO,QAAQ;IACxC,aAAa,MAAM,OAAO,QAAQ,UAChC,gBAAgB,MAAM,OAAO,OAAO,QAAQ;IAC9C,YAAY,EAIV,OAAO,qCACT;GACF;GACA,UAAU;GACV,WAAW,oBAAC,cAAD,CAAe;aAtB5B,CAwBE,qBAAC,eAAD;IAAe,WAAU;cAAzB,CACE,oBAAC,oBAAD;KAAoB,WAAU;eAC3B,QAAQ,UAAU;IACD,IACpB,oBAAC,mBAAD,YACG,gBAAgB,KAAK,SACpB,qBAAC,mBAAD;KACE,OAAO,KAAK;KACZ,YAAY,QAAQ,KAAK,UAAU,GAAG;KACtC,WAAU;eAHZ,CAME,oBAAC,OAAD;MAAK,WAAU;gBACZ,KAAK;KACH,IACL,qBAAC,OAAD,aACE,oBAAC,KAAD;MAAG,WAAU;gBAAe,KAAK;KAAS,IAC1C,oBAAC,KAAD;MAAG,WAAU;gBACV,KAAK;KACL,EACA,IACY;OAXZ,KAAK,KAWO,CACpB,EACgB,EACN;OAEd,WACC,oBAAC,sBAAD;IAAsB,MAAM;IAAQ,cAAc;cAC/C;GACmB,KAEtB,oBAAC,cAAD;IACE,SAAS,EAAE,WAAW,MAAM;IAC5B,WAAU;cAET;GACW,EAEH;KACL;CACT;AAET"}
1
+ {"version":3,"file":"MarkdownEditor.mjs","names":[],"sources":["../../../../src/components/MarkdownEditor/MarkdownEditor.tsx"],"sourcesContent":["'use client';\n\nimport { cn } from '@utils/cn';\nimport { type FC, useMemo, useState } from 'react';\nimport { useIntlayer } from 'react-intlayer';\nimport { defaultExtensions } from './extensions';\nimport { GenerativeMenuSwitch } from './generative/GenerativeMenuSwitch';\nimport {\n type CreateEditorUploadFnOptions,\n createEditorUploadFn,\n} from './imageUpload';\nimport {\n EditorBubble,\n EditorCommand,\n EditorCommandEmpty,\n EditorCommandItem,\n EditorCommandList,\n EditorContent,\n type EditorInstance,\n EditorRoot,\n handleCommandNavigation,\n handleImageDrop,\n handleImagePaste,\n ImageResizer,\n} from './novel';\nimport { ColorSelector } from './selectors/ColorSelector';\nimport { LinkSelector } from './selectors/LinkSelector';\nimport { NodeSelector } from './selectors/NodeSelector';\nimport { TextButtons } from './selectors/TextButtons';\nimport { createSlashCommand } from './slashCommand';\n\nconst Divider = () => <div className=\"h-full w-px shrink-0 bg-border\" />;\n\nexport type MarkdownEditorProps = {\n /** Initial markdown value. */\n defaultValue?: string;\n /** Called with the serialized markdown on every change. */\n onChange?: (markdown: string) => void;\n /** Whether the document can be edited. Defaults to `true`. */\n editable?: boolean;\n /** Enables the AI bubble menu (\"Ask AI\"). Defaults to `true`. */\n enableAI?: boolean;\n /**\n * Custom image uploader. When omitted, images are embedded inline as base64\n * data URLs (no backend required).\n */\n uploadImage?: CreateEditorUploadFnOptions['uploadImage'];\n /** Maximum allowed image size in megabytes. Defaults to `20`. */\n maxImageSizeMb?: number;\n /** Class applied to the editor surface. */\n className?: string;\n};\n\n/**\n * Notion-style WYSIWYG markdown editor built on Novel / Tiptap.\n *\n * Features a slash command menu, a formatting bubble menu (node / link / color\n * / inline marks), image upload (drop / paste / slash), and an optional\n * AI assistant wired to the Intlayer AI backend.\n *\n * The value contract is markdown: pass `defaultValue` as a markdown string and\n * read changes from `onChange`.\n */\nexport const MarkdownEditor: FC<MarkdownEditorProps> = ({\n defaultValue,\n onChange,\n editable = true,\n enableAI = true,\n uploadImage,\n maxImageSizeMb,\n className,\n}) => {\n const [openAI, setOpenAI] = useState(false);\n const content = useIntlayer('markdown-editor');\n\n const uploadFn = useMemo(\n () => createEditorUploadFn({ uploadImage, maxSizeMb: maxImageSizeMb }),\n [uploadImage, maxImageSizeMb]\n );\n\n const { extensions, suggestionItems } = useMemo(() => {\n const { slashCommand, suggestionItems: items } = createSlashCommand(\n uploadFn,\n content\n );\n\n return {\n extensions: [...defaultExtensions, slashCommand],\n suggestionItems: items,\n };\n }, [uploadFn, content]);\n\n const handleUpdate = ({ editor }: { editor: EditorInstance }) => {\n onChange?.(editor.storage.markdown.getMarkdown() as string);\n };\n\n const toolbar = (\n <>\n <NodeSelector />\n <Divider />\n <LinkSelector />\n <Divider />\n <TextButtons />\n <Divider />\n <ColorSelector />\n </>\n );\n\n return (\n <div className=\"relative w-full\">\n <EditorRoot>\n <EditorContent\n immediatelyRender={false}\n editable={editable}\n initialContent={defaultValue ?? ''}\n extensions={extensions}\n className={cn('min-h-[350px] w-full max-w-full', className)}\n editorProps={{\n handleDOMEvents: {\n keydown: (_view, event) => handleCommandNavigation(event),\n },\n handlePaste: (view, event) =>\n handleImagePaste(view, event, uploadFn),\n handleDrop: (view, event, _slice, moved) =>\n handleImageDrop(view, event, moved, uploadFn),\n attributes: {\n // Content typography (headings, lists, spacing) is defined in\n // `markdown-editor.css`, scoped to `.ProseMirror` — this project\n // has no Tailwind `prose` plugin.\n class: 'max-w-full pl-6 focus:outline-none',\n },\n }}\n onUpdate={handleUpdate}\n slotAfter={<ImageResizer />}\n >\n <EditorCommand className=\"z-50 h-auto max-h-[330px] overflow-y-auto rounded-lg border bg-card px-1 py-2 text-foreground shadow-xl transition-all\">\n <EditorCommandEmpty className=\"px-2 text-muted-foreground\">\n {content.noResults.value}\n </EditorCommandEmpty>\n <EditorCommandList>\n {suggestionItems.map((item) => (\n <EditorCommandItem\n value={item.title}\n onCommand={(val) => item.command?.(val)}\n className=\"flex w-full items-center space-x-2 rounded-md px-2 py-1 text-left text-sm hover:bg-accent aria-selected:bg-accent\"\n key={item.title}\n >\n <div className=\"flex size-10 items-center justify-center rounded-md border border-card bg-background\">\n {item.icon}\n </div>\n <div>\n <p className=\"font-medium\">{item.title}</p>\n <p className=\"text-muted-foreground text-xs\">\n {item.description}\n </p>\n </div>\n </EditorCommandItem>\n ))}\n </EditorCommandList>\n </EditorCommand>\n\n {enableAI ? (\n <GenerativeMenuSwitch open={openAI} onOpenChange={setOpenAI}>\n {toolbar}\n </GenerativeMenuSwitch>\n ) : (\n <EditorBubble\n options={{ placement: 'top' }}\n className=\"flex w-fit max-w-[90vw] rounded-lg border-[1.3px] bg-card/95 text-foreground shadow-xl backdrop-blur [corner-shape:squircle] supports-[corner-shape:squircle]:rounded-xl\"\n >\n {toolbar}\n </EditorBubble>\n )}\n </EditorContent>\n </EditorRoot>\n </div>\n );\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AA+BA,MAAM,gBAAgB,oBAAC,OAAD,EAAK,WAAU,iCAAkC;;;;;;;;;;;AAgCvE,MAAa,kBAA2C,EACtD,cACA,UACA,WAAW,MACX,WAAW,MACX,aACA,gBACA,gBACI;CACJ,MAAM,CAAC,QAAQ,aAAa,SAAS,KAAK;CAC1C,MAAM,UAAU,YAAY,iBAAiB;CAE7C,MAAM,WAAW,cACT,qBAAqB;EAAE;EAAa,WAAW;CAAe,CAAC,GACrE,CAAC,aAAa,cAAc,CAC9B;CAEA,MAAM,EAAE,YAAY,oBAAoB,cAAc;EACpD,MAAM,EAAE,cAAc,iBAAiB,UAAU,mBAC/C,UACA,OACF;EAEA,OAAO;GACL,YAAY,CAAC,GAAG,mBAAmB,YAAY;GAC/C,iBAAiB;EACnB;CACF,GAAG,CAAC,UAAU,OAAO,CAAC;CAEtB,MAAM,gBAAgB,EAAE,aAAyC;EAC/D,WAAW,OAAO,QAAQ,SAAS,YAAY,CAAW;CAC5D;CAEA,MAAM,UACJ;EACE,oBAAC,cAAD,CAAe;EACf,oBAAC,SAAD,CAAU;EACV,oBAAC,cAAD,CAAe;EACf,oBAAC,SAAD,CAAU;EACV,oBAAC,aAAD,CAAc;EACd,oBAAC,SAAD,CAAU;EACV,oBAAC,eAAD,CAAgB;CAChB;CAGJ,OACE,oBAAC,OAAD;EAAK,WAAU;YACb,oBAAC,YAAD,YACE,qBAAC,eAAD;GACE,mBAAmB;GACT;GACV,gBAAgB,gBAAgB;GACpB;GACZ,WAAW,GAAG,mCAAmC,SAAS;GAC1D,aAAa;IACX,iBAAiB,EACf,UAAU,OAAO,UAAU,wBAAwB,KAAK,EAC1D;IACA,cAAc,MAAM,UAClB,iBAAiB,MAAM,OAAO,QAAQ;IACxC,aAAa,MAAM,OAAO,QAAQ,UAChC,gBAAgB,MAAM,OAAO,OAAO,QAAQ;IAC9C,YAAY,EAIV,OAAO,qCACT;GACF;GACA,UAAU;GACV,WAAW,oBAAC,cAAD,CAAe;aAtB5B,CAwBE,qBAAC,eAAD;IAAe,WAAU;cAAzB,CACE,oBAAC,oBAAD;KAAoB,WAAU;eAC3B,QAAQ,UAAU;IACD,IACpB,oBAAC,mBAAD,YACG,gBAAgB,KAAK,SACpB,qBAAC,mBAAD;KACE,OAAO,KAAK;KACZ,YAAY,QAAQ,KAAK,UAAU,GAAG;KACtC,WAAU;eAHZ,CAME,oBAAC,OAAD;MAAK,WAAU;gBACZ,KAAK;KACH,IACL,qBAAC,OAAD,aACE,oBAAC,KAAD;MAAG,WAAU;gBAAe,KAAK;KAAS,IAC1C,oBAAC,KAAD;MAAG,WAAU;gBACV,KAAK;KACL,EACA,IACY;OAXZ,KAAK,KAWO,CACpB,EACgB,EACN;OAEd,WACC,oBAAC,sBAAD;IAAsB,MAAM;IAAQ,cAAc;cAC/C;GACmB,KAEtB,oBAAC,cAAD;IACE,SAAS,EAAE,WAAW,MAAM;IAC5B,WAAU;cAET;GACW,EAEH;KACL;CACT;AAET"}
@@ -4,7 +4,7 @@ import { toast } from "../../Toaster/useToast.mjs";
4
4
  import { Loader } from "../../Loader/index.mjs";
5
5
  import { Button } from "../../Button/Button.mjs";
6
6
  import { Command, CommandRoot } from "../../Command/index.mjs";
7
- import { MarkdownRenderer as MarkdownRenderer$1 } from "../../MarkDownRender/MarkDownRender.mjs";
7
+ import { MarkdownRenderer } from "../../MarkDownRender/MarkDownRender.mjs";
8
8
  import { useEditor } from "../novel/components/index.mjs";
9
9
  import { addAIHighlight } from "../novel/extensions/ai-highlight.mjs";
10
10
  import { AICompletionCommands } from "./AICompletionCommands.mjs";
@@ -45,12 +45,12 @@ const AISelector = ({ onOpenChange, sendButtonVariant = "default", sendButtonCol
45
45
  className: "relative overflow-y-auto",
46
46
  children: /* @__PURE__ */ jsx("div", {
47
47
  className: "prose prose-sm dark:prose-invert p-2 px-4",
48
- children: /* @__PURE__ */ jsx(MarkdownRenderer$1, { children: completion })
48
+ children: /* @__PURE__ */ jsx(MarkdownRenderer, { children: completion })
49
49
  })
50
50
  })
51
51
  }),
52
52
  isLoading && /* @__PURE__ */ jsxs("div", {
53
- className: "flex h-12 w-full items-center px-4 font-medium text-sm text-text",
53
+ className: "flex h-12 w-full items-center px-4 font-medium text-foreground text-sm",
54
54
  children: [
55
55
  /* @__PURE__ */ jsx(Sparkles, { className: "mr-2 size-4 shrink-0" }),
56
56
  content.aiThinking,
@@ -1 +1 @@
1
- {"version":3,"file":"AISelector.mjs","names":["MarkdownRenderer"],"sources":["../../../../../src/components/MarkdownEditor/generative/AISelector.tsx"],"sourcesContent":["'use client';\n\nimport {\n Button,\n type ButtonColor,\n type ButtonVariant,\n} from '@components/Button';\nimport { Command, CommandRoot } from '@components/Command';\nimport { Loader } from '@components/Loader';\nimport { MarkdownRenderer } from '@components/MarkDownRender';\nimport { toast } from '@components/Toaster';\nimport { ArrowUp, Sparkles } from 'lucide-react';\nimport { useState } from 'react';\nimport { useIntlayer } from 'react-intlayer';\nimport { addAIHighlight, useEditor } from '../novel';\nimport { AICompletionCommands } from './AICompletionCommands';\nimport { AISelectorCommands } from './AISelectorCommands';\nimport { useAICompletion } from './useAICompletion';\n\nexport type AISelectorProps = {\n onOpenChange: (open: boolean) => void;\n /** Visual variant for the send button. @default \"default\" */\n sendButtonVariant?: ButtonVariant;\n /** Color theme for the send button. @default \"text\" */\n sendButtonColor?: ButtonColor;\n};\n\n/**\n * The AI command palette shown inside the bubble menu. Lets the user run a\n * preset transformation or a free-form prompt against the current selection,\n * preview the markdown result, then replace / insert / discard it.\n */\nexport const AISelector = ({\n onOpenChange,\n sendButtonVariant = 'default',\n sendButtonColor = 'text',\n}: AISelectorProps) => {\n const { editor } = useEditor();\n const [inputValue, setInputValue] = useState('');\n const content = useIntlayer('markdown-editor');\n\n const { completion, complete, isLoading, reset } = useAICompletion((error) =>\n toast({\n variant: 'error',\n title: content.aiError.value,\n description: error.message,\n })\n );\n\n if (!editor) return null;\n\n const hasCompletion = completion.length > 0;\n\n const getSelectionMarkdown = () => {\n const slice = editor.state.selection.content();\n return editor.storage.markdown.serializer.serialize(\n slice.content\n ) as string;\n };\n\n return (\n <CommandRoot className=\"w-[350px]\">\n {hasCompletion && (\n <div className=\"flex max-h-[400px]\">\n <div className=\"relative overflow-y-auto\">\n <div className=\"prose prose-sm dark:prose-invert p-2 px-4\">\n <MarkdownRenderer>{completion}</MarkdownRenderer>\n </div>\n </div>\n </div>\n )}\n\n {isLoading && (\n <div className=\"flex h-12 w-full items-center px-4 font-medium text-sm text-text\">\n <Sparkles className=\"mr-2 size-4 shrink-0\" />\n {content.aiThinking}\n <div className=\"mt-1 ml-2\">\n <Loader />\n </div>\n </div>\n )}\n\n {!isLoading && (\n <>\n <div className=\"flex items-center gap-2 px-3 py-3\">\n <Command.Input\n value={inputValue}\n onValueChange={setInputValue}\n autoFocus\n placeholder={\n hasCompletion ? content.tellAI.value : content.askAIToEdit.value\n }\n onFocus={() => addAIHighlight(editor)}\n />\n <Button\n label={content.send.value}\n size=\"icon-sm\"\n roundedSize=\"full\"\n variant={sendButtonVariant}\n color={sendButtonColor}\n className=\"size-7 shrink-0\"\n Icon={ArrowUp}\n onClick={() => {\n const text = completion || getSelectionMarkdown();\n complete(text, {\n body: { option: 'zap', command: inputValue },\n }).then(() => setInputValue(''));\n }}\n />\n </div>\n {hasCompletion ? (\n <AICompletionCommands\n completion={completion}\n onDiscard={() => {\n editor.chain().unsetHighlight().focus().run();\n reset();\n onOpenChange(false);\n }}\n />\n ) : (\n <AISelectorCommands\n onSelect={(value, option) =>\n complete(value, { body: { option } })\n }\n />\n )}\n </>\n )}\n </CommandRoot>\n );\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AAgCA,MAAa,cAAc,EACzB,cACA,oBAAoB,WACpB,kBAAkB,aACG;CACrB,MAAM,EAAE,WAAW,UAAU;CAC7B,MAAM,CAAC,YAAY,iBAAiB,SAAS,EAAE;CAC/C,MAAM,UAAU,YAAY,iBAAiB;CAE7C,MAAM,EAAE,YAAY,UAAU,WAAW,UAAU,iBAAiB,UAClE,MAAM;EACJ,SAAS;EACT,OAAO,QAAQ,QAAQ;EACvB,aAAa,MAAM;CACrB,CAAC,CACH;CAEA,IAAI,CAAC,QAAQ,OAAO;CAEpB,MAAM,gBAAgB,WAAW,SAAS;CAE1C,MAAM,6BAA6B;EACjC,MAAM,QAAQ,OAAO,MAAM,UAAU,QAAQ;EAC7C,OAAO,OAAO,QAAQ,SAAS,WAAW,UACxC,MAAM,OACR;CACF;CAEA,OACE,qBAAC,aAAD;EAAa,WAAU;YAAvB;GACG,iBACC,oBAAC,OAAD;IAAK,WAAU;cACb,oBAAC,OAAD;KAAK,WAAU;eACb,oBAAC,OAAD;MAAK,WAAU;gBACb,oBAACA,oBAAD,YAAmB,WAA6B;KAC7C;IACF;GACF;GAGN,aACC,qBAAC,OAAD;IAAK,WAAU;cAAf;KACE,oBAAC,UAAD,EAAU,WAAU,uBAAwB;KAC3C,QAAQ;KACT,oBAAC,OAAD;MAAK,WAAU;gBACb,oBAAC,QAAD,CAAS;KACN;IACF;;GAGN,CAAC,aACA,8CACE,qBAAC,OAAD;IAAK,WAAU;cAAf,CACE,oBAAC,QAAQ,OAAT;KACE,OAAO;KACP,eAAe;KACf;KACA,aACE,gBAAgB,QAAQ,OAAO,QAAQ,QAAQ,YAAY;KAE7D,eAAe,eAAe,MAAM;IACrC,IACD,oBAAC,QAAD;KACE,OAAO,QAAQ,KAAK;KACpB,MAAK;KACL,aAAY;KACZ,SAAS;KACT,OAAO;KACP,WAAU;KACV,MAAM;KACN,eAAe;MACb,MAAM,OAAO,cAAc,qBAAqB;MAChD,SAAS,MAAM,EACb,MAAM;OAAE,QAAQ;OAAO,SAAS;MAAW,EAC7C,CAAC,CAAC,CAAC,WAAW,cAAc,EAAE,CAAC;KACjC;IACD,EACE;OACJ,gBACC,oBAAC,sBAAD;IACc;IACZ,iBAAiB;KACf,OAAO,MAAM,CAAC,CAAC,eAAe,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI;KAC5C,MAAM;KACN,aAAa,KAAK;IACpB;GACD,KAED,oBAAC,oBAAD,EACE,WAAW,OAAO,WAChB,SAAS,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,EAEvC,EAEH;EAEO;;AAEjB"}
1
+ {"version":3,"file":"AISelector.mjs","names":[],"sources":["../../../../../src/components/MarkdownEditor/generative/AISelector.tsx"],"sourcesContent":["'use client';\n\nimport {\n Button,\n type ButtonColor,\n type ButtonVariant,\n} from '@components/Button';\nimport { Command, CommandRoot } from '@components/Command';\nimport { Loader } from '@components/Loader';\nimport { MarkdownRenderer } from '@components/MarkDownRender';\nimport { toast } from '@components/Toaster';\nimport { ArrowUp, Sparkles } from 'lucide-react';\nimport { useState } from 'react';\nimport { useIntlayer } from 'react-intlayer';\nimport { addAIHighlight, useEditor } from '../novel';\nimport { AICompletionCommands } from './AICompletionCommands';\nimport { AISelectorCommands } from './AISelectorCommands';\nimport { useAICompletion } from './useAICompletion';\n\nexport type AISelectorProps = {\n onOpenChange: (open: boolean) => void;\n /** Visual variant for the send button. @default \"default\" */\n sendButtonVariant?: ButtonVariant;\n /** Color theme for the send button. @default \"text\" */\n sendButtonColor?: ButtonColor;\n};\n\n/**\n * The AI command palette shown inside the bubble menu. Lets the user run a\n * preset transformation or a free-form prompt against the current selection,\n * preview the markdown result, then replace / insert / discard it.\n */\nexport const AISelector = ({\n onOpenChange,\n sendButtonVariant = 'default',\n sendButtonColor = 'text',\n}: AISelectorProps) => {\n const { editor } = useEditor();\n const [inputValue, setInputValue] = useState('');\n const content = useIntlayer('markdown-editor');\n\n const { completion, complete, isLoading, reset } = useAICompletion((error) =>\n toast({\n variant: 'error',\n title: content.aiError.value,\n description: error.message,\n })\n );\n\n if (!editor) return null;\n\n const hasCompletion = completion.length > 0;\n\n const getSelectionMarkdown = () => {\n const slice = editor.state.selection.content();\n return editor.storage.markdown.serializer.serialize(\n slice.content\n ) as string;\n };\n\n return (\n <CommandRoot className=\"w-[350px]\">\n {hasCompletion && (\n <div className=\"flex max-h-[400px]\">\n <div className=\"relative overflow-y-auto\">\n <div className=\"prose prose-sm dark:prose-invert p-2 px-4\">\n <MarkdownRenderer>{completion}</MarkdownRenderer>\n </div>\n </div>\n </div>\n )}\n\n {isLoading && (\n <div className=\"flex h-12 w-full items-center px-4 font-medium text-foreground text-sm\">\n <Sparkles className=\"mr-2 size-4 shrink-0\" />\n {content.aiThinking}\n <div className=\"mt-1 ml-2\">\n <Loader />\n </div>\n </div>\n )}\n\n {!isLoading && (\n <>\n <div className=\"flex items-center gap-2 px-3 py-3\">\n <Command.Input\n value={inputValue}\n onValueChange={setInputValue}\n autoFocus\n placeholder={\n hasCompletion ? content.tellAI.value : content.askAIToEdit.value\n }\n onFocus={() => addAIHighlight(editor)}\n />\n <Button\n label={content.send.value}\n size=\"icon-sm\"\n roundedSize=\"full\"\n variant={sendButtonVariant}\n color={sendButtonColor}\n className=\"size-7 shrink-0\"\n Icon={ArrowUp}\n onClick={() => {\n const text = completion || getSelectionMarkdown();\n complete(text, {\n body: { option: 'zap', command: inputValue },\n }).then(() => setInputValue(''));\n }}\n />\n </div>\n {hasCompletion ? (\n <AICompletionCommands\n completion={completion}\n onDiscard={() => {\n editor.chain().unsetHighlight().focus().run();\n reset();\n onOpenChange(false);\n }}\n />\n ) : (\n <AISelectorCommands\n onSelect={(value, option) =>\n complete(value, { body: { option } })\n }\n />\n )}\n </>\n )}\n </CommandRoot>\n );\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AAgCA,MAAa,cAAc,EACzB,cACA,oBAAoB,WACpB,kBAAkB,aACG;CACrB,MAAM,EAAE,WAAW,UAAU;CAC7B,MAAM,CAAC,YAAY,iBAAiB,SAAS,EAAE;CAC/C,MAAM,UAAU,YAAY,iBAAiB;CAE7C,MAAM,EAAE,YAAY,UAAU,WAAW,UAAU,iBAAiB,UAClE,MAAM;EACJ,SAAS;EACT,OAAO,QAAQ,QAAQ;EACvB,aAAa,MAAM;CACrB,CAAC,CACH;CAEA,IAAI,CAAC,QAAQ,OAAO;CAEpB,MAAM,gBAAgB,WAAW,SAAS;CAE1C,MAAM,6BAA6B;EACjC,MAAM,QAAQ,OAAO,MAAM,UAAU,QAAQ;EAC7C,OAAO,OAAO,QAAQ,SAAS,WAAW,UACxC,MAAM,OACR;CACF;CAEA,OACE,qBAAC,aAAD;EAAa,WAAU;YAAvB;GACG,iBACC,oBAAC,OAAD;IAAK,WAAU;cACb,oBAAC,OAAD;KAAK,WAAU;eACb,oBAAC,OAAD;MAAK,WAAU;gBACb,oBAAC,kBAAD,YAAmB,WAA6B;KAC7C;IACF;GACF;GAGN,aACC,qBAAC,OAAD;IAAK,WAAU;cAAf;KACE,oBAAC,UAAD,EAAU,WAAU,uBAAwB;KAC3C,QAAQ;KACT,oBAAC,OAAD;MAAK,WAAU;gBACb,oBAAC,QAAD,CAAS;KACN;IACF;;GAGN,CAAC,aACA,8CACE,qBAAC,OAAD;IAAK,WAAU;cAAf,CACE,oBAAC,QAAQ,OAAT;KACE,OAAO;KACP,eAAe;KACf;KACA,aACE,gBAAgB,QAAQ,OAAO,QAAQ,QAAQ,YAAY;KAE7D,eAAe,eAAe,MAAM;IACrC,IACD,oBAAC,QAAD;KACE,OAAO,QAAQ,KAAK;KACpB,MAAK;KACL,aAAY;KACZ,SAAS;KACT,OAAO;KACP,WAAU;KACV,MAAM;KACN,eAAe;MACb,MAAM,OAAO,cAAc,qBAAqB;MAChD,SAAS,MAAM,EACb,MAAM;OAAE,QAAQ;OAAO,SAAS;MAAW,EAC7C,CAAC,CAAC,CAAC,WAAW,cAAc,EAAE,CAAC;KACjC;IACD,EACE;OACJ,gBACC,oBAAC,sBAAD;IACc;IACZ,iBAAiB;KACf,OAAO,MAAM,CAAC,CAAC,eAAe,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI;KAC5C,MAAM;KACN,aAAa,KAAK;IACpB;GACD,KAED,oBAAC,oBAAD,EACE,WAAW,OAAO,WAChB,SAAS,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,EAEvC,EAEH;EAEO;;AAEjB"}
@@ -57,7 +57,7 @@ const AISelectorCommands = ({ onSelect }) => {
57
57
  const slice = editor.state.selection.content();
58
58
  onSelect(editor.storage.markdown.serializer.serialize(slice.content), value);
59
59
  },
60
- children: [/* @__PURE__ */ jsx(option.icon, { className: "size-4 text-text" }), getOptionLabel(option.value)]
60
+ children: [/* @__PURE__ */ jsx(option.icon, { className: "size-4 text-foreground" }), getOptionLabel(option.value)]
61
61
  }, option.value))
62
62
  }),
63
63
  /* @__PURE__ */ jsx(Command.Separator, {}),
@@ -70,7 +70,7 @@ const AISelectorCommands = ({ onSelect }) => {
70
70
  const pos = editor.state.selection.from;
71
71
  onSelect(getPrevText(editor, pos), "continue");
72
72
  },
73
- children: [/* @__PURE__ */ jsx(StepForward, { className: "size-4 text-text" }), content.continueWriting]
73
+ children: [/* @__PURE__ */ jsx(StepForward, { className: "size-4 text-foreground" }), content.continueWriting]
74
74
  })
75
75
  })
76
76
  ] });
@@ -1 +1 @@
1
- {"version":3,"file":"AISelectorCommands.mjs","names":[],"sources":["../../../../../src/components/MarkdownEditor/generative/AISelectorCommands.tsx"],"sourcesContent":["'use client';\n\nimport { Command } from '@components/Command';\nimport {\n ArrowDownWideNarrow,\n CheckCheck,\n RefreshCcwDot,\n StepForward,\n WrapText,\n} from 'lucide-react';\nimport { useIntlayer } from 'react-intlayer';\nimport { getPrevText, useEditor } from '../novel';\nimport type { AICompletionOption } from './useAICompletion';\n\nconst options: {\n value: Exclude<AICompletionOption, 'zap'>;\n label: string;\n icon: typeof RefreshCcwDot;\n}[] = [\n { value: 'improve', label: 'Improve writing', icon: RefreshCcwDot },\n { value: 'fix', label: 'Fix grammar', icon: CheckCheck },\n { value: 'shorter', label: 'Make shorter', icon: ArrowDownWideNarrow },\n { value: 'longer', label: 'Make longer', icon: WrapText },\n];\n\nexport type AISelectorCommandsProps = {\n onSelect: (value: string, option: AICompletionOption) => void;\n};\n\n/**\n * The default command list shown in the AI bubble before a completion exists:\n * transforms applied to the current selection plus \"continue writing\".\n */\nexport const AISelectorCommands = ({ onSelect }: AISelectorCommandsProps) => {\n const { editor } = useEditor();\n const content = useIntlayer('markdown-editor');\n\n if (!editor) return null;\n\n const getOptionLabel = (value: string) => {\n switch (value) {\n case 'improve':\n return content.improveWriting.value;\n case 'fix':\n return content.fixGrammar.value;\n case 'shorter':\n return content.makeShorter.value;\n case 'longer':\n return content.makeLonger.value;\n default:\n return value;\n }\n };\n\n return (\n <>\n <Command.Group heading={content.editHeading.value}>\n {options.map((option) => (\n <Command.Item\n key={option.value}\n value={option.value}\n className=\"flex gap-2 px-4\"\n onSelect={(value) => {\n const slice = editor.state.selection.content();\n const text = editor.storage.markdown.serializer.serialize(\n slice.content\n );\n onSelect(text, value as AICompletionOption);\n }}\n >\n <option.icon className=\"size-4 text-text\" />\n {getOptionLabel(option.value)}\n </Command.Item>\n ))}\n </Command.Group>\n <Command.Separator />\n <Command.Group heading={content.moreHeading.value}>\n <Command.Item\n value=\"continue\"\n className=\"gap-2 px-4\"\n onSelect={() => {\n const pos = editor.state.selection.from;\n const text = getPrevText(editor, pos);\n onSelect(text, 'continue');\n }}\n >\n <StepForward className=\"size-4 text-text\" />\n {content.continueWriting}\n </Command.Item>\n </Command.Group>\n </>\n );\n};\n"],"mappings":";;;;;;;;;;AAcA,MAAM,UAIA;CACJ;EAAE,OAAO;EAAW,OAAO;EAAmB,MAAM;CAAc;CAClE;EAAE,OAAO;EAAO,OAAO;EAAe,MAAM;CAAW;CACvD;EAAE,OAAO;EAAW,OAAO;EAAgB,MAAM;CAAoB;CACrE;EAAE,OAAO;EAAU,OAAO;EAAe,MAAM;CAAS;AAC1D;;;;;AAUA,MAAa,sBAAsB,EAAE,eAAwC;CAC3E,MAAM,EAAE,WAAW,UAAU;CAC7B,MAAM,UAAU,YAAY,iBAAiB;CAE7C,IAAI,CAAC,QAAQ,OAAO;CAEpB,MAAM,kBAAkB,UAAkB;EACxC,QAAQ,OAAR;GACE,KAAK,WACH,OAAO,QAAQ,eAAe;GAChC,KAAK,OACH,OAAO,QAAQ,WAAW;GAC5B,KAAK,WACH,OAAO,QAAQ,YAAY;GAC7B,KAAK,UACH,OAAO,QAAQ,WAAW;GAC5B,SACE,OAAO;EACX;CACF;CAEA,OACE;EACE,oBAAC,QAAQ,OAAT;GAAe,SAAS,QAAQ,YAAY;aACzC,QAAQ,KAAK,WACZ,qBAAC,QAAQ,MAAT;IAEE,OAAO,OAAO;IACd,WAAU;IACV,WAAW,UAAU;KACnB,MAAM,QAAQ,OAAO,MAAM,UAAU,QAAQ;KAI7C,SAHa,OAAO,QAAQ,SAAS,WAAW,UAC9C,MAAM,OAEI,GAAG,KAA2B;IAC5C;cAVF,CAYE,oBAAC,OAAO,MAAR,EAAa,WAAU,mBAAoB,IAC1C,eAAe,OAAO,KAAK,CAChB;MAbP,OAAO,KAaA,CACf;EACY;EACf,oBAAC,QAAQ,WAAT,CAAoB;EACpB,oBAAC,QAAQ,OAAT;GAAe,SAAS,QAAQ,YAAY;aAC1C,qBAAC,QAAQ,MAAT;IACE,OAAM;IACN,WAAU;IACV,gBAAgB;KACd,MAAM,MAAM,OAAO,MAAM,UAAU;KAEnC,SADa,YAAY,QAAQ,GACrB,GAAG,UAAU;IAC3B;cAPF,CASE,oBAAC,aAAD,EAAa,WAAU,mBAAoB,IAC1C,QAAQ,eACG;;EACD;CACf;AAEN"}
1
+ {"version":3,"file":"AISelectorCommands.mjs","names":[],"sources":["../../../../../src/components/MarkdownEditor/generative/AISelectorCommands.tsx"],"sourcesContent":["'use client';\n\nimport { Command } from '@components/Command';\nimport {\n ArrowDownWideNarrow,\n CheckCheck,\n RefreshCcwDot,\n StepForward,\n WrapText,\n} from 'lucide-react';\nimport { useIntlayer } from 'react-intlayer';\nimport { getPrevText, useEditor } from '../novel';\nimport type { AICompletionOption } from './useAICompletion';\n\nconst options: {\n value: Exclude<AICompletionOption, 'zap'>;\n label: string;\n icon: typeof RefreshCcwDot;\n}[] = [\n { value: 'improve', label: 'Improve writing', icon: RefreshCcwDot },\n { value: 'fix', label: 'Fix grammar', icon: CheckCheck },\n { value: 'shorter', label: 'Make shorter', icon: ArrowDownWideNarrow },\n { value: 'longer', label: 'Make longer', icon: WrapText },\n];\n\nexport type AISelectorCommandsProps = {\n onSelect: (value: string, option: AICompletionOption) => void;\n};\n\n/**\n * The default command list shown in the AI bubble before a completion exists:\n * transforms applied to the current selection plus \"continue writing\".\n */\nexport const AISelectorCommands = ({ onSelect }: AISelectorCommandsProps) => {\n const { editor } = useEditor();\n const content = useIntlayer('markdown-editor');\n\n if (!editor) return null;\n\n const getOptionLabel = (value: string) => {\n switch (value) {\n case 'improve':\n return content.improveWriting.value;\n case 'fix':\n return content.fixGrammar.value;\n case 'shorter':\n return content.makeShorter.value;\n case 'longer':\n return content.makeLonger.value;\n default:\n return value;\n }\n };\n\n return (\n <>\n <Command.Group heading={content.editHeading.value}>\n {options.map((option) => (\n <Command.Item\n key={option.value}\n value={option.value}\n className=\"flex gap-2 px-4\"\n onSelect={(value) => {\n const slice = editor.state.selection.content();\n const text = editor.storage.markdown.serializer.serialize(\n slice.content\n );\n onSelect(text, value as AICompletionOption);\n }}\n >\n <option.icon className=\"size-4 text-foreground\" />\n {getOptionLabel(option.value)}\n </Command.Item>\n ))}\n </Command.Group>\n <Command.Separator />\n <Command.Group heading={content.moreHeading.value}>\n <Command.Item\n value=\"continue\"\n className=\"gap-2 px-4\"\n onSelect={() => {\n const pos = editor.state.selection.from;\n const text = getPrevText(editor, pos);\n onSelect(text, 'continue');\n }}\n >\n <StepForward className=\"size-4 text-foreground\" />\n {content.continueWriting}\n </Command.Item>\n </Command.Group>\n </>\n );\n};\n"],"mappings":";;;;;;;;;;AAcA,MAAM,UAIA;CACJ;EAAE,OAAO;EAAW,OAAO;EAAmB,MAAM;CAAc;CAClE;EAAE,OAAO;EAAO,OAAO;EAAe,MAAM;CAAW;CACvD;EAAE,OAAO;EAAW,OAAO;EAAgB,MAAM;CAAoB;CACrE;EAAE,OAAO;EAAU,OAAO;EAAe,MAAM;CAAS;AAC1D;;;;;AAUA,MAAa,sBAAsB,EAAE,eAAwC;CAC3E,MAAM,EAAE,WAAW,UAAU;CAC7B,MAAM,UAAU,YAAY,iBAAiB;CAE7C,IAAI,CAAC,QAAQ,OAAO;CAEpB,MAAM,kBAAkB,UAAkB;EACxC,QAAQ,OAAR;GACE,KAAK,WACH,OAAO,QAAQ,eAAe;GAChC,KAAK,OACH,OAAO,QAAQ,WAAW;GAC5B,KAAK,WACH,OAAO,QAAQ,YAAY;GAC7B,KAAK,UACH,OAAO,QAAQ,WAAW;GAC5B,SACE,OAAO;EACX;CACF;CAEA,OACE;EACE,oBAAC,QAAQ,OAAT;GAAe,SAAS,QAAQ,YAAY;aACzC,QAAQ,KAAK,WACZ,qBAAC,QAAQ,MAAT;IAEE,OAAO,OAAO;IACd,WAAU;IACV,WAAW,UAAU;KACnB,MAAM,QAAQ,OAAO,MAAM,UAAU,QAAQ;KAI7C,SAHa,OAAO,QAAQ,SAAS,WAAW,UAC9C,MAAM,OAEI,GAAG,KAA2B;IAC5C;cAVF,CAYE,oBAAC,OAAO,MAAR,EAAa,WAAU,yBAA0B,IAChD,eAAe,OAAO,KAAK,CAChB;MAbP,OAAO,KAaA,CACf;EACY;EACf,oBAAC,QAAQ,WAAT,CAAoB;EACpB,oBAAC,QAAQ,OAAT;GAAe,SAAS,QAAQ,YAAY;aAC1C,qBAAC,QAAQ,MAAT;IACE,OAAM;IACN,WAAU;IACV,gBAAgB;KACd,MAAM,MAAM,OAAO,MAAM,UAAU;KAEnC,SADa,YAAY,QAAQ,GACrB,GAAG,UAAU;IAC3B;cAPF,CASE,oBAAC,aAAD,EAAa,WAAU,yBAA0B,IAChD,QAAQ,eACG;;EACD;CACf;AAEN"}