summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorAdam Malczewski <[email protected]>2026-03-24 17:30:44 +0900
committerAdam Malczewski <[email protected]>2026-03-24 17:30:44 +0900
commita5f54269f6b7ace71c4509fb8105993a7f064e63 (patch)
treedfdfd0349cbfa357c02d26d02edbf3d6fe689872
parent3690c97ceaf8a20bb2c6d38bd600e5ae8bc2dac6 (diff)
downloadai-pulse-obsidian-plugin-a5f54269f6b7ace71c4509fb8105993a7f064e63.tar.gz
ai-pulse-obsidian-plugin-a5f54269f6b7ace71c4509fb8105993a7f064e63.zip
rename project
-rw-r--r--.notes/research/plugin-feature-ideas.md2
-rw-r--r--.rules/default/obsidian.md4
-rw-r--r--.rules/default/ollama.md2
-rw-r--r--README.md14
-rw-r--r--manifest.json6
-rw-r--r--package-lock.json4
-rw-r--r--package.json4
-rw-r--r--src/chat-view.ts148
-rw-r--r--src/main.ts8
-rw-r--r--src/settings-modal.ts16
-rw-r--r--src/settings.ts4
-rw-r--r--src/tool-modal.ts10
-rw-r--r--styles.css206
13 files changed, 214 insertions, 214 deletions
diff --git a/.notes/research/plugin-feature-ideas.md b/.notes/research/plugin-feature-ideas.md
index ffd6752..fa76f62 100644
--- a/.notes/research/plugin-feature-ideas.md
+++ b/.notes/research/plugin-feature-ideas.md
@@ -1,6 +1,6 @@
# Plugin Feature Ideas
-Ideas for the AI Note Organizer plugin, drawn from the Obsidian and Ollama APIs.
+Ideas for the AI Pulse plugin, drawn from the Obsidian and Ollama APIs.
---
diff --git a/.rules/default/obsidian.md b/.rules/default/obsidian.md
index 0330edd..22fe06f 100644
--- a/.rules/default/obsidian.md
+++ b/.rules/default/obsidian.md
@@ -1,6 +1,6 @@
-# Obsidian API Docs — AI Note Organizer
+# Obsidian API Docs — AI Pulse
-This plugin organizes notes via AI powered by Ollama. Docs are in `.rules/docs/obsidian/`.
+The Obsidian AI Note Management Plugin — powered by Ollama. Docs are in `.rules/docs/obsidian/`.
## Where to Look
diff --git a/.rules/default/ollama.md b/.rules/default/ollama.md
index 83562b8..0a6b559 100644
--- a/.rules/default/ollama.md
+++ b/.rules/default/ollama.md
@@ -1,4 +1,4 @@
-# Ollama API Docs — AI Note Organizer
+# Ollama API Docs — AI Pulse
Local LLM inference via Ollama (`http://localhost:11434`). Docs are in `.rules/docs/ollama/`.
diff --git a/README.md b/README.md
index 93c50da..0f2e19f 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
-# AI Organizer
+# AI Pulse
-An Obsidian plugin that organizes notes via AI powered by [Ollama](https://ollama.com).
+The Obsidian AI Note Management Plugin — powered by [Ollama](https://ollama.com).
## Prerequisites
@@ -11,8 +11,8 @@ An Obsidian plugin that organizes notes via AI powered by [Ollama](https://ollam
## Building from Source
```bash
-git clone https://github.com/your-repo/aiorganizer_obsidian.git
-cd aiorganizer_obsidian
+git clone https://github.com/your-repo/ai-pulse.git
+cd ai-pulse
npm install
npm run build
```
@@ -30,9 +30,9 @@ npm run dev
1. Build the plugin (see above).
2. Copy `main.js`, `styles.css`, and `manifest.json` into your vault at:
```
- <VaultFolder>/.obsidian/plugins/ai-organizer/
+ <VaultFolder>/.obsidian/plugins/ai-pulse/
```
-3. Open Obsidian, go to **Settings > Community Plugins**, and enable **AI Organizer**.
+3. Open Obsidian, go to **Settings > Community Plugins**, and enable **AI Pulse**.
### Development Installation
@@ -40,7 +40,7 @@ Clone or symlink this repo directly into your vault's plugin folder for live dev
```bash
cd /path/to/your/vault/.obsidian/plugins
-ln -s /path/to/aiorganizer_obsidian ai-organizer
+ln -s /path/to/ai-pulse ai-pulse
```
Then run `npm run dev` and reload Obsidian to pick up changes.
diff --git a/manifest.json b/manifest.json
index 9ba100f..e2432e8 100644
--- a/manifest.json
+++ b/manifest.json
@@ -1,9 +1,9 @@
{
- "id": "ai-organizer",
- "name": "AI Organizer",
+ "id": "ai-pulse",
+ "name": "AI Pulse",
"version": "1.0.0",
"minAppVersion": "0.15.0",
- "description": "Organize notes via AI powered by Ollama.",
+ "description": "The Obsidian AI Note Management Plugin — powered by Ollama.",
"author": "Adam Malczewski",
"authorUrl": "https://malcz.com",
"isDesktopOnly": false
diff --git a/package-lock.json b/package-lock.json
index 32e7b1f..d94f1e7 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,11 +1,11 @@
{
- "name": "ai-organizer",
+ "name": "ai-pulse",
"version": "1.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
- "name": "ai-organizer",
+ "name": "ai-pulse",
"version": "1.0.0",
"license": "0-BSD",
"dependencies": {
diff --git a/package.json b/package.json
index 5f99ba4..f77c8b2 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
{
- "name": "ai-organizer",
+ "name": "ai-pulse",
"version": "1.0.0",
- "description": "Organize notes via AI powered by Ollama.",
+ "description": "The Obsidian AI Note Management Plugin \u2014 powered by Ollama.",
"main": "main.js",
"type": "module",
"scripts": {
diff --git a/src/chat-view.ts b/src/chat-view.ts
index 7d34fcb..7975cd2 100644
--- a/src/chat-view.ts
+++ b/src/chat-view.ts
@@ -1,5 +1,5 @@
import { ItemView, MarkdownRenderer, Notice, TFile, WorkspaceLeaf, setIcon } from "obsidian";
-import type AIOrganizer from "./main";
+import type AIPulse from "./main";
import type { ChatMessage, ToolCallEvent, ApprovalRequestEvent } from "./ollama-client";
import { sendChatMessageStreaming } from "./ollama-client";
import { SettingsModal } from "./settings-modal";
@@ -7,10 +7,10 @@ import { ToolModal } from "./tool-modal";
import { TOOL_REGISTRY } from "./tools";
import type { OllamaToolDefinition } from "./tools";
-export const VIEW_TYPE_CHAT = "ai-organizer-chat";
+export const VIEW_TYPE_CHAT = "ai-pulse-chat";
export class ChatView extends ItemView {
- private plugin: AIOrganizer;
+ private plugin: AIPulse;
private messages: ChatMessage[] = [];
private messageContainer: HTMLDivElement | null = null;
private textarea: HTMLTextAreaElement | null = null;
@@ -21,7 +21,7 @@ export class ChatView extends ItemView {
private bubbleContent: Map<HTMLDivElement, string> = new Map();
private modelBadge: HTMLDivElement | null = null;
- constructor(leaf: WorkspaceLeaf, plugin: AIOrganizer) {
+ constructor(leaf: WorkspaceLeaf, plugin: AIPulse) {
super(leaf);
this.plugin = plugin;
}
@@ -41,32 +41,32 @@ export class ChatView extends ItemView {
async onOpen(): Promise<void> {
const { contentEl } = this;
contentEl.empty();
- contentEl.addClass("ai-organizer-chat-container");
+ contentEl.addClass("ai-pulse-chat-container");
// --- Top region: Chat area ---
- const messagesArea = contentEl.createDiv({ cls: "ai-organizer-messages-area" });
- this.messageContainer = messagesArea.createDiv({ cls: "ai-organizer-messages" });
+ const messagesArea = contentEl.createDiv({ cls: "ai-pulse-messages-area" });
+ this.messageContainer = messagesArea.createDiv({ cls: "ai-pulse-messages" });
// --- Model Badge (top left) ---
- this.modelBadge = messagesArea.createDiv({ cls: "ai-organizer-model-badge" });
+ this.modelBadge = messagesArea.createDiv({ cls: "ai-pulse-model-badge" });
this.updateModelBadge();
// --- FAB Speed Dial ---
- const fab = messagesArea.createDiv({ cls: "ai-organizer-fab" });
+ const fab = messagesArea.createDiv({ cls: "ai-pulse-fab" });
// Main FAB trigger button (first child)
const fabTrigger = fab.createEl("button", {
- cls: "ai-organizer-fab-trigger",
+ cls: "ai-pulse-fab-trigger",
attr: { "aria-label": "Actions", tabindex: "0" },
});
setIcon(fabTrigger, "settings");
// Speed dial actions (revealed on focus-within)
- const settingsAction = fab.createDiv({ cls: "ai-organizer-fab-action" });
- const settingsLabel = settingsAction.createSpan({ cls: "ai-organizer-fab-label", text: "AI Settings" });
+ const settingsAction = fab.createDiv({ cls: "ai-pulse-fab-action" });
+ const settingsLabel = settingsAction.createSpan({ cls: "ai-pulse-fab-label", text: "AI Settings" });
void settingsLabel;
const settingsBtn = settingsAction.createEl("button", {
- cls: "ai-organizer-fab-btn",
+ cls: "ai-pulse-fab-btn",
attr: { "aria-label": "Settings" },
});
setIcon(settingsBtn, "sliders-horizontal");
@@ -80,11 +80,11 @@ export class ChatView extends ItemView {
(document.activeElement as HTMLElement)?.blur();
});
- const toolsAction = fab.createDiv({ cls: "ai-organizer-fab-action" });
- const toolsLabel = toolsAction.createSpan({ cls: "ai-organizer-fab-label", text: "Tools" });
+ const toolsAction = fab.createDiv({ cls: "ai-pulse-fab-action" });
+ const toolsLabel = toolsAction.createSpan({ cls: "ai-pulse-fab-label", text: "Tools" });
void toolsLabel;
this.toolsButton = toolsAction.createEl("button", {
- cls: "ai-organizer-fab-btn",
+ cls: "ai-pulse-fab-btn",
attr: { "aria-label": "Tools" },
});
setIcon(this.toolsButton, "wrench");
@@ -98,11 +98,11 @@ export class ChatView extends ItemView {
(document.activeElement as HTMLElement)?.blur();
});
- const clearAction = fab.createDiv({ cls: "ai-organizer-fab-action" });
- const clearLabel = clearAction.createSpan({ cls: "ai-organizer-fab-label", text: "Clear Chat" });
+ const clearAction = fab.createDiv({ cls: "ai-pulse-fab-action" });
+ const clearLabel = clearAction.createSpan({ cls: "ai-pulse-fab-label", text: "Clear Chat" });
void clearLabel;
const clearBtn = clearAction.createEl("button", {
- cls: "ai-organizer-fab-btn",
+ cls: "ai-pulse-fab-btn",
attr: { "aria-label": "Clear Chat" },
});
setIcon(clearBtn, "trash-2");
@@ -115,7 +115,7 @@ export class ChatView extends ItemView {
(document.activeElement as HTMLElement)?.blur();
});
- const inputRow = messagesArea.createDiv({ cls: "ai-organizer-input-row" });
+ const inputRow = messagesArea.createDiv({ cls: "ai-pulse-input-row" });
this.textarea = inputRow.createEl("textarea", {
attr: { placeholder: "Type a message...", rows: "2" },
});
@@ -176,7 +176,7 @@ export class ChatView extends ItemView {
private updateToolsButtonState(): void {
if (this.toolsButton === null) return;
- this.toolsButton.toggleClass("ai-organizer-tools-active", this.hasAnyToolEnabled());
+ this.toolsButton.toggleClass("ai-pulse-tools-active", this.hasAnyToolEnabled());
}
private updateModelBadge(): void {
@@ -184,10 +184,10 @@ export class ChatView extends ItemView {
const model = this.plugin.settings.model;
if (model === "") {
this.modelBadge.setText("No model selected");
- this.modelBadge.addClass("ai-organizer-model-badge-empty");
+ this.modelBadge.addClass("ai-pulse-model-badge-empty");
} else {
this.modelBadge.setText(model);
- this.modelBadge.removeClass("ai-organizer-model-badge-empty");
+ this.modelBadge.removeClass("ai-pulse-model-badge-empty");
}
}
@@ -267,7 +267,7 @@ export class ChatView extends ItemView {
const onChunk = (chunk: string): void => {
if (currentBubble !== null) {
// Remove the loading indicator on first chunk
- const loadingIcon = currentBubble.querySelector(".ai-organizer-loading-icon");
+ const loadingIcon = currentBubble.querySelector(".ai-pulse-loading-icon");
if (loadingIcon !== null) {
loadingIcon.remove();
}
@@ -307,8 +307,8 @@ export class ChatView extends ItemView {
} catch (err: unknown) {
// Finalize bubble even on error
if (currentBubble !== null) {
- (currentBubble as HTMLDivElement).removeClass("ai-organizer-streaming");
- const errorIcon = (currentBubble as HTMLDivElement).querySelector(".ai-organizer-loading-icon");
+ (currentBubble as HTMLDivElement).removeClass("ai-pulse-streaming");
+ const errorIcon = (currentBubble as HTMLDivElement).querySelector(".ai-pulse-loading-icon");
if (errorIcon !== null) {
errorIcon.remove();
}
@@ -337,10 +337,10 @@ export class ChatView extends ItemView {
throw new Error("Message container not initialized.");
}
const bubble = this.messageContainer.createDiv({
- cls: "ai-organizer-message assistant ai-organizer-streaming",
+ cls: "ai-pulse-message assistant ai-pulse-streaming",
});
// Add a loading indicator icon
- const iconSpan = bubble.createSpan({ cls: "ai-organizer-loading-icon" });
+ const iconSpan = bubble.createSpan({ cls: "ai-pulse-loading-icon" });
setIcon(iconSpan, "more-horizontal");
return bubble;
}
@@ -350,10 +350,10 @@ export class ChatView extends ItemView {
* and clean up the accumulated content tracker.
*/
private async finalizeBubble(bubble: HTMLDivElement): Promise<void> {
- bubble.removeClass("ai-organizer-streaming");
+ bubble.removeClass("ai-pulse-streaming");
// Remove loading icon if still present
- const loadingIcon = bubble.querySelector(".ai-organizer-loading-icon");
+ const loadingIcon = bubble.querySelector(".ai-pulse-loading-icon");
if (loadingIcon !== null) {
loadingIcon.remove();
}
@@ -369,8 +369,8 @@ export class ChatView extends ItemView {
// Replace plain text with rendered markdown
bubble.empty();
- bubble.removeClass("ai-organizer-streaming-text");
- bubble.addClass("ai-organizer-markdown");
+ bubble.removeClass("ai-pulse-streaming-text");
+ bubble.addClass("ai-pulse-markdown");
await MarkdownRenderer.render(
this.plugin.app,
rawText,
@@ -400,8 +400,8 @@ export class ChatView extends ItemView {
const cls =
role === "error"
- ? "ai-organizer-message assistant error"
- : `ai-organizer-message ${role}`;
+ ? "ai-pulse-message assistant error"
+ : `ai-pulse-message ${role}`;
this.messageContainer.createDiv({ cls, text: content });
}
@@ -411,32 +411,32 @@ export class ChatView extends ItemView {
return;
}
- const container = this.messageContainer.createDiv({ cls: "ai-organizer-tool-call" });
+ const container = this.messageContainer.createDiv({ cls: "ai-pulse-tool-call" });
- const header = container.createDiv({ cls: "ai-organizer-tool-call-header" });
- setIcon(header.createSpan({ cls: "ai-organizer-tool-call-icon" }), "wrench");
- header.createSpan({ text: event.friendlyName, cls: "ai-organizer-tool-call-name" });
+ const header = container.createDiv({ cls: "ai-pulse-tool-call-header" });
+ setIcon(header.createSpan({ cls: "ai-pulse-tool-call-icon" }), "wrench");
+ header.createSpan({ text: event.friendlyName, cls: "ai-pulse-tool-call-name" });
- container.createDiv({ text: event.summary, cls: "ai-organizer-tool-call-summary" });
- container.createDiv({ text: event.resultSummary, cls: "ai-organizer-tool-call-result-summary" });
+ container.createDiv({ text: event.summary, cls: "ai-pulse-tool-call-summary" });
+ container.createDiv({ text: event.resultSummary, cls: "ai-pulse-tool-call-result-summary" });
// DaisyUI-style collapse with checkbox
- const collapse = container.createDiv({ cls: "ai-organizer-collapse ai-organizer-collapse-arrow" });
+ const collapse = container.createDiv({ cls: "ai-pulse-collapse ai-pulse-collapse-arrow" });
const collapseId = `tool-collapse-${Date.now()}-${Math.random().toString(36).slice(2, 6)}`;
const checkbox = collapse.createEl("input", {
type: "checkbox",
attr: { id: collapseId },
});
- checkbox.addClass("ai-organizer-collapse-toggle");
+ checkbox.addClass("ai-pulse-collapse-toggle");
const titleEl = collapse.createEl("label", {
- cls: "ai-organizer-collapse-title",
+ cls: "ai-pulse-collapse-title",
attr: { for: collapseId },
text: "Details",
});
void titleEl; // suppress unused warning
- const collapseContent = collapse.createDiv({ cls: "ai-organizer-collapse-content" });
- const contentInner = collapseContent.createDiv({ cls: "ai-organizer-collapse-content-inner" });
+ const collapseContent = collapse.createDiv({ cls: "ai-pulse-collapse-content" });
+ const contentInner = collapseContent.createDiv({ cls: "ai-pulse-collapse-content-inner" });
if (event.toolName === "edit_file") {
// For edit_file, show old_text / new_text in dedicated labeled blocks
@@ -445,28 +445,28 @@ export class ChatView extends ItemView {
const newText = typeof event.args.new_text === "string" ? event.args.new_text : "";
if (filePath !== "") {
- contentInner.createEl("div", { text: `File: ${filePath}`, cls: "ai-organizer-tool-call-label" });
+ contentInner.createEl("div", { text: `File: ${filePath}`, cls: "ai-pulse-tool-call-label" });
}
- contentInner.createEl("div", { text: "Old text:", cls: "ai-organizer-tool-call-label" });
+ contentInner.createEl("div", { text: "Old text:", cls: "ai-pulse-tool-call-label" });
contentInner.createEl("pre", {
text: oldText === "" ? "(empty — new file)" : oldText,
- cls: "ai-organizer-tool-call-args",
+ cls: "ai-pulse-tool-call-args",
});
- contentInner.createEl("div", { text: "New text:", cls: "ai-organizer-tool-call-label" });
+ contentInner.createEl("div", { text: "New text:", cls: "ai-pulse-tool-call-label" });
contentInner.createEl("pre", {
text: newText,
- cls: "ai-organizer-tool-call-result",
+ cls: "ai-pulse-tool-call-result",
});
} else {
const argsStr = JSON.stringify(event.args, null, 2);
- contentInner.createEl("pre", { text: argsStr, cls: "ai-organizer-tool-call-args" });
+ contentInner.createEl("pre", { text: argsStr, cls: "ai-pulse-tool-call-args" });
const resultPreview = event.result.length > 500
? event.result.substring(0, 500) + "..."
: event.result;
- contentInner.createEl("pre", { text: resultPreview, cls: "ai-organizer-tool-call-result" });
+ contentInner.createEl("pre", { text: resultPreview, cls: "ai-pulse-tool-call-result" });
}
}
@@ -477,78 +477,78 @@ export class ChatView extends ItemView {
return;
}
- const container = this.messageContainer.createDiv({ cls: "ai-organizer-approval" });
+ const container = this.messageContainer.createDiv({ cls: "ai-pulse-approval" });
- const header = container.createDiv({ cls: "ai-organizer-approval-header" });
- setIcon(header.createSpan({ cls: "ai-organizer-approval-icon" }), "shield-alert");
- header.createSpan({ text: event.friendlyName, cls: "ai-organizer-approval-name" });
+ const header = container.createDiv({ cls: "ai-pulse-approval-header" });
+ setIcon(header.createSpan({ cls: "ai-pulse-approval-icon" }), "shield-alert");
+ header.createSpan({ text: event.friendlyName, cls: "ai-pulse-approval-name" });
- container.createDiv({ text: event.message, cls: "ai-organizer-approval-message" });
+ container.createDiv({ text: event.message, cls: "ai-pulse-approval-message" });
// Show details for edit_file so the user can review the change
if (event.toolName === "edit_file" || event.toolName === "create_file") {
- const collapse = container.createDiv({ cls: "ai-organizer-collapse ai-organizer-collapse-arrow" });
+ const collapse = container.createDiv({ cls: "ai-pulse-collapse ai-pulse-collapse-arrow" });
const collapseId = `approval-collapse-${Date.now()}-${Math.random().toString(36).slice(2, 6)}`;
const checkbox = collapse.createEl("input", {
type: "checkbox",
attr: { id: collapseId, checked: "" },
});
- checkbox.addClass("ai-organizer-collapse-toggle");
+ checkbox.addClass("ai-pulse-collapse-toggle");
checkbox.checked = true;
const titleEl = collapse.createEl("label", {
- cls: "ai-organizer-collapse-title",
+ cls: "ai-pulse-collapse-title",
attr: { for: collapseId },
text: event.toolName === "create_file" ? "Review content" : "Review changes",
});
void titleEl;
- const collapseContent = collapse.createDiv({ cls: "ai-organizer-collapse-content" });
- const contentInner = collapseContent.createDiv({ cls: "ai-organizer-collapse-content-inner" });
+ const collapseContent = collapse.createDiv({ cls: "ai-pulse-collapse-content" });
+ const contentInner = collapseContent.createDiv({ cls: "ai-pulse-collapse-content-inner" });
if (event.toolName === "edit_file") {
const oldText = typeof event.args.old_text === "string" ? event.args.old_text : "";
const newText = typeof event.args.new_text === "string" ? event.args.new_text : "";
- contentInner.createEl("div", { text: "Old text:", cls: "ai-organizer-tool-call-label" });
+ contentInner.createEl("div", { text: "Old text:", cls: "ai-pulse-tool-call-label" });
contentInner.createEl("pre", {
text: oldText === "" ? "(empty \u2014 new file)" : oldText,
- cls: "ai-organizer-tool-call-args",
+ cls: "ai-pulse-tool-call-args",
});
- contentInner.createEl("div", { text: "New text:", cls: "ai-organizer-tool-call-label" });
+ contentInner.createEl("div", { text: "New text:", cls: "ai-pulse-tool-call-label" });
contentInner.createEl("pre", {
text: newText,
- cls: "ai-organizer-tool-call-result",
+ cls: "ai-pulse-tool-call-result",
});
} else {
// create_file
const content = typeof event.args.content === "string" ? event.args.content : "";
- contentInner.createEl("div", { text: "Content:", cls: "ai-organizer-tool-call-label" });
+ contentInner.createEl("div", { text: "Content:", cls: "ai-pulse-tool-call-label" });
contentInner.createEl("pre", {
text: content === "" ? "(empty file)" : content,
- cls: "ai-organizer-tool-call-result",
+ cls: "ai-pulse-tool-call-result",
});
}
}
- const buttonRow = container.createDiv({ cls: "ai-organizer-approval-buttons" });
+ const buttonRow = container.createDiv({ cls: "ai-pulse-approval-buttons" });
const approveBtn = buttonRow.createEl("button", {
text: "Approve",
- cls: "ai-organizer-approval-approve",
+ cls: "ai-pulse-approval-approve",
});
const declineBtn = buttonRow.createEl("button", {
text: "Decline",
- cls: "ai-organizer-approval-decline",
+ cls: "ai-pulse-approval-decline",
});
const finalize = (approved: boolean): void => {
approveBtn.disabled = true;
declineBtn.disabled = true;
- container.addClass(approved ? "ai-organizer-approval-approved" : "ai-organizer-approval-declined");
- const statusEl = container.createDiv({ cls: "ai-organizer-approval-status" });
+ container.addClass(approved ? "ai-pulse-approval-approved" : "ai-pulse-approval-declined");
+ const statusEl = container.createDiv({ cls: "ai-pulse-approval-status" });
statusEl.setText(approved ? "Approved" : "Declined");
this.scrollToBottom();
resolve(approved);
@@ -581,7 +581,7 @@ export class ChatView extends ItemView {
}
if (this.sendButton !== null) {
this.sendButton.textContent = streaming ? "Stop" : "Send";
- this.sendButton.toggleClass("ai-organizer-stop-btn", streaming);
+ this.sendButton.toggleClass("ai-pulse-stop-btn", streaming);
}
}
}
diff --git a/src/main.ts b/src/main.ts
index d120bdf..e66a126 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -1,11 +1,11 @@
import { Plugin, WorkspaceLeaf } from "obsidian";
-import { AIOrganizerSettings, DEFAULT_SETTINGS } from "./settings";
+import { AIPulseSettings, DEFAULT_SETTINGS } from "./settings";
import { ChatView, VIEW_TYPE_CHAT } from "./chat-view";
import { testConnection, listModels } from "./ollama-client";
import { getDefaultToolStates } from "./tools";
-export default class AIOrganizer extends Plugin {
- settings: AIOrganizerSettings = DEFAULT_SETTINGS;
+export default class AIPulse extends Plugin {
+ settings: AIPulseSettings = DEFAULT_SETTINGS;
// Runtime connection state (not persisted)
connectionStatus: "disconnected" | "connecting" | "connected" | "error" = "disconnected";
@@ -57,7 +57,7 @@ export default class AIOrganizer extends Plugin {
this.settings = Object.assign(
{},
DEFAULT_SETTINGS,
- await this.loadData() as Partial<AIOrganizerSettings> | null,
+ await this.loadData() as Partial<AIPulseSettings> | null,
);
// Ensure enabledTools has entries for all registered tools
this.settings.enabledTools = Object.assign(
diff --git a/src/settings-modal.ts b/src/settings-modal.ts
index e475c3f..9a4218c 100644
--- a/src/settings-modal.ts
+++ b/src/settings-modal.ts
@@ -1,15 +1,15 @@
import { Modal, Setting } from "obsidian";
-import type AIOrganizer from "./main";
+import type AIPulse from "./main";
import { showModel } from "./ollama-client";
import type { ModelInfo } from "./ollama-client";
export class SettingsModal extends Modal {
- private plugin: AIOrganizer;
+ private plugin: AIPulse;
private modelInfo: ModelInfo | null = null;
private ctxMaxEl: HTMLElement | null = null;
private ctxInputEl: HTMLInputElement | null = null;
- constructor(plugin: AIOrganizer) {
+ constructor(plugin: AIPulse) {
super(plugin.app);
this.plugin = plugin;
}
@@ -17,7 +17,7 @@ export class SettingsModal extends Modal {
onOpen(): void {
const { contentEl } = this;
contentEl.empty();
- contentEl.addClass("ai-organizer-settings-modal");
+ contentEl.addClass("ai-pulse-settings-modal");
this.setTitle("AI Settings");
@@ -97,7 +97,7 @@ export class SettingsModal extends Modal {
});
const updateFileSettingState = (enabled: boolean): void => {
- fileSetting.settingEl.toggleClass("ai-organizer-setting-disabled", !enabled);
+ fileSetting.settingEl.toggleClass("ai-pulse-setting-disabled", !enabled);
if (fileInputEl !== null) {
fileInputEl.disabled = !enabled;
}
@@ -135,7 +135,7 @@ export class SettingsModal extends Modal {
.setDesc("Controls randomness. Lower = more focused, higher = more creative.");
const tempValueEl = tempSetting.descEl.createSpan({
- cls: "ai-organizer-param-value",
+ cls: "ai-pulse-param-value",
text: ` (${this.plugin.settings.temperature.toFixed(2)})`,
});
@@ -180,7 +180,7 @@ export class SettingsModal extends Modal {
const ctxControlEl = ctxSetting.controlEl;
ctxControlEl.style.flexDirection = "column";
ctxControlEl.style.alignItems = "flex-end";
- this.ctxMaxEl = ctxControlEl.createDiv({ cls: "ai-organizer-ctx-max" });
+ this.ctxMaxEl = ctxControlEl.createDiv({ cls: "ai-pulse-ctx-max" });
this.ctxMaxEl.style.cursor = "pointer";
this.ctxMaxEl.addEventListener("click", async () => {
if (this.modelInfo !== null && this.ctxInputEl !== null) {
@@ -242,7 +242,7 @@ export class SettingsModal extends Modal {
private updateCtxMaxWarning(): void {
if (this.ctxMaxEl === null || this.modelInfo === null) return;
const exceeds = this.plugin.settings.numCtx > this.modelInfo.contextLength;
- this.ctxMaxEl.toggleClass("ai-organizer-ctx-max-warn", exceeds);
+ this.ctxMaxEl.toggleClass("ai-pulse-ctx-max-warn", exceeds);
}
private populateModelDropdown(selectEl: HTMLSelectElement): void {
diff --git a/src/settings.ts b/src/settings.ts
index 8e73770..eb42c3f 100644
--- a/src/settings.ts
+++ b/src/settings.ts
@@ -1,6 +1,6 @@
import { getDefaultToolStates } from "./tools";
-export interface AIOrganizerSettings {
+export interface AIPulseSettings {
ollamaUrl: string;
model: string;
enabledTools: Record<string, boolean>;
@@ -11,7 +11,7 @@ export interface AIOrganizerSettings {
systemPromptFile: string;
}
-export const DEFAULT_SETTINGS: AIOrganizerSettings = {
+export const DEFAULT_SETTINGS: AIPulseSettings = {
ollamaUrl: "http://localhost:11434",
model: "",
enabledTools: getDefaultToolStates(),
diff --git a/src/tool-modal.ts b/src/tool-modal.ts
index c1b1522..4da8edb 100644
--- a/src/tool-modal.ts
+++ b/src/tool-modal.ts
@@ -1,11 +1,11 @@
import { Modal, Setting } from "obsidian";
-import type AIOrganizer from "./main";
+import type AIPulse from "./main";
import { TOOL_REGISTRY } from "./tools";
export class ToolModal extends Modal {
- private plugin: AIOrganizer;
+ private plugin: AIPulse;
- constructor(plugin: AIOrganizer) {
+ constructor(plugin: AIPulse) {
super(plugin.app);
this.plugin = plugin;
}
@@ -13,13 +13,13 @@ export class ToolModal extends Modal {
onOpen(): void {
const { contentEl } = this;
contentEl.empty();
- contentEl.addClass("ai-organizer-tool-modal");
+ contentEl.addClass("ai-pulse-tool-modal");
this.setTitle("AI Tools");
contentEl.createEl("p", {
text: "Enable tools to give the AI access to your vault. Changes take effect on the next message.",
- cls: "ai-organizer-tool-modal-desc",
+ cls: "ai-pulse-tool-modal-desc",
});
for (const tool of TOOL_REGISTRY) {
diff --git a/styles.css b/styles.css
index 9503bca..adce317 100644
--- a/styles.css
+++ b/styles.css
@@ -1,10 +1,10 @@
-.ai-organizer-chat-container {
+.ai-pulse-chat-container {
display: flex;
flex-direction: column;
height: 100%;
}
-.ai-organizer-messages-area {
+.ai-pulse-messages-area {
flex: 1;
display: flex;
flex-direction: column;
@@ -13,7 +13,7 @@
position: relative;
}
-.ai-organizer-messages {
+.ai-pulse-messages {
flex: 1;
overflow-y: auto;
padding: 8px;
@@ -23,7 +23,7 @@
gap: 6px;
}
-.ai-organizer-message {
+.ai-pulse-message {
padding: 8px 12px;
border-radius: 8px;
max-width: 85%;
@@ -33,47 +33,47 @@
-webkit-user-select: text;
}
-.ai-organizer-message.user {
+.ai-pulse-message.user {
align-self: flex-end;
background-color: var(--interactive-accent);
color: var(--text-on-accent);
}
-.ai-organizer-message.assistant {
+.ai-pulse-message.assistant {
align-self: flex-start;
background-color: var(--background-primary);
border: 1px solid var(--background-modifier-border);
}
-.ai-organizer-streaming {
+.ai-pulse-streaming {
opacity: 0.85;
}
-.ai-organizer-loading-icon {
+.ai-pulse-loading-icon {
display: flex;
align-items: center;
color: var(--text-muted);
}
-.ai-organizer-loading-icon svg {
+.ai-pulse-loading-icon svg {
width: 20px;
height: 20px;
}
-.ai-organizer-message.error {
+.ai-pulse-message.error {
color: var(--text-error);
}
/* ===== Input Row ===== */
-.ai-organizer-input-row {
+.ai-pulse-input-row {
display: flex;
flex-direction: row;
gap: 6px;
padding: 8px;
}
-.ai-organizer-input-row textarea {
+.ai-pulse-input-row textarea {
flex: 1;
resize: vertical;
background-color: var(--background-primary);
@@ -85,14 +85,14 @@
font-size: inherit;
}
-.ai-organizer-input-row textarea:focus {
+.ai-pulse-input-row textarea:focus {
border-color: var(--interactive-accent);
outline: none;
}
/* ===== FAB / Speed Dial ===== */
-.ai-organizer-fab {
+.ai-pulse-fab {
position: absolute;
top: 8px;
right: 12px;
@@ -104,12 +104,12 @@
pointer-events: none;
}
-.ai-organizer-fab > * {
+.ai-pulse-fab > * {
pointer-events: auto;
}
/* Main FAB trigger */
-.ai-organizer-fab-trigger {
+.ai-pulse-fab-trigger {
width: 40px;
height: 40px;
border-radius: 50%;
@@ -125,22 +125,22 @@
background-color 0.2s;
}
-.ai-organizer-fab-trigger:hover {
+.ai-pulse-fab-trigger:hover {
background-color: var(--interactive-accent-hover);
}
-.ai-organizer-fab-trigger svg {
+.ai-pulse-fab-trigger svg {
width: 18px;
height: 18px;
}
/* Rotate trigger when FAB is open */
-.ai-organizer-fab:focus-within > .ai-organizer-fab-trigger {
+.ai-pulse-fab:focus-within > .ai-pulse-fab-trigger {
transform: rotate(90deg);
}
/* FAB action items (hidden by default) */
-.ai-organizer-fab-action {
+.ai-pulse-fab-action {
display: flex;
align-items: center;
gap: 8px;
@@ -153,27 +153,27 @@
}
/* Staggered delays */
-.ai-organizer-fab-action:nth-child(2) {
+.ai-pulse-fab-action:nth-child(2) {
transition-delay: 30ms;
}
-.ai-organizer-fab-action:nth-child(3) {
+.ai-pulse-fab-action:nth-child(3) {
transition-delay: 60ms;
}
-.ai-organizer-fab-action:nth-child(4) {
+.ai-pulse-fab-action:nth-child(4) {
transition-delay: 90ms;
}
/* Reveal actions on focus-within */
-.ai-organizer-fab:focus-within > .ai-organizer-fab-action {
+.ai-pulse-fab:focus-within > .ai-pulse-fab-action {
visibility: visible;
opacity: 1;
transform: scale(1);
}
/* FAB action label */
-.ai-organizer-fab-label {
+.ai-pulse-fab-label {
padding: 3px 8px;
border-radius: 4px;
background-color: var(--background-secondary);
@@ -184,7 +184,7 @@
}
/* FAB action button (circle) */
-.ai-organizer-fab-btn {
+.ai-pulse-fab-btn {
width: 40px;
height: 40px;
border-radius: 50%;
@@ -199,19 +199,19 @@
transition: background-color 0.2s, color 0.2s, border-color 0.2s;
}
-.ai-organizer-fab-btn:hover {
+.ai-pulse-fab-btn:hover {
color: var(--text-normal);
background-color: var(--background-modifier-hover);
}
-.ai-organizer-fab-btn svg {
+.ai-pulse-fab-btn svg {
width: 18px;
height: 18px;
}
/* ===== Tool Call Bubbles ===== */
-.ai-organizer-tool-call {
+.ai-pulse-tool-call {
align-self: flex-start;
max-width: 85%;
padding: 6px 10px;
@@ -224,7 +224,7 @@
-webkit-user-select: text;
}
-.ai-organizer-tool-call-header {
+.ai-pulse-tool-call-header {
display: flex;
align-items: center;
gap: 6px;
@@ -232,27 +232,27 @@
color: var(--text-muted);
}
-.ai-organizer-tool-call-icon {
+.ai-pulse-tool-call-icon {
display: flex;
align-items: center;
}
-.ai-organizer-tool-call-icon svg {
+.ai-pulse-tool-call-icon svg {
width: 14px;
height: 14px;
}
-.ai-organizer-tool-call-name {
+.ai-pulse-tool-call-name {
font-weight: 600;
}
-.ai-organizer-tool-call-summary {
+.ai-pulse-tool-call-summary {
margin: 4px 0 2px 0;
color: var(--text-normal);
font-style: italic;
}
-.ai-organizer-tool-call-result-summary {
+.ai-pulse-tool-call-result-summary {
margin: 0 0 0 0;
color: var(--text-muted);
font-size: 0.9em;
@@ -260,7 +260,7 @@
/* ===== DaisyUI-inspired Collapse ===== */
-.ai-organizer-collapse {
+.ai-pulse-collapse {
display: grid;
position: relative;
overflow: hidden;
@@ -272,7 +272,7 @@
margin-top: 2px;
}
-.ai-organizer-collapse-toggle {
+.ai-pulse-collapse-toggle {
position: absolute;
opacity: 0;
width: 0;
@@ -280,7 +280,7 @@
pointer-events: none;
}
-.ai-organizer-collapse-title {
+.ai-pulse-collapse-title {
grid-column-start: 1;
grid-row-start: 1;
position: relative;
@@ -293,12 +293,12 @@
transition: color 0.15s;
}
-.ai-organizer-collapse-title:hover {
+.ai-pulse-collapse-title:hover {
color: var(--text-normal);
}
/* Collapse arrow indicator */
-.ai-organizer-collapse-arrow > .ai-organizer-collapse-title::after {
+.ai-pulse-collapse-arrow > .ai-pulse-collapse-title::after {
content: "";
position: absolute;
top: 50%;
@@ -314,16 +314,16 @@
}
/* Arrow rotates when open */
-.ai-organizer-collapse-toggle:checked ~ .ai-organizer-collapse-title::after {
+.ai-pulse-collapse-toggle:checked ~ .ai-pulse-collapse-title::after {
transform: translateY(-25%) rotate(225deg);
}
/* Expand grid when checked */
-.ai-organizer-collapse:has(.ai-organizer-collapse-toggle:checked) {
+.ai-pulse-collapse:has(.ai-pulse-collapse-toggle:checked) {
grid-template-rows: max-content 1fr;
}
-.ai-organizer-collapse-content {
+.ai-pulse-collapse-content {
grid-column-start: 1;
grid-row-start: 2;
min-height: 0;
@@ -331,16 +331,16 @@
transition: min-height 0.2s ease-out;
}
-.ai-organizer-collapse-toggle:checked ~ .ai-organizer-collapse-content {
+.ai-pulse-collapse-toggle:checked ~ .ai-pulse-collapse-content {
min-height: fit-content;
}
-.ai-organizer-collapse-content-inner {
+.ai-pulse-collapse-content-inner {
padding: 2px 0 4px 0;
}
-.ai-organizer-tool-call-args,
-.ai-organizer-tool-call-result {
+.ai-pulse-tool-call-args,
+.ai-pulse-tool-call-result {
margin: 2px 0;
padding: 4px 6px;
border-radius: 4px;
@@ -352,69 +352,69 @@
overflow-y: auto;
}
-.ai-organizer-tool-call-result {
+.ai-pulse-tool-call-result {
color: var(--text-muted);
}
-.ai-organizer-tool-call-label {
+.ai-pulse-tool-call-label {
font-weight: 600;
color: var(--text-muted);
font-size: 0.85em;
margin: 6px 0 2px 0;
}
-.ai-organizer-tool-call-label:first-child {
+.ai-pulse-tool-call-label:first-child {
margin-top: 0;
}
/* ===== Markdown in Assistant Bubbles ===== */
-.ai-organizer-message.assistant.ai-organizer-markdown {
+.ai-pulse-message.assistant.ai-pulse-markdown {
white-space: normal;
}
-.ai-organizer-message.assistant.ai-organizer-markdown p {
+.ai-pulse-message.assistant.ai-pulse-markdown p {
margin: 0 0 0.4em 0;
}
-.ai-organizer-message.assistant.ai-organizer-markdown p:last-child {
+.ai-pulse-message.assistant.ai-pulse-markdown p:last-child {
margin-bottom: 0;
}
-.ai-organizer-message.assistant.ai-organizer-markdown h1,
-.ai-organizer-message.assistant.ai-organizer-markdown h2,
-.ai-organizer-message.assistant.ai-organizer-markdown h3,
-.ai-organizer-message.assistant.ai-organizer-markdown h4,
-.ai-organizer-message.assistant.ai-organizer-markdown h5,
-.ai-organizer-message.assistant.ai-organizer-markdown h6 {
+.ai-pulse-message.assistant.ai-pulse-markdown h1,
+.ai-pulse-message.assistant.ai-pulse-markdown h2,
+.ai-pulse-message.assistant.ai-pulse-markdown h3,
+.ai-pulse-message.assistant.ai-pulse-markdown h4,
+.ai-pulse-message.assistant.ai-pulse-markdown h5,
+.ai-pulse-message.assistant.ai-pulse-markdown h6 {
margin: 0.4em 0 0.2em 0;
font-size: 1em;
line-height: 1.3;
}
-.ai-organizer-message.assistant.ai-organizer-markdown h1 {
+.ai-pulse-message.assistant.ai-pulse-markdown h1 {
font-size: 1.15em;
}
-.ai-organizer-message.assistant.ai-organizer-markdown h2 {
+.ai-pulse-message.assistant.ai-pulse-markdown h2 {
font-size: 1.1em;
}
-.ai-organizer-message.assistant.ai-organizer-markdown h3 {
+.ai-pulse-message.assistant.ai-pulse-markdown h3 {
font-size: 1.05em;
}
-.ai-organizer-message.assistant.ai-organizer-markdown ul,
-.ai-organizer-message.assistant.ai-organizer-markdown ol {
+.ai-pulse-message.assistant.ai-pulse-markdown ul,
+.ai-pulse-message.assistant.ai-pulse-markdown ol {
margin: 0.2em 0;
padding-left: 1.4em;
}
-.ai-organizer-message.assistant.ai-organizer-markdown li {
+.ai-pulse-message.assistant.ai-pulse-markdown li {
margin: 0.1em 0;
}
-.ai-organizer-message.assistant.ai-organizer-markdown pre {
+.ai-pulse-message.assistant.ai-pulse-markdown pre {
margin: 0.3em 0;
padding: 6px 8px;
border-radius: 4px;
@@ -425,88 +425,88 @@
word-wrap: break-word;
}
-.ai-organizer-message.assistant.ai-organizer-markdown code {
+.ai-pulse-message.assistant.ai-pulse-markdown code {
padding: 1px 4px;
border-radius: 3px;
background-color: var(--background-secondary);
font-size: 0.9em;
}
-.ai-organizer-message.assistant.ai-organizer-markdown pre code {
+.ai-pulse-message.assistant.ai-pulse-markdown pre code {
padding: 0;
background-color: transparent;
font-size: inherit;
}
-.ai-organizer-message.assistant.ai-organizer-markdown blockquote {
+.ai-pulse-message.assistant.ai-pulse-markdown blockquote {
margin: 0.3em 0;
padding: 2px 8px;
border-left: 3px solid var(--interactive-accent);
color: var(--text-muted);
}
-.ai-organizer-message.assistant.ai-organizer-markdown table {
+.ai-pulse-message.assistant.ai-pulse-markdown table {
border-collapse: collapse;
margin: 0.3em 0;
font-size: 0.9em;
width: 100%;
}
-.ai-organizer-message.assistant.ai-organizer-markdown th,
-.ai-organizer-message.assistant.ai-organizer-markdown td {
+.ai-pulse-message.assistant.ai-pulse-markdown th,
+.ai-pulse-message.assistant.ai-pulse-markdown td {
padding: 3px 6px;
border: 1px solid var(--background-modifier-border);
}
-.ai-organizer-message.assistant.ai-organizer-markdown th {
+.ai-pulse-message.assistant.ai-pulse-markdown th {
background-color: var(--background-secondary);
font-weight: 600;
}
-.ai-organizer-message.assistant.ai-organizer-markdown hr {
+.ai-pulse-message.assistant.ai-pulse-markdown hr {
margin: 0.4em 0;
border: none;
border-top: 1px solid var(--background-modifier-border);
}
-.ai-organizer-message.assistant.ai-organizer-markdown img {
+.ai-pulse-message.assistant.ai-pulse-markdown img {
max-width: 100%;
border-radius: 4px;
}
/* ===== Misc ===== */
-.ai-organizer-tool-modal-desc {
+.ai-pulse-tool-modal-desc {
color: var(--text-muted);
font-size: 0.9em;
margin-bottom: 8px;
}
-.ai-organizer-stop-btn {
+.ai-pulse-stop-btn {
background-color: var(--text-error) !important;
color: var(--text-on-accent) !important;
border-color: var(--text-error) !important;
}
-.ai-organizer-param-value {
+.ai-pulse-param-value {
font-weight: 600;
color: var(--text-normal);
}
-.ai-organizer-ctx-max {
+.ai-pulse-ctx-max {
font-size: 0.8em;
color: var(--text-muted);
margin-top: 4px;
text-align: right;
}
-.ai-organizer-ctx-max-warn {
+.ai-pulse-ctx-max-warn {
color: var(--text-error);
}
/* ===== Tool Approval Prompt ===== */
-.ai-organizer-approval {
+.ai-pulse-approval {
align-self: flex-start;
max-width: 85%;
padding: 8px 12px;
@@ -516,7 +516,7 @@
margin: 2px 0;
}
-.ai-organizer-approval-header {
+.ai-pulse-approval-header {
display: flex;
align-items: center;
gap: 6px;
@@ -524,34 +524,34 @@
color: var(--text-warning);
}
-.ai-organizer-approval-icon {
+.ai-pulse-approval-icon {
display: flex;
align-items: center;
}
-.ai-organizer-approval-icon svg {
+.ai-pulse-approval-icon svg {
width: 16px;
height: 16px;
}
-.ai-organizer-approval-name {
+.ai-pulse-approval-name {
font-weight: 600;
font-size: 0.9em;
}
-.ai-organizer-approval-message {
+.ai-pulse-approval-message {
margin: 4px 0 8px 0;
color: var(--text-normal);
font-size: 0.9em;
}
-.ai-organizer-approval-buttons {
+.ai-pulse-approval-buttons {
display: flex;
gap: 8px;
}
-.ai-organizer-approval-approve,
-.ai-organizer-approval-decline {
+.ai-pulse-approval-approve,
+.ai-pulse-approval-decline {
padding: 4px 16px;
border-radius: 4px;
border: none;
@@ -561,62 +561,62 @@
transition: opacity 0.15s;
}
-.ai-organizer-approval-approve {
+.ai-pulse-approval-approve {
background-color: var(--interactive-accent);
color: var(--text-on-accent);
}
-.ai-organizer-approval-approve:hover {
+.ai-pulse-approval-approve:hover {
opacity: 0.85;
}
-.ai-organizer-approval-decline {
+.ai-pulse-approval-decline {
background-color: var(--background-modifier-border);
color: var(--text-normal);
}
-.ai-organizer-approval-decline:hover {
+.ai-pulse-approval-decline:hover {
opacity: 0.85;
}
-.ai-organizer-approval-approve:disabled,
-.ai-organizer-approval-decline:disabled {
+.ai-pulse-approval-approve:disabled,
+.ai-pulse-approval-decline:disabled {
opacity: 0.5;
cursor: default;
}
-.ai-organizer-approval-status {
+.ai-pulse-approval-status {
margin-top: 6px;
font-size: 0.8em;
font-weight: 600;
}
-.ai-organizer-approval-approved .ai-organizer-approval-status {
+.ai-pulse-approval-approved .ai-pulse-approval-status {
color: var(--interactive-accent);
}
-.ai-organizer-approval-declined .ai-organizer-approval-status {
+.ai-pulse-approval-declined .ai-pulse-approval-status {
color: var(--text-error);
}
-.ai-organizer-approval-approved {
+.ai-pulse-approval-approved {
border-left-color: var(--interactive-accent);
}
-.ai-organizer-approval-declined {
+.ai-pulse-approval-declined {
border-left-color: var(--text-error);
}
/* ===== Disabled Setting ===== */
-.ai-organizer-setting-disabled {
+.ai-pulse-setting-disabled {
opacity: 0.5;
pointer-events: none;
}
/* ===== Model Badge ===== */
-.ai-organizer-model-badge {
+.ai-pulse-model-badge {
position: absolute;
top: 8px;
left: 12px;
@@ -640,7 +640,7 @@
user-select: none;
}
-.ai-organizer-model-badge-empty {
+.ai-pulse-model-badge-empty {
color: var(--text-muted);
font-style: italic;
}