minor updates to inputPrompt

This commit is contained in:
zsviczian
2025-05-03 11:07:46 +02:00
parent 526299e41f
commit 5b341cb5fb
5 changed files with 75 additions and 17 deletions

View File

@@ -23,6 +23,7 @@ I build this plugin in my free time, as a labor of love. Curious about the philo
- Expose parameter in plugin settings to disable AI functionality [#2325](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/2325)
- Enable double-click text editing option in Excalidraw appearance and behavior (based on request on Discord)
- Added two new PDF export sizes: "Match image", "HD Screen".
- Quickly change the shape of the selected element by pressing TAB [#9270](https://github.com/excalidraw/excalidraw/pull/9270)
## Fixed in the plugin
- Scaling multiple embeddables at once did not work. [#2276](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/2276)

View File

@@ -22,8 +22,7 @@ import { MAX_IMAGE_SIZE, REG_LINKINDEX_INVALIDCHARS } from "src/constants/consta
import { REGEX_LINK, REGEX_TAGS } from "../ExcalidrawData";
import { ScriptEngine } from "../Scripts";
import { openExternalLink, openTagSearch, parseObsidianLink } from "src/utils/excalidrawViewUtils";
export type ButtonDefinition = { caption: string; tooltip?:string; action: Function };
import { ButtonDefinition } from "src/types/promptTypes";
export class Prompt extends Modal {
private promptEl: HTMLInputElement;
@@ -98,6 +97,7 @@ export class GenericInputPrompt extends Modal {
private selectionUpdateTimer: number = 0;
private customComponents: (container: HTMLElement) => void;
private blockPointerInputOutsideModal: boolean = false;
private controlsOnTop: boolean = false;
public static Prompt(
view: ExcalidrawView,
@@ -111,6 +111,7 @@ export class GenericInputPrompt extends Modal {
displayEditorButtons?: boolean,
customComponents?: (container: HTMLElement) => void,
blockPointerInputOutsideModal?: boolean,
controlsOnTop?: boolean,
): Promise<string> {
const newPromptModal = new GenericInputPrompt(
view,
@@ -124,6 +125,7 @@ export class GenericInputPrompt extends Modal {
displayEditorButtons,
customComponents,
blockPointerInputOutsideModal,
controlsOnTop,
);
return newPromptModal.waitForClose;
}
@@ -140,6 +142,7 @@ export class GenericInputPrompt extends Modal {
displayEditorButtons?: boolean,
customComponents?: (container: HTMLElement) => void,
blockPointerInputOutsideModal?: boolean,
controlsOnTop?: boolean,
) {
super(app);
this.view = view;
@@ -151,6 +154,7 @@ export class GenericInputPrompt extends Modal {
this.displayEditorButtons = this.lines > 1 ? (displayEditorButtons ?? false) : false;
this.customComponents = customComponents;
this.blockPointerInputOutsideModal = blockPointerInputOutsideModal ?? false;
this.controlsOnTop = controlsOnTop ?? false;
this.waitForClose = new Promise<string>((resolve, reject) => {
this.resolvePromise = resolve;
@@ -173,13 +177,29 @@ export class GenericInputPrompt extends Modal {
this.titleEl.textContent = this.header;
const mainContentContainer: HTMLDivElement = this.contentEl.createDiv();
this.inputComponent = this.createInputField(
mainContentContainer,
this.placeholder,
this.input
);
this.customComponents?.(mainContentContainer);
this.createButtonBar(mainContentContainer);
// Conditionally order elements based on controlsOnTop flag
if (this.controlsOnTop) {
// Create button bar first
this.customComponents?.(mainContentContainer);
this.createButtonBar(mainContentContainer);
// Then add input field and custom components
this.inputComponent = this.createInputField(
mainContentContainer,
this.placeholder,
this.input
);
} else {
// Original order: input field, custom components, then buttons
this.inputComponent = this.createInputField(
mainContentContainer,
this.placeholder,
this.input
);
this.customComponents?.(mainContentContainer);
this.createButtonBar(mainContentContainer);
}
}
protected createInputField(
@@ -242,7 +262,12 @@ export class GenericInputPrompt extends Modal {
const buttonBarContainer: HTMLDivElement = mainContentContainer.createDiv();
buttonBarContainer.style.display = "flex";
buttonBarContainer.style.justifyContent = "space-between";
buttonBarContainer.style.marginTop = "1rem";
if(this.controlsOnTop) {
buttonBarContainer.style.padding = "0.5em 0";
buttonBarContainer.style.borderTop = "1px solid var(--background-modifier-border)";
} else {
buttonBarContainer.style.marginTop = "1rem";
}
const editorButtonContainer: HTMLDivElement = buttonBarContainer.createDiv();

View File

@@ -947,15 +947,16 @@ export const EXCALIDRAW_AUTOMATE_INFO: SuggesterInfo[] = [
export const EXCALIDRAW_SCRIPTENGINE_INFO: SuggesterInfo[] = [
{
field: "inputPrompt",
code: "inputPrompt: (header: string, placeholder?: string, value?: string, buttons?: {caption:string, tooltip?:string, action:Function}[], lines?: number, displayEditorButtons?: boolean, customComponents?: (container: HTMLElement) => void, blockPointerInputOutsideModal?: boolean);",
code: "inputPrompt: (opts: {header: string, placeholder?: string, value?: string, buttons?: {caption:string, tooltip?:string, action:Function}[], lines?: number, displayEditorButtons?: boolean, customComponents?: (container: HTMLElement) => void, blockPointerInputOutsideModal?: boolean, controlsOnTop?: boolean});",
desc:
"Opens a prompt that asks for an input.\nReturns a string with the input.\nYou need to await the result of inputPrompt.\n" +
"Editor buttons are text editing buttons like delete, enter, allcaps - these are only displayed if lines is greater than 1 \n" +
"Custom components are components that you can add to the prompt. These will be displayed between the text input area and the buttons.\n" +
"blockPointerInputOutsideModal will block pointer input outside the modal. This is useful if you want to prevent the user accidently closing the modal or interacting with the excalidraw canvas while the prompt is open.\n" +
"controlsOnTop when set to true will move all the buttons to the top of the modal, leaving the text area at the bottom. This feature was developed for Scribble Helper script to avoid your palm pressing buttons while scribbling.\n"+
"buttons.action(input: string) => string\nThe button action function will receive the actual input string. If action returns null, input will be unchanged. If action returns a string, input will receive that value when the promise is resolved. " +
"example:\n<code>let fileType = '';\nconst filename = await utils.inputPrompt (\n 'Filename',\n '',\n '',\n, [\n {\n caption: 'Markdown',\n action: ()=>{fileType='md';return;}\n },\n {\n caption: 'Excalidraw',\n action: ()=>{fileType='ex';return;}\n }\n ]\n);</code>",
after: "",
after: `({header: string, placeholder?: string, value?: string, buttons?: {caption:string, tooltip?:string, action:Function}[], lines?: number, displayEditorButtons?: boolean, customComponents?: (container: HTMLElement) => void, blockPointerInputOutsideModal?: boolean, controlsOnTop?: boolean})`,
},
{
field: "suggester",

View File

@@ -8,13 +8,14 @@ import {
import { PLUGIN_ID } from "../constants/constants";
import ExcalidrawView from "../view/ExcalidrawView";
import ExcalidrawPlugin from "../core/main";
import { ButtonDefinition, GenericInputPrompt, GenericSuggester } from "./Dialogs/Prompt";
import { GenericInputPrompt, GenericSuggester } from "./Dialogs/Prompt";
import { getIMGFilename } from "../utils/fileUtils";
import { splitFolderAndFilename } from "../utils/fileUtils";
import { getEA } from "src/core";
import { ExcalidrawAutomate } from "../shared/ExcalidrawAutomate";
import { WeakArray } from "./WeakArray";
import { getExcalidrawViews } from "../utils/obsidianUtils";
import { ButtonDefinition, InputPromptOptions } from "src/types/promptTypes";
export type ScriptIconMap = {
[key: string]: { name: string; group: string; svgString: string };
@@ -271,7 +272,7 @@ export class ScriptEngine {
//try {
result = await new AsyncFunction("ea", "utils", script)(ea, {
inputPrompt: (
header: string,
header: string | InputPromptOptions,
placeholder?: string,
value?: string,
buttons?: ButtonDefinition[],
@@ -279,8 +280,21 @@ export class ScriptEngine {
displayEditorButtons?: boolean,
customComponents?: (container: HTMLElement) => void,
blockPointerInputOutsideModal?: boolean,
) =>
ScriptEngine.inputPrompt(
controlsOnTop?: boolean,
) => {
if (typeof header === "object") {
const options = header as InputPromptOptions;
header = options.header;
placeholder = options.placeholder;
value = options.value;
buttons = options.buttons;
lines = options.lines;
displayEditorButtons = options.displayEditorButtons;
customComponents = options.customComponents;
blockPointerInputOutsideModal = options.blockPointerInputOutsideModal;
controlsOnTop = options.controlsOnTop;
}
return ScriptEngine.inputPrompt(
view,
this.plugin,
this.app,
@@ -292,7 +306,9 @@ export class ScriptEngine {
displayEditorButtons,
customComponents,
blockPointerInputOutsideModal,
),
controlsOnTop,
);
},
suggester: (
displayItems: string[],
items: any[],
@@ -336,6 +352,7 @@ export class ScriptEngine {
displayEditorButtons?: boolean,
customComponents?: (container: HTMLElement) => void,
blockPointerInputOutsideModal?: boolean,
controlsOnTop?: boolean,
) {
try {
return await GenericInputPrompt.Prompt(
@@ -350,6 +367,7 @@ export class ScriptEngine {
displayEditorButtons,
customComponents,
blockPointerInputOutsideModal,
controlsOnTop
);
} catch {
return undefined;

13
src/types/promptTypes.ts Normal file
View File

@@ -0,0 +1,13 @@
export type ButtonDefinition = { caption: string; tooltip?:string; action: Function };
export interface InputPromptOptions {
header: string,
placeholder?: string,
value?: string,
buttons?: ButtonDefinition[],
lines?: number,
displayEditorButtons?: boolean,
customComponents?: (container: HTMLElement) => void,
blockPointerInputOutsideModal?: boolean,
controlsOnTop?: boolean,
}