lossless-group_perplexed-pl.../main.ts
mpstaton 14962edde1 meet(requirements): misc changes related to meeting Obsidian plugin community requirements
Six docs-derived blockers fixed in one pass; punch-list plan filed
under context-v/plans/ for traceability.

manifest.json
- description rewritten: action-verb start (was "A plugin for
  Obsidian…", which the docs call out as an anti-pattern), all four
  providers named (Perplexity, Anthropic Claude, Perplexica, LM
  Studio), 144 chars, ends with period.
- fundingUrl removed — was pointed at the company website, not a
  donation service. Docs: "If you don't accept donations, remove
  fundingUrl from your manifest."

main.ts
- Three command IDs un-prefixed: 'perplexed-debug-commands' →
  'debug-commands', 'perplexed-reset-prompts' → 'reset-prompts',
  'perplexed-reinitialize-services' → 'reinitialize-services'.
  Obsidian auto-prefixes commands with the plugin id, so the
  doubled form would have registered as 'perplexed.perplexed-…'.
- Deleted the top-level <h2>Perplexed Plugin Settings</h2> from the
  settings tab. Docs: "Avoid adding a top-level heading in the
  settings tab, such as 'General', 'Settings', or the name of your
  plugin."

README.md
- Placeholder URLs replaced with the real lossless-group/perplexed-plugin
  repo (clone command, Issues link x2, Discussions link).
- Stale "Version: 0.0.0.1" footer line deleted (manifest is the
  source of truth).
- New "## Network use and accounts" section: per-provider table with
  endpoint, account requirement, and API-key requirement, plus an
  explicit no-telemetry / no-self-update statement. Required by
  Developer Policies (network-use disclosure + paid-account
  disclosure). Key Features bullet updated to mention Claude.

context-v/plans/2026-05-02_Submission-Blockers-Punch-List.md
- New plan documenting the audit findings: six blockers fixed here,
  four will-be-flagged items (7-10: settings <h3>/<h4> → setHeading,
  sentence case, console.log hygiene, vault.adapter.write in
  logger.ts) deferred to a separate pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-02 20:34:40 -05:00

1537 lines
67 KiB
TypeScript

import type { App, Editor} from 'obsidian';
import { Notice, Plugin, PluginSettingTab, Setting } from 'obsidian';
import * as dotenv from 'dotenv';
// Import services
import { PerplexityService } from './src/services/perplexityService';
import { PerplexicaService } from './src/services/perplexicaService';
import { LMStudioService } from './src/services/lmStudioService';
import { ClaudeService } from './src/services/claudeService';
import { PromptsService } from './src/services/promptsService';
// Import modals
import { PerplexityModal } from './src/modals/PerplexityModal';
import { PerplexicaModal } from './src/modals/PerplexicaModal';
import { LMStudioModal } from './src/modals/LMStudioModal';
import { ClaudeModal } from './src/modals/ClaudeModal';
import { URLUpdateModal } from './src/modals/URLUpdateModal';
import { ArticleGeneratorModal } from './src/modals/ArticleGeneratorModal';
import { TextEnhancementModal } from './src/modals/TextEnhancementModal';
import { TextEnhancementWithImagesModal } from './src/modals/TextEnhancementWithImagesModal';
// Load environment variables
dotenv.config({ path: `${process.cwd()}/.env` });
interface PerplexedPluginSettings {
mySetting: string;
localLLMPath: string;
requestBodyTemplate: string;
perplexityRequestTemplate: string;
perplexityApiKey: string;
perplexicaEndpoint: string;
perplexityEndpoint: string;
lmStudioEndpoint: string;
lmStudioRequestTemplate: string;
anthropicApiKey: string;
claudeDefaultModel: string;
defaultModel: string;
defaultOptimizationMode: string;
defaultFocusMode: string;
defaultLMStudioModel: string;
// Display Settings
headerPosition: 'top' | 'bottom';
// Prompt Settings
prompts: {
// System prompts
perplexitySystemPrompt: string;
perplexicaSystemPrompt: string;
lmStudioDefaultSystemPrompt: string;
// Placeholder text
perplexityQueryPlaceholder: string;
perplexicaQueryPlaceholder: string;
lmStudioQueryPlaceholder: string;
lmStudioSystemPromptPlaceholder: string;
articleTermPlaceholder: string;
// Article generator template
articleGeneratorTemplate: string;
// Deep Research article generator template
deepResearchArticleTemplate: string;
// Image prompts
imageReferencesPrompt: string;
// Text enhancement prompt
enhancePrompt: string;
// Text enhancement with images prompt
enhanceWithImagesPrompt: string;
};
}
const DEFAULT_SETTINGS: PerplexedPluginSettings = {
mySetting: 'default',
// Use host.docker.internal to connect to the host machine from Docker containers
localLLMPath: 'http://host.docker.internal:3030/api/search',
perplexicaEndpoint: 'http://localhost:3030/api/search',
perplexityEndpoint: 'https://api.perplexity.ai/chat/completions',
lmStudioEndpoint: 'http://localhost:1234/v1/chat/completions',
headerPosition: 'top',
requestBodyTemplate: `{
"chatModel": {
"provider": "ollama",
"name": "llama3.2:latest"
},
"embeddingModel": {
"provider": "ollama",
"name": "llama3.2:latest"
},
"optimizationMode": "speed",
"focusMode": "webSearch",
"query": "What is Perplexica's architecture?",
"history": [
{
"role": "user",
"content": "What is Perplexica's architecture?"
}
],
"systemInstructions": "{{PERPLEXICA_SYSTEM_PROMPT}}",
"stream": false,
"maxTokens": 2048,
"temperature": 0.7
}`,
perplexityApiKey: process.env.PERPLEXITY_API_KEY || '',
anthropicApiKey: process.env.ANTHROPIC_API_KEY || '',
claudeDefaultModel: 'claude-opus-4-7',
perplexityRequestTemplate: `{
"model": "llama-3.1-sonar-small-128k-online",
"messages": [
{
"role": "system",
"content": "{{PERPLEXITY_SYSTEM_PROMPT}}"
},
{
"role": "user",
"content": "What is Perplexity AI's approach to search?"
}
],
"max_tokens": 2048,
"temperature": 0.7,
"top_p": 0.9,
"return_citations": true,
"search_domain_filter": [],
"return_images": false,
"return_related_questions": false,
"search_recency_filter": "month",
"top_k": 0,
"stream": false,
"presence_penalty": 0,
"frequency_penalty": 1
}`,
lmStudioRequestTemplate: `{
"model": "ibm/granite-3.2-8b",
"messages": [
{
"role": "user",
"content": "Hello, can you help me with this question?"
}
],
"max_tokens": 2048,
"temperature": 0.7,
"stream": false
}`,
defaultModel: 'llama3.2:latest',
defaultOptimizationMode: 'speed',
defaultFocusMode: 'webSearch',
defaultLMStudioModel: 'ibm/granite-3.2-8b',
// Prompt Settings
prompts: {
// System prompts
perplexitySystemPrompt: "You are a helpful AI assistant. Provide clear, concise, and accurate information with proper citations.",
perplexicaSystemPrompt: "You are a helpful AI assistant. Provide clear, concise, and accurate information.",
lmStudioDefaultSystemPrompt: "You are a helpful AI assistant. Provide clear, concise, and accurate information.",
// Placeholder text
perplexityQueryPlaceholder: "What would you like to ask Perplexity?",
perplexicaQueryPlaceholder: "What would you like to ask Perplexica?",
lmStudioQueryPlaceholder: "What would you like to ask?",
lmStudioSystemPromptPlaceholder: "You are a helpful AI assistant...",
articleTermPlaceholder: "e.g., AI Copilots, AI Studios, Machine Learning, etc.",
// Article generator template
articleGeneratorTemplate: `Write a comprehensive one-page article about "{TERM}".
Structure the article as follows:
1. **Introduction** (2-3 sentences)
- Define the term and its significance
- Provide context for why it matters
2. **Main Content** (3-4 paragraphs)
- Explain the concept in detail
- Include practical examples and use cases
- Discuss benefits and potential applications
- Address any challenges or considerations
3. **Current State and Trends** (1-2 paragraphs)
- Discuss current adoption and market status
- Mention key players or technologies
- Highlight recent developments
4. **Future Outlook** (1 paragraph)
- Predict future developments
- Discuss potential impact
5. **Conclusion** (1-2 sentences)
- Summarize key points
- End with a forward-looking statement
**Important Guidelines:**
- Keep the total length to approximately one page (500-800 words)
- Use clear, accessible language
- Include specific examples and real-world applications
- Make it engaging and informative for a general audience
- Use markdown formatting for structure`,
// Deep Research article generator template
deepResearchArticleTemplate: `Conduct comprehensive research and write an in-depth article about "{TERM}".
**Research Requirements:**
- Conduct exhaustive research across hundreds of sources
- Analyze multiple perspectives and viewpoints
- Include academic, industry, and expert sources
- Provide detailed citations and references
- Examine historical context and evolution
- Consider global implications and regional variations
**Article Structure:**
1. **Executive Summary** (1 paragraph)
- Concise overview of key findings
- Main conclusions and implications
2. **Introduction and Definition** (2-3 paragraphs)
- Comprehensive definition and scope
- Historical context and evolution
- Current significance and relevance
3. **Comprehensive Analysis** (6-8 paragraphs)
- Detailed examination of core concepts
- Multiple perspectives and approaches
- Industry applications and use cases
- Technical implementation details
- Market analysis and competitive landscape
- Regulatory and ethical considerations
4. **Current State and Market Dynamics** (3-4 paragraphs)
- Global adoption patterns and trends
- Key players, technologies, and platforms
- Regional variations and cultural factors
- Economic impact and market size
- Recent developments and breakthroughs
5. **Challenges and Opportunities** (2-3 paragraphs)
- Technical challenges and limitations
- Implementation barriers and solutions
- Future opportunities and potential
- Risk factors and mitigation strategies
6. **Future Outlook and Predictions** (2-3 paragraphs)
- Short-term developments (1-2 years)
- Medium-term trends (3-5 years)
- Long-term implications (5+ years)
- Strategic recommendations
7. **Conclusion** (1-2 paragraphs)
- Synthesis of key findings
- Strategic implications
- Call to action or forward-looking statement
**Research Guidelines:**
- Include diverse source types (academic, industry, news, expert opinions)
- Provide detailed citations for all claims
- Analyze conflicting viewpoints and evidence
- Consider global and regional perspectives
- Include quantitative data where available
- Examine both benefits and risks
- Address ethical and societal implications
**Quality Standards:**
- Academic rigor with practical relevance
- Balanced analysis of multiple perspectives
- Evidence-based conclusions
- Clear, professional writing style
- Comprehensive bibliography`,
// Image prompts
imageReferencesPrompt: "**Image References:**\nPlease include the following image references throughout your response where appropriate:\n- [IMAGE 1: Relevant diagram or illustration related to the topic]\n- [IMAGE 2: Practical example or use case visualization]\n- [IMAGE 3: Additional supporting visual content]",
// Text enhancement prompt
enhancePrompt: "Please enhance the following text by improving clarity, adding relevant details, expanding on key points, and making it more comprehensive and engaging. Maintain the original meaning and tone while making it more informative and well-structured:\n\n{TEXT}",
// Text enhancement with images prompt
enhanceWithImagesPrompt: "Please provide 1-3 relevant images for the following text. Return ONLY the image markers in the format [IMAGE 1: description], [IMAGE 2: description], etc. Each image should illustrate a key concept, example, or visual representation related to the text. Do not include any other text or explanation:\n\n{TEXT}"
}
};
export default class PerplexedPlugin extends Plugin {
public settings: PerplexedPluginSettings = DEFAULT_SETTINGS;
private statusBarItemEl: HTMLElement | null = null;
private ribbonIconEl: HTMLElement | null = null;
// Service instances
private perplexityService!: PerplexityService | null;
private perplexicaService!: PerplexicaService | null;
private lmStudioService!: LMStudioService | null;
private claudeService!: ClaudeService | null;
private promptsService!: PromptsService | null;
async onload(): Promise<void> {
try {
console.log('Perplexed Plugin: Starting initialization...');
await this.loadSettings();
console.log('Perplexed Plugin: Settings loaded successfully');
// Initialize prompts service first
try {
this.promptsService = new PromptsService(this.settings.prompts);
console.log('Perplexed Plugin: PromptsService initialized successfully');
} catch (error) {
console.error('Perplexed Plugin: Failed to initialize PromptsService:', error);
new Notice('Failed to initialize PromptsService');
this.promptsService = null;
}
// Initialize services with error handling - only if promptsService is available
if (this.promptsService) {
try {
this.perplexityService = new PerplexityService({
perplexityApiKey: this.settings.perplexityApiKey,
perplexityEndpoint: this.settings.perplexityEndpoint,
promptsService: this.promptsService,
requestTemplate: this.settings.perplexityRequestTemplate,
headerPosition: this.settings.headerPosition
});
console.log('Perplexed Plugin: PerplexityService initialized successfully');
} catch (error) {
console.error('Perplexed Plugin: Failed to initialize PerplexityService:', error);
new Notice('Failed to initialize PerplexityService');
this.perplexityService = null;
}
try {
this.perplexicaService = new PerplexicaService({
perplexicaEndpoint: this.settings.perplexicaEndpoint,
localLLMPath: this.settings.localLLMPath,
defaultModel: this.settings.defaultModel,
promptsService: this.promptsService,
requestTemplate: this.settings.requestBodyTemplate
});
console.log('Perplexed Plugin: PerplexicaService initialized successfully');
} catch (error) {
console.error('Perplexed Plugin: Failed to initialize PerplexicaService:', error);
new Notice('Failed to initialize PerplexicaService');
this.perplexicaService = null;
}
try {
this.lmStudioService = new LMStudioService({
lmStudioEndpoint: this.settings.lmStudioEndpoint,
promptsService: this.promptsService,
requestTemplate: this.settings.lmStudioRequestTemplate
});
console.log('Perplexed Plugin: LMStudioService initialized successfully');
} catch (error) {
console.error('Perplexed Plugin: Failed to initialize LMStudioService:', error);
new Notice('Failed to initialize LMStudioService');
this.lmStudioService = null;
}
try {
this.claudeService = new ClaudeService({
anthropicApiKey: this.settings.anthropicApiKey,
promptsService: this.promptsService,
headerPosition: this.settings.headerPosition,
});
console.log('Perplexed Plugin: ClaudeService initialized successfully');
} catch (error) {
console.error('Perplexed Plugin: Failed to initialize ClaudeService:', error);
new Notice('Failed to initialize ClaudeService');
this.claudeService = null;
}
} else {
// If promptsService failed, set all other services to null
this.perplexityService = null;
this.perplexicaService = null;
this.lmStudioService = null;
this.claudeService = null;
console.log('Perplexed Plugin: Skipping service initialization due to PromptsService failure');
}
// Debug: Log current settings
console.log('Perplexed Plugin: Current Perplexica Path:', this.settings.perplexicaEndpoint);
console.log('Perplexed Plugin: Full settings:', JSON.stringify(this.settings, null, 2));
// This adds a settings tab so the user can configure various aspects of the plugin
this.addSettingTab(new PerplexedSettingTab(this.app, this));
console.log('Perplexed Plugin: Settings tab added successfully');
// Register commands with error handling
try {
this.registerPerplexicaCommands();
console.log('Perplexed Plugin: Perplexica commands registered successfully');
} catch (error) {
console.error('Perplexed Plugin: Failed to register Perplexica commands:', error);
}
try {
this.registerPerplexityCommands();
console.log('Perplexed Plugin: Perplexity commands registered successfully');
} catch (error) {
console.error('Perplexed Plugin: Failed to register Perplexity commands:', error);
}
try {
this.registerLMStudioCommands();
console.log('Perplexed Plugin: LM Studio commands registered successfully');
} catch (error) {
console.error('Perplexed Plugin: Failed to register LM Studio commands:', error);
}
try {
this.registerClaudeCommands();
console.log('Perplexed Plugin: Claude commands registered successfully');
} catch (error) {
console.error('Perplexed Plugin: Failed to register Claude commands:', error);
}
try {
this.registerArticleGeneratorCommands();
console.log('Perplexed Plugin: Article generator commands registered successfully');
} catch (error) {
console.error('Perplexed Plugin: Failed to register article generator commands:', error);
}
try {
this.registerTextEnhancementCommands();
console.log('Perplexed Plugin: Text enhancement commands registered successfully');
} catch (error) {
console.error('Perplexed Plugin: Failed to register text enhancement commands:', error);
}
try {
this.registerTextEnhancementWithImagesCommands();
console.log('Perplexed Plugin: Get related images commands registered successfully');
} catch (error) {
console.error('Perplexed Plugin: Failed to register get related images commands:', error);
}
// Add debug command to check command status
this.addCommand({
id: 'debug-commands',
name: 'Debug: Check Perplexed Commands',
callback: () => {
this.debugCommands();
}
});
// Add command to reset prompts to defaults
this.addCommand({
id: 'reset-prompts',
name: 'Reset Prompts to Default',
callback: async () => {
await this.resetPromptsToDefault();
}
});
// Add command to reinitialize services
this.addCommand({
id: 'reinitialize-services',
name: 'Reinitialize Perplexed Services',
callback: async () => {
await this.reinitializeServices();
}
});
console.log('Perplexed Plugin: Initialization completed successfully');
new Notice('Perplexed Plugin loaded successfully');
} catch (error) {
console.error('Perplexed Plugin: Critical initialization error:', error);
new Notice('Perplexed Plugin failed to load properly');
}
}
onunload(): void {
this.statusBarItemEl?.remove();
this.ribbonIconEl?.remove();
}
private async loadSettings() {
const savedData = await this.loadData();
this.settings = Object.assign({}, DEFAULT_SETTINGS, savedData);
// Ensure new fields are always present (migration for existing users)
if (!this.settings.prompts.deepResearchArticleTemplate) {
this.settings.prompts.deepResearchArticleTemplate = DEFAULT_SETTINGS.prompts.deepResearchArticleTemplate;
await this.saveSettings();
}
if (!this.settings.prompts.enhancePrompt) {
this.settings.prompts.enhancePrompt = DEFAULT_SETTINGS.prompts.enhancePrompt;
await this.saveSettings();
}
if (!this.settings.prompts.enhanceWithImagesPrompt) {
this.settings.prompts.enhanceWithImagesPrompt = DEFAULT_SETTINGS.prompts.enhanceWithImagesPrompt;
await this.saveSettings();
}
}
public async saveSettings(): Promise<void> {
try {
await this.saveData(this.settings);
} catch (error) {
console.error('Failed to save settings:', error);
new Notice('Failed to save settings');
}
}
// Delegate methods to services
public async queryPerplexity(query: string, model: string, stream: boolean, editor: Editor, options?: {
return_citations?: boolean;
return_images?: boolean;
return_related_questions?: boolean;
search_recency_filter?: string;
}): Promise<void> {
if (!this.perplexityService) {
throw new Error('Perplexity service not initialized');
}
await this.perplexityService.queryPerplexity(query, model, stream, editor, options);
}
public async queryPerplexica(query: string, focusMode: string, optimizationMode: string, stream: boolean, editor: Editor, options?: {
return_images?: boolean;
}): Promise<void> {
if (!this.perplexicaService) {
throw new Error('Perplexica service not initialized');
}
await this.perplexicaService.queryPerplexica(query, focusMode, optimizationMode, stream, editor, options);
}
public async queryLMStudio(query: string, model: string, stream: boolean, editor: Editor, options?: {
max_tokens?: number;
temperature?: number;
top_p?: number;
system_prompt?: string;
return_images?: boolean;
}): Promise<void> {
if (!this.lmStudioService) {
throw new Error('LM Studio service not initialized');
}
await this.lmStudioService.queryLMStudio(query, model, stream, editor, options);
}
// Getter for prompts service
public getPromptsService(): PromptsService | null {
return this.promptsService;
}
private registerPerplexicaCommands(): void {
// Command to update Perplexica URL
this.addCommand({
id: 'update-perplexica-url',
name: 'Update Perplexica URL',
callback: () => {
const modal = new URLUpdateModal(this.app, {
title: 'Update Perplexica API URL',
label: 'Perplexica API URL',
placeholder: 'http://localhost:3030/api/search',
currentValue: this.settings.perplexicaEndpoint,
onSave: async (newUrl: string) => {
this.settings.perplexicaEndpoint = newUrl;
await this.saveSettings();
}
});
modal.open();
}
});
// Command to show current settings
this.addCommand({
id: 'show-perplexica-settings',
name: 'Show Perplexica Settings',
callback: () => {
new Notice(`Current Perplexica URL: ${this.settings.perplexicaEndpoint}`);
console.log('Perplexica Settings:', this.settings);
}
});
// Command to ask Perplexica
this.addCommand({
id: 'ask-perplexica',
name: 'Ask Perplexica',
editorCallback: (editor: Editor) => {
try {
if (!this.perplexicaService) {
new Notice('Perplexica service not initialized. Please check console for errors and try the debug command.');
console.error('Perplexica service is not initialized');
return;
}
if (!this.promptsService) {
new Notice('Prompts service not initialized. Please check console for errors and try the debug command.');
console.error('Prompts service is not initialized');
return;
}
const modal = new PerplexicaModal(this.app, editor, this.perplexicaService, this.promptsService);
modal.open();
} catch (error) {
console.error('Error opening Perplexica modal:', error);
new Notice('Failed to open Perplexica modal. Check console for details.');
}
}
});
}
private registerPerplexityCommands(): void {
try {
// Command to update Perplexity URL
this.addCommand({
id: 'update-perplexity-url',
name: 'Update Perplexity URL',
callback: () => {
const modal = new URLUpdateModal(this.app, {
title: 'Update Perplexity API URL',
label: 'Perplexity API URL',
placeholder: 'https://api.perplexity.ai/chat/completions',
currentValue: this.settings.perplexityEndpoint,
onSave: async (newUrl: string) => {
this.settings.perplexityEndpoint = newUrl;
await this.saveSettings();
}
});
modal.open();
}
});
// Command to show current Perplexity settings
this.addCommand({
id: 'show-perplexity-settings',
name: 'Show Perplexity Settings',
callback: () => {
new Notice(`Current Perplexity URL: ${this.settings.perplexityEndpoint}`);
console.log('Perplexity Settings:', this.settings);
}
});
// Command to ask Perplexity
this.addCommand({
id: 'ask-perplexity',
name: 'Ask Perplexity',
editorCallback: (editor: Editor) => {
try {
if (!this.perplexityService) {
new Notice('Perplexity service not initialized. Please check console for errors and try the debug command.');
console.error('Perplexity service is not initialized');
return;
}
if (!this.promptsService) {
new Notice('Prompts service not initialized. Please check console for errors and try the debug command.');
console.error('Prompts service is not initialized');
return;
}
const modal = new PerplexityModal(this.app, editor, this.perplexityService, this.promptsService);
modal.open();
} catch (error) {
console.error('Error opening Perplexity modal:', error);
new Notice('Failed to open Perplexity modal. Check console for details.');
}
}
});
// Add a fallback command that shows service status
this.addCommand({
id: 'perplexity-service-status',
name: 'Check Perplexity Service Status',
callback: () => {
if (this.perplexityService) {
new Notice('Perplexity service is initialized and ready');
console.log('Perplexity service status: OK');
} else {
new Notice('Perplexity service is NOT initialized. Check console for errors.');
console.error('Perplexity service status: FAILED');
}
}
});
console.log('Perplexed Plugin: Perplexity commands registered successfully');
} catch (error) {
console.error('Perplexed Plugin: Error registering Perplexity commands:', error);
throw error;
}
}
private registerClaudeCommands(): void {
this.addCommand({
id: 'ask-claude',
name: 'Ask Claude',
editorCallback: (editor: Editor) => {
if (!this.claudeService) {
new Notice('Claude service not initialized. Set ANTHROPIC_API_KEY in .env or settings, then reinitialize services.');
return;
}
if (!this.promptsService) {
new Notice('Prompts service not initialized.');
return;
}
new ClaudeModal(this.app, editor, this.claudeService, this.promptsService).open();
},
});
this.addCommand({
id: 'claude-service-status',
name: 'Check Claude Service Status',
callback: () => {
if (this.claudeService && this.settings.anthropicApiKey) {
new Notice('Claude service is initialized and an API key is configured.');
} else if (this.claudeService) {
new Notice('Claude service is initialized but no API key is set.');
} else {
new Notice('Claude service is NOT initialized.');
}
},
});
}
private registerLMStudioCommands(): void {
// Command to update LM Studio URL
this.addCommand({
id: 'update-lmstudio-url',
name: 'Update LM Studio URL',
callback: () => {
const modal = new URLUpdateModal(this.app, {
title: 'Update LM Studio API URL',
label: 'LM Studio API URL',
placeholder: 'http://localhost:1234/v1/chat/completions',
currentValue: this.settings.lmStudioEndpoint,
onSave: async (newUrl: string) => {
this.settings.lmStudioEndpoint = newUrl;
await this.saveSettings();
}
});
modal.open();
}
});
// Command to show current LM Studio settings
this.addCommand({
id: 'show-lmstudio-settings',
name: 'Show LM Studio Settings',
callback: () => {
new Notice(`Current LM Studio URL: ${this.settings.lmStudioEndpoint}`);
console.log('LM Studio Settings:', this.settings);
}
});
// Command to ask LM Studio
this.addCommand({
id: 'ask-lmstudio',
name: 'Ask LM Studio',
editorCallback: (editor: Editor) => {
try {
if (!this.lmStudioService) {
new Notice('LM Studio service not initialized. Please check console for errors and try the debug command.');
console.error('LM Studio service is not initialized');
return;
}
if (!this.promptsService) {
new Notice('Prompts service not initialized. Please check console for errors and try the debug command.');
console.error('Prompts service is not initialized');
return;
}
const modal = new LMStudioModal(this.app, editor, this.lmStudioService, this.promptsService);
modal.open();
} catch (error) {
console.error('Error opening LM Studio modal:', error);
new Notice('Failed to open LM Studio modal. Check console for details.');
}
}
});
}
private registerArticleGeneratorCommands(): void {
// Register Article Generator command
this.addCommand({
id: 'generate-article',
name: 'Generate One-Page Article',
editorCallback: (editor: Editor) => {
try {
if (!this.perplexityService) {
new Notice('Perplexity service not initialized. Please check console for errors and try the debug command.');
console.error('Perplexity service is not initialized');
return;
}
if (!this.promptsService) {
new Notice('Prompts service not initialized. Please check console for errors and try the debug command.');
console.error('Prompts service is not initialized');
return;
}
new ArticleGeneratorModal(this.app, editor, this.perplexityService, this.promptsService).open();
} catch (error) {
console.error('Error opening Article Generator modal:', error);
new Notice('Failed to open Article Generator modal. Check console for details.');
}
}
});
}
private registerTextEnhancementCommands(): void {
// Register Text Enhancement command
this.addCommand({
id: 'enhance-text',
name: 'Enhance Selected Text with Perplexity',
editorCallback: (editor: Editor) => {
try {
const selectedText = editor.getSelection();
if (!selectedText || selectedText.trim() === '') {
new Notice('Please select some text to enhance');
return;
}
if (!this.perplexityService) {
new Notice('Perplexity service not initialized. Please check console for errors and try the debug command.');
console.error('Perplexity service is not initialized');
return;
}
if (!this.promptsService) {
new Notice('Prompts service not initialized. Please check console for errors and try the debug command.');
console.error('Prompts service is not initialized');
return;
}
new TextEnhancementModal(this.app, editor, this.perplexityService, this.promptsService, selectedText).open();
} catch (error) {
console.error('Error opening Text Enhancement modal:', error);
new Notice('Failed to open Text Enhancement modal. Check console for details.');
}
}
});
}
private registerTextEnhancementWithImagesCommands(): void {
// Register Get Related Images command
this.addCommand({
id: 'enhance-text-with-images',
name: 'Get Related Images for Selected Text',
editorCallback: (editor: Editor) => {
try {
const selectedText = editor.getSelection();
if (!selectedText || selectedText.trim() === '') {
new Notice('Please select some text to get related images for');
return;
}
if (!this.perplexityService) {
new Notice('Perplexity service not initialized. Please check console for errors and try the debug command.');
console.error('Perplexity service is not initialized');
return;
}
if (!this.promptsService) {
new Notice('Prompts service not initialized. Please check console for errors and try the debug command.');
console.error('Prompts service is not initialized');
return;
}
new TextEnhancementWithImagesModal(this.app, editor, this.perplexityService, this.promptsService, selectedText).open();
} catch (error) {
console.error('Error opening Get Related Images modal:', error);
new Notice('Failed to open Get Related Images modal. Check console for details.');
}
}
});
}
private debugCommands(): void {
console.log('=== Perplexed Plugin Debug Information ===');
console.log('Plugin instance:', this);
console.log('Settings:', this.settings);
console.log('Services status:');
console.log('- PromptsService:', this.promptsService ? 'Initialized' : 'NOT INITIALIZED');
console.log('- PerplexityService:', this.perplexityService ? 'Initialized' : 'NOT INITIALIZED');
console.log('- PerplexicaService:', this.perplexicaService ? 'Initialized' : 'NOT INITIALIZED');
console.log('- LMStudioService:', this.lmStudioService ? 'Initialized' : 'NOT INITIALIZED');
// Check if commands are registered in Obsidian
const registeredCommands = this.app.commands.commands;
const perplexedCommands = Object.keys(registeredCommands).filter(cmd =>
cmd.startsWith('perplexed') ||
cmd.includes('perplexity') ||
cmd.includes('perplexica') ||
cmd.includes('lmstudio') ||
cmd.includes('generate-article') ||
cmd.includes('enhance-text')
);
console.log('Registered Perplexed commands:', perplexedCommands);
if (perplexedCommands.length === 0) {
new Notice('No Perplexed commands found! Check console for details.');
} else {
new Notice(`Found ${perplexedCommands.length} Perplexed commands. Check console for details.`);
}
console.log('=== End Debug Information ===');
}
private async resetPromptsToDefault(): Promise<void> {
try {
console.log('Perplexed Plugin: Resetting prompts to default...');
new Notice('Resetting prompts to default values...');
// Reset all prompt settings to default values
this.settings.prompts = { ...DEFAULT_SETTINGS.prompts };
// Save the updated settings
await this.saveSettings();
// Reinitialize the prompts service with new settings
if (this.promptsService) {
this.promptsService.updateSettings(this.settings.prompts);
console.log('Perplexed Plugin: PromptsService updated with default settings');
}
new Notice('✅ Prompts reset to default values successfully');
console.log('Perplexed Plugin: Prompts reset to default successfully');
} catch (error) {
console.error('Perplexed Plugin: Failed to reset prompts to default:', error);
new Notice('❌ Failed to reset prompts to default. Check console for details.');
}
}
private async reinitializeServices(): Promise<void> {
try {
console.log('Perplexed Plugin: Reinitializing services...');
new Notice('Reinitializing Perplexed services...');
// Reinitialize prompts service first
try {
this.promptsService = new PromptsService(this.settings.prompts);
console.log('Perplexed Plugin: PromptsService reinitialized successfully');
} catch (error) {
console.error('Perplexed Plugin: Failed to reinitialize PromptsService:', error);
this.promptsService = null;
}
// Reinitialize other services only if promptsService is available
if (this.promptsService) {
try {
this.perplexityService = new PerplexityService({
perplexityApiKey: this.settings.perplexityApiKey,
perplexityEndpoint: this.settings.perplexityEndpoint,
promptsService: this.promptsService,
requestTemplate: this.settings.perplexityRequestTemplate,
headerPosition: this.settings.headerPosition
});
console.log('Perplexed Plugin: PerplexityService reinitialized successfully');
} catch (error) {
console.error('Perplexed Plugin: Failed to reinitialize PerplexityService:', error);
this.perplexityService = null;
}
try {
this.perplexicaService = new PerplexicaService({
perplexicaEndpoint: this.settings.perplexicaEndpoint,
localLLMPath: this.settings.localLLMPath,
defaultModel: this.settings.defaultModel,
promptsService: this.promptsService,
requestTemplate: this.settings.requestBodyTemplate
});
console.log('Perplexed Plugin: PerplexicaService reinitialized successfully');
} catch (error) {
console.error('Perplexed Plugin: Failed to reinitialize PerplexicaService:', error);
this.perplexicaService = null;
}
try {
this.lmStudioService = new LMStudioService({
lmStudioEndpoint: this.settings.lmStudioEndpoint,
promptsService: this.promptsService,
requestTemplate: this.settings.lmStudioRequestTemplate
});
console.log('Perplexed Plugin: LMStudioService reinitialized successfully');
} catch (error) {
console.error('Perplexed Plugin: Failed to reinitialize LMStudioService:', error);
this.lmStudioService = null;
}
try {
this.claudeService = new ClaudeService({
anthropicApiKey: this.settings.anthropicApiKey,
promptsService: this.promptsService,
headerPosition: this.settings.headerPosition,
});
console.log('Perplexed Plugin: ClaudeService reinitialized successfully');
} catch (error) {
console.error('Perplexed Plugin: Failed to reinitialize ClaudeService:', error);
this.claudeService = null;
}
} else {
// If promptsService failed, set all other services to null
this.perplexityService = null;
this.perplexicaService = null;
this.lmStudioService = null;
this.claudeService = null;
console.log('Perplexed Plugin: Skipping service reinitialization due to PromptsService failure');
}
new Notice('Services reinitialization completed. Check console for details.');
console.log('Perplexed Plugin: Services reinitialization completed');
} catch (error) {
console.error('Perplexed Plugin: Error during services reinitialization:', error);
new Notice('Failed to reinitialize services. Check console for details.');
}
}
}
class PerplexedSettingTab extends PluginSettingTab {
plugin: PerplexedPlugin;
constructor(app: App, plugin: PerplexedPlugin) {
super(app, plugin);
this.plugin = plugin;
}
// Helper method to safely update prompts service
private updatePromptsService(): void {
const promptsService = this.plugin.getPromptsService();
if (promptsService) {
promptsService.updateSettings(this.plugin.settings.prompts);
}
}
display(): void {
const { containerEl } = this;
containerEl.empty();
// Perplexity Section
const perplexityHeader = containerEl.createEl('h3', { text: 'Perplexity (Remote Service)' });
perplexityHeader.style.color = 'var(--text-accent)';
containerEl.createEl('p', {
text: 'Configure settings for the hosted Perplexity AI service',
cls: 'setting-item-description'
});
new Setting(containerEl)
.setName('Endpoint')
.setDesc('API endpoint for Perplexity service')
.addText(text => text
.setPlaceholder('https://api.perplexity.ai/chat/completions')
.setValue(this.plugin.settings.perplexityEndpoint)
.onChange(async (value: string) => {
this.plugin.settings.perplexityEndpoint = value;
await this.plugin.saveSettings();
})
);
new Setting(containerEl)
.setName('API Key')
.setDesc('Your Perplexity API key (required for remote service)')
.addText(text => text
.setPlaceholder('pplx-xxxxxxxxxxxxxxxxxxxxx')
.setValue(this.plugin.settings.perplexityApiKey)
.onChange(async (value: string) => {
this.plugin.settings.perplexityApiKey = value;
await this.plugin.saveSettings();
})
);
new Setting(containerEl)
.setName('Header Position')
.setDesc('Where to place the query header in generated articles')
.addDropdown(dropdown => dropdown
.addOption('top', 'Top of article')
.addOption('bottom', 'Bottom of article')
.setValue(this.plugin.settings.headerPosition)
.onChange(async (value: string) => {
this.plugin.settings.headerPosition = value as 'top' | 'bottom';
await this.plugin.saveSettings();
})
);
// Perplexity Request Template
const perplexityJsonSetting = new Setting(containerEl)
.setName('Request Body Template')
.setDesc('JSON template for Perplexity API requests');
// Create a textarea element for Perplexity
const perplexityTextArea = document.createElement('textarea');
perplexityTextArea.rows = 10;
perplexityTextArea.cols = 50;
perplexityTextArea.style.width = '100%';
perplexityTextArea.style.minHeight = '300px';
perplexityTextArea.style.fontFamily = 'monospace';
perplexityTextArea.placeholder = 'Enter Perplexity JSON request template...';
// Set initial value if it exists
if (this.plugin.settings.perplexityRequestTemplate) {
try {
const config = JSON.parse(this.plugin.settings.perplexityRequestTemplate);
perplexityTextArea.value = JSON.stringify(config, null, 2);
} catch (e) {
// If not valid JSON, use as is
perplexityTextArea.value = this.plugin.settings.perplexityRequestTemplate;
}
}
// Add input event listener for Perplexity
perplexityTextArea.addEventListener('input', async () => {
this.plugin.settings.perplexityRequestTemplate = perplexityTextArea.value;
await this.plugin.saveSettings();
});
// Add the textarea to the setting
perplexityJsonSetting.settingEl.appendChild(perplexityTextArea);
// Claude (Anthropic) Section
const claudeHeader = containerEl.createEl('h3', { text: 'Claude (Anthropic)' });
claudeHeader.style.color = 'var(--text-accent)';
containerEl.createEl('p', {
text: 'Configure Claude API access. Web-search citations supported in this iteration; document-grounded citations are deferred.',
cls: 'setting-item-description'
});
new Setting(containerEl)
.setName('Anthropic API Key')
.setDesc('Your Anthropic API key. Read from ANTHROPIC_API_KEY in .env if set; can be overridden here.')
.addText(text => text
.setPlaceholder('sk-ant-...')
.setValue(this.plugin.settings.anthropicApiKey)
.onChange(async (value) => {
this.plugin.settings.anthropicApiKey = value;
await this.plugin.saveSettings();
}));
new Setting(containerEl)
.setName('Default Claude Model')
.setDesc('Default model used by the Ask Claude command. Recommended: claude-opus-4-7.')
.addDropdown(dropdown => dropdown
.addOption('claude-opus-4-7', 'claude-opus-4-7 (recommended)')
.addOption('claude-opus-4-6', 'claude-opus-4-6')
.addOption('claude-sonnet-4-6', 'claude-sonnet-4-6')
.addOption('claude-haiku-4-5', 'claude-haiku-4-5')
.setValue(this.plugin.settings.claudeDefaultModel)
.onChange(async (value) => {
this.plugin.settings.claudeDefaultModel = value;
await this.plugin.saveSettings();
}));
// Perplexica Section
const perplexicaHeader = containerEl.createEl('h3', { text: 'Perplexica (Self-Hosted)' });
perplexicaHeader.style.color = 'var(--text-accent)';
containerEl.createEl('p', {
text: 'Configure settings for your local Perplexica installation',
cls: 'setting-item-description'
});
new Setting(containerEl)
.setName('Endpoint')
.setDesc('API endpoint for your local Perplexica instance')
.addText(text => text
.setPlaceholder('http://localhost:3030/api/search')
.setValue(this.plugin.settings.perplexicaEndpoint)
.onChange(async (value: string) => {
this.plugin.settings.perplexicaEndpoint = value;
await this.plugin.saveSettings();
})
);
new Setting(containerEl)
.setName('Fallback Container Path')
.setDesc('Alternative endpoint for Docker container setups')
.addText(text => text
.setPlaceholder('http://host.docker.internal:3030/api/search')
.setValue(this.plugin.settings.localLLMPath)
.onChange(async (value: string) => {
this.plugin.settings.localLLMPath = value;
await this.plugin.saveSettings();
})
);
new Setting(containerEl)
.setName('Default Model')
.setDesc('Default AI model for Perplexica to use')
.addText(text => text
.setPlaceholder('llama3.2:latest')
.setValue(this.plugin.settings.defaultModel)
.onChange(async (value: string) => {
this.plugin.settings.defaultModel = value;
await this.plugin.saveSettings();
})
);
// Perplexica Request Template
const perplexicaJsonSetting = new Setting(containerEl)
.setName('Request Body Template')
.setDesc('JSON template for Perplexica API requests');
// Create a textarea element for Perplexica
const perplexicaTextArea = document.createElement('textarea');
perplexicaTextArea.rows = 10;
perplexicaTextArea.cols = 50;
perplexicaTextArea.style.width = '100%';
perplexicaTextArea.style.minHeight = '300px';
perplexicaTextArea.style.fontFamily = 'monospace';
perplexicaTextArea.placeholder = 'Enter Perplexica JSON request template...';
// Set initial value if it exists
if (this.plugin.settings.requestBodyTemplate) {
try {
const config = JSON.parse(this.plugin.settings.requestBodyTemplate);
perplexicaTextArea.value = JSON.stringify(config, null, 2);
} catch (e) {
// If not valid JSON, use as is
perplexicaTextArea.value = this.plugin.settings.requestBodyTemplate;
}
}
// Add input event listener for Perplexica
perplexicaTextArea.addEventListener('input', async () => {
this.plugin.settings.requestBodyTemplate = perplexicaTextArea.value;
await this.plugin.saveSettings();
});
// Add the textarea to the setting
perplexicaJsonSetting.settingEl.appendChild(perplexicaTextArea);
// LM Studio Section
const lmStudioHeader = containerEl.createEl('h3', { text: 'LM Studio (Local Models)' });
lmStudioHeader.style.color = 'var(--text-accent)';
containerEl.createEl('p', {
text: 'Configure settings for your local LM Studio installation with loaded models',
cls: 'setting-item-description'
});
new Setting(containerEl)
.setName('Endpoint')
.setDesc('API endpoint for your local LM Studio instance')
.addText(text => text
.setPlaceholder('http://localhost:1234/v1/chat/completions')
.setValue(this.plugin.settings.lmStudioEndpoint)
.onChange(async (value: string) => {
this.plugin.settings.lmStudioEndpoint = value;
await this.plugin.saveSettings();
})
);
new Setting(containerEl)
.setName('Default Model')
.setDesc('Default model name for LM Studio to use')
.addText(text => text
.setPlaceholder('ibm/granite-3.2-8b')
.setValue(this.plugin.settings.defaultLMStudioModel)
.onChange(async (value: string) => {
this.plugin.settings.defaultLMStudioModel = value;
await this.plugin.saveSettings();
})
);
// LM Studio Request Template
const lmStudioJsonSetting = new Setting(containerEl)
.setName('Request Body Template')
.setDesc('JSON template for LM Studio API requests');
// Create a textarea element for LM Studio
const lmStudioTextArea = document.createElement('textarea');
lmStudioTextArea.rows = 10;
lmStudioTextArea.cols = 50;
lmStudioTextArea.style.width = '100%';
lmStudioTextArea.style.minHeight = '300px';
lmStudioTextArea.style.fontFamily = 'monospace';
lmStudioTextArea.placeholder = 'Enter LM Studio JSON request template...';
// Set initial value if it exists
if (this.plugin.settings.lmStudioRequestTemplate) {
try {
const config = JSON.parse(this.plugin.settings.lmStudioRequestTemplate);
lmStudioTextArea.value = JSON.stringify(config, null, 2);
} catch (e) {
// If not valid JSON, use as is
lmStudioTextArea.value = this.plugin.settings.lmStudioRequestTemplate;
}
}
// Add input event listener for LM Studio
lmStudioTextArea.addEventListener('input', async () => {
this.plugin.settings.lmStudioRequestTemplate = lmStudioTextArea.value;
await this.plugin.saveSettings();
});
// Add the textarea to the setting
lmStudioJsonSetting.settingEl.appendChild(lmStudioTextArea);
// Prompts Section
const promptsHeader = containerEl.createEl('h3', { text: 'Prompts & Text Configuration' });
promptsHeader.style.color = 'var(--text-accent)';
containerEl.createEl('p', {
text: 'Customize all prompts, placeholders, descriptions, and messages used throughout the plugin',
cls: 'setting-item-description'
});
// System Prompts
containerEl.createEl('h4', { text: 'System Prompts' });
new Setting(containerEl)
.setName('Perplexity System Prompt')
.setDesc('System prompt used for Perplexity AI requests')
.addTextArea(text => text
.setPlaceholder('Enter system prompt for Perplexity...')
.setValue(this.plugin.settings.prompts.perplexitySystemPrompt)
.onChange(async (value: string) => {
this.plugin.settings.prompts.perplexitySystemPrompt = value;
const promptsService = this.plugin.getPromptsService();
if (promptsService) {
promptsService.updateSettings(this.plugin.settings.prompts);
}
await this.plugin.saveSettings();
})
);
new Setting(containerEl)
.setName('Perplexica System Prompt')
.setDesc('System prompt used for Perplexica requests')
.addTextArea(text => text
.setPlaceholder('Enter system prompt for Perplexica...')
.setValue(this.plugin.settings.prompts.perplexicaSystemPrompt)
.onChange(async (value: string) => {
this.plugin.settings.prompts.perplexicaSystemPrompt = value;
const promptsService = this.plugin.getPromptsService();
if (promptsService) {
promptsService.updateSettings(this.plugin.settings.prompts);
}
await this.plugin.saveSettings();
})
);
new Setting(containerEl)
.setName('LM Studio Default System Prompt')
.setDesc('Default system prompt used for LM Studio requests')
.addTextArea(text => text
.setPlaceholder('Enter default system prompt for LM Studio...')
.setValue(this.plugin.settings.prompts.lmStudioDefaultSystemPrompt)
.onChange(async (value: string) => {
this.plugin.settings.prompts.lmStudioDefaultSystemPrompt = value;
const promptsService = this.plugin.getPromptsService();
if (promptsService) {
promptsService.updateSettings(this.plugin.settings.prompts);
}
await this.plugin.saveSettings();
})
);
// Placeholder Text
containerEl.createEl('h4', { text: 'Placeholder Text' });
new Setting(containerEl)
.setName('Perplexity Query Placeholder')
.setDesc('Placeholder text for Perplexity query input')
.addText(text => text
.setPlaceholder('Enter placeholder text...')
.setValue(this.plugin.settings.prompts.perplexityQueryPlaceholder)
.onChange(async (value: string) => {
this.plugin.settings.prompts.perplexityQueryPlaceholder = value;
const promptsService = this.plugin.getPromptsService();
if (promptsService) {
promptsService.updateSettings(this.plugin.settings.prompts);
}
await this.plugin.saveSettings();
})
);
new Setting(containerEl)
.setName('Perplexica Query Placeholder')
.setDesc('Placeholder text for Perplexica query input')
.addText(text => text
.setPlaceholder('Enter placeholder text...')
.setValue(this.plugin.settings.prompts.perplexicaQueryPlaceholder)
.onChange(async (value: string) => {
this.plugin.settings.prompts.perplexicaQueryPlaceholder = value;
const promptsService = this.plugin.getPromptsService();
if (promptsService) {
promptsService.updateSettings(this.plugin.settings.prompts);
}
await this.plugin.saveSettings();
})
);
new Setting(containerEl)
.setName('LM Studio Query Placeholder')
.setDesc('Placeholder text for LM Studio query input')
.addText(text => text
.setPlaceholder('Enter placeholder text...')
.setValue(this.plugin.settings.prompts.lmStudioQueryPlaceholder)
.onChange(async (value: string) => {
this.plugin.settings.prompts.lmStudioQueryPlaceholder = value;
const promptsService = this.plugin.getPromptsService();
if (promptsService) {
promptsService.updateSettings(this.plugin.settings.prompts);
}
await this.plugin.saveSettings();
})
);
new Setting(containerEl)
.setName('LM Studio System Prompt Placeholder')
.setDesc('Placeholder text for LM Studio system prompt input')
.addText(text => text
.setPlaceholder('Enter placeholder text...')
.setValue(this.plugin.settings.prompts.lmStudioSystemPromptPlaceholder)
.onChange(async (value: string) => {
this.plugin.settings.prompts.lmStudioSystemPromptPlaceholder = value;
const promptsService = this.plugin.getPromptsService();
if (promptsService) {
promptsService.updateSettings(this.plugin.settings.prompts);
}
await this.plugin.saveSettings();
})
);
new Setting(containerEl)
.setName('Article Term Placeholder')
.setDesc('Placeholder text for article generator term input')
.addText(text => text
.setPlaceholder('Enter placeholder text...')
.setValue(this.plugin.settings.prompts.articleTermPlaceholder)
.onChange(async (value: string) => {
this.plugin.settings.prompts.articleTermPlaceholder = value;
this.updatePromptsService();
await this.plugin.saveSettings();
})
);
// Article Generator Template
containerEl.createEl('h4', { text: 'Article Generator Template' });
const articleTemplateSetting = new Setting(containerEl)
.setName('Article Generator Template')
.setDesc('Template for generating articles. Use {TERM} as placeholder for the term.');
const articleTemplateTextArea = document.createElement('textarea');
articleTemplateTextArea.rows = 15;
articleTemplateTextArea.cols = 50;
articleTemplateTextArea.style.width = '100%';
articleTemplateTextArea.style.minHeight = '400px';
articleTemplateTextArea.style.fontFamily = 'monospace';
articleTemplateTextArea.placeholder = 'Enter article generator template...';
articleTemplateTextArea.value = this.plugin.settings.prompts.articleGeneratorTemplate;
articleTemplateTextArea.addEventListener('input', async () => {
this.plugin.settings.prompts.articleGeneratorTemplate = articleTemplateTextArea.value;
this.updatePromptsService();
await this.plugin.saveSettings();
});
articleTemplateSetting.settingEl.appendChild(articleTemplateTextArea);
// Deep Research Article Generator Template
const deepResearchTemplateSetting = new Setting(containerEl)
.setName('Deep Research Article Generator Template')
.setDesc('Template for generating articles with Deep Research model. Use {TERM} as placeholder for the term.');
const deepResearchTemplateTextArea = document.createElement('textarea');
deepResearchTemplateTextArea.rows = 20;
deepResearchTemplateTextArea.cols = 50;
deepResearchTemplateTextArea.style.width = '100%';
deepResearchTemplateTextArea.style.minHeight = '500px';
deepResearchTemplateTextArea.style.fontFamily = 'monospace';
deepResearchTemplateTextArea.placeholder = 'Enter Deep Research article generator template...';
deepResearchTemplateTextArea.value = this.plugin.settings.prompts.deepResearchArticleTemplate;
deepResearchTemplateTextArea.addEventListener('input', async () => {
this.plugin.settings.prompts.deepResearchArticleTemplate = deepResearchTemplateTextArea.value;
this.updatePromptsService();
await this.plugin.saveSettings();
});
deepResearchTemplateSetting.settingEl.appendChild(deepResearchTemplateTextArea);
// Image Prompts
containerEl.createEl('h4', { text: 'Image Prompts' });
const imagePromptsSetting = new Setting(containerEl)
.setName('Image References Prompt')
.setDesc('Prompt added to queries when images are enabled');
const imagePromptsTextArea = document.createElement('textarea');
imagePromptsTextArea.rows = 8;
imagePromptsTextArea.cols = 50;
imagePromptsTextArea.style.width = '100%';
imagePromptsTextArea.style.minHeight = '200px';
imagePromptsTextArea.style.fontFamily = 'monospace';
imagePromptsTextArea.placeholder = 'Enter image references prompt...';
imagePromptsTextArea.value = this.plugin.settings.prompts.imageReferencesPrompt;
imagePromptsTextArea.addEventListener('input', async () => {
this.plugin.settings.prompts.imageReferencesPrompt = imagePromptsTextArea.value;
this.updatePromptsService();
await this.plugin.saveSettings();
});
imagePromptsSetting.settingEl.appendChild(imagePromptsTextArea);
// Text Enhancement Prompt
containerEl.createEl('h4', { text: 'Text Enhancement' });
const enhancePromptSetting = new Setting(containerEl)
.setName('Text Enhancement Prompt')
.setDesc('Template for enhancing selected text. Use {TEXT} as placeholder for the selected text.');
const enhancePromptTextArea = document.createElement('textarea');
enhancePromptTextArea.rows = 10;
enhancePromptTextArea.cols = 50;
enhancePromptTextArea.style.width = '100%';
enhancePromptTextArea.style.minHeight = '250px';
enhancePromptTextArea.style.fontFamily = 'monospace';
enhancePromptTextArea.placeholder = 'Enter text enhancement prompt template...';
enhancePromptTextArea.value = this.plugin.settings.prompts.enhancePrompt;
enhancePromptTextArea.addEventListener('input', async () => {
this.plugin.settings.prompts.enhancePrompt = enhancePromptTextArea.value;
this.updatePromptsService();
await this.plugin.saveSettings();
});
enhancePromptSetting.settingEl.appendChild(enhancePromptTextArea);
// Text Enhancement with Images Prompt
const enhanceWithImagesPromptSetting = new Setting(containerEl)
.setName('Related Images Prompt')
.setDesc('Template for requesting related images for selected text. Use {TEXT} as placeholder for the selected text.');
const enhanceWithImagesPromptTextArea = document.createElement('textarea');
enhanceWithImagesPromptTextArea.rows = 10;
enhanceWithImagesPromptTextArea.cols = 50;
enhanceWithImagesPromptTextArea.style.width = '100%';
enhanceWithImagesPromptTextArea.style.minHeight = '250px';
enhanceWithImagesPromptTextArea.style.fontFamily = 'monospace';
enhanceWithImagesPromptTextArea.placeholder = 'Enter related images prompt template...';
enhanceWithImagesPromptTextArea.value = this.plugin.settings.prompts.enhanceWithImagesPrompt;
enhanceWithImagesPromptTextArea.addEventListener('input', async () => {
this.plugin.settings.prompts.enhanceWithImagesPrompt = enhanceWithImagesPromptTextArea.value;
this.updatePromptsService();
await this.plugin.saveSettings();
});
enhanceWithImagesPromptSetting.settingEl.appendChild(enhanceWithImagesPromptTextArea);
}
}