diff --git a/apps/client/src/translations/en/translation.json b/apps/client/src/translations/en/translation.json
index 796e4c08fa..9fbf88bc26 100644
--- a/apps/client/src/translations/en/translation.json
+++ b/apps/client/src/translations/en/translation.json
@@ -310,7 +310,12 @@
"description_placeholder": "Add a description (optional)",
"revision_saved": "Note revision has been saved.",
"edit_description": "Edit description",
- "description_updated": "Revision description has been updated."
+ "description_updated": "Revision description has been updated.",
+ "source_auto": "Auto",
+ "source_manual": "Manual",
+ "source_etapi": "ETAPI",
+ "source_llm": "LLM",
+ "source_restore": "Restore"
},
"sort_child_notes": {
"sort_children_by": "Sort children by...",
diff --git a/apps/client/src/widgets/dialogs/revisions.tsx b/apps/client/src/widgets/dialogs/revisions.tsx
index 06a44ed859..b3d3803960 100644
--- a/apps/client/src/widgets/dialogs/revisions.tsx
+++ b/apps/client/src/widgets/dialogs/revisions.tsx
@@ -200,7 +200,23 @@ function RevisionsList({ revisions, onSelect, currentRevision }: { revisions: Re
active={currentRevision && item.revisionId === currentRevision.revisionId}
>
- {item.dateCreated && item.dateCreated.substr(0, 16)} ({item.contentLength && utils.formatSize(item.contentLength)})
+
+ {item.dateCreated && item.dateCreated.substr(0, 16)}
+ {item.source && item.source !== "auto" && (
+
+ {t(`revisions.source_${item.source}`)}
+
+ )}
+
+
+ {item.contentLength && utils.formatSize(item.contentLength)}
+
{item.description && (
{item.description}
diff --git a/apps/server/src/assets/db/schema.sql b/apps/server/src/assets/db/schema.sql
index 361a6ccdf4..60f0485471 100644
--- a/apps/server/src/assets/db/schema.sql
+++ b/apps/server/src/assets/db/schema.sql
@@ -49,6 +49,7 @@ CREATE TABLE IF NOT EXISTS "revisions" (`revisionId` TEXT NOT NULL PRIMARY KEY,
mime TEXT DEFAULT '' NOT NULL,
`title` TEXT NOT NULL,
`description` TEXT DEFAULT '' NOT NULL,
+ `source` TEXT DEFAULT 'auto' NOT NULL,
`isProtected` INT NOT NULL DEFAULT 0,
blobId TEXT DEFAULT NULL,
`utcDateLastEdited` TEXT NOT NULL,
diff --git a/apps/server/src/becca/entities/bnote.ts b/apps/server/src/becca/entities/bnote.ts
index 63b447c2b3..56251e76ea 100644
--- a/apps/server/src/becca/entities/bnote.ts
+++ b/apps/server/src/becca/entities/bnote.ts
@@ -1,4 +1,4 @@
-import type { AttachmentRow, AttributeType, CloneResponse, NoteRow, NoteType, RevisionRow } from "@triliumnext/commons";
+import type { AttachmentRow, AttributeType, CloneResponse, NoteRow, NoteType, RevisionRow, RevisionSource } from "@triliumnext/commons";
import { dayjs, getNoteIcon } from "@triliumnext/commons";
import cloningService from "../../services/cloning.js";
@@ -1543,7 +1543,7 @@ class BNote extends AbstractBeccaEntity {
return !(this.noteId in this.becca.notes) || this.isBeingDeleted;
}
- saveRevision(description?: string): BRevision {
+ saveRevision(opts: { description?: string; source?: RevisionSource } = {}): BRevision {
return sql.transactional(() => {
let noteContent = this.getContent();
@@ -1552,7 +1552,8 @@ class BNote extends AbstractBeccaEntity {
noteId: this.noteId,
// title and text should be decrypted now
title: this.title,
- description: description || "",
+ description: opts.description || "",
+ source: opts.source || "auto",
type: this.type,
mime: this.mime,
isProtected: this.isProtected,
diff --git a/apps/server/src/becca/entities/brevision.ts b/apps/server/src/becca/entities/brevision.ts
index d9bf2e1c72..4e90471cd2 100644
--- a/apps/server/src/becca/entities/brevision.ts
+++ b/apps/server/src/becca/entities/brevision.ts
@@ -7,7 +7,7 @@ import becca from "../becca.js";
import AbstractBeccaEntity from "./abstract_becca_entity.js";
import sql from "../../services/sql.js";
import BAttachment from "./battachment.js";
-import type { AttachmentRow, NoteType, RevisionPojo, RevisionRow } from "@triliumnext/commons";
+import type { AttachmentRow, NoteType, RevisionPojo, RevisionRow, RevisionSource } from "@triliumnext/commons";
import eraseService from "../../services/erase.js";
interface ContentOpts {
@@ -31,7 +31,7 @@ class BRevision extends AbstractBeccaEntity {
return "revisionId";
}
static get hashedProperties() {
- return ["revisionId", "noteId", "title", "description", "isProtected", "dateLastEdited", "dateCreated", "utcDateLastEdited", "utcDateCreated", "utcDateModified", "blobId"];
+ return ["revisionId", "noteId", "title", "description", "source", "isProtected", "dateLastEdited", "dateCreated", "utcDateLastEdited", "utcDateCreated", "utcDateModified", "blobId"];
}
revisionId?: string;
@@ -40,6 +40,7 @@ class BRevision extends AbstractBeccaEntity {
mime!: string;
title!: string;
description!: string;
+ source!: RevisionSource;
dateLastEdited?: string;
utcDateLastEdited?: string;
contentLength?: number;
@@ -63,6 +64,7 @@ class BRevision extends AbstractBeccaEntity {
this.isProtected = !!row.isProtected;
this.title = row.title;
this.description = row.description || "";
+ this.source = row.source || "auto";
this.blobId = row.blobId;
this.dateLastEdited = row.dateLastEdited;
this.dateCreated = row.dateCreated;
@@ -196,6 +198,7 @@ class BRevision extends AbstractBeccaEntity {
isProtected: this.isProtected,
title: this.title,
description: this.description,
+ source: this.source,
blobId: this.blobId,
dateLastEdited: this.dateLastEdited,
dateCreated: this.dateCreated,
diff --git a/apps/server/src/etapi/mappers.ts b/apps/server/src/etapi/mappers.ts
index bff50af5c6..04e59b1000 100644
--- a/apps/server/src/etapi/mappers.ts
+++ b/apps/server/src/etapi/mappers.ts
@@ -74,6 +74,7 @@ function mapRevisionToPojo(revision: BRevision) {
isProtected: revision.isProtected,
title: revision.title,
description: revision.description,
+ source: revision.source,
blobId: revision.blobId,
dateLastEdited: revision.dateLastEdited,
dateCreated: revision.dateCreated,
diff --git a/apps/server/src/etapi/notes.ts b/apps/server/src/etapi/notes.ts
index 96206f2dc6..8edc583435 100644
--- a/apps/server/src/etapi/notes.ts
+++ b/apps/server/src/etapi/notes.ts
@@ -193,7 +193,7 @@ function register(router: Router) {
const note = eu.getAndCheckNote(req.params.noteId);
const description = req.body?.description || "";
- const revision = note.saveRevision(description);
+ const revision = note.saveRevision({ description, source: "etapi" });
res.status(201).json(mappers.mapRevisionToPojo(revision));
});
diff --git a/apps/server/src/migrations/migrations.ts b/apps/server/src/migrations/migrations.ts
index 93b25be8f1..0c9dd4e8d1 100644
--- a/apps/server/src/migrations/migrations.ts
+++ b/apps/server/src/migrations/migrations.ts
@@ -11,6 +11,7 @@ const MIGRATIONS: (SqlMigration | JsMigration)[] = [
version: 238,
sql: /*sql*/`
ALTER TABLE revisions ADD COLUMN description TEXT DEFAULT '' NOT NULL;
+ ALTER TABLE revisions ADD COLUMN source TEXT DEFAULT 'auto' NOT NULL;
`,
ignoreErrors: true
},
diff --git a/apps/server/src/routes/api/notes.ts b/apps/server/src/routes/api/notes.ts
index 9cca431f91..8aec006a09 100644
--- a/apps/server/src/routes/api/notes.ts
+++ b/apps/server/src/routes/api/notes.ts
@@ -352,7 +352,7 @@ function forceSaveRevision(req: Request<{ noteId: string }>) {
}
const description = req.body?.description || "";
- const revision = note.saveRevision(description);
+ const revision = note.saveRevision({ description, source: "manual" });
return {
revisionId: revision.revisionId
diff --git a/apps/server/src/routes/api/revisions.ts b/apps/server/src/routes/api/revisions.ts
index 8598810791..12e1c990ac 100644
--- a/apps/server/src/routes/api/revisions.ts
+++ b/apps/server/src/routes/api/revisions.ts
@@ -137,7 +137,7 @@ function restoreRevision(req: Request<{ revisionId: string }>) {
const note = revision.getNote();
sql.transactional(() => {
- note.saveRevision();
+ note.saveRevision({ source: "restore" });
for (const oldNoteAttachment of note.getAttachments()) {
oldNoteAttachment.markAsDeleted();
diff --git a/apps/server/src/services/llm/tools/note_tools.ts b/apps/server/src/services/llm/tools/note_tools.ts
index 8a354175f3..d1ce83e3e0 100644
--- a/apps/server/src/services/llm/tools/note_tools.ts
+++ b/apps/server/src/services/llm/tools/note_tools.ts
@@ -116,7 +116,7 @@ export const noteTools = defineTools({
return { error: `Cannot update content for note type: ${note.type}` };
}
- note.saveRevision();
+ note.saveRevision({ source: "llm" });
setNoteContentFromLlm(note, content);
return {
success: true,
@@ -158,7 +158,7 @@ export const noteTools = defineTools({
newContent = existingContent + (existingContent.endsWith("\n") ? "" : "\n") + content;
}
- note.saveRevision();
+ note.saveRevision({ source: "llm" });
note.setContent(newContent);
return {
success: true,
diff --git a/packages/commons/src/lib/rows.ts b/packages/commons/src/lib/rows.ts
index 977120c21f..5f198aa02f 100644
--- a/packages/commons/src/lib/rows.ts
+++ b/packages/commons/src/lib/rows.ts
@@ -21,6 +21,9 @@ export interface AttachmentRow {
encoding?: "base64";
}
+export const REVISION_SOURCES = ["auto", "manual", "etapi", "llm", "restore"] as const;
+export type RevisionSource = (typeof REVISION_SOURCES)[number];
+
export interface RevisionRow {
revisionId?: string;
noteId: string;
@@ -29,6 +32,7 @@ export interface RevisionRow {
isProtected?: boolean;
title: string;
description?: string;
+ source?: RevisionSource;
blobId?: string;
dateLastEdited?: string;
dateCreated?: string;
diff --git a/packages/commons/src/lib/server_api.ts b/packages/commons/src/lib/server_api.ts
index c43b155094..63e5c63680 100644
--- a/packages/commons/src/lib/server_api.ts
+++ b/packages/commons/src/lib/server_api.ts
@@ -1,4 +1,4 @@
-import { AttachmentRow, AttributeRow, BranchRow, NoteRow, NoteType } from "./rows.js";
+import { AttachmentRow, AttributeRow, BranchRow, NoteRow, NoteType, RevisionSource } from "./rows.js";
type Response = {
success: true,
@@ -34,6 +34,7 @@ export interface RevisionItem {
type: NoteType;
title: string;
description?: string;
+ source?: RevisionSource;
isProtected?: boolean;
mime: string;
}
@@ -46,6 +47,7 @@ export interface RevisionPojo {
isProtected?: boolean;
title: string;
description?: string;
+ source?: RevisionSource;
blobId?: string;
dateLastEdited?: string;
dateCreated?: string;