diff --git a/src/lang/locale/en.ts b/src/lang/locale/en.ts index 59085f5..c595aa5 100644 --- a/src/lang/locale/en.ts +++ b/src/lang/locale/en.ts @@ -961,6 +961,7 @@ FILENAME_HEAD: "Filename", PROMPT_BUTTON_INSERT_SPACE: "Insert space", PROMPT_BUTTON_INSERT_LINK: "Insert markdown link to file", PROMPT_BUTTON_UPPERCASE: "Uppercase", + PROMPT_BUTTON_SPECIAL_CHARS: "Special Characters", PROMPT_SELECT_TEMPLATE: "Select a template", //ModifierKeySettings diff --git a/src/shared/Dialogs/Messages.ts b/src/shared/Dialogs/Messages.ts index 63359b4..5533439 100644 --- a/src/shared/Dialogs/Messages.ts +++ b/src/shared/Dialogs/Messages.ts @@ -24,6 +24,7 @@ I build this plugin in my free time, as a labor of love. Curious about the philo - 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) +- Updated the Scribble Helper Script. Now controls are at the top so your palm does accidently trigger controls. I added a new button to inser special characters. Scribble helper now makes use of the new text wrapping in Excalidraw. ## Fixed in the plugin - Scaling multiple embeddables at once did not work. [#2276](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/2276) diff --git a/src/shared/Dialogs/Prompt.ts b/src/shared/Dialogs/Prompt.ts index 268354e..2eff45c 100644 --- a/src/shared/Dialogs/Prompt.ts +++ b/src/shared/Dialogs/Prompt.ts @@ -260,17 +260,8 @@ export class GenericInputPrompt extends Modal { private createButtonBar(mainContentContainer: HTMLDivElement) { const buttonBarContainer: HTMLDivElement = mainContentContainer.createDiv(); - buttonBarContainer.style.display = "flex"; - buttonBarContainer.style.justifyContent = "space-between"; - if(this.controlsOnTop) { - buttonBarContainer.style.padding = "0.5em 0"; - buttonBarContainer.style.borderTop = "1px solid var(--background-modifier-border)"; - } else { - buttonBarContainer.style.marginTop = "1rem"; - } - + buttonBarContainer.addClass(`excalidraw-prompt-buttonbar-${this.controlsOnTop ? "top" : "bottom"}`); const editorButtonContainer: HTMLDivElement = buttonBarContainer.createDiv(); - const actionButtonContainer: HTMLDivElement = buttonBarContainer.createDiv(); if (this.buttons && this.buttons.length > 0) { @@ -304,6 +295,7 @@ export class GenericInputPrompt extends Modal { this.createButton(editorButtonContainer, "⏎", ()=>this.insertStringBtnClickCallback("\n"), t("PROMPT_BUTTON_INSERT_LINE"), "0"); this.createButton(editorButtonContainer, "⌫", this.delBtnClickCallback.bind(this), "Delete"); this.createButton(editorButtonContainer, "⎵", ()=>this.insertStringBtnClickCallback(" "), t("PROMPT_BUTTON_INSERT_SPACE")); + this.createButton(editorButtonContainer, "§", this.specialCharsBtnClickCallback.bind(this), t("PROMPT_BUTTON_SPECIAL_CHARS")); if(this.view) { this.createButton(editorButtonContainer, "🔗", this.linkBtnClickCallback.bind(this), t("PROMPT_BUTTON_INSERT_LINK")); } @@ -409,6 +401,74 @@ export class GenericInputPrompt extends Modal { ); } + private specialCharsBtnClickCallback = (evt: MouseEvent) => { + this.view.ownerWindow.clearTimeout(this.selectionUpdateTimer); + + // Remove any existing popup + const existingPopup = document.querySelector('.excalidraw-special-chars-popup'); + if (existingPopup) { + existingPopup.remove(); + return; + } + + // Create popup element + const popup = document.createElement('div'); + popup.className = 'excalidraw-special-chars-popup'; + popup.style.position = 'absolute'; + popup.style.zIndex = '1000'; + popup.style.background = 'var(--background-primary)'; + popup.style.border = '1px solid var(--background-modifier-border)'; + popup.style.borderRadius = '4px'; + popup.style.padding = '4px'; + popup.style.boxShadow = '0 2px 8px var(--background-modifier-box-shadow)'; + popup.style.display = 'flex'; + popup.style.flexWrap = 'wrap'; + popup.style.maxWidth = '200px'; + + // Position near the button + const rect = (evt.target as HTMLElement).getBoundingClientRect(); + popup.style.top = `${rect.bottom + 5}px`; + popup.style.left = `${rect.left}px`; + + // Special characters to include + const specialChars = [',', '.', ':', ';', '!', '?', '"', '{', '}', '[', ']', '(', ')']; + + // Add character buttons + specialChars.forEach(char => { + const charButton = document.createElement('button'); + charButton.textContent = char; + charButton.style.margin = '2px'; + charButton.style.width = '28px'; + charButton.style.height = '28px'; + charButton.style.cursor = 'pointer'; + charButton.style.background = 'var(--interactive-normal)'; + charButton.style.border = 'none'; + charButton.style.borderRadius = '4px'; + + charButton.addEventListener('click', () => { + this.insertStringBtnClickCallback(char); + popup.remove(); + }); + + popup.appendChild(charButton); + }); + + // Add click outside listener to close popup + const closePopupListener = (e: MouseEvent) => { + if (!popup.contains(e.target as Node) && + (evt.target as HTMLElement) !== e.target) { + popup.remove(); + document.removeEventListener('click', closePopupListener); + } + }; + + // Add to document and set up listeners + document.body.appendChild(popup); + setTimeout(() => { + document.addEventListener('click', closePopupListener); + }, 10); + } + onOpen() { super.onOpen(); this.inputComponent.inputEl.focus(); diff --git a/styles.css b/styles.css index b12f554..2a14b0c 100644 --- a/styles.css +++ b/styles.css @@ -734,4 +734,41 @@ textarea.excalidraw-wysiwyg, .excalidraw input { .excalidraw .context-menu { height: fit-content; -} \ No newline at end of file +} + +.excalidraw-prompt-buttonbar-top, +.excalidraw-prompt-buttonbar-bottom { + display: flex; + flex-wrap: wrap; + align-items: flex-start; /* keep both rows top‐aligned */ + row-gap: 0.5em; /* vertical space when wrapped */ +} + +/* top bar specifics */ +.excalidraw-prompt-buttonbar-top { + padding: 0.5em 0; + border-top: 1px solid var(--background-modifier-border); +} + +/* bottom bar specifics */ +.excalidraw-prompt-buttonbar-bottom { + margin-top: 1rem; +} + +/* make each child a flex row */ +.excalidraw-prompt-buttonbar-top > div, +.excalidraw-prompt-buttonbar-bottom > div { + display: flex; +} + +/* push the first group to the left */ +.excalidraw-prompt-buttonbar-top > div:first-child, +.excalidraw-prompt-buttonbar-bottom > div:first-child { + margin-right: auto; +} + +/* push the second group to the right */ +.excalidraw-prompt-buttonbar-top > div:last-child, +.excalidraw-prompt-buttonbar-bottom > div:last-child { + margin-left: auto; +}