Commands and prefix can be set through the settings

This commit is contained in:
Barrca 2025-01-29 11:23:51 -08:00
parent c84137da2e
commit a3b709c2b2
4 changed files with 51 additions and 22 deletions

View file

@ -17,12 +17,11 @@ export default class InlineAIChatPlugin extends Plugin {
this.chatapi = new ChatApiManager(this.settings, this.app);
this.registerEditorExtension([
FloatingTooltipExtension(this.chatapi),
FloatingTooltipExtension(this.chatapi, this),
generatedResponseState,
currentSelectionState,
buildSelectionHiglightState,
diffExtension
]);
// Add command to show tooltip

View file

@ -21,7 +21,7 @@ import { ChatApiManager } from "../api";
import { currentSelectionState, SelectionInfo } from "./SelectionState";
import { slashCommandAutocompletion } from "./commands/source";
import { slashCommands } from "./commands/commands";
import InlineAIChatPlugin from "src/main";
@ -33,6 +33,7 @@ export const acceptTooltipEffect = StateEffect.define<null>();
class FloatingWidget extends WidgetType {
private chatApiManager: ChatApiManager;
private selectionInfo: SelectionInfo | null;
private plugin: InlineAIChatPlugin;
private outerEditorView: EditorView | null = null;
@ -48,10 +49,11 @@ class FloatingWidget extends WidgetType {
private acceptButton!: HTMLButtonElement;
private discardButton!: HTMLButtonElement;
constructor(chatApiManager: ChatApiManager, selectionInfo: SelectionInfo | null) {
constructor(chatApiManager: ChatApiManager, selectionInfo: SelectionInfo | null, plugin:InlineAIChatPlugin) {
super();
this.chatApiManager = chatApiManager;
this.selectionInfo = selectionInfo;
this.plugin = plugin;
// Create main DOM structure using createEl
this.dom = createEl("div", { cls: "cm-cursor-overlay", attr: { style: "user-select: none;" } });
@ -152,8 +154,8 @@ class FloatingWidget extends WidgetType {
]),
// 3) Enable slash-command autocompletion
slashCommandAutocompletion({
prefix: ':',
customCommands: slashCommands
prefix: this.plugin.settings.commandPrefix,
customCommands: this.plugin.settings.customCommands
})
],
}),
@ -297,14 +299,15 @@ class FloatingWidget extends WidgetType {
*/
function renderFloatingWidget(
state: EditorState,
chatApiManager: ChatApiManager
chatApiManager: ChatApiManager,
plugin:InlineAIChatPlugin
): DecorationSet {
const firstSelectedRange = state.selection.ranges.find((range) => !range.empty) ?? state.selection.main;
const selectionInfo = state.field(currentSelectionState, false) ?? null;
const deco = Decoration.widget({
widget: new FloatingWidget(chatApiManager, selectionInfo),
widget: new FloatingWidget(chatApiManager, selectionInfo, plugin),
above: true,
inline: true,
side: -9999,
@ -323,7 +326,7 @@ function renderFloatingWidget(
* When the user dismisses the tooltip, it clears the decoration set.
* Otherwise, it returns the existing decoration set.
*/
function FloatingTooltipState(chatApiManager: ChatApiManager) {
function FloatingTooltipState(chatApiManager: ChatApiManager, plugin:InlineAIChatPlugin) {
return StateField.define<DecorationSet>({
create(state) {
return Decoration.none;
@ -331,7 +334,7 @@ function FloatingTooltipState(chatApiManager: ChatApiManager) {
update(decorations, tr) {
// Recompute if the user triggers the command
if (tr.effects.some((e) => e.is(commandEffect))) {
return renderFloatingWidget(tr.state, chatApiManager);
return renderFloatingWidget(tr.state, chatApiManager, plugin);
}
// Or dismiss it
if (tr.effects.some((e) => e.is(dismissTooltipEffect))) {
@ -347,6 +350,6 @@ function FloatingTooltipState(chatApiManager: ChatApiManager) {
/**
* Extension enabling selection overlay widgets.
*/
export function FloatingTooltipExtension(chatApiManager: ChatApiManager) {
return [FloatingTooltipState(chatApiManager)];
export function FloatingTooltipExtension(chatApiManager: ChatApiManager, plugin:InlineAIChatPlugin) {
return [FloatingTooltipState(chatApiManager, plugin)];
}

View file

@ -1,5 +1,12 @@
import { autocompletion, CompletionContext } from "@codemirror/autocomplete"
import { SlashCommand, slashCommands } from "./commands";
/**
*
* Interface describing a slash command.
*/
export interface SlashCommand {
keyword: string;
prompt: string;
}
// Factory function that creates a completion source with custom parameters
@ -8,10 +15,11 @@ function createSlashCommandSource(options: {
customCommands: SlashCommand[]
} = {
prefix: '/',
customCommands: slashCommands
customCommands: []
}) {
const { prefix, customCommands } = options;
console.log(prefix, customCommands)
return (context: CompletionContext) => {
let word = context.matchBefore(new RegExp(`^\\${prefix}\\w*`))
if (!word || (word.from == word.to && !context.explicit))
@ -19,8 +27,9 @@ function createSlashCommandSource(options: {
return {
from: word.from+1,
options: (customCommands).map(cmd => ({
label: cmd.label,
type: "text"
label: cmd.keyword,
type: "text",
detail: cmd.prompt,
}))
}
}
@ -31,4 +40,4 @@ export function slashCommandAutocompletion(options: { prefix?: string, customCom
return autocompletion({
override: [createSlashCommandSource(options)]
})
}
}

View file

@ -1,6 +1,7 @@
import { App, PluginSettingTab, Setting } from "obsidian";
import MyPlugin from "./main";
import { cursorPrompt, selectionPrompt } from "./default_prompts";
import { SlashCommand } from "./modules/commands/source";
// Interface for the settings
export interface InlineAISettings {
@ -10,7 +11,8 @@ export interface InlineAISettings {
customURL?: string; // Add a custom URL field
selectionPrompt: string;
cursorPrompt: string;
customCommands: { name: string; prompt: string }[]; // Add custom commands array
customCommands: SlashCommand[];
commandPrefix: string; // Add command prefix setting
}
// Default settings values
@ -22,6 +24,7 @@ export const DEFAULT_SETTINGS: InlineAISettings = {
selectionPrompt: selectionPrompt,
cursorPrompt: cursorPrompt,
customCommands: [], // Default is an empty array
commandPrefix: "/" // Default command prefix
};
// Settings tab class to display settings in Obsidian UI
@ -113,6 +116,21 @@ export class InlineAISettingsTab extends PluginSettingTab {
.setName("Advanced")
.setHeading();
// Command Prefix setting
new Setting(containerEl)
.setName("Command Prefix")
.setDesc("The prefix used to trigger custom commands (e.g., /, !, #)")
.addText((text) =>
text
.setPlaceholder("/")
.setValue(this.plugin.settings.commandPrefix)
.onChange(async (value) => {
// Ensure the prefix is not empty
this.plugin.settings.commandPrefix = value || "/";
await this.plugin.saveSettings();
})
);
// Selection Prompt setting
new Setting(containerEl)
.setName("Selection prompt")
@ -157,14 +175,14 @@ export class InlineAISettingsTab extends PluginSettingTab {
// Display existing commands
this.plugin.settings.customCommands.forEach((command, index) => {
const setting = new Setting(containerEl)
.setName(`Command: ${command.name}`)
.setName(`Command: ${command.keyword}`)
.setDesc("Edit the command prompt.")
.addText((text) =>
text
.setValue(command.name)
.setValue(command.keyword)
.setPlaceholder("Command name")
.onChange(async (value) => {
this.plugin.settings.customCommands[index].name = value;
this.plugin.settings.customCommands[index].keyword = value;
await this.plugin.saveSettings();
})
)
@ -196,7 +214,7 @@ export class InlineAISettingsTab extends PluginSettingTab {
.setButtonText("Add Command")
.setCta()
.onClick(async () => {
this.plugin.settings.customCommands.push({ name: "New Command", prompt: "" });
this.plugin.settings.customCommands.push({ keyword: "new_command", prompt: "" });
await this.plugin.saveSettings();
this.display(); // Refresh the display
})