From 455b190a5bff753240862b26b514e5fa17a9f003 Mon Sep 17 00:00:00 2001
From: Elian Doran
Date: Sat, 15 Nov 2025 09:36:18 +0200
Subject: [PATCH 01/21] refactor(client): rename to user attributes
---
...utesDisplay.css => UserAttributesList.css} | 10 +++---
...utesDisplay.tsx => UserAttributesList.tsx} | 34 +++++++++----------
.../src/widgets/collections/board/card.tsx | 4 +--
3 files changed, 24 insertions(+), 24 deletions(-)
rename apps/client/src/widgets/attribute_widgets/{PromotedAttributesDisplay.css => UserAttributesList.css} (72%)
rename apps/client/src/widgets/attribute_widgets/{PromotedAttributesDisplay.tsx => UserAttributesList.tsx} (74%)
diff --git a/apps/client/src/widgets/attribute_widgets/PromotedAttributesDisplay.css b/apps/client/src/widgets/attribute_widgets/UserAttributesList.css
similarity index 72%
rename from apps/client/src/widgets/attribute_widgets/PromotedAttributesDisplay.css
rename to apps/client/src/widgets/attribute_widgets/UserAttributesList.css
index 4bed347e4..ef8c1763d 100644
--- a/apps/client/src/widgets/attribute_widgets/PromotedAttributesDisplay.css
+++ b/apps/client/src/widgets/attribute_widgets/UserAttributesList.css
@@ -1,4 +1,4 @@
-.promoted-attributes {
+.user-attributes {
display: flex;
flex-wrap: wrap;
gap: 8px;
@@ -6,7 +6,7 @@
margin-top: 8px;
}
-.promoted-attributes .promoted-attribute {
+.user-attributes .user-attribute {
padding: 2px 10px;
border-radius: 9999px;
white-space: nowrap;
@@ -17,15 +17,15 @@
line-height: 1.2;
}
-.promoted-attributes .promoted-attribute:hover {
+.user-attributes .user-attribute:hover {
background-color: var(--chip-bg-hover, rgba(0, 0, 0, 0.12));
border-color: var(--chip-border-hover, rgba(0, 0, 0, 0.22));
}
-.promoted-attributes .promoted-attribute .name {
+.user-attributes .user-attribute .name {
font-weight: 600;
}
-.promoted-attributes .promoted-attribute .value {
+.user-attributes .user-attribute .value {
opacity: 0.9;
}
\ No newline at end of file
diff --git a/apps/client/src/widgets/attribute_widgets/PromotedAttributesDisplay.tsx b/apps/client/src/widgets/attribute_widgets/UserAttributesList.tsx
similarity index 74%
rename from apps/client/src/widgets/attribute_widgets/PromotedAttributesDisplay.tsx
rename to apps/client/src/widgets/attribute_widgets/UserAttributesList.tsx
index 987647519..f01e70b49 100644
--- a/apps/client/src/widgets/attribute_widgets/PromotedAttributesDisplay.tsx
+++ b/apps/client/src/widgets/attribute_widgets/UserAttributesList.tsx
@@ -1,16 +1,16 @@
import { useState } from "preact/hooks";
import FNote from "../../entities/fnote";
-import "./PromotedAttributesDisplay.css";
+import "./UserAttributesList.css";
import { useTriliumEvent } from "../react/hooks";
import attributes from "../../services/attributes";
import { DefinitionObject } from "../../services/promoted_attribute_definition_parser";
import { formatDateTime } from "../../utils/formatters";
-import { ComponentChild, ComponentChildren, CSSProperties } from "preact";
+import { ComponentChildren, CSSProperties } from "preact";
import Icon from "../react/Icon";
import NoteLink from "../react/NoteLink";
import { getReadableTextColor } from "../../services/css_class_manager";
-interface PromotedAttributesDisplayProps {
+interface UserAttributesListProps {
note: FNote;
ignoredAttributes?: string[];
}
@@ -23,39 +23,39 @@ interface AttributeWithDefinitions {
def: DefinitionObject;
}
-export default function PromotedAttributesDisplay({ note, ignoredAttributes }: PromotedAttributesDisplayProps) {
- const promotedDefinitionAttributes = useNoteAttributesWithDefinitions(note, ignoredAttributes);
- return promotedDefinitionAttributes?.length > 0 && (
-
- {promotedDefinitionAttributes?.map(attr => buildPromotedAttribute(attr))}
+export default function UserAttributesDisplay({ note, ignoredAttributes }: UserAttributesListProps) {
+ const userAttributes = useNoteAttributesWithDefinitions(note, ignoredAttributes);
+ return userAttributes?.length > 0 && (
+
+ {userAttributes?.map(attr => buildUserAttribute(attr))}
)
}
function useNoteAttributesWithDefinitions(note: FNote, attributesToIgnore: string[] = []): AttributeWithDefinitions[] {
- const [ promotedDefinitionAttributes, setPromotedDefinitionAttributes ] = useState
(getAttributesWithDefinitions(note, attributesToIgnore));
+ const [ userAttributes, setUserAttributes ] = useState(getAttributesWithDefinitions(note, attributesToIgnore));
useTriliumEvent("entitiesReloaded", ({ loadResults }) => {
if (loadResults.getAttributeRows().some(attr => attributes.isAffecting(attr, note))) {
- setPromotedDefinitionAttributes(getAttributesWithDefinitions(note, attributesToIgnore));
+ setUserAttributes(getAttributesWithDefinitions(note, attributesToIgnore));
}
});
- return promotedDefinitionAttributes;
+ return userAttributes;
}
-function PromotedAttribute({ attr, children, style }: { attr: AttributeWithDefinitions, children: ComponentChildren, style?: CSSProperties }) {
+function UserAttribute({ attr, children, style }: { attr: AttributeWithDefinitions, children: ComponentChildren, style?: CSSProperties }) {
const className = `${attr.type === "label" ? "label" + " " + attr.def.labelType : "relation"}`;
return (
-
+
{children}
)
}
-function buildPromotedAttribute(attr: AttributeWithDefinitions): ComponentChildren {
+function buildUserAttribute(attr: AttributeWithDefinitions): ComponentChildren {
const defaultLabel = <>{attr.friendlyName}: {" "}>;
let content: ComponentChildren;
let style: CSSProperties | undefined;
@@ -102,13 +102,13 @@ function buildPromotedAttribute(attr: AttributeWithDefinitions): ComponentChildr
content = <>{defaultLabel} >;
}
- return {content}
+ return {content}
}
function getAttributesWithDefinitions(note: FNote, attributesToIgnore: string[] = []): AttributeWithDefinitions[] {
- const promotedDefinitionAttributes = note.getAttributeDefinitions();
+ const attributeDefintions = note.getAttributeDefinitions();
const result: AttributeWithDefinitions[] = [];
- for (const attr of promotedDefinitionAttributes) {
+ for (const attr of attributeDefintions) {
const def = attr.getDefinition();
const [ type, name ] = attr.name.split(":", 2);
const friendlyName = def?.promotedAlias || name;
diff --git a/apps/client/src/widgets/collections/board/card.tsx b/apps/client/src/widgets/collections/board/card.tsx
index 3fff79aab..fc81e9f0e 100644
--- a/apps/client/src/widgets/collections/board/card.tsx
+++ b/apps/client/src/widgets/collections/board/card.tsx
@@ -6,7 +6,7 @@ import { BoardViewContext, TitleEditor } from ".";
import { ContextMenuEvent } from "../../../menus/context_menu";
import { openNoteContextMenu } from "./context_menu";
import { t } from "../../../services/i18n";
-import PromotedAttributesDisplay from "../../attribute_widgets/PromotedAttributesDisplay";
+import UserAttributesDisplay from "../../attribute_widgets/UserAttributesList";
export const CARD_CLIPBOARD_TYPE = "trilium/board-card";
@@ -109,7 +109,7 @@ export default function Card({
title={t("board_view.edit-note-title")}
onClick={handleEdit}
/>
-
+
>
) : (
Date: Sat, 15 Nov 2025 10:15:11 +0200
Subject: [PATCH 02/21] feat(board/relation): group by relation
---
apps/client/src/entities/fnote.ts | 10 ++++++++++
apps/client/src/widgets/collections/board/data.ts | 5 +++--
2 files changed, 13 insertions(+), 2 deletions(-)
diff --git a/apps/client/src/entities/fnote.ts b/apps/client/src/entities/fnote.ts
index 230c4783c..425742ee3 100644
--- a/apps/client/src/entities/fnote.ts
+++ b/apps/client/src/entities/fnote.ts
@@ -804,6 +804,16 @@ export default class FNote {
return this.getAttributeValue(LABEL, name);
}
+ getLabelOrRelation(nameWithPrefix: string) {
+ if (nameWithPrefix.startsWith("#")) {
+ return this.getLabelValue(nameWithPrefix.substring(1));
+ } else if (nameWithPrefix.startsWith("~")) {
+ return this.getRelationValue(nameWithPrefix.substring(1));
+ } else {
+ return this.getLabelValue(nameWithPrefix);
+ }
+ }
+
/**
* @param name - relation name
* @returns relation value if relation exists, null otherwise
diff --git a/apps/client/src/widgets/collections/board/data.ts b/apps/client/src/widgets/collections/board/data.ts
index f283e2dc7..a37487ec9 100644
--- a/apps/client/src/widgets/collections/board/data.ts
+++ b/apps/client/src/widgets/collections/board/data.ts
@@ -57,7 +57,8 @@ export async function getBoardData(parentNote: FNote, groupByColumn: string, per
return {
byColumn,
- newPersistedData
+ newPersistedData,
+ isInRelationMode: groupByColumn.startsWith("~")
};
}
@@ -70,7 +71,7 @@ async function recursiveGroupBy(branches: FBranch[], byColumn: ColumnMap, groupB
await recursiveGroupBy(note.getChildBranches(), byColumn, groupByColumn, includeArchived, seenNoteIds);
}
- const group = note.getLabelValue(groupByColumn);
+ const group = note.getLabelOrRelation(groupByColumn);
if (!group || seenNoteIds.has(note.noteId)) {
continue;
}
From ad6d61f1f75c629cb86361543e31b5f6cfb36998 Mon Sep 17 00:00:00 2001
From: Elian Doran
Date: Sat, 15 Nov 2025 10:17:40 +0200
Subject: [PATCH 03/21] feat(board/relation): display note titles
---
apps/client/src/widgets/collections/board/column.tsx | 11 +++++++++--
apps/client/src/widgets/collections/board/index.css | 5 +++++
apps/client/src/widgets/collections/board/index.tsx | 5 ++++-
3 files changed, 18 insertions(+), 3 deletions(-)
diff --git a/apps/client/src/widgets/collections/board/column.tsx b/apps/client/src/widgets/collections/board/column.tsx
index 4ea8768e9..791eb585e 100644
--- a/apps/client/src/widgets/collections/board/column.tsx
+++ b/apps/client/src/widgets/collections/board/column.tsx
@@ -12,6 +12,7 @@ import Card, { CARD_CLIPBOARD_TYPE, CardDragData } from "./card";
import { JSX } from "preact/jsx-runtime";
import froca from "../../../services/froca";
import { DragData, TREE_CLIPBOARD_TYPE } from "../../note_tree";
+import NoteLink from "../../react/NoteLink";
interface DragContext {
column: string;
@@ -27,12 +28,14 @@ export default function Column({
api,
onColumnHover,
isAnyColumnDragging,
+ isInRelationMode
}: {
columnItems?: { note: FNote, branch: FBranch }[];
isDraggingColumn: boolean,
api: BoardApi,
onColumnHover?: (index: number, mouseX: number, rect: DOMRect) => void,
- isAnyColumnDragging?: boolean
+ isAnyColumnDragging?: boolean,
+ isInRelationMode: boolean
} & DragContext) {
const [ isVisible, setVisible ] = useState(true);
const { columnNameToEdit, setColumnNameToEdit, dropTarget, draggedCard, dropPosition } = useContext(BoardViewContext)!;
@@ -103,7 +106,11 @@ export default function Column({
>
{!isEditing ? (
<>
- {column}
+
+ {isInRelationMode
+ ?
+ : column}
+
{columnItems?.length ?? 0}
();
const [ columns, setColumns ] = useState();
+ const [ isInRelationMode, setIsRelationMode ] = useState(false);
const [ draggedCard, setDraggedCard ] = useState<{ noteId: string, branchId: string, fromColumn: string, index: number } | null>(null);
const [ dropTarget, setDropTarget ] = useState(null);
const [ dropPosition, setDropPosition ] = useState<{ column: string, index: number } | null>(null);
@@ -78,8 +79,9 @@ export default function BoardView({ note: parentNote, noteIds, viewConfig, saveC
]);
function refresh() {
- getBoardData(parentNote, statusAttribute, viewConfig ?? {}, includeArchived).then(({ byColumn, newPersistedData }) => {
+ getBoardData(parentNote, statusAttribute, viewConfig ?? {}, includeArchived).then(({ byColumn, newPersistedData, isInRelationMode }) => {
setByColumn(byColumn);
+ setIsRelationMode(isInRelationMode);
if (newPersistedData) {
viewConfig = { ...newPersistedData };
@@ -171,6 +173,7 @@ export default function BoardView({ note: parentNote, noteIds, viewConfig, saveC
)}
Date: Sat, 15 Nov 2025 10:26:19 +0200
Subject: [PATCH 04/21] feat(board/relation): allow dragging between columns
---
apps/client/src/services/attributes.ts | 10 ++++++++++
.../src/widgets/collections/board/api.ts | 20 ++++++++++++++++---
2 files changed, 27 insertions(+), 3 deletions(-)
diff --git a/apps/client/src/services/attributes.ts b/apps/client/src/services/attributes.ts
index 0b605f5bd..13011b7a6 100644
--- a/apps/client/src/services/attributes.ts
+++ b/apps/client/src/services/attributes.ts
@@ -22,6 +22,15 @@ export async function setLabel(noteId: string, name: string, value: string = "",
});
}
+export async function setRelation(noteId: string, name: string, value: string = "", isInheritable = false) {
+ await server.put(`notes/${noteId}/set-attribute`, {
+ type: "relation",
+ name: name,
+ value: value,
+ isInheritable
+ });
+}
+
async function removeAttributeById(noteId: string, attributeId: string) {
await server.remove(`notes/${noteId}/attributes/${attributeId}`);
}
@@ -116,6 +125,7 @@ function isAffecting(attrRow: AttributeRow, affectedNote: FNote | null | undefin
export default {
addLabel,
setLabel,
+ setRelation,
setAttribute,
removeAttributeById,
removeOwnedLabelByName,
diff --git a/apps/client/src/widgets/collections/board/api.ts b/apps/client/src/widgets/collections/board/api.ts
index 14ad4d587..6543bafd9 100644
--- a/apps/client/src/widgets/collections/board/api.ts
+++ b/apps/client/src/widgets/collections/board/api.ts
@@ -12,15 +12,25 @@ import { ColumnMap } from "./data";
export default class BoardApi {
+ private isRelationMode: boolean;
+ statusAttribute: string;
+
constructor(
private byColumn: ColumnMap | undefined,
public columns: string[],
private parentNote: FNote,
- readonly statusAttribute: string,
+ statusAttribute: string,
private viewConfig: BoardViewData,
private saveConfig: (newConfig: BoardViewData) => void,
private setBranchIdToEdit: (branchId: string | undefined) => void
- ) {};
+ ) {
+ this.isRelationMode = statusAttribute.startsWith("~");
+
+ if (statusAttribute.startsWith("~") || statusAttribute.startsWith("#")) {
+ statusAttribute = statusAttribute.substring(1);
+ }
+ this.statusAttribute = statusAttribute;
+ };
async createNewItem(column: string, title: string) {
try {
@@ -42,7 +52,11 @@ export default class BoardApi {
}
async changeColumn(noteId: string, newColumn: string) {
- await attributes.setLabel(noteId, this.statusAttribute, newColumn);
+ if (this.isRelationMode) {
+ await attributes.setRelation(noteId, this.statusAttribute, newColumn);
+ } else {
+ await attributes.setLabel(noteId, this.statusAttribute, newColumn);
+ }
}
async addNewColumn(columnName: string) {
From 0f000ccd933f0a31e9669c7991e503f8705280a4 Mon Sep 17 00:00:00 2001
From: Elian Doran
Date: Sat, 15 Nov 2025 10:42:32 +0200
Subject: [PATCH 05/21] fix(board/relation): not reacting to status change
---
apps/client/src/widgets/collections/board/index.tsx | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/apps/client/src/widgets/collections/board/index.tsx b/apps/client/src/widgets/collections/board/index.tsx
index 63e3d58cc..effcb7fda 100644
--- a/apps/client/src/widgets/collections/board/index.tsx
+++ b/apps/client/src/widgets/collections/board/index.tsx
@@ -42,7 +42,7 @@ interface BoardViewContextData {
export const BoardViewContext = createContext(undefined);
export default function BoardView({ note: parentNote, noteIds, viewConfig, saveConfig }: ViewModeProps) {
- const [ statusAttribute ] = useNoteLabelWithDefault(parentNote, "board:groupBy", "status");
+ const [ statusAttributeWithPrefix ] = useNoteLabelWithDefault(parentNote, "board:groupBy", "status");
const [ includeArchived ] = useNoteLabelBoolean(parentNote, "includeArchived");
const [ byColumn, setByColumn ] = useState();
const [ columns, setColumns ] = useState();
@@ -56,8 +56,8 @@ export default function BoardView({ note: parentNote, noteIds, viewConfig, saveC
const [ branchIdToEdit, setBranchIdToEdit ] = useState();
const [ columnNameToEdit, setColumnNameToEdit ] = useState();
const api = useMemo(() => {
- return new Api(byColumn, columns ?? [], parentNote, statusAttribute, viewConfig ?? {}, saveConfig, setBranchIdToEdit );
- }, [ byColumn, columns, parentNote, statusAttribute, viewConfig, saveConfig, setBranchIdToEdit ]);
+ return new Api(byColumn, columns ?? [], parentNote, statusAttributeWithPrefix, viewConfig ?? {}, saveConfig, setBranchIdToEdit );
+ }, [ byColumn, columns, parentNote, statusAttributeWithPrefix, viewConfig, saveConfig, setBranchIdToEdit ]);
const boardViewContext = useMemo(() => ({
api,
parentNote,
@@ -79,7 +79,7 @@ export default function BoardView({ note: parentNote, noteIds, viewConfig, saveC
]);
function refresh() {
- getBoardData(parentNote, statusAttribute, viewConfig ?? {}, includeArchived).then(({ byColumn, newPersistedData, isInRelationMode }) => {
+ getBoardData(parentNote, statusAttributeWithPrefix, viewConfig ?? {}, includeArchived).then(({ byColumn, newPersistedData, isInRelationMode }) => {
setByColumn(byColumn);
setIsRelationMode(isInRelationMode);
@@ -112,7 +112,7 @@ export default function BoardView({ note: parentNote, noteIds, viewConfig, saveC
// Check if any changes affect our board
const hasRelevantChanges =
// React to changes in status attribute for notes in this board
- loadResults.getAttributeRows().some(attr => attr.name === statusAttribute && noteIds.includes(attr.noteId!)) ||
+ loadResults.getAttributeRows().some(attr => attr.name === api.statusAttribute && noteIds.includes(attr.noteId!)) ||
// React to changes in note title
loadResults.getNoteIds().some(noteId => noteIds.includes(noteId)) ||
// React to changes in branches for subchildren (e.g., moved, added, or removed notes)
From d1e80815d56533c01e2aec0f5520ad04b1fb5ce5 Mon Sep 17 00:00:00 2001
From: Elian Doran
Date: Sat, 15 Nov 2025 10:50:55 +0200
Subject: [PATCH 06/21] feat(board/relation): add support for more APIs
---
apps/client/src/services/attributes.ts | 18 ++++++++++++
.../src/widgets/collections/board/api.ts | 28 +++++++++----------
2 files changed, 32 insertions(+), 14 deletions(-)
diff --git a/apps/client/src/services/attributes.ts b/apps/client/src/services/attributes.ts
index 13011b7a6..7fad04947 100644
--- a/apps/client/src/services/attributes.ts
+++ b/apps/client/src/services/attributes.ts
@@ -60,6 +60,23 @@ function removeOwnedLabelByName(note: FNote, labelName: string) {
return false;
}
+/**
+ * Removes a relation identified by its name from the given note, if it exists. Note that the relation must be owned, i.e.
+ * it will not remove inherited attributes.
+ *
+ * @param note the note from which to remove the relation.
+ * @param relationName the name of the relation to remove.
+ * @returns `true` if an attribute was identified and removed, `false` otherwise.
+ */
+function removeOwnedRelationByName(note: FNote, relationName: string) {
+ const label = note.getOwnedRelation(relationName);
+ if (label) {
+ removeAttributeById(note.noteId, label.attributeId);
+ return true;
+ }
+ return false;
+}
+
/**
* Sets the attribute of the given note to the provided value if its truthy, or removes the attribute if the value is falsy.
* For an attribute with an empty value, pass an empty string instead.
@@ -129,5 +146,6 @@ export default {
setAttribute,
removeAttributeById,
removeOwnedLabelByName,
+ removeOwnedRelationByName,
isAffecting
};
diff --git a/apps/client/src/widgets/collections/board/api.ts b/apps/client/src/widgets/collections/board/api.ts
index 6543bafd9..52f7a6d4a 100644
--- a/apps/client/src/widgets/collections/board/api.ts
+++ b/apps/client/src/widgets/collections/board/api.ts
@@ -1,3 +1,4 @@
+import { BulkAction } from "@triliumnext/commons";
import { BoardViewData } from ".";
import appContext from "../../../components/app_context";
import FNote from "../../../entities/fnote";
@@ -83,13 +84,11 @@ export default class BoardApi {
async removeColumn(column: string) {
// Remove the value from the notes.
const noteIds = this.byColumn?.get(column)?.map(item => item.note.noteId) || [];
- await executeBulkActions(noteIds, [
- {
- name: "deleteLabel",
- labelName: this.statusAttribute
- }
- ]);
+ const action: BulkAction = this.isRelationMode
+ ? { name: "deleteRelation", relationName: this.statusAttribute }
+ : { name: "deleteLabel", labelName: this.statusAttribute }
+ await executeBulkActions(noteIds, [ action ]);
this.viewConfig.columns = (this.viewConfig.columns ?? []).filter(col => col.value !== column);
this.saveConfig(this.viewConfig);
}
@@ -98,13 +97,10 @@ export default class BoardApi {
const noteIds = this.byColumn?.get(oldValue)?.map(item => item.note.noteId) || [];
// Change the value in the notes.
- await executeBulkActions(noteIds, [
- {
- name: "updateLabelValue",
- labelName: this.statusAttribute,
- labelValue: newValue
- }
- ]);
+ const action: BulkAction = this.isRelationMode
+ ? { name: "updateRelationTarget", relationName: this.statusAttribute, targetNoteId: newValue }
+ : { name: "updateLabelValue", labelName: this.statusAttribute, labelValue: newValue }
+ await executeBulkActions(noteIds, [ action ]);
// Rename the column in the persisted data.
for (const column of this.viewConfig.columns || []) {
@@ -181,7 +177,11 @@ export default class BoardApi {
removeFromBoard(noteId: string) {
const note = froca.getNoteFromCache(noteId);
if (!note) return;
- return attributes.removeOwnedLabelByName(note, this.statusAttribute);
+ if (this.isRelationMode) {
+ return attributes.removeOwnedRelationByName(note, this.statusAttribute);
+ } else {
+ return attributes.removeOwnedLabelByName(note, this.statusAttribute);
+ }
}
async moveWithinBoard(noteId: string, sourceBranchId: string, sourceIndex: number, targetIndex: number, sourceColumn: string, targetColumn: string) {
From 092a84693fbaa1a6c8be9466d6febd9120eeae4e Mon Sep 17 00:00:00 2001
From: Elian Doran
Date: Sat, 15 Nov 2025 11:11:42 +0200
Subject: [PATCH 07/21] feat(board/relation): basic support for editing
relations in columns
---
.../src/widgets/collections/board/card.tsx | 2 +-
.../src/widgets/collections/board/column.tsx | 3 +-
.../src/widgets/collections/board/index.tsx | 87 ++++++++++++-------
.../src/widgets/react/NoteAutocomplete.tsx | 16 +++-
4 files changed, 69 insertions(+), 39 deletions(-)
diff --git a/apps/client/src/widgets/collections/board/card.tsx b/apps/client/src/widgets/collections/board/card.tsx
index fc81e9f0e..0ee92a11d 100644
--- a/apps/client/src/widgets/collections/board/card.tsx
+++ b/apps/client/src/widgets/collections/board/card.tsx
@@ -119,7 +119,7 @@ export default function Card({
setTitle(newTitle);
}}
dismiss={() => api.dismissEditingTitle()}
- multiline
+ mode="multiline"
/>
)}
diff --git a/apps/client/src/widgets/collections/board/column.tsx b/apps/client/src/widgets/collections/board/column.tsx
index 791eb585e..f014b67bf 100644
--- a/apps/client/src/widgets/collections/board/column.tsx
+++ b/apps/client/src/widgets/collections/board/column.tsx
@@ -124,6 +124,7 @@ export default function Column({
currentValue={column}
save={newTitle => api.renameColumn(column, newTitle)}
dismiss={() => setColumnNameToEdit?.(undefined)}
+ mode={isInRelationMode ? "relation" : "normal"}
/>
)}
@@ -187,7 +188,7 @@ function AddNewItem({ column, api }: { column: string, api: BoardApi }) {
placeholder={t("board_view.new-item-placeholder")}
save={(title) => api.createNewItem(column, title)}
dismiss={() => setIsCreatingNewItem(false)}
- multiline isNewItem
+ mode="multiline" isNewItem
/>
)}
diff --git a/apps/client/src/widgets/collections/board/index.tsx b/apps/client/src/widgets/collections/board/index.tsx
index effcb7fda..97b9632bf 100644
--- a/apps/client/src/widgets/collections/board/index.tsx
+++ b/apps/client/src/widgets/collections/board/index.tsx
@@ -13,6 +13,7 @@ import Column from "./column";
import BoardApi from "./api";
import FormTextArea from "../../react/FormTextArea";
import FNote from "../../../entities/fnote";
+import NoteAutocomplete from "../../react/NoteAutocomplete";
export interface BoardViewData {
columns?: BoardColumnData[];
@@ -188,14 +189,14 @@ export default function BoardView({ note: parentNote, noteIds, viewConfig, saveC
)}
-
+
)
}
-function AddNewColumn({ api }: { api: BoardApi }) {
+function AddNewColumn({ api, isInRelationMode }: { api: BoardApi, isInRelationMode: boolean }) {
const [ isCreatingNewColumn, setIsCreatingNewColumn ] = useState(false);
const addColumnCallback = useCallback(() => {
@@ -215,19 +216,20 @@ function AddNewColumn({ api }: { api: BoardApi }) {
save={(columnName) => api.addNewColumn(columnName)}
dismiss={() => setIsCreatingNewColumn(false)}
isNewItem
+ mode={isInRelationMode ? "relation" : "normal"}
/>
)}
)
}
-export function TitleEditor({ currentValue, placeholder, save, dismiss, multiline, isNewItem }: {
+export function TitleEditor({ currentValue, placeholder, save, dismiss, mode, isNewItem }: {
currentValue?: string;
placeholder?: string;
save: (newValue: string) => void;
dismiss: () => void;
- multiline?: boolean;
isNewItem?: boolean;
+ mode?: "normal" | "multiline" | "relation";
}) {
const inputRef = useRef(null);
const focusElRef = useRef(null);
@@ -240,8 +242,6 @@ export function TitleEditor({ currentValue, placeholder, save, dismiss, multilin
inputRef.current?.select();
}, [ inputRef ]);
- const Element = multiline ? FormTextArea : FormTextBox;
-
useEffect(() => {
if (dismissOnNextRefreshRef.current) {
dismiss();
@@ -249,31 +249,52 @@ export function TitleEditor({ currentValue, placeholder, save, dismiss, multilin
}
});
- return (
- ) => {
- if (e.key === "Enter" || e.key === "Escape") {
- e.preventDefault();
- e.stopPropagation();
- shouldDismiss.current = (e.key === "Escape");
- if (focusElRef.current instanceof HTMLElement) {
- focusElRef.current.focus();
- }
- }
- }}
- onBlur={(newValue) => {
- if (!shouldDismiss.current && newValue.trim() && (newValue !== currentValue || isNewItem)) {
- save(newValue);
- dismissOnNextRefreshRef.current = true;
- } else {
- dismiss();
- }
- }}
- />
- );
+ const onKeyDown = (e: TargetedKeyboardEvent | KeyboardEvent) => {
+ if (e.key === "Enter" || e.key === "Escape") {
+ e.preventDefault();
+ e.stopPropagation();
+ shouldDismiss.current = (e.key === "Escape");
+ if (focusElRef.current instanceof HTMLElement) {
+ focusElRef.current.focus();
+ }
+ }
+ };
+
+ const onBlur = (newValue) => {
+ if (!shouldDismiss.current && newValue.trim() && (newValue !== currentValue || isNewItem)) {
+ save(newValue);
+ dismissOnNextRefreshRef.current = true;
+ } else {
+ dismiss();
+ }
+ };
+
+ if (mode !== "relation") {
+ const Element = mode === "multiline" ? FormTextArea : FormTextBox;
+
+ return (
+
+ );
+ } else {
+ return (
+
+ )
+ }
}
diff --git a/apps/client/src/widgets/react/NoteAutocomplete.tsx b/apps/client/src/widgets/react/NoteAutocomplete.tsx
index 223dbb5d4..198e69695 100644
--- a/apps/client/src/widgets/react/NoteAutocomplete.tsx
+++ b/apps/client/src/widgets/react/NoteAutocomplete.tsx
@@ -5,7 +5,7 @@ import type { RefObject } from "preact";
import type { CSSProperties } from "preact/compat";
import { useSyncedRef } from "./hooks";
-interface NoteAutocompleteProps {
+interface NoteAutocompleteProps {
id?: string;
inputRef?: RefObject;
text?: string;
@@ -15,13 +15,15 @@ interface NoteAutocompleteProps {
opts?: Omit;
onChange?: (suggestion: Suggestion | null) => void;
onTextChange?: (text: string) => void;
+ onKeyDown?: (e: KeyboardEvent) => void;
+ onBlur?: (newValue: string) => void;
noteIdChanged?: (noteId: string) => void;
noteId?: string;
}
-export default function NoteAutocomplete({ id, inputRef: externalInputRef, text, placeholder, onChange, onTextChange, container, containerStyle, opts, noteId, noteIdChanged }: NoteAutocompleteProps) {
+export default function NoteAutocomplete({ id, inputRef: externalInputRef, text, placeholder, onChange, onTextChange, container, containerStyle, opts, noteId, noteIdChanged, onKeyDown, onBlur }: NoteAutocompleteProps) {
const ref = useSyncedRef(externalInputRef);
-
+
useEffect(() => {
if (!ref.current) return;
const $autoComplete = $(ref.current);
@@ -57,6 +59,12 @@ export default function NoteAutocomplete({ id, inputRef: externalInputRef, text,
if (onTextChange) {
$autoComplete.on("input", () => onTextChange($autoComplete[0].value));
}
+ if (onKeyDown) {
+ $autoComplete.on("keydown", (e) => e.originalEvent && onKeyDown(e.originalEvent));
+ }
+ if (onBlur) {
+ $autoComplete.on("blur", () => onBlur($autoComplete.getSelectedNoteId() ?? ""));
+ }
}, [opts, container?.current]);
useEffect(() => {
@@ -81,4 +89,4 @@ export default function NoteAutocomplete({ id, inputRef: externalInputRef, text,
placeholder={placeholder ?? t("add_link.search_note")} />
);
-}
\ No newline at end of file
+}
From 0c616fecdf929ed67b2e83de8c58f70b0bfb04d4 Mon Sep 17 00:00:00 2001
From: Elian Doran
Date: Sat, 15 Nov 2025 11:21:23 +0200
Subject: [PATCH 08/21] feat(board/relation): improve relation editing
experience
---
apps/client/src/widgets/collections/board/index.tsx | 12 ++++++++++--
1 file changed, 10 insertions(+), 2 deletions(-)
diff --git a/apps/client/src/widgets/collections/board/index.tsx b/apps/client/src/widgets/collections/board/index.tsx
index 97b9632bf..a2a0d9001 100644
--- a/apps/client/src/widgets/collections/board/index.tsx
+++ b/apps/client/src/widgets/collections/board/index.tsx
@@ -292,8 +292,16 @@ export function TitleEditor({ currentValue, placeholder, save, dismiss, mode, is
hideAllButtons: true,
allowCreatingNotes: true
}}
- onKeyDown={onKeyDown}
- onBlur={onBlur}
+ onKeyDown={(e) => {
+ if (e.key === "Escape") {
+ dismiss();
+ }
+ }}
+ onBlur={() => dismiss()}
+ noteIdChanged={(newValue) => {
+ save(newValue);
+ dismiss();
+ }}
/>
)
}
From f820c6f23be16c7013b6f63fb3b14b3c208cc92a Mon Sep 17 00:00:00 2001
From: Elian Doran
Date: Sat, 15 Nov 2025 11:28:35 +0200
Subject: [PATCH 09/21] feat(board/relation): react to column title changes
---
apps/client/src/services/load_results.ts | 12 +++++++++++-
apps/client/src/widgets/react/NoteLink.tsx | 16 ++++++++++++++--
2 files changed, 25 insertions(+), 3 deletions(-)
diff --git a/apps/client/src/services/load_results.ts b/apps/client/src/services/load_results.ts
index 4a4875725..3e00e4a0f 100644
--- a/apps/client/src/services/load_results.ts
+++ b/apps/client/src/services/load_results.ts
@@ -1,11 +1,21 @@
-import type { AttachmentRow, EtapiTokenRow, OptionNames } from "@triliumnext/commons";
+import type { AttachmentRow, EtapiTokenRow, NoteType, OptionNames } from "@triliumnext/commons";
import type { AttributeType } from "../entities/fattribute.js";
import type { EntityChange } from "../server_types.js";
// TODO: Deduplicate with server.
interface NoteRow {
+ blobId: string;
+ dateCreated: string;
+ dateMOdified: string;
isDeleted?: boolean;
+ isProtected?: boolean;
+ mime: string;
+ noteId: string;
+ title: string;
+ type: NoteType;
+ utcDateCreated: string;
+ utcDateModified: string;
}
// TODO: Deduplicate with BranchRow from `rows.ts`/
diff --git a/apps/client/src/widgets/react/NoteLink.tsx b/apps/client/src/widgets/react/NoteLink.tsx
index 9e6ddc905..758122ed0 100644
--- a/apps/client/src/widgets/react/NoteLink.tsx
+++ b/apps/client/src/widgets/react/NoteLink.tsx
@@ -1,6 +1,6 @@
import { useEffect, useRef, useState } from "preact/hooks";
import link, { ViewScope } from "../../services/link";
-import { useImperativeSearchHighlighlighting } from "./hooks";
+import { useImperativeSearchHighlighlighting, useTriliumEvent } from "./hooks";
interface NoteLinkOpts {
className?: string;
@@ -19,9 +19,11 @@ interface NoteLinkOpts {
export default function NoteLink({ className, notePath, showNotePath, showNoteIcon, style, noPreview, noTnLink, highlightedTokens, title, viewScope, noContextMenu }: NoteLinkOpts) {
const stringifiedNotePath = Array.isArray(notePath) ? notePath.join("/") : notePath;
+ const noteId = stringifiedNotePath.split("/").at(-1);
const ref = useRef(null);
const [ jqueryEl, setJqueryEl ] = useState>();
const highlightSearch = useImperativeSearchHighlighlighting(highlightedTokens);
+ const [ noteTitle, setNoteTitle ] = useState();
useEffect(() => {
link.createLink(stringifiedNotePath, {
@@ -30,7 +32,7 @@ export default function NoteLink({ className, notePath, showNotePath, showNoteIc
showNoteIcon,
viewScope
}).then(setJqueryEl);
- }, [ stringifiedNotePath, showNotePath, title, viewScope ]);
+ }, [ stringifiedNotePath, showNotePath, title, viewScope, noteTitle ]);
useEffect(() => {
if (!ref.current || !jqueryEl) return;
@@ -38,6 +40,16 @@ export default function NoteLink({ className, notePath, showNotePath, showNoteIc
highlightSearch(ref.current);
}, [ jqueryEl, highlightedTokens ]);
+ useTriliumEvent("entitiesReloaded", ({ loadResults }) => {
+ // React to note title changes, but only if the title is not overwritten.
+ if (!title && noteId) {
+ const entityRow = loadResults.getEntityRow("notes", noteId);
+ if (entityRow) {
+ setNoteTitle(entityRow.title);
+ }
+ }
+ });
+
if (style) {
jqueryEl?.css(style);
}
From df8da0fd4fdf32b654f309ba1288039de6308b80 Mon Sep 17 00:00:00 2001
From: Elian Doran
Date: Sat, 15 Nov 2025 11:34:01 +0200
Subject: [PATCH 10/21] feat(board): warn when column already exists
---
apps/client/src/services/toast.ts | 4 ++--
apps/client/src/translations/en/translation.json | 3 ++-
apps/client/src/widgets/collections/board/api.ts | 8 ++++----
apps/client/src/widgets/collections/board/index.tsx | 8 +++++++-
4 files changed, 15 insertions(+), 8 deletions(-)
diff --git a/apps/client/src/services/toast.ts b/apps/client/src/services/toast.ts
index cc67fa225..5325a06bc 100644
--- a/apps/client/src/services/toast.ts
+++ b/apps/client/src/services/toast.ts
@@ -77,11 +77,11 @@ function closePersistent(id: string) {
$(`#toast-${id}`).remove();
}
-function showMessage(message: string, delay = 2000) {
+function showMessage(message: string, delay = 2000, icon = "check") {
console.debug(utils.now(), "message:", message);
toast({
- icon: "check",
+ icon,
message: message,
autohide: true,
delay
diff --git a/apps/client/src/translations/en/translation.json b/apps/client/src/translations/en/translation.json
index ef9605d5e..54025d690 100644
--- a/apps/client/src/translations/en/translation.json
+++ b/apps/client/src/translations/en/translation.json
@@ -2035,7 +2035,8 @@
"add-column": "Add Column",
"add-column-placeholder": "Enter column name...",
"edit-note-title": "Click to edit note title",
- "edit-column-title": "Click to edit column title"
+ "edit-column-title": "Click to edit column title",
+ "column-already-exists": "This column already exists on the board."
},
"presentation_view": {
"edit-slide": "Edit this slide",
diff --git a/apps/client/src/widgets/collections/board/api.ts b/apps/client/src/widgets/collections/board/api.ts
index 52f7a6d4a..af88f935e 100644
--- a/apps/client/src/widgets/collections/board/api.ts
+++ b/apps/client/src/widgets/collections/board/api.ts
@@ -75,10 +75,10 @@ export default class BoardApi {
// Add the new column to persisted data if it doesn't exist
const existingColumn = this.viewConfig.columns.find(col => col.value === columnName);
- if (!existingColumn) {
- this.viewConfig.columns.push({ value: columnName });
- this.saveConfig(this.viewConfig);
- }
+ if (existingColumn) return false;
+ this.viewConfig.columns.push({ value: columnName });
+ this.saveConfig(this.viewConfig);
+ return true;
}
async removeColumn(column: string) {
diff --git a/apps/client/src/widgets/collections/board/index.tsx b/apps/client/src/widgets/collections/board/index.tsx
index a2a0d9001..519e1103c 100644
--- a/apps/client/src/widgets/collections/board/index.tsx
+++ b/apps/client/src/widgets/collections/board/index.tsx
@@ -14,6 +14,7 @@ import BoardApi from "./api";
import FormTextArea from "../../react/FormTextArea";
import FNote from "../../../entities/fnote";
import NoteAutocomplete from "../../react/NoteAutocomplete";
+import toast from "../../../services/toast";
export interface BoardViewData {
columns?: BoardColumnData[];
@@ -213,7 +214,12 @@ function AddNewColumn({ api, isInRelationMode }: { api: BoardApi, isInRelationMo
: (
api.addNewColumn(columnName)}
+ save={async (columnName) => {
+ const created = await api.addNewColumn(columnName);
+ if (!created) {
+ toast.showMessage(t("board_view.column-already-exists"), undefined, "bx bx-duplicate");
+ }
+ }}
dismiss={() => setIsCreatingNewColumn(false)}
isNewItem
mode={isInRelationMode ? "relation" : "normal"}
From 5e35aa8079c225eb73a15a0dfb002f93a4ef71fc Mon Sep 17 00:00:00 2001
From: Elian Doran
Date: Sat, 15 Nov 2025 12:24:18 +0200
Subject: [PATCH 11/21] fix(board): columns leaking between notes
---
apps/client/src/widgets/collections/NoteList.tsx | 16 +++++++++++-----
1 file changed, 11 insertions(+), 5 deletions(-)
diff --git a/apps/client/src/widgets/collections/NoteList.tsx b/apps/client/src/widgets/collections/NoteList.tsx
index 421f45cc4..0a79b5720 100644
--- a/apps/client/src/widgets/collections/NoteList.tsx
+++ b/apps/client/src/widgets/collections/NoteList.tsx
@@ -77,8 +77,8 @@ export function CustomNoteList({ note, isEnabled: shouldEnable
props = {
note, noteIds, notePath,
highlightedTokens,
- viewConfig: viewModeConfig[0],
- saveConfig: viewModeConfig[1],
+ viewConfig: viewModeConfig.config,
+ saveConfig: viewModeConfig.storeFn,
onReady: onReady ?? (() => {}),
...restProps
}
@@ -192,7 +192,11 @@ export function useNoteIds(note: FNote | null | undefined, viewType: ViewTypeOpt
}
export function useViewModeConfig(note: FNote | null | undefined, viewType: ViewTypeOptions | undefined) {
- const [ viewConfig, setViewConfig ] = useState<[T | undefined, (data: T) => void]>();
+ const [ viewConfig, setViewConfig ] = useState<{
+ config: T | undefined;
+ storeFn: (data: T) => void;
+ note: FNote;
+ }>();
useEffect(() => {
if (!note || !viewType) return;
@@ -200,12 +204,14 @@ export function useViewModeConfig(note: FNote | null | undefin
const viewStorage = new ViewModeStorage(note, viewType);
viewStorage.restore().then(config => {
const storeFn = (config: T) => {
- setViewConfig([ config, storeFn ]);
+ setViewConfig({ note, config, storeFn });
viewStorage.store(config);
};
- setViewConfig([ config, storeFn ]);
+ setViewConfig({ note, config, storeFn });
});
}, [ note, viewType ]);
+ // Only expose config for the current note, avoid leaking notes when switching between them.
+ if (viewConfig?.note !== note) return undefined;
return viewConfig;
}
From a8992d08b38f70f987a006c1beddd3addef46f82 Mon Sep 17 00:00:00 2001
From: Elian Doran
Date: Sat, 15 Nov 2025 12:35:10 +0200
Subject: [PATCH 12/21] fix(board): escape not working in "Add column"
---
apps/client/src/widgets/collections/board/index.tsx | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/apps/client/src/widgets/collections/board/index.tsx b/apps/client/src/widgets/collections/board/index.tsx
index 519e1103c..811ae5d18 100644
--- a/apps/client/src/widgets/collections/board/index.tsx
+++ b/apps/client/src/widgets/collections/board/index.tsx
@@ -243,7 +243,7 @@ export function TitleEditor({ currentValue, placeholder, save, dismiss, mode, is
const shouldDismiss = useRef(false);
useEffect(() => {
- focusElRef.current = document.activeElement;
+ focusElRef.current = document.activeElement !== document.body ? document.activeElement : null;
inputRef.current?.focus();
inputRef.current?.select();
}, [ inputRef ]);
@@ -259,9 +259,11 @@ export function TitleEditor({ currentValue, placeholder, save, dismiss, mode, is
if (e.key === "Enter" || e.key === "Escape") {
e.preventDefault();
e.stopPropagation();
- shouldDismiss.current = (e.key === "Escape");
if (focusElRef.current instanceof HTMLElement) {
+ shouldDismiss.current = (e.key === "Escape");
focusElRef.current.focus();
+ } else {
+ dismiss();
}
}
};
From 8d3892757a3e1faaa8e9f162e7e06cec33b6e6ef Mon Sep 17 00:00:00 2001
From: Elian Doran
Date: Sat, 15 Nov 2025 12:37:25 +0200
Subject: [PATCH 13/21] fix(board): not refreshing on status attribute change
---
apps/client/src/widgets/collections/board/index.tsx | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/apps/client/src/widgets/collections/board/index.tsx b/apps/client/src/widgets/collections/board/index.tsx
index 811ae5d18..e34ef1225 100644
--- a/apps/client/src/widgets/collections/board/index.tsx
+++ b/apps/client/src/widgets/collections/board/index.tsx
@@ -98,7 +98,7 @@ export default function BoardView({ note: parentNote, noteIds, viewConfig, saveC
});
}
- useEffect(refresh, [ parentNote, noteIds, viewConfig ]);
+ useEffect(refresh, [ parentNote, noteIds, viewConfig, statusAttributeWithPrefix ]);
const handleColumnDrop = useCallback((fromIndex: number, toIndex: number) => {
const newColumns = api.reorderColumn(fromIndex, toIndex);
From 254d3a1c8e73bb5b2cad2af42f391a63249c52b0 Mon Sep 17 00:00:00 2001
From: Elian Doran
Date: Sat, 15 Nov 2025 12:42:49 +0200
Subject: [PATCH 14/21] fix(board): not reacting to external title changes
---
apps/client/src/widgets/collections/board/card.tsx | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/apps/client/src/widgets/collections/board/card.tsx b/apps/client/src/widgets/collections/board/card.tsx
index 0ee92a11d..b67a00408 100644
--- a/apps/client/src/widgets/collections/board/card.tsx
+++ b/apps/client/src/widgets/collections/board/card.tsx
@@ -7,6 +7,7 @@ import { ContextMenuEvent } from "../../../menus/context_menu";
import { openNoteContextMenu } from "./context_menu";
import { t } from "../../../services/i18n";
import UserAttributesDisplay from "../../attribute_widgets/UserAttributesList";
+import { useTriliumEvent } from "../../react/hooks";
export const CARD_CLIPBOARD_TYPE = "trilium/board-card";
@@ -40,6 +41,13 @@ export default function Card({
const [ isVisible, setVisible ] = useState(true);
const [ title, setTitle ] = useState(note.title);
+ useTriliumEvent("entitiesReloaded", ({ loadResults }) => {
+ const row = loadResults.getEntityRow("notes", note.noteId);
+ if (row) {
+ setTitle(row.title);
+ }
+ });
+
const handleDragStart = useCallback((e: DragEvent) => {
e.dataTransfer!.effectAllowed = 'move';
const data: CardDragData = { noteId: note.noteId, branchId: branch.branchId, fromColumn: column, index };
From b7703fc4dfa4a2c32eefee009c0d279df9ecbd55 Mon Sep 17 00:00:00 2001
From: Elian Doran
Date: Sat, 15 Nov 2025 12:52:20 +0200
Subject: [PATCH 15/21] style(next): use main background color on mobile
---
apps/client/src/stylesheets/theme-next/shell.css | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/apps/client/src/stylesheets/theme-next/shell.css b/apps/client/src/stylesheets/theme-next/shell.css
index e84c4544d..2ae36db2a 100644
--- a/apps/client/src/stylesheets/theme-next/shell.css
+++ b/apps/client/src/stylesheets/theme-next/shell.css
@@ -16,6 +16,10 @@
background-color: var(--root-background);
}
+body.mobile #root-widget {
+ background-color: var(--main-background-color);
+}
+
body {
--native-titlebar-darwin-x-offset: 10;
--native-titlebar-darwin-y-offset: 12 !important;
From 63cc5b21b4fec6372322c717aaf2ed73e2d1848f Mon Sep 17 00:00:00 2001
From: Elian Doran
Date: Sat, 15 Nov 2025 13:01:30 +0200
Subject: [PATCH 16/21] feat(board): scroll snapping on mobile
---
apps/client/src/widgets/collections/board/index.css | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/apps/client/src/widgets/collections/board/index.css b/apps/client/src/widgets/collections/board/index.css
index 39ae024ed..aaf694686 100644
--- a/apps/client/src/widgets/collections/board/index.css
+++ b/apps/client/src/widgets/collections/board/index.css
@@ -9,6 +9,12 @@
--card-padding: 0.6em;
}
+body.mobile .board-view {
+ scroll-snap-type: x mandatory;
+ -webkit-overflow-scrolling: touch;
+ scroll-behavior: smooth;
+}
+
.board-view-container {
height: 100%;
display: flex;
@@ -31,6 +37,12 @@
flex-direction: column;
}
+body.mobile .board-view-container .board-column {
+ width: 75vw;
+ max-width: 300px;
+ scroll-snap-align: center;
+}
+
.board-view-container .board-column.drag-over {
border-color: var(--main-text-color);
background-color: var(--hover-item-background-color);
From d2184682e563ba1f3bb67158c603b0c8acfc3690 Mon Sep 17 00:00:00 2001
From: Elian Doran
Date: Sat, 15 Nov 2025 13:37:24 +0200
Subject: [PATCH 17/21] docs(user): grouping by relation in board
---
.../Collections/1_Kanban Board_image.png | Bin 0 -> 18644 bytes
.../User Guide/Collections/Kanban Board.html | 116 +++++++++++-------
.../Collections/Kanban Board_image.png | Bin 18644 -> 15518 bytes
docs/Developer Guide/!!!meta.json | 32 ++---
.../Developer Guide/Documentation.md | 2 +-
docs/Release Notes/!!!meta.json | 2 +-
docs/User Guide/!!!meta.json | 33 ++++-
.../Collections/1_Kanban Board_image.png | Bin 0 -> 18644 bytes
.../User Guide/Collections/Kanban Board.md | 40 ++++--
.../Collections/Kanban Board_image.png | Bin 18644 -> 15518 bytes
10 files changed, 155 insertions(+), 70 deletions(-)
create mode 100644 apps/server/src/assets/doc_notes/en/User Guide/User Guide/Collections/1_Kanban Board_image.png
create mode 100644 docs/User Guide/User Guide/Collections/1_Kanban Board_image.png
diff --git a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Collections/1_Kanban Board_image.png b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Collections/1_Kanban Board_image.png
new file mode 100644
index 0000000000000000000000000000000000000000..be4b027d2b9eee4fedd1ac6eadbea6e38faa08b2
GIT binary patch
literal 18644
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
literal 0
HcmV?d00001
diff --git a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Collections/Kanban Board.html b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Collections/Kanban Board.html
index e4a92ba49..86dc4e408 100644
--- a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Collections/Kanban Board.html
+++ b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Collections/Kanban Board.html
@@ -1,5 +1,5 @@
-
The Board view presents sub-notes in columns for a Kanban-like experience.
@@ -15,83 +15,115 @@
Interaction
Working with columns
- Create a new column by pressing Add Column near the last column.
+ Create a new column by pressing Add Column near the last column.
- Once pressed, a text box will be displayed to set the name of the column.
+ Once pressed, a text box will be displayed to set the name of the column.
Press Enter to confirm, or Escape to dismiss.
- To reorder a column, simply hold the mouse over the title and drag it
+ To reorder a column, simply hold the mouse over the title and drag it
to the desired position.
- To delete a column, right click on its title and select Delete column .
- To rename a column, click on the note title.
+ To delete a column, right click on its title and select Delete column .
+ To rename a column, click on the note title.
- Press Enter to confirm.
- Upon renaming a column, the corresponding status attribute of all its
+ Press Enter to confirm.
+ Upon renaming a column, the corresponding status attribute of all its
notes will be changed in bulk.
-
- If there are many columns, use the mouse wheel to scroll.
+
+ If there are many columns, use the mouse wheel to scroll.
Working with notes
- Create a new note in any column by pressing New item
+ Create a new note in any column by pressing New item
- Enter the name of the note and press Enter or click away. To
+ Enter the name of the note and press Enter or click away. To
dismiss the creation of a new note, simply press Escape or leave
the name empty.
- Once created, the new note will have an attribute (status label
+ Once created, the new note will have an attribute (status label
by default) set to the name of the column.
- To open the note, simply click on it.
- To change the title of the note directly from the board, hover the mouse
+ To open the note, simply click on it.
+ To change the title of the note directly from the board, hover the mouse
over its card and press the edit button on the right.
- To change the state of a note, simply drag a note from one column to the
+ To change the state of a note, simply drag a note from one column to the
other to change its state.
- The order of the notes in each column corresponds to their position in
+ The order of the notes in each column corresponds to their position in
the tree.
- It's possible to reorder notes simply by dragging them to the desired
+ It's possible to reorder notes simply by dragging them to the desired
position within the same columns.
- It's also possible to drag notes across columns, at the desired position.
+ It's also possible to drag notes across columns, at the desired position.
- For more options, right click on a note to display a context menu with
+ For more options, right click on a note to display a context menu with
the following options:
- Open the note in a new tab/split/window or quick edit.
- Move the note to any column.
- Insert a new note above/below the current one.
- Archive/unarchive the current note.
- Delete the current note.
+ Open the note in a new tab/split/window or quick edit.
+ Move the note to any column.
+ Insert a new note above/below the current one.
+ Archive/unarchive the current note.
+ Delete the current note.
- If there are many notes within the column, move the mouse over the column
+ If there are many notes within the column, move the mouse over the column
and use the mouse wheel to scroll.
-Keyboard interaction
+Keyboard interaction
The board view has mild support for keyboard-based navigation:
- Use Tab and Shift +Tab to navigate between
+ Use Tab and Shift +Tab to navigate between
column titles, notes and the “New item” button for each of the columns,
in sequential order.
- To rename a column or a note, press F2 while it is focused.
- To open a specific note or create a new item, press Enter while
+ To rename a column or a note, press F2 while it is focused.
+ To open a specific note or create a new item, press Enter while
it is focused.
- To dismiss a rename of a note or a column, press Escape .
+ To dismiss a rename of a note or a column, press Escape .
Configuration
-Grouping by another attribute
+Grouping by another label
By default, the label used to group the notes is #status.
It is possible to use a different label if needed by defining a label named #board:groupBy with
- the value being the attribute to use (without # attribute prefix).
-
- It's currently not possible to set a relation as the grouping criteria.
- There are plans to add support for it.
-
- Limitations
-
- It is not possible yet to use group by a relation, only by label.
-
\ No newline at end of file
+ the value being the attribute to use (with or without # attribute
+ prefix).
+Grouping by relations
+
+
+
+A more advanced use-case is grouping by Relations .
+During this mode:
+
+ The columns represent the target notes of a relation.
+ When creating a new column, a note is selected instead of a column name.
+ The column icon will match the target note.
+ Moving notes between columns will change its relation.
+ Renaming an existing column will change the target note of all the notes
+ in that column.
+
+Using relations instead of labels has some benefits:
+
+ The status/grouping of the notes is visible outside the Kanban board,
+ for example on the Note Map .
+ Columns can have icons.
+ Renaming columns is less intensive since it simply involves changing the
+ note title of the target note instead of having to do a bulk rename.
+
+To do so:
+
+ First, create a Kanban board from scratch and not a template:
+ Assign #viewType=board #hidePromotedAttributes to emulate the
+ default template.
+ Set #board:groupBy to the name of a relation to group by, including the ~ prefix (e.g. ~status).
+
+ Optionally, use Promoted Attributes for
+ easy status change within the note:
#relation:status(inheritable)="promoted,alias=Status,single"
+
+
\ No newline at end of file
diff --git a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Collections/Kanban Board_image.png b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Collections/Kanban Board_image.png
index be4b027d2b9eee4fedd1ac6eadbea6e38faa08b2..1ccb0a3ccf55ff39e6a614e421f6914a9c4584c8 100644
GIT binary patch
literal 15518
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
literal 18644
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
diff --git a/docs/Developer Guide/!!!meta.json b/docs/Developer Guide/!!!meta.json
index 5d95a894c..cdf47264f 100644
--- a/docs/Developer Guide/!!!meta.json
+++ b/docs/Developer Guide/!!!meta.json
@@ -1,6 +1,6 @@
{
"formatVersion": 2,
- "appVersion": "0.99.4",
+ "appVersion": "0.99.5",
"files": [
{
"isClone": false,
@@ -110,6 +110,13 @@
"type": "text",
"mime": "text/html",
"attributes": [
+ {
+ "type": "relation",
+ "name": "internalLink",
+ "value": "4nwtTJyjNDKd",
+ "isInheritable": false,
+ "position": 10
+ },
{
"type": "label",
"name": "iconClass",
@@ -117,13 +124,6 @@
"isInheritable": false,
"position": 20
},
- {
- "type": "relation",
- "name": "internalLink",
- "value": "4nwtTJyjNDKd",
- "isInheritable": false,
- "position": 30
- },
{
"type": "label",
"name": "shareAlias",
@@ -1263,10 +1263,17 @@
{
"type": "relation",
"name": "internalLink",
- "value": "zdQzavvHDl1k",
+ "value": "ccIoz7nqgDRK",
"isInheritable": false,
"position": 10
},
+ {
+ "type": "relation",
+ "name": "internalLink",
+ "value": "zdQzavvHDl1k",
+ "isInheritable": false,
+ "position": 20
+ },
{
"type": "label",
"name": "iconClass",
@@ -1280,13 +1287,6 @@
"value": "releasing",
"isInheritable": false,
"position": 40
- },
- {
- "type": "relation",
- "name": "internalLink",
- "value": "ccIoz7nqgDRK",
- "isInheritable": false,
- "position": 50
}
],
"format": "markdown",
diff --git a/docs/Developer Guide/Developer Guide/Documentation.md b/docs/Developer Guide/Developer Guide/Documentation.md
index 63cfdc33e..5f9f17e5f 100644
--- a/docs/Developer Guide/Developer Guide/Documentation.md
+++ b/docs/Developer Guide/Developer Guide/Documentation.md
@@ -1,5 +1,5 @@
# Documentation
-There are multiple types of documentation for Trilium:
+There are multiple types of documentation for Trilium:
* The _User Guide_ represents the user-facing documentation. This documentation can be browsed by users directly from within Trilium, by pressing F1 .
* The _Developer's Guide_ represents a set of Markdown documents that present the internals of Trilium, for developers.
diff --git a/docs/Release Notes/!!!meta.json b/docs/Release Notes/!!!meta.json
index c2c0b02ba..a2210f04e 100644
--- a/docs/Release Notes/!!!meta.json
+++ b/docs/Release Notes/!!!meta.json
@@ -1,6 +1,6 @@
{
"formatVersion": 2,
- "appVersion": "0.99.4",
+ "appVersion": "0.99.5",
"files": [
{
"isClone": false,
diff --git a/docs/User Guide/!!!meta.json b/docs/User Guide/!!!meta.json
index bb262ac18..de161c37d 100644
--- a/docs/User Guide/!!!meta.json
+++ b/docs/User Guide/!!!meta.json
@@ -1,6 +1,6 @@
{
"formatVersion": 2,
- "appVersion": "0.99.4",
+ "appVersion": "0.99.5",
"files": [
{
"isClone": false,
@@ -9884,18 +9884,47 @@
"value": "kanban-board",
"isInheritable": false,
"position": 20
+ },
+ {
+ "type": "relation",
+ "name": "internalLink",
+ "value": "Cq5X6iKQop6R",
+ "isInheritable": false,
+ "position": 30
+ },
+ {
+ "type": "relation",
+ "name": "internalLink",
+ "value": "OFXdgB2nNk1F",
+ "isInheritable": false,
+ "position": 40
+ },
+ {
+ "type": "relation",
+ "name": "internalLink",
+ "value": "bdUJEHsAPYQR",
+ "isInheritable": false,
+ "position": 50
}
],
"format": "markdown",
"dataFileName": "Kanban Board.md",
"attachments": [
{
- "attachmentId": "usSSa0WI6dDK",
+ "attachmentId": "IrIeh59VGjHq",
"title": "image.png",
"role": "image",
"mime": "image/png",
"position": 10,
"dataFileName": "Kanban Board_image.png"
+ },
+ {
+ "attachmentId": "usSSa0WI6dDK",
+ "title": "image.png",
+ "role": "image",
+ "mime": "image/png",
+ "position": 10,
+ "dataFileName": "1_Kanban Board_image.png"
}
]
},
diff --git a/docs/User Guide/User Guide/Collections/1_Kanban Board_image.png b/docs/User Guide/User Guide/Collections/1_Kanban Board_image.png
new file mode 100644
index 0000000000000000000000000000000000000000..be4b027d2b9eee4fedd1ac6eadbea6e38faa08b2
GIT binary patch
literal 18644
zcmch7Uj<)NZu)kE|NipOQBk~9Il#OMez;<*ps8@_5-yJT)RF-FOz5g=r0nZ?UDBs^nA=Wz7eFFj!u5qrlOl>`}pmfS){J%bETm+!_3Q0Zg#n<
zL-W6W8Qi+-#)V4we&hO;%Y=j!n6rc62JbYIH}6IKZB@K3T2~qee@&+~ioChADZ3);
z^*)+OqB7T{-Z|G{yix%JLt^rEJnik0P6d$Q2c7Qq!X$7EW`joaKnHs8cpd0PNf8kR
z&~Y9rD)m1vRwBYL97oj4t>zZqL8^eX_?f;#|prCkLc&`h?X-#22DPLVtaG-({HqM*a+Ts0sd*
zAg6u7CxAl((Lp_jr_zqzocmc;MMf^Ob=O_n9(C{VcqNDY{YIThs%^G!+jC+8dNP@<
z50YUb-p{^Ku-|`~7bhpZ-h4QNSRLrH5^On|_qq7yJ)5|GUD|mg|
zi>)yEmk;yqcXVMsoNoqSM5W0h4du>}!51g93ukm@Pao2YJl1d@6hwUvhsB+&nr%$J
zv31wBtZF^?3xMyc#>t-_9kC!yeS^WQ7_Q^YoEw+gxN-t8GFCc&Un7f-*va<)#lRA{
zH`MSCMfa-0wVTvTf2nqk;M4AHhcY>bR~>G5v33Otww}5-^>=#c22jwMIO2|eSKKrN
z^Qv=_rxYLiaaybOsrns~5v`AVUJlawXyoP(%dnfY{@XKP(WFk$WPFLA#+9yXN_?ZH
z5~Ae8z$nXh2kr=JNG&DJ_3%|icX9D7#U*dcVmM7gJJ{Eo};3hOjI&VZO6ot<=PZb6$rykIlp&0
z`u>7^-c6FFD{vu30)07yKII(MNGG8+yyyp$3~XAyvq3%lX~c9WP0J!e4{
z7XNiz>X(}0&xRI(G&2Fa{fpTat{G=hf-OE8FQb%eI#2RdJeRPBv73kg{7rWK;&?C^
zUH13*YQV3?6^u$)s`WWI65DQqlxFq&k(&x1mj1ZA*|a;*FwfWAWa^S+Zob^%+%Ol4
z^AD(OT#oFT3v|z@2);N+G(P^ZeX`lg8rPd|5!>Ykme9~dMmkH%m7JMJ(7w1|qPveO
ztebZ4O}&J0*{%vcb0|ZSw(E^FMwRob3#Y|d*7emcrtUp{o}VyRG#hwmwvN?E=o|8v
z*zFSuV~>@*&DjVGXdIpb+lP7T<8=G^%?^~;q)q$d3@6&SG=~U|+7w!M
zXRApO97wWe7dXq_Vxo2@OG~oQy{DgFDIh3d&m0Fz%PJYE3c<*VMKenYAFLzZQo|xw
zt4;g_S39iDm%$8D(Y>`4Uf-H)UpPBm6z@{Yu_zCrGjDiw`|Eq}%_Cohs>hM}v4_eo
zXg_3ffJncnuIWR%V?tLC`k(5G{icJHk2Clu@064jrt_Z5`UcK~fd!KP`wI`1di)oJ
z9#v7MyV`n;!@%cUC99gQ&w9&-m@S&Gsx7LRRMxZsD{BtD&{r7Ag0fe`{>Wivmzzq+
zRU7MKfvCUcvb<*^nj?w%-#$ROcXKSX9JI~LJ(_S$|Iyw;r!>O=)USYyC_hm-}oDBqPViOR^H+tRRTSoYrU
z1ic|9q_B$1+>fEWc6GsK>YVn^dZAy2GbFw}9q9<%r~s9=+a;tRg2RJrB-;>L5sF1x
z@Wz(MeonpMA*z~OLt;)7d=M7#mgO)5ey8%#o=jz->BPB8m~~%S{y1i)mw*;^i!&2t
z6(ct9|LCt}%1luyJ^%Q5>a;gLUo~>1!&5apsxg+d48N0hTwX582c2T!=UxOy
zH#yy!?Y;p&qE@=K-1198s`-7s
zMi%z2Ppoa}oQ1IHqAV}traF$OrN!Gru(?HgSOIfhYWBzSWdJLx4`=+l=1Rdn)a4>@
zg7De;-jH)a)oyHL$89K`lzc^4V(u}Akw9yk?WFf?;KDw_BXI*1R{Wa7j^Ezc@`Wrk
zS6J44?;Y`-`sr!-x8Wv%{%^xm_RXyjc!9kz^?Ct&ejK`bu#{rK8Fzgy!=)u{vE?bv
z&?x{5r{{;Qe>Fx1D#DYyioUx|8n8Wu_8Q!Ry5`cildgDqqLh7VT
z{+uEaZwgB@jl=Y+&JJf;$1H-+T)S?1xN*Hd8|o8Xbjc0>x#4>ejbVGjv})XIq!G8qC-M)m`ItfM
ztokG^3Eg8Y4tapabn*JW5Y8Eu|95De6{L@XRj)f*%Vkj7wU)eqXq
z|n}r9v{&?Gv>KDBU|2l@YOQQbBxgJ-BaX&np)uLcGp}@uK=-CSFGm2
z>6qi2e%b?ol|O^+-&%WPZf^$w?a*ti0YWn~k6w_rsqs}6|2mf+k|`JRFBhalHXOa
z)=J?{&rden8Ei8t{s4Y$`{I0NpYP8s!P{ybe5ov}e8(FAIKte;`Q}C27hUbFb?jGi
z>~D`j+4>H=d#u#*b#QXmA0tKDHAM%m!%e?`mbDMcUog9j6u<2ic>j{d0L3;VV12YT
z2f_asL_MBT{bfb{jCA(X(Q|Gx^Wd|6*LG3~{DqQ7VcO&LYA{q`9ds7c_b($ck$V7F
zhbfUs!2kl_@6NX1Z}|Rv?sRvM+@Ye@V6p9kaD_0F}eQ_6391HN>bi57eHJ;zaB|zgip$)>3@)_|L=vAr(BsIe8phOJDl6M8kn`buT>eyPMV
zGw^M6yw;Hi&&?OpL5iGbumqNBQ+57Yy$L`Qh*~T{U%DzTEhazVp{SstCIWcfYD7xF
zP!^VaT9rEXA%2OcC{q~P0xiLM(*AW;0k&;HQ@_=M?Te1B$CwM_lXC!0`OE|NMcFhPp0XK9w^vk~Q&LPnXXQW+3z`QVFHPJH0!u1vA@+{IpuWxp6t&p)K~#uhjJlavM!}l)g*E5__BsrOArKY`|`zIjx_8>T7$;S7?h~`qvuu
zcjQfm0cbu_u6(+tY61rS3&YmMZ3@d&g622B;w>x
z=g7C24SOS&?rb(uQTYv6g<#rK%>8(pz{7&*F47k>Oi!x$JPbA`Ryvk=7_#-_vfn{?EVu;XImrXrIjGNC`KBiB2bT$sfHK83WAET*VEp|)6_r!dE)dY~#;
zkI--qW9#gDjoN!Q_}h9wmC7D2~0%p3=hFfwVIBTM$JAjpV_s`m=k)Zosd
z`U4U_bJPcP^Zr;=j=a-oRR>E=?8N)=Avr2pZ$nczWuNU1xS222ZA
z)OUP7D_rZK;@Un0tcY($d#6Ka0=j4prL)yqYdiN`0)OM5xGO6PM4Enl4uJ`vNb-hI
zue}}7in5VM+3ydjusRusab#JgEwjLrl;Sw^Zg&f!?uOZiV%xrC@z+laLy`^d+Wx_m
zOPsEs+8j+K75qwj0Gly+fW|n1y)2eLQ
zOC5Qt2;vB)Zf-uGF~%7xC03MIdNw--kCwnN`0<93FkqM~>b6_iz3fQU!`|0W@-T7D
ziPw4RQtw)5NSQrLG8bH?ujXCN4P#-vu&srg$q>E0w~zbAJ<
zrFWQn9VI~YM`{65N@6nxMhK7Xs@5?Gb*exl%e?fv<$1d>z5MO4TDL6r3W#x?GCiCTLsU)NQqax
zD&}PXpa^p>skE+xdV|s^U1j5vs@%`7$y*(-vT%IGHZ%2nWji329f{%W=vf6d$V}r4
zO8a%Ay*JF$v=t*QNjlQYUvKKJZS%QXVUUOY(^2<^P8y1K>$4Jhh&D7y2i2j98Ki4U;x{p1bWDvZhrv?8c9t5h%{K1hxaM^5IUkIm_U#k6c#*
z3>ot9&@!9DFwN)in>Rp_uW(r%#Mgk}$uy}AG@D58=m+(W%?P#kNfFFB+F4T>>32Y3z@;4gFkGWv~+GW!*QjFie|SZkIIY-zB7G5Mkz2JNi|0U2)Pije>T@GWM6Q
zwk&p?pDb?0SEhAL@IAnsV*Ehdn=8(Xjf`4&;4EAdyU&U0ix2THrowq$jan8GD<1YC
zhx;JSqI0GN^p~ZQ^}gFzJZ{nBX)yQ-R2W1zZ0t$TnG6qaizCU1Rlrlk&yDA&wLji|$wM~)VXFgg4djy_uE>JYu#TKm
z!4J=6VM%+Uf^`#U&n!K!d$5qcB2cx4m&>y=r#>Y|olOre_{a>3lqA7QBu)SN=c*Cy
z-duWu#;o-5V&a-y$-NAJ_=v+0f;ow>q`;g@NOR(enzBa>B`XHf=E#*_rKGm
z|5I{=ObNZ;y=l4i{bRQacq0sq#uoE0cOzd%irN4=^nY%1{I3Yqf9sR14u9r8Zsvv%
zG!>`Dbt*0`jRRHQuu8M{q0Zmlg|Nu_!Tfs$kFZ}={JW}fe6{4g=A-Y`0>Yy7pQ4d>
zfr}%Mlp3bS`!GeoOmQ$xaVeTXQduPa&xKo#`aAC{f7}8&k$bg6N}0D
zYClHH42`tNI;-@2m%=Tq3`=F8_jC^on|fU=mHf6&wY1#5BWs&a-&|^bU;@iKSYxd6`{fV(N>Qa@#Qud|OjD4o$?yYs{If8ieeUk~elwx1LL
zrqc>?$N+pr0-~_p6|?C8IJW~XmNLz&m1{KsHgro|2QBfxJgJ*XVE;~>RZHOr3IkiD>Ilo0OnID
zL@N8lryM!D?092F(`>V)o&vH>a#LC~npIv7-n=(F2e2E-*~y_c3y-J9s2Uvb=ksa+
zC}8zLVoOQ7p`b|1K#EPSdWry$!;UrJ`kSQ*sJfE}6A%fFW)$OfuAg>SpY9rbYGJ}x
zuID8Wgfkx!-Vbm4AcF~FbulL#qgT=U0O`dxr?iKWlLI-#)&~%lw$}6gsznOS*T1om
zMcIC(B~5`qm2QKqHm0WxF}IL9Qlofgbs`x*v1MR|2?357_SfH6UqV#1vEK9TJArpU
zUjLB^d?t7?K_wJO!&2gpwu1%%_iB)r6$ErtbX!cYX{&7_F`W@u
zi2>TvgSLylt7p*T31L{~f!?(p-|T>$Zl4^z-tYp*bm;@Q8o^2bD|QUcA2amq?`z^6
zOfd}>0o%$u;WrjuNZ!s4XPS?aOw_w0_xnd);Xqb7H?e*B>!F5J=?~vOzTlFwT~v$P
z2PmDxKPYOKX8-C>BFK`1NTs{>8pNh&d=_F*D-TXe{m@8EJs6nI%l0{e2N6muNA6DV
zRtx8V#iz-l&m7uT*)Si8!*r#rfT6TN*=Bj7|MCU8N7y&7DgM*loC^|Qh8@AMs>q53
z`~|@99r-(esS`t@rp6d#uN2;^qzC&@G}kfMfjaJa;KL~p3a4up7mNmGo}<-mM10hq
z=C_XXcBI`GKhe+P<>PF$Y`^;KZPd?(0xga!6MV4*o+L4`K*OUE-)HyI7F>X~j`(Y7
zpNAPR@de2%K9M|y;mBP`*+8wfe1A?nk8lBQohx7hA^dd{w`pDVhNZ4-T5ZI#;M9@Z@gy;I42m%ge_XG_R~!bix@I38X&^QSJi}(QP<&7Jr@ATsv0oW&PL-AtvgOW
z8@1yYe-rV?eW|I@6o-3zfL&X=$s;+p)dY!f#(loea^UbJrde3yaY&z@t|m>Q0%iOD
zn2BJ^z}|>9t1}>5C_=44;%UwjnEm59s@UU`*$X+cmEhHalzFH4d%99ZlmpvGCZ~t3
z7o^1S>bp%ArJkbKhmO>2swadSfEzyt@J1v%5oD-u1z#Kn;|~9^x*;-d5gZuJJ|EEl
z7OzMwN67-4LY7Uk5t`l6BkHNn^2pJY>5fXc+9n)K8+Q|@~(&J8Er%F-XDQa2VXKH{(aqSKvy<1tvr)kpJDVUTWt
z3AS@MqTD7gc&)y6&7zJY8HL6WDE=U|q)0dwe4Rfp^hMNc2sM%>wWtD6~hza#aqEi^`AMzZb;hF;|N9FTOv%S_%67ycy3
zBM+yVub&W>l(^4ee^)94ZT14}zH%~PU27tg1?6R5lZ0)o)9{z%u}xZChez4ym}9Kn
z&5;zl0P8tPE~i?}a~DMkyeqbcQJ|*7EsvAhxw4+>WVVe>`_6-_j$26qqbanvJ}ENE
zG%reXUpYU5r7IuYEO}KoF2lqlF-yc9_SWeLtkqmO-@0A8a$&Jy;eI%8H#5
zjW5bcv2vU3=E`BNZ#~;^=|AZ6x+;d~!U(GUO5x~8$xpf*k*wS4I+77@AaIPlomQ3V
z?2nv$$W~zZl1hfD`I(`ar@m=c4abtSAoUCNS3-1&k>U}&N?tz#=VWl3Ehn9GmDIww
zaMt*Cz^GY?Gaxm{pdqc5ZB!Z^MsZcNE}#ACSN1Wx0JJ233NNpBlNv9Xk~)veNK6SD
z=0n1xd01;Rz|qY@TC1U88A-^_?7kwS6FOWRfTvKwTSsHRGf-TSeul!6cd-TsGzEt;
zJPs$V3yELH-7xyvkm1lTHXg0}{lh2X0VC$bD{&Oc(V2GYfjJN3L|-TF=}7p1=iEw3
zyvxRqg!smCi-n&mx!lorlncrRtB?NnDk&BhoBuO{ga2j#8}hpDQw|Y941sU)51JQ(
zyu?o)JgOf8L3MWRuV9PH)1R-S*L)J_(1mBy$}F+{0lY=Bslg<=-w(a3cO
z3}`ou!BBu+zAwvQkr2C)^&McfFWeU-t%#5Ixs{vGmFWAJn)&%#Zm5A
zbJeTTfA2jO7EPvj9<6iPhEHirfwJ>BgyMcC6a8N};F;BaotUQVc|2gb_XzXSy^kZs
zR9M_qOv2?2#}^u?mLA<<#~d-QIt8sXz;bvn4g=4c1oE;QstVqzsu=9!F@jV`mNYr6
zBCi|#^4;ER9u4VAAG`9uf#?Z)U4!G>q5HU#G~WlO1M(^Qw~rNv&~0u~a;>^Z$2Edk
zj@ni*CV0ryX9(-Mu|i^K<4i|P?5wGC;^)2mL4L+*5M(Hgt)
zl-tF>-hv@H=kh;Tr`|dQ#AXe5YpI4;hkwo=*(g8pdtEs7wCXSgc1O
zzeg=Jo7%~;Y@y4>9t+dImgiJVSHzUUgod%k++CHUT~t#6ixw8^JDSuAoNCX^3soak
z6|xoPWyWF2jHz&q%V{%OVe!i?oZXK+A~+m`T$_^b%jF-gup|z4J0x3uWcf+fCt|o(
zdTxl$zDhtYzdHO_j#@o^*QTI0SL&&gNZgC(n2syOH+Gl}!ZcQb5!WI+?m~$rAJbUv
zC_<~;s0~7_o^MAcogMS*l@PXoy-1>%RQ;?*D|7^pG5|@s^3zM
zj|ofCA;8--Tp{0mm8zT7NkC(Wym{@W_}(Xekh;M=jELzp2rbtkOZ?{{`QXjxJ*qi-
z;t{vq!)AqFV#)Fpxr2R!f(F@qm;!Q
zGoH84x0G`onhVUsbhasu+jRdD%hhYn<*2IQWoX?fDA>Jx!t-JHto%k7v#OtMs>Kjy
zuj%eHJv$!({#UEhv5G6CGjzcgGlpMNIhGt8P7T<-dLLfZ{VV^uooVrmC%z`7vj74N
z;=ga?6}+Ba>>*NBA%AAB%4lmum~MFxC->XVbRrgOV*eJMMX>BS?4!(}DabYQg+@m^3Lnq*UKgg&
zS($U|UPhrDtC|lYs|HKQU>&Ds+=nGfTQcYOrwiHAN9{(Y8oORw##(+XufIfl(H$5h
zW^+tdudlhkrsKOnj2g(NWR6=%*HhasYTUg{OeQ3Qbue)ovI%`F%$)q3-#cV-Ie#T&
zc{xCbr?B8n5=Y#vEo@ZOm3b;JbyORx4i5s{1lu2Hi@9chm6#)pQEdZbN_F~o*Ge%=atbP
zI8j{Elgy9|y;co)!EfuyGC#u@{4hr>M4$g_=I_5{Z8nXuOCa@znT26XEn0&>GNN{+
z_HDEoPsTwcAf!($M&bSiYQ>$N@Lmi`?TI|Cv(DL*D!h))NzJ-`bJ>0L>VtndqyKIE
z@ZT>bzdVJ>e(3>*=*Mt@`i_Yu7PT`yf&E(8JU>c441jEr9`U6`x-b0z~`o37RY_&CNnQc1rcu3&Xo1
zkGwv=XBEu)sk5QdGG3T}8uCKu%SSXsG=G5L+Rk<4F+?Fk$yr(M$1u`Zg-~5;ry0In
zsS{BE?x0d<+hJX8Nx!>BLOFBiU4>q4LCyC&ectblksLwxh4z0xhw*>6Lln4X*cYb3
zr}GVNu;Y8I6E$hMjz(S|1H{sWQd=mM1$2H&Uj)(vHcBbp>uoV$R4->7M5`@zdPi;2
z&(=QS#M*hp*%&uaBms^Q_+z%U6|ASr#t8Y7^*2X%2yRvsk@R=FNb!5efvd6^pMkDw
zBahOi>Wh($vs`X`UfFD$CNC*MNjc&8p?#S>$A3Jd*@46A9N(BS_b4
z1qzBBc=zKpl$!oyhm!wv0Mdv4N4oXjm)F0?V}Z70@K_*@MOOOQNk=Be;Zn@aJL1vw
zh}(U@9O{-0ajSR*r5FQ1QR~ibOEz`yHi+Z!qy}0)88qvuAYaf9tTz_F{hu_@yuW+8
zijlefvL*0@vlUzU;53=>z05h-3&;WF45}%rhEf=CKqnHLyi3>}29Nkv)}MFD0`GBK
zs(&u93fIZF?V*~b;Gfd0W2=wD?>xK4avgvH-&8P_7}?g2?dw}0wpjCh0j$QNKL#L3
zMFI|5`{{PnOAy2pItAVz`TuZ=k&2}@`|&6;Mp5o^5C_P$9n1##;kFmd4YU&tZVPQ7
zCcq2;dxZ6-OCY4*L!K$LH>U#L0J(7VRKv#F_mBN$fh4fq^cK)`4*$`~Q6)4mV*Nv*
z6k<#3@)iRWlXC^^h0{L>8*~^(%9OmGrUwS~kHioh4CMr%=xAY<*H@oAbeqlmOK)&R
z2anJ^wQ-T8AqB4VAT4+VxKYM8$Qp!n0~C^f5kxrtUz3_Gj9Iw*eiXdti<7Z<;1>#sIjueTQd4Hzv_Kz+VLu#g;c;zJVWGyu{xHxfTVoPUOM#-2oa
zM7M#^MDQc?7qVY6lnmaeER1i?VQD-
zg52>XV2nn{Pf^KAIO^Db;C|71x+M6=hZk(#(`f)qsvQt|CMc)qI%Z_sfq(m^>`S8x
zZwK_yI)hs*tU36R7Z-~}ef^C8@j_}jmEHI0ZqGxxdO9m@Oix}r|4iZ(l`0<}jgj9y
zD9RvtdKDy_d?$iVHWoh1g^oX%7<&Hu6MNiCh^WjA4(>@FPXt3OJew|i5I16#KLc_A
z)gYpUpQ8a$Xxjkzq3*}Rm1H6@CXeA^1D#)Yc0RE$AQ;jWqJ-MB^8LDm4nP!E*g*BY
z=UVbQsU=X8$f}xl)KWo`CQ+7620&`dQGLUh?Zz)U>LEZ~(HhR1G<7W4S+E18Jl)#a
z8?miENED+l(+WBD0EgOp!3c&uNNHSWs5Uo1`ZU{mLau8?Y~|HB*kI?*taszxAZv;ps&mha8mLgB-xoGxY$4H+%D*kF*V
z>geHZKY=pOt{}+~3c|7cGupi?P>7SSm)aZ>1CPdA&uO3>`h<=@&>p)^CQ}nTUGEYh
zh}t2sVmkQZ4Cmf|k~iP)pZtVWX=G%AgVxxX*&0O9W3hdCvC_N)xWyAPOdq5h9z_1*
zGk>QQtm>}K3ow0j3&}kk#*zF3T*q#EAo$(c$-w(A)8e!7Ddx)NyR}LjS18*7^U6-I
zVBBt3dV6E&peK0vJ5pPK;55HoEc)WG^8}g>E9KO~fdB;M1{jO7?~Pm+Qji9hijL%I
z19FKAfnnMWE~PLsr^wm4_AstPYb7I3AiIP@bkxmRoNxhB;uij{zz%k1!$uoBsqCUa
zVbXVXfZt_g%7k;c&Ugm$)-TGmZIcKFIj9C{>I4Fo>X<^d4$82+64R?}T3_M?L+tzvg}XlMev
zinu7P-S#2m(X%Ax^v@l|5NNc6;O4?c1QnO~dfk+>X4Mo5nY9>1!YnD&atIkHGt0Qf
z;5d+pFSL9{K${BuVW1{;9)3lkIQ_p2ZCyBP1Tg}d`Qqp%XJL57G^+RZq2#K;lXXhs
zm~PYPL~2Bbw}^ySgbn(gRMW2p4UGCGt~TsYgq`9(vKTANprS
zZSvcA{PLE$xbw~U*m1o?=j;1*taMrF0jlP+$^xN$-6eAn-*WR5FlJ!L9-|Dc%ko`L
z(`53fx6`~5^8t$B0?5(9B6?Qkxy`Y
z1^D8O!OurUjT90Or%}1cgY(Fh01$P+b-~U|K8sAD2qS~9l~nw|IcjXB-`}b(mGg8pR5sOKUDt-3W&N{zzHu~rM
zHI012
zt@~mBX`{oAkW{L#y+`({Y$=enE#1qW!n#OFNj00fz8?UnMeA?p?r)+a6LylCl74b1
zxsvJ82JGns8MP-&%N%1mI)tF){WR&IR1fVp12QviC(W58B+n*(beb2dCrp?VrS`_|
zQb_No1oj2F(9O@hYC9?g*sG&lKcFSV_j1VPpivKFrg0?CI$st@J)ZogHB>cFziN
zAtA!y3ZW$k=Byd*12=`IEK2LukFK_lMI$6%cFHW_c(bdTDs-qix=BpTEtV5H0Z}IK}*|;Kg*iZzNk2B$(bE^IVovx$XjwHga`W>
z@=Pj8)CCD;1^YJgUD>u5&Y&cy+kUsA(ys9vsPltmV()8u-!>Z650t23QR|cwRreH@Dp)mh
zp37_t4%1tGQa2w6*&fk3X~&NO#O3(dDN#6Du-{?O%RU8zbDMo?$)K>>HCgapjgiMc
z2|5*)f0v9=qxZ+?&At=;95DWTC{Kl}RWq_^s757@sUqO*)+iMbnV$xC1NVooN+uRB
zlr@Usy7mG#=HWldo4w&IAKUZ;REEuWr>$c)wM}RuXP`=D~J+f3^C;?oI
zC*ui3nQe6YA+jaRm2Cfrj8#r%lCOjw-#3A?Qab$gZGqtZ$mPoa`eO1Bj$8hRzivYO
zHI(e;6XMb@MCg|&1_b(Es-znlRBsjd_wWKWVXuPWQN>fn_d2Uj3P3rk`4eC!W|Z}N
zx-*sU!#D-f{B1|u3+<^0tNHt7+I(S2(z3?tJQKyt2cYxrJU>2fKZ(A_Np@qZ-et#R
zmFQnr9Px*6m+4LNc^ZIN?nhn1&u`<|A59*et>gs99&XbU%XoU(TXtw0tJrfC2(1A!
zK0W+A=ER*?Jn0VZC614#iV-53lltsl1p{yobi~{Eya(zM&Tm+0U5Bd(m7&X-DNriwvDf$oGv|N}Y`4j)*js&szYCU%o_otouy_SHD1XSAuR?YC=^Z;w<
z^3tf(^TWQ|=FW1QMP7#U<9C7u^MUw4|6a$Qmj((2m}4W&N<{e7(P=ez6V=8|bCaJ8
z;)QL1t}tT$hjW+7_L`Tx;dMasl$`P=0R4q^BMbVlc>EG;8X}qjszXS
zf$Q(x03dMiu3Ubs9f&l$Iq=QDY~Z*bF>1OR2${4sMS-#a)y~aF2@X6J!2Ilha^H}M
za`dKx+YoS{xWOvJb-k_|cGmxSjyhH_@l;1p-Vc)Q1V$7ui8><{_>bu)}s5
zLsN