From b1e597ee3cf2ba6758bb1f75b0b26d785fc57dd3 Mon Sep 17 00:00:00 2001 From: Arjun <6592213+arkml@users.noreply.github.com> Date: Wed, 27 May 2026 22:45:24 +0530 Subject: [PATCH] add drive button --- apps/x/apps/renderer/src/App.tsx | 69 +++++-------------- .../src/components/editor-toolbar.tsx | 55 +++++++++++++++ .../src/components/markdown-editor.tsx | 5 +- 3 files changed, 78 insertions(+), 51 deletions(-) diff --git a/apps/x/apps/renderer/src/App.tsx b/apps/x/apps/renderer/src/App.tsx index 88675b3e..6619bbe4 100644 --- a/apps/x/apps/renderer/src/App.tsx +++ b/apps/x/apps/renderer/src/App.tsx @@ -5,7 +5,7 @@ import { RunEvent, ListRunsResponse } from '@x/shared/src/runs.js'; import type { LanguageModelUsage, ToolUIPart } from 'ai'; import './App.css' import z from 'zod'; -import { Bug, CheckIcon, LoaderIcon, PanelLeftIcon, ArrowRight, MessageSquare, ChevronLeftIcon, ChevronRightIcon, MoreHorizontal, Plus, HistoryIcon, DownloadIcon, UploadCloud } from 'lucide-react'; +import { Bug, CheckIcon, LoaderIcon, PanelLeftIcon, ArrowRight, MessageSquare, ChevronLeftIcon, ChevronRightIcon, MoreHorizontal, Plus, HistoryIcon } from 'lucide-react'; import { cn } from '@/lib/utils'; import { MarkdownEditor, type MarkdownEditorHandle } from './components/markdown-editor'; import { ChatSidebar } from './components/chat-sidebar'; @@ -1431,8 +1431,8 @@ function App() { setEditorContent(markdown) }, [setEditorCacheForPath]) - const syncGoogleDocDown = useCallback(async () => { - const path = selectedPathRef.current + const syncGoogleDocDown = useCallback(async (targetPath?: string) => { + const path = targetPath ?? selectedPathRef.current if (!path || !path.startsWith('knowledge/') || !path.endsWith('.md')) return setGoogleDocSyncDirection('down') @@ -1450,8 +1450,8 @@ function App() { } }, [markRecentLocalMarkdownWrite, reloadMarkdownFileIntoEditor]) - const syncGoogleDocUp = useCallback(async () => { - const path = selectedPathRef.current + const syncGoogleDocUp = useCallback(async (targetPath?: string) => { + const path = targetPath ?? selectedPathRef.current if (!path || !path.startsWith('knowledge/') || !path.endsWith('.md')) return const body = editorContentByPathRef.current.get(path) ?? editorContentRef.current @@ -5368,10 +5368,6 @@ function App() { } return markdownTabs }, [fileTabs, selectedPath]) - const selectedLinkedGoogleDoc = React.useMemo(() => { - if (!selectedPath?.startsWith('knowledge/') || !selectedPath.endsWith('.md')) return null - return parseLinkedGoogleDocFrontmatter(frontmatterByPathRef.current.get(selectedPath) ?? null) - }, [selectedPath, editorContent, editorContentByPath]) return ( { @@ -5481,46 +5477,6 @@ function App() { ) : null} )} - {selectedLinkedGoogleDoc && ( - <> - - - - - Sync down from Google Doc - - - - - - Sync up to Google Doc - - - )} {selectedPath && selectedPath.startsWith('knowledge/') && selectedPath.endsWith('.md') && ( @@ -5794,6 +5750,8 @@ function App() { ? tab.id === activeFileTabId || tab.path === selectedPath : tab.path === selectedPath const isViewingHistory = viewingHistoricalVersion && isActive && versionHistoryPath === tab.path + const tabFrontmatter = frontmatterByPathRef.current.get(tab.path) ?? null + const linkedGoogleDoc = parseLinkedGoogleDocFrontmatter(tabFrontmatter) const tabContent = isViewingHistory ? viewingHistoricalVersion.content : editorContentByPath[tab.path] @@ -5824,7 +5782,7 @@ function App() { wikiLinks={wikiLinkConfig} onImageUpload={handleImageUpload} editorSessionKey={editorSessionByTabId[tab.id] ?? 0} - frontmatter={frontmatterByPathRef.current.get(tab.path) ?? null} + frontmatter={tabFrontmatter} onFrontmatterChange={(newRaw) => { frontmatterByPathRef.current.set(tab.path, newRaw) // Write updated frontmatter to disk immediately @@ -5846,6 +5804,17 @@ function App() { } }} editable={!isViewingHistory} + googleDoc={linkedGoogleDoc && !isViewingHistory ? { + title: linkedGoogleDoc.title, + isSyncing: isActive ? googleDocSyncDirection : null, + onOpen: () => { + if (linkedGoogleDoc.url) { + window.open(linkedGoogleDoc.url, '_blank') + } + }, + onSyncDown: () => { void syncGoogleDocDown(tab.path) }, + onSyncUp: () => { void syncGoogleDocUp(tab.path) }, + } : undefined} onExport={async (format) => { const markdown = tabContent const title = getBaseName(tab.path) diff --git a/apps/x/apps/renderer/src/components/editor-toolbar.tsx b/apps/x/apps/renderer/src/components/editor-toolbar.tsx index b48cee49..0475e268 100644 --- a/apps/x/apps/renderer/src/components/editor-toolbar.tsx +++ b/apps/x/apps/renderer/src/components/editor-toolbar.tsx @@ -26,15 +26,21 @@ import { Trash2Icon, ImageIcon, DownloadIcon, + ChevronDownIcon, FileTextIcon, FileIcon, FileTypeIcon, + CloudDownloadIcon, + LoaderIcon, + TriangleIcon, + UploadCloudIcon, Radio, } from 'lucide-react' import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, + DropdownMenuSeparator, DropdownMenuTrigger, } from '@/components/ui/dropdown-menu' @@ -45,6 +51,15 @@ interface EditorToolbarProps { onExport?: (format: 'md' | 'pdf' | 'docx') => void onOpenLiveNote?: () => void liveState?: LivePillState + googleDoc?: GoogleDocToolbarState +} + +export interface GoogleDocToolbarState { + title: string + isSyncing?: 'up' | 'down' | null + onOpen: () => void + onSyncDown: () => void + onSyncUp: () => void } export type LivePillVariant = 'passive' | 'idle' | 'running' | 'error' @@ -67,6 +82,7 @@ export function EditorToolbar({ onExport, onOpenLiveNote, liveState, + googleDoc, }: EditorToolbarProps) { const [linkUrl, setLinkUrl] = useState('') const [isLinkPopoverOpen, setIsLinkPopoverOpen] = useState(false) @@ -404,6 +420,45 @@ export function EditorToolbar({ )} + {googleDoc && ( + <> +
+ + + + + + + + Open Google Doc + + + + + Sync down + + + + Sync up + + + + + )} + {/* Live Note pill — pushed to far right */} {onOpenLiveNote && liveState && (