chore(client/ts): port more files

This commit is contained in:
Elian Doran 2025-03-16 18:31:31 +02:00
parent 7f4f8bcc75
commit 4e7572cf04
No known key found for this signature in database
11 changed files with 101 additions and 26 deletions

View File

@ -177,7 +177,7 @@ export type CommandMappings = {
ExecuteCommandData<TextEditor> & {
callback?: GetTextEditorCallback;
};
executeWithCodeEditor: CommandData & ExecuteCommandData<null>;
executeWithCodeEditor: CommandData & ExecuteCommandData<CodeMirrorInstance>;
/**
* Called upon when attempting to retrieve the content element of a {@link NoteContext}.
* Generally should not be invoked manually, as it is used by {@link NoteContext.getContentElement}.
@ -246,7 +246,7 @@ export type CommandMappings = {
toggleZenMode: CommandData;
updateAttributeList: CommandData & { attributes: FAttribute[] };
updateAttributeList: CommandData & { attributes: Attribute[] };
saveAttributes: CommandData;
reloadAttributes: CommandData;
refreshNoteList: CommandData & { noteId: string };

View File

@ -311,7 +311,7 @@ class NoteContext extends Component implements EventListener<"entitiesReloaded">
async getCodeEditor() {
return this.timeout(
new Promise((resolve) =>
new Promise<CodeMirrorInstance>((resolve) =>
appContext.triggerCommand("executeWithCodeEditor", {
resolve,
ntxId: this.ntxId

View File

@ -1,7 +1,7 @@
import utils from "./utils.js";
type ElementType = HTMLElement | Document;
type Handler = (e: JQuery.TriggeredEvent<ElementType, string, ElementType, ElementType>) => void;
type Handler = (e: JQuery.TriggeredEvent<ElementType | Element, string, ElementType | Element, ElementType | Element>) => void;
function removeGlobalShortcut(namespace: string) {
bindGlobalShortcut("", null, namespace);
@ -11,7 +11,7 @@ function bindGlobalShortcut(keyboardShortcut: string, handler: Handler | null, n
bindElShortcut($(document), keyboardShortcut, handler, namespace);
}
function bindElShortcut($el: JQuery<ElementType>, keyboardShortcut: string, handler: Handler | null, namespace: string | null = null) {
function bindElShortcut($el: JQuery<ElementType | Element>, keyboardShortcut: string, handler: Handler | null, namespace: string | null = null) {
if (utils.isDesktop()) {
keyboardShortcut = normalizeShortcut(keyboardShortcut);

View File

@ -184,6 +184,48 @@ declare global {
}
};
var CodeMirror: {
(el: HTMLElement, opts: {
value: string;
viewportMargin: number;
indentUnit: number;
matchBrackets: boolean;
matchTags: { bothTags: boolean };
highlightSelectionMatches: {
showToken: boolean;
annotateScrollbar: boolean;
};
lineNumbers: boolean;
lineWrapping: boolean;
}): CodeMirrorInstance;
keyMap: {
default: Record<string, string>;
};
modeURL: string;
modeInfo: ModeInfo[];
findModeByMIME(mime: string): ModeInfo;
autoLoadMode(instance: CodeMirrorInstance, mode: string)
}
interface ModeInfo {
name: string;
mode: string;
mime: string;
mimes: string[];
}
interface CodeMirrorInstance {
getValue(): string;
setValue(val: string);
clearHistory();
setOption(name: string, value: string);
refresh();
focus();
setCursor(line: number, col: number);
lineCount(): number;
on(event: string, callback: () => void);
}
var katex: {
renderToString(text: string, opts: {
throwOnError: boolean

View File

@ -42,10 +42,21 @@ const TPL = `
const MAX_DISPLAYED_NOTES = 15;
// TODO: Deduplicate with server.
interface QuickSearchResponse {
searchResultNoteIds: string[];
error: string;
}
export default class QuickSearchWidget extends BasicWidget {
private dropdown!: bootstrap.Dropdown;
private $searchString!: JQuery<HTMLElement>;
private $dropdownMenu!: JQuery<HTMLElement>;
doRender() {
this.$widget = $(TPL);
this.dropdown = Dropdown.getOrCreateInstance(this.$widget.find("[data-bs-toggle='dropdown']"));
this.dropdown = Dropdown.getOrCreateInstance(this.$widget.find("[data-bs-toggle='dropdown']")[0]);
this.$searchString = this.$widget.find(".search-string");
this.$dropdownMenu = this.$widget.find(".dropdown-menu");
@ -88,7 +99,7 @@ export default class QuickSearchWidget extends BasicWidget {
}
async search() {
const searchString = this.$searchString.val().trim();
const searchString = String(this.$searchString.val())?.trim();
if (!searchString) {
this.dropdown.hide();
@ -98,10 +109,10 @@ export default class QuickSearchWidget extends BasicWidget {
this.$dropdownMenu.empty();
this.$dropdownMenu.append(`<span class="dropdown-item disabled"><span class="bx bx-loader bx-spin"></span>${t("quick-search.searching")}</span>`);
const { searchResultNoteIds, error } = await server.get(`quick-search/${encodeURIComponent(searchString)}`);
const { searchResultNoteIds, error } = await server.get<QuickSearchResponse>(`quick-search/${encodeURIComponent(searchString)}`);
if (error) {
let tooltip = new Tooltip(this.$searchString, {
let tooltip = new Tooltip(this.$searchString[0], {
trigger: "manual",
title: `Search error: ${error}`,
placement: "right"
@ -164,7 +175,7 @@ export default class QuickSearchWidget extends BasicWidget {
this.dropdown.hide();
await appContext.triggerCommand("searchNotes", {
searchString: this.$searchString.val()
searchString: String(this.$searchString.val())
});
}

View File

@ -4,6 +4,7 @@ import froca from "../../services/froca.js";
import NoteContextAwareWidget from "../note_context_aware_widget.js";
import options from "../../services/options.js";
import { t } from "../../services/i18n.js";
import type FNote from "../../entities/fnote.js";
const TPL = `
<div class="edited-notes-widget">
@ -15,27 +16,39 @@ const TPL = `
overflow: auto;
}
</style>
<div class="no-edited-notes-found">${t("edited_notes.no_edited_notes_found")}</div>
<div class="edited-notes-list"></div>
</div>
`;
// TODO: Deduplicate with server.
interface EditedNotesResponse {
noteId: string;
isDeleted: boolean;
title: string;
notePath: string[];
}
export default class EditedNotesWidget extends NoteContextAwareWidget {
private $list!: JQuery<HTMLElement>;
private $noneFound!: JQuery<HTMLElement>;
get name() {
return "editedNotes";
}
isEnabled() {
return super.isEnabled() && this.note.hasOwnedLabel("dateNote");
return super.isEnabled() && this.note?.hasOwnedLabel("dateNote");
}
getTitle() {
return {
show: this.isEnabled(),
// promoted attributes have priority over edited notes
activate: (this.note.getPromotedDefinitionAttributes().length === 0 || !options.is("promotedAttributesOpenInRibbon")) && options.is("editedNotesOpenInRibbon"),
activate: (this.note?.getPromotedDefinitionAttributes().length === 0 || !options.is("promotedAttributesOpenInRibbon")) && options.is("editedNotesOpenInRibbon"),
title: t("edited_notes.title"),
icon: "bx bx-calendar-edit"
};
@ -48,8 +61,8 @@ export default class EditedNotesWidget extends NoteContextAwareWidget {
this.$noneFound = this.$widget.find(".no-edited-notes-found");
}
async refreshWithNote(note) {
let editedNotes = await server.get(`edited-notes/${note.getLabelValue("dateNote")}`);
async refreshWithNote(note: FNote) {
let editedNotes = await server.get<EditedNotesResponse[]>(`edited-notes/${note.getLabelValue("dateNote")}`);
editedNotes = editedNotes.filter((n) => n.noteId !== note.noteId);

View File

@ -3,6 +3,7 @@ import NoteContextAwareWidget from "../note_context_aware_widget.js";
import AttributeDetailWidget from "../attribute_widgets/attribute_detail.js";
import AttributeEditorWidget from "../attribute_widgets/attribute_editor.js";
import type { CommandListenerData } from "../../components/app_context.js";
import type FAttribute from "../../entities/fattribute.js";
const TPL = `
<div class="attribute-list">
@ -75,7 +76,8 @@ export default class OwnedAttributeListWidget extends NoteContextAwareWidget {
}
async updateAttributeListCommand({ attributes }: CommandListenerData<"updateAttributeList">) {
await this.attributeEditorWidget.updateAttributeList(attributes);
// TODO: See why we need FAttribute[] and Attribute[]
await this.attributeEditorWidget.updateAttributeList(attributes as FAttribute[]);
}
focus() {

View File

@ -1,6 +1,7 @@
import TypeWidget from "./type_widget.js";
import libraryLoader from "../../services/library_loader.js";
import options from "../../services/options.js";
import type FNote from "../../entities/fnote.js";
/**
* An abstract {@link TypeWidget} which implements the CodeMirror editor, meant to be used as a parent for
@ -16,6 +17,10 @@ import options from "../../services/options.js";
* - Call `this._update(note, content)` in `#doRefresh(note)`.
*/
export default class AbstractCodeTypeWidget extends TypeWidget {
protected $editor!: JQuery<HTMLElement>;
protected codeEditor!: CodeMirrorInstance;
doRender() {
this.initialized = this.#initEditor();
}
@ -28,8 +33,14 @@ export default class AbstractCodeTypeWidget extends TypeWidget {
delete CodeMirror.keyMap.default["Alt-Right"];
CodeMirror.modeURL = `${window.glob.assetPath}/node_modules/codemirror/mode/%N/%N.js`;
CodeMirror.modeInfo.find((mode) => mode.name === "JavaScript").mimes.push(...["application/javascript;env=frontend", "application/javascript;env=backend"]);
CodeMirror.modeInfo.find((mode) => mode.name === "SQLite").mimes = ["text/x-sqlite", "text/x-sqlite;schema=trilium"];
const jsMode = CodeMirror.modeInfo.find((mode) => mode.name === "JavaScript");
if (jsMode) {
jsMode.mimes.push(...["application/javascript;env=frontend", "application/javascript;env=backend"]);
}
const sqlMode = CodeMirror.modeInfo.find((mode) => mode.name === "SQLite");
if (sqlMode) {
sqlMode.mimes = ["text/x-sqlite", "text/x-sqlite;schema=trilium"];
}
this.codeEditor = CodeMirror(this.$editor[0], {
value: "",
@ -73,7 +84,7 @@ export default class AbstractCodeTypeWidget extends TypeWidget {
* @param {*} note the note that was changed.
* @param {*} content the new content of the note.
*/
_update(note, content) {
_update(note: { mime: string }, content: string) {
// CodeMirror breaks pretty badly on null, so even though it shouldn't happen (guarded by a consistency check)
// we provide fallback
this.codeEditor.setValue(content || "");

View File

@ -21,7 +21,6 @@ const TPL = `<div style="height: 100%; display: flex; flex-direction: column;">
export default class BackendLogWidget extends AbstractCodeTypeWidget {
private $editor!: JQuery<HTMLElement>;
private $refreshBackendLog!: JQuery<HTMLElement>;
doRender() {
@ -45,7 +44,7 @@ export default class BackendLogWidget extends AbstractCodeTypeWidget {
}
async load() {
const content = await server.get("backend-log");
const content = await server.get<string>("backend-log");
await this.initialized;
this._update(

View File

@ -24,8 +24,6 @@ const TPL = `
export default class EditableCodeTypeWidget extends AbstractCodeTypeWidget {
private $editor!: JQuery<HTMLElement>;
static getType() {
return "editableCode";
}
@ -59,7 +57,7 @@ export default class EditableCodeTypeWidget extends AbstractCodeTypeWidget {
const blob = await this.note?.getBlob();
await this.spacedUpdate.allowUpdateWithoutChange(() => {
this._update(note, blob?.content);
this._update(note, blob?.content ?? "");
});
this.show();

View File

@ -19,7 +19,6 @@ const TPL = `
</div>`;
export default class ReadOnlyCodeTypeWidget extends AbstractCodeTypeWidget {
$editor!: JQuery<HTMLElement>;
static getType() {
return "readOnlyCode";