Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/gui/AIAssistantSettingsModal.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { App } from "obsidian";
import { Modal, Setting, TextAreaComponent } from "obsidian";
import type { QuickAddSettings } from "src/quickAddSettingsTab";
import type { QuickAddSettings } from "src/settings";
import { FormatSyntaxSuggester } from "./suggesters/formatSyntaxSuggester";
import QuickAdd from "src/main";
import { FormatDisplayFormatter } from "src/formatters/formatDisplayFormatter";
Expand Down
34 changes: 31 additions & 3 deletions src/gui/GenericInputPrompt/GenericInputPrompt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type { App } from "obsidian";
import { ButtonComponent, Modal, TextComponent } from "obsidian";
import { FileSuggester } from "../suggesters/fileSuggester";
import { TagSuggester } from "../suggesters/tagSuggester";
import { InputPromptDraftHandler } from "../../utils/InputPromptDraftHandler";

export default class GenericInputPrompt extends Modal {
public waitForClose: Promise<string>;
Expand All @@ -12,6 +13,7 @@ export default class GenericInputPrompt extends Modal {
private inputComponent: TextComponent;
protected input: string;
private readonly placeholder: string;
private readonly draftHandler: InputPromptDraftHandler;
private fileSuggester: FileSuggester;
private tagSuggester: TagSuggester;

Expand Down Expand Up @@ -57,7 +59,13 @@ export default class GenericInputPrompt extends Modal {
) {
super(app);
this.placeholder = placeholder ?? "";
this.input = value ?? "";
this.draftHandler = new InputPromptDraftHandler({
kind: "single",
header: this.header,
placeholder: this.placeholder,
linkSourcePath: this.linkSourcePath,
});
this.input = this.draftHandler.hydrate(value ?? "");

this.waitForClose = new Promise<string>((resolve, reject) => {
this.resolvePromise = resolve;
Expand Down Expand Up @@ -103,7 +111,7 @@ export default class GenericInputPrompt extends Modal {
textComponent
.setPlaceholder(placeholder ?? "")
.setValue(value ?? "")
.onChange((value) => (this.input = value))
.onChange((value) => this.onInputChanged(value))
.inputEl.addEventListener("keydown", this.submitEnterCallback);

return textComponent;
Expand Down Expand Up @@ -152,6 +160,7 @@ export default class GenericInputPrompt extends Modal {
};

private submit() {
this.input = this.inputComponent?.inputEl?.value ?? this.input;
this.didSubmit = true;

this.close();
Expand All @@ -166,6 +175,21 @@ export default class GenericInputPrompt extends Modal {
else this.resolvePromise(this.input);
}

protected onInputChanged(value: string) {
this.draftHandler.markChanged();
this.input = value;
}

private syncInputFromEl() {
if (this.inputComponent?.inputEl) {
this.input = this.inputComponent.inputEl.value;
}
}

private persistDraft() {
this.draftHandler.persist(this.input, this.didSubmit);
}

private removeInputListener() {
this.inputComponent.inputEl.removeEventListener(
"keydown",
Expand All @@ -181,8 +205,12 @@ export default class GenericInputPrompt extends Modal {
}

onClose() {
super.onClose();
if (!this.didSubmit) {
this.syncInputFromEl();
}
this.persistDraft();
this.resolveInput();
this.removeInputListener();
super.onClose();
}
}
34 changes: 31 additions & 3 deletions src/gui/GenericWideInputPrompt/GenericWideInputPrompt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type { App } from "obsidian";
import { ButtonComponent, Modal, TextAreaComponent } from "obsidian";
import { FileSuggester } from "../suggesters/fileSuggester";
import { TagSuggester } from "../suggesters/tagSuggester";
import { InputPromptDraftHandler } from "../../utils/InputPromptDraftHandler";

export default class GenericWideInputPrompt extends Modal {
public waitForClose: Promise<string>;
Expand All @@ -12,6 +13,7 @@ export default class GenericWideInputPrompt extends Modal {
private inputComponent: TextAreaComponent;
private input: string;
private readonly placeholder: string;
private readonly draftHandler: InputPromptDraftHandler;
private fileSuggester: FileSuggester;
private tagSuggester: TagSuggester;

Expand Down Expand Up @@ -57,7 +59,13 @@ export default class GenericWideInputPrompt extends Modal {
) {
super(app);
this.placeholder = placeholder ?? "";
this.input = value ?? "";
this.draftHandler = new InputPromptDraftHandler({
kind: "multi",
header: this.header,
placeholder: this.placeholder,
linkSourcePath: this.linkSourcePath,
});
this.input = this.draftHandler.hydrate(value ?? "");

this.waitForClose = new Promise<string>((resolve, reject) => {
this.resolvePromise = resolve;
Expand Down Expand Up @@ -101,7 +109,7 @@ export default class GenericWideInputPrompt extends Modal {
textComponent
.setPlaceholder(placeholder ?? "")
.setValue(value ?? "")
.onChange((value) => (this.input = value))
.onChange((value) => this.onInputChanged(value))
.inputEl.addEventListener("keydown", this.submitEnterCallback);

return textComponent;
Expand Down Expand Up @@ -155,6 +163,7 @@ export default class GenericWideInputPrompt extends Modal {

private submit() {
if (this.didSubmit) return;
this.input = this.inputComponent?.inputEl?.value ?? this.input;
this.didSubmit = true;
this.input = this.escapeBackslashes(this.input);

Expand All @@ -170,6 +179,21 @@ export default class GenericWideInputPrompt extends Modal {
else this.resolvePromise(this.input);
}

private onInputChanged(value: string) {
this.draftHandler.markChanged();
this.input = value;
}

private syncInputFromEl() {
if (this.inputComponent?.inputEl) {
this.input = this.inputComponent.inputEl.value;
}
}

private persistDraft() {
this.draftHandler.persist(this.input, this.didSubmit);
}

private removeInputListener() {
this.inputComponent.inputEl.removeEventListener(
"keydown",
Expand All @@ -185,8 +209,12 @@ export default class GenericWideInputPrompt extends Modal {
}

onClose() {
super.onClose();
if (!this.didSubmit) {
this.syncInputFromEl();
}
this.persistDraft();
this.resolveInput();
this.removeInputListener();
Comment thread
chhoumann marked this conversation as resolved.
super.onClose();
}
}
4 changes: 2 additions & 2 deletions src/gui/VDateInputPrompt/VDateInputPrompt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,8 @@ export default class VDateInputPrompt extends GenericInputPrompt {
.setPlaceholder(placeholder ?? "")
.setValue(value ?? "")
.onChange((newValue) => {
this.onInputChanged(newValue);
this.currentInput = newValue;
this.input = newValue; // Keep parent's input in sync
this.updatePreviewDebounced();
})
.inputEl.addEventListener("keydown", this.submitEnterCallback);
Expand Down Expand Up @@ -168,4 +168,4 @@ export default class VDateInputPrompt extends GenericInputPrompt {

super.onClose();
}
}
}
6 changes: 3 additions & 3 deletions src/main.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
/** biome-ignore-all assist/source/organizeImports: Import order is critical to prevent circular dependencies - ChoiceExecutor must load before dependent classes */
import type { TFile } from "obsidian";
import { Plugin } from "obsidian";
import { DEFAULT_SETTINGS, QuickAddSettingsTab } from "./quickAddSettingsTab";
import type { QuickAddSettings } from "./quickAddSettingsTab";
import { QuickAddSettingsTab } from "./quickAddSettingsTab";
import { DEFAULT_SETTINGS } from "./settings";
import type { QuickAddSettings } from "./settings";
import { log } from "./logger/logManager";
import { ConsoleErrorLogger } from "./logger/consoleErrorLogger";
import { GuiLogger } from "./logger/guiLogger";
Expand Down Expand Up @@ -323,4 +324,3 @@ export default class QuickAdd extends Plugin {
updateModal.open();
}
}

2 changes: 1 addition & 1 deletion src/migrations/Migrations.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type QuickAdd from "src/main";
import type { QuickAddSettings } from "src/quickAddSettingsTab";
import type { QuickAddSettings } from "src/settings";

export type Migration = {
description: string;
Expand Down
104 changes: 23 additions & 81 deletions src/quickAddSettingsTab.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,89 +12,10 @@ import ChoiceView from "./gui/choiceList/ChoiceView.svelte";
import { GenericTextSuggester } from "./gui/suggesters/genericTextSuggester";
import GlobalVariablesView from "./gui/GlobalVariables/GlobalVariablesView.svelte";
import { settingsStore } from "./settingsStore";
import type { Model } from "./ai/Provider";
import { DefaultProviders, type AIProvider } from "./ai/Provider";
import { ExportPackageModal } from "./gui/PackageManager/ExportPackageModal";
import { ImportPackageModal } from "./gui/PackageManager/ImportPackageModal";

export interface QuickAddSettings {
choices: IChoice[];
inputPrompt: "multi-line" | "single-line";
devMode: boolean;
templateFolderPath: string;
announceUpdates: "all" | "major" | "none";
version: string;
globalVariables: Record<string, string>;
/**
* Enables the one-page input flow that pre-collects variables
* and renders a single dynamic GUI before executing a choice.
*/
onePageInputEnabled: boolean;
/**
* If this is true, then the plugin is not to contact external services (e.g. OpenAI, etc.) via plugin features.
* Users _can_ still use User Scripts to do so by executing arbitrary JavaScript, but that is not something the plugin controls.
*/
disableOnlineFeatures: boolean;
enableRibbonIcon: boolean;
showCaptureNotification: boolean;
showInputCancellationNotification: boolean;
enableTemplatePropertyTypes: boolean;
ai: {
defaultModel: Model["name"] | "Ask me";
defaultSystemPrompt: string;
promptTemplatesFolderPath: string;
showAssistant: boolean;
providers: AIProvider[];
};
migrations: {
migrateToMacroIDFromEmbeddedMacro: boolean;
useQuickAddTemplateFolder: boolean;
incrementFileNameSettingMoveToDefaultBehavior: boolean;
mutualExclusionInsertAfterAndWriteToBottomOfFile: boolean;
setVersionAfterUpdateModalRelease: boolean;
addDefaultAIProviders: boolean;
removeMacroIndirection: boolean;
migrateFileOpeningSettings: boolean;
setProviderModelDiscoveryMode: boolean;
};
}

export const DEFAULT_SETTINGS: QuickAddSettings = {
choices: [],
inputPrompt: "single-line",
devMode: false,
templateFolderPath: "",
announceUpdates: "major",
version: "0.0.0",
globalVariables: {},
onePageInputEnabled: false,
disableOnlineFeatures: true,
enableRibbonIcon: false,
showCaptureNotification: true,
showInputCancellationNotification: false,
enableTemplatePropertyTypes: false,
ai: {
defaultModel: "Ask me",
defaultSystemPrompt: `As an AI assistant within Obsidian, your primary goal is to help users manage their ideas and knowledge more effectively. Format your responses using Markdown syntax. Please use the [[Obsidian]] link format. You can write aliases for the links by writing [[Obsidian|the alias after the pipe symbol]]. To use mathematical notation, use LaTeX syntax. LaTeX syntax for larger equations should be on separate lines, surrounded with double dollar signs ($$). You can also inline math expressions by wrapping it in $ symbols. For example, use $$w_{ij}^{\text{new}}:=w_{ij}^{\text{current}}+\eta\cdot\delta_j\cdot x_{ij}$$ on a separate line, but you can write "($\eta$ = learning rate, $\delta_j$ = error term, $x_{ij}$ = input)" inline.`,
promptTemplatesFolderPath: "",
showAssistant: true,
providers: DefaultProviders,
},
migrations: {
/**
* @deprecated kept for backward compatibility; always true, ignored.
*/
migrateToMacroIDFromEmbeddedMacro: true,
useQuickAddTemplateFolder: false,
incrementFileNameSettingMoveToDefaultBehavior: false,
mutualExclusionInsertAfterAndWriteToBottomOfFile: false,
setVersionAfterUpdateModalRelease: false,
addDefaultAIProviders: false,
removeMacroIndirection: false,
migrateFileOpeningSettings: false,
setProviderModelDiscoveryMode: false,
},
};
import { InputPromptDraftStore } from "./utils/InputPromptDraftStore";
import type { QuickAddSettings } from "./settings";

type SettingGroupLike = {
addSetting(cb: (setting: Setting) => void): void;
Expand Down Expand Up @@ -129,6 +50,7 @@ export class QuickAddSettingsTab extends PluginSettingTab {

const inputGroup = this.createSettingGroup("Input");
this.addUseMultiLineInputPromptSetting(inputGroup);
this.addPersistInputPromptDraftsSetting(inputGroup);
this.addOnePageInputSetting(inputGroup);

const templatesGroup = this.createSettingGroup("Templates & Properties");
Expand Down Expand Up @@ -423,6 +345,26 @@ export class QuickAddSettingsTab extends PluginSettingTab {
});
}

private addPersistInputPromptDraftsSetting(group: SettingGroupLike) {
group.addSetting((setting) => {
setting
.setName("Persist Input Prompt Drafts")
.setDesc(
"Keep drafts when closing input prompts so they can be restored on reopen. Drafts are stored only for this session.",
)
.addToggle((toggle) =>
toggle
.setValue(settingsStore.getState().persistInputPromptDrafts)
.onChange((value) => {
settingsStore.setState({ persistInputPromptDrafts: value });
if (!value) {
InputPromptDraftStore.getInstance().clearAll();
}
}),
);
});
}

private addTemplateFolderPathSetting(group: SettingGroupLike) {
group.addSetting((setting) => {
setting.setName("Template Folder Path");
Expand Down
Loading