feat: enhance ResearchBlock tab functionality

This commit is contained in:
Jiang Feng
2025-04-27 14:51:00 +08:00
parent 0e10362639
commit f35131da19
18 changed files with 77 additions and 395 deletions

View File

@@ -124,7 +124,12 @@ export function ResearchBlock({
</TabsTrigger> </TabsTrigger>
</TabsList> </TabsList>
</div> </div>
<TabsContent className="h-full min-h-0 flex-grow px-8" value="report"> <TabsContent
className="h-full min-h-0 flex-grow px-8"
value="report"
forceMount
hidden={activeTab !== "report"}
>
<ScrollContainer <ScrollContainer
className="px-5pb-20 h-full" className="px-5pb-20 h-full"
scrollShadowColor="var(--card)" scrollShadowColor="var(--card)"
@@ -141,6 +146,8 @@ export function ResearchBlock({
<TabsContent <TabsContent
className="h-full min-h-0 flex-grow px-8" className="h-full min-h-0 flex-grow px-8"
value="activities" value="activities"
forceMount
hidden={activeTab !== "activities"}
> >
<ScrollContainer className="h-full" scrollShadowColor="var(--card)"> <ScrollContainer className="h-full" scrollShadowColor="var(--card)">
{researchId && ( {researchId && (

View File

@@ -1,7 +1,7 @@
// Copyright (c) 2025 Bytedance Ltd. and/or its affiliates // Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
import { useCallback, useEffect, useLayoutEffect, useRef } from "react"; import { useCallback, useRef } from "react";
import ReportEditor from "~/components/editor"; import ReportEditor from "~/components/editor";
import { useMessage, useStore } from "~/core/store"; import { useMessage, useStore } from "~/core/store";
@@ -48,6 +48,7 @@ export function ResearchReportBlock({
// }, 500); // }, 500);
// } // }
// }, [isCompleted]); // }, [isCompleted]);
return ( return (
<div <div
ref={contentRef} ref={contentRef}

View File

@@ -1,3 +1,6 @@
// Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
// SPDX-License-Identifier: MIT
import ReportEditor from "~/components/editor"; import ReportEditor from "~/components/editor";
const content = ` const content = `

View File

@@ -1,356 +0,0 @@
export const defaultEditorContent = {
type: "doc",
content: [
{
type: "heading",
attrs: { level: 2 },
content: [{ type: "text", text: "Introducing Novel" }],
},
{
type: "paragraph",
content: [
{
type: "text",
marks: [
{
type: "link",
attrs: {
href: "https://github.com/steven-tey/novel",
target: "_blank",
},
},
],
text: "Novel",
},
{
type: "text",
text: " is a Notion-style WYSIWYG editor with AI-powered autocompletion. Built with ",
},
{
type: "text",
marks: [
{
type: "link",
attrs: {
href: "https://tiptap.dev/",
target: "_blank",
},
},
],
text: "Tiptap",
},
{ type: "text", text: " + " },
{
type: "text",
marks: [
{
type: "link",
attrs: {
href: "https://sdk.vercel.ai/docs",
target: "_blank",
},
},
],
text: "Vercel AI SDK",
},
{ type: "text", text: "." },
],
},
{
type: "heading",
attrs: { level: 3 },
content: [{ type: "text", text: "Installation" }],
},
{
type: "codeBlock",
attrs: { language: null },
content: [{ type: "text", text: "npm i novel" }],
},
{
type: "heading",
attrs: { level: 3 },
content: [{ type: "text", text: "Usage" }],
},
{
type: "codeBlock",
attrs: { language: null },
content: [
{
type: "text",
text: 'import { Editor } from "novel";\n\nexport default function App() {\n return (\n <Editor />\n )\n}',
},
],
},
{
type: "heading",
attrs: { level: 3 },
content: [{ type: "text", text: "Features" }],
},
{
type: "orderedList",
attrs: { tight: true, start: 1 },
content: [
{
type: "listItem",
content: [
{
type: "paragraph",
content: [{ type: "text", text: "Slash menu & bubble menu" }],
},
],
},
{
type: "listItem",
content: [
{
type: "paragraph",
content: [
{ type: "text", text: "AI autocomplete (type " },
{ type: "text", marks: [{ type: "code" }], text: "++" },
{
type: "text",
text: " to activate, or select from slash menu)",
},
],
},
],
},
{
type: "listItem",
content: [
{
type: "paragraph",
content: [
{
type: "text",
text: "Image uploads (drag & drop / copy & paste, or select from slash menu) ",
},
],
},
],
},
{
type: "listItem",
content: [
{
type: "paragraph",
content: [
{
type: "text",
text: "Add tweets from the command slash menu:",
},
],
},
{
type: "twitter",
attrs: {
src: "https://x.com/elonmusk/status/1800759252224729577",
},
},
],
},
{
type: "listItem",
content: [
{
type: "paragraph",
content: [
{
type: "text",
text: "Mathematical symbols with LaTeX expression:",
},
],
},
{
type: "orderedList",
attrs: {
tight: true,
start: 1,
},
content: [
{
type: "listItem",
content: [
{
type: "paragraph",
content: [
{
type: "math",
attrs: {
latex: "E = mc^2",
},
},
],
},
],
},
{
type: "listItem",
content: [
{
type: "paragraph",
content: [
{
type: "math",
attrs: {
latex: "a^2 = \\sqrt{b^2 + c^2}",
},
},
],
},
],
},
{
type: "listItem",
content: [
{
type: "paragraph",
content: [
{
type: "math",
attrs: {
latex:
"\\hat{f} (\\xi)=\\int_{-\\infty}^{\\infty}f(x)e^{-2\\pi ix\\xi}dx",
},
},
],
},
],
},
{
type: "listItem",
content: [
{
type: "paragraph",
content: [
{
type: "math",
attrs: {
latex:
"A=\\begin{bmatrix}a&b\\\\c&d \\end{bmatrix}",
},
},
],
},
],
},
{
type: "listItem",
content: [
{
type: "paragraph",
content: [
{
type: "math",
attrs: {
latex: "\\sum_{i=0}^n x_i",
},
},
],
},
],
},
],
},
],
},
],
},
{
type: "image",
attrs: {
src: "https://public.blob.vercel-storage.com/pJrjXbdONOnAeZAZ/banner-2wQk82qTwyVgvlhTW21GIkWgqPGD2C.png",
alt: "banner.png",
title: "banner.png",
width: null,
height: null,
},
},
{ type: "horizontalRule" },
{
type: "heading",
attrs: { level: 3 },
content: [{ type: "text", text: "Learn more" }],
},
{
type: "taskList",
content: [
{
type: "taskItem",
attrs: { checked: false },
content: [
{
type: "paragraph",
content: [
{ type: "text", text: "Star us on " },
{
type: "text",
marks: [
{
type: "link",
attrs: {
href: "https://github.com/steven-tey/novel",
target: "_blank",
},
},
],
text: "GitHub",
},
],
},
],
},
{
type: "taskItem",
attrs: { checked: false },
content: [
{
type: "paragraph",
content: [
{ type: "text", text: "Install the " },
{
type: "text",
marks: [
{
type: "link",
attrs: {
href: "https://www.npmjs.com/package/novel",
target: "_blank",
},
},
],
text: "NPM package",
},
],
},
],
},
{
type: "taskItem",
attrs: { checked: false },
content: [
{
type: "paragraph",
content: [
{
type: "text",
marks: [
{
type: "link",
attrs: {
href: "https://vercel.com/templates/next.js/novel",
target: "_blank",
},
},
],
text: "Deploy your own",
},
{ type: "text", text: " to Vercel" },
],
},
],
},
],
},
],
};

View File

@@ -1,3 +1,6 @@
// Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
// SPDX-License-Identifier: MIT
import { import {
AIHighlight, AIHighlight,
CharacterCount, CharacterCount,

View File

@@ -1,3 +1,6 @@
// Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
// SPDX-License-Identifier: MIT
import { CommandGroup, CommandItem, CommandSeparator } from "../../ui/command"; import { CommandGroup, CommandItem, CommandSeparator } from "../../ui/command";
import { useEditor } from "novel"; import { useEditor } from "novel";
import { Check, TextQuote, TrashIcon } from "lucide-react"; import { Check, TextQuote, TrashIcon } from "lucide-react";

View File

@@ -1,3 +1,6 @@
// Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
// SPDX-License-Identifier: MIT
import { import {
ArrowDownWideNarrow, ArrowDownWideNarrow,
CheckCheck, CheckCheck,
@@ -14,6 +17,7 @@ const options = [
label: "Improve writing", label: "Improve writing",
icon: RefreshCcwDot, icon: RefreshCcwDot,
}, },
// TODO: add this back in
// { // {
// value: "fix", // value: "fix",
// label: "Fix grammar", // label: "Fix grammar",

View File

@@ -1,3 +1,6 @@
// Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
// SPDX-License-Identifier: MIT
"use client"; "use client";
import { Command, CommandInput } from "../../ui/command"; import { Command, CommandInput } from "../../ui/command";

View File

@@ -1,3 +1,6 @@
// Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
// SPDX-License-Identifier: MIT
import { EditorBubble, removeAIHighlight, useEditor } from "novel"; import { EditorBubble, removeAIHighlight, useEditor } from "novel";
import { Fragment, type ReactNode, useEffect } from "react"; import { Fragment, type ReactNode, useEffect } from "react";
import { Button } from "../../ui/button"; import { Button } from "../../ui/button";

View File

@@ -1,3 +1,6 @@
// Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
// SPDX-License-Identifier: MIT
import { createImageUpload } from "novel"; import { createImageUpload } from "novel";
import { toast } from "sonner"; import { toast } from "sonner";
@@ -26,7 +29,9 @@ const onUpload = (file: File) => {
// No blob store configured // No blob store configured
} else if (res.status === 401) { } else if (res.status === 401) {
resolve(file); resolve(file);
throw new Error("`BLOB_READ_WRITE_TOKEN` environment variable not found, reading image locally instead."); throw new Error(
"`BLOB_READ_WRITE_TOKEN` environment variable not found, reading image locally instead.",
);
// Unknown error // Unknown error
} else { } else {
throw new Error("Error uploading image. Please try again."); throw new Error("Error uploading image. Please try again.");

View File

@@ -1,3 +1,6 @@
// Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
// SPDX-License-Identifier: MIT
"use client"; "use client";
import { import {

View File

@@ -1,3 +1,6 @@
// Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
// SPDX-License-Identifier: MIT
import { Check, ChevronDown } from "lucide-react"; import { Check, ChevronDown } from "lucide-react";
import { EditorBubbleItem, useEditor } from "novel"; import { EditorBubbleItem, useEditor } from "novel";

View File

@@ -1,3 +1,6 @@
// Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
// SPDX-License-Identifier: MIT
import { Button } from "../../ui/button"; import { Button } from "../../ui/button";
import { PopoverContent } from "../../ui/popover"; import { PopoverContent } from "../../ui/popover";
import { cn } from "../../../lib/utils"; import { cn } from "../../../lib/utils";

View File

@@ -1,3 +1,6 @@
// Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
// SPDX-License-Identifier: MIT
import { Button } from "../../ui/button"; import { Button } from "../../ui/button";
import { cn } from "../../../lib/utils"; import { cn } from "../../../lib/utils";
import { SigmaIcon } from "lucide-react"; import { SigmaIcon } from "lucide-react";

View File

@@ -1,3 +1,6 @@
// Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
// SPDX-License-Identifier: MIT
import { import {
Check, Check,
CheckSquare, CheckSquare,

View File

@@ -1,3 +1,6 @@
// Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
// SPDX-License-Identifier: MIT
import { Button } from "../../ui/button"; import { Button } from "../../ui/button";
import { cn } from "../../../lib/utils"; import { cn } from "../../../lib/utils";
import { import {

View File

@@ -1,3 +1,6 @@
// Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
// SPDX-License-Identifier: MIT
import { import {
CheckSquare, CheckSquare,
Code, Code,

View File

@@ -58,38 +58,27 @@
--color-sidebar-ring: var(--sidebar-ring); --color-sidebar-ring: var(--sidebar-ring);
--color-app: var(--app-background); --color-app: var(--app-background);
--color-brand: var(--brand); --color-brand: var(--brand);
--animate-aurora: --animate-aurora: aurora 8s ease-in-out infinite alternate;
aurora 8s ease-in-out infinite alternate;
@keyframes aurora { @keyframes aurora {
0% { 0% {
background-position: background-position: 0% 50%;
0% 50%; transform: rotate(-5deg) scale(0.9);
transform:
rotate(-5deg) scale(0.9);
} }
25% { 25% {
background-position: background-position: 50% 100%;
50% 100%; transform: rotate(5deg) scale(1.1);
transform:
rotate(5deg) scale(1.1);
} }
50% { 50% {
background-position: background-position: 100% 50%;
100% 50%; transform: rotate(-3deg) scale(0.95);
transform:
rotate(-3deg) scale(0.95);
} }
75% { 75% {
background-position: background-position: 50% 0%;
50% 0%; transform: rotate(3deg) scale(1.05);
transform:
rotate(3deg) scale(1.05);
} }
100% { 100% {
background-position: background-position: 0% 50%;
0% 50%; transform: rotate(-5deg) scale(0.9);
transform:
rotate(-5deg) scale(0.9);
} }
} }
} }
@@ -192,7 +181,7 @@
@apply border-border outline-ring/50; @apply border-border outline-ring/50;
} }
body { body {
@apply text-foreground; @apply bg-background text-foreground;
} }
} }
@@ -201,11 +190,7 @@ textarea {
outline: none; outline: none;
} }
@layer base { [role="button"],
* { button {
@apply border-border outline-ring/50; cursor: pointer;
} }
body {
@apply bg-background text-foreground;
}
}