app-tutor-ai-consumer 1.2.0 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (108) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/config/vitest/__mocks__/sparkie.tsx +12 -0
  3. package/config/vitest/setupTests.ts +6 -1
  4. package/config/vitest/vitest.config.mts +1 -0
  5. package/environments/.env.test +2 -2
  6. package/package.json +12 -2
  7. package/public/assets/images/default-image.png +0 -0
  8. package/public/assets/svg/tutor-onboarding.svg +128 -0
  9. package/src/@types/declarations.d.ts +16 -6
  10. package/src/config/dayjs/index.ts +2 -0
  11. package/src/config/dayjs/init.ts +28 -0
  12. package/src/config/dayjs/utils/format-fulldate.ts +7 -0
  13. package/src/config/dayjs/utils/format-time.ts +20 -0
  14. package/src/config/dayjs/utils/index.ts +2 -0
  15. package/src/config/optimizely/optimizely-provider.tsx +3 -3
  16. package/src/config/optimizely/optimizely.ts +1 -1
  17. package/src/config/styles/global.css +20 -1
  18. package/src/config/styles/utilities/bg-utilities.module.css +11 -0
  19. package/src/config/styles/utilities/text-utilities.module.css +6 -0
  20. package/src/config/tanstack/query-provider.tsx +1 -1
  21. package/src/config/tests/handlers.ts +9 -0
  22. package/src/index.tsx +4 -0
  23. package/src/lib/components/button/button.tsx +86 -0
  24. package/src/lib/components/button/index.ts +1 -0
  25. package/src/lib/components/index.ts +2 -0
  26. package/src/lib/components/markdownrenderer/__tests__/markdown.stub.ts +334 -0
  27. package/src/lib/components/markdownrenderer/components/index.ts +1 -0
  28. package/src/lib/components/markdownrenderer/components/md-code-block/index.ts +1 -0
  29. package/src/lib/components/markdownrenderer/components/md-code-block/md-code-block.tsx +71 -0
  30. package/src/lib/components/markdownrenderer/index.ts +2 -0
  31. package/src/lib/components/markdownrenderer/markdownrenderer.tsx +115 -0
  32. package/src/lib/hooks/index.ts +1 -0
  33. package/src/lib/hooks/use-ref-event-listener/index.ts +2 -0
  34. package/src/lib/hooks/use-ref-event-listener/use-ref-event-listener.tsx +32 -0
  35. package/src/lib/utils/constants.ts +1 -1
  36. package/src/lib/utils/copy-text-to-clipboard.tsx +13 -0
  37. package/src/lib/utils/extract-text-from-react-nodes.ts +23 -0
  38. package/src/lib/utils/index.ts +3 -0
  39. package/src/lib/utils/urls.ts +20 -0
  40. package/src/main/main.spec.tsx +17 -7
  41. package/src/main/main.tsx +2 -13
  42. package/src/modules/messages/__tests__/imessage-with-sender-data.builder.ts +113 -0
  43. package/src/modules/messages/__tests__/imessage-with-sender-data.mock.ts +15 -0
  44. package/src/modules/messages/components/chat-input/chat-input.atom.ts +12 -0
  45. package/src/modules/{create-message → messages}/components/chat-input/chat-input.tsx +6 -2
  46. package/src/modules/{create-message → messages}/components/chat-input/index.ts +1 -0
  47. package/src/modules/{create-message → messages}/components/chat-input/types.ts +1 -0
  48. package/src/modules/messages/components/index.ts +4 -0
  49. package/src/modules/messages/components/message-img/index.ts +1 -0
  50. package/src/modules/messages/components/message-img/message-img.tsx +47 -0
  51. package/src/modules/messages/components/message-item/index.ts +2 -0
  52. package/src/modules/messages/components/message-item/message-item.spec.tsx +26 -0
  53. package/src/modules/messages/components/message-item/message-item.tsx +15 -0
  54. package/src/modules/messages/components/messages-list/index.ts +2 -0
  55. package/src/modules/messages/components/messages-list/messages-list.tsx +53 -0
  56. package/src/modules/messages/constants.ts +1 -0
  57. package/src/modules/messages/hooks/index.ts +1 -0
  58. package/src/modules/messages/hooks/use-fetch-messages/index.ts +2 -0
  59. package/src/modules/messages/hooks/use-fetch-messages/use-fetch-messages.spec.tsx +46 -0
  60. package/src/modules/messages/hooks/use-fetch-messages/use-fetch-messages.tsx +103 -0
  61. package/src/modules/messages/index.ts +3 -0
  62. package/src/modules/messages/service.ts +86 -0
  63. package/src/modules/messages/types.ts +78 -0
  64. package/src/modules/messages/utils/index.ts +1 -0
  65. package/src/modules/messages/utils/messages-parser/index.ts +1 -0
  66. package/src/modules/messages/utils/messages-parser/utils.ts +28 -0
  67. package/src/modules/profile/__tests__/profile-api-props.builder.ts +74 -0
  68. package/src/modules/profile/__tests__/profile-props.builder.ts +42 -0
  69. package/src/modules/profile/constants.ts +3 -0
  70. package/src/modules/profile/hooks/index.ts +1 -0
  71. package/src/modules/profile/hooks/use-get-profile/index.ts +1 -0
  72. package/src/modules/profile/hooks/use-get-profile/use-get-profile.spec.tsx +20 -0
  73. package/src/modules/profile/hooks/use-get-profile/use-get-profile.tsx +14 -0
  74. package/src/modules/profile/index.ts +4 -0
  75. package/src/modules/profile/service.tsx +19 -0
  76. package/src/modules/profile/types.ts +17 -0
  77. package/src/modules/sparkie/constants.ts +21 -0
  78. package/src/modules/sparkie/index.ts +3 -0
  79. package/src/modules/sparkie/service.ts +94 -0
  80. package/src/modules/sparkie/types.ts +47 -0
  81. package/src/modules/sparkie/utils/validate-firebase-config.spec.ts +17 -0
  82. package/src/modules/sparkie/utils/validate-firebase-config.ts +12 -0
  83. package/src/modules/widget/__tests__/widget-settings-props.builder.ts +121 -0
  84. package/src/modules/widget/components/chat-page/chat-page.tsx +20 -0
  85. package/src/modules/widget/components/chat-page/index.ts +2 -0
  86. package/src/modules/widget/components/constants.tsx +9 -0
  87. package/src/modules/widget/components/container/container.tsx +32 -0
  88. package/src/modules/widget/components/container/index.ts +2 -0
  89. package/src/modules/widget/components/container/types.ts +3 -0
  90. package/src/modules/widget/components/greetings-card/greetings-card.tsx +1 -1
  91. package/src/modules/widget/components/greetings-card/styles.module.css +1 -3
  92. package/src/modules/widget/components/index.ts +3 -0
  93. package/src/modules/widget/components/onboarding-page/index.ts +1 -0
  94. package/src/modules/widget/components/onboarding-page/onboarding-page.tsx +40 -0
  95. package/src/modules/widget/components/onboarding-page/styles.module.css +7 -0
  96. package/src/modules/widget/components/starter-page/index.ts +1 -0
  97. package/src/modules/widget/components/starter-page/starter-page.tsx +41 -0
  98. package/src/modules/widget/hooks/index.ts +1 -0
  99. package/src/modules/widget/hooks/use-init-sparkie/index.ts +1 -0
  100. package/src/modules/widget/hooks/use-init-sparkie/use-init-sparkie.tsx +18 -0
  101. package/src/modules/widget/store/index.ts +1 -0
  102. package/src/modules/widget/store/widget-settings.atom.ts +3 -1
  103. package/src/modules/widget/store/widget-tabs.atom.ts +53 -0
  104. package/tailwind.config.js +95 -1
  105. package/config/vitest/index.ts +0 -1
  106. package/src/config/styles/shared-styles.module.css +0 -16
  107. package/src/main/styles.module.css +0 -15
  108. package/src/modules/create-message/components/index.ts +0 -1
@@ -7,15 +7,25 @@ declare module '*.css'
7
7
 
8
8
  declare module '*.css?inline'
9
9
 
10
- declare module '*.png'
11
-
12
- declare module '*.jpg'
10
+ declare module '*.png' {
11
+ const content: string
12
+ export default content
13
+ }
13
14
 
14
- declare module '*.gif'
15
+ declare module '*.jpg' {
16
+ const content: string
17
+ export default content
18
+ }
15
19
 
16
- declare module '*?url'
20
+ declare module '*.gif' {
21
+ const content: string
22
+ export default content
23
+ }
17
24
 
18
- declare module '*.svg?url'
25
+ declare module '*.svg?url' {
26
+ const content: string
27
+ export default content
28
+ }
19
29
 
20
30
  declare module '*.svg' {
21
31
  const ReactComponent: React.FunctionComponent<React.SVGProps<SVGSVGElement>>
@@ -0,0 +1,2 @@
1
+ export * from './init'
2
+ export * from './utils'
@@ -0,0 +1,28 @@
1
+ import dayjs from 'dayjs'
2
+ import calendar from 'dayjs/plugin/calendar'
3
+ import isBetween from 'dayjs/plugin/isBetween'
4
+ import isToday from 'dayjs/plugin/isToday'
5
+ import localizedFormat from 'dayjs/plugin/localizedFormat'
6
+ import timezone from 'dayjs/plugin/timezone'
7
+ import utc from 'dayjs/plugin/utc'
8
+
9
+ import { DEFAULT_LANGUAGE } from '../i18n'
10
+
11
+ export const initDayjs = async (locale: string = DEFAULT_LANGUAGE) => {
12
+ dayjs.extend(localizedFormat)
13
+ dayjs.extend(utc)
14
+ dayjs.extend(timezone)
15
+ dayjs.extend(isBetween)
16
+ dayjs.extend(isToday)
17
+ dayjs.extend(calendar)
18
+
19
+ const lng = locale.match(/^pt/i) ? 'pt-br' : locale
20
+
21
+ try {
22
+ await import(`dayjs/locale/${lng}`)
23
+ dayjs.locale(lng)
24
+ } catch {
25
+ await import(`dayjs/locale/${DEFAULT_LANGUAGE}`)
26
+ dayjs.locale(DEFAULT_LANGUAGE)
27
+ }
28
+ }
@@ -0,0 +1,7 @@
1
+ import dayjs from 'dayjs'
2
+
3
+ export const formatFullDate = (timestamp?: dayjs.ConfigType) => {
4
+ const time = dayjs(timestamp)
5
+
6
+ return time.format('L')
7
+ }
@@ -0,0 +1,20 @@
1
+ import dayjs from 'dayjs'
2
+
3
+ import { t } from '@/src/config/i18n'
4
+
5
+ export const formatTime = (timestamp: dayjs.ConfigType, ignoreTime = false) => {
6
+ const time = dayjs(timestamp)
7
+
8
+ if (!time.isValid()) {
9
+ return 'Invalid date'
10
+ }
11
+
12
+ const daysDiff = dayjs().diff(time, 'day')
13
+
14
+ return time.calendar(null, {
15
+ sameDay: ignoreTime ? `[${t('general.today')}]` : 'LT',
16
+ lastDay: `[${t('general.yesterday')}]`,
17
+ lastWeek: daysDiff <= 7 ? 'dddd' : 'L',
18
+ sameElse: 'L'
19
+ })
20
+ }
@@ -0,0 +1,2 @@
1
+ export * from './format-fulldate'
2
+ export * from './format-time'
@@ -8,7 +8,7 @@ import optimizelyClient from './optimizely'
8
8
 
9
9
  function OptimizelyProvider({
10
10
  settings,
11
- children,
11
+ children
12
12
  }: PropsWithChildren<{ settings: WidgetSettingProps }>) {
13
13
  const userInfo = {
14
14
  id: settings.user?.ucode ?? '',
@@ -17,8 +17,8 @@ function OptimizelyProvider({
17
17
  appSystem: APP_SYSTEM,
18
18
  appDebug: !productionMode,
19
19
  locale: settings.locale ?? '',
20
- slug: settings.membershipSlug ?? '',
21
- },
20
+ slug: settings.membershipSlug ?? ''
21
+ }
22
22
  }
23
23
 
24
24
  return (
@@ -3,7 +3,7 @@ import { createInstance } from '@optimizely/react-sdk'
3
3
  const optimizelyClient = createInstance({
4
4
  sdkKey: process.env.OPTIMIZELY_SDK_KEY,
5
5
  eventBatchSize: 10,
6
- eventFlushInterval: 1000,
6
+ eventFlushInterval: 1000
7
7
  })
8
8
 
9
9
  export default optimizelyClient
@@ -76,10 +76,29 @@
76
76
  --ai-color-primary: #a095ec;
77
77
  --ai-color-secondary: #6ba1f0;
78
78
  --ai-color-dark: #111925;
79
+ --ai-color-chat-response: #26202f;
80
+
81
+ /* Size */
82
+ --hc-size-spacing-2: 0.5rem;
83
+ --hc-size-border-medium: 0.5rem;
79
84
  }
80
85
 
81
86
  #hotmart-app-tutor-ai-consumer-root {
82
- composes: scrollbar from './shared-styles.module.css';
87
+ & *::-webkit-scrollbar {
88
+ width: var(--hc-size-spacing-2);
89
+ height: var(--hc-size-spacing-2);
90
+ }
91
+
92
+ & *::-webkit-scrollbar-track {
93
+ background: transparent;
94
+ }
95
+
96
+ & *::-webkit-scrollbar-thumb {
97
+ background: var(--hc-color-neutral-800);
98
+ border-radius: var(--hc-size-border-medium);
99
+ border: calc(var(--hc-size-border-medium) / 2) solid transparent;
100
+ }
101
+
83
102
  font-family:
84
103
  'Nunito Sans',
85
104
  -apple-system,
@@ -0,0 +1,11 @@
1
+ .gradientBg {
2
+ background:
3
+ linear-gradient(
4
+ 186.9deg,
5
+ rgb(from var(--ai-color-primary) r g b / 0.1) 6.65%,
6
+ rgb(from var(--ai-color-secondary) r g b / 0.1) 28.99%,
7
+ rgb(from var(--ai-color-dark) r g b / 0.1) 46.97%,
8
+ rgb(from var(--hc-color-neutral-900) r g b / 0.8) 57.9%
9
+ ),
10
+ linear-gradient(0deg, var(--hc-color-neutral-900), var(--hc-color-neutral-900));
11
+ }
@@ -0,0 +1,6 @@
1
+ .gradientText {
2
+ background: linear-gradient(226.83deg, #7ab0ff 21.46%, #b48eff 95.76%);
3
+ background-clip: text;
4
+ -webkit-background-clip: text;
5
+ -webkit-text-fill-color: transparent;
6
+ }
@@ -10,7 +10,7 @@ function QueryProvider({ children, showDevTools = true }: QueryProviderProps) {
10
10
  return (
11
11
  <PersistQueryClientProvider client={queryClient} persistOptions={{ persister }}>
12
12
  {children}
13
- {showDevTools && <ReactQueryDevtools />}
13
+ {showDevTools && <ReactQueryDevtools buttonPosition='top-right' />}
14
14
  </PersistQueryClientProvider>
15
15
  )
16
16
  }
@@ -1,7 +1,16 @@
1
1
  import { http, HttpResponse } from 'msw'
2
2
 
3
+ import { ProfileEndpoints } from '@/src/modules/profile'
4
+ import ProfileAPIPropsBuilder from '@/src/modules/profile/__tests__/profile-api-props.builder'
5
+
3
6
  export const handlers = [
4
7
  http.all('https://tracking-api.buildstaging.com/rest/track/event/json/sync', () => {
5
8
  return HttpResponse.json({ ok: true })
9
+ }),
10
+ http.all('https://c3po-api-auth.buildstaging.com/v1/auth/sparkie', () => {
11
+ return HttpResponse.json({ ok: true })
12
+ }),
13
+ http.all(ProfileEndpoints.getProfile(), () => {
14
+ return HttpResponse.json(new ProfileAPIPropsBuilder())
6
15
  })
7
16
  ]
package/src/index.tsx CHANGED
@@ -1,7 +1,9 @@
1
1
  import { StrictMode } from 'react'
2
2
  import { createRoot } from 'react-dom/client'
3
3
 
4
+ import { initDayjs } from './config/dayjs'
4
5
  import { initLanguage } from './config/i18n'
6
+ import { initAxios } from './config/request/api'
5
7
  import { Main } from './main'
6
8
  import { TutorWidgetEvents, TutorWidgetEventTypes } from './modules/widget'
7
9
  import type { StartTutorWidgetProps } from './types'
@@ -13,7 +15,9 @@ window.startTutorWidget = async ({
13
15
  const rootElement = document.getElementById(elementId) as HTMLElement
14
16
  const root = createRoot(rootElement)
15
17
 
18
+ initAxios(settings.hotmartToken)
16
19
  await initLanguage(settings.locale)
20
+ await initDayjs(settings.locale)
17
21
 
18
22
  if (root)
19
23
  root.render(
@@ -0,0 +1,86 @@
1
+ import clsx from 'clsx'
2
+ import type { HTMLAttributes, PropsWithChildren } from 'react'
3
+
4
+ export type ButtonProps = PropsWithChildren<
5
+ HTMLAttributes<HTMLButtonElement> & {
6
+ variant?: 'brand' | 'secondary' | 'primary' | 'tertiary' | 'gradient-outline'
7
+ }
8
+ >
9
+
10
+ function Button({ children, className, variant = 'brand', ...props }: ButtonProps) {
11
+ const defaultClasses =
12
+ 'rounded focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-500 text-base font-medium border border-transparent'
13
+ const defaultPadding = 'px-4 py-2'
14
+
15
+ switch (variant) {
16
+ case 'gradient-outline':
17
+ return (
18
+ <button
19
+ className={clsx(
20
+ defaultClasses,
21
+ 'group relative inline-flex items-center justify-center overflow-hidden bg-gradient-to-br from-purple-600 to-blue-500 p-0.5 hover:text-neutral-0 group-hover:from-purple-600 group-hover:to-blue-500',
22
+ className
23
+ )}
24
+ {...props}>
25
+ <span className='relative flex-1 rounded-md bg-neutral-900 px-5 py-2.5 text-neutral-0 transition-all duration-75 ease-in group-hover:bg-transparent'>
26
+ {children}
27
+ </span>
28
+ </button>
29
+ )
30
+ case 'primary':
31
+ return (
32
+ <button
33
+ className={clsx(
34
+ defaultPadding,
35
+ defaultClasses,
36
+ 'bg-primary-500 text-neutral-0 hover:bg-primary-600 focus:outline-none',
37
+ className
38
+ )}
39
+ {...props}>
40
+ {children}
41
+ </button>
42
+ )
43
+ case 'secondary':
44
+ return (
45
+ <button
46
+ className={clsx(
47
+ defaultPadding,
48
+ defaultClasses,
49
+ 'border-neutral-100 bg-transparent text-neutral-100 hover:bg-neutral-100 hover:text-neutral-900',
50
+ className
51
+ )}
52
+ {...props}>
53
+ {children}
54
+ </button>
55
+ )
56
+ case 'tertiary':
57
+ return (
58
+ <button
59
+ className={clsx(
60
+ defaultPadding,
61
+ defaultClasses,
62
+ 'bg-transparent text-neutral-0 hover:bg-neutral-900',
63
+ className
64
+ )}
65
+ {...props}>
66
+ {children}
67
+ </button>
68
+ )
69
+ case 'brand':
70
+ default:
71
+ return (
72
+ <button
73
+ className={clsx(
74
+ defaultPadding,
75
+ defaultClasses,
76
+ 'rounded bg-neutral-100 text-neutral-900 outline-none hover:bg-neutral-200',
77
+ className
78
+ )}
79
+ {...props}>
80
+ {children}
81
+ </button>
82
+ )
83
+ }
84
+ }
85
+
86
+ export default Button
@@ -0,0 +1 @@
1
+ export { default as Button } from './button'
@@ -1,3 +1,5 @@
1
+ export * from './button'
1
2
  export * from './errors'
2
3
  export * from './icons'
4
+ export * from './markdownrenderer'
3
5
  export * from './spinner'
@@ -0,0 +1,334 @@
1
+ export const TEST_MARKDOWN_STUB = `# Main Heading - Testing Markdown Renderer
2
+
3
+ This is a comprehensive test for our markdown renderer component. Let's see how it handles various elements.
4
+
5
+ ## Subheading Level 2
6
+
7
+ ### Subheading Level 3
8
+
9
+ #### Subheading Level 4
10
+
11
+ ---
12
+
13
+ ## Text Formatting
14
+
15
+ Here's some **bold text** and *italic text*. You can also combine them for ***bold and italic***.
16
+
17
+ We also support ~~strikethrough text~~ and \`inline code snippets\`.
18
+
19
+ > This is a blockquote. It should stand out from regular text.
20
+ >
21
+ > Blockquotes can span multiple lines and even contain other markdown elements like **bold text**.
22
+
23
+ ---
24
+
25
+ ## Links
26
+
27
+ - [External Link to React Documentation](https://reactjs.org/docs/getting-started.html)
28
+ - [Internal Link](#code-examples)
29
+ - [Email Link](mailto:test@example.com)
30
+ - Auto-link: https://github.com
31
+
32
+ ---
33
+
34
+ ## Images
35
+
36
+ ### Regular Image
37
+ ![React Logo](https://upload.wikimedia.org/wikipedia/commons/thumb/a/a7/React-icon.svg/512px-React-icon.svg.png)
38
+
39
+ ### Image with Alt Text
40
+ ![TypeScript Logo - A blue square with white TS text](https://upload.wikimedia.org/wikipedia/commons/thumb/4/4c/Typescript_logo_2020.svg/512px-Typescript_logo_2020.svg.png)
41
+
42
+ ### Broken Image (for error handling)
43
+ ![This image doesn't exist](https://invalid-url.com/image.png)
44
+
45
+ ---
46
+
47
+ ## Lists
48
+
49
+ ### Unordered List
50
+ - First item
51
+ - Second item with **bold text**
52
+ - Third item with [a link](https://example.com)
53
+ - Nested item 1
54
+ - Nested item 2
55
+ - Double nested item
56
+ - Fourth item with \`inline code\`
57
+
58
+ ### Ordered List
59
+ 1. First numbered item
60
+ 2. Second numbered item
61
+ 3. Third item with multiple lines
62
+
63
+ This paragraph is part of item 3.
64
+
65
+ 4. Fourth item
66
+ 1. Nested numbered item
67
+ 2. Another nested item
68
+
69
+ ### Task List (GitHub Flavored Markdown)
70
+ - [x] Completed task
71
+ - [ ] Incomplete task
72
+ - [x] Another completed task
73
+ - [ ] Task with **bold text**
74
+
75
+ ---
76
+
77
+ ## Code Examples
78
+
79
+ ### Inline Code
80
+ Use the \`useState\` hook for state management in React.
81
+
82
+ ### JavaScript Code Block
83
+ \`\`\`javascript
84
+ function greetUser(name) {
85
+ if (!name) {
86
+ return "Hello, World!";
87
+ }
88
+ return \`Hello, \${name}!\`;
89
+ }
90
+
91
+ const user = "John Doe";
92
+ console.log(greetUser(user));
93
+ \`\`\`
94
+
95
+ ### TypeScript React Component
96
+ \`\`\`tsx
97
+ import React, { useState, useEffect } from 'react';
98
+
99
+ interface UserProps {
100
+ id: number;
101
+ name: string;
102
+ email?: string;
103
+ }
104
+
105
+ const UserComponent: React.FC<UserProps> = ({ id, name, email }) => {
106
+ const [isLoading, setIsLoading] = useState<boolean>(false);
107
+
108
+ useEffect(() => {
109
+ console.log(\`User \${name} loaded\`);
110
+ }, [name]);
111
+
112
+ return (
113
+ <div className="user-card">
114
+ <h2>{name}</h2>
115
+ {email && <p>Email: {email}</p>}
116
+ <button onClick={() => setIsLoading(!isLoading)}>
117
+ {isLoading ? 'Loading...' : 'Load Data'}
118
+ </button>
119
+ </div>
120
+ );
121
+ };
122
+
123
+ export default UserComponent;
124
+ \`\`\`
125
+
126
+ ### CSS Code Block
127
+ \`\`\`css
128
+ .markdown-container {
129
+ max-width: 800px;
130
+ margin: 0 auto;
131
+ padding: 2rem;
132
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
133
+ }
134
+
135
+ .code-block {
136
+ background-color: #f6f8fa;
137
+ border-radius: 6px;
138
+ padding: 16px;
139
+ overflow-x: auto;
140
+ }
141
+
142
+ @media (max-width: 768px) {
143
+ .markdown-container {
144
+ padding: 1rem;
145
+ }
146
+ }
147
+ \`\`\`
148
+
149
+ ### JSON Data
150
+ \`\`\`json
151
+ {
152
+ "name": "my-react-app",
153
+ "version": "1.0.0",
154
+ "dependencies": {
155
+ "react": "^18.2.0",
156
+ "react-dom": "^18.2.0",
157
+ "react-markdown": "^8.0.7"
158
+ },
159
+ "scripts": {
160
+ "dev": "vite",
161
+ "build": "vite build",
162
+ "test": "vitest"
163
+ }
164
+ }
165
+ \`\`\`
166
+
167
+ ### Bash Commands
168
+ \`\`\`bash
169
+ # Install dependencies
170
+ npm install react-markdown
171
+
172
+ # Start development server
173
+ npm run dev
174
+
175
+ # Build for production
176
+ npm run build
177
+
178
+ # Run tests
179
+ npm test
180
+ \`\`\`
181
+
182
+ ### Python Code
183
+ \`\`\`python
184
+ def fibonacci(n):
185
+ """Generate Fibonacci sequence up to n terms."""
186
+ if n <= 0:
187
+ return []
188
+ elif n == 1:
189
+ return [0]
190
+ elif n == 2:
191
+ return [0, 1]
192
+
193
+ sequence = [0, 1]
194
+ for i in range(2, n):
195
+ sequence.append(sequence[i-1] + sequence[i-2])
196
+
197
+ return sequence
198
+
199
+ # Example usage
200
+ print(fibonacci(10))
201
+ \`\`\`
202
+
203
+ ---
204
+
205
+ ## Tables
206
+
207
+ ### Simple Table
208
+ | Name | Age | City |
209
+ |------|-----|------|
210
+ | John | 25 | New York |
211
+ | Jane | 30 | Los Angeles |
212
+ | Bob | 35 | Chicago |
213
+
214
+ ### Complex Table with Alignment
215
+ | Feature | React | Vue | Angular |
216
+ |:--------|:-----:|:---:|--------:|
217
+ | Learning Curve | Easy | Easy | Steep |
218
+ | Performance | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐⭐ |
219
+ | Community | Large | Medium | Large |
220
+ | Bundle Size | Small | Small | Large |
221
+
222
+ ### Table with Code and Links
223
+ | Language | Extension | Documentation |
224
+ |----------|-----------|---------------|
225
+ | JavaScript | \`.js\` | [MDN Docs](https://developer.mozilla.org/en-US/docs/Web/JavaScript) |
226
+ | TypeScript | \`.ts\` | [TS Docs](https://www.typescriptlang.org/docs/) |
227
+ | Python | \`.py\` | [Python Docs](https://docs.python.org/3/) |
228
+
229
+ ---
230
+
231
+ ## Special Characters and Escaping
232
+
233
+ Here are some special characters: & < > " '
234
+
235
+ HTML entities: &amp; &lt; &gt; &quot; &#39;
236
+
237
+ Markdown escaping: \\* \\_ \\# \\[ \\] \\( \\)
238
+
239
+ ---
240
+
241
+ ## Horizontal Rules
242
+
243
+ Above this line is a horizontal rule.
244
+
245
+ ---
246
+
247
+ Below this line is another horizontal rule.
248
+
249
+ ***
250
+
251
+ And here's one more made with asterisks.
252
+
253
+ ---
254
+
255
+ ## Mathematical Expressions (if supported)
256
+
257
+ Inline math: $E = mc^2$
258
+
259
+ Block math:
260
+ $$
261
+ \\int_{-\\infty}^{\\infty} e^{-x^2} dx = \\sqrt{\\pi}
262
+ $$
263
+
264
+ ---
265
+
266
+ ## HTML Elements (if raw HTML is enabled)
267
+
268
+ <div style="background-color: #f0f0f0; padding: 10px; border-radius: 5px;">
269
+ This is raw HTML content within markdown.
270
+ <br>
271
+ <strong>Bold text via HTML</strong>
272
+ <br>
273
+ <em>Italic text via HTML</em>
274
+ </div>
275
+
276
+ <details>
277
+ <summary>Click to expand</summary>
278
+
279
+ This content is hidden by default and can be toggled.
280
+
281
+ - Item 1
282
+ - Item 2
283
+ - Item 3
284
+
285
+ </details>
286
+
287
+ ---
288
+
289
+ ## Edge Cases
290
+
291
+ ### Empty Code Block
292
+ \`\`\`
293
+
294
+ \`\`\`
295
+
296
+ ### Code Block Without Language
297
+ \`\`\`
298
+ function noLanguageSpecified() {
299
+ return "This should still render";
300
+ }
301
+ \`\`\`
302
+
303
+ ### Very Long Line
304
+ This is a very long line that should test how the renderer handles text wrapping and overflow in different containers and screen sizes to ensure proper responsive behavior.
305
+
306
+ ### Unicode and Emojis
307
+ Unicode characters: café, naïve, résumé
308
+ Emojis: 🚀 ⚡ 💻 🎉 ✅ ❌ 🔥 💡
309
+
310
+ ---
311
+
312
+ ## Conclusion
313
+
314
+ This markdown document tests:
315
+
316
+ ✅ **Headings** (H1-H6)
317
+ ✅ **Text formatting** (bold, italic, strikethrough)
318
+ ✅ **Links** (external, internal, email, auto-links)
319
+ ✅ **Images** (valid and invalid URLs)
320
+ ✅ **Lists** (ordered, unordered, nested, task lists)
321
+ ✅ **Code blocks** (multiple languages, inline code)
322
+ ✅ **Tables** (simple and complex)
323
+ ✅ **Blockquotes**
324
+ ✅ **Horizontal rules**
325
+ ✅ **Special characters and escaping**
326
+ ✅ **HTML elements** (if enabled)
327
+ ✅ **Edge cases**
328
+
329
+ If all these elements render correctly, your markdown component is working properly! 🎉
330
+
331
+ ---
332
+
333
+ *Last updated: ${new Date().toLocaleDateString()}*
334
+ `
@@ -0,0 +1 @@
1
+ export * from './md-code-block'
@@ -0,0 +1 @@
1
+ export { default } from './md-code-block'