diff --git a/src/gui/ChoiceBuilder/choiceBuilder.ts b/src/gui/ChoiceBuilder/choiceBuilder.ts index ae601948..a854896d 100644 --- a/src/gui/ChoiceBuilder/choiceBuilder.ts +++ b/src/gui/ChoiceBuilder/choiceBuilder.ts @@ -1,30 +1,25 @@ -import { type App, Modal, Setting } from "obsidian"; +import { type App, Modal, Setting, setIcon } from "obsidian"; import type { SvelteComponent } from "svelte"; -import { log } from "../../logger/logManager"; import type IChoice from "../../types/choices/IChoice"; import type { FileViewMode2, OpenLocation } from "../../types/fileOpening"; import { normalizeFileOpening, type FileOpeningSettings, } from "../../utils/fileOpeningDefaults"; -import GenericInputPrompt from "../GenericInputPrompt/GenericInputPrompt"; import { GenericTextSuggester } from "../suggesters/genericTextSuggester"; +import { promptRenameChoice } from "../choiceRename"; export abstract class ChoiceBuilder extends Modal { private resolvePromise: (input: IChoice) => void; - private rejectPromise: (reason?: unknown) => void; - private input: IChoice; public waitForClose: Promise; abstract choice: IChoice; - private didSubmit = false; protected svelteElements: SvelteComponent[] = []; protected constructor(app: App) { super(app); - this.waitForClose = new Promise((resolve, reject) => { + this.waitForClose = new Promise((resolve) => { this.resolvePromise = resolve; - this.rejectPromise = reject; }); this.containerEl.addClass("quickAddModal"); @@ -85,23 +80,21 @@ export abstract class ChoiceBuilder extends Modal { const headerEl: HTMLHeadingElement = this.contentEl.createEl("h2", { cls: "choiceNameHeader", }); - headerEl.setText(choice.name); + const textEl = headerEl.createSpan({ + text: choice.name, + cls: "choiceNameHeaderText", + }); + const iconEl = headerEl.createSpan({ + cls: "choiceNameHeaderIcon", + attr: { "aria-hidden": "true" }, + }); + setIcon(iconEl, "pencil"); headerEl.addEventListener("click", async (ev) => { - try { - const newName: string = await GenericInputPrompt.Prompt( - this.app, - choice.name, - "Choice name", - choice.name, - ); - if (newName !== choice.name) { - choice.name = newName; - headerEl.setText(newName); - } - } catch { - log.logMessage(`No new name given for ${choice.name}`); - } + const newName = await promptRenameChoice(this.app, choice.name); + if (!newName) return; + choice.name = newName; + textEl.setText(newName); }); } @@ -211,12 +204,9 @@ export abstract class ChoiceBuilder extends Modal { onClose() { super.onClose(); - this.resolvePromise(this.choice); this.svelteElements.forEach((el) => { if (el && el.$destroy) el.$destroy(); }); - - if (!this.didSubmit) this.rejectPromise("No answer given."); - else this.resolvePromise(this.input); + this.resolvePromise(this.choice); } } diff --git a/src/gui/choiceList/ChoiceList.svelte b/src/gui/choiceList/ChoiceList.svelte index 4f58e827..0cdab3ba 100644 --- a/src/gui/choiceList/ChoiceList.svelte +++ b/src/gui/choiceList/ChoiceList.svelte @@ -73,6 +73,7 @@ on:configureChoice on:toggleCommand on:duplicateChoice + on:renameChoice on:moveChoice startDrag={startDrag} bind:choice @@ -86,6 +87,7 @@ on:configureChoice on:toggleCommand on:duplicateChoice + on:renameChoice on:moveChoice on:reorderChoices startDrag={startDrag} diff --git a/src/gui/choiceList/ChoiceListItem.svelte b/src/gui/choiceList/ChoiceListItem.svelte index 4d0e90f4..b5db54fe 100644 --- a/src/gui/choiceList/ChoiceListItem.svelte +++ b/src/gui/choiceList/ChoiceListItem.svelte @@ -47,6 +47,7 @@ function onContextMenu(evt: MouseEvent) { showChoiceContextMenu(app, evt, choice, roots, { + onRename: () => dispatcher("renameChoice", { choice }), onToggle: () => toggleCommandForChoice(), onConfigure: () => configureChoice(), onDuplicate: () => duplicateChoice(), diff --git a/src/gui/choiceList/ChoiceView.svelte b/src/gui/choiceList/ChoiceView.svelte index 7df4e0a3..d0bdf77b 100644 --- a/src/gui/choiceList/ChoiceView.svelte +++ b/src/gui/choiceList/ChoiceView.svelte @@ -16,6 +16,7 @@ import type IChoice from "../../types/choices/IChoice"; import { AIAssistantSettingsModal } from "../AIAssistantSettingsModal"; import ObsidianIcon from "../components/ObsidianIcon.svelte"; + import { promptRenameChoice } from "../choiceRename"; import AddChoiceBox from "./AddChoiceBox.svelte"; import ChoiceList from "./ChoiceList.svelte"; import { moveChoice as moveChoiceService } from "../../services/choiceService"; @@ -132,6 +133,21 @@ return oldChoice; } + async function handleRenameChoice(e: any) { + const { choice } = e.detail; + if (!choice) return; + + const newName = await promptRenameChoice(app, choice.name); + if (!newName) return; + + const updatedChoice = { ...choice, name: newName }; + choices = choices.map((entry) => + updateChoiceHelper(entry, updatedChoice), + ); + commandRegistry.updateCommand(choice, updatedChoice); + saveChoices(choices); + } + async function toggleCommandForChoice(e: any) { const { choice: oldChoice } = e.detail; const updatedChoice = createToggleCommandChoice(oldChoice); @@ -209,6 +225,7 @@ on:configureChoice={handleConfigureChoice} on:toggleCommand={toggleCommandForChoice} on:duplicateChoice={handleDuplicateChoice} + on:renameChoice={handleRenameChoice} on:moveChoice={handleMoveChoice} on:reorderChoices={(e) => saveChoices(e.detail.choices)} /> @@ -223,6 +240,7 @@ on:configureChoice={handleConfigureChoice} on:toggleCommand={toggleCommandForChoice} on:duplicateChoice={handleDuplicateChoice} + on:renameChoice={handleRenameChoice} on:moveChoice={handleMoveChoice} /> {/if} diff --git a/src/gui/choiceList/MultiChoiceListItem.svelte b/src/gui/choiceList/MultiChoiceListItem.svelte index 11671f03..d011a026 100644 --- a/src/gui/choiceList/MultiChoiceListItem.svelte +++ b/src/gui/choiceList/MultiChoiceListItem.svelte @@ -52,6 +52,7 @@ function onContextMenu(evt: MouseEvent) { showChoiceContextMenu(app, evt, choice, roots, { + onRename: () => dispatcher('renameChoice', { choice }), onToggle: () => toggleCommandForChoice(), onConfigure: () => configureChoice(), onDuplicate: () => duplicateChoice(), @@ -114,6 +115,7 @@ on:toggleCommand on:duplicateChoice on:moveChoice + on:renameChoice on:reorderChoices={handleNestedReorder} bind:multiChoice={choice} bind:choices={choice.choices} diff --git a/src/gui/choiceList/contextMenu.ts b/src/gui/choiceList/contextMenu.ts index abb01c2a..f1178564 100644 --- a/src/gui/choiceList/contextMenu.ts +++ b/src/gui/choiceList/contextMenu.ts @@ -49,6 +49,7 @@ function isInvalidTarget(moving: IChoice, target: IChoice): boolean { } type MenuActions = { + onRename: () => void; onToggle: () => void; onConfigure: () => void; onDuplicate: () => void; @@ -79,6 +80,7 @@ export function showChoiceContextMenu( .setIcon("zap") .onClick(actions.onToggle), ) + .addItem((item) => item.setTitle("Rename").setIcon("pencil").onClick(actions.onRename)) .addItem((item) => item.setTitle("Configure").setIcon("settings").onClick(actions.onConfigure)) .addItem((item) => item.setTitle("Duplicate").setIcon("copy").onClick(actions.onDuplicate)) .addItem((item) => item.setTitle("Delete").setIcon("trash-2").onClick(actions.onDelete)) diff --git a/src/gui/choiceRename.ts b/src/gui/choiceRename.ts new file mode 100644 index 00000000..8cdb3e27 --- /dev/null +++ b/src/gui/choiceRename.ts @@ -0,0 +1,21 @@ +import type { App } from "obsidian"; +import GenericInputPrompt from "./GenericInputPrompt/GenericInputPrompt"; + +export async function promptRenameChoice( + app: App, + currentName: string, +): Promise { + try { + const newName = await GenericInputPrompt.Prompt( + app, + "Choice name", + undefined, + currentName, + ); + const trimmed = newName.trim(); + if (!trimmed || trimmed === currentName) return null; + return trimmed; + } catch { + return null; + } +} diff --git a/src/styles.css b/src/styles.css index 534065f9..165fc793 100644 --- a/src/styles.css +++ b/src/styles.css @@ -36,12 +36,22 @@ .choiceNameHeader { text-align: center; + display: flex; + align-items: center; + justify-content: center; + gap: 6px; } .choiceNameHeader:hover { cursor: pointer; } +.choiceNameHeaderIcon { + display: inline-flex; + align-items: center; + opacity: 0.5; +} + .folderInputContainer { display: flex; align-content: center;