From d1b81dd703f4578cdb923fcd31aedc6fc9c498fe Mon Sep 17 00:00:00 2001 From: duanfuxiang Date: Tue, 29 Apr 2025 11:25:44 +0800 Subject: [PATCH] add system preview --- src/PreviewView.tsx | 62 +++++++++++ src/components/chat-view/CustomModeView.tsx | 54 ++++++++- .../chat-view/chat-input/ModeSelect.tsx | 2 +- .../preview-view/PreviewViewRoot.tsx | 105 ++++++++++++++++++ src/constants.ts | 1 + src/main.ts | 4 +- src/utils/obsidian.ts | 4 +- src/utils/prompt-generator.ts | 2 +- 8 files changed, 227 insertions(+), 7 deletions(-) create mode 100644 src/PreviewView.tsx create mode 100644 src/components/preview-view/PreviewViewRoot.tsx diff --git a/src/PreviewView.tsx b/src/PreviewView.tsx new file mode 100644 index 0000000..13430ca --- /dev/null +++ b/src/PreviewView.tsx @@ -0,0 +1,62 @@ +import { TFile, View, WorkspaceLeaf } from 'obsidian' +import { Root, createRoot } from 'react-dom/client' + +import PreviewViewRoot from './components/preview-view/PreviewViewRoot' +import { PREVIEW_VIEW_TYPE } from './constants' +import { AppProvider } from './contexts/AppContext' + +export type PreviewViewState = { + content: string + title?: string + file?: TFile + onClose?: () => void +} + +export class PreviewView extends View { + private root: Root | null = null + private state: PreviewViewState | null = null + + constructor(leaf: WorkspaceLeaf) { + super(leaf) + } + + getViewType() { + return PREVIEW_VIEW_TYPE + } + + getDisplayText() { + if (this.state?.title) { + return `Preview: ${this.state.title}` + } + return `Preview: ${this.state?.file?.name ?? 'Markdown'}` + } + + async setState(state: PreviewViewState) { + this.state = state + // Should render here because onOpen is called before setState + this.render() + } + + async onOpen() { + this.root = createRoot(this.containerEl) + } + + async onClose() { + if (this.state?.onClose) { + this.state.onClose() + } + this.root?.unmount() + } + + async render() { + if (!this.root || !this.state) return + this.root.render( + + this.leaf.detach()} + /> + , + ) + } +} diff --git a/src/components/chat-view/CustomModeView.tsx b/src/components/chat-view/CustomModeView.tsx index 9e6f2ba..d17a1e2 100644 --- a/src/components/chat-view/CustomModeView.tsx +++ b/src/components/chat-view/CustomModeView.tsx @@ -1,11 +1,19 @@ import { ChevronDown, ChevronRight, Plus, Trash2, Undo2 } from 'lucide-react'; -import React, { useEffect, useState } from 'react'; +import { getLanguage } from 'obsidian'; +import React, { useEffect, useMemo, useState } from 'react'; +import { PREVIEW_VIEW_TYPE } from '../../constants'; import { useApp } from '../../contexts/AppContext'; +import { useDiffStrategy } from '../../contexts/DiffStrategyContext'; +import { useRAG } from '../../contexts/RAGContext'; +import { useSettings } from '../../contexts/SettingsContext'; import { CustomMode, GroupEntry, ToolGroup } from '../../database/json/custom-mode/types'; import { useCustomModes } from '../../hooks/use-custom-mode'; +import { PreviewView, PreviewViewState } from '../../PreviewView'; import { modes as buildinModes } from '../../utils/modes'; import { openOrCreateMarkdownFile } from '../../utils/obsidian'; +import { PromptGenerator, getFullLanguageName } from '../../utils/prompt-generator'; + const CustomModeView = () => { const app = useApp() @@ -14,7 +22,16 @@ const CustomModeView = () => { deleteCustomMode, updateCustomMode, customModeList, + customModePrompts } = useCustomModes() + const { settings } = useSettings() + const { getRAGEngine } = useRAG() + const diffStrategy = useDiffStrategy() + + const promptGenerator = useMemo(() => { + // @ts-expect-error + return new PromptGenerator(getRAGEngine, app, settings, diffStrategy, customModePrompts, customModeList) + }, [app, settings, diffStrategy, customModePrompts, customModeList]) // 当前选择的模式 const [selectedMode, setSelectedMode] = useState('ask') @@ -326,8 +343,41 @@ const CustomModeView = () => {

)} + + {/* 预览和保存 */}
- +
+ + + +
+
+
+
+ {state.title || (state.file ? state.file.name.replace(/\.[^/.]+$/, '') : '')} +
+
+
+
+
+ + + ) +} diff --git a/src/constants.ts b/src/constants.ts index 8bcb96a..85c2210 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -2,6 +2,7 @@ import { LLMModel } from './types/llm/model' // import { ApiProvider } from './utils/api' export const CHAT_VIEW_TYPE = 'infio-chat-view' export const APPLY_VIEW_TYPE = 'infio-apply-view' +export const PREVIEW_VIEW_TYPE = 'infio-preview-view' export const DEFAULT_MODELS: LLMModel[] = [] diff --git a/src/main.ts b/src/main.ts index f553f95..f6452dc 100644 --- a/src/main.ts +++ b/src/main.ts @@ -6,13 +6,14 @@ import { Editor, MarkdownView, Notice, Plugin, TFile } from 'obsidian' import { ApplyView } from './ApplyView' import { ChatView } from './ChatView' import { ChatProps } from './components/chat-view/ChatView' -import { APPLY_VIEW_TYPE, CHAT_VIEW_TYPE } from './constants' +import { APPLY_VIEW_TYPE, CHAT_VIEW_TYPE, PREVIEW_VIEW_TYPE } from './constants' import { getDiffStrategy } from "./core/diff/DiffStrategy" import { InlineEdit } from './core/edit/inline-edit-processor' import { RAGEngine } from './core/rag/rag-engine' import { DBManager } from './database/database-manager' import { migrateToJsonDatabase } from './database/json/migrateToJsonDatabase' import EventListener from "./event-listener" +import { PreviewView } from './PreviewView' import CompletionKeyWatcher from "./render-plugin/completion-key-watcher" import DocumentChangesListener, { DocumentChanges, @@ -62,6 +63,7 @@ export default class InfioPlugin extends Plugin { // register views this.registerView(CHAT_VIEW_TYPE, (leaf) => new ChatView(leaf, this)) this.registerView(APPLY_VIEW_TYPE, (leaf) => new ApplyView(leaf)) + this.registerView(PREVIEW_VIEW_TYPE, (leaf) => new PreviewView(leaf)) // register markdown processor for Inline Edit this.inlineEdit = new InlineEdit(this, this.settings); diff --git a/src/utils/obsidian.ts b/src/utils/obsidian.ts index 3c3069f..6b5dff5 100644 --- a/src/utils/obsidian.ts +++ b/src/utils/obsidian.ts @@ -1,7 +1,7 @@ -import { App, Editor, MarkdownView, TFile, TFolder, Vault, WorkspaceLeaf } from 'obsidian' - import * as path from 'path' +import { App, Editor, MarkdownView, TFile, TFolder, Vault, WorkspaceLeaf } from 'obsidian' + import { MentionableBlockData } from '../types/mentionable' export async function readTFileContent( diff --git a/src/utils/prompt-generator.ts b/src/utils/prompt-generator.ts index bbba00b..4ea20de 100644 --- a/src/utils/prompt-generator.ts +++ b/src/utils/prompt-generator.ts @@ -472,7 +472,7 @@ export class PromptGenerator { } } - private async getSystemMessageNew(mode: Mode, filesSearchMethod: string, preferredLanguage: string): Promise { + public async getSystemMessageNew(mode: Mode, filesSearchMethod: string, preferredLanguage: string): Promise { const prompt = await this.systemPrompt.getSystemPrompt( this.app.vault.getRoot().path, false,