Add React support and settings management for Inscribe plugin: update TypeScript configuration, add dependencies, and implement settings page

This commit is contained in:
Ahmet Ildirim 2025-01-05 21:31:53 +01:00
parent 19403c7c77
commit b15c820968
6 changed files with 350 additions and 7 deletions

61
package-lock.json generated
View file

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

View file

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

View file

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

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

View file

@ -16,9 +16,11 @@
"ES5",
"ES6",
"ES7"
]
],
"jsx": "react",
"allowSyntheticDefaultImports": true
},
"include": [
"**/*.ts"
]
}
}