Compare commits

...

30 Commits

Author SHA1 Message Date
Elian Doran
f64d11b7c8 chore(deps): update dependency @smithy/middleware-retry to v4.4.10 (#7709) 2025-11-13 09:42:30 +02:00
Elian Doran
2bf9e0edd9 chore(deps): update dependency js-yaml to v4.1.1 (#7710) 2025-11-13 09:42:18 +02:00
Elian Doran
b807079c55 chore: add class name to content-header (#7713) 2025-11-13 09:41:35 +02:00
SiriusXT
2cc5b896e0 Merge branch 'main' into share 2025-11-13 14:24:04 +08:00
SiriusXT
7c79caba78 chore: add class name to content-header 2025-11-13 14:21:07 +08:00
Elian Doran
fbf4a910fa Merge branch 'main' into renovate/smithy-middleware-retry-4.x 2025-11-13 08:07:46 +02:00
Elian Doran
95947a9f8c chore(deps): update node.js to v24.11.1 (#7697) 2025-11-13 07:43:55 +02:00
Elian Doran
1c05acf5ed fix(deps): update dependency react-i18next to v16.3.1 (#7712) 2025-11-13 07:43:18 +02:00
Elian Doran
5cc5f3ffae chore(deps): update pnpm to v10.22.0 (#7711) 2025-11-13 07:42:47 +02:00
renovate[bot]
54556c73e2 fix(deps): update dependency react-i18next to v16.3.1 2025-11-13 01:08:04 +00:00
renovate[bot]
5aa63ac50c chore(deps): update pnpm to v10.22.0 2025-11-13 01:07:17 +00:00
renovate[bot]
38eaa94a53 chore(deps): update node.js to v24.11.1 2025-11-13 01:07:05 +00:00
renovate[bot]
7810f6c8da chore(deps): update dependency js-yaml to v4.1.1 2025-11-13 01:06:16 +00:00
renovate[bot]
6d94efb6c8 chore(deps): update dependency @smithy/middleware-retry to v4.4.10 2025-11-13 01:05:36 +00:00
Elian Doran
54d3936c7b feat(board/promoted_attributes): support multiple values 2025-11-12 21:06:44 +02:00
Elian Doran
02452a0513 feat(board/promoted_attributes): display relations with icon 2025-11-12 21:01:53 +02:00
Elian Doran
e9f40c48e3 feat(board/promoted_attributes): basic support for color attributes 2025-11-12 20:56:15 +02:00
Elian Doran
6b74b227cb feat(board/promoted_attributes): format URL 2025-11-12 20:50:01 +02:00
Elian Doran
00874840b7 feat(board/promoted_attributes): format time 2025-11-12 20:48:45 +02:00
Elian Doran
d79a23bc9e feat(board/promoted_attributes): format boolean 2025-11-12 20:42:30 +02:00
Elian Doran
3015576d7e feat(board/promoted_attributes): format date & time 2025-11-12 20:34:48 +02:00
Elian Doran
46c2e162f0 feat(board/promoted_attributes): format number with precision 2025-11-12 20:31:27 +02:00
Elian Doran
3c42577da4 feat(board/promoted_attributes): react to changes 2025-11-12 20:27:13 +02:00
Elian Doran
76f791da93 Merge branch 'main' of https://github.com/TriliumNext/Trilium 2025-11-12 19:59:36 +02:00
Elian Doran
0c5adcee2d fix(board): no promoted attributes creates margin 2025-11-12 19:58:42 +02:00
Elian Doran
b11b3ff67f refactor(board): use hook for obtaining the list of attributes 2025-11-12 19:57:28 +02:00
Elian Doran
e006afc5a2 feat(board): display in chip format 2025-11-12 19:38:56 +02:00
Elian Doran
40dbb818c5 feat(board): ignore status attribute 2025-11-12 19:09:59 +02:00
Elian Doran
62dc570d38 feat(board): display promoted attributes 2025-11-12 19:07:23 +02:00
Elian Doran
b759c5e7d2 feat(board): display number of items in column 2025-11-12 18:47:29 +02:00
22 changed files with 527 additions and 452 deletions

2
.nvmrc
View File

@@ -1 +1 @@
24.11.0
24.11.1

View File

@@ -38,7 +38,7 @@
"@playwright/test": "1.56.1",
"@stylistic/eslint-plugin": "5.5.0",
"@types/express": "5.0.5",
"@types/node": "24.10.0",
"@types/node": "24.10.1",
"@types/yargs": "17.0.34",
"@vitest/coverage-v8": "3.2.4",
"eslint": "9.39.1",

View File

@@ -9,7 +9,7 @@
"keywords": [],
"author": "Elian Doran <contact@eliandoran.me>",
"license": "AGPL-3.0-only",
"packageManager": "pnpm@10.21.0",
"packageManager": "pnpm@10.22.0",
"devDependencies": {
"@redocly/cli": "2.11.1",
"archiver": "7.0.1",

View File

@@ -59,7 +59,7 @@
"normalize.css": "8.0.1",
"panzoom": "9.4.3",
"preact": "10.27.2",
"react-i18next": "16.2.4",
"react-i18next": "16.3.1",
"reveal.js": "5.2.1",
"svg-pan-zoom": "3.6.2",
"tabulator-tables": "6.3.1",

View File

@@ -3,7 +3,7 @@ import { DESKTOP_FLOATING_BUTTONS } from "../widgets/FloatingButtonsDefinitions.
import ApiLog from "../widgets/api_log.jsx";
import ClosePaneButton from "../widgets/buttons/close_pane_button.js";
import CloseZenModeButton from "../widgets/close_zen_button.jsx";
import ContentHeader from "../widgets/containers/content-header.js";
import ContentHeader from "../widgets/containers/content_header.js";
import CreatePaneButton from "../widgets/buttons/create_pane_button.js";
import FindWidget from "../widgets/find.js";
import FlexContainer from "../widgets/containers/flex_container.js";

View File

@@ -10,7 +10,7 @@ import LauncherContainer from "../widgets/containers/launcher_container.js";
import MobileDetailMenu from "../widgets/mobile_widgets/mobile_detail_menu.js";
import NoteList from "../widgets/collections/NoteList.jsx";
import NoteTitleWidget from "../widgets/note_title.js";
import ContentHeader from "../widgets/containers/content-header.js";
import ContentHeader from "../widgets/containers/content_header.js";
import NoteTreeWidget from "../widgets/note_tree.js";
import NoteWrapperWidget from "../widgets/note_wrapper.js";
import PromotedAttributesWidget from "../widgets/promoted_attributes.js";

View File

@@ -0,0 +1,31 @@
.promoted-attributes {
display: flex;
flex-wrap: wrap;
gap: 8px;
align-items: center;
margin-top: 8px;
}
.promoted-attributes .promoted-attribute {
padding: 2px 10px;
border-radius: 9999px;
white-space: nowrap;
background-color: var(--chip-bg, rgba(0, 0, 0, 0.08));
color: var(--chip-fg, inherit);
border: 1px solid var(--chip-border, rgba(0, 0, 0, 0.15));
font-size: 12px;
line-height: 1.2;
}
.promoted-attributes .promoted-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 {
font-weight: 600;
}
.promoted-attributes .promoted-attribute .value {
opacity: 0.9;
}

View File

@@ -0,0 +1,119 @@
import { useState } from "preact/hooks";
import FNote from "../../entities/fnote";
import "./PromotedAttributesDisplay.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 { ComponentChildren } from "preact";
import Icon from "../react/Icon";
import NoteLink from "../react/NoteLink";
interface PromotedAttributesDisplayProps {
note: FNote;
ignoredAttributes?: string[];
}
interface AttributeWithDefinitions {
friendlyName: string;
name: string;
type: string;
value: string;
def: DefinitionObject;
}
export default function PromotedAttributesDisplay({ note, ignoredAttributes }: PromotedAttributesDisplayProps) {
const promotedDefinitionAttributes = useNoteAttributesWithDefinitions(note, ignoredAttributes);
return promotedDefinitionAttributes?.length > 0 && (
<div className="promoted-attributes">
{promotedDefinitionAttributes?.map((attr) => {
const className = `${attr.type === "label" ? "label" + " " + attr.def.labelType : "relation"}`;
return (
<span key={attr.friendlyName} className={`promoted-attribute type-${className}`}>
{attr.type === "relation" ? formatRelation(attr) : formatLabelValue(attr)}
</span>
);
}
)}
</div>
)
}
function useNoteAttributesWithDefinitions(note: FNote, attributesToIgnore: string[] = []): AttributeWithDefinitions[] {
const [ promotedDefinitionAttributes, setPromotedDefinitionAttributes ] = useState<AttributeWithDefinitions[]>(getAttributesWithDefinitions(note, attributesToIgnore));
useTriliumEvent("entitiesReloaded", ({ loadResults }) => {
if (loadResults.getAttributeRows().some(attr => attributes.isAffecting(attr, note))) {
setPromotedDefinitionAttributes(getAttributesWithDefinitions(note, attributesToIgnore));
}
});
return promotedDefinitionAttributes;
}
function formatLabelValue(attr: AttributeWithDefinitions): ComponentChildren {
let value = attr.value;
switch (attr.def.labelType) {
case "number":
let formattedValue = value;
const numberValue = Number(value);
if (attr.def.numberPrecision) {
formattedValue = numberValue.toFixed(attr.def.numberPrecision);
}
return <><strong>{attr.friendlyName}:</strong> {formattedValue}</>;
case "date":
case "datetime": {
const date = new Date(value);
const timeFormat = attr.def.labelType !== "date" ? "short" : "none";
return <><strong>{attr.friendlyName}:</strong> {formatDateTime(date, "short", timeFormat)}</>;
}
case "time": {
const date = new Date(`1970-01-01T${value}Z`);
return <><strong>{attr.friendlyName}:</strong> {formatDateTime(date, "none", "short")}</>;
}
case "boolean":
return <><Icon icon={value === "true" ? "bx bx-check-square" : "bx bx-square"} /> <strong>{attr.friendlyName}</strong></>;
case "url":
return <><a href={value} target="_blank" rel="noopener noreferrer">{attr.friendlyName}</a></>;
case "color":
return <><span style={{ color: value }}>{attr.friendlyName}</span></>;
case "text":
default:
return <><strong>{attr.friendlyName}:</strong> {value}</>;
}
}
function formatRelation(attr: AttributeWithDefinitions): ComponentChildren {
return (
<><strong>{attr.friendlyName}:</strong> <NoteLink notePath={attr.value} showNoteIcon /></>
)
}
function getAttributesWithDefinitions(note: FNote, attributesToIgnore: string[] = []): AttributeWithDefinitions[] {
const promotedDefinitionAttributes = note.getPromotedDefinitionAttributes();
const result: AttributeWithDefinitions[] = [];
for (const attr of promotedDefinitionAttributes) {
const def = attr.getDefinition();
const [ type, name ] = attr.name.split(":", 2);
const friendlyName = def?.promotedAlias || name;
const props: Omit<AttributeWithDefinitions, "value"> = { def, name, type, friendlyName };
if (type === "label") {
const labels = note.getLabels(name);
for (const label of labels) {
if (!label.value) continue;
result.push({ ...props, value: label.value } );
}
} else if (type === "relation") {
const relations = note.getRelations(name);
for (const relation of relations) {
if (!relation.value) continue;
result.push({ ...props, value: relation.value } );
}
}
if (attributesToIgnore.includes(name)) continue;
}
return result;
}

View File

@@ -16,7 +16,7 @@ export default class BoardApi {
private byColumn: ColumnMap | undefined,
public columns: string[],
private parentNote: FNote,
private statusAttribute: string,
readonly statusAttribute: string,
private viewConfig: BoardViewData,
private saveConfig: (newConfig: BoardViewData) => void,
private setBranchIdToEdit: (branchId: string | undefined) => void

View File

@@ -6,6 +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";
export const CARD_CLIPBOARD_TYPE = "trilium/board-card";
@@ -108,6 +109,7 @@ export default function Card({
title={t("board_view.edit-note-title")}
onClick={handleEdit}
/>
<PromotedAttributesDisplay note={note} ignoredAttributes={[api.statusAttribute]} />
</>
) : (
<TitleEditor

View File

@@ -104,6 +104,8 @@ export default function Column({
{!isEditing ? (
<>
<span className="title">{column}</span>
<span className="counter-badge">{columnItems?.length ?? 0}</span>
<div className="spacer" />
<span
className="edit-icon icon bx bx-edit-alt"
title={t("board_view.edit-column-title")}

View File

@@ -53,7 +53,16 @@
align-items: center;
}
.board-view-container .board-column h3 > .title {
.board-view-container .board-column h3 .counter-badge {
background-color: var(--muted-text-color);
color: var(--main-background-color);
border-radius: 12px;
padding: 0.1em 0.6em;
font-size: 0.75em;
margin-inline-start: 0.5em;
}
.board-view-container .board-column h3 > .spacer {
flex-grow: 1;
}

View File

@@ -15,6 +15,7 @@ export default class ContentHeader extends Container<BasicWidget> {
constructor() {
super();
this.class("content-header-widget");
this.css("contain", "unset");
this.resizeObserver = new ResizeObserver(this.onResize.bind(this));
}

View File

@@ -1,4 +1,4 @@
FROM node:24.11.0-bullseye-slim AS builder
FROM node:24.11.1-bullseye-slim AS builder
RUN corepack enable
# Install native dependencies since we might be building cross-platform.
@@ -7,7 +7,7 @@ COPY ./docker/package.json ./docker/pnpm-workspace.yaml /usr/src/app/
# We have to use --no-frozen-lockfile due to CKEditor patches
RUN pnpm install --no-frozen-lockfile --prod && pnpm rebuild
FROM node:24.11.0-bullseye-slim
FROM node:24.11.1-bullseye-slim
# Install only runtime dependencies
RUN apt-get update && \
apt-get install -y --no-install-recommends \

View File

@@ -1,4 +1,4 @@
FROM node:24.11.0-alpine AS builder
FROM node:24.11.1-alpine AS builder
RUN corepack enable
# Install native dependencies since we might be building cross-platform.
@@ -7,7 +7,7 @@ COPY ./docker/package.json ./docker/pnpm-workspace.yaml /usr/src/app/
# We have to use --no-frozen-lockfile due to CKEditor patches
RUN pnpm install --no-frozen-lockfile --prod && pnpm rebuild
FROM node:24.11.0-alpine
FROM node:24.11.1-alpine
# Install runtime dependencies
RUN apk add --no-cache su-exec shadow

View File

@@ -1,4 +1,4 @@
FROM node:24.11.0-alpine AS builder
FROM node:24.11.1-alpine AS builder
RUN corepack enable
# Install native dependencies since we might be building cross-platform.
@@ -7,7 +7,7 @@ COPY ./docker/package.json ./docker/pnpm-workspace.yaml /usr/src/app/
# We have to use --no-frozen-lockfile due to CKEditor patches
RUN pnpm install --no-frozen-lockfile --prod && pnpm rebuild
FROM node:24.11.0-alpine
FROM node:24.11.1-alpine
# Create a non-root user with configurable UID/GID
ARG USER=trilium
ARG UID=1001

View File

@@ -1,4 +1,4 @@
FROM node:24.11.0-bullseye-slim AS builder
FROM node:24.11.1-bullseye-slim AS builder
RUN corepack enable
# Install native dependencies since we might be building cross-platform.
@@ -7,7 +7,7 @@ COPY ./docker/package.json ./docker/pnpm-workspace.yaml /usr/src/app/
# We have to use --no-frozen-lockfile due to CKEditor patches
RUN pnpm install --no-frozen-lockfile --prod && pnpm rebuild
FROM node:24.11.0-bullseye-slim
FROM node:24.11.1-bullseye-slim
# Create a non-root user with configurable UID/GID
ARG USER=trilium
ARG UID=1001

View File

@@ -104,7 +104,7 @@
"is-animated": "2.0.2",
"is-svg": "6.1.0",
"jimp": "1.6.0",
"js-yaml": "4.1.0",
"js-yaml": "4.1.1",
"marked": "16.4.2",
"mime-types": "3.0.1",
"multer": "2.0.2",

View File

@@ -14,7 +14,7 @@
"preact": "10.27.2",
"preact-iso": "2.11.0",
"preact-render-to-string": "6.6.3",
"react-i18next": "16.2.4"
"react-i18next": "16.3.1"
},
"devDependencies": {
"@preact/preset-vite": "2.10.2",

View File

@@ -43,7 +43,7 @@
"@playwright/test": "1.56.1",
"@triliumnext/server": "workspace:*",
"@types/express": "5.0.5",
"@types/node": "24.10.0",
"@types/node": "24.10.1",
"@vitest/coverage-v8": "3.2.4",
"@vitest/ui": "3.2.4",
"chalk": "5.6.2",
@@ -83,7 +83,7 @@
"url": "https://github.com/TriliumNext/Trilium/issues"
},
"homepage": "https://triliumnotes.org",
"packageManager": "pnpm@10.21.0",
"packageManager": "pnpm@10.22.0",
"pnpm": {
"patchedDependencies": {
"@ckeditor/ckeditor5-mention": "patches/@ckeditor__ckeditor5-mention.patch",

View File

@@ -15,7 +15,7 @@
"ckeditor5-premium-features": "47.2.0"
},
"devDependencies": {
"@smithy/middleware-retry": "4.4.7",
"@smithy/middleware-retry": "4.4.10",
"@types/jquery": "3.5.33"
}
}

773
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff