mirror of
https://github.com/ahmetildirim/obsidian-inscribe.git
synced 2026-07-22 05:44:10 +00:00
Add React support and settings management for Inscribe plugin: update TypeScript configuration, add dependencies, and implement settings page
This commit is contained in:
parent
19403c7c77
commit
b15c820968
6 changed files with 350 additions and 7 deletions
61
package-lock.json
generated
61
package-lock.json
generated
|
|
@ -1,16 +1,20 @@
|
|||
{
|
||||
"name": "obsidian-sample-plugin",
|
||||
"name": "obsidian-inscibe-plugin",
|
||||
"version": "1.0.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "obsidian-sample-plugin",
|
||||
"name": "obsidian-inscibe-plugin",
|
||||
"version": "1.0.0",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/react": "^19.0.2",
|
||||
"@types/react-dom": "^19.0.2",
|
||||
"codemirror-companion-extension": "^0.0.11",
|
||||
"ollama": "^0.5.11"
|
||||
"ollama": "^0.5.11",
|
||||
"react": "^19.0.0",
|
||||
"react-dom": "^19.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^16.11.6",
|
||||
|
|
@ -684,6 +688,24 @@
|
|||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/react": {
|
||||
"version": "19.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.0.2.tgz",
|
||||
"integrity": "sha512-USU8ZI/xyKJwFTpjSVIrSeHBVAGagkHQKPNbxeWwql/vDmnTIBgx+TJnhFnj1NXgz8XfprU0egV2dROLGpsBEg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"csstype": "^3.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/react-dom": {
|
||||
"version": "19.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.0.2.tgz",
|
||||
"integrity": "sha512-c1s+7TKFaDRRxr1TxccIX2u7sfCnc3RxkVyBIUA2lCpyqCF+QoAwQ/CBg7bsMdVwP120HEH143VQezKtef5nCg==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"@types/react": "^19.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/tern": {
|
||||
"version": "0.23.9",
|
||||
"resolved": "https://registry.npmjs.org/@types/tern/-/tern-0.23.9.tgz",
|
||||
|
|
@ -1138,6 +1160,12 @@
|
|||
"node": ">= 8"
|
||||
}
|
||||
},
|
||||
"node_modules/csstype": {
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
|
||||
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/debug": {
|
||||
"version": "4.4.0",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz",
|
||||
|
|
@ -2181,6 +2209,27 @@
|
|||
],
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/react": {
|
||||
"version": "19.0.0",
|
||||
"resolved": "https://registry.npmjs.org/react/-/react-19.0.0.tgz",
|
||||
"integrity": "sha512-V8AVnmPIICiWpGfm6GLzCR/W5FXLchHop40W4nXBmdlEceh16rCN8O8LNWm5bh5XUX91fh7KpA+W0TgMKmgTpQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-dom": {
|
||||
"version": "19.0.0",
|
||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.0.0.tgz",
|
||||
"integrity": "sha512-4GV5sHFG0e/0AD4X+ySy6UJd3jVl1iNsNHdpad0qhABJ11twS3TTBnseqsKurKcsNqCEFeGL3uLpVChpIO3QfQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"scheduler": "^0.25.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^19.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/regexpp": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz",
|
||||
|
|
@ -2258,6 +2307,12 @@
|
|||
"queue-microtask": "^1.2.2"
|
||||
}
|
||||
},
|
||||
"node_modules/scheduler": {
|
||||
"version": "0.25.0",
|
||||
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.25.0.tgz",
|
||||
"integrity": "sha512-xFVuu11jh+xcO7JOAGJNOXld8/TcEHK/4CituBUeUb5hqxJLj9YuemAEuvm9gQ/+pgXYfbQuqAkiYu+u7YEsNA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/semver": {
|
||||
"version": "7.6.3",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz",
|
||||
|
|
|
|||
|
|
@ -22,7 +22,11 @@
|
|||
"typescript": "4.7.4"
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/react": "^19.0.2",
|
||||
"@types/react-dom": "^19.0.2",
|
||||
"codemirror-companion-extension": "^0.0.11",
|
||||
"ollama": "^0.5.11"
|
||||
"ollama": "^0.5.11",
|
||||
"react": "^19.0.0",
|
||||
"react-dom": "^19.0.0"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,8 @@ import { Editor, Plugin } from 'obsidian';
|
|||
import { inlineSuggestion, Suggestion } from "codemirror-companion-extension";
|
||||
import { Model } from './model';
|
||||
import OllamaModel from './providers/ollama/ollama';
|
||||
import { InscribeSettingTab, InscribeSettings, DEFAULT_SETTINGS } from './settings';
|
||||
import { InscribeSettings, DEFAULT_SETTINGS } from './settings';
|
||||
import { InscribeSettingTab } from './settings/settings';
|
||||
|
||||
export default class Inscribe extends Plugin {
|
||||
settings: InscribeSettings;
|
||||
|
|
|
|||
125
src/settings/SettingsPage.tsx
Normal file
125
src/settings/SettingsPage.tsx
Normal file
|
|
@ -0,0 +1,125 @@
|
|||
import { App, Plugin, PluginSettingTab } from "obsidian";
|
||||
import { createRoot, Root } from "react-dom/client";
|
||||
import { InscribeSettings, DEFAULT_SETTINGS } from "./settings";
|
||||
import Inscribe from "../main";
|
||||
import React, { useState } from "react";
|
||||
|
||||
interface SettingsPageProps {
|
||||
settings: InscribeSettings;
|
||||
saveSettings: () => Promise<void>;
|
||||
}
|
||||
|
||||
const SettingsPage: React.FC<SettingsPageProps> = ({ settings, saveSettings }) => {
|
||||
const [provider, setProvider] = useState(settings.defaultProvider);
|
||||
const [openAIKey, setOpenAIKey] = useState(settings.openAI.apiKey);
|
||||
const [huggingFaceToken, setHuggingFaceToken] = useState(settings.huggingFace.accessToken);
|
||||
|
||||
const handleProviderChange = async (value: string) => {
|
||||
console.log(value);
|
||||
setProvider(value);
|
||||
settings.defaultProvider = value;
|
||||
await saveSettings();
|
||||
};
|
||||
|
||||
const handleOpenAIKeyChange = async (value: string) => {
|
||||
setOpenAIKey(value);
|
||||
settings.openAI.apiKey = value;
|
||||
await saveSettings();
|
||||
};
|
||||
|
||||
const handleHuggingFaceTokenChange = async (value: string) => {
|
||||
setHuggingFaceToken(value);
|
||||
settings.huggingFace.accessToken = value;
|
||||
await saveSettings();
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h2>AI Integration Settings</h2>
|
||||
|
||||
{/* Provider Selection */}
|
||||
<div>
|
||||
<label>Default AI Provider:</label>
|
||||
<select
|
||||
value={provider}
|
||||
onChange={(e) => handleProviderChange(e.target.value)}
|
||||
>
|
||||
<option value="openai">OpenAI</option>
|
||||
<option value="anthropic">Anthropic</option>
|
||||
<option value="huggingface">Hugging Face</option>
|
||||
<option value="custom">Custom</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
{/* Conditional Fields Based on Provider */}
|
||||
{provider === "openai" && (
|
||||
<div>
|
||||
<h3>OpenAI Settings</h3>
|
||||
<label>API Key:</label>
|
||||
<input
|
||||
type="text"
|
||||
value={openAIKey}
|
||||
onChange={(e) => handleOpenAIKeyChange(e.target.value)}
|
||||
placeholder="sk-..."
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{provider === "huggingface" && (
|
||||
<div>
|
||||
<h3>Hugging Face Settings</h3>
|
||||
<label>Access Token:</label>
|
||||
<input
|
||||
type="text"
|
||||
value={huggingFaceToken}
|
||||
onChange={(e) => handleHuggingFaceTokenChange(e.target.value)}
|
||||
placeholder="hf_..."
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Add More Providers Here */}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default SettingsPage;
|
||||
|
||||
|
||||
export class InscribeSettingTab extends PluginSettingTab {
|
||||
plugin: Inscribe;
|
||||
root: Root | null = null; // Keep track of the React root for cleanup
|
||||
|
||||
constructor(app: App, plugin: Inscribe) {
|
||||
super(app, plugin);
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
display(): void {
|
||||
const { containerEl } = this;
|
||||
|
||||
// Clear existing content
|
||||
containerEl.empty();
|
||||
|
||||
// Create a new React root if it doesn't exist
|
||||
if (!this.root) {
|
||||
this.root = createRoot(containerEl);
|
||||
}
|
||||
|
||||
// Render the React component
|
||||
this.root.render(
|
||||
<SettingsPage
|
||||
settings={this.plugin.settings}
|
||||
saveSettings={() => this.plugin.saveSettings()}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
hide(): void {
|
||||
// Clean up React root to avoid memory leaks
|
||||
if (this.root) {
|
||||
this.root.unmount();
|
||||
this.root = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
156
src/settings/settings.ts
Normal file
156
src/settings/settings.ts
Normal file
|
|
@ -0,0 +1,156 @@
|
|||
import { App, Notice, PluginSettingTab, Setting } from 'obsidian';
|
||||
import Inscribe from 'src/main';
|
||||
|
||||
export interface InscribeSettings {
|
||||
defaultProvider: string;
|
||||
openAI: {
|
||||
apiKey: string;
|
||||
model: string;
|
||||
};
|
||||
anthropic: {
|
||||
apiKey: string;
|
||||
};
|
||||
huggingFace: {
|
||||
accessToken: string;
|
||||
};
|
||||
custom: {
|
||||
apiEndpoint: string;
|
||||
};
|
||||
}
|
||||
|
||||
export const DEFAULT_SETTINGS: InscribeSettings = {
|
||||
defaultProvider: "openai",
|
||||
openAI: {
|
||||
apiKey: "",
|
||||
model: "gpt-4",
|
||||
},
|
||||
anthropic: {
|
||||
apiKey: "",
|
||||
},
|
||||
huggingFace: {
|
||||
accessToken: "",
|
||||
},
|
||||
custom: {
|
||||
apiEndpoint: "",
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
export class InscribeSettingTab extends PluginSettingTab {
|
||||
constructor(app: App, private plugin: Inscribe) {
|
||||
super(app, plugin);
|
||||
}
|
||||
|
||||
async display() {
|
||||
const { containerEl } = this;
|
||||
|
||||
containerEl.empty();
|
||||
containerEl.createEl("h2", { text: "AI Integration Settings" });
|
||||
|
||||
// Provider Selection
|
||||
new Setting(containerEl)
|
||||
.setName("Default AI Provider")
|
||||
.setDesc("Choose your preferred AI provider.")
|
||||
.addDropdown((dropdown) => {
|
||||
dropdown
|
||||
.addOption("openai", "OpenAI")
|
||||
.addOption("anthropic", "Anthropic")
|
||||
.addOption("huggingface", "Hugging Face")
|
||||
.addOption("custom", "Custom")
|
||||
.setValue(this.plugin.settings.defaultProvider)
|
||||
.onChange(async (value) => {
|
||||
this.plugin.settings.defaultProvider = value;
|
||||
await this.plugin.saveSettings();
|
||||
this.display(); // Refresh settings to load dynamic options
|
||||
});
|
||||
});
|
||||
|
||||
// Dynamic Provider Settings
|
||||
if (this.plugin.settings.defaultProvider === "openai") {
|
||||
this.openAISettings(containerEl);
|
||||
} else if (this.plugin.settings.defaultProvider === "anthropic") {
|
||||
this.anthropicSettings(containerEl);
|
||||
} else if (this.plugin.settings.defaultProvider === "huggingface") {
|
||||
this.huggingFaceSettings(containerEl);
|
||||
} else if (this.plugin.settings.defaultProvider === "custom") {
|
||||
this.customModelSettings(containerEl);
|
||||
}
|
||||
}
|
||||
|
||||
openAISettings(containerEl: HTMLElement) {
|
||||
containerEl.createEl("h3", { text: "OpenAI Settings" });
|
||||
new Setting(containerEl)
|
||||
.setName("API Key")
|
||||
.setDesc("Enter your OpenAI API Key.")
|
||||
.addText((text) =>
|
||||
text
|
||||
.setPlaceholder("sk-...")
|
||||
.setValue(this.plugin.settings.openAI.apiKey)
|
||||
.onChange(async (value) => {
|
||||
this.plugin.settings.openAI.apiKey = value;
|
||||
await this.plugin.saveSettings();
|
||||
})
|
||||
);
|
||||
new Setting(containerEl)
|
||||
.setName("Model")
|
||||
.setDesc("Choose the OpenAI model.")
|
||||
.addDropdown((dropdown) => {
|
||||
dropdown
|
||||
.addOption("gpt-4", "GPT-4")
|
||||
.addOption("gpt-3.5", "GPT-3.5")
|
||||
.setValue(this.plugin.settings.openAI.model)
|
||||
.onChange(async (value) => {
|
||||
this.plugin.settings.openAI.model = value;
|
||||
await this.plugin.saveSettings();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
anthropicSettings(containerEl: HTMLElement) {
|
||||
containerEl.createEl("h3", { text: "Anthropic Settings" });
|
||||
new Setting(containerEl)
|
||||
.setName("API Key")
|
||||
.setDesc("Enter your Anthropic API Key.")
|
||||
.addText((text) =>
|
||||
text
|
||||
.setPlaceholder("api-key")
|
||||
.setValue(this.plugin.settings.anthropic.apiKey)
|
||||
.onChange(async (value) => {
|
||||
this.plugin.settings.anthropic.apiKey = value;
|
||||
await this.plugin.saveSettings();
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
huggingFaceSettings(containerEl: HTMLElement) {
|
||||
containerEl.createEl("h3", { text: "Hugging Face Settings" });
|
||||
new Setting(containerEl)
|
||||
.setName("Access Token")
|
||||
.setDesc("Enter your Hugging Face Access Token.")
|
||||
.addText((text) =>
|
||||
text
|
||||
.setPlaceholder("hf_...")
|
||||
.setValue(this.plugin.settings.huggingFace.accessToken)
|
||||
.onChange(async (value) => {
|
||||
this.plugin.settings.huggingFace.accessToken = value;
|
||||
await this.plugin.saveSettings();
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
customModelSettings(containerEl: HTMLElement) {
|
||||
containerEl.createEl("h3", { text: "Custom Model Settings" });
|
||||
new Setting(containerEl)
|
||||
.setName("API Endpoint")
|
||||
.setDesc("Enter the API endpoint for your custom model.")
|
||||
.addText((text) =>
|
||||
text
|
||||
.setPlaceholder("https://api.example.com")
|
||||
.setValue(this.plugin.settings.custom.apiEndpoint)
|
||||
.onChange(async (value) => {
|
||||
this.plugin.settings.custom.apiEndpoint = value;
|
||||
await this.plugin.saveSettings();
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -16,9 +16,11 @@
|
|||
"ES5",
|
||||
"ES6",
|
||||
"ES7"
|
||||
]
|
||||
],
|
||||
"jsx": "react",
|
||||
"allowSyntheticDefaultImports": true
|
||||
},
|
||||
"include": [
|
||||
"**/*.ts"
|
||||
]
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue