Compare commits

..

1 Commits

Author SHA1 Message Date
renovate[bot]
00368fc131 fix(deps): update dependency node-html-parser to v7.1.0 2026-03-03 02:01:49 +00:00
22 changed files with 54 additions and 3392 deletions

View File

@@ -35,8 +35,6 @@
"@triliumnext/highlightjs": "workspace:*",
"@triliumnext/share-theme": "workspace:*",
"@triliumnext/split.js": "workspace:*",
"@univerjs/preset-sheets-core": "0.16.1",
"@univerjs/presets": "0.16.1",
"@zumer/snapdom": "2.0.2",
"autocomplete.js": "0.38.1",
"bootstrap": "5.3.8",

View File

@@ -18,7 +18,7 @@ const RELATION = "relation";
* end user. Those types should be used only for checking against, they are
* not for direct use.
*/
export type NoteType = "file" | "image" | "search" | "noteMap" | "launcher" | "doc" | "contentWidget" | "text" | "relationMap" | "render" | "canvas" | "mermaid" | "book" | "webView" | "code" | "mindMap" | "spreadsheet";
export type NoteType = "file" | "image" | "search" | "noteMap" | "launcher" | "doc" | "contentWidget" | "text" | "relationMap" | "render" | "canvas" | "mermaid" | "book" | "webView" | "code" | "mindMap";
export interface NotePathRecord {
isArchived: boolean;

View File

@@ -1,9 +1,9 @@
import type { NoteType } from "../entities/fnote.js";
import type { MenuCommandItem, MenuItem, MenuItemBadge, MenuSeparatorItem } from "../menus/context_menu.js";
import type { TreeCommandNames } from "../menus/tree_context_menu.js";
import froca from "./froca.js";
import { t } from "./i18n.js";
import froca from "./froca.js";
import server from "./server.js";
import type { MenuCommandItem, MenuItem, MenuItemBadge, MenuSeparatorItem } from "../menus/context_menu.js";
import type { NoteType } from "../entities/fnote.js";
import type { TreeCommandNames } from "../menus/tree_context_menu.js";
export interface NoteTypeMapping {
type: NoteType;
@@ -26,7 +26,6 @@ export const NOTE_TYPES: NoteTypeMapping[] = [
// The default note type (always the first item)
{ type: "text", mime: "text/html", title: t("note_types.text"), icon: "bx-note" },
{ type: "spreadsheet", mime: "application/json", title: t("note_types.spreadsheet"), icon: "bx-table" },
// Text notes group
{ type: "book", mime: "", title: t("note_types.book"), icon: "bx-book" },
@@ -97,9 +96,9 @@ function getBlankNoteTypes(command?: TreeCommandNames): MenuItem<TreeCommandName
title: nt.title,
command,
type: nt.type,
uiIcon: `bx ${ nt.icon}`,
uiIcon: "bx " + nt.icon,
badges: []
};
}
if (nt.isNew) {
menuItem.badges?.push(NEW_BADGE);
@@ -131,7 +130,7 @@ async function getUserTemplates(command?: TreeCommandNames) {
const item: MenuItem<TreeCommandNames> = {
title: templateNote.title,
uiIcon: templateNote.getIcon(),
command,
command: command,
type: templateNote.type,
templateNoteId: templateNote.noteId
};
@@ -160,7 +159,7 @@ async function getBuiltInTemplates(title: string | null, command: TreeCommandNam
const items: MenuItem<TreeCommandNames>[] = [];
if (title) {
items.push({
title,
title: title,
kind: "header"
});
} else {
@@ -176,7 +175,7 @@ async function getBuiltInTemplates(title: string | null, command: TreeCommandNam
const item: MenuItem<TreeCommandNames> = {
title: templateNote.title,
uiIcon: templateNote.getIcon(),
command,
command: command,
type: templateNote.type,
templateNoteId: templateNote.noteId
};
@@ -194,7 +193,7 @@ async function isNewTemplate(templateNoteId) {
if (rootCreationDate === undefined) {
// Retrieve the root note creation date
try {
const rootNoteInfo: any = await server.get("notes/root");
let rootNoteInfo: any = await server.get("notes/root");
if ("dateCreated" in rootNoteInfo) {
rootCreationDate = new Date(rootNoteInfo.dateCreated);
}
@@ -209,7 +208,7 @@ async function isNewTemplate(templateNoteId) {
if (creationDate === undefined) {
// The creation date isn't available in the cache, try to retrieve it from the server
try {
const noteInfo: any = await server.get(`notes/${ templateNoteId}`);
const noteInfo: any = await server.get("notes/" + templateNoteId);
if ("dateCreated" in noteInfo) {
creationDate = new Date(noteInfo.dateCreated);
creationDateCache.set(templateNoteId, creationDate);
@@ -231,9 +230,9 @@ async function isNewTemplate(templateNoteId) {
const age = (new Date().getTime() - creationDate.getTime()) / DAY_LENGTH;
// Return true if the template is at most NEW_TEMPLATE_MAX_AGE days old
return (age <= NEW_TEMPLATE_MAX_AGE);
} else {
return false;
}
return false;
}
export default {

View File

@@ -12,7 +12,6 @@ export default class SpacedUpdate {
private updateInterval: number;
private changeForbidden?: boolean;
private stateCallback?: StateCallback;
private lastState: SaveState = "saved";
constructor(updater: Callback, updateInterval = 1000, stateCallback?: StateCallback) {
this.updater = updater;
@@ -25,7 +24,7 @@ export default class SpacedUpdate {
scheduleUpdate() {
if (!this.changeForbidden) {
this.changed = true;
this.onStateChanged("unsaved");
this.stateCallback?.("unsaved");
setTimeout(() => this.triggerUpdate());
}
}
@@ -35,12 +34,12 @@ export default class SpacedUpdate {
this.changed = false; // optimistic...
try {
this.onStateChanged("saving");
this.stateCallback?.("saving");
await this.updater();
this.onStateChanged("saved");
this.stateCallback?.("saved");
} catch (e) {
this.changed = true;
this.onStateChanged("error");
this.stateCallback?.("error");
logError(getErrorMessage(e));
throw e;
}
@@ -77,13 +76,13 @@ export default class SpacedUpdate {
}
if (Date.now() - this.lastUpdated > this.updateInterval) {
this.onStateChanged("saving");
this.stateCallback?.("saving");
try {
await this.updater();
this.onStateChanged("saved");
this.stateCallback?.("saved");
this.changed = false;
} catch (e) {
this.onStateChanged("error");
this.stateCallback?.("error");
logError(getErrorMessage(e));
}
this.lastUpdated = Date.now();
@@ -93,13 +92,6 @@ export default class SpacedUpdate {
}
}
onStateChanged(state: SaveState) {
if (state === this.lastState) return;
this.stateCallback?.(state);
this.lastState = state;
}
async allowUpdateWithoutChange(callback: Callback) {
this.changeForbidden = true;

View File

@@ -1582,8 +1582,7 @@
"ai-chat": "AI Chat",
"task-list": "Task List",
"new-feature": "New",
"collections": "Collections",
"spreadsheet": "Spreadsheet"
"collections": "Collections"
},
"protect_note": {
"toggle-on": "Protect the note",

View File

@@ -4,7 +4,6 @@
overflow: visible;
contain: none !important;
clear: both;
&.full-height {
overflow: auto;

View File

@@ -54,8 +54,6 @@ export default function PopupEditor() {
}
});
// Events triggered at note context level (e.g. the save indicator) would not work since the note context has no parent component. Propagate events to parent component so that they can be handled properly.
noteContext.triggerEvent = (name, data) => parentComponent?.handleEventInChildren(name, data);
setNoteContext(noteContext);
setShown(true);
});

View File

@@ -7,7 +7,7 @@ import { t } from "../../services/i18n";
import { goToLinkExt } from "../../services/link";
import { Badge, BadgeWithDropdown } from "../react/Badge";
import { FormDropdownDivider, FormListItem } from "../react/FormList";
import { useGetContextDataFrom, useIsNoteReadOnly, useNoteContext, useNoteLabel, useNoteLabelBoolean } from "../react/hooks";
import { useGetContextData, useIsNoteReadOnly, useNoteContext, useNoteLabel, useNoteLabelBoolean } from "../react/hooks";
import { useShareState } from "../ribbon/BasicPropertiesTab";
import { useShareInfo } from "../shared_info";
import { ActiveContentBadges } from "./ActiveContentBadges";
@@ -112,8 +112,7 @@ function ExecuteBadge() {
}
export function SaveStatusBadge() {
const { noteContext} = useNoteContext();
const saveState = useGetContextDataFrom(noteContext, "saveState");
const saveState = useGetContextData("saveState");
if (!saveState) return;
const stateConfig = {

View File

@@ -141,11 +141,5 @@ export const TYPE_MAPPINGS: Record<ExtendedNoteType, NoteTypeMapping> = {
view: () => import("./type_widgets/SqlConsole"),
className: "sql-console-widget-container",
isFullHeight: true
},
spreadsheet: {
view: () => import("./type_widgets/Spreadsheet"),
className: "note-detail-spreadsheet",
printable: true,
isFullHeight: true
}
};

View File

@@ -79,7 +79,7 @@ export default class NoteWrapperWidget extends FlexContainer<BasicWidget> {
}
#isFullWidthNote(note: FNote) {
if (["code", "image", "mermaid", "book", "render", "canvas", "webView", "mindMap", "spreadsheet"].includes(note.type)) {
if (["code", "image", "mermaid", "book", "render", "canvas", "webView", "mindMap"].includes(note.type)) {
return true;
}
@@ -102,7 +102,7 @@ export default class NoteWrapperWidget extends FlexContainer<BasicWidget> {
const COLLECTIONS_WITH_BACKGROUND_EFFECTS = [
"grid",
"list"
];
]
if (note.isOptions()) {
return true;

View File

@@ -85,7 +85,7 @@ export function NoteContextMenu({ note, noteContext, itemsAtStart, itemsNearNote
);
const isElectron = getIsElectron();
const isMac = getIsMac();
const hasSource = ["text", "code", "relationMap", "mermaid", "canvas", "mindMap", "spreadsheet"].includes(noteType);
const hasSource = ["text", "code", "relationMap", "mermaid", "canvas", "mindMap"].includes(noteType);
const isSearchOrBook = ["search", "book"].includes(noteType);
const isHelpPage = note.noteId.startsWith("_help");
const [syncServerHost] = useTriliumOption("syncServerHost");

View File

@@ -1,3 +0,0 @@
.note-detail-spreadsheet > .spreadsheet {
height: 100%;
}

View File

@@ -1,122 +0,0 @@
import "@univerjs/preset-sheets-core/lib/index.css";
import "./Spreadsheet.css";
import { UniverSheetsCorePreset } from '@univerjs/preset-sheets-core';
import UniverPresetSheetsCoreEnUS from '@univerjs/preset-sheets-core/locales/en-US';
import { CommandType, createUniver, FUniver, IWorkbookData, LocaleType, mergeLocales } from '@univerjs/presets';
import { MutableRef, useEffect, useRef, useState } from "preact/hooks";
import NoteContext from "../../components/note_context";
import FNote from "../../entities/fnote";
import { useColorScheme, useEditorSpacedUpdate } from "../react/hooks";
import { TypeWidgetProps } from "./type_widget";
interface PersistedData {
version: number;
workbook: Parameters<FUniver["createWorkbook"]>[0];
}
export default function Spreadsheet({ note, noteContext }: TypeWidgetProps) {
const containerRef = useRef<HTMLDivElement>(null);
const apiRef = useRef<FUniver>();
useInitializeSpreadsheet(containerRef, apiRef);
useDarkMode(apiRef);
usePersistence(note, noteContext, apiRef);
return <div ref={containerRef} className="spreadsheet" />;
}
function useInitializeSpreadsheet(containerRef: MutableRef<HTMLDivElement | null>, apiRef: MutableRef<FUniver | undefined>) {
useEffect(() => {
if (!containerRef.current) return;
const { univerAPI } = createUniver({
locale: LocaleType.EN_US,
locales: {
[LocaleType.EN_US]: mergeLocales(
UniverPresetSheetsCoreEnUS
),
},
presets: [
UniverSheetsCorePreset({
container: containerRef.current,
})
]
});
apiRef.current = univerAPI;
return () => univerAPI.dispose();
}, [ apiRef, containerRef ]);
}
function useDarkMode(apiRef: MutableRef<FUniver | undefined>) {
const colorScheme = useColorScheme();
// React to dark mode.
useEffect(() => {
const univerAPI = apiRef.current;
if (!univerAPI) return;
univerAPI.toggleDarkMode(colorScheme === 'dark');
}, [ colorScheme, apiRef ]);
}
function usePersistence(note: FNote, noteContext: NoteContext | null | undefined, apiRef: MutableRef<FUniver | undefined>) {
const [ workbookLoaded, setWorkbookLoaded ] = useState(false);
const spacedUpdate = useEditorSpacedUpdate({
noteType: "spreadsheet",
note,
noteContext,
getData() {
const univerAPI = apiRef.current;
if (!univerAPI) return undefined;
const workbook = univerAPI.getActiveWorkbook();
if (!workbook) return undefined;
const content = {
version: 1,
workbook: workbook.save()
};
return {
content: JSON.stringify(content)
};
},
onContentChange(newContent) {
const univerAPI = apiRef.current;
if (!univerAPI) return undefined;
// Dispose the existing workbook.
const existingNotebook = univerAPI.getActiveWorkbook();
if (existingNotebook) {
univerAPI.disposeUnit(existingNotebook.getId());
}
let workbook: Partial<IWorkbookData> = {};
if (newContent) {
try {
const parsedContent = JSON.parse(newContent) as unknown;
if (parsedContent && typeof parsedContent === "object" && "workbook" in parsedContent) {
const persistedData = parsedContent as PersistedData;
workbook = persistedData.workbook;
}
} catch (e) {
console.error("Failed to parse spreadsheet content", e);
}
}
univerAPI.createWorkbook(workbook);
setWorkbookLoaded(true);
},
});
useEffect(() => {
const univerAPI = apiRef.current;
const workbook = apiRef.current?.getActiveWorkbook();
if (!univerAPI || !workbook) return;
const disposable = workbook.onCommandExecuted(command => {
if (command.type !== CommandType.MUTATION) return;
spacedUpdate.scheduleUpdate();
});
return () => disposable.dispose();
}, [ apiRef, spacedUpdate, workbookLoaded ]);
}

View File

@@ -31,7 +31,7 @@
"dependencies": {
"better-sqlite3": "12.6.2",
"html-to-text": "9.0.5",
"node-html-parser": "7.0.2",
"node-html-parser": "7.1.0",
"sucrase": "3.35.1"
},
"devDependencies": {

View File

@@ -57,7 +57,7 @@ function importFile(taskContext: TaskContext<"importNotes">, file: File, parentN
const mime = mimeService.getMime(originalName) || file.mimetype;
const { note } = noteService.createNewNote({
parentNoteId: parentNote.noteId,
title: getNoteTitle(originalName, mime === "application/pdf", { mime }),
title: getNoteTitle(originalName, mime === "application/pdf"),
content: file.buffer,
isProtected: parentNote.isProtected && protectedSessionService.isProtectedSessionAvailable(),
type: "file",

View File

@@ -14,8 +14,7 @@ const noteTypes = [
{ type: "launcher", defaultMime: "" },
{ type: "doc", defaultMime: "" },
{ type: "contentWidget", defaultMime: "" },
{ type: "mindMap", defaultMime: "application/json" },
{ type: "spreadsheet", defaultMime: "application/json" }
{ type: "mindMap", defaultMime: "application/json" }
];
function getDefaultMimeForNoteType(typeName: string) {

View File

@@ -204,13 +204,9 @@ export function formatDownloadTitle(fileName: string, type: string | null, mime:
return `${fileNameBase}${getExtension()}`;
}
export function removeFileExtension(filePath: string, mime?: string) {
export function removeFileExtension(filePath: string) {
const extension = path.extname(filePath).toLowerCase();
if (mime?.startsWith("video/") || mime?.startsWith("audio/")) {
return filePath.substring(0, filePath.length - extension.length);
}
switch (extension) {
case ".md":
case ".mdx":
@@ -231,7 +227,7 @@ export function getNoteTitle(filePath: string, replaceUnderscoresWithSpaces: boo
const trimmedNoteMeta = noteMeta?.title?.trim();
if (trimmedNoteMeta) return trimmedNoteMeta;
const basename = path.basename(removeFileExtension(filePath, noteMeta?.mime));
const basename = path.basename(removeFileExtension(filePath));
return replaceUnderscoresWithSpaces ? basename.replace(/_/g, " ").trim() : basename;
}

View File

@@ -20,8 +20,7 @@ export const NOTE_TYPE_ICONS = {
launcher: "bx bx-link",
doc: "bx bxs-file-doc",
contentWidget: "bx bxs-widget",
mindMap: "bx bx-sitemap",
spreadsheet: "bx bx-table"
mindMap: "bx bx-sitemap"
};
const FILE_MIME_MAPPINGS = {
@@ -60,8 +59,6 @@ export function getNoteIcon({ noteId, type, mime, iconClass, workspaceIconClass,
const correspondingMimeType = MIME_TYPES_DICT.find(m => m.mime === mime);
return correspondingMimeType?.icon ?? NOTE_TYPE_ICONS.code;
} else if (type === "file") {
if (mime.startsWith("video/")) return "bx bx-video";
if (mime.startsWith("audio/")) return "bx bx-music";
return FILE_MIME_MAPPINGS[mime] ?? NOTE_TYPE_ICONS.file;
} else if (type === "image") {
return IMAGE_MIME_MAPPINGS[mime] ?? NOTE_TYPE_ICONS.image;

View File

@@ -119,8 +119,7 @@ export const ALLOWED_NOTE_TYPES = [
"book",
"webView",
"code",
"mindMap",
"spreadsheet"
"mindMap"
] as const;
export type NoteType = (typeof ALLOWED_NOTE_TYPES)[number];

View File

@@ -1,6 +1,6 @@
{
"name": "@triliumnext/pdfjs-viewer",
"version": "0.102.0",
"version": "1.0.0",
"private": true,
"scripts": {
"build": "tsx scripts/build.ts",
@@ -12,4 +12,4 @@
"devDependencies": {
"pdfjs-dist": "5.4.624"
}
}
}

3216
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -30,7 +30,7 @@ function main() {
patchPackageJson(join(__dirname, "..", "apps", appName, "package.json"), version);
}
for (const packageName of ["commons", "pdfjs-viewer"]) {
for (const packageName of ["commons"]) {
patchPackageJson(join(__dirname, "..", "packages", packageName, "package.json"), version);
}
}