diff --git a/README.md b/README.md index 84b64afc8..ce0b930bc 100644 --- a/README.md +++ b/README.md @@ -128,7 +128,7 @@ Download the repository, install dependencies using `pnpm` and then run the envi git clone https://github.com/TriliumNext/Notes.git cd Notes pnpm install -pnpm nx run edit-docs:serve +pnpm nx run edit-docs:edit-docs ``` ### Building the Executable diff --git a/apps/client/src/widgets/buttons/create_ai_chat_button.ts b/apps/client/src/widgets/buttons/create_ai_chat_button.ts deleted file mode 100644 index 1ccd52cda..000000000 --- a/apps/client/src/widgets/buttons/create_ai_chat_button.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { t } from "../../services/i18n.js"; -import options from "../../services/options.js"; -import CommandButtonWidget from "./command_button.js"; - -export default class CreateAiChatButton extends CommandButtonWidget { - constructor() { - super(); - - this.icon("bx bx-bot") - .title(t("ai.create_new_ai_chat")) - .titlePlacement("bottom") - .command("createAiChat") - .class("icon-action"); - } - - isEnabled() { - return options.get("aiEnabled") === "true"; - } - - async refreshWithNote() { - if (this.isEnabled()) { - this.$widget.show(); - } else { - this.$widget.hide(); - } - } -} diff --git a/apps/client/src/widgets/buttons/note_actions.ts b/apps/client/src/widgets/buttons/note_actions.ts index 020da9318..6989d8152 100644 --- a/apps/client/src/widgets/buttons/note_actions.ts +++ b/apps/client/src/widgets/buttons/note_actions.ts @@ -186,7 +186,7 @@ export default class NoteActionsWidget extends NoteContextAwareWidget { this.$convertNoteIntoAttachmentButton.toggle(note.isEligibleForConversionToAttachment()); - this.toggleDisabled(this.$findInTextButton, ["text", "code", "book"].includes(note.type)); + this.toggleDisabled(this.$findInTextButton, ["text", "code", "book", "mindMap"].includes(note.type)); this.toggleDisabled(this.$showAttachmentsButton, !isInOptions); this.toggleDisabled(this.$showSourceButton, ["text", "code", "relationMap", "mermaid", "canvas", "mindMap", "geoMap"].includes(note.type)); diff --git a/apps/client/src/widgets/find.ts b/apps/client/src/widgets/find.ts index fc8bebb88..c5b3470b2 100644 --- a/apps/client/src/widgets/find.ts +++ b/apps/client/src/widgets/find.ts @@ -188,7 +188,7 @@ export default class FindWidget extends NoteContextAwareWidget { return; } - if (!["text", "code", "render"].includes(this.note?.type ?? "")) { + if (!["text", "code", "render", "mindMap"].includes(this.note?.type ?? "")) { return; } @@ -250,6 +250,8 @@ export default class FindWidget extends NoteContextAwareWidget { case "text": const readOnly = await this.noteContext?.isReadOnly(); return readOnly ? this.htmlHandler : this.textHandler; + case "mindMap": + return this.htmlHandler; default: console.warn("FindWidget: Unsupported note type for find widget", this.note?.type); } @@ -352,7 +354,7 @@ export default class FindWidget extends NoteContextAwareWidget { } isEnabled() { - return super.isEnabled() && ["text", "code", "render"].includes(this.note?.type ?? ""); + return super.isEnabled() && ["text", "code", "render", "mindMap"].includes(this.note?.type ?? ""); } async entitiesReloadedEvent({ loadResults }: EventData<"entitiesReloaded">) { diff --git a/apps/client/src/widgets/find_in_html.ts b/apps/client/src/widgets/find_in_html.ts index 304bea656..0bf2bd376 100644 --- a/apps/client/src/widgets/find_in_html.ts +++ b/apps/client/src/widgets/find_in_html.ts @@ -85,7 +85,7 @@ export default class FindInHtml { if (this.$results?.length) { const $current = this.$results.eq(this.currentIndex); this.$results.removeClass(FIND_RESULT_SELECTED_CSS_CLASSNAME); - $current[0].scrollIntoView(); + $current[0].scrollIntoView({ block: 'center', inline: 'center'}); $current.addClass(FIND_RESULT_SELECTED_CSS_CLASSNAME); } } diff --git a/apps/client/src/widgets/tab_row.ts b/apps/client/src/widgets/tab_row.ts index 92b81f6dd..6d0fc2fba 100644 --- a/apps/client/src/widgets/tab_row.ts +++ b/apps/client/src/widgets/tab_row.ts @@ -378,16 +378,45 @@ export default class TabRowWidget extends BasicWidget { } scrollTabContainer(direction: number, behavior: ScrollBehavior = "smooth") { - const currentScrollLeft = this.$tabScrollingContainer[0]?.scrollLeft; - this.$tabScrollingContainer[0].scrollTo({ - left: currentScrollLeft + direction, + this.$tabScrollingContainer[0].scrollBy({ + left: direction, behavior }); }; setupScrollEvents() { - this.$tabScrollingContainer[0].addEventListener('wheel', (event) => { - this.scrollTabContainer(event.deltaY * 1.5); + let deltaX = 0; + let isScrolling = false; + const stepScroll = () => { + if (Math.abs(deltaX) > 5) { + const step = Math.round(deltaX * 0.2); + deltaX -= step; + this.scrollTabContainer(step, "instant"); + requestAnimationFrame(stepScroll); + } else { + this.scrollTabContainer(deltaX, "instant"); + deltaX = 0; + isScrolling = false; + } + }; + this.$tabScrollingContainer[0].addEventListener('wheel', async (event) => { + if (!event.shiftKey && event.deltaX === 0) { + event.preventDefault(); + // Clamp deltaX between TAB_CONTAINER_MIN_WIDTH and TAB_CONTAINER_MIN_WIDTH * 3 + deltaX += Math.sign(event.deltaY) * Math.max(Math.min(Math.abs(event.deltaY), TAB_CONTAINER_MIN_WIDTH * 3), TAB_CONTAINER_MIN_WIDTH); + if (!isScrolling) { + isScrolling = true; + stepScroll(); + } + } else if (event.shiftKey) { + event.preventDefault(); + if (event.deltaY > 0) { + await appContext.tabManager.activateNextTabCommand(); + } else { + await appContext.tabManager.activatePreviousTabCommand(); + } + this.activeTabEl.scrollIntoView(); + } }); this.$scrollButtonLeft[0].addEventListener('click', () => this.scrollTabContainer(-200)); diff --git a/apps/client/src/widgets/type_widgets/mind_map.ts b/apps/client/src/widgets/type_widgets/mind_map.ts index 18867bc83..898de7a26 100644 --- a/apps/client/src/widgets/type_widgets/mind_map.ts +++ b/apps/client/src/widgets/type_widgets/mind_map.ts @@ -286,4 +286,13 @@ export default class MindMapWidget extends TypeWidget { utils.downloadSvgAsPng(this.note.title, svg); } + async executeWithContentElementEvent({ resolve, ntxId }: EventData<"executeWithContentElement">) { + if (!this.isNoteContext(ntxId)) { + return; + } + + await this.initialized; + + resolve(this.$content.find('.main-node-container')); + } } diff --git a/apps/server-e2e/src/layout/tab_bar.spec.ts b/apps/server-e2e/src/layout/tab_bar.spec.ts index c064dbf57..cb82f8f82 100644 --- a/apps/server-e2e/src/layout/tab_bar.spec.ts +++ b/apps/server-e2e/src/layout/tab_bar.spec.ts @@ -115,7 +115,7 @@ test("Search works when dismissing a tab", async ({ page, context }) => { await app.getTab(0).click(); await app.openAndClickNoteActionMenu("Search in note"); - await expect(app.findAndReplaceWidget).toBeVisible(); + await expect(app.findAndReplaceWidget.first()).toBeVisible(); }); test("New tab displays workspaces", async ({ page, context }) => { diff --git a/apps/server/src/services/hidden_subtree_launcherbar.ts b/apps/server/src/services/hidden_subtree_launcherbar.ts index 633dbe363..3ddef26df 100644 --- a/apps/server/src/services/hidden_subtree_launcherbar.ts +++ b/apps/server/src/services/hidden_subtree_launcherbar.ts @@ -72,7 +72,7 @@ export default function buildLaunchBarConfig() { id: "_lbLlmChat", title: t("hidden-subtree.llm-chat-title"), type: "launcher", - command: "createAiChat", + builtinWidget: "aiChatLauncher", icon: "bx bx-bot", attributes: [ { type: "label", name: "desktopOnly" } diff --git a/docs/Release Notes/Release Notes/v0.94.0.md b/docs/Release Notes/Release Notes/v0.94.0.md index 1761b94ad..03dfa000f 100644 --- a/docs/Release Notes/Release Notes/v0.94.0.md +++ b/docs/Release Notes/Release Notes/v0.94.0.md @@ -59,6 +59,7 @@ * [Text notes: add a way to move up and down text lines via a keyboard shortcut](https://github.com/TriliumNext/Notes/issues/1002) by @dogfuntom * [improve tab scroll UX by switching from instant to smooth behavior](https://github.com/TriliumNext/Notes/pull/2030) by @SiriusXT * Calendar view: display calendar view if `#viewType=calendar` is set. +* [Mind map: add search support](https://github.com/TriliumNext/Notes/pull/2055) by @SiriusXT ## 📖 Documentation