Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/react/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,7 @@
"@tiptap/extension-typography": "^2.11.5",
"@tiptap/extension-underline": "^2.11.5",
"@tiptap/react": "^2.11.5",
"@tiptap/core": "^2.11.5",
"@tiptap/starter-kit": "^2.11.5",
"cva": "1.0.0-beta.3",
"embla-carousel-autoplay": "^8.5.2",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { Editor } from "@tiptap/react"
import { motion } from "framer-motion"
import { useEffect, useRef, useState } from "react"
import { EnhanceActivator } from "../Enhance"
import { SummaryActivator } from "../SummaryBlock/SummaryActivator"
import { Toolbar, ToolbarDivider } from "../Toolbar"
import {
actionType,
Expand Down Expand Up @@ -37,6 +38,8 @@ interface FooterProps {
toolbarLabels: toolbarLabels
setIsToolbarOpen: (isToolbarOpen: boolean) => void
isToolbarOpen: boolean
onGenerateSummary: () => Promise<void>
isLoadingSummary: boolean
}

const Footer = ({
Expand All @@ -55,6 +58,8 @@ const Footer = ({
disableButtons,
setIsToolbarOpen,
isToolbarOpen,
onGenerateSummary,
isLoadingSummary,
}: FooterProps) => {
const [toolbarAnimationComplete, setToolbarAnimationComplete] =
useState(false)
Expand Down Expand Up @@ -167,6 +172,76 @@ const Footer = ({
/>
</>
)}
<ToolbarDivider />
<SummaryActivator
editor={editor}
onGenerateSummary={onGenerateSummary}
isLoading={isLoadingSummary}
disableButtons={disableButtons}
hideLabel={useLittleMode}
/>
<Button
label="Load JSON"
onClick={() => {
//load hardcoded json into the editor
const json = {
type: "doc",
content: [
{
type: "paragraph",
attrs: {
textAlign: null,
},
content: [
{
type: "text",
text: "There was a time when I wandered in the dark — lost in the chaos of tangled syntax, broken builds, and tabs that betrayed me. My code was clumsy, my patience thin. But then, like a lighthouse in a storm, ",
},
{
type: "text",
marks: [
{
type: "bold",
},
],
text: "you appeared",
},
{
type: "text",
text: ". Sleek, fast, and strangely comforting, my text editor. You didn't just open files — you opened ",
},
{
type: "text",
marks: [
{
type: "italic",
},
],
text: "possibilities",
},
],
},
{
type: "summaryBlock",
attrs: {
summary: "This is a summary",
highlights: ["highlight1", "highlight2"],
nextSteps: ["step1", "step2"],
},
content: [
{
type: "paragraph",
attrs: {
textAlign: null,
},
},
],
},
],
}
editor.chain().focus().insertContent(json).run()
}}
/>
{maxCharacters && !useLittleMode && (
<p className="text-sm font-normal text-f1-foreground-secondary">
{editor.storage.characterCount.characters()}/{maxCharacters}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { Button } from "@/components/Actions/Button"
import { File } from "@/icons/app" // Or use a more appropriate icon
import { Editor } from "@tiptap/react"

interface SummaryActivatorProps {
editor: Editor
onGenerateSummary: () => Promise<void>
isLoading: boolean
disableButtons: boolean
hideLabel?: boolean
}

export const SummaryActivator = ({
editor,
onGenerateSummary,
isLoading,
disableButtons,
hideLabel,
}: SummaryActivatorProps) => {
return (
<Button
type="button"
variant="outline"
size="md"
label="Generate Summary"
icon={File} // Use appropriate icon
hideLabel={hideLabel}
onClick={(e) => {
e.preventDefault()
onGenerateSummary()
}}
disabled={disableButtons || isLoading}
data-loading={isLoading ? "true" : "false"}
/>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
import { Icon } from "@/components/Utilities/Icon"
import { ChevronDown, ChevronUp } from "@/icons/app"
import { NodeViewContent, NodeViewProps, NodeViewWrapper } from "@tiptap/react"
import { useState } from "react"
import "./styles.css"

export const SummaryBlockView: React.FC<NodeViewProps> = ({ node }) => {
const { summary, highlights, nextSteps } = node.attrs
const [collapsedSections, setCollapsedSections] = useState({
summary: false,
highlights: false,
nextSteps: false,
})

const toggleSection = (section: keyof typeof collapsedSections) => {
setCollapsedSections((prev) => ({
...prev,
[section]: !prev[section],
}))
}

// Prevent default but allow propagation for content clicks to enable text selection
const handleContentClick = (e: React.MouseEvent) => {
e.preventDefault()
}

return (
<NodeViewWrapper
className="summary-block user-select-text my-4 rounded-lg border border-f1-border bg-f1-background-secondary p-4"
contentEditable={false}
data-selectable="true"
draggable={false}
>
<div className="summary-section mb-4">
<h3
className="mb-2 flex cursor-pointer items-center gap-1 text-lg font-medium"
onClick={() => toggleSection("summary")}
>
Summary
<span className="inline-flex items-center justify-center">
<Icon
icon={collapsedSections.summary ? ChevronDown : ChevronUp}
size="sm"
/>
</span>
</h3>
{!collapsedSections.summary && (
<div
className="user-select-text summary-content"
onClick={handleContentClick}
data-selectable="true"
>
<p>{summary}</p>
</div>
)}
</div>

{highlights && highlights.length > 0 && (
<div className="highlights-section mb-4">
<h3
className="mb-2 flex cursor-pointer items-center gap-1 text-lg font-medium"
onClick={() => toggleSection("highlights")}
>
Highlights
<span className="inline-flex items-center justify-center">
<Icon
icon={collapsedSections.highlights ? ChevronDown : ChevronUp}
size="sm"
/>
</span>
</h3>
{!collapsedSections.highlights && (
<div
className="user-select-text highlights-content"
onClick={handleContentClick}
data-selectable="true"
>
<ul className="list-disc pl-5">
{highlights.map((highlight: string, index: number) => (
<li key={index}>{highlight}</li>
))}
</ul>
</div>
)}
</div>
)}

{nextSteps && nextSteps.length > 0 && (
<div className="next-steps-section">
<h3
className="mb-2 flex cursor-pointer items-center gap-1 text-lg font-medium"
onClick={() => toggleSection("nextSteps")}
>
Next Steps
<span className="inline-flex items-center justify-center">
<Icon
icon={collapsedSections.nextSteps ? ChevronDown : ChevronUp}
size="sm"
/>
</span>
</h3>
{!collapsedSections.nextSteps && (
<div
className="user-select-text next-steps-content"
onClick={handleContentClick}
data-selectable="true"
>
<ul className="list-disc pl-5">
{nextSteps.map((step: string, index: number) => (
<li key={index}>{step}</li>
))}
</ul>
</div>
)}
</div>
)}

{/* Empty NodeViewContent to satisfy Tiptap */}
<NodeViewContent className="hidden" />
</NodeViewWrapper>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
/* Summary Block Selection Styles */
.summary-block {
position: relative;
user-select: text !important;
-webkit-user-select: text !important;
-moz-user-select: text !important;
-ms-user-select: text !important;
}

/* General selection styles for all content */
.summary-block [data-selectable="true"],
.summary-block .user-select-text,
.summary-block p,
.summary-block li,
.summary-block ul,
.summary-block .summary-content,
.summary-block .highlights-content,
.summary-block .next-steps-content {
-webkit-user-select: text !important;
-moz-user-select: text !important;
-ms-user-select: text !important;
user-select: text !important;
cursor: text !important;
pointer-events: auto !important;
}

/* Fix for selection issues in Firefox */
.summary-block p::selection,
.summary-block li::selection,
.summary-block *::selection {
background: rgba(0, 0, 0, 0.1) !important;
color: inherit !important;
}

/* Disable drag-n-drop to avoid interference with selection */
.summary-block,
.summary-block * {
-webkit-user-drag: none !important;
-khtml-user-drag: none !important;
-moz-user-drag: none !important;
-o-user-drag: none !important;
user-drag: none !important;
}

/* Fix for iOS selection issues */
@supports (-webkit-touch-callout: none) {
.summary-block [data-selectable="true"],
.summary-block .user-select-text,
.summary-block p,
.summary-block li {
-webkit-touch-callout: default !important;
}
}

/* Override TipTap's default ProseMirror selection behavior */
.ProseMirror .summary-block ::selection {
background-color: rgba(0, 0, 0, 0.1) !important;
}

/* Prevent ProseMirror from interfering with text selection */
.ProseMirror:focus .summary-block:not(:focus) {
pointer-events: auto;
}

/* Add specific styles for the content sections */
.summary-content,
.highlights-content,
.next-steps-content {
isolation: isolate;
position: relative;
z-index: 1;
pointer-events: auto !important;
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Calendar } from "@/icons/app"
import type { Meta, StoryObj } from "@storybook/react"
import { Editor } from "@tiptap/react"
import { EnhancementOption, FILE_TYPES, resultType, RichTextEditor } from "."

const meta = {
component: RichTextEditor,
title: "Rich text/RichTextEditor",
Expand Down Expand Up @@ -131,7 +131,16 @@ export const Default: Story = {
primaryAction: {
action: {
label: "Add",
onClick: () => alert("Add"),
onClick: (editor?: Editor) => {
alert("Add2")
// serialize the editor content
if (editor) {
const content = editor.getHTML()
const jsonContent = editor.getJSON()
console.log(content)
console.log(jsonContent)
}
},
variant: "default",
},
subActions: [
Expand Down Expand Up @@ -185,7 +194,7 @@ export const Default: Story = {
maxCharacters: 10000,
initialEditorState: {
content:
"<p>There was a time when I wandered in the dark — lost in the chaos of tangled syntax, broken builds, and tabs that betrayed me. My code was clumsy, my patience thin. But then, like a lighthouse in a storm, <strong>you appeared</strong>. Sleek, fast, and strangely comforting, my text editor. You didnt just open files — you opened <em>possibilities</em>",
"<p>There was a time when I wandered in the dark — lost in the chaos of tangled syntax, broken builds, and tabs that betrayed me. My code was clumsy, my patience thin. But then, like a lighthouse in a storm, <strong>you appeared</strong>. Sleek, fast, and strangely comforting, my text editor. You didn't just open files — you opened <em>possibilities</em>",
},
errorConfig: {
onClose: () => alert("Close"),
Expand Down
Loading