add system preview
This commit is contained in:
parent
7791baabaa
commit
d1b81dd703
62
src/PreviewView.tsx
Normal file
62
src/PreviewView.tsx
Normal file
@ -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(
|
||||||
|
<AppProvider app={this.app}>
|
||||||
|
<PreviewViewRoot
|
||||||
|
state={this.state}
|
||||||
|
close={() => this.leaf.detach()}
|
||||||
|
/>
|
||||||
|
</AppProvider>,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,11 +1,19 @@
|
|||||||
import { ChevronDown, ChevronRight, Plus, Trash2, Undo2 } from 'lucide-react';
|
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 { 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 { CustomMode, GroupEntry, ToolGroup } from '../../database/json/custom-mode/types';
|
||||||
import { useCustomModes } from '../../hooks/use-custom-mode';
|
import { useCustomModes } from '../../hooks/use-custom-mode';
|
||||||
|
import { PreviewView, PreviewViewState } from '../../PreviewView';
|
||||||
import { modes as buildinModes } from '../../utils/modes';
|
import { modes as buildinModes } from '../../utils/modes';
|
||||||
import { openOrCreateMarkdownFile } from '../../utils/obsidian';
|
import { openOrCreateMarkdownFile } from '../../utils/obsidian';
|
||||||
|
import { PromptGenerator, getFullLanguageName } from '../../utils/prompt-generator';
|
||||||
|
|
||||||
const CustomModeView = () => {
|
const CustomModeView = () => {
|
||||||
const app = useApp()
|
const app = useApp()
|
||||||
|
|
||||||
@ -14,7 +22,16 @@ const CustomModeView = () => {
|
|||||||
deleteCustomMode,
|
deleteCustomMode,
|
||||||
updateCustomMode,
|
updateCustomMode,
|
||||||
customModeList,
|
customModeList,
|
||||||
|
customModePrompts
|
||||||
} = useCustomModes()
|
} = 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<string>('ask')
|
const [selectedMode, setSelectedMode] = useState<string>('ask')
|
||||||
@ -326,8 +343,41 @@ const CustomModeView = () => {
|
|||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* 预览和保存 */}
|
||||||
<div className="infio-custom-modes-actions">
|
<div className="infio-custom-modes-actions">
|
||||||
<button className="infio-preview-btn">
|
<button
|
||||||
|
className="infio-preview-btn"
|
||||||
|
onClick={async () => {
|
||||||
|
let filesSearchMethod = settings.filesSearchMethod
|
||||||
|
if (filesSearchMethod === 'auto' && settings.embeddingModelId && settings.embeddingModelId !== '') {
|
||||||
|
filesSearchMethod = 'semantic'
|
||||||
|
}
|
||||||
|
|
||||||
|
const userLanguage = getFullLanguageName(getLanguage())
|
||||||
|
const systemPrompt = await promptGenerator.getSystemMessageNew(modeName, filesSearchMethod, userLanguage)
|
||||||
|
const existingLeaf = app.workspace
|
||||||
|
.getLeavesOfType(PREVIEW_VIEW_TYPE)
|
||||||
|
.find(
|
||||||
|
(leaf) =>
|
||||||
|
leaf.view instanceof PreviewView && leaf.view.state.title === `${modeName} system prompt`
|
||||||
|
)
|
||||||
|
if (existingLeaf) {
|
||||||
|
console.log(existingLeaf)
|
||||||
|
app.workspace.setActiveLeaf(existingLeaf, { focus: true })
|
||||||
|
} else {
|
||||||
|
app.workspace.getLeaf(true).setViewState({
|
||||||
|
type: PREVIEW_VIEW_TYPE,
|
||||||
|
active: true,
|
||||||
|
state: {
|
||||||
|
content: systemPrompt.content as string,
|
||||||
|
title: `${modeName} system prompt`,
|
||||||
|
} satisfies PreviewViewState,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
>
|
||||||
预览
|
预览
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import * as DropdownMenu from '@radix-ui/react-dropdown-menu'
|
import * as DropdownMenu from '@radix-ui/react-dropdown-menu'
|
||||||
import { ChevronDown, ChevronUp } from 'lucide-react'
|
import { ChevronDown, ChevronUp } from 'lucide-react'
|
||||||
import React, { useEffect, useState, useMemo } from 'react'
|
import { useEffect, useMemo, useState } from 'react'
|
||||||
|
|
||||||
import { useSettings } from '../../../contexts/SettingsContext'
|
import { useSettings } from '../../../contexts/SettingsContext'
|
||||||
import { useCustomModes } from '../../../hooks/use-custom-mode'
|
import { useCustomModes } from '../../../hooks/use-custom-mode'
|
||||||
|
|||||||
105
src/components/preview-view/PreviewViewRoot.tsx
Normal file
105
src/components/preview-view/PreviewViewRoot.tsx
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
import { getIcon } from 'obsidian'
|
||||||
|
import { useEffect, useRef } from 'react'
|
||||||
|
|
||||||
|
import { PreviewViewState } from '../../PreviewView'
|
||||||
|
|
||||||
|
export default function PreviewViewRoot({
|
||||||
|
state,
|
||||||
|
close,
|
||||||
|
}: {
|
||||||
|
state: PreviewViewState
|
||||||
|
close: () => void
|
||||||
|
}) {
|
||||||
|
const closeIcon = getIcon('x')
|
||||||
|
const contentRef = useRef<HTMLDivElement>(null)
|
||||||
|
|
||||||
|
// 显示原始文本内容
|
||||||
|
useEffect(() => {
|
||||||
|
if (contentRef.current && state.content) {
|
||||||
|
// 清空现有内容
|
||||||
|
contentRef.current.empty()
|
||||||
|
|
||||||
|
// 创建预格式化文本元素
|
||||||
|
const preElement = document.createElement('pre')
|
||||||
|
preElement.className = 'infio-raw-content'
|
||||||
|
preElement.textContent = state.content
|
||||||
|
|
||||||
|
// 添加到容器
|
||||||
|
contentRef.current.appendChild(preElement)
|
||||||
|
}
|
||||||
|
}, [state.content, state.file])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div id="infio-preview-view">
|
||||||
|
<div className="view-header">
|
||||||
|
<div className="view-header-left">
|
||||||
|
<div className="view-header-nav-buttons"></div>
|
||||||
|
</div>
|
||||||
|
<div className="view-header-title-container mod-at-start">
|
||||||
|
<div className="view-header-title">
|
||||||
|
{state.title || (state.file ? state.file.name : 'Markdown Preview')}
|
||||||
|
</div>
|
||||||
|
<div className="view-actions">
|
||||||
|
<button
|
||||||
|
className="clickable-icon view-action infio-close-button"
|
||||||
|
aria-label="Close preview"
|
||||||
|
onClick={close}
|
||||||
|
>
|
||||||
|
{closeIcon && '✕'}
|
||||||
|
Close
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="view-content">
|
||||||
|
<div className="markdown-preview-view is-readable-line-width">
|
||||||
|
<div className="markdown-preview-sizer">
|
||||||
|
<div className="infio-preview-title">
|
||||||
|
{state.title || (state.file ? state.file.name.replace(/\.[^/.]+$/, '') : '')}
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
ref={contentRef}
|
||||||
|
className="markdown-preview-section"
|
||||||
|
></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<style>{`
|
||||||
|
#infio-preview-view {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
height: 100%;
|
||||||
|
background-color: var(--background-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
#infio-preview-view .view-content {
|
||||||
|
flex-grow: 1;
|
||||||
|
overflow: auto;
|
||||||
|
padding: 0 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.infio-preview-title {
|
||||||
|
font-size: 1.8em;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
padding-bottom: 10px;
|
||||||
|
border-bottom: 1px solid var(--background-modifier-border);
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-preview-section {
|
||||||
|
padding: 10px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.infio-raw-content {
|
||||||
|
white-space: pre-wrap;
|
||||||
|
word-break: break-word;
|
||||||
|
font-family: var(--font-monospace);
|
||||||
|
padding: 10px;
|
||||||
|
background-color: var(--background-secondary);
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
`}</style>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
@ -2,6 +2,7 @@ import { LLMModel } from './types/llm/model'
|
|||||||
// import { ApiProvider } from './utils/api'
|
// import { ApiProvider } from './utils/api'
|
||||||
export const CHAT_VIEW_TYPE = 'infio-chat-view'
|
export const CHAT_VIEW_TYPE = 'infio-chat-view'
|
||||||
export const APPLY_VIEW_TYPE = 'infio-apply-view'
|
export const APPLY_VIEW_TYPE = 'infio-apply-view'
|
||||||
|
export const PREVIEW_VIEW_TYPE = 'infio-preview-view'
|
||||||
|
|
||||||
export const DEFAULT_MODELS: LLMModel[] = []
|
export const DEFAULT_MODELS: LLMModel[] = []
|
||||||
|
|
||||||
|
|||||||
@ -6,13 +6,14 @@ import { Editor, MarkdownView, Notice, Plugin, TFile } from 'obsidian'
|
|||||||
import { ApplyView } from './ApplyView'
|
import { ApplyView } from './ApplyView'
|
||||||
import { ChatView } from './ChatView'
|
import { ChatView } from './ChatView'
|
||||||
import { ChatProps } from './components/chat-view/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 { getDiffStrategy } from "./core/diff/DiffStrategy"
|
||||||
import { InlineEdit } from './core/edit/inline-edit-processor'
|
import { InlineEdit } from './core/edit/inline-edit-processor'
|
||||||
import { RAGEngine } from './core/rag/rag-engine'
|
import { RAGEngine } from './core/rag/rag-engine'
|
||||||
import { DBManager } from './database/database-manager'
|
import { DBManager } from './database/database-manager'
|
||||||
import { migrateToJsonDatabase } from './database/json/migrateToJsonDatabase'
|
import { migrateToJsonDatabase } from './database/json/migrateToJsonDatabase'
|
||||||
import EventListener from "./event-listener"
|
import EventListener from "./event-listener"
|
||||||
|
import { PreviewView } from './PreviewView'
|
||||||
import CompletionKeyWatcher from "./render-plugin/completion-key-watcher"
|
import CompletionKeyWatcher from "./render-plugin/completion-key-watcher"
|
||||||
import DocumentChangesListener, {
|
import DocumentChangesListener, {
|
||||||
DocumentChanges,
|
DocumentChanges,
|
||||||
@ -62,6 +63,7 @@ export default class InfioPlugin extends Plugin {
|
|||||||
// register views
|
// register views
|
||||||
this.registerView(CHAT_VIEW_TYPE, (leaf) => new ChatView(leaf, this))
|
this.registerView(CHAT_VIEW_TYPE, (leaf) => new ChatView(leaf, this))
|
||||||
this.registerView(APPLY_VIEW_TYPE, (leaf) => new ApplyView(leaf))
|
this.registerView(APPLY_VIEW_TYPE, (leaf) => new ApplyView(leaf))
|
||||||
|
this.registerView(PREVIEW_VIEW_TYPE, (leaf) => new PreviewView(leaf))
|
||||||
|
|
||||||
// register markdown processor for Inline Edit
|
// register markdown processor for Inline Edit
|
||||||
this.inlineEdit = new InlineEdit(this, this.settings);
|
this.inlineEdit = new InlineEdit(this, this.settings);
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { App, Editor, MarkdownView, TFile, TFolder, Vault, WorkspaceLeaf } from 'obsidian'
|
|
||||||
|
|
||||||
import * as path from 'path'
|
import * as path from 'path'
|
||||||
|
|
||||||
|
import { App, Editor, MarkdownView, TFile, TFolder, Vault, WorkspaceLeaf } from 'obsidian'
|
||||||
|
|
||||||
import { MentionableBlockData } from '../types/mentionable'
|
import { MentionableBlockData } from '../types/mentionable'
|
||||||
|
|
||||||
export async function readTFileContent(
|
export async function readTFileContent(
|
||||||
|
|||||||
@ -472,7 +472,7 @@ export class PromptGenerator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async getSystemMessageNew(mode: Mode, filesSearchMethod: string, preferredLanguage: string): Promise<RequestMessage> {
|
public async getSystemMessageNew(mode: Mode, filesSearchMethod: string, preferredLanguage: string): Promise<RequestMessage> {
|
||||||
const prompt = await this.systemPrompt.getSystemPrompt(
|
const prompt = await this.systemPrompt.getSystemPrompt(
|
||||||
this.app.vault.getRoot().path,
|
this.app.vault.getRoot().path,
|
||||||
false,
|
false,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user