Skip to content

Feature/export with share theme #5830

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 32 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
2c6ba9b
refactor(share): extract note rendering logic
eliandoran Jun 13, 2025
9c460db
feat(export/zip): get same rendering engine as share
eliandoran Jun 13, 2025
f189deb
feat(export/zip): get tree to render
eliandoran Jun 13, 2025
4d5e866
feat(export/zip): get CSS to load
eliandoran Jun 13, 2025
d8958ad
feat(export/zip): basic tree navigation
eliandoran Jun 13, 2025
01a552c
feat(export/zip): get boxicons to work
eliandoran Jun 13, 2025
d3115e8
feat(export/zip): get logo to work
eliandoran Jun 13, 2025
01beebf
feat(export/zip): load script as well
eliandoran Jun 13, 2025
968c75b
Merge remote-tracking branch 'origin/main' into feature/export_with_s…
eliandoran Jun 23, 2025
c519672
chore(nx): sync tsconfig
eliandoran Jun 23, 2025
dfd575b
refactor(export/zip): extract into separate provider
eliandoran Jun 23, 2025
e529633
chore(export/zip): bring back markdown exporter
eliandoran Jun 23, 2025
55bb2fd
refactor(export/zip): extract prepare content into providers
eliandoran Jun 23, 2025
a9f68f5
feat(export/zip): add option to export with share theme
eliandoran Jun 23, 2025
acb0991
refactor(export/zip): separate building provider into own method
eliandoran Jun 23, 2025
0efdf65
refactor(export/share): build index file
eliandoran Jun 23, 2025
8523050
fix(export/share): note children preview links not working
eliandoran Jun 23, 2025
77e4c3d
refactor(export/share): use different URL rewriting mechanism
eliandoran Jun 23, 2025
35622a2
feat(export/share): always render empty files
eliandoran Jun 23, 2025
b475037
feat(export/share): render non-text note types
eliandoran Jun 23, 2025
61dbc15
feat(export/share): use translation
eliandoran Jun 23, 2025
9bc9664
fix(edit-docs): import error
eliandoran Jun 23, 2025
413137a
chore(nx): sync tsconfig
eliandoran Jun 23, 2025
a2110ca
fix(export/share): tree not expanding properly
eliandoran Jun 24, 2025
bc4643f
refactor(share): use internal rendering method for subtemplates
eliandoran Jun 24, 2025
3a55490
refactor(share): use a string cache for templates
eliandoran Jun 24, 2025
6d446c5
fix(export/share): asset path in prod
eliandoran Jun 24, 2025
3ebfee8
fix(export/share): tree error in prod
eliandoran Jun 24, 2025
9abdbbb
refactor(export/share): fix type
eliandoran Jun 24, 2025
06de06b
refactor(export/share): share type for format
eliandoran Jun 24, 2025
fded714
fix(export/share): use right extension for images
eliandoran Jun 24, 2025
9cf7fa1
fix(export/share): use right extension for clones
eliandoran Jun 24, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions apps/client/src/translations/en/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,8 @@
"export_status": "Export status",
"export_in_progress": "Export in progress: {{progressCount}}",
"export_finished_successfully": "Export finished successfully.",
"format_pdf": "PDF - for printing or sharing purposes."
"format_pdf": "PDF - for printing or sharing purposes.",
"share-format": "HTML for web publishing - uses the same theme that is used shared notes, but can be published as a static website."
},
"help": {
"fullDocumentation": "Help (full documentation is available <a class=\"external\" href=\"https://triliumnext.github.io/Docs/\">online</a>)",
Expand Down Expand Up @@ -1197,7 +1198,6 @@
"restore_provider": "Restore provider to search",
"similarity_threshold": "Similarity Threshold",
"similarity_threshold_description": "Minimum similarity score (0-1) for notes to be included in context for LLM queries",

"reprocess_index": "Rebuild Search Index",
"reprocessing_index": "Rebuilding...",
"reprocess_index_started": "Search index optimization started in the background",
Expand Down
7 changes: 7 additions & 0 deletions apps/client/src/widgets/dialogs/export.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,13 @@ const TPL = /*html*/`
</label>
</div>
</div>

<div class="form-check">
<label class="form-check-label tn-radio">
<input class="form-check-input" type="radio" name="export-subtree-format" value="share">
${t("export.share-format")}
</label>
</div>
</div>

<div class="form-check">
Expand Down
4 changes: 2 additions & 2 deletions apps/edit-docs/src/edit-docs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { initializeTranslations } from "@triliumnext/server/src/services/i18n.js
import debounce from "@triliumnext/client/src/services/debounce.js";
import { extractZip, importData, initializeDatabase, startElectron } from "./utils.js";
import cls from "@triliumnext/server/src/services/cls.js";
import type { AdvancedExportOptions } from "@triliumnext/server/src/services/export/zip.js";
import type { AdvancedExportOptions, ExportFormat } from "@triliumnext/server/src/services/export/zip/abstract_provider.js";
import { parseNoteMetaFile } from "@triliumnext/server/src/services/in_app_help.js";
import type NoteMeta from "@triliumnext/server/src/services/meta/note_meta.js";

Expand Down Expand Up @@ -75,7 +75,7 @@ async function setOptions() {
optionsService.setOption("compressImages", "false");
}

async function exportData(noteId: string, format: "html" | "markdown", outputPath: string, ignoredFiles?: Set<string>) {
async function exportData(noteId: string, format: ExportFormat, outputPath: string, ignoredFiles?: Set<string>) {
const zipFilePath = "output.zip";

try {
Expand Down
5 changes: 5 additions & 0 deletions apps/server/src/becca/entities/bbranch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,11 @@ class BBranch extends AbstractBeccaEntity<BBranch> {
});
}
}

getParentNote() {
return this.parentNote;
}

}

export default BBranch;
20 changes: 20 additions & 0 deletions apps/server/src/becca/entities/bnote.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1758,6 +1758,26 @@ class BNote extends AbstractBeccaEntity<BNote> {
return childBranches;
}

get encodedTitle() {
return encodeURIComponent(this.title);
}

getVisibleChildBranches() {
return this.getChildBranches().filter((branch) => !branch.getNote().isLabelTruthy("shareHiddenFromTree"));
}

getVisibleChildNotes() {
return this.getVisibleChildBranches().map((branch) => branch.getNote());
}

hasVisibleChildren() {
return this.getVisibleChildNotes().length > 0;
}

get shareId() {
return this.noteId;
}

}

export default BNote;
5 changes: 3 additions & 2 deletions apps/server/src/etapi/notes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import type { ParsedQs } from "qs";
import type { NoteParams } from "../services/note-interface.js";
import type { SearchParams } from "../services/search/services/types.js";
import type { ValidatorMap } from "./etapi-interface.js";
import type { ExportFormat } from "../services/export/zip/abstract_provider.js";

function register(router: Router) {
eu.route(router, "get", "/etapi/notes", (req, res, next) => {
Expand Down Expand Up @@ -147,7 +148,7 @@ function register(router: Router) {
const note = eu.getAndCheckNote(req.params.noteId);
const format = req.query.format || "html";

if (typeof format !== "string" || !["html", "markdown"].includes(format)) {
if (typeof format !== "string" || !["html", "markdown", "share"].includes(format)) {
throw new eu.EtapiError(400, "UNRECOGNIZED_EXPORT_FORMAT", `Unrecognized export format '${format}', supported values are 'html' (default) or 'markdown'.`);
}

Expand All @@ -157,7 +158,7 @@ function register(router: Router) {
// (e.g. branchIds are not seen in UI), that we export "note export" instead.
const branch = note.getParentBranches()[0];

zipExportService.exportToZip(taskContext, branch, format as "html" | "markdown", res);
zipExportService.exportToZip(taskContext, branch, format as ExportFormat, res);
});

eu.route(router, "post", "/etapi/notes/:noteId/import", (req, res, next) => {
Expand Down
2 changes: 1 addition & 1 deletion apps/server/src/routes/api/export.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ function exportBranch(req: Request, res: Response) {
const taskContext = new TaskContext(taskId, "export");

try {
if (type === "subtree" && (format === "html" || format === "markdown")) {
if (type === "subtree" && (format === "html" || format === "markdown" || format === "share")) {
zipExportService.exportToZip(taskContext, branch, format, res);
} else if (type === "single") {
if (format !== "html" && format !== "markdown") {
Expand Down
5 changes: 3 additions & 2 deletions apps/server/src/services/export/single.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,9 @@ import type TaskContext from "../task_context.js";
import type BBranch from "../../becca/entities/bbranch.js";
import type { Response } from "express";
import type BNote from "../../becca/entities/bnote.js";
import type { ExportFormat } from "./zip/abstract_provider.js";

function exportSingleNote(taskContext: TaskContext, branch: BBranch, format: "html" | "markdown", res: Response) {
function exportSingleNote(taskContext: TaskContext, branch: BBranch, format: ExportFormat, res: Response) {
const note = branch.getNote();

if (note.type === "image" || note.type === "file") {
Expand All @@ -33,7 +34,7 @@ function exportSingleNote(taskContext: TaskContext, branch: BBranch, format: "ht
taskContext.taskSucceeded();
}

export function mapByNoteType(note: BNote, content: string | Buffer<ArrayBufferLike>, format: "html" | "markdown") {
export function mapByNoteType(note: BNote, content: string | Buffer<ArrayBufferLike>, format: ExportFormat) {
let payload, extension, mime;

if (typeof content !== "string") {
Expand Down
Loading
Loading