添加对 CodeMirror 的支持,更新相关依赖版本,并在样式中增加 JSON 视图的样式。新增 JSON 视图类型并实现打开配置文件的功能,更新国际化文本以支持配置文件操作。

This commit is contained in:
duanfuxiang 2025-07-19 06:49:59 +08:00
parent 0f04b3c413
commit 3ca234c1a2
12 changed files with 491 additions and 36 deletions

View File

@ -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
View File

@ -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
View 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
View 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;
}
}

View File

@ -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;

View File

@ -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,

View File

@ -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 {

View File

@ -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",

View File

@ -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: {

View File

@ -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);

View 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,
},
])
];

View File

@ -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;
}