From 2cd6094b44c81b35d0598cbce5b980b815a4ed12 Mon Sep 17 00:00:00 2001 From: Heesu Suh Date: Wed, 23 Oct 2024 16:11:01 +0900 Subject: [PATCH 1/6] show file preview on MentionableBadge & go to file on click --- package-lock.json | 35 ++++++++- package.json | 3 +- .../chat-view/chat-input/MentionableBadge.tsx | 78 +++++++++++++++---- styles.css | 14 ++++ 4 files changed, 114 insertions(+), 16 deletions(-) diff --git a/package-lock.json b/package-lock.json index 2b929ae..de89463 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,17 +1,18 @@ { "name": "obsidian-smart-composer", - "version": "0.1.1", + "version": "0.1.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "obsidian-smart-composer", - "version": "0.1.1", + "version": "0.1.2", "license": "MIT", "dependencies": { "@anthropic-ai/sdk": "^0.27.3", "@lexical/react": "^0.17.1", "@radix-ui/react-dropdown-menu": "^2.1.2", + "@radix-ui/react-hover-card": "^1.1.2", "@tanstack/react-query": "^5.56.2", "diff": "^7.0.0", "fuzzysort": "^3.1.0", @@ -1624,6 +1625,36 @@ } } }, + "node_modules/@radix-ui/react-hover-card": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-hover-card/-/react-hover-card-1.1.2.tgz", + "integrity": "sha512-Y5w0qGhysvmqsIy6nQxaPa6mXNKznfoGjOfBgzOjocLxr2XlSjqBMYQQL+FfyogsMuX+m8cZyQGYhJxvxUzO4w==", + "dependencies": { + "@radix-ui/primitive": "1.1.0", + "@radix-ui/react-compose-refs": "1.1.0", + "@radix-ui/react-context": "1.1.1", + "@radix-ui/react-dismissable-layer": "1.1.1", + "@radix-ui/react-popper": "1.2.0", + "@radix-ui/react-portal": "1.1.2", + "@radix-ui/react-presence": "1.1.1", + "@radix-ui/react-primitive": "2.0.0", + "@radix-ui/react-use-controllable-state": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-id": { "version": "1.1.0", "license": "MIT", diff --git a/package.json b/package.json index 922121b..2706bc5 100644 --- a/package.json +++ b/package.json @@ -45,6 +45,7 @@ "@anthropic-ai/sdk": "^0.27.3", "@lexical/react": "^0.17.1", "@radix-ui/react-dropdown-menu": "^2.1.2", + "@radix-ui/react-hover-card": "^1.1.2", "@tanstack/react-query": "^5.56.2", "diff": "^7.0.0", "fuzzysort": "^3.1.0", @@ -62,4 +63,4 @@ "remark-gfm": "^4.0.0", "uuid": "^10.0.0" } -} \ No newline at end of file +} diff --git a/src/components/chat-view/chat-input/MentionableBadge.tsx b/src/components/chat-view/chat-input/MentionableBadge.tsx index 1e6a6ac..4896912 100644 --- a/src/components/chat-view/chat-input/MentionableBadge.tsx +++ b/src/components/chat-view/chat-input/MentionableBadge.tsx @@ -1,6 +1,10 @@ +import * as HoverCard from '@radix-ui/react-hover-card' +import { useQuery } from '@tanstack/react-query' import { X } from 'lucide-react' +import { MarkdownView } from 'obsidian' import { PropsWithChildren } from 'react' +import { useApp } from '../../../contexts/app-context' import { Mentionable, MentionableBlock, @@ -34,12 +38,44 @@ function FileBadge({ mentionable: MentionableFile onDelete: () => void }) { + const app = useApp() + const { data: fileContent } = useQuery({ + queryKey: ['file', mentionable.file.path], + queryFn: () => app.vault.cachedRead(mentionable.file), + }) + + const handleClick = () => { + const existingLeaf = app.workspace + .getLeavesOfType('markdown') + .find( + (leaf) => + leaf.view instanceof MarkdownView && + leaf.view.file?.path === mentionable.file.path, + ) + + if (existingLeaf) { + app.workspace.setActiveLeaf(existingLeaf, { focus: true }) + } else { + const leaf = app.workspace.getLeaf('tab') + leaf.openFile(mentionable.file) + } + } + return ( - -
- {mentionable.file.name} -
-
+ + + +
+ {mentionable.file.name} +
+
+
+ + + {fileContent} + + +
) } @@ -50,15 +86,31 @@ function CurrentFileBadge({ mentionable: MentionableCurrentFile onDelete: () => void }) { + const app = useApp() + const { data: fileContent } = useQuery({ + queryKey: ['file', mentionable.file?.path], + queryFn: () => + mentionable.file ? app.vault.cachedRead(mentionable?.file) : null, + }) + return mentionable.file ? ( - -
- {`${mentionable.file.name}`} -
-
- {' (Current File)'} -
-
+ + + +
+ {`${mentionable.file.name}`} +
+
+ {' (Current File)'} +
+
+
+ + + {fileContent} + + +
) : null } diff --git a/styles.css b/styles.css index abce5a2..6c24f7f 100644 --- a/styles.css +++ b/styles.css @@ -252,6 +252,20 @@ button:not(.clickable-icon).smtcmp-chat-list-dropdown { color: var(--color-base-50); } +.smtcmp-chat-mentionable-hover-content { + background-color: var(--background-primary); + border-radius: var(--radius-m); + border: 1px solid var(--background-modifier-border); + padding: var(--size-4-2); + box-shadow: var(--shadow-s); + white-space: pre-line; + max-width: 300px; + z-index: 1000; + max-height: 300px; + overflow-y: auto; + font-size: var(--font-ui-small); +} + /** * ChatUserInput */ From b38d7bb61c99a6997527c697c0c45dd245d1984e Mon Sep 17 00:00:00 2001 From: Heesu Suh Date: Wed, 23 Oct 2024 20:46:28 +0900 Subject: [PATCH 2/6] fix UI & show mentionable content on added --- package-lock.json | 31 ----- package.json | 1 - src/components/chat-view/Chat.tsx | 17 +++ .../chat-view/chat-input/ChatUserInput.tsx | 97 ++++++++++++++ .../chat-view/chat-input/MentionableBadge.tsx | 121 ++++++++---------- src/utils/obsidian.ts | 4 +- styles.css | 20 +-- 7 files changed, 178 insertions(+), 113 deletions(-) diff --git a/package-lock.json b/package-lock.json index de89463..c9d2909 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,7 +12,6 @@ "@anthropic-ai/sdk": "^0.27.3", "@lexical/react": "^0.17.1", "@radix-ui/react-dropdown-menu": "^2.1.2", - "@radix-ui/react-hover-card": "^1.1.2", "@tanstack/react-query": "^5.56.2", "diff": "^7.0.0", "fuzzysort": "^3.1.0", @@ -1625,36 +1624,6 @@ } } }, - "node_modules/@radix-ui/react-hover-card": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-hover-card/-/react-hover-card-1.1.2.tgz", - "integrity": "sha512-Y5w0qGhysvmqsIy6nQxaPa6mXNKznfoGjOfBgzOjocLxr2XlSjqBMYQQL+FfyogsMuX+m8cZyQGYhJxvxUzO4w==", - "dependencies": { - "@radix-ui/primitive": "1.1.0", - "@radix-ui/react-compose-refs": "1.1.0", - "@radix-ui/react-context": "1.1.1", - "@radix-ui/react-dismissable-layer": "1.1.1", - "@radix-ui/react-popper": "1.2.0", - "@radix-ui/react-portal": "1.1.2", - "@radix-ui/react-presence": "1.1.1", - "@radix-ui/react-primitive": "2.0.0", - "@radix-ui/react-use-controllable-state": "1.1.0" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, "node_modules/@radix-ui/react-id": { "version": "1.1.0", "license": "MIT", diff --git a/package.json b/package.json index 2706bc5..ec9f3c7 100644 --- a/package.json +++ b/package.json @@ -45,7 +45,6 @@ "@anthropic-ai/sdk": "^0.27.3", "@lexical/react": "^0.17.1", "@radix-ui/react-dropdown-menu": "^2.1.2", - "@radix-ui/react-hover-card": "^1.1.2", "@tanstack/react-query": "^5.56.2", "diff": "^7.0.0", "fuzzysort": "^3.1.0", diff --git a/src/components/chat-view/Chat.tsx b/src/components/chat-view/Chat.tsx index fb4564c..36bc35e 100644 --- a/src/components/chat-view/Chat.tsx +++ b/src/components/chat-view/Chat.tsx @@ -30,6 +30,10 @@ import { LLMAPIKeyInvalidException, LLMAPIKeyNotSetException, } from '../../utils/llm/exception' +import { + getMentionableKey, + serializeMentionable, +} from '../../utils/mentionable' import { readTFileContent } from '../../utils/obsidian' import { parseRequestMessages } from '../../utils/prompt' @@ -87,6 +91,16 @@ const Chat = forwardRef((props, ref) => { } return newMessage }) + const [addedBlockKey, setAddedBlockKey] = useState( + props.selectedBlock + ? getMentionableKey( + serializeMentionable({ + type: 'block', + ...props.selectedBlock, + }), + ) + : null, + ) const [chatMessages, setChatMessages] = useState([]) const [focusedMessageId, setFocusedMessageId] = useState(null) const [currentConversationId, setCurrentConversationId] = @@ -356,6 +370,8 @@ const Chat = forwardRef((props, ref) => { ...data, } + setAddedBlockKey(getMentionableKey(serializeMentionable(mentionable))) + if (focusedMessageId === inputMessage.id) { setInputMessage((prevInputMessage) => ({ ...prevInputMessage, @@ -498,6 +514,7 @@ const Chat = forwardRef((props, ref) => { })) }} autoFocus + addedBlockKey={addedBlockKey} /> ) diff --git a/src/components/chat-view/chat-input/ChatUserInput.tsx b/src/components/chat-view/chat-input/ChatUserInput.tsx index 99233cd..79e5590 100644 --- a/src/components/chat-view/chat-input/ChatUserInput.tsx +++ b/src/components/chat-view/chat-input/ChatUserInput.tsx @@ -9,13 +9,16 @@ import { LexicalErrorBoundary } from '@lexical/react/LexicalErrorBoundary' import { HistoryPlugin } from '@lexical/react/LexicalHistoryPlugin' import { OnChangePlugin } from '@lexical/react/LexicalOnChangePlugin' import { RichTextPlugin } from '@lexical/react/LexicalRichTextPlugin' +import { useQuery } from '@tanstack/react-query' import { $nodesOfType, LexicalEditor, SerializedEditorState } from 'lexical' +import { MarkdownView } from 'obsidian' import { forwardRef, useCallback, useEffect, useImperativeHandle, useRef, + useState, } from 'react' import { deserializeMentionable, @@ -26,6 +29,7 @@ import { useApp } from '../../../contexts/app-context' import { Mentionable, SerializedMentionable } from '../../../types/mentionable' import { fuzzySearch } from '../../../utils/fuzzy-search' import { getMentionableKey } from '../../../utils/mentionable' +import { readTFileContent } from '../../../utils/obsidian' import MentionableBadge from './MentionableBadge' import { ModelSelect } from './ModelSelect' @@ -53,6 +57,7 @@ export type ChatUserInputProps = { mentionables: Mentionable[] setMentionables: (mentionables: Mentionable[]) => void autoFocus?: boolean + addedBlockKey?: string | null } const ChatUserInput = forwardRef( @@ -65,6 +70,7 @@ const ChatUserInput = forwardRef( mentionables, setMentionables, autoFocus = false, + addedBlockKey, }, ref, ) => { @@ -74,6 +80,16 @@ const ChatUserInput = forwardRef( const contentEditableRef = useRef(null) const updaterRef = useRef(null) + const [displayedMentionableKey, setDisplayedMentionableKey] = useState< + string | null + >(addedBlockKey ?? null) + + useEffect(() => { + if (addedBlockKey) { + setDisplayedMentionableKey(addedBlockKey) + } + }, [addedBlockKey]) + useImperativeHandle(ref, () => ({ focus: () => { contentEditableRef.current?.focus() @@ -120,6 +136,7 @@ const ChatUserInput = forwardRef( return } setMentionables([...mentionables, mentionable]) + setDisplayedMentionableKey(mentionableKey) } const handleMentionNodeMutation = ( @@ -196,6 +213,48 @@ const ChatUserInput = forwardRef( }) } + const { data: fileContent } = useQuery({ + queryKey: [ + 'file', + displayedMentionableKey, + mentionables.map((m) => getMentionableKey(serializeMentionable(m))), // should be updated when mentionables change (especially on delete) + ], + queryFn: async () => { + if (!displayedMentionableKey) return null + + const displayedMentionable = mentionables.find( + (m) => + getMentionableKey(serializeMentionable(m)) === + displayedMentionableKey, + ) + + if (!displayedMentionable) return null + + if ( + displayedMentionable.type === 'file' || + displayedMentionable.type === 'current-file' + ) { + if (!displayedMentionable.file) return null + return await readTFileContent(displayedMentionable.file, app.vault) + } else if (displayedMentionable.type === 'block') { + const fileContent = await readTFileContent( + displayedMentionable.file, + app.vault, + ) + + return fileContent + .split('\n') + .slice( + displayedMentionable.startLine - 1, + displayedMentionable.endLine, + ) + .join('\n') + } + + return null + }, + }) + return (
{mentionables.length > 0 && ( @@ -205,11 +264,49 @@ const ChatUserInput = forwardRef( key={getMentionableKey(serializeMentionable(m))} mentionable={m} onDelete={() => handleMentionableDelete(m)} + onClick={() => { + const mentionableKey = getMentionableKey( + serializeMentionable(m), + ) + if ( + (m.type === 'current-file' || + m.type === 'file' || + m.type === 'block') && + m.file && + mentionableKey === displayedMentionableKey + ) { + setDisplayedMentionableKey(null) + + // Open the file if clicked again + const existingLeaf = app.workspace + .getLeavesOfType('markdown') + .find( + (leaf) => + leaf.view instanceof MarkdownView && + leaf.view.file?.path === m.file?.path, + ) + + if (existingLeaf) { + app.workspace.setActiveLeaf(existingLeaf, { focus: true }) + } else { + const leaf = app.workspace.getLeaf('tab') + leaf.openFile(m.file) + } + } else { + setDisplayedMentionableKey(mentionableKey) + } + }} /> ))}
)} + {fileContent && ( +
+ {fileContent} +
+ )} + {/* There was two approach to make mentionable node copy and pasteable. diff --git a/src/components/chat-view/chat-input/MentionableBadge.tsx b/src/components/chat-view/chat-input/MentionableBadge.tsx index 4896912..63c7551 100644 --- a/src/components/chat-view/chat-input/MentionableBadge.tsx +++ b/src/components/chat-view/chat-input/MentionableBadge.tsx @@ -1,10 +1,6 @@ -import * as HoverCard from '@radix-ui/react-hover-card' -import { useQuery } from '@tanstack/react-query' import { X } from 'lucide-react' -import { MarkdownView } from 'obsidian' import { PropsWithChildren } from 'react' -import { useApp } from '../../../contexts/app-context' import { Mentionable, MentionableBlock, @@ -15,15 +11,20 @@ import { function BadgeBase({ children, onDelete, + onClick, }: PropsWithChildren<{ onDelete: () => void + onClick: () => void }>) { return ( -
+
{children}
{ + evt.stopPropagation() + onDelete() + }} >
@@ -34,95 +35,53 @@ function BadgeBase({ function FileBadge({ mentionable, onDelete, + onClick, }: { mentionable: MentionableFile onDelete: () => void + onClick: () => void }) { - const app = useApp() - const { data: fileContent } = useQuery({ - queryKey: ['file', mentionable.file.path], - queryFn: () => app.vault.cachedRead(mentionable.file), - }) - - const handleClick = () => { - const existingLeaf = app.workspace - .getLeavesOfType('markdown') - .find( - (leaf) => - leaf.view instanceof MarkdownView && - leaf.view.file?.path === mentionable.file.path, - ) - - if (existingLeaf) { - app.workspace.setActiveLeaf(existingLeaf, { focus: true }) - } else { - const leaf = app.workspace.getLeaf('tab') - leaf.openFile(mentionable.file) - } - } - return ( - - - -
- {mentionable.file.name} -
-
-
- - - {fileContent} - - -
+ +
+ {mentionable.file.name} +
+
) } function CurrentFileBadge({ mentionable, onDelete, + onClick, }: { mentionable: MentionableCurrentFile onDelete: () => void + onClick: () => void }) { - const app = useApp() - const { data: fileContent } = useQuery({ - queryKey: ['file', mentionable.file?.path], - queryFn: () => - mentionable.file ? app.vault.cachedRead(mentionable?.file) : null, - }) - return mentionable.file ? ( - - - -
- {`${mentionable.file.name}`} -
-
- {' (Current File)'} -
-
-
- - - {fileContent} - - -
+ +
+ {`${mentionable.file.name}`} +
+
+ {' (Current File)'} +
+
) : null } function BlockBadge({ mentionable, onDelete, + onClick, }: { mentionable: MentionableBlock onDelete: () => void + onClick: () => void }) { return ( - +
{`${mentionable.file.name}`}
@@ -136,16 +95,36 @@ function BlockBadge({ export default function MentionableBadge({ mentionable, onDelete, + onClick, }: { mentionable: Mentionable onDelete: () => void + onClick: () => void }) { switch (mentionable.type) { case 'file': - return + return ( + + ) case 'current-file': - return + return ( + + ) case 'block': - return + return ( + + ) } } diff --git a/src/utils/obsidian.ts b/src/utils/obsidian.ts index d12d9d7..ad10604 100644 --- a/src/utils/obsidian.ts +++ b/src/utils/obsidian.ts @@ -39,8 +39,8 @@ export async function getMentionableBlockData( return { content: selectionContent, file, - startLine, - endLine, + startLine: startLine + 1, // +1 because startLine is 0-indexed + endLine: endLine + 1, // +1 because startLine is 0-indexed } } diff --git a/styles.css b/styles.css index 6c24f7f..997437f 100644 --- a/styles.css +++ b/styles.css @@ -223,6 +223,11 @@ button:not(.clickable-icon).smtcmp-chat-list-dropdown { white-space: nowrap; } +.smtcmp-chat-user-input-file-badge:hover { + background-color: var(--background-modifier-hover); + cursor: pointer; +} + .smtcmp-chat-user-input-file-badge-delete { cursor: pointer; display: flex; @@ -252,18 +257,17 @@ button:not(.clickable-icon).smtcmp-chat-list-dropdown { color: var(--color-base-50); } -.smtcmp-chat-mentionable-hover-content { +/* + * TODO: Fix style + */ +.smtcmp-chat-user-input-file-content-preview { background-color: var(--background-primary); - border-radius: var(--radius-m); + border-radius: var(--radius-s); border: 1px solid var(--background-modifier-border); padding: var(--size-4-2); - box-shadow: var(--shadow-s); - white-space: pre-line; - max-width: 300px; - z-index: 1000; - max-height: 300px; + max-height: 400px; overflow-y: auto; - font-size: var(--font-ui-small); + white-space: pre-line; } /** From e3f1f3e7db34907cde1b27034d5b1c1541987b1e Mon Sep 17 00:00:00 2001 From: Heesu Suh Date: Thu, 24 Oct 2024 00:59:49 +0900 Subject: [PATCH 3/6] use SyntaxHighlighterWrapper for mentionable preview --- .../chat-view/chat-input/ChatUserInput.tsx | 12 +++++++++++- styles.css | 3 +-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/components/chat-view/chat-input/ChatUserInput.tsx b/src/components/chat-view/chat-input/ChatUserInput.tsx index 05dd9cb..12c309d 100644 --- a/src/components/chat-view/chat-input/ChatUserInput.tsx +++ b/src/components/chat-view/chat-input/ChatUserInput.tsx @@ -26,10 +26,12 @@ import { } from 'src/utils/mentionable' import { useApp } from '../../../contexts/app-context' +import { useDarkModeContext } from '../../../contexts/dark-mode-context' import { Mentionable, SerializedMentionable } from '../../../types/mentionable' import { fuzzySearch } from '../../../utils/fuzzy-search' import { getMentionableKey } from '../../../utils/mentionable' import { readTFileContent } from '../../../utils/obsidian' +import { MemoizedSyntaxHighlighterWrapper } from '../SyntaxHighlighterWrapper' import MentionableBadge from './MentionableBadge' import { ModelSelect } from './ModelSelect' @@ -76,6 +78,7 @@ const ChatUserInput = forwardRef( ref, ) => { const app = useApp() + const { isDarkMode } = useDarkModeContext() const editorRef = useRef(null) const contentEditableRef = useRef(null) @@ -309,7 +312,14 @@ const ChatUserInput = forwardRef( {fileContent && (
- {fileContent} + + {fileContent} +
)} diff --git a/styles.css b/styles.css index e9745b9..2dfcac8 100644 --- a/styles.css +++ b/styles.css @@ -304,8 +304,7 @@ button:not(.clickable-icon).smtcmp-chat-list-dropdown { background-color: var(--background-primary); border-radius: var(--radius-s); border: 1px solid var(--background-modifier-border); - padding: var(--size-4-2); - max-height: 400px; + max-height: 350px; overflow-y: auto; white-space: pre-line; } From a583abbc717eff37b24dbe041930f932bc96c2c9 Mon Sep 17 00:00:00 2001 From: Heesu Suh Date: Thu, 24 Oct 2024 10:36:32 +0900 Subject: [PATCH 4/6] do not reset displayed mentionable on click again --- src/components/chat-view/chat-input/ChatUserInput.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/components/chat-view/chat-input/ChatUserInput.tsx b/src/components/chat-view/chat-input/ChatUserInput.tsx index 12c309d..4bb3050 100644 --- a/src/components/chat-view/chat-input/ChatUserInput.tsx +++ b/src/components/chat-view/chat-input/ChatUserInput.tsx @@ -284,8 +284,6 @@ const ChatUserInput = forwardRef( m.file && mentionableKey === displayedMentionableKey ) { - setDisplayedMentionableKey(null) - // Open the file if clicked again const existingLeaf = app.workspace .getLeavesOfType('markdown') From d64db779f01acd083b61f523140d027bec11e00d Mon Sep 17 00:00:00 2001 From: Heesu Suh Date: Thu, 24 Oct 2024 11:29:03 +0900 Subject: [PATCH 5/6] extract openMarkdownFile as a separate util --- .../chat-view/MarkdownReferenceBlock.tsx | 25 ++-------------- .../chat-view/chat-input/ChatUserInput.tsx | 19 ++---------- src/utils/obsidian.ts | 30 +++++++++++++++++++ styles.css | 3 -- 4 files changed, 34 insertions(+), 43 deletions(-) diff --git a/src/components/chat-view/MarkdownReferenceBlock.tsx b/src/components/chat-view/MarkdownReferenceBlock.tsx index bad20c1..fb8f602 100644 --- a/src/components/chat-view/MarkdownReferenceBlock.tsx +++ b/src/components/chat-view/MarkdownReferenceBlock.tsx @@ -1,9 +1,8 @@ -import { MarkdownView } from 'obsidian' import { PropsWithChildren, useEffect, useMemo, useState } from 'react' import { useApp } from '../../contexts/app-context' import { useDarkModeContext } from '../../contexts/dark-mode-context' -import { readTFileContent } from '../../utils/obsidian' +import { openMarkdownFile, readTFileContent } from '../../utils/obsidian' import { MemoizedSyntaxHighlighterWrapper } from './SyntaxHighlighterWrapper' @@ -45,27 +44,7 @@ export default function MarkdownReferenceBlock({ }, [filename, startLine, endLine, app.vault]) const handleClick = () => { - const file = app.vault.getFileByPath(filename) - if (!file) return - - const existingLeaf = app.workspace - .getLeavesOfType('markdown') - .find( - (leaf) => - leaf.view instanceof MarkdownView && - leaf.view.file?.path === file.path, - ) - - if (existingLeaf) { - app.workspace.setActiveLeaf(existingLeaf, { focus: true }) - const view = existingLeaf.view as MarkdownView - view.setEphemeralState({ line: startLine }) - } else { - const leaf = app.workspace.getLeaf('tab') - leaf.openFile(file, { - eState: { line: startLine }, - }) - } + openMarkdownFile(app, filename, startLine) } // TODO: Update styles diff --git a/src/components/chat-view/chat-input/ChatUserInput.tsx b/src/components/chat-view/chat-input/ChatUserInput.tsx index 4bb3050..0a612ca 100644 --- a/src/components/chat-view/chat-input/ChatUserInput.tsx +++ b/src/components/chat-view/chat-input/ChatUserInput.tsx @@ -11,7 +11,6 @@ import { OnChangePlugin } from '@lexical/react/LexicalOnChangePlugin' import { RichTextPlugin } from '@lexical/react/LexicalRichTextPlugin' import { useQuery } from '@tanstack/react-query' import { $nodesOfType, LexicalEditor, SerializedEditorState } from 'lexical' -import { MarkdownView } from 'obsidian' import { forwardRef, useCallback, @@ -30,7 +29,7 @@ import { useDarkModeContext } from '../../../contexts/dark-mode-context' import { Mentionable, SerializedMentionable } from '../../../types/mentionable' import { fuzzySearch } from '../../../utils/fuzzy-search' import { getMentionableKey } from '../../../utils/mentionable' -import { readTFileContent } from '../../../utils/obsidian' +import { openMarkdownFile, readTFileContent } from '../../../utils/obsidian' import { MemoizedSyntaxHighlighterWrapper } from '../SyntaxHighlighterWrapper' import MentionableBadge from './MentionableBadge' @@ -284,21 +283,7 @@ const ChatUserInput = forwardRef( m.file && mentionableKey === displayedMentionableKey ) { - // Open the file if clicked again - const existingLeaf = app.workspace - .getLeavesOfType('markdown') - .find( - (leaf) => - leaf.view instanceof MarkdownView && - leaf.view.file?.path === m.file?.path, - ) - - if (existingLeaf) { - app.workspace.setActiveLeaf(existingLeaf, { focus: true }) - } else { - const leaf = app.workspace.getLeaf('tab') - leaf.openFile(m.file) - } + openMarkdownFile(app, m.file.path) } else { setDisplayedMentionableKey(mentionableKey) } diff --git a/src/utils/obsidian.ts b/src/utils/obsidian.ts index 7e2986b..d0e7a61 100644 --- a/src/utils/obsidian.ts +++ b/src/utils/obsidian.ts @@ -92,3 +92,33 @@ export function calculateFileDistance( return distance } + +export function openMarkdownFile( + app: App, + filePath: string, + startLine?: number, +) { + const file = app.vault.getFileByPath(filePath) + if (!file) return + + const existingLeaf = app.workspace + .getLeavesOfType('markdown') + .find( + (leaf) => + leaf.view instanceof MarkdownView && leaf.view.file?.path === file.path, + ) + + if (existingLeaf) { + app.workspace.setActiveLeaf(existingLeaf, { focus: true }) + + if (startLine) { + const view = existingLeaf.view as MarkdownView + view.setEphemeralState({ line: startLine }) + } + } else { + const leaf = app.workspace.getLeaf('tab') + leaf.openFile(file, { + eState: startLine ? { line: startLine } : undefined, + }) + } +} diff --git a/styles.css b/styles.css index 2dfcac8..d589b19 100644 --- a/styles.css +++ b/styles.css @@ -297,9 +297,6 @@ button:not(.clickable-icon).smtcmp-chat-list-dropdown { color: var(--color-base-50); } -/* - * TODO: Fix style - */ .smtcmp-chat-user-input-file-content-preview { background-color: var(--background-primary); border-radius: var(--radius-s); From 4d9e8bef259c032c54a5e34a25ae56c02a31e34b Mon Sep 17 00:00:00 2001 From: Heesu Suh Date: Thu, 24 Oct 2024 11:30:28 +0900 Subject: [PATCH 6/6] add comment --- src/components/chat-view/chat-input/ChatUserInput.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/chat-view/chat-input/ChatUserInput.tsx b/src/components/chat-view/chat-input/ChatUserInput.tsx index 0a612ca..17dc3ee 100644 --- a/src/components/chat-view/chat-input/ChatUserInput.tsx +++ b/src/components/chat-view/chat-input/ChatUserInput.tsx @@ -283,6 +283,7 @@ const ChatUserInput = forwardRef( m.file && mentionableKey === displayedMentionableKey ) { + // open file on click again openMarkdownFile(app, m.file.path) } else { setDisplayedMentionableKey(mentionableKey)