Add global live feed for task comments
This commit is contained in:
122
frontend/src/components/atoms/Markdown.tsx
Normal file
122
frontend/src/components/atoms/Markdown.tsx
Normal file
@@ -0,0 +1,122 @@
|
||||
"use client";
|
||||
|
||||
import { memo } from "react";
|
||||
|
||||
import ReactMarkdown, { type Components } from "react-markdown";
|
||||
import remarkBreaks from "remark-breaks";
|
||||
import remarkGfm from "remark-gfm";
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
const MARKDOWN_TABLE_COMPONENTS: Components = {
|
||||
table: ({ node: _node, className, ...props }) => (
|
||||
<div className="my-3 overflow-x-auto">
|
||||
<table className={cn("w-full border-collapse", className)} {...props} />
|
||||
</div>
|
||||
),
|
||||
thead: ({ node: _node, className, ...props }) => (
|
||||
<thead className={cn("bg-slate-50", className)} {...props} />
|
||||
),
|
||||
tbody: ({ node: _node, className, ...props }) => (
|
||||
<tbody className={cn("divide-y divide-slate-100", className)} {...props} />
|
||||
),
|
||||
tr: ({ node: _node, className, ...props }) => (
|
||||
<tr className={cn("align-top", className)} {...props} />
|
||||
),
|
||||
th: ({ node: _node, className, ...props }) => (
|
||||
<th
|
||||
className={cn(
|
||||
"border border-slate-200 px-3 py-2 text-left text-xs font-semibold",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
),
|
||||
td: ({ node: _node, className, ...props }) => (
|
||||
<td
|
||||
className={cn("border border-slate-200 px-3 py-2 align-top", className)}
|
||||
{...props}
|
||||
/>
|
||||
),
|
||||
};
|
||||
|
||||
const MARKDOWN_COMPONENTS_BASIC: Components = {
|
||||
...MARKDOWN_TABLE_COMPONENTS,
|
||||
p: ({ node: _node, className, ...props }) => (
|
||||
<p className={cn("mb-2 last:mb-0", className)} {...props} />
|
||||
),
|
||||
ul: ({ node: _node, className, ...props }) => (
|
||||
<ul className={cn("mb-2 list-disc pl-5", className)} {...props} />
|
||||
),
|
||||
ol: ({ node: _node, className, ...props }) => (
|
||||
<ol className={cn("mb-2 list-decimal pl-5", className)} {...props} />
|
||||
),
|
||||
li: ({ node: _node, className, ...props }) => (
|
||||
<li className={cn("mb-1", className)} {...props} />
|
||||
),
|
||||
strong: ({ node: _node, className, ...props }) => (
|
||||
<strong className={cn("font-semibold", className)} {...props} />
|
||||
),
|
||||
};
|
||||
|
||||
const MARKDOWN_COMPONENTS_DESCRIPTION: Components = {
|
||||
...MARKDOWN_COMPONENTS_BASIC,
|
||||
p: ({ node: _node, className, ...props }) => (
|
||||
<p className={cn("mb-3 last:mb-0", className)} {...props} />
|
||||
),
|
||||
h1: ({ node: _node, className, ...props }) => (
|
||||
<h1 className={cn("mb-2 text-base font-semibold", className)} {...props} />
|
||||
),
|
||||
h2: ({ node: _node, className, ...props }) => (
|
||||
<h2 className={cn("mb-2 text-sm font-semibold", className)} {...props} />
|
||||
),
|
||||
h3: ({ node: _node, className, ...props }) => (
|
||||
<h3 className={cn("mb-2 text-sm font-semibold", className)} {...props} />
|
||||
),
|
||||
code: ({ node: _node, className, ...props }) => (
|
||||
<code
|
||||
className={cn("rounded bg-slate-100 px-1 py-0.5 text-xs", className)}
|
||||
{...props}
|
||||
/>
|
||||
),
|
||||
pre: ({ node: _node, className, ...props }) => (
|
||||
<pre
|
||||
className={cn(
|
||||
"overflow-auto rounded-lg bg-slate-900 p-3 text-xs text-slate-100",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
),
|
||||
};
|
||||
|
||||
const MARKDOWN_REMARK_PLUGINS_BASIC = [remarkGfm];
|
||||
const MARKDOWN_REMARK_PLUGINS_WITH_BREAKS = [remarkGfm, remarkBreaks];
|
||||
|
||||
export type MarkdownVariant = "basic" | "comment" | "description";
|
||||
|
||||
export const Markdown = memo(function Markdown({
|
||||
content,
|
||||
variant,
|
||||
}: {
|
||||
content: string;
|
||||
variant: MarkdownVariant;
|
||||
}) {
|
||||
const trimmed = content.trim();
|
||||
const remarkPlugins =
|
||||
variant === "comment"
|
||||
? MARKDOWN_REMARK_PLUGINS_WITH_BREAKS
|
||||
: MARKDOWN_REMARK_PLUGINS_BASIC;
|
||||
const components =
|
||||
variant === "description"
|
||||
? MARKDOWN_COMPONENTS_DESCRIPTION
|
||||
: MARKDOWN_COMPONENTS_BASIC;
|
||||
return (
|
||||
<ReactMarkdown remarkPlugins={remarkPlugins} components={components}>
|
||||
{trimmed}
|
||||
</ReactMarkdown>
|
||||
);
|
||||
});
|
||||
|
||||
Markdown.displayName = "Markdown";
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
import Link from "next/link";
|
||||
import { usePathname } from "next/navigation";
|
||||
import { BarChart3, Bot, LayoutGrid, Network } from "lucide-react";
|
||||
import { Activity, BarChart3, Bot, LayoutGrid, Network } from "lucide-react";
|
||||
|
||||
import { ApiError } from "@/api/mutator";
|
||||
import {
|
||||
@@ -81,6 +81,18 @@ export function DashboardSidebar() {
|
||||
<LayoutGrid className="h-4 w-4" />
|
||||
Boards
|
||||
</Link>
|
||||
<Link
|
||||
href="/activity"
|
||||
className={cn(
|
||||
"flex items-center gap-3 rounded-lg px-3 py-2.5 text-slate-700 transition",
|
||||
pathname.startsWith("/activity")
|
||||
? "bg-blue-100 text-blue-800 font-medium"
|
||||
: "hover:bg-slate-100"
|
||||
)}
|
||||
>
|
||||
<Activity className="h-4 w-4" />
|
||||
Live feed
|
||||
</Link>
|
||||
<Link
|
||||
href="/agents"
|
||||
className={cn(
|
||||
|
||||
Reference in New Issue
Block a user