mirror of
https://github.com/EthanMarti/infio-copilot.git
synced 2026-01-16 08:21:55 +00:00
添加对 CodeMirror 的支持,更新相关依赖版本,并在样式中增加 JSON 视图的样式。新增 JSON 视图类型并实现打开配置文件的功能,更新国际化文本以支持配置文件操作。
This commit is contained in:
parent
0f04b3c413
commit
3ca234c1a2
@ -55,6 +55,12 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@anthropic-ai/sdk": "^0.27.3",
|
||||
"codemirror": "^6.0.1",
|
||||
"@codemirror/commands": "^6.7.1",
|
||||
"@codemirror/lang-json": "^6.0.1",
|
||||
"@codemirror/state": "^6.5.2",
|
||||
"@codemirror/theme-one-dark": "^6.1.2",
|
||||
"@codemirror/view": "^6.35.0",
|
||||
"@codemirror/basic-setup": "^0.20.0",
|
||||
"@codemirror/lang-markdown": "^6.3.2",
|
||||
"@codemirror/merge": "^6.10.0",
|
||||
|
||||
77
pnpm-lock.yaml
generated
77
pnpm-lock.yaml
generated
@ -18,12 +18,27 @@ importers:
|
||||
'@codemirror/basic-setup':
|
||||
specifier: ^0.20.0
|
||||
version: 0.20.0
|
||||
'@codemirror/commands':
|
||||
specifier: ^6.7.1
|
||||
version: 6.8.1
|
||||
'@codemirror/lang-json':
|
||||
specifier: ^6.0.1
|
||||
version: 6.0.2
|
||||
'@codemirror/lang-markdown':
|
||||
specifier: ^6.3.2
|
||||
version: 6.3.3
|
||||
'@codemirror/merge':
|
||||
specifier: ^6.10.0
|
||||
version: 6.10.2
|
||||
'@codemirror/state':
|
||||
specifier: ^6.5.2
|
||||
version: 6.5.2
|
||||
'@codemirror/theme-one-dark':
|
||||
specifier: ^6.1.2
|
||||
version: 6.1.3
|
||||
'@codemirror/view':
|
||||
specifier: ^6.35.0
|
||||
version: 6.38.0
|
||||
'@electric-sql/pglite':
|
||||
specifier: 0.2.14
|
||||
version: 0.2.14
|
||||
@ -87,6 +102,9 @@ importers:
|
||||
clsx:
|
||||
specifier: ^2.1.1
|
||||
version: 2.1.1
|
||||
codemirror:
|
||||
specifier: ^6.0.1
|
||||
version: 6.0.2
|
||||
delay:
|
||||
specifier: ^6.0.0
|
||||
version: 6.0.0
|
||||
@ -565,6 +583,9 @@ packages:
|
||||
'@codemirror/commands@0.20.0':
|
||||
resolution: {integrity: sha512-v9L5NNVA+A9R6zaFvaTbxs30kc69F6BkOoiEbeFw4m4I0exmDEKBILN6mK+GksJtvTzGBxvhAPlVFTdQW8GB7Q==}
|
||||
|
||||
'@codemirror/commands@6.8.1':
|
||||
resolution: {integrity: sha512-KlGVYufHMQzxbdQONiLyGQDUW0itrLZwq3CcY7xpv9ZLRHqzkBSoteocBHtMCoY7/Ci4xhzSrToIeLg7FxHuaw==}
|
||||
|
||||
'@codemirror/lang-css@6.3.1':
|
||||
resolution: {integrity: sha512-kr5fwBGiGtmz6l0LSJIbno9QrifNMUusivHbnA1H6Dmqy4HZFte3UAICix1VuKo0lMPKQr2rqB+0BkKi/S3Ejg==}
|
||||
|
||||
@ -574,6 +595,9 @@ packages:
|
||||
'@codemirror/lang-javascript@6.2.4':
|
||||
resolution: {integrity: sha512-0WVmhp1QOqZ4Rt6GlVGwKJN3KW7Xh4H2q8ZZNGZaP6lRdxXJzmjm4FqvmOojVj6khWJHIb9sp7U/72W7xQgqAA==}
|
||||
|
||||
'@codemirror/lang-json@6.0.2':
|
||||
resolution: {integrity: sha512-x2OtO+AvwEHrEwR0FyyPtfDUiloG3rnVTSZV1W8UteaLL8/MajQd8DpvUb2YVzC+/T18aSDv0H9mu+xw0EStoQ==}
|
||||
|
||||
'@codemirror/lang-markdown@6.3.3':
|
||||
resolution: {integrity: sha512-1fn1hQAPWlSSMCvnF810AkhWpNLkJpl66CRfIy3vVl20Sl4NwChkorCHqpMtNbXr1EuMJsrDnhEpjZxKZ2UX3A==}
|
||||
|
||||
@ -599,12 +623,18 @@ packages:
|
||||
'@codemirror/search@0.20.1':
|
||||
resolution: {integrity: sha512-ROe6gRboQU5E4z6GAkNa2kxhXqsGNbeLEisbvzbOeB7nuDYXUZ70vGIgmqPu0tB+1M3F9yWk6W8k2vrFpJaD4Q==}
|
||||
|
||||
'@codemirror/search@6.5.11':
|
||||
resolution: {integrity: sha512-KmWepDE6jUdL6n8cAAqIpRmLPBZ5ZKnicE8oGU/s3QrAVID+0VhLFrzUucVKHG5035/BSykhExDL/Xm7dHthiA==}
|
||||
|
||||
'@codemirror/state@0.20.1':
|
||||
resolution: {integrity: sha512-ms0tlV5A02OK0pFvTtSUGMLkoarzh1F8mr6jy1cD7ucSC2X/VLHtQCxfhdSEGqTYlQF2hoZtmLv+amqhdgbwjQ==}
|
||||
|
||||
'@codemirror/state@6.5.2':
|
||||
resolution: {integrity: sha512-FVqsPqtPWKVVL3dPSxy8wEF/ymIEuVzF1PK3VbUgrxXpJUSHQWWZz4JMToquRxnkw+36LTamCZG2iua2Ptq0fA==}
|
||||
|
||||
'@codemirror/theme-one-dark@6.1.3':
|
||||
resolution: {integrity: sha512-NzBdIvEJmx6fjeremiGp3t/okrLPYT0d9orIc7AFun8oZcRk58aejkqhv6spnz4MLAevrKNPMQYXEWMg4s+sKA==}
|
||||
|
||||
'@codemirror/view@0.20.7':
|
||||
resolution: {integrity: sha512-pqEPCb9QFTOtHgAH5XU/oVy9UR/Anj6r+tG5CRmkNVcqSKEPmBU05WtN/jxJCFZBXf6HumzWC9ydE4qstO3TxQ==}
|
||||
|
||||
@ -1650,6 +1680,9 @@ packages:
|
||||
'@lezer/javascript@1.5.1':
|
||||
resolution: {integrity: sha512-ATOImjeVJuvgm3JQ/bpo2Tmv55HSScE2MTPnKRMRIPx2cLhHGyX2VnqpHhtIV1tVzIjZDbcWQm+NCTF40ggZVw==}
|
||||
|
||||
'@lezer/json@1.0.3':
|
||||
resolution: {integrity: sha512-BP9KzdF9Y35PDpv04r0VeSTKDeox5vVr3efE7eBbx3r4s3oNLfunchejZhjArmeieBH+nVOpgIiBJpEAv8ilqQ==}
|
||||
|
||||
'@lezer/lr@0.16.3':
|
||||
resolution: {integrity: sha512-pau7um4eAw94BEuuShUIeQDTf3k4Wt6oIUOYxMmkZgDHdqtIcxWND4LRxi8nI9KuT4I1bXQv67BCapkxt7Ywqw==}
|
||||
|
||||
@ -3107,6 +3140,9 @@ packages:
|
||||
resolution: {integrity: sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==}
|
||||
engines: {iojs: '>= 1.0.0', node: '>= 0.12.0'}
|
||||
|
||||
codemirror@6.0.2:
|
||||
resolution: {integrity: sha512-VhydHotNW5w1UGK0Qj96BwSk/Zqbp9WbnyK2W/eVMv4QyF41INRGpjUhFJY7/uDNuudSc33a/PKr4iDqRduvHw==}
|
||||
|
||||
collect-v8-coverage@1.0.2:
|
||||
resolution: {integrity: sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==}
|
||||
|
||||
@ -7037,6 +7073,13 @@ snapshots:
|
||||
'@codemirror/view': 0.20.7
|
||||
'@lezer/common': 0.16.1
|
||||
|
||||
'@codemirror/commands@6.8.1':
|
||||
dependencies:
|
||||
'@codemirror/language': 6.11.2
|
||||
'@codemirror/state': 6.5.2
|
||||
'@codemirror/view': 6.38.0
|
||||
'@lezer/common': 1.2.3
|
||||
|
||||
'@codemirror/lang-css@6.3.1':
|
||||
dependencies:
|
||||
'@codemirror/autocomplete': 6.18.6
|
||||
@ -7067,6 +7110,11 @@ snapshots:
|
||||
'@lezer/common': 1.2.3
|
||||
'@lezer/javascript': 1.5.1
|
||||
|
||||
'@codemirror/lang-json@6.0.2':
|
||||
dependencies:
|
||||
'@codemirror/language': 6.11.2
|
||||
'@lezer/json': 1.0.3
|
||||
|
||||
'@codemirror/lang-markdown@6.3.3':
|
||||
dependencies:
|
||||
'@codemirror/autocomplete': 6.18.6
|
||||
@ -7130,12 +7178,25 @@ snapshots:
|
||||
'@codemirror/view': 0.20.7
|
||||
crelt: 1.0.6
|
||||
|
||||
'@codemirror/search@6.5.11':
|
||||
dependencies:
|
||||
'@codemirror/state': 6.5.2
|
||||
'@codemirror/view': 6.38.0
|
||||
crelt: 1.0.6
|
||||
|
||||
'@codemirror/state@0.20.1': {}
|
||||
|
||||
'@codemirror/state@6.5.2':
|
||||
dependencies:
|
||||
'@marijn/find-cluster-break': 1.0.2
|
||||
|
||||
'@codemirror/theme-one-dark@6.1.3':
|
||||
dependencies:
|
||||
'@codemirror/language': 6.11.2
|
||||
'@codemirror/state': 6.5.2
|
||||
'@codemirror/view': 6.38.0
|
||||
'@lezer/highlight': 1.2.1
|
||||
|
||||
'@codemirror/view@0.20.7':
|
||||
dependencies:
|
||||
'@codemirror/state': 0.20.1
|
||||
@ -8107,6 +8168,12 @@ snapshots:
|
||||
'@lezer/highlight': 1.2.1
|
||||
'@lezer/lr': 1.4.2
|
||||
|
||||
'@lezer/json@1.0.3':
|
||||
dependencies:
|
||||
'@lezer/common': 1.2.3
|
||||
'@lezer/highlight': 1.2.1
|
||||
'@lezer/lr': 1.4.2
|
||||
|
||||
'@lezer/lr@0.16.3':
|
||||
dependencies:
|
||||
'@lezer/common': 0.16.1
|
||||
@ -9758,6 +9825,16 @@ snapshots:
|
||||
|
||||
co@4.6.0: {}
|
||||
|
||||
codemirror@6.0.2:
|
||||
dependencies:
|
||||
'@codemirror/autocomplete': 6.18.6
|
||||
'@codemirror/commands': 6.8.1
|
||||
'@codemirror/language': 6.11.2
|
||||
'@codemirror/lint': 6.8.5
|
||||
'@codemirror/search': 6.5.11
|
||||
'@codemirror/state': 6.5.2
|
||||
'@codemirror/view': 6.38.0
|
||||
|
||||
collect-v8-coverage@1.0.2: {}
|
||||
|
||||
color-convert@2.0.1:
|
||||
|
||||
168
src/BaseFileView.tsx
Normal file
168
src/BaseFileView.tsx
Normal file
@ -0,0 +1,168 @@
|
||||
import { EditorState, Extension } from "@codemirror/state";
|
||||
import { EditorView, ViewUpdate } from "@codemirror/view";
|
||||
import { TFile, TextFileView, WorkspaceLeaf } from "obsidian";
|
||||
|
||||
import InfioPlugin from './main';
|
||||
|
||||
export default abstract class BaseView extends TextFileView {
|
||||
public plugin: InfioPlugin;
|
||||
protected cmEditor: EditorView;
|
||||
protected editorEl: HTMLElement;
|
||||
protected state: { filePath?: string } | null = null;
|
||||
protected isEditorLoaded: boolean = false;
|
||||
protected currentFilePath: string | null = null;
|
||||
|
||||
protected constructor(leaf: WorkspaceLeaf, plugin: InfioPlugin) {
|
||||
super(leaf);
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
onload(): void {
|
||||
super.onload();
|
||||
this.editorEl = this.contentEl.createDiv("datafile-source-view mod-cm6");
|
||||
|
||||
this.cmEditor = new EditorView({
|
||||
state: this.createDefaultEditorState(),
|
||||
parent: this.editorEl,
|
||||
});
|
||||
|
||||
this.app.workspace.trigger("codemirror", this.cmEditor);
|
||||
this.isEditorLoaded = true;
|
||||
|
||||
// Load file content if state contains filePath and editor is now loaded
|
||||
if (this.state?.filePath) {
|
||||
this.loadFileFromPath(this.state.filePath);
|
||||
}
|
||||
}
|
||||
|
||||
async setState(state: { filePath?: string }): Promise<void> {
|
||||
this.state = state;
|
||||
// If filePath is provided and editor is loaded, load the file immediately
|
||||
if (state.filePath && this.isEditorLoaded) {
|
||||
await this.loadFileFromPath(state.filePath);
|
||||
}
|
||||
}
|
||||
|
||||
getState(): { filePath?: string } {
|
||||
return { filePath: this.currentFilePath };
|
||||
}
|
||||
|
||||
private async loadFileFromPath(filePath: string): Promise<void> {
|
||||
// Store the current file path for saving
|
||||
this.currentFilePath = filePath;
|
||||
|
||||
// Try to get the file from vault first (for regular files)
|
||||
const file = this.app.vault.getAbstractFileByPath(filePath);
|
||||
|
||||
if (file && file instanceof TFile) {
|
||||
// Regular file in vault
|
||||
this.file = file;
|
||||
await this.onLoadFile(file);
|
||||
} else {
|
||||
// File not in vault (hidden directory), read directly from filesystem
|
||||
console.log('File not in vault, reading directly from filesystem');
|
||||
await this.loadFileFromFilesystem(filePath);
|
||||
}
|
||||
}
|
||||
|
||||
private async loadFileFromFilesystem(filePath: string): Promise<void> {
|
||||
try {
|
||||
// Use vault adapter to read file directly from filesystem
|
||||
const content = await this.app.vault.adapter.read(filePath);
|
||||
this.setViewData(content, true);
|
||||
} catch (error) {
|
||||
console.error('Failed to load file from filesystem:', error);
|
||||
// If file doesn't exist, create it with empty content
|
||||
this.setViewData('{}', true);
|
||||
}
|
||||
}
|
||||
|
||||
async onLoadFile(file: TFile): Promise<void> {
|
||||
try {
|
||||
const content = await this.app.vault.read(file);
|
||||
this.setViewData(content, true);
|
||||
} catch (error) {
|
||||
console.error('Failed to load file content:', error);
|
||||
}
|
||||
}
|
||||
|
||||
getViewData(): string {
|
||||
return this.cmEditor.state.doc.toString();
|
||||
}
|
||||
|
||||
setViewData(data: string, clear: boolean): void {
|
||||
if (clear) {
|
||||
this.cmEditor.dispatch({ changes: { from: 0, to: this.cmEditor.state.doc.length, insert: data } });
|
||||
} else {
|
||||
this.cmEditor.dispatch({ changes: { from: 0, to: this.cmEditor.state.doc.length, insert: data } });
|
||||
}
|
||||
}
|
||||
|
||||
clear(): void {
|
||||
this.setViewData('', true);
|
||||
}
|
||||
|
||||
async save(clear?: boolean): Promise<void> {
|
||||
const content = this.getViewData();
|
||||
|
||||
if (this.file) {
|
||||
// Regular file in vault
|
||||
await this.app.vault.modify(this.file, content);
|
||||
} else if (this.currentFilePath) {
|
||||
// File in hidden directory, save directly to filesystem
|
||||
await this.app.vault.adapter.write(this.currentFilePath, content);
|
||||
}
|
||||
|
||||
if (clear) {
|
||||
this.clear();
|
||||
}
|
||||
}
|
||||
|
||||
// gets the title of the document
|
||||
getDisplayText(): string {
|
||||
if (this.file) {
|
||||
return this.file.basename;
|
||||
}
|
||||
if (this.currentFilePath) {
|
||||
return this.currentFilePath.split('/').pop() || "JSON File";
|
||||
}
|
||||
if (this.state?.filePath) {
|
||||
return this.state.filePath.split('/').pop() || "JSON File";
|
||||
}
|
||||
return "NOFILE";
|
||||
}
|
||||
|
||||
onClose(): Promise<void> {
|
||||
return super.onClose();
|
||||
}
|
||||
|
||||
async reload(): Promise<void> {
|
||||
await this.save(false);
|
||||
|
||||
const data = this.getViewData();
|
||||
this.cmEditor.setState(this.createDefaultEditorState());
|
||||
this.setViewData(data, false);
|
||||
}
|
||||
|
||||
protected onEditorUpdate(update: ViewUpdate): void {
|
||||
if (update.docChanged) {
|
||||
this.requestSave();
|
||||
}
|
||||
}
|
||||
|
||||
abstract getViewType(): string;
|
||||
|
||||
protected abstract getEditorExtensions(): Extension[];
|
||||
|
||||
private createDefaultEditorState(): EditorState {
|
||||
return EditorState.create({
|
||||
extensions: [...this.getCommonEditorExtensions(), ...this.getEditorExtensions()]
|
||||
});
|
||||
}
|
||||
|
||||
private getCommonEditorExtensions(): Extension[] {
|
||||
const extensions: Extension[] = [];
|
||||
extensions.push(EditorView.lineWrapping);
|
||||
return extensions;
|
||||
}
|
||||
}
|
||||
32
src/JsonFileView.tsx
Normal file
32
src/JsonFileView.tsx
Normal file
@ -0,0 +1,32 @@
|
||||
|
||||
import { json } from "@codemirror/lang-json";
|
||||
import { Extension } from "@codemirror/state";
|
||||
import { EditorView } from "@codemirror/view";
|
||||
import { basicSetup } from "codemirror";
|
||||
import { WorkspaceLeaf } from "obsidian";
|
||||
|
||||
import BaseView from "./BaseFileView";
|
||||
import { JSON_VIEW_TYPE } from './constants';
|
||||
import InfioPlugin from './main';
|
||||
import { getIndentByTabExtension } from "./utils/indentation-provider";
|
||||
|
||||
export default class JsonView extends BaseView {
|
||||
constructor(leaf: WorkspaceLeaf, plugin: InfioPlugin) {
|
||||
super(leaf, plugin);
|
||||
}
|
||||
|
||||
getViewType(): string {
|
||||
return JSON_VIEW_TYPE;
|
||||
}
|
||||
|
||||
protected getEditorExtensions(): Extension[] {
|
||||
const extensions = [
|
||||
basicSetup,
|
||||
getIndentByTabExtension(),
|
||||
json(),
|
||||
EditorView.updateListener.of(this.onEditorUpdate.bind(this))
|
||||
];
|
||||
|
||||
return extensions;
|
||||
}
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
import { AlertTriangle, ChevronDown, ChevronRight, FileText, Folder, Power, RotateCcw, Trash2, Wrench } from 'lucide-react'
|
||||
import { AlertTriangle, ChevronDown, ChevronRight, ExternalLink, FileText, Folder, Power, RotateCcw, Trash2, Wrench } from 'lucide-react'
|
||||
import { Notice } from 'obsidian'
|
||||
import React, { useEffect, useState } from 'react'
|
||||
|
||||
@ -108,6 +108,17 @@ const McpHubView = () => {
|
||||
}
|
||||
}
|
||||
|
||||
const handleOpenConfigFile = async () => {
|
||||
const hub = await getMcpHub();
|
||||
if (hub) {
|
||||
try {
|
||||
await hub.openMcpSettingsFile();
|
||||
} catch (error) {
|
||||
console.error('Failed to open config file:', error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const toggleServerExpansion = (serverKey: string) => {
|
||||
setExpandedServers(prev => ({ ...prev, [serverKey]: !prev[serverKey] }));
|
||||
if (!expandedServers[serverKey] && !activeServerDetailTab[serverKey]) {
|
||||
@ -196,7 +207,15 @@ const McpHubView = () => {
|
||||
<div className="infio-mcp-hub-container">
|
||||
{/* Header Section */}
|
||||
<div className="infio-mcp-hub-header">
|
||||
<h2 className="infio-mcp-hub-title">{t('mcpHub.title')}</h2>
|
||||
<h3 className="infio-mcp-hub-title">{t('mcpHub.title')}</h3>
|
||||
<div className="infio-mcp-hub-actions">
|
||||
<button
|
||||
onClick={fetchServers}
|
||||
className="obsidian-insight-refresh-btn"
|
||||
>
|
||||
<RotateCcw size={16} />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* MCP Settings */}
|
||||
@ -218,6 +237,15 @@ const McpHubView = () => {
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Configuration File Access */}
|
||||
<button
|
||||
onClick={handleOpenConfigFile}
|
||||
className="infio-mcp-config-button"
|
||||
>
|
||||
<ExternalLink size={16} />
|
||||
<span>{t('mcpHub.openConfigFile')}</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Create New Server Section */}
|
||||
@ -448,6 +476,57 @@ const McpHubView = () => {
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.infio-mcp-hub-actions {
|
||||
display: flex;
|
||||
gap: var(--size-2-2);
|
||||
}
|
||||
|
||||
.obsidian-insight-refresh-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-color: transparent !important;
|
||||
border: none !important;
|
||||
box-shadow: none !important;
|
||||
color: var(--text-muted);
|
||||
padding: 0 !important;
|
||||
margin: 0 !important;
|
||||
width: 24px !important;
|
||||
height: 24px !important;
|
||||
|
||||
&:hover {
|
||||
background-color: var(--background-modifier-hover) !important;
|
||||
}
|
||||
}
|
||||
|
||||
.obsidian-insight-refresh-btn:hover:not(:disabled) {
|
||||
background-color: var(--interactive-hover);
|
||||
}
|
||||
|
||||
.infio-mcp-config-button {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
background-color: var(--interactive-normal);
|
||||
color: var(--text-normal);
|
||||
border: 1px solid var(--background-modifier-border);
|
||||
border-radius: var(--radius-s);
|
||||
padding: 8px 16px;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.infio-mcp-config-button:hover {
|
||||
background-color: var(--interactive-hover);
|
||||
border-color: var(--interactive-accent);
|
||||
}
|
||||
|
||||
.infio-mcp-config-button:active {
|
||||
transform: translateY(1px);
|
||||
}
|
||||
|
||||
/* Search Section */
|
||||
.infio-mcp-search-section {
|
||||
margin-bottom: 16px;
|
||||
|
||||
@ -3,22 +3,10 @@ import { LLMModel } from './types/llm/model'
|
||||
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 JSON_VIEW_TYPE = 'infio-json-view'
|
||||
|
||||
export const DEFAULT_MODELS: LLMModel[] = []
|
||||
|
||||
// export const PROVIDERS: ApiProvider[] = [
|
||||
// 'Infio',
|
||||
// 'OpenRouter',
|
||||
// 'SiliconFlow',
|
||||
// 'Anthropic',
|
||||
// 'Deepseek',
|
||||
// 'OpenAI',
|
||||
// 'Google',
|
||||
// 'Groq',
|
||||
// 'Ollama',
|
||||
// 'OpenAICompatible',
|
||||
// ]
|
||||
|
||||
export const SUPPORT_EMBEDDING_SIMENTION: number[] = [
|
||||
384,
|
||||
512,
|
||||
|
||||
@ -1,7 +1,5 @@
|
||||
// Obsidian
|
||||
import { App, EventRef, Notice, TFile, normalizePath } from 'obsidian';
|
||||
|
||||
// Node built-in
|
||||
import { App, EventRef, Notice, TFile, normalizePath } from 'obsidian';
|
||||
import * as path from "path";
|
||||
|
||||
// SDK / External Libraries
|
||||
@ -23,7 +21,7 @@ import { EnvironmentVariables, shellEnvSync } from 'shell-env';
|
||||
import { z } from "zod"; // Keep zod
|
||||
// Internal/Project imports
|
||||
|
||||
import { INFIO_BASE_URL } from '../../constants'
|
||||
import { INFIO_BASE_URL, JSON_VIEW_TYPE } from '../../constants';
|
||||
import { t } from "../../lang/helpers";
|
||||
import InfioPlugin from "../../main";
|
||||
// Assuming path is correct and will be resolved, if not, this will remain an error.
|
||||
@ -331,24 +329,16 @@ export class McpHub {
|
||||
async ensureMcpFileExists(): Promise<void> {
|
||||
const mcpFolderPath = ".infio_json_db/mcp"
|
||||
if (!await this.app.vault.adapter.exists(normalizePath(mcpFolderPath))) {
|
||||
await this.app.vault.createFolder(mcpFolderPath);
|
||||
await this.app.vault.createFolder(normalizePath(mcpFolderPath));
|
||||
}
|
||||
this.mcpSettingsFilePath = normalizePath(path.join(mcpFolderPath, "settings.json"))
|
||||
const fileExists = await this.app.vault.adapter.exists(this.mcpSettingsFilePath);
|
||||
const fileExists = await this.app.vault.adapter.exists(normalizePath(this.mcpSettingsFilePath));
|
||||
if (!fileExists) {
|
||||
await this.app.vault.adapter.write(
|
||||
this.mcpSettingsFilePath,
|
||||
await this.app.vault.create(
|
||||
normalizePath(this.mcpSettingsFilePath),
|
||||
JSON.stringify({ mcpServers: {} }, null, 2)
|
||||
);
|
||||
}
|
||||
// this.globalMcpFilePath = normalizePath(path.join(mcpFolderPath, "global.json"))
|
||||
// const fileExists1 = await this.app.vault.adapter.exists(this.globalMcpFilePath);
|
||||
// if (!fileExists1) {
|
||||
// await this.app.vault.adapter.write(
|
||||
// this.globalMcpFilePath,
|
||||
// JSON.stringify({ mcpServers: {} }, null, 2)
|
||||
// );
|
||||
// }
|
||||
}
|
||||
|
||||
async getMcpSettingsFilePath(): Promise<string> {
|
||||
@ -363,6 +353,61 @@ export class McpHub {
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens the MCP settings file in Obsidian
|
||||
*/
|
||||
async openMcpSettingsFile(): Promise<void> {
|
||||
try {
|
||||
await this.ensureMcpFileExists();
|
||||
const filePath = this.mcpSettingsFilePath;
|
||||
|
||||
console.log('Attempting to open MCP settings file:', filePath);
|
||||
|
||||
// 检查文件是否已经打开
|
||||
let existingLeaf: any = null;
|
||||
this.app.workspace.iterateAllLeaves((leaf) => {
|
||||
if (leaf.view.getViewType() === JSON_VIEW_TYPE) {
|
||||
// 检查视图状态中的文件路径
|
||||
const viewState = leaf.view.getState();
|
||||
if (viewState && viewState.filePath === filePath) {
|
||||
existingLeaf = leaf;
|
||||
return false; // 停止遍历
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (existingLeaf) {
|
||||
// 如果文件已经打开,重新加载最新内容并激活 leaf
|
||||
await existingLeaf.setViewState({
|
||||
type: JSON_VIEW_TYPE,
|
||||
active: true,
|
||||
state: { filePath } // 重新设置状态以触发重新加载
|
||||
});
|
||||
this.app.workspace.setActiveLeaf(existingLeaf);
|
||||
this.app.workspace.revealLeaf(existingLeaf);
|
||||
console.log('MCP settings file is already open, reloading content and activating existing view:', filePath);
|
||||
} else {
|
||||
// 如果文件没有打开,创建新的 leaf
|
||||
const leaf = this.app.workspace.getLeaf(true);
|
||||
|
||||
if (leaf) {
|
||||
await leaf.setViewState({
|
||||
type: JSON_VIEW_TYPE,
|
||||
active: true,
|
||||
state: { filePath } // 传递文件路径到视图
|
||||
});
|
||||
|
||||
this.app.workspace.revealLeaf(leaf);
|
||||
console.log('Successfully opened MCP settings file in JSON view:', filePath);
|
||||
} else {
|
||||
console.error('Failed to get workspace leaf for JSON view');
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to open MCP settings file:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// Combined and simplified initializeMcpServers, only for global scope
|
||||
private async initializeGlobalMcpServers(): Promise<void> {
|
||||
try {
|
||||
|
||||
@ -529,6 +529,13 @@ export default {
|
||||
parameters: "Parameters",
|
||||
toolNoDescription: "No description",
|
||||
useMcpToolFrom: "Use MCP tool from",
|
||||
configurationFile: "Configuration File",
|
||||
configurationFileDescription: "Directly edit the MCP servers configuration file to add, modify, or remove servers.",
|
||||
openConfigFile: "Open Configuration File",
|
||||
configFileOpened: "Configuration file opened in Obsidian",
|
||||
configFileNotFound: "Configuration file not found",
|
||||
failedToOpenConfig: "Failed to open configuration file",
|
||||
openedWithSystemApp: "Configuration file opened with system default application",
|
||||
},
|
||||
semanticSearch: {
|
||||
title: "Semantic Index",
|
||||
|
||||
@ -525,10 +525,17 @@ export default {
|
||||
errors: "错误",
|
||||
noTools: "没有可用工具",
|
||||
noResources: "没有可用资源",
|
||||
noErrors: "没有错误记录",
|
||||
parameters: "参数",
|
||||
toolNoDescription: "无描述",
|
||||
useMcpToolFrom: "使用来自以下的 MCP 工具:",
|
||||
noErrors: "没有错误记录",
|
||||
parameters: "参数",
|
||||
toolNoDescription: "无描述",
|
||||
useMcpToolFrom: "使用来自以下的 MCP 工具:",
|
||||
configurationFile: "配置文件",
|
||||
configurationFileDescription: "直接编辑 MCP 服务器配置文件来添加、修改或删除服务器。",
|
||||
openConfigFile: "打开配置文件",
|
||||
configFileOpened: "配置文件已在 Obsidian 中打开",
|
||||
configFileNotFound: "配置文件未找到",
|
||||
failedToOpenConfig: "打开配置文件失败",
|
||||
openedWithSystemApp: "配置文件已使用系统默认应用程序打开",
|
||||
}
|
||||
},
|
||||
semanticSearch: {
|
||||
|
||||
@ -6,7 +6,7 @@ import { Editor, MarkdownView, Modal, 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, PREVIEW_VIEW_TYPE } from './constants'
|
||||
import { APPLY_VIEW_TYPE, CHAT_VIEW_TYPE, JSON_VIEW_TYPE, PREVIEW_VIEW_TYPE } from './constants'
|
||||
import { getDiffStrategy } from "./core/diff/DiffStrategy"
|
||||
import { InlineEdit } from './core/edit/inline-edit-processor'
|
||||
import { McpHub } from './core/mcp/McpHub'
|
||||
@ -16,6 +16,7 @@ import { DBManager } from './database/database-manager'
|
||||
import { migrateToJsonDatabase } from './database/json/migrateToJsonDatabase'
|
||||
import { EmbeddingManager } from './embedworker/EmbeddingManager'
|
||||
import EventListener from "./event-listener"
|
||||
import JsonView from './JsonFileView'
|
||||
import { t } from './lang/helpers'
|
||||
import { PreviewView } from './PreviewView'
|
||||
import CompletionKeyWatcher from "./render-plugin/completion-key-watcher"
|
||||
@ -88,6 +89,7 @@ export default class InfioPlugin extends Plugin {
|
||||
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))
|
||||
this.registerView(JSON_VIEW_TYPE, (leaf) => new JsonView(leaf, this))
|
||||
|
||||
// register markdown processor for Inline Edit
|
||||
this.inlineEdit = new InlineEdit(this, this.settings);
|
||||
|
||||
26
src/utils/indentation-provider.ts
Normal file
26
src/utils/indentation-provider.ts
Normal file
@ -0,0 +1,26 @@
|
||||
import { indentLess, indentWithTab } from "@codemirror/commands";
|
||||
import { indentUnit } from "@codemirror/language";
|
||||
import { Extension } from "@codemirror/state";
|
||||
import { keymap } from "@codemirror/view";
|
||||
|
||||
export const getIndentByTabExtension = (): Extension[] =>
|
||||
[
|
||||
keymap.of([indentWithTab]),
|
||||
indentUnit.of(" ")
|
||||
];
|
||||
|
||||
export const getInsertTabsExtension = (): Extension[] =>
|
||||
[
|
||||
keymap.of([
|
||||
// {
|
||||
// key: 'Tab',
|
||||
// preventDefault: true,
|
||||
// run: insertTab,
|
||||
// },
|
||||
{
|
||||
key: 'Shift-Tab',
|
||||
preventDefault: true,
|
||||
run: indentLess,
|
||||
},
|
||||
])
|
||||
];
|
||||
18
styles.css
18
styles.css
@ -2598,3 +2598,21 @@ button.infio-chat-input-model-select {
|
||||
max-width: 80vw;
|
||||
max-height: 80vh;
|
||||
}
|
||||
|
||||
/*
|
||||
* JSON View Styles
|
||||
*/
|
||||
.datafile-source-view.mod-cm6 .cm-gutters {
|
||||
flex: 0 0 auto;
|
||||
background-color: transparent;
|
||||
color: var(--text-faint) !important;
|
||||
border-right: none !important;
|
||||
margin-inline-end: var(--file-folding-offset);
|
||||
font-size: var(--font-ui-smaller);
|
||||
z-index: 1;
|
||||
font-variant: tabular-nums;
|
||||
}
|
||||
|
||||
.cm-gutterElement.cm-activeLineGutter {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user