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.
- package/CHANGELOG.md +23 -0
- package/config/vitest/__mocks__/sparkie.tsx +12 -0
- package/config/vitest/setupTests.ts +6 -1
- package/config/vitest/vitest.config.mts +1 -0
- package/environments/.env.test +2 -2
- package/package.json +12 -2
- package/public/assets/images/default-image.png +0 -0
- package/public/assets/svg/tutor-onboarding.svg +128 -0
- package/src/@types/declarations.d.ts +16 -6
- package/src/config/dayjs/index.ts +2 -0
- package/src/config/dayjs/init.ts +28 -0
- package/src/config/dayjs/utils/format-fulldate.ts +7 -0
- package/src/config/dayjs/utils/format-time.ts +20 -0
- package/src/config/dayjs/utils/index.ts +2 -0
- package/src/config/optimizely/optimizely-provider.tsx +3 -3
- package/src/config/optimizely/optimizely.ts +1 -1
- package/src/config/styles/global.css +20 -1
- package/src/config/styles/utilities/bg-utilities.module.css +11 -0
- package/src/config/styles/utilities/text-utilities.module.css +6 -0
- package/src/config/tanstack/query-provider.tsx +1 -1
- package/src/config/tests/handlers.ts +9 -0
- package/src/index.tsx +4 -0
- package/src/lib/components/button/button.tsx +86 -0
- package/src/lib/components/button/index.ts +1 -0
- package/src/lib/components/index.ts +2 -0
- package/src/lib/components/markdownrenderer/__tests__/markdown.stub.ts +334 -0
- package/src/lib/components/markdownrenderer/components/index.ts +1 -0
- package/src/lib/components/markdownrenderer/components/md-code-block/index.ts +1 -0
- package/src/lib/components/markdownrenderer/components/md-code-block/md-code-block.tsx +71 -0
- package/src/lib/components/markdownrenderer/index.ts +2 -0
- package/src/lib/components/markdownrenderer/markdownrenderer.tsx +115 -0
- package/src/lib/hooks/index.ts +1 -0
- package/src/lib/hooks/use-ref-event-listener/index.ts +2 -0
- package/src/lib/hooks/use-ref-event-listener/use-ref-event-listener.tsx +32 -0
- package/src/lib/utils/constants.ts +1 -1
- package/src/lib/utils/copy-text-to-clipboard.tsx +13 -0
- package/src/lib/utils/extract-text-from-react-nodes.ts +23 -0
- package/src/lib/utils/index.ts +3 -0
- package/src/lib/utils/urls.ts +20 -0
- package/src/main/main.spec.tsx +17 -7
- package/src/main/main.tsx +2 -13
- package/src/modules/messages/__tests__/imessage-with-sender-data.builder.ts +113 -0
- package/src/modules/messages/__tests__/imessage-with-sender-data.mock.ts +15 -0
- package/src/modules/messages/components/chat-input/chat-input.atom.ts +12 -0
- package/src/modules/{create-message → messages}/components/chat-input/chat-input.tsx +6 -2
- package/src/modules/{create-message → messages}/components/chat-input/index.ts +1 -0
- package/src/modules/{create-message → messages}/components/chat-input/types.ts +1 -0
- package/src/modules/messages/components/index.ts +4 -0
- package/src/modules/messages/components/message-img/index.ts +1 -0
- package/src/modules/messages/components/message-img/message-img.tsx +47 -0
- package/src/modules/messages/components/message-item/index.ts +2 -0
- package/src/modules/messages/components/message-item/message-item.spec.tsx +26 -0
- package/src/modules/messages/components/message-item/message-item.tsx +15 -0
- package/src/modules/messages/components/messages-list/index.ts +2 -0
- package/src/modules/messages/components/messages-list/messages-list.tsx +53 -0
- package/src/modules/messages/constants.ts +1 -0
- package/src/modules/messages/hooks/index.ts +1 -0
- package/src/modules/messages/hooks/use-fetch-messages/index.ts +2 -0
- package/src/modules/messages/hooks/use-fetch-messages/use-fetch-messages.spec.tsx +46 -0
- package/src/modules/messages/hooks/use-fetch-messages/use-fetch-messages.tsx +103 -0
- package/src/modules/messages/index.ts +3 -0
- package/src/modules/messages/service.ts +86 -0
- package/src/modules/messages/types.ts +78 -0
- package/src/modules/messages/utils/index.ts +1 -0
- package/src/modules/messages/utils/messages-parser/index.ts +1 -0
- package/src/modules/messages/utils/messages-parser/utils.ts +28 -0
- package/src/modules/profile/__tests__/profile-api-props.builder.ts +74 -0
- package/src/modules/profile/__tests__/profile-props.builder.ts +42 -0
- package/src/modules/profile/constants.ts +3 -0
- package/src/modules/profile/hooks/index.ts +1 -0
- package/src/modules/profile/hooks/use-get-profile/index.ts +1 -0
- package/src/modules/profile/hooks/use-get-profile/use-get-profile.spec.tsx +20 -0
- package/src/modules/profile/hooks/use-get-profile/use-get-profile.tsx +14 -0
- package/src/modules/profile/index.ts +4 -0
- package/src/modules/profile/service.tsx +19 -0
- package/src/modules/profile/types.ts +17 -0
- package/src/modules/sparkie/constants.ts +21 -0
- package/src/modules/sparkie/index.ts +3 -0
- package/src/modules/sparkie/service.ts +94 -0
- package/src/modules/sparkie/types.ts +47 -0
- package/src/modules/sparkie/utils/validate-firebase-config.spec.ts +17 -0
- package/src/modules/sparkie/utils/validate-firebase-config.ts +12 -0
- package/src/modules/widget/__tests__/widget-settings-props.builder.ts +121 -0
- package/src/modules/widget/components/chat-page/chat-page.tsx +20 -0
- package/src/modules/widget/components/chat-page/index.ts +2 -0
- package/src/modules/widget/components/constants.tsx +9 -0
- package/src/modules/widget/components/container/container.tsx +32 -0
- package/src/modules/widget/components/container/index.ts +2 -0
- package/src/modules/widget/components/container/types.ts +3 -0
- package/src/modules/widget/components/greetings-card/greetings-card.tsx +1 -1
- package/src/modules/widget/components/greetings-card/styles.module.css +1 -3
- package/src/modules/widget/components/index.ts +3 -0
- package/src/modules/widget/components/onboarding-page/index.ts +1 -0
- package/src/modules/widget/components/onboarding-page/onboarding-page.tsx +40 -0
- package/src/modules/widget/components/onboarding-page/styles.module.css +7 -0
- package/src/modules/widget/components/starter-page/index.ts +1 -0
- package/src/modules/widget/components/starter-page/starter-page.tsx +41 -0
- package/src/modules/widget/hooks/index.ts +1 -0
- package/src/modules/widget/hooks/use-init-sparkie/index.ts +1 -0
- package/src/modules/widget/hooks/use-init-sparkie/use-init-sparkie.tsx +18 -0
- package/src/modules/widget/store/index.ts +1 -0
- package/src/modules/widget/store/widget-settings.atom.ts +3 -1
- package/src/modules/widget/store/widget-tabs.atom.ts +53 -0
- package/tailwind.config.js +95 -1
- package/config/vitest/index.ts +0 -1
- package/src/config/styles/shared-styles.module.css +0 -16
- package/src/main/styles.module.css +0 -15
- 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
|
-
|
|
10
|
+
declare module '*.png' {
|
|
11
|
+
const content: string
|
|
12
|
+
export default content
|
|
13
|
+
}
|
|
13
14
|
|
|
14
|
-
declare module '*.
|
|
15
|
+
declare module '*.jpg' {
|
|
16
|
+
const content: string
|
|
17
|
+
export default content
|
|
18
|
+
}
|
|
15
19
|
|
|
16
|
-
declare module '
|
|
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,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,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
|
+
}
|
|
@@ -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 (
|
|
@@ -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
|
-
|
|
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
|
+
}
|
|
@@ -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'
|
|
@@ -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
|
+

|
|
38
|
+
|
|
39
|
+
### Image with Alt Text
|
|
40
|
+

|
|
41
|
+
|
|
42
|
+
### Broken Image (for error handling)
|
|
43
|
+

|
|
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: & < > " '
|
|
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'
|