Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
d847d96
Initial plan
Copilot Feb 12, 2026
89ea0a2
Replace Leaflet with MapLibre GL for geo map collection
Copilot Feb 12, 2026
29ebb31
fix(map): crashing due to maxBounds
eliandoran Feb 15, 2026
f0d91d4
fix(collections): collection properties not shown if there are no chi…
eliandoran Feb 15, 2026
6e32aaa
fix(collections): list/grid collection properties clipped when little…
eliandoran Feb 15, 2026
cffcfeb
feat(collections): improve display of no children
eliandoran Feb 15, 2026
aa9b056
fix(collections): collection properties still clipped in list/grid view
eliandoran Feb 15, 2026
9e3bb58
fix(collections/map): shifting into collection properties when switch…
eliandoran Feb 15, 2026
e53fd1b
Merge remote-tracking branch 'origin/main' into copilot/switch-to-map…
eliandoran Feb 15, 2026
ea09018
Merge remote-tracking branch 'origin/main' into copilot/switch-to-map…
eliandoran Feb 15, 2026
647ba85
feat(collections/map): faster rendering of markers (use circle for now)
eliandoran Feb 15, 2026
3865d96
feat(collections/map): use SVG for markers
eliandoran Feb 15, 2026
8d0c61e
refactor(collections/map): extract marker data build to separate module
eliandoran Feb 15, 2026
60facce
chore(collections/map): bring back colors
eliandoran Feb 15, 2026
12d9391
chore(collections/map): basic icon snapshot mechanism without cache
eliandoran Feb 15, 2026
ad1f83f
feat(collections/map): faster icon rendering
eliandoran Feb 15, 2026
f197756
feat(collections/map): proper DPI scaling of icons
eliandoran Feb 15, 2026
156aa55
refactor(collections/map): extract marker icon rendering
eliandoran Feb 15, 2026
638beb5
feat(collections/map): cache icon renderings
eliandoran Feb 15, 2026
3041cb0
chore(collections/map): reintroduce zoom buttons
eliandoran Feb 16, 2026
b812d7f
chore(collections/map): reintroduce marker context menu
eliandoran Feb 16, 2026
c7842c7
chore(collections/map): reintroduce tooltip on mouse enter
eliandoran Feb 16, 2026
71e9eae
fix(collections/map): tooltips not always available on first reload
eliandoran Feb 16, 2026
48ff643
refactor(collections/map): extract context menus to standalone component
eliandoran Feb 16, 2026
7829957
refactor(collections/map): extract markers into own component
eliandoran Feb 16, 2026
693a936
chore(client): types broken after removing import in type declaration
eliandoran Feb 16, 2026
c080bb7
refactor(collections/map): remove use of namespace in map.tsx
eliandoran Feb 16, 2026
0d169b0
fix(collections/map): crash when reloading markers
eliandoran Feb 16, 2026
a53dc11
chore(collections/map): fix type issue
eliandoran Feb 16, 2026
5c1c0ef
fix(collections/map): markers loading slowly due to not waiting for s…
eliandoran Feb 16, 2026
49c0adb
chore(collections/map): reintroduce labels
eliandoran Feb 16, 2026
1812fdc
chore(collections/map): remove circle from marker
eliandoran Feb 16, 2026
5fec097
feat(collections/map): add shadow to markers
eliandoran Feb 16, 2026
d2cf843
chore(collections/map): make icon inside marker bigger
eliandoran Feb 16, 2026
11d3960
refactor(collections/map): remove namespaced types
eliandoran Feb 16, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 1 addition & 5 deletions apps/client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
"@fullcalendar/list": "6.1.20",
"@fullcalendar/multimonth": "6.1.20",
"@fullcalendar/timegrid": "6.1.20",
"@maplibre/maplibre-gl-leaflet": "0.1.3",
"@mermaid-js/layout-elk": "0.2.0",
"@mind-elixir/node-menu": "5.0.1",
"@popperjs/core": "2.11.8",
Expand Down Expand Up @@ -51,9 +50,8 @@
"jsplumb": "2.15.6",
"katex": "0.16.28",
"knockout": "3.5.1",
"leaflet": "1.9.4",
"leaflet-gpx": "2.2.0",
"mark.js": "8.11.1",
"maplibre-gl": "5.18.0",
"marked": "17.0.2",
"mermaid": "11.12.2",
"mind-elixir": "5.8.0",
Expand All @@ -72,8 +70,6 @@
"@prefresh/vite": "2.4.11",
"@types/bootstrap": "5.2.10",
"@types/jquery": "3.5.33",
"@types/leaflet": "1.9.21",
"@types/leaflet-gpx": "1.3.8",
"@types/mark.js": "8.11.12",
"@types/reveal.js": "5.2.2",
"@types/tabulator-tables": "6.3.1",
Expand Down
8 changes: 4 additions & 4 deletions apps/client/src/menus/link_context_menu.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { LeafletMouseEvent } from "leaflet";
import type { GeoMouseEvent } from "../widgets/collections/geomap/map.js";

import appContext, { type CommandNames } from "../components/app_context.js";
import { t } from "../services/i18n.js";
Expand All @@ -16,7 +16,7 @@ function openContextMenu(notePath: string, e: ContextMenuEvent, viewScope: ViewS
});
}

function getItems(e: ContextMenuEvent | LeafletMouseEvent): MenuItem<CommandNames>[] {
function getItems(e: ContextMenuEvent | GeoMouseEvent): MenuItem<CommandNames>[] {
const ntxId = getNtxId(e);
const isMobileSplitOpen = isMobile() && appContext.tabManager.getNoteContextById(ntxId).getMainContext().getSubContexts().length > 1;

Expand All @@ -28,7 +28,7 @@ function getItems(e: ContextMenuEvent | LeafletMouseEvent): MenuItem<CommandName
];
}

function handleLinkContextMenuItem(command: string | undefined, e: ContextMenuEvent | LeafletMouseEvent, notePath: string, viewScope = {}, hoistedNoteId: string | null = null) {
function handleLinkContextMenuItem(command: string | undefined, e: ContextMenuEvent | GeoMouseEvent, notePath: string, viewScope = {}, hoistedNoteId: string | null = null) {
if (!hoistedNoteId) {
hoistedNoteId = appContext.tabManager.getActiveContext()?.hoistedNoteId ?? null;
}
Expand All @@ -52,7 +52,7 @@ function handleLinkContextMenuItem(command: string | undefined, e: ContextMenuEv
return false;
}

function getNtxId(e: ContextMenuEvent | LeafletMouseEvent) {
function getNtxId(e: ContextMenuEvent | GeoMouseEvent) {
if (utils.isDesktop()) {
const subContexts = appContext.tabManager.getActiveContext()?.getSubContexts();
if (!subContexts) return null;
Expand Down
2 changes: 1 addition & 1 deletion apps/client/src/translations/en/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -1010,7 +1010,7 @@
"no_attachments": "This note has no attachments."
},
"book": {
"no_children_help": "This collection doesn't have any child notes so there's nothing to display. See <a href=\"https://triliumnext.github.io/Docs/Wiki/book-note.html\">wiki</a> for details.",
"no_children_help": "This collection doesn't have any child notes so there's nothing to display.",
"drag_locked_title": "Locked for editing",
"drag_locked_message": "Dragging not allowed since the collection is locked for editing."
},
Expand Down
22 changes: 2 additions & 20 deletions apps/client/src/types-lib.d.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
export {};

// TODO: Use real @types/ but that one generates a lot of errors.
declare module "draggabilly" {
type DraggabillyEventData = {};
Expand Down Expand Up @@ -32,26 +34,6 @@ declare module "katex/contrib/auto-render" {
export default renderMathInElement;
}

import * as L from "leaflet";

declare module "leaflet" {
interface GPXMarker {
startIcon?: DivIcon | Icon | string | undefined;
endIcon?: DivIcon | Icon | string | undefined;
wptIcons?: {
[key: string]: DivIcon | Icon | string;
};
wptTypeIcons?: {
[key: string]: DivIcon | Icon | string;
};
pointMatchers?: Array<{ regex: RegExp; icon: DivIcon | Icon | string}>;
}

interface GPXOptions {
markers?: GPXMarker | undefined;
}
}

declare global {
interface Navigator {
/** Returns a boolean indicating whether the browser is running in standalone mode. Available on Apple's iOS Safari only. */
Expand Down
6 changes: 0 additions & 6 deletions apps/client/src/widgets/FloatingButtons.css
Original file line number Diff line number Diff line change
Expand Up @@ -88,12 +88,6 @@
}
/* #endregion */

/* #region Geo map buttons */
.leaflet-pane {
z-index: 50;
}
/* #endregion */

/* #region Close floating buttons */
.close-floating-buttons {
margin-inline-start: 5px !important;
Expand Down
9 changes: 9 additions & 0 deletions apps/client/src/widgets/NoteDetail.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import "./NoteDetail.css";

import clsx from "clsx";
import { note } from "mermaid/dist/rendering-util/rendering-elements/shapes/note.js";
import { isValidElement, VNode } from "preact";
import { useEffect, useRef, useState } from "preact/hooks";

Expand Down Expand Up @@ -355,6 +356,14 @@ export function checkFullHeight(noteContext: NoteContext | undefined, type: Exte
// https://github.com/zadam/trilium/issues/2522
const isBackendNote = noteContext?.noteId === "_backendLog";
const isFullHeightNoteType = type && TYPE_MAPPINGS[type].isFullHeight;

// Allow vertical centering when there are no results.
if (type === "book" &&
[ "grid", "list" ].includes(noteContext.note?.getLabelValue("viewType") ?? "grid") &&
!noteContext.note?.hasChildren()) {
return true;
}

return (!noteContext?.hasNoteList() && isFullHeightNoteType)
|| noteContext?.viewScope?.viewMode === "attachments"
|| isBackendNote;
Expand Down
6 changes: 5 additions & 1 deletion apps/client/src/widgets/collections/NoteList.css
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,12 @@
min-height: 0;
max-width: var(--max-content-width); /* Inherited from .note-split */

overflow: auto;
overflow: visible;
contain: none !important;

&.full-height {
overflow: auto;
}
}

body.prefers-centered-content .note-list-widget:not(.full-height) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,51 @@
import type { LatLng, LeafletMouseEvent } from "leaflet";
import { MapMouseEvent } from "maplibre-gl";
import { useCallback, useContext, useEffect } from "preact/hooks";

import appContext, { type CommandMappings } from "../../../components/app_context.js";
import FNote from "../../../entities/fnote.js";
import contextMenu, { type MenuItem } from "../../../menus/context_menu.js";
import linkContextMenu from "../../../menus/link_context_menu.js";
import NoteColorPicker from "../../../menus/custom-items/NoteColorPicker.jsx";
import { t } from "../../../services/i18n.js";
import { createNewNote } from "./api.js";
import linkContextMenu from "../../../menus/link_context_menu.js";
import { copyTextWithToast } from "../../../services/clipboard_ext.js";
import { t } from "../../../services/i18n.js";
import link from "../../../services/link.js";
import { createNewNote } from "./api.js";
import { type GeoMouseEvent,ParentMap, toMapLibreEvent } from "./map.js";
import { MARKER_LAYER } from "./Markers.js";

export default function ContextMenus({ note, isReadOnly }: { note: FNote, isReadOnly }) {
const map = useContext(ParentMap);

export default function openContextMenu(noteId: string, e: LeafletMouseEvent, isEditable: boolean) {
const onContextMenu = useCallback((e: GeoMouseEvent) => {
if (!map) return;
const features = map.queryRenderedFeatures(e.point, {
layers: [ MARKER_LAYER ]
});

if (features.length > 0) {
// Marker context menu.
openContextMenu(features[0].properties.id, e, !isReadOnly);
} else {
// Empty area context menu.
openMapContextMenu(note.noteId, e, !isReadOnly);
}
}, [ map, note.noteId, isReadOnly ]);

useEffect(() => {
if (!onContextMenu || !map) return;

const handler = (e: MapMouseEvent) => {
e.preventDefault();
onContextMenu(toMapLibreEvent(e));
};
map.on("contextmenu", handler);
return () => { map.off("contextmenu", handler); };
}, [ map, onContextMenu ]);

return null;
}

export function openContextMenu(noteId: string, e: GeoMouseEvent, isEditable: boolean) {
let items: MenuItem<keyof CommandMappings>[] = [
...buildGeoLocationItem(e),
{ kind: "separator" },
Expand Down Expand Up @@ -44,7 +81,7 @@ export default function openContextMenu(noteId: string, e: LeafletMouseEvent, is
});
}

export function openMapContextMenu(noteId: string, e: LeafletMouseEvent, isEditable: boolean) {
export function openMapContextMenu(noteId: string, e: GeoMouseEvent, isEditable: boolean) {
let items: MenuItem<keyof CommandMappings>[] = [
...buildGeoLocationItem(e)
];
Expand All @@ -58,7 +95,7 @@ export function openMapContextMenu(noteId: string, e: LeafletMouseEvent, isEdita
handler: () => createNewNote(noteId, e),
uiIcon: "bx bx-plus"
}
]
];
}

contextMenu.show({
Expand All @@ -71,8 +108,8 @@ export function openMapContextMenu(noteId: string, e: LeafletMouseEvent, isEdita
});
}

function buildGeoLocationItem(e: LeafletMouseEvent) {
function formatGeoLocation(latlng: LatLng, precision: number = 6) {
function buildGeoLocationItem(e: GeoMouseEvent) {
function formatGeoLocation(latlng: { lat: number; lng: number }, precision: number = 6) {
return `${latlng.lat.toFixed(precision)}, ${latlng.lng.toFixed(precision)}`;
}

Expand Down
Loading