Skip to content

Commit 5a0a2b9

Browse files
committed
feat: copy doc to clipboard
1 parent 8044099 commit 5a0a2b9

File tree

3 files changed

+59
-7
lines changed

3 files changed

+59
-7
lines changed

packages/webapp/components/TipTap/toolbar/ToolbarDesktop.tsx

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import dynamic from 'next/dynamic'
2-
import React, { useCallback, useState } from 'react'
3-
import { Link, Gear, ClearMark, Filter, Folder } from '@icons'
2+
import React, { useState } from 'react'
3+
import { Link, ClearMark } from '@icons'
44
import ToolbarButton from '@components/TipTap/toolbar/ToolbarButton'
55
import Icon from '@components/TipTap/toolbar/Icon'
66
import FilterModal from './FilterModal'
@@ -11,8 +11,16 @@ import Dropdown from '@components/ui/Dropdown'
1111
import Loading from '@components/ui/Loading'
1212
import Modal from '@components/ui/Modal'
1313
import ToolbarSkeleton from '@components/skeleton/ToolbarLoader'
14-
import { MdAddComment } from 'react-icons/md'
14+
import {
15+
MdAddComment,
16+
MdOutlineFileCopy,
17+
MdOutlinePrint,
18+
MdOutlineFolder,
19+
MdOutlineSettings,
20+
MdFilterAlt
21+
} from 'react-icons/md'
1522
import useTurnSelectedTextIntoComment from '@pages/document/hooks/useTurnSelectedTextIntoComment'
23+
import useCopyDocumentToClipboard from '@pages/document/hooks/useCopyDocumentToClipboard'
1624
import { FaDiscord } from 'react-icons/fa'
1725

1826
const ControlCenter = dynamic(() => import('@components/ControlCenter'), {
@@ -25,15 +33,15 @@ const GearModal = dynamic(() => import('./GearModal'), {
2533
const FilterButton = () => {
2634
return (
2735
<ToolbarButton tooltip="Filter Document" position="tooltip-left">
28-
<Filter fill="rgba(0,0,0,.7)" size={20} />
36+
<MdFilterAlt fill="rgba(0,0,0,.7)" size={20} />
2937
</ToolbarButton>
3038
)
3139
}
3240

3341
const GearButton = () => {
3442
return (
3543
<ToolbarButton tooltip="Document Settings" position="tooltip-left">
36-
<Gear fill="rgba(0,0,0,.7)" size={16} />
44+
<MdOutlineSettings fill="rgba(0,0,0,.7)" size={20} />
3745
</ToolbarButton>
3846
)
3947
}
@@ -47,6 +55,7 @@ const ToolbarDesktop = () => {
4755
const user = useAuthStore((state) => state.profile)
4856

4957
const { createComment } = useTurnSelectedTextIntoComment()
58+
const { copyDocumentToClipboard } = useCopyDocumentToClipboard(editor ?? null)
5059

5160
// TODO: skeleton loading
5261
if (loading || providerSyncing || !editor) return <ToolbarSkeleton />
@@ -175,13 +184,17 @@ const ToolbarDesktop = () => {
175184
</ToolbarButton>
176185

177186
<div className="divided"></div>
187+
<ToolbarButton tooltip="Copy Document" onClick={copyDocumentToClipboard}>
188+
<MdOutlineFileCopy fill="rgba(0,0,0,.7)" size={18} />
189+
</ToolbarButton>
178190

179191
<ToolbarButton onClick={() => window.print()} tooltip="Print (⌘+P)">
180-
<Icon type="Printer" size={16} />
192+
<MdOutlinePrint fill="rgba(0,0,0,.7)" size={20} />
181193
</ToolbarButton>
194+
182195
{isAuthServiceAvailable && user && (
183196
<ToolbarButton tooltip="Open" onClick={() => setModalOpen(true)}>
184-
<Folder fill="rgba(0,0,0,.7)" size={18} />
197+
<MdOutlineFolder fill="rgba(0,0,0,.7)" size={20} />
185198
</ToolbarButton>
186199
)}
187200

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
export * from './useAdjustEditorSizeForChatRoom'
22
export * from './useTOCResize'
33
export { default as useScrollSyncToc } from './useScrollSyncToc'
4+
export { default as useCopyDocumentToClipboard } from './useCopyDocumentToClipboard'
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import { useCallback } from 'react'
2+
import { Editor } from '@tiptap/react'
3+
import * as toast from '@components/toast'
4+
5+
const useCopyDocumentToClipboard = (editor: Editor | null) => {
6+
const copyDocumentToClipboard = useCallback(async () => {
7+
if (!editor) return
8+
9+
try {
10+
const htmlContent = editor.getHTML()
11+
const textContent = editor.getText()
12+
13+
// Try to copy as both HTML and plain text
14+
if (navigator.clipboard && navigator.clipboard.write) {
15+
await navigator.clipboard.write([
16+
new ClipboardItem({
17+
'text/html': new Blob([htmlContent], { type: 'text/html' }),
18+
'text/plain': new Blob([textContent], { type: 'text/plain' })
19+
})
20+
])
21+
} else {
22+
// Fallback to plain text only
23+
await navigator.clipboard.writeText(textContent)
24+
}
25+
26+
toast.Success('Document copied to clipboard')
27+
} catch (error) {
28+
console.error('Failed to copy document to clipboard:', error)
29+
// Fallback: try to select all and copy
30+
editor.commands.selectAll()
31+
document.execCommand('copy')
32+
}
33+
}, [editor])
34+
35+
return { copyDocumentToClipboard }
36+
}
37+
38+
export default useCopyDocumentToClipboard

0 commit comments

Comments
 (0)