mirror of
https://github.com/danswer-ai/danswer.git
synced 2025-03-17 13:22:42 +01:00
Fix slack links (#4254)
* fix slack links * updates * k * nit improvements
This commit is contained in:
parent
ecbd4eb1ad
commit
4e70f99214
@ -3,7 +3,7 @@
|
||||
import { SettingsContext } from "@/components/settings/SettingsProvider";
|
||||
import { useContext, useState, useRef, useLayoutEffect } from "react";
|
||||
import { ChevronDownIcon } from "@/components/icons/icons";
|
||||
import { MinimalMarkdown } from "@/components/chat/MinimalMarkdown";
|
||||
import MinimalMarkdown from "@/components/chat/MinimalMarkdown";
|
||||
|
||||
export function ChatBanner() {
|
||||
const settings = useContext(SettingsContext);
|
||||
|
@ -109,7 +109,6 @@ import {
|
||||
} from "@/components/resizable/constants";
|
||||
import FixedLogo from "../../components/logo/FixedLogo";
|
||||
|
||||
import { MinimalMarkdown } from "@/components/chat/MinimalMarkdown";
|
||||
import ExceptionTraceModal from "@/components/modals/ExceptionTraceModal";
|
||||
|
||||
import {
|
||||
@ -138,6 +137,7 @@ import { useSidebarShortcut } from "@/lib/browserUtilities";
|
||||
import { ConfirmEntityModal } from "@/components/modals/ConfirmEntityModal";
|
||||
import { ChatSearchModal } from "./chat_search/ChatSearchModal";
|
||||
import { ErrorBanner } from "./message/Resubmit";
|
||||
import MinimalMarkdown from "@/components/chat/MinimalMarkdown";
|
||||
|
||||
const TEMP_USER_MESSAGE_ID = -1;
|
||||
const TEMP_ASSISTANT_MESSAGE_ID = -2;
|
||||
|
@ -6,6 +6,7 @@ import { Button } from "@/components/ui/button";
|
||||
import { useContext, useEffect, useState } from "react";
|
||||
import ReactMarkdown from "react-markdown";
|
||||
import remarkGfm from "remark-gfm";
|
||||
import { transformLinkUri } from "@/lib/utils";
|
||||
|
||||
const ALL_USERS_INITIAL_POPUP_FLOW_COMPLETED =
|
||||
"allUsersInitialPopupFlowCompleted";
|
||||
@ -44,23 +45,26 @@ export function ChatPopup() {
|
||||
return (
|
||||
<Modal width="w-3/6 xl:w-[700px]" title={popupTitle}>
|
||||
<>
|
||||
<ReactMarkdown
|
||||
className="prose text-text-800 dark:text-neutral-100 max-w-full"
|
||||
components={{
|
||||
a: ({ node, ...props }) => (
|
||||
<a
|
||||
{...props}
|
||||
className="text-link hover:text-link-hover"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
/>
|
||||
),
|
||||
p: ({ node, ...props }) => <p {...props} className="text-sm" />,
|
||||
}}
|
||||
remarkPlugins={[remarkGfm]}
|
||||
>
|
||||
{popupContent}
|
||||
</ReactMarkdown>
|
||||
<div className="overflow-y-auto max-h-[90vh] py-8 px-4 text-left">
|
||||
<ReactMarkdown
|
||||
className="prose text-text-800 dark:text-neutral-100 max-w-full"
|
||||
components={{
|
||||
a: ({ node, ...props }) => (
|
||||
<a
|
||||
{...props}
|
||||
className="text-link hover:text-link-hover"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
/>
|
||||
),
|
||||
p: ({ node, ...props }) => <p {...props} className="text-sm" />,
|
||||
}}
|
||||
remarkPlugins={[remarkGfm]}
|
||||
urlTransform={transformLinkUri}
|
||||
>
|
||||
{popupContent}
|
||||
</ReactMarkdown>
|
||||
</div>
|
||||
|
||||
{showConsentError && (
|
||||
<p className="text-red-500 text-sm mt-2">
|
||||
|
@ -53,6 +53,7 @@ import { copyAll, handleCopy } from "./copyingUtils";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { RefreshCw } from "lucide-react";
|
||||
import { ErrorBanner, Resubmit } from "./Resubmit";
|
||||
import { transformLinkUri } from "@/lib/utils";
|
||||
|
||||
export const AgenticMessage = ({
|
||||
isStreamingQuestions,
|
||||
@ -336,6 +337,7 @@ export const AgenticMessage = ({
|
||||
}}
|
||||
remarkPlugins={[remarkGfm, remarkMath]}
|
||||
rehypePlugins={[[rehypePrism, { ignoreMissing: true }], rehypeKatex]}
|
||||
urlTransform={transformLinkUri}
|
||||
>
|
||||
{finalAlternativeContent}
|
||||
</ReactMarkdown>
|
||||
@ -349,6 +351,7 @@ export const AgenticMessage = ({
|
||||
components={markdownComponents}
|
||||
remarkPlugins={[remarkGfm, remarkMath]}
|
||||
rehypePlugins={[[rehypePrism, { ignoreMissing: true }], rehypeKatex]}
|
||||
urlTransform={transformLinkUri}
|
||||
>
|
||||
{streamedContent +
|
||||
(!isComplete && !secondLevelGenerating ? " [*]() " : "")}
|
||||
|
@ -160,8 +160,9 @@ export const MemoizedLink = memo(
|
||||
|
||||
const handleMouseDown = () => {
|
||||
let url = href || rest.children?.toString();
|
||||
if (url && !url.startsWith("http://") && !url.startsWith("https://")) {
|
||||
// Try to construct a valid URL
|
||||
|
||||
if (url && !url.includes("://")) {
|
||||
// Only add https:// if the URL doesn't already have a protocol
|
||||
const httpsUrl = `https://${url}`;
|
||||
try {
|
||||
new URL(httpsUrl);
|
||||
|
@ -71,6 +71,7 @@ import remarkMath from "remark-math";
|
||||
import rehypeKatex from "rehype-katex";
|
||||
import "katex/dist/katex.min.css";
|
||||
import { copyAll, handleCopy } from "./copyingUtils";
|
||||
import { transformLinkUri } from "@/lib/utils";
|
||||
|
||||
const TOOLS_WITH_CUSTOM_HANDLING = [
|
||||
SEARCH_TOOL_NAME,
|
||||
@ -348,7 +349,7 @@ export const AIMessage = ({
|
||||
a: anchorCallback,
|
||||
p: paragraphCallback,
|
||||
b: ({ node, className, children }: any) => {
|
||||
return <span className={className}>||||{children}</span>;
|
||||
return <span className={className}>{children}</span>;
|
||||
},
|
||||
code: ({ node, className, children }: any) => {
|
||||
const codeText = extractCodeText(
|
||||
@ -381,6 +382,7 @@ export const AIMessage = ({
|
||||
components={markdownComponents}
|
||||
remarkPlugins={[remarkGfm, remarkMath]}
|
||||
rehypePlugins={[[rehypePrism, { ignoreMissing: true }], rehypeKatex]}
|
||||
urlTransform={transformLinkUri}
|
||||
>
|
||||
{finalContent}
|
||||
</ReactMarkdown>
|
||||
|
@ -16,15 +16,15 @@ import ReactMarkdown from "react-markdown";
|
||||
import { MemoizedAnchor } from "./MemoizedTextComponents";
|
||||
import { MemoizedParagraph } from "./MemoizedTextComponents";
|
||||
import { extractCodeText, preprocessLaTeX } from "./codeUtils";
|
||||
|
||||
import remarkGfm from "remark-gfm";
|
||||
import remarkMath from "remark-math";
|
||||
import rehypeKatex from "rehype-katex";
|
||||
import remarkGfm from "remark-gfm";
|
||||
import { CodeBlock } from "./CodeBlock";
|
||||
import { CheckIcon, ChevronDown } from "lucide-react";
|
||||
import { PHASE_MIN_MS, useStreamingMessages } from "./StreamingMessages";
|
||||
import { CirclingArrowIcon } from "@/components/icons/icons";
|
||||
import { handleCopy } from "./copyingUtils";
|
||||
import { transformLinkUri } from "@/lib/utils";
|
||||
|
||||
export const StatusIndicator = ({ status }: { status: ToggleState }) => {
|
||||
return (
|
||||
@ -301,6 +301,7 @@ const SubQuestionDisplay: React.FC<{
|
||||
components={markdownComponents}
|
||||
remarkPlugins={[remarkGfm, remarkMath]}
|
||||
rehypePlugins={[rehypeKatex]}
|
||||
urlTransform={transformLinkUri}
|
||||
>
|
||||
{finalContent}
|
||||
</ReactMarkdown>
|
||||
|
@ -33,6 +33,8 @@ import Link from "next/link";
|
||||
import { CheckboxField } from "@/components/ui/checkbox";
|
||||
import { CheckedState } from "@radix-ui/react-checkbox";
|
||||
|
||||
import { transformLinkUri } from "@/lib/utils";
|
||||
|
||||
export function SectionHeader({
|
||||
children,
|
||||
}: {
|
||||
@ -432,6 +434,7 @@ export const MarkdownFormField = ({
|
||||
<ReactMarkdown
|
||||
className="prose dark:prose-invert"
|
||||
remarkPlugins={[remarkGfm]}
|
||||
urlTransform={transformLinkUri}
|
||||
>
|
||||
{field.value}
|
||||
</ReactMarkdown>
|
||||
|
@ -4,19 +4,26 @@ import {
|
||||
MemoizedLink,
|
||||
MemoizedParagraph,
|
||||
} from "@/app/chat/message/MemoizedTextComponents";
|
||||
import React, { useMemo } from "react";
|
||||
import React, { useMemo, CSSProperties } from "react";
|
||||
import ReactMarkdown from "react-markdown";
|
||||
import remarkGfm from "remark-gfm";
|
||||
import rehypePrism from "rehype-prism-plus";
|
||||
import remarkMath from "remark-math";
|
||||
import rehypeKatex from "rehype-katex";
|
||||
import "katex/dist/katex.min.css";
|
||||
import { transformLinkUri } from "@/lib/utils";
|
||||
|
||||
interface MinimalMarkdownProps {
|
||||
content: string;
|
||||
className?: string;
|
||||
style?: CSSProperties;
|
||||
}
|
||||
|
||||
export const MinimalMarkdown: React.FC<MinimalMarkdownProps> = ({
|
||||
export default function MinimalMarkdown({
|
||||
content,
|
||||
className = "",
|
||||
}) => {
|
||||
style,
|
||||
}: MinimalMarkdownProps) {
|
||||
const markdownComponents = useMemo(
|
||||
() => ({
|
||||
a: MemoizedLink,
|
||||
@ -34,12 +41,16 @@ export const MinimalMarkdown: React.FC<MinimalMarkdownProps> = ({
|
||||
);
|
||||
|
||||
return (
|
||||
<ReactMarkdown
|
||||
className={`w-full text-wrap break-word prose dark:prose-invert ${className}`}
|
||||
components={markdownComponents}
|
||||
remarkPlugins={[remarkGfm]}
|
||||
>
|
||||
{content}
|
||||
</ReactMarkdown>
|
||||
<div style={style || {}} className={`${className}`}>
|
||||
<ReactMarkdown
|
||||
className="prose dark:prose-invert max-w-full text-sm break-words"
|
||||
components={markdownComponents}
|
||||
rehypePlugins={[[rehypePrism, { ignoreMissing: true }], rehypeKatex]}
|
||||
remarkPlugins={[remarkGfm, remarkMath]}
|
||||
urlTransform={transformLinkUri}
|
||||
>
|
||||
{content}
|
||||
</ReactMarkdown>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
@ -10,7 +10,7 @@ import {
|
||||
} from "@/components/ui/dialog";
|
||||
import { Download, XIcon, ZoomIn, ZoomOut } from "lucide-react";
|
||||
import { OnyxDocument } from "@/lib/search/interfaces";
|
||||
import { MinimalMarkdown } from "./MinimalMarkdown";
|
||||
import MinimalMarkdown from "@/components/chat/MinimalMarkdown";
|
||||
|
||||
interface TextViewProps {
|
||||
presentingDocument: OnyxDocument;
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { Quote } from "@/lib/search/interfaces";
|
||||
import { ResponseSection, StatusOptions } from "./ResponseSection";
|
||||
import { MinimalMarkdown } from "@/components/chat/MinimalMarkdown";
|
||||
import MinimalMarkdown from "@/components/chat/MinimalMarkdown";
|
||||
|
||||
const TEMP_STRING = "__$%^TEMP$%^__";
|
||||
|
||||
|
@ -91,3 +91,17 @@ export const NEXT_PUBLIC_INCLUDE_ERROR_POPUP_SUPPORT_LINK =
|
||||
|
||||
export const NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY =
|
||||
process.env.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY;
|
||||
|
||||
// Add support for custom URL protocols in markdown links
|
||||
export const ALLOWED_URL_PROTOCOLS = [
|
||||
"http:",
|
||||
"https:",
|
||||
"mailto:",
|
||||
"tel:",
|
||||
"slack:",
|
||||
"vscode:",
|
||||
"file:",
|
||||
"sms:",
|
||||
"spotify:",
|
||||
"zoommtg:",
|
||||
];
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { clsx, type ClassValue } from "clsx";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
import { ALLOWED_URL_PROTOCOLS } from "./constants";
|
||||
|
||||
export function cn(...inputs: ClassValue[]) {
|
||||
return twMerge(clsx(inputs));
|
||||
@ -8,3 +9,28 @@ export function cn(...inputs: ClassValue[]) {
|
||||
export const truncateString = (str: string, maxLength: number) => {
|
||||
return str.length > maxLength ? str.slice(0, maxLength - 1) + "..." : str;
|
||||
};
|
||||
|
||||
/**
|
||||
* Custom URL transformer function for ReactMarkdown
|
||||
* Allows specific protocols to be used in markdown links
|
||||
* We use this with the urlTransform prop in ReactMarkdown
|
||||
*/
|
||||
export function transformLinkUri(href: string) {
|
||||
if (!href) return href;
|
||||
|
||||
const url = href.trim();
|
||||
try {
|
||||
const parsedUrl = new URL(url);
|
||||
if (
|
||||
ALLOWED_URL_PROTOCOLS.some((protocol) =>
|
||||
parsedUrl.protocol.startsWith(protocol)
|
||||
)
|
||||
) {
|
||||
return url;
|
||||
}
|
||||
} catch (e) {
|
||||
// If it's not a valid URL with protocol, return the original href
|
||||
return href;
|
||||
}
|
||||
return href;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user