docs: changes to AI assistant icon + add button to code blocks (#11227)
* docs: changes to AI assistant icon + add button to code blocks * fix code blocks in questions * collapse code block in answer * styling fixes * fix new line at cursor position
This commit is contained in:
1 parent
386ce4b857
commit
be33a56290
21 files changed
+294
-97
No files matched your search
@@ -55,6 +55,10 @@
|
|||||||
body[data-modal="opened"] {
|
body[data-modal="opened"] {
|
||||||
@apply !overflow-hidden;
|
@apply !overflow-hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.text-wrap {
|
||||||
|
text-wrap: wrap;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.grecaptcha-badge {
|
.grecaptcha-badge {
|
||||||
|
|||||||
@@ -32,6 +32,10 @@
|
|||||||
body[data-modal="opened"] {
|
body[data-modal="opened"] {
|
||||||
@apply !overflow-hidden;
|
@apply !overflow-hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.text-wrap {
|
||||||
|
text-wrap: wrap;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.grecaptcha-badge {
|
.grecaptcha-badge {
|
||||||
|
|||||||
@@ -35,6 +35,10 @@
|
|||||||
body[data-modal="opened"] {
|
body[data-modal="opened"] {
|
||||||
@apply !overflow-hidden;
|
@apply !overflow-hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.text-wrap {
|
||||||
|
text-wrap: wrap;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.grecaptcha-badge {
|
.grecaptcha-badge {
|
||||||
|
|||||||
@@ -40,6 +40,10 @@
|
|||||||
body[data-modal="opened"] {
|
body[data-modal="opened"] {
|
||||||
@apply !overflow-hidden text-ui-fg-base;
|
@apply !overflow-hidden text-ui-fg-base;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.text-wrap {
|
||||||
|
text-wrap: wrap;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.grecaptcha-badge {
|
.grecaptcha-badge {
|
||||||
|
|||||||
@@ -28,6 +28,10 @@
|
|||||||
body[data-modal="opened"] {
|
body[data-modal="opened"] {
|
||||||
@apply !overflow-hidden;
|
@apply !overflow-hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.text-wrap {
|
||||||
|
text-wrap: wrap;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.grecaptcha-badge {
|
.grecaptcha-badge {
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ export const AiAssistantChatWindowFooter = () => {
|
|||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<span className="text-medusa-fg-muted">Chat is cleared on refresh</span>
|
<span className="text-medusa-fg-muted">Chat is cleared on refresh</span>
|
||||||
<span className="h-full w-px bg-medusa-border-base"></span>
|
<span className="h-docs_0.75 w-px bg-medusa-border-base"></span>
|
||||||
<div className="flex items-center gap-docs_0.5">
|
<div className="flex items-center gap-docs_0.5">
|
||||||
<span className="text-medusa-fg-subtle">Line break</span>
|
<span className="text-medusa-fg-subtle">Line break</span>
|
||||||
<div className="flex items-center gap-[5px]">
|
<div className="flex items-center gap-[5px]">
|
||||||
|
|||||||
@@ -23,7 +23,9 @@ export const AiAssistantChatWindowHeader = () => {
|
|||||||
<Tooltip
|
<Tooltip
|
||||||
tooltipChildren={
|
tooltipChildren={
|
||||||
<>
|
<>
|
||||||
This site is protected by reCAPTCHA and the{" "}
|
This site is protected by reCAPTCHA and
|
||||||
|
<br />
|
||||||
|
the{" "}
|
||||||
<Link href="https://policies.google.com/privacy">
|
<Link href="https://policies.google.com/privacy">
|
||||||
Google Privacy Policy
|
Google Privacy Policy
|
||||||
</Link>{" "}
|
</Link>{" "}
|
||||||
@@ -32,6 +34,7 @@ export const AiAssistantChatWindowHeader = () => {
|
|||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
clickable={true}
|
clickable={true}
|
||||||
|
tooltipClassName={"!text-compact-small-plus"}
|
||||||
>
|
>
|
||||||
<ShieldCheck className="text-medusa-fg-muted" />
|
<ShieldCheck className="text-medusa-fg-muted" />
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import React, { useEffect, useRef } from "react"
|
|||||||
import clsx from "clsx"
|
import clsx from "clsx"
|
||||||
import { useAiAssistantChat } from "../../../../providers/AiAssistant/Chat"
|
import { useAiAssistantChat } from "../../../../providers/AiAssistant/Chat"
|
||||||
import { ArrowUpCircleSolid } from "@medusajs/icons"
|
import { ArrowUpCircleSolid } from "@medusajs/icons"
|
||||||
|
import { useAiAssistant } from "../../../../providers"
|
||||||
|
|
||||||
export const AiAssistantChatWindowInput = () => {
|
export const AiAssistantChatWindowInput = () => {
|
||||||
const {
|
const {
|
||||||
@@ -12,6 +13,7 @@ export const AiAssistantChatWindowInput = () => {
|
|||||||
loading,
|
loading,
|
||||||
getThreadItems,
|
getThreadItems,
|
||||||
} = useAiAssistantChat()
|
} = useAiAssistantChat()
|
||||||
|
const { chatOpened } = useAiAssistant()
|
||||||
const formRef = useRef<HTMLFormElement | null>(null)
|
const formRef = useRef<HTMLFormElement | null>(null)
|
||||||
|
|
||||||
const onSubmit = (e?: React.FormEvent<HTMLFormElement>) => {
|
const onSubmit = (e?: React.FormEvent<HTMLFormElement>) => {
|
||||||
@@ -33,7 +35,17 @@ export const AiAssistantChatWindowInput = () => {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (e.shiftKey) {
|
if (e.shiftKey) {
|
||||||
setQuestion((prev) => `${prev}\n`)
|
const { selectionStart, selectionEnd } = e.currentTarget
|
||||||
|
setQuestion(
|
||||||
|
(prev) =>
|
||||||
|
`${prev.substring(0, selectionStart)}\n${prev.substring(selectionEnd)}`
|
||||||
|
)
|
||||||
|
setTimeout(() => {
|
||||||
|
if (inputRef.current) {
|
||||||
|
inputRef.current.selectionStart = inputRef.current.selectionEnd =
|
||||||
|
selectionStart + 1
|
||||||
|
}
|
||||||
|
}, 0)
|
||||||
} else {
|
} else {
|
||||||
onSubmit()
|
onSubmit()
|
||||||
}
|
}
|
||||||
@@ -52,6 +64,7 @@ export const AiAssistantChatWindowInput = () => {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
adjustTextareaHeight()
|
adjustTextareaHeight()
|
||||||
|
inputRef.current?.focus()
|
||||||
}, [question])
|
}, [question])
|
||||||
|
|
||||||
const handleTouch = (e: React.TouchEvent<HTMLTextAreaElement>) => {
|
const handleTouch = (e: React.TouchEvent<HTMLTextAreaElement>) => {
|
||||||
@@ -61,6 +74,19 @@ export const AiAssistantChatWindowInput = () => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!chatOpened || !inputRef.current) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const isCursorAtEnd =
|
||||||
|
inputRef.current.selectionStart === inputRef.current.value.length
|
||||||
|
|
||||||
|
if (isCursorAtEnd) {
|
||||||
|
inputRef.current.scrollTop = inputRef.current.scrollHeight
|
||||||
|
}
|
||||||
|
}, [chatOpened, inputRef.current])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={clsx(
|
className={clsx(
|
||||||
|
|||||||
@@ -1,6 +1,13 @@
|
|||||||
import clsx from "clsx"
|
import clsx from "clsx"
|
||||||
import React from "react"
|
import React from "react"
|
||||||
import { AiAssistantIcon, DotsLoading, MarkdownContent } from "@/components"
|
import {
|
||||||
|
AiAssistantIcon,
|
||||||
|
CodeMdx,
|
||||||
|
CodeMdxProps,
|
||||||
|
DotsLoading,
|
||||||
|
MarkdownContent,
|
||||||
|
MDXComponents,
|
||||||
|
} from "@/components"
|
||||||
import { AiAssistantThreadItemActions } from "./Actions"
|
import { AiAssistantThreadItemActions } from "./Actions"
|
||||||
import { AiAssistantThread } from "../../../providers/AiAssistant/Chat"
|
import { AiAssistantThread } from "../../../providers/AiAssistant/Chat"
|
||||||
|
|
||||||
@@ -27,7 +34,7 @@ export const AiAssistantThreadItem = ({ item }: AiAssistantThreadItemProps) => {
|
|||||||
"txt-small text-medusa-fg-base",
|
"txt-small text-medusa-fg-base",
|
||||||
item.type === "question" && [
|
item.type === "question" && [
|
||||||
"rounded-docs_xl bg-medusa-tag-neutral-bg",
|
"rounded-docs_xl bg-medusa-tag-neutral-bg",
|
||||||
"px-docs_0.75 py-docs_0.5",
|
"px-docs_0.75 py-docs_0.5 max-w-full",
|
||||||
],
|
],
|
||||||
item.type !== "question" && "flex-1",
|
item.type !== "question" && "flex-1",
|
||||||
item.type === "answer" && "text-pretty flex-1 max-w-[calc(100%-20px)]"
|
item.type === "answer" && "text-pretty flex-1 max-w-[calc(100%-20px)]"
|
||||||
@@ -38,6 +45,39 @@ export const AiAssistantThreadItem = ({ item }: AiAssistantThreadItemProps) => {
|
|||||||
className="[&>*:last-child]:mb-0"
|
className="[&>*:last-child]:mb-0"
|
||||||
allowedElements={["br", "p", "code", "pre"]}
|
allowedElements={["br", "p", "code", "pre"]}
|
||||||
unwrapDisallowed={true}
|
unwrapDisallowed={true}
|
||||||
|
components={{
|
||||||
|
...MDXComponents,
|
||||||
|
code: (props: CodeMdxProps) => {
|
||||||
|
return (
|
||||||
|
<CodeMdx
|
||||||
|
{...props}
|
||||||
|
noCopy
|
||||||
|
noReport
|
||||||
|
forceNoTitle
|
||||||
|
noAskAi
|
||||||
|
inlineCodeProps={{
|
||||||
|
...props.inlineCodeProps,
|
||||||
|
className: "!text-wrap !break-words",
|
||||||
|
variant: "grey-bg",
|
||||||
|
}}
|
||||||
|
collapsibleLines="11"
|
||||||
|
codeBlockProps={{
|
||||||
|
className: clsx(
|
||||||
|
"rounded-docs_lg p-[5px]",
|
||||||
|
props.className
|
||||||
|
),
|
||||||
|
wrapperClassName: "rounded-docs_lg",
|
||||||
|
innerClassName: "border rounded-docs_lg",
|
||||||
|
overrideColors: {
|
||||||
|
bg: "bg-medusa-contrast-bg-subtle",
|
||||||
|
innerBg: "bg-medusa-contrast-bg-subtle",
|
||||||
|
innerBorder: "border-medusa-contrast-border-bot",
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
{item.content}
|
{item.content}
|
||||||
</MarkdownContent>
|
</MarkdownContent>
|
||||||
@@ -45,7 +85,15 @@ export const AiAssistantThreadItem = ({ item }: AiAssistantThreadItemProps) => {
|
|||||||
{item.type === "answer" && (
|
{item.type === "answer" && (
|
||||||
<div className="flex flex-col gap-docs_0.75">
|
<div className="flex flex-col gap-docs_0.75">
|
||||||
{!item.question_id && item.content.length === 0 && <DotsLoading />}
|
{!item.question_id && item.content.length === 0 && <DotsLoading />}
|
||||||
<MarkdownContent className="[&>*:last-child]:mb-0">
|
<MarkdownContent
|
||||||
|
className="[&>*:last-child]:mb-0"
|
||||||
|
components={{
|
||||||
|
...MDXComponents,
|
||||||
|
code: (props: CodeMdxProps) => {
|
||||||
|
return <CodeMdx {...props} noReport noAskAi />
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
>
|
||||||
{item.content}
|
{item.content}
|
||||||
</MarkdownContent>
|
</MarkdownContent>
|
||||||
{item.question_id && <AiAssistantThreadItemActions item={item} />}
|
{item.question_id && <AiAssistantThreadItemActions item={item} />}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
import React, { useMemo, useState } from "react"
|
import React from "react"
|
||||||
import { Button } from "../../Button"
|
import { Button } from "../../Button"
|
||||||
import { Tooltip } from "../../Tooltip"
|
import { Tooltip } from "../../Tooltip"
|
||||||
import { Kbd } from "../../Kbd"
|
import { Kbd } from "../../Kbd"
|
||||||
@@ -9,17 +9,13 @@ import { useAiAssistant, useSearch, useSiteConfig } from "../../../providers"
|
|||||||
import { useKeyboardShortcut } from "../../../hooks"
|
import { useKeyboardShortcut } from "../../../hooks"
|
||||||
import Image from "next/image"
|
import Image from "next/image"
|
||||||
|
|
||||||
const AI_ASSISTANT_ICON = "/images/ai-assistent-luminosity.png"
|
// const AI_ASSISTANT_ICON = "/images/ai-assistent-luminosity.png"
|
||||||
const AI_ASSISTANT_ICON_ACTIVE = "/images/ai-assistent.png"
|
const AI_ASSISTANT_ICON_ACTIVE = "/images/ai-assistent.png"
|
||||||
|
|
||||||
export const AiAssistantTriggerButton = () => {
|
export const AiAssistantTriggerButton = () => {
|
||||||
const [hovered, setHovered] = useState(false)
|
|
||||||
const { config } = useSiteConfig()
|
const { config } = useSiteConfig()
|
||||||
const { chatOpened, setChatOpened } = useAiAssistant()
|
const { setChatOpened } = useAiAssistant()
|
||||||
const { setIsOpen } = useSearch()
|
const { setIsOpen } = useSearch()
|
||||||
const isActive = useMemo(() => {
|
|
||||||
return hovered || chatOpened
|
|
||||||
}, [hovered, chatOpened])
|
|
||||||
const osShortcut = getOsShortcut()
|
const osShortcut = getOsShortcut()
|
||||||
|
|
||||||
useKeyboardShortcut({
|
useKeyboardShortcut({
|
||||||
@@ -35,36 +31,29 @@ export const AiAssistantTriggerButton = () => {
|
|||||||
return (
|
return (
|
||||||
<Tooltip
|
<Tooltip
|
||||||
render={() => (
|
render={() => (
|
||||||
<span className="flex gap-[6px] items-center">
|
<span className="flex gap-[5px] items-center">
|
||||||
<span className="text-compact-x-small-plus text-medusa-fg-base">
|
<Kbd className="bg-medusa-bg-field-component border-medusa-border-strong w-[18px] h-[18px] inline-block">
|
||||||
Ask AI
|
{osShortcut}
|
||||||
</span>
|
</Kbd>
|
||||||
<span className="flex gap-[5px] items-center">
|
<Kbd className="bg-medusa-bg-field-component border-medusa-border-strong w-[18px] h-[18px] inline-block">
|
||||||
<Kbd className="bg-medusa-bg-field-component border-medusa-border-strong w-[18px] h-[18px] inline-block">
|
i
|
||||||
{osShortcut}
|
</Kbd>
|
||||||
</Kbd>
|
|
||||||
<Kbd className="bg-medusa-bg-field-component border-medusa-border-strong w-[18px] h-[18px] inline-block">
|
|
||||||
i
|
|
||||||
</Kbd>
|
|
||||||
</span>
|
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<Button
|
<Button
|
||||||
variant="transparent-clear"
|
variant="transparent-clear"
|
||||||
className="!p-[6.5px]"
|
|
||||||
onMouseOver={() => setHovered(true)}
|
|
||||||
onMouseOut={() => setHovered(false)}
|
|
||||||
onTouchStart={() => setHovered(true)}
|
|
||||||
onTouchEnd={() => setHovered(false)}
|
|
||||||
onClick={() => setChatOpened((prev) => !prev)}
|
onClick={() => setChatOpened((prev) => !prev)}
|
||||||
>
|
>
|
||||||
<Image
|
<Image
|
||||||
src={`${config.basePath}${isActive ? AI_ASSISTANT_ICON_ACTIVE : AI_ASSISTANT_ICON}`}
|
src={`${config.basePath}${AI_ASSISTANT_ICON_ACTIVE}`}
|
||||||
width={15}
|
width={15}
|
||||||
height={15}
|
height={15}
|
||||||
alt="AI Assistant"
|
alt="AI Assistant"
|
||||||
/>
|
/>
|
||||||
|
<span className="hidden md:inline-block text-medusa-fg-subtle">
|
||||||
|
Ask AI
|
||||||
|
</span>
|
||||||
</Button>
|
</Button>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -0,0 +1,59 @@
|
|||||||
|
"use client"
|
||||||
|
|
||||||
|
import React from "react"
|
||||||
|
import { useAiAssistant, useSiteConfig } from "../../../../providers"
|
||||||
|
import { useAiAssistantChat } from "../../../../providers/AiAssistant/Chat"
|
||||||
|
import clsx from "clsx"
|
||||||
|
import { Tooltip } from "../../../Tooltip"
|
||||||
|
import Image from "next/image"
|
||||||
|
|
||||||
|
export type CodeBlockCopyActionProps = {
|
||||||
|
source: string
|
||||||
|
inHeader: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export const CodeBlockAskAiAction = ({
|
||||||
|
source,
|
||||||
|
inHeader,
|
||||||
|
}: CodeBlockCopyActionProps) => {
|
||||||
|
const { setChatOpened } = useAiAssistant()
|
||||||
|
const { setQuestion, loading } = useAiAssistantChat()
|
||||||
|
const { config } = useSiteConfig()
|
||||||
|
|
||||||
|
const handleClick = () => {
|
||||||
|
if (loading) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
setQuestion(`\`\`\`tsx\n${source.trim()}\n\`\`\`\n\nExplain the code above`)
|
||||||
|
setChatOpened(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Tooltip
|
||||||
|
text="Ask AI"
|
||||||
|
tooltipClassName="font-base"
|
||||||
|
className={clsx("group")}
|
||||||
|
innerClassName={clsx(
|
||||||
|
inHeader && "flex",
|
||||||
|
"h-fit rounded-docs_sm",
|
||||||
|
"group-hover:bg-medusa-contrast-bg-base-hover group-focus:bg-medusa-contrast-bg-base-hover"
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
className={clsx(
|
||||||
|
!inHeader && "p-[6px]",
|
||||||
|
inHeader && "p-[4.5px]",
|
||||||
|
"cursor-pointer"
|
||||||
|
)}
|
||||||
|
onClick={handleClick}
|
||||||
|
>
|
||||||
|
<Image
|
||||||
|
src={`${config.basePath}/images/ai-assistent-luminosity.png`}
|
||||||
|
width={15}
|
||||||
|
height={15}
|
||||||
|
alt="Ask AI"
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
</Tooltip>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
"use client"
|
||||||
|
|
||||||
import React, { useEffect, useState } from "react"
|
import React, { useEffect, useState } from "react"
|
||||||
import { CopyButton } from "../../../.."
|
import { CopyButton } from "../../../.."
|
||||||
import clsx from "clsx"
|
import clsx from "clsx"
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { Link, Tooltip } from "@/components"
|
|||||||
import { ExclamationCircle, PlaySolid } from "@medusajs/icons"
|
import { ExclamationCircle, PlaySolid } from "@medusajs/icons"
|
||||||
import { GITHUB_ISSUES_LINK } from "@/constants"
|
import { GITHUB_ISSUES_LINK } from "@/constants"
|
||||||
import { CodeBlockCopyAction } from "./Copy"
|
import { CodeBlockCopyAction } from "./Copy"
|
||||||
|
import { CodeBlockAskAiAction } from "./AskAi"
|
||||||
|
|
||||||
export type CodeBlockActionsProps = {
|
export type CodeBlockActionsProps = {
|
||||||
source: string
|
source: string
|
||||||
@@ -74,6 +75,7 @@ export const CodeBlockActions = ({
|
|||||||
]
|
]
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
|
<CodeBlockAskAiAction source={source} inHeader={inHeader} />
|
||||||
{canShowApiTesting && (
|
{canShowApiTesting && (
|
||||||
<Tooltip
|
<Tooltip
|
||||||
text="Test API"
|
text="Test API"
|
||||||
|
|||||||
@@ -38,9 +38,13 @@ export type CodeBlockMetaFields = {
|
|||||||
noCopy?: boolean
|
noCopy?: boolean
|
||||||
noReport?: boolean
|
noReport?: boolean
|
||||||
noLineNumbers?: boolean
|
noLineNumbers?: boolean
|
||||||
|
noAskAi?: boolean
|
||||||
collapsibleLines?: string
|
collapsibleLines?: string
|
||||||
expandButtonLabel?: string
|
expandButtonLabel?: string
|
||||||
isTerminal?: boolean
|
isTerminal?: boolean
|
||||||
|
forceNoTitle?: boolean
|
||||||
|
collapsed?: boolean
|
||||||
|
wrapperClassName?: string
|
||||||
} & CodeBlockHeaderMeta
|
} & CodeBlockHeaderMeta
|
||||||
|
|
||||||
export type CodeBlockStyle = "loud" | "subtle" | "inline"
|
export type CodeBlockStyle = "loud" | "subtle" | "inline"
|
||||||
@@ -48,14 +52,11 @@ export type CodeBlockStyle = "loud" | "subtle" | "inline"
|
|||||||
export type CodeBlockProps = {
|
export type CodeBlockProps = {
|
||||||
source: string
|
source: string
|
||||||
lang?: string
|
lang?: string
|
||||||
wrapperClassName?: string
|
|
||||||
innerClassName?: string
|
innerClassName?: string
|
||||||
className?: string
|
className?: string
|
||||||
collapsed?: boolean
|
|
||||||
blockStyle?: CodeBlockStyle
|
blockStyle?: CodeBlockStyle
|
||||||
children?: React.ReactNode
|
children?: React.ReactNode
|
||||||
style?: React.HTMLAttributes<HTMLDivElement>["style"]
|
style?: React.HTMLAttributes<HTMLDivElement>["style"]
|
||||||
forceNoTitle?: boolean
|
|
||||||
animateTokenHighlights?: boolean
|
animateTokenHighlights?: boolean
|
||||||
overrideColors?: {
|
overrideColors?: {
|
||||||
bg?: string
|
bg?: string
|
||||||
@@ -277,6 +278,7 @@ export const CodeBlock = ({
|
|||||||
getCollapsedLinesElm,
|
getCollapsedLinesElm,
|
||||||
getNonCollapsedLinesElm,
|
getNonCollapsedLinesElm,
|
||||||
type: collapsibleType,
|
type: collapsibleType,
|
||||||
|
isCollapsible,
|
||||||
...collapsibleResult
|
...collapsibleResult
|
||||||
} = useCollapsibleCodeLines({
|
} = useCollapsibleCodeLines({
|
||||||
collapsibleLinesStr: collapsibleLines,
|
collapsibleLinesStr: collapsibleLines,
|
||||||
@@ -462,7 +464,7 @@ export const CodeBlock = ({
|
|||||||
isSingleLine={tokens.length <= 1}
|
isSingleLine={tokens.length <= 1}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{collapsibleType === "end" && (
|
{collapsibleType === "end" && isCollapsible(tokens) && (
|
||||||
<>
|
<>
|
||||||
<CodeBlockCollapsibleFade
|
<CodeBlockCollapsibleFade
|
||||||
type={collapsibleType}
|
type={collapsibleType}
|
||||||
|
|||||||
@@ -2,7 +2,9 @@ import React from "react"
|
|||||||
import {
|
import {
|
||||||
CodeBlock,
|
CodeBlock,
|
||||||
CodeBlockMetaFields,
|
CodeBlockMetaFields,
|
||||||
|
CodeBlockProps,
|
||||||
InlineCode,
|
InlineCode,
|
||||||
|
InlineCodeProps,
|
||||||
MermaidDiagram,
|
MermaidDiagram,
|
||||||
} from "@/components"
|
} from "@/components"
|
||||||
import { Npm2YarnCode } from "../Npm2YarnCode"
|
import { Npm2YarnCode } from "../Npm2YarnCode"
|
||||||
@@ -10,13 +12,21 @@ import { Npm2YarnCode } from "../Npm2YarnCode"
|
|||||||
export type CodeMdxProps = {
|
export type CodeMdxProps = {
|
||||||
className?: string
|
className?: string
|
||||||
children?: React.ReactNode
|
children?: React.ReactNode
|
||||||
|
inlineCodeProps?: Partial<InlineCodeProps>
|
||||||
|
codeBlockProps?: Partial<CodeBlockProps>
|
||||||
} & CodeBlockMetaFields
|
} & CodeBlockMetaFields
|
||||||
|
|
||||||
// due to how mdx handles code blocks
|
// due to how mdx handles code blocks
|
||||||
// it is required that a code block specify a language
|
// it is required that a code block specify a language
|
||||||
// to be considered a block. Otherwise, it will be
|
// to be considered a block. Otherwise, it will be
|
||||||
// considered as inline code
|
// considered as inline code
|
||||||
export const CodeMdx = ({ className, children, ...rest }: CodeMdxProps) => {
|
export const CodeMdx = ({
|
||||||
|
className,
|
||||||
|
children,
|
||||||
|
inlineCodeProps = {},
|
||||||
|
codeBlockProps = {},
|
||||||
|
...rest
|
||||||
|
}: CodeMdxProps) => {
|
||||||
if (!children) {
|
if (!children) {
|
||||||
return <></>
|
return <></>
|
||||||
}
|
}
|
||||||
@@ -33,8 +43,15 @@ export const CodeMdx = ({ className, children, ...rest }: CodeMdxProps) => {
|
|||||||
} else if (match[1] === "mermaid") {
|
} else if (match[1] === "mermaid") {
|
||||||
return <MermaidDiagram diagramContent={codeContent} />
|
return <MermaidDiagram diagramContent={codeContent} />
|
||||||
}
|
}
|
||||||
return <CodeBlock source={codeContent} lang={match[1]} {...rest} />
|
return (
|
||||||
|
<CodeBlock
|
||||||
|
source={codeContent}
|
||||||
|
lang={match[1]}
|
||||||
|
{...codeBlockProps}
|
||||||
|
{...rest}
|
||||||
|
/>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return <InlineCode>{codeContent}</InlineCode>
|
return <InlineCode {...inlineCodeProps}>{codeContent}</InlineCode>
|
||||||
}
|
}
|
||||||
@@ -4,9 +4,14 @@ import React from "react"
|
|||||||
import clsx from "clsx"
|
import clsx from "clsx"
|
||||||
import { CopyButton } from "@/components"
|
import { CopyButton } from "@/components"
|
||||||
|
|
||||||
export type InlineCodeProps = React.ComponentProps<"code">
|
export type InlineCodeProps = React.ComponentProps<"code"> & {
|
||||||
|
variant?: "default" | "grey-bg"
|
||||||
|
}
|
||||||
|
|
||||||
export const InlineCode = (props: InlineCodeProps) => {
|
export const InlineCode = ({
|
||||||
|
variant = "default",
|
||||||
|
...props
|
||||||
|
}: InlineCodeProps) => {
|
||||||
return (
|
return (
|
||||||
<CopyButton
|
<CopyButton
|
||||||
text={props.children as string}
|
text={props.children as string}
|
||||||
@@ -18,11 +23,18 @@ export const InlineCode = (props: InlineCodeProps) => {
|
|||||||
<code
|
<code
|
||||||
{...props}
|
{...props}
|
||||||
className={clsx(
|
className={clsx(
|
||||||
"border-medusa-tag-neutral-border border",
|
"text-medusa-tag-neutral-text border",
|
||||||
"text-medusa-tag-neutral-text",
|
"font-monospace text-code-label rounded-docs_sm py-0 px-[5px]",
|
||||||
"bg-medusa-tag-neutral-bg font-monospace text-code-label rounded-docs_sm py-0 px-[6px]",
|
variant === "default" && [
|
||||||
"group-active:bg-medusa-bg-subtle-pressed group-focus:bg-medusa-bg-subtle-pressed",
|
"bg-medusa-tag-neutral-bg group-hover:bg-medusa-tag-neutral-bg-hover",
|
||||||
"group-hover:bg-medusa-tag-neutral-bg-hover",
|
"group-active:bg-medusa-bg-subtle-pressed group-focus:bg-medusa-bg-subtle-pressed",
|
||||||
|
"border-medusa-tag-neutral-border",
|
||||||
|
],
|
||||||
|
variant === "grey-bg" && [
|
||||||
|
"bg-medusa-bg-switch-off group-hover:bg-medusa-bg-switch-off-hover",
|
||||||
|
"group-active:bg-medusa-bg-switch-off-hover group-focus:bg-medusa-switch-off-hover",
|
||||||
|
"border-medusa-border-strong",
|
||||||
|
],
|
||||||
props.className
|
props.className
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import {
|
|||||||
useKeyboardShortcut,
|
useKeyboardShortcut,
|
||||||
type useKeyboardShortcutOptions,
|
type useKeyboardShortcutOptions,
|
||||||
} from "../use-keyboard-shortcut"
|
} from "../use-keyboard-shortcut"
|
||||||
|
import { useAiAssistantChat } from "../../providers/AiAssistant/Chat"
|
||||||
|
|
||||||
export type UseAiAssistantChatNavigationProps = {
|
export type UseAiAssistantChatNavigationProps = {
|
||||||
getChatWindowElm: () => HTMLElement | null
|
getChatWindowElm: () => HTMLElement | null
|
||||||
@@ -23,6 +24,7 @@ export const useAiAssistantChatNavigation = ({
|
|||||||
}: UseAiAssistantChatNavigationProps) => {
|
}: UseAiAssistantChatNavigationProps) => {
|
||||||
const shortcutKeys = useMemo(() => ["ArrowUp", "ArrowDown", "Enter"], [])
|
const shortcutKeys = useMemo(() => ["ArrowUp", "ArrowDown", "Enter"], [])
|
||||||
const { chatOpened } = useAiAssistant()
|
const { chatOpened } = useAiAssistant()
|
||||||
|
const { question } = useAiAssistantChat()
|
||||||
|
|
||||||
const handleKeyAction = (e: KeyboardEvent) => {
|
const handleKeyAction = (e: KeyboardEvent) => {
|
||||||
const chatElm = getChatWindowElm()
|
const chatElm = getChatWindowElm()
|
||||||
@@ -30,7 +32,8 @@ export const useAiAssistantChatNavigation = ({
|
|||||||
!chatOpened ||
|
!chatOpened ||
|
||||||
e.metaKey ||
|
e.metaKey ||
|
||||||
e.ctrlKey ||
|
e.ctrlKey ||
|
||||||
!chatElm?.contains(document.activeElement)
|
!chatElm?.contains(document.activeElement) ||
|
||||||
|
(question.length && question.includes("\n"))
|
||||||
) {
|
) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -135,6 +138,7 @@ export const useAiAssistantChatNavigation = ({
|
|||||||
shortcutKeys: shortcutKeys,
|
shortcutKeys: shortcutKeys,
|
||||||
checkEditing: false,
|
checkEditing: false,
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
|
preventDefault: false,
|
||||||
action: handleKeyAction,
|
action: handleKeyAction,
|
||||||
...keyboardProps,
|
...keyboardProps,
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ export const useCollapsibleCodeLines = ({
|
|||||||
const collapsedRange:
|
const collapsedRange:
|
||||||
| {
|
| {
|
||||||
start: number
|
start: number
|
||||||
end: number
|
end: number | undefined
|
||||||
}
|
}
|
||||||
| undefined = useMemo(() => {
|
| undefined = useMemo(() => {
|
||||||
if (!collapsibleLinesStr) {
|
if (!collapsibleLinesStr) {
|
||||||
@@ -46,8 +46,10 @@ export const useCollapsibleCodeLines = ({
|
|||||||
.map((lineNumber) => parseInt(lineNumber))
|
.map((lineNumber) => parseInt(lineNumber))
|
||||||
|
|
||||||
if (
|
if (
|
||||||
splitCollapsedLines.length !== 2 ||
|
!splitCollapsedLines.length ||
|
||||||
(splitCollapsedLines[0] !== 1 && splitCollapsedLines[1] < 2)
|
(splitCollapsedLines.length >= 2 &&
|
||||||
|
splitCollapsedLines[0] !== 1 &&
|
||||||
|
splitCollapsedLines[1] < 2)
|
||||||
) {
|
) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -58,6 +60,13 @@ export const useCollapsibleCodeLines = ({
|
|||||||
}
|
}
|
||||||
}, [collapsibleLinesStr])
|
}, [collapsibleLinesStr])
|
||||||
|
|
||||||
|
const isCollapsible = useCallback(
|
||||||
|
(tokens: Token[][]) => {
|
||||||
|
return collapsedRange && collapsedRange.start < tokens.length
|
||||||
|
},
|
||||||
|
[collapsedRange]
|
||||||
|
)
|
||||||
|
|
||||||
const type: CollapsedCodeLinesPosition | undefined = useMemo(() => {
|
const type: CollapsedCodeLinesPosition | undefined = useMemo(() => {
|
||||||
if (!collapsedRange) {
|
if (!collapsedRange) {
|
||||||
return undefined
|
return undefined
|
||||||
@@ -81,7 +90,7 @@ export const useCollapsibleCodeLines = ({
|
|||||||
tokens: Token[][]
|
tokens: Token[][]
|
||||||
highlightProps: HighlightProps
|
highlightProps: HighlightProps
|
||||||
}) => {
|
}) => {
|
||||||
if (!collapsedRange || !type) {
|
if (!collapsedRange || !type || !isCollapsible(tokens)) {
|
||||||
return <></>
|
return <></>
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -90,7 +99,9 @@ export const useCollapsibleCodeLines = ({
|
|||||||
|
|
||||||
const lines = tokens.slice(
|
const lines = tokens.slice(
|
||||||
startIndex,
|
startIndex,
|
||||||
Math.min(collapsedRange.end, tokens.length)
|
collapsedRange.end
|
||||||
|
? Math.min(collapsedRange.end, tokens.length)
|
||||||
|
: tokens.length
|
||||||
)
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -99,7 +110,7 @@ export const useCollapsibleCodeLines = ({
|
|||||||
</CodeBlockCollapsibleLines>
|
</CodeBlockCollapsibleLines>
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
[collapsedRange, collapsibleHookResult]
|
[collapsedRange, collapsibleHookResult, isCollapsible, type]
|
||||||
)
|
)
|
||||||
|
|
||||||
const getNonCollapsedLinesElm = useCallback(
|
const getNonCollapsedLinesElm = useCallback(
|
||||||
@@ -110,13 +121,13 @@ export const useCollapsibleCodeLines = ({
|
|||||||
tokens: Token[][]
|
tokens: Token[][]
|
||||||
highlightProps: HighlightProps
|
highlightProps: HighlightProps
|
||||||
}) => {
|
}) => {
|
||||||
if (!collapsedRange) {
|
if (!collapsedRange || !isCollapsible(tokens)) {
|
||||||
return getLines(tokens, highlightProps)
|
return getLines(tokens, highlightProps)
|
||||||
}
|
}
|
||||||
|
|
||||||
const isCollapseBeginning = collapsedRange.start === 1
|
const isCollapseBeginning = collapsedRange.start === 1
|
||||||
const lines = tokens.slice(
|
const lines = tokens.slice(
|
||||||
isCollapseBeginning ? collapsedRange.end : 0,
|
isCollapseBeginning ? collapsedRange.end || tokens.length : 0,
|
||||||
isCollapseBeginning ? undefined : collapsedRange.start
|
isCollapseBeginning ? undefined : collapsedRange.start
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -126,13 +137,14 @@ export const useCollapsibleCodeLines = ({
|
|||||||
isCollapseBeginning ? collapsedRange.end : 0
|
isCollapseBeginning ? collapsedRange.end : 0
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
[collapsedRange, collapsibleHookResult]
|
[collapsedRange, collapsibleHookResult, isCollapsible]
|
||||||
)
|
)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
getCollapsedLinesElm,
|
getCollapsedLinesElm,
|
||||||
getNonCollapsedLinesElm,
|
getNonCollapsedLinesElm,
|
||||||
type,
|
type,
|
||||||
|
isCollapsible,
|
||||||
...collapsibleHookResult,
|
...collapsibleHookResult,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -260,45 +260,47 @@ export const AiAssistantChatProvider = ({
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (
|
if (
|
||||||
!loading &&
|
loading ||
|
||||||
answer.length &&
|
!answer.length ||
|
||||||
thread[lastAnswerIndex]?.content !== answer
|
thread[lastAnswerIndex]?.content === answer
|
||||||
) {
|
) {
|
||||||
const uniqueAnswerSources = answerSources
|
return
|
||||||
.filter(
|
|
||||||
(source, index) =>
|
|
||||||
answerSources.findIndex(
|
|
||||||
(s) => s.source_url === source.source_url
|
|
||||||
) === index
|
|
||||||
)
|
|
||||||
.map((source) => {
|
|
||||||
const separatorIndex = source.title.indexOf("|")
|
|
||||||
return {
|
|
||||||
...source,
|
|
||||||
title:
|
|
||||||
separatorIndex !== -1
|
|
||||||
? source.title.slice(0, separatorIndex)
|
|
||||||
: source.title,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
setThread((prevThread) => [
|
|
||||||
...prevThread,
|
|
||||||
{
|
|
||||||
type: "answer",
|
|
||||||
content: answer,
|
|
||||||
question_id: identifiers?.question_answer_id,
|
|
||||||
order: getNewOrder(prevThread),
|
|
||||||
sources:
|
|
||||||
uniqueAnswerSources.length > 3
|
|
||||||
? uniqueAnswerSources.slice(0, 3)
|
|
||||||
: uniqueAnswerSources,
|
|
||||||
},
|
|
||||||
])
|
|
||||||
setAnswer("")
|
|
||||||
setAnswerSources([])
|
|
||||||
setMessagesCount((prev) => prev + 1)
|
|
||||||
inputRef.current?.focus()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const uniqueAnswerSources = answerSources
|
||||||
|
.filter(
|
||||||
|
(source, index) =>
|
||||||
|
answerSources.findIndex((s) => s.source_url === source.source_url) ===
|
||||||
|
index
|
||||||
|
)
|
||||||
|
.map((source) => {
|
||||||
|
const separatorIndex = source.title.indexOf("|")
|
||||||
|
return {
|
||||||
|
...source,
|
||||||
|
title:
|
||||||
|
separatorIndex !== -1
|
||||||
|
? source.title.slice(0, separatorIndex)
|
||||||
|
: source.title,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
setThread((prevThread) => [
|
||||||
|
...prevThread,
|
||||||
|
{
|
||||||
|
type: "answer",
|
||||||
|
content: answer,
|
||||||
|
question_id: identifiers?.question_answer_id,
|
||||||
|
order: getNewOrder(prevThread),
|
||||||
|
sources:
|
||||||
|
uniqueAnswerSources.length > 3
|
||||||
|
? uniqueAnswerSources.slice(0, 3)
|
||||||
|
: uniqueAnswerSources,
|
||||||
|
},
|
||||||
|
])
|
||||||
|
setAnswer("")
|
||||||
|
setAnswerSources([])
|
||||||
|
setMessagesCount((prev) => prev + 1)
|
||||||
|
inputRef.current?.focus()
|
||||||
|
scrollToBottom()
|
||||||
}, [loading, answer, thread, lastAnswerIndex, inputRef.current])
|
}, [loading, answer, thread, lastAnswerIndex, inputRef.current])
|
||||||
|
|
||||||
useResizeObserver(contentRef as React.RefObject<HTMLDivElement>, () => {
|
useResizeObserver(contentRef as React.RefObject<HTMLDivElement>, () => {
|
||||||
|
|||||||
@@ -508,8 +508,8 @@ module.exports = {
|
|||||||
"code-label": [
|
"code-label": [
|
||||||
"12px",
|
"12px",
|
||||||
{
|
{
|
||||||
lineHeight: "20px",
|
lineHeight: "15px",
|
||||||
fontWeight: "400",
|
fontWeight: "500",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
"code-body": [
|
"code-body": [
|
||||||
|
|||||||
@@ -1,12 +1,11 @@
|
|||||||
import path from "path"
|
import path from "path"
|
||||||
import coreConfig from "./modified.tailwind.config"
|
import coreConfig from "./modified.tailwind.config"
|
||||||
|
|
||||||
// Get two levels up from require.resolve("@medusajs/ui")
|
|
||||||
const root = path.join(require.resolve("docs-ui"), "../..")
|
const root = path.join(require.resolve("docs-ui"), "../..")
|
||||||
const uiPath = path.join(root, "**/*.{js,ts,jsx,tsx,mdx}")
|
const files = path.join(root, "**/*.{js,ts,jsx,tsx,mdx}")
|
||||||
|
|
||||||
/** @type {import('tailwindcss').Config} */
|
/** @type {import('tailwindcss').Config} */
|
||||||
module.exports = {
|
module.exports = {
|
||||||
...coreConfig,
|
...coreConfig,
|
||||||
content: [uiPath],
|
content: [files],
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user