mirror of
https://github.com/andy-stack/vaultkeeper-ai.git
synced 2026-07-22 06:42:03 +00:00
feat: add comprehensive help modal with guide and troubleshooting content
- Add detailed plugin guide covering modes, references, and instructions - Add troubleshooting section with API key and rate limit solutions - Add privacy documentation explaining data storage and exclusions - Make "User Instructions" text clickable to open help modal - Support opening help modal to specific topic via initialTopic prop - Add internal link handling to navigate from help modal to vault files - Update About section with GitHub link and contribution info
This commit is contained in:
parent
0a318a1caf
commit
c9942e9ffa
4 changed files with 208 additions and 38 deletions
|
|
@ -2,6 +2,7 @@
|
|||
import { Copy } from "Enums/Copy";
|
||||
import { Path } from "Enums/Path";
|
||||
import type AIAgentPlugin from "main";
|
||||
import { HelpModal } from "Modals/HelpModal";
|
||||
import { basename } from "path-browserify";
|
||||
import { Resolve } from "Services/DependencyService";
|
||||
import type { FileSystemService } from "Services/FileSystemService";
|
||||
|
|
@ -123,6 +124,11 @@
|
|||
}
|
||||
});
|
||||
}
|
||||
|
||||
function openHelpModal() {
|
||||
const modal = Resolve<HelpModal>(Services.HelpModal);
|
||||
modal.open(2);
|
||||
}
|
||||
</script>
|
||||
|
||||
<div id="user-instruction-results" style:height="{height}px" bind:this={resultsContainer}>
|
||||
|
|
@ -133,7 +139,11 @@
|
|||
<div class="user-instruction-title">
|
||||
<span>
|
||||
{Copy.UserInstructions1}
|
||||
<span id="user-instruction-link">
|
||||
<span id="user-instruction-link"
|
||||
role="link"
|
||||
tabindex="-1"
|
||||
on:click={openHelpModal}
|
||||
on:keydown={openHelpModal}>
|
||||
{Copy.UserInstructions2}
|
||||
</span>
|
||||
{Copy.UserInstructions3}
|
||||
|
|
|
|||
138
Enums/Copy.ts
138
Enums/Copy.ts
|
|
@ -58,18 +58,146 @@ export enum Copy {
|
|||
|
||||
// Help Modal Copy
|
||||
HelpModalAboutTitle = "About",
|
||||
HelpModalAboutContent = `### About AI Agent
|
||||
HelpModalAboutContent = `#### About AI Agent
|
||||
|
||||
**AI Agent** brings the power of Claude, Gemini, and OpenAI directly into your Obsidian vault with intelligent note management capabilities.`,
|
||||
This plugin was originally created for a friend who found it useful, so I have decided to release it to the Obsidian community.
|
||||
|
||||
If you find any issues, please feel free to raise them on GitHub:`,
|
||||
|
||||
HelpModalGuideTitle = "Plugin Guide",
|
||||
HelpModalGuideContent = "",
|
||||
HelpModalGuideContent = `#### How to Use AI Agent
|
||||
|
||||
##### Getting Started
|
||||
1. **Add an API Key**: Go to Settings and add at least one API key (Claude, Gemini, or OpenAI)
|
||||
2. **Select a Model**: Choose your preferred AI model from the dropdown
|
||||
3. **Open the Chat**: Click the sparkles icon in the sidebar to start chatting
|
||||
|
||||
##### Operating Modes
|
||||
|
||||
**Read-Only Mode (Default)** - The AI can safely explore your vault:
|
||||
- Search through your notes
|
||||
- Read file contents
|
||||
- List directory structures
|
||||
- Cannot modify anything
|
||||
|
||||
**Agent Mode** - Toggle when you need the AI to make changes:
|
||||
- Create new notes
|
||||
- Edit existing content
|
||||
- Delete or move files
|
||||
- Rename files
|
||||
|
||||
##### Reference System
|
||||
|
||||
Quickly provide context to the AI:
|
||||
|
||||
- **@filename** - Reference specific files
|
||||
- **#tag** - Reference all notes with a tag
|
||||
- **/folder** - Reference entire directories
|
||||
|
||||
The autocomplete dropdown supports keyboard navigation.
|
||||
|
||||
##### Custom Instructions
|
||||
|
||||
Customize AI behavior for specific workflows:
|
||||
|
||||
1. Create markdown files in **AI Agent/User Instructions/**
|
||||
2. Click the "User Instructions" button in chat
|
||||
3. Select your instruction set
|
||||
4. The AI follows these instructions for all interactions
|
||||
|
||||
See [Example Template](#/page/AI%20Agent%2FUser%20Instructions%2FEXAMPLE_INSTRUCTIONS) for help getting started.`,
|
||||
|
||||
HelpModalTroubleshootTitle = "Troubleshooting",
|
||||
HelpModalTroubleshootContent = "",
|
||||
HelpModalTroubleshootContent = `#### Common Issues & Solutions
|
||||
|
||||
##### API Key Issues
|
||||
|
||||
**Problem**: "Invalid API key" or authentication errors
|
||||
|
||||
**Solutions**:
|
||||
- Verify your API key is correct (copy fresh from provider console)
|
||||
- Check you've added the key for the correct provider
|
||||
- Ensure no extra spaces when pasting
|
||||
- API keys are provider-specific - Claude keys only work with Claude models
|
||||
|
||||
##### Error Code 429: Rate Limit Exceeded
|
||||
|
||||
This error means you've made too many API requests in a given time period. This is separate from your token usage limits.
|
||||
|
||||
**How to resolve:**
|
||||
- Wait for your rate limit to reset (see provider-specific details below)
|
||||
- Lower the context settings in the plugin settings to reduce request frequency
|
||||
- Understand that rate limits typically increase automatically as you use the API more
|
||||
|
||||
###### Claude
|
||||
- **Wait time:** A few hours
|
||||
- **Long-term solution:** Rate limits increase significantly after reaching cumulative spending thresholds
|
||||
- [Claude Rate Limits Documentation](https://docs.claude.com/en/api/rate-limits)
|
||||
|
||||
###### OpenAI
|
||||
- **Wait time:** A few minutes to an hour
|
||||
- **Long-term solution:** Rate limits automatically increase as you progress through usage tiers with higher spending
|
||||
- [OpenAI Rate Limits Documentation](https://platform.openai.com/docs/guides/rate-limits)
|
||||
|
||||
###### Gemini
|
||||
- **Wait time:** A few minutes (per-minute limits) or until midnight Pacific Time (daily quotas)
|
||||
- **Long-term solution:** Enable billing to move from free tier to paid tier for significantly higher limits. Paid tier limits increase automatically with cumulative Google Cloud spending
|
||||
- [Gemini API Rate Limits Documentation](https://ai.google.dev/gemini-api/docs/rate-limits)`,
|
||||
|
||||
HelpModalPrivacyTitle = "Privacy",
|
||||
HelpModalPrivacyContent = "",
|
||||
HelpModalPrivacyContent = `#### Privacy & Security
|
||||
|
||||
##### Data Storage
|
||||
|
||||
**Everything stays local** - This plugin does NOT send data to any third-party services except the AI providers you choose. It also doesn't collect any telemetry data and you can review the full source code on GitHub.
|
||||
|
||||
**What's stored locally**:
|
||||
- API keys (stored in your vault's plugin settings)
|
||||
- Conversation history (stored in \`AI Agent/Conversations/\`)
|
||||
- Custom instructions (stored in \`AI Agent/User Instructions/\`)
|
||||
- Plugin settings (stored in \`.obsidian/plugins/ai-agent-plugin/\`)
|
||||
|
||||
##### API Communication
|
||||
|
||||
**Direct connections only** - The plugin communicates directly with your chosen AI provider:
|
||||
|
||||
- **Claude**: Anthropic's API
|
||||
- **Gemini**: Google's API
|
||||
- **OpenAI**: OpenAI's API
|
||||
|
||||
**What gets sent**:
|
||||
- Your messages and referenced file contents
|
||||
- Conversation context (for continuity)
|
||||
- File names and directory structures (when AI searches)
|
||||
|
||||
**What does NOT get sent**:
|
||||
- Files excluded in settings
|
||||
- Other plugins' data
|
||||
- Your vault structure (unless explicitly requested)
|
||||
- API keys to anyone except the respective provider
|
||||
|
||||
##### File Exclusions
|
||||
|
||||
**Protect sensitive information** using glob patterns in settings:
|
||||
|
||||
**Examples**:
|
||||
- \`private/**\` - Exclude entire directories
|
||||
- \`*.secret.md\` - Exclude specific file patterns
|
||||
- \`journal/personal/**\` - Exclude nested directories
|
||||
- \`.obsidian/workspace.json\` - Exclude specific files
|
||||
|
||||
**How exclusions work**:
|
||||
- Excluded files are completely invisible to the AI
|
||||
- The AI cannot read, search, modify, or even list excluded files
|
||||
- Exclusions apply to All operations
|
||||
|
||||
##### AI Provider Policies
|
||||
|
||||
Each AI provider has their own data policies:
|
||||
|
||||
- [Anthropic (Claude)](https://www.anthropic.com/privacy)
|
||||
- [Google (Gemini)](https://policies.google.com/privacy)
|
||||
- [OpenAI (ChatGPT)](https://openai.com/privacy/)`,
|
||||
|
||||
// SVG Icons
|
||||
GitHubIconPath = "M48.854 0C21.839 0 0 22 0 49.217c0 21.756 13.993 40.172 33.405 46.69 2.427.49 3.316-1.059 3.316-2.362 0-1.141-.08-5.052-.08-9.127-13.59 2.934-16.42-5.867-16.42-5.867-2.184-5.704-5.42-7.17-5.42-7.17-4.448-3.015.324-3.015.324-3.015 4.934.326 7.523 5.052 7.523 5.052 4.367 7.496 11.404 5.378 14.235 4.074.404-3.178 1.699-5.378 3.074-6.6-10.839-1.141-22.243-5.378-22.243-24.283 0-5.378 1.94-9.778 5.014-13.2-.485-1.222-2.184-6.275.486-13.038 0 0 4.125-1.304 13.426 5.052a46.97 46.97 0 0 1 12.214-1.63c4.125 0 8.33.571 12.213 1.63 9.302-6.356 13.427-5.052 13.427-5.052 2.67 6.763.97 11.816.485 13.038 3.155 3.422 5.015 7.822 5.015 13.2 0 18.905-11.404 23.06-22.324 24.283 1.78 1.548 3.316 4.481 3.316 9.126 0 6.6-.08 11.897-.08 13.526 0 1.304.89 2.853 3.316 2.364 19.412-6.52 33.405-24.935 33.405-46.691C97.707 22 75.788 0 48.854 0z",
|
||||
|
|
|
|||
|
|
@ -24,11 +24,17 @@ export class HelpModal extends Modal {
|
|||
this.component = mount(HelpModalSvelte, {
|
||||
target: contentEl,
|
||||
props: {
|
||||
onClose: () => this.close()
|
||||
onClose: () => this.close(),
|
||||
initialTopic: (this as any).initialTopic
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public open(initialTopic?: number): void {
|
||||
(this as any).initialTopic = initialTopic;
|
||||
super.open();
|
||||
}
|
||||
|
||||
onClose() {
|
||||
if (this.component) {
|
||||
unmount(this.component);
|
||||
|
|
|
|||
|
|
@ -1,20 +1,25 @@
|
|||
<script lang="ts">
|
||||
import { Copy } from "Enums/Copy";
|
||||
import { Selector } from "Enums/Selector";
|
||||
import type AIAgentPlugin from "main";
|
||||
import { DropdownComponent, setIcon } from "obsidian";
|
||||
import { Resolve } from "Services/DependencyService";
|
||||
import { Services } from "Services/Services";
|
||||
import type { StreamingMarkdownService } from "Services/StreamingMarkdownService";
|
||||
import type { WorkSpaceService } from "Services/WorkSpaceService";
|
||||
import { fade } from "svelte/transition";
|
||||
import { onMount } from "svelte";
|
||||
|
||||
export let onClose: () => void;
|
||||
export let initialTopic: number = 1;
|
||||
|
||||
const plugin: AIAgentPlugin = Resolve<AIAgentPlugin>(Services.AIAgentPlugin);
|
||||
const streamingMarkdownService: StreamingMarkdownService = Resolve<StreamingMarkdownService>(Services.StreamingMarkdownService);
|
||||
const workSpaceService: WorkSpaceService = Resolve<WorkSpaceService>(Services.WorkSpaceService);
|
||||
|
||||
let closeButton: HTMLButtonElement;
|
||||
let dropdownContainer: HTMLDivElement;
|
||||
let contentContainer: HTMLDivElement;
|
||||
|
||||
const topics: Record<number, { title: string; content: string }> = {
|
||||
1: {
|
||||
|
|
@ -35,7 +40,7 @@
|
|||
}
|
||||
};
|
||||
|
||||
let selectedTopic: number = 1;
|
||||
let selectedTopic: number = initialTopic;
|
||||
let title: string = topics[selectedTopic].title;
|
||||
let content: string = streamingMarkdownService.formatText(topics[selectedTopic].content);
|
||||
|
||||
|
|
@ -53,6 +58,29 @@
|
|||
setIcon(closeButton, 'circle-x');
|
||||
}
|
||||
|
||||
async function handleLinkClick(evt: MouseEvent) {
|
||||
const target = evt.target as HTMLElement;
|
||||
|
||||
// Check for both internal wikilinks and regular markdown links
|
||||
const link = target.closest('a') as HTMLAnchorElement | null;
|
||||
if (!link) {
|
||||
return;
|
||||
}
|
||||
|
||||
const href = link.getAttribute('href');
|
||||
if (!href || !href.startsWith('#/page/')) {
|
||||
return;
|
||||
}
|
||||
|
||||
evt.preventDefault();
|
||||
evt.stopPropagation();
|
||||
|
||||
const encodedPath = href.replace('#/page/', '');
|
||||
const notePath = decodeURIComponent(encodedPath);
|
||||
await workSpaceService.openNote(notePath);
|
||||
onClose();
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
if (dropdownContainer) {
|
||||
const dropdown = new DropdownComponent(dropdownContainer);
|
||||
|
|
@ -70,6 +98,10 @@
|
|||
selectTopic(Number(value));
|
||||
});
|
||||
}
|
||||
|
||||
if (contentContainer) {
|
||||
plugin.registerDomEvent(contentContainer, 'click', handleLinkClick);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
|
|
@ -144,39 +176,33 @@
|
|||
Plugin version: {plugin.manifest.version}
|
||||
</div>
|
||||
</div>
|
||||
<div class="help-modal-content">
|
||||
<div class="help-modal-content" bind:this={contentContainer}>
|
||||
{#if content !== ""}
|
||||
<div transition:fade={{ duration: 100 }}>
|
||||
{@html content}
|
||||
{#if selectedTopic === 1}
|
||||
<hr style="margin: 2em 0; border-width: 1px">
|
||||
<h4>Links</h4>
|
||||
<ul style="list-style: none; padding-left: 0;">
|
||||
<li style="margin-bottom: 0.5em;">
|
||||
<a
|
||||
href="https://github.com/yourusername/ai-agent-plugin"
|
||||
style="text-decoration: none; display: inline-flex; align-items: center; gap: 0.5em;">
|
||||
<svg
|
||||
width="1em"
|
||||
height="1em"
|
||||
viewBox="0 0 98 96"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
aria-label="GitHub"
|
||||
style="display: inline-block; vertical-align: middle;">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d={Copy.GitHubIconPath} fill="currentColor"/>
|
||||
</svg>
|
||||
<span>View on GitHub</span>
|
||||
</a>
|
||||
</li>
|
||||
<li style="margin-bottom: 0.5em;">
|
||||
<a href="https://buymeacoffee.com/yourusername" style="text-decoration: none; display: inline-flex; align-items: center; gap: 0.5em;">
|
||||
<span>☕</span>
|
||||
<span>Buy me a coffee</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
<hr style="margin: 2em 0; border-width: 1px">
|
||||
<p style="font-style: italic; text-align: center;">Created with ❤️ for the Obsidian community</p>
|
||||
<a
|
||||
href="https://github.com/Andy-Stack/ai-agent-plugin"
|
||||
style="text-decoration: none; display: inline-flex; align-items: center; gap: 0.5em; margin: 0 0 1em 0;">
|
||||
<svg
|
||||
width="1em"
|
||||
height="1em"
|
||||
viewBox="0 0 98 96"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
aria-label="GitHub"
|
||||
style="display: inline-block; vertical-align: middle;">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d={Copy.GitHubIconPath} fill="currentColor"/>
|
||||
</svg>
|
||||
<span>View on GitHub</span>
|
||||
</a>
|
||||
<br>
|
||||
<span style="display: inline-block; margin-bottom: 1em;">If you enjoy using the plugin or find it useful and want to contribute, you can buy me a Coffee using the link below:</span>
|
||||
<br>
|
||||
<a href="a" style="text-decoration: none; display: inline-flex; align-items: center; gap: 0.5em;">
|
||||
<span>☕</span>
|
||||
<span>Buy me a coffee</span>
|
||||
</a>
|
||||
<p style="margin-top: 2em; font-style: italic;">Thanks for using the AI Agent plugin!</p>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
|
|
@ -337,7 +363,7 @@
|
|||
height: 100%;
|
||||
border-radius: var(--radius-m);
|
||||
background-color: var(--alt-background-primary);
|
||||
padding: 0 var(--size-4-2) var(--size-4-2) var(--size-4-6);
|
||||
padding: 0 var(--size-4-2) var(--size-4-2) var(--size-4-3);
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue