chore(react/launch_bar): port note launcher

This commit is contained in:
Elian Doran 2025-12-04 16:33:58 +02:00
parent 06a9f95979
commit 0d6bcba023
No known key found for this signature in database
4 changed files with 33 additions and 9 deletions

View File

@ -1,7 +1,6 @@
import CalendarWidget from "../buttons/calendar.js"; import CalendarWidget from "../buttons/calendar.js";
import SyncStatusWidget from "../sync_status.js"; import SyncStatusWidget from "../sync_status.js";
import BasicWidget, { wrapReactWidgets } from "../basic_widget.js"; import BasicWidget, { wrapReactWidgets } from "../basic_widget.js";
import NoteLauncher from "../buttons/launcher/note_launcher.js";
import ScriptLauncher from "../buttons/launcher/script_launcher.js"; import ScriptLauncher from "../buttons/launcher/script_launcher.js";
import utils from "../../services/utils.js"; import utils from "../../services/utils.js";
import TodayLauncher from "../buttons/launcher/today_launcher.js"; import TodayLauncher from "../buttons/launcher/today_launcher.js";
@ -13,7 +12,7 @@ import HistoryNavigationButton from "../launch_bar/HistoryNavigation.jsx";
import AiChatButton from "../launch_bar/AiChatButton.jsx"; import AiChatButton from "../launch_bar/AiChatButton.jsx";
import ProtectedSessionStatusWidget from "../launch_bar/ProtectedSessionStatusWidget.jsx"; import ProtectedSessionStatusWidget from "../launch_bar/ProtectedSessionStatusWidget.jsx";
import { VNode } from "preact"; import { VNode } from "preact";
import { CommandButton } from "../launch_bar/GenericButtons.jsx"; import { CommandButton, NoteLauncher } from "../launch_bar/GenericButtons.jsx";
interface InnerWidget extends BasicWidget { interface InnerWidget extends BasicWidget {
settings?: { settings?: {
@ -58,7 +57,7 @@ export default class LauncherWidget extends BasicWidget {
if (launcherType === "command") { if (launcherType === "command") {
widget = wrapReactWidgets<BasicWidget>([ <CommandButton launcherNote={note} /> ])[0]; widget = wrapReactWidgets<BasicWidget>([ <CommandButton launcherNote={note} /> ])[0];
} else if (launcherType === "note") { } else if (launcherType === "note") {
widget = new NoteLauncher(note).class("launcher-button"); widget = wrapReactWidgets<BasicWidget>([ <NoteLauncher launcherNote={note} /> ])[0];
} else if (launcherType === "script") { } else if (launcherType === "script") {
widget = new ScriptLauncher(note).class("launcher-button"); widget = new ScriptLauncher(note).class("launcher-button");
} else if (launcherType === "customWidget") { } else if (launcherType === "customWidget") {

View File

@ -5,7 +5,7 @@ import type FNote from "../../entities/fnote";
import { useChildNotes, useNoteLabelBoolean } from "../react/hooks"; import { useChildNotes, useNoteLabelBoolean } from "../react/hooks";
import "./BookmarkButtons.css"; import "./BookmarkButtons.css";
import NoteLink from "../react/NoteLink"; import NoteLink from "../react/NoteLink";
import { NoteLauncher } from "./GenericButtons"; import { CustomNoteLauncher } from "./GenericButtons";
const PARENT_NOTE_ID = "_lbBookmarks"; const PARENT_NOTE_ID = "_lbBookmarks";
@ -28,7 +28,7 @@ function SingleBookmark({ note }: { note: FNote }) {
const [ bookmarkFolder ] = useNoteLabelBoolean(note, "bookmarkFolder"); const [ bookmarkFolder ] = useNoteLabelBoolean(note, "bookmarkFolder");
return bookmarkFolder return bookmarkFolder
? <BookmarkFolder note={note} /> ? <BookmarkFolder note={note} />
: <NoteLauncher launcherNote={note} targetNoteId={note.noteId} /> : <CustomNoteLauncher launcherNote={note} targetNoteId={note.noteId} />
} }
function BookmarkFolder({ note }: { note: FNote }) { function BookmarkFolder({ note }: { note: FNote }) {

View File

@ -2,8 +2,10 @@ import appContext, { CommandNames } from "../../components/app_context";
import FNote from "../../entities/fnote"; import FNote from "../../entities/fnote";
import link_context_menu from "../../menus/link_context_menu"; import link_context_menu from "../../menus/link_context_menu";
import { escapeHtml, isCtrlKey } from "../../services/utils"; import { escapeHtml, isCtrlKey } from "../../services/utils";
import { useNoteLabel, useNoteProperty } from "../react/hooks"; import { useNoteLabel, useNoteProperty, useNoteRelation } from "../react/hooks";
import { LaunchBarActionButton, useLauncherIconAndTitle } from "./launch_bar_widgets"; import { LaunchBarActionButton, useLauncherIconAndTitle } from "./launch_bar_widgets";
import dialog from "../../services/dialog";
import { t } from "../../services/i18n";
export function CommandButton({ launcherNote }: { launcherNote: FNote }) { export function CommandButton({ launcherNote }: { launcherNote: FNote }) {
const { icon, title } = useLauncherIconAndTitle(launcherNote); const { icon, title } = useLauncherIconAndTitle(launcherNote);
@ -18,13 +20,19 @@ export function CommandButton({ launcherNote }: { launcherNote: FNote }) {
) )
} }
export function NoteLauncher({ launcherNote, targetNoteId, hoistedNoteId }: { launcherNote: FNote, targetNoteId: string, hoistedNoteId?: string }) { export function CustomNoteLauncher({ launcherNote, targetNoteId, hoistedNoteId }: { launcherNote: FNote, targetNoteId: string | null, hoistedNoteId?: string }) {
const { icon, title } = useLauncherIconAndTitle(launcherNote); const { icon, title } = useLauncherIconAndTitle(launcherNote);
async function launch(evt: MouseEvent) { async function launch(evt: MouseEvent) {
if (evt.which === 3) { if (evt.which === 3) {
return; return;
} }
if (!targetNoteId) {
dialog.info(t("note_launcher.this_launcher_doesnt_define_target_note"));
return;
}
const hoistedNoteIdWithDefault = hoistedNoteId || launcherNote.getRelationValue("hoistedNote") || appContext.tabManager.getActiveContext()?.hoistedNoteId; const hoistedNoteIdWithDefault = hoistedNoteId || launcherNote.getRelationValue("hoistedNote") || appContext.tabManager.getActiveContext()?.hoistedNoteId;
const ctrlKey = isCtrlKey(evt); const ctrlKey = isCtrlKey(evt);
@ -44,8 +52,22 @@ export function NoteLauncher({ launcherNote, targetNoteId, hoistedNoteId }: { la
onAuxClick={launch} onAuxClick={launch}
onContextMenu={evt => { onContextMenu={evt => {
evt.preventDefault(); evt.preventDefault();
link_context_menu.openContextMenu(targetNoteId, evt); if (targetNoteId) {
link_context_menu.openContextMenu(targetNoteId, evt);
}
}} }}
/> />
) )
} }
// we're intentionally displaying the launcher title and icon instead of the target,
// e.g. you want to make launchers to 2 mermaid diagrams which both have mermaid icon (ok),
// but on the launchpad you want them distinguishable.
// for titles, the note titles may follow a different scheme than maybe desirable on the launchpad
// another reason is the discrepancy between what user sees on the launchpad and in the config (esp. icons).
// The only downside is more work in setting up the typical case
// where you actually want to have both title and icon in sync, but for those cases there are bookmarks
export function NoteLauncher({ launcherNote, ...restProps }: { launcherNote: FNote, hoistedNoteId?: string }) {
const [ targetNote ] = useNoteRelation(launcherNote, "target");
return <CustomNoteLauncher launcherNote={launcherNote} targetNoteId={targetNote ?? null} {...restProps} />
}

View File

@ -59,7 +59,10 @@ type Labels = {
*/ */
type Relations = [ type Relations = [
"searchScript", "searchScript",
"ancestor" "ancestor",
// Launcher-specific
"target"
]; ];
export type LabelNames = keyof Labels; export type LabelNames = keyof Labels;