mirror of
https://github.com/zadam/trilium.git
synced 2026-03-21 11:21:40 +01:00
Compare commits
12 Commits
experiment
...
feature/vi
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2d63e2a00f | ||
|
|
5a16aa416f | ||
|
|
df2a53e010 | ||
|
|
4f08389f80 | ||
|
|
ff0fb4bcfd | ||
|
|
5f410faaa9 | ||
|
|
25bd9e8abd | ||
|
|
301a1b2288 | ||
|
|
00c4933344 | ||
|
|
cd9b46e1c7 | ||
|
|
b356b355ca | ||
|
|
8834899012 |
@@ -24,7 +24,6 @@
|
||||
"@fullcalendar/multimonth": "6.1.20",
|
||||
"@fullcalendar/rrule": "6.1.20",
|
||||
"@fullcalendar/timegrid": "6.1.20",
|
||||
"@lexical/react": "0.42.0",
|
||||
"@maplibre/maplibre-gl-leaflet": "0.1.3",
|
||||
"@mermaid-js/layout-elk": "0.2.1",
|
||||
"@mind-elixir/node-menu": "5.0.1",
|
||||
@@ -62,7 +61,6 @@
|
||||
"katex": "0.16.39",
|
||||
"leaflet": "1.9.4",
|
||||
"leaflet-gpx": "2.2.0",
|
||||
"lexical": "0.42.0",
|
||||
"mark.js": "8.11.1",
|
||||
"marked": "17.0.4",
|
||||
"mermaid": "11.13.0",
|
||||
|
||||
@@ -39,7 +39,6 @@ export interface MenuCommandItem<T> {
|
||||
title: string;
|
||||
command?: T;
|
||||
type?: string;
|
||||
mime?: string;
|
||||
/**
|
||||
* The icon to display in the menu item.
|
||||
*
|
||||
|
||||
@@ -288,7 +288,7 @@ export default class TreeContextMenu implements SelectMenuItemEventListener<Tree
|
||||
return items.filter((row) => row !== null) as MenuItem<TreeCommandNames>[];
|
||||
}
|
||||
|
||||
async selectMenuItemHandler({ command, type, mime, templateNoteId }: MenuCommandItem<TreeCommandNames>) {
|
||||
async selectMenuItemHandler({ command, type, templateNoteId }: MenuCommandItem<TreeCommandNames>) {
|
||||
const notePath = treeService.getNotePath(this.node);
|
||||
|
||||
if (utils.isMobile()) {
|
||||
@@ -305,7 +305,6 @@ export default class TreeContextMenu implements SelectMenuItemEventListener<Tree
|
||||
target: "after",
|
||||
targetBranchId: this.node.data.branchId,
|
||||
type,
|
||||
mime,
|
||||
isProtected,
|
||||
templateNoteId
|
||||
});
|
||||
@@ -314,7 +313,6 @@ export default class TreeContextMenu implements SelectMenuItemEventListener<Tree
|
||||
|
||||
noteCreateService.createNote(parentNotePath, {
|
||||
type,
|
||||
mime,
|
||||
isProtected: this.node.data.isProtected,
|
||||
templateNoteId
|
||||
});
|
||||
|
||||
@@ -8,6 +8,7 @@ import FAttachment from "../entities/fattachment.js";
|
||||
import FNote from "../entities/fnote.js";
|
||||
import imageContextMenuService from "../menus/image_context_menu.js";
|
||||
import { t } from "../services/i18n.js";
|
||||
import { renderReactWidget, renderReactWidgetAtElement } from "../widgets/react/react_utils";
|
||||
import renderText from "./content_renderer_text.js";
|
||||
import renderDoc from "./doc_renderer.js";
|
||||
import { loadElkIfNeeded, postprocessMermaidSvg } from "./mermaid.js";
|
||||
@@ -212,15 +213,16 @@ async function renderFile(entity: FNote | FAttachment, type: string, $renderedCo
|
||||
|
||||
$content.append($audioPreview);
|
||||
} else if (type === "video") {
|
||||
const $videoPreview = $("<video controls></video>")
|
||||
.attr("src", openService.getUrlForDownload(`api/${entityType}/${entityId}/open-partial`))
|
||||
.attr("type", entity.mime)
|
||||
.css("width", "100%");
|
||||
const url = openService.getUrlForDownload(`api/${entityType}/${entityId}/open-partial`);
|
||||
const mime = entity.mime;
|
||||
|
||||
$content.append($videoPreview);
|
||||
const VideoPreviewContent = (await import("../widgets/type_widgets/file/Video")).VideoPreviewContent;
|
||||
const $viewer = renderReactWidget(null, h(VideoPreviewContent, { url, mime }));
|
||||
|
||||
$content.append($viewer);
|
||||
}
|
||||
|
||||
if (entityType === "notes" && "noteId" in entity) {
|
||||
if (entityType === "notes" && "noteId" in entity && type !== "video") {
|
||||
// TODO: we should make this available also for attachments, but there's a problem with "Open externally" support
|
||||
// in attachment list
|
||||
const $downloadButton = $(`
|
||||
|
||||
@@ -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: "text", mime: "application/json", title: "Text (Lexical)", icon: "bx-note" },
|
||||
{ type: "spreadsheet", mime: "application/json", title: t("note_types.spreadsheet"), icon: "bx-table", isBeta: true },
|
||||
|
||||
// Text notes group
|
||||
@@ -98,7 +97,6 @@ function getBlankNoteTypes(command?: TreeCommandNames): MenuItem<TreeCommandName
|
||||
title: nt.title,
|
||||
command,
|
||||
type: nt.type,
|
||||
mime: nt.mime,
|
||||
uiIcon: `bx ${nt.icon}`,
|
||||
badges: []
|
||||
};
|
||||
|
||||
@@ -1042,6 +1042,7 @@
|
||||
"pause": "Pause (Space)",
|
||||
"back-10s": "Back 10s (Left arrow key)",
|
||||
"forward-30s": "Forward 30s",
|
||||
"volume": "Volume",
|
||||
"mute": "Mute (M)",
|
||||
"unmute": "Unmute (M)",
|
||||
"playback-speed": "Playback speed",
|
||||
@@ -1054,7 +1055,8 @@
|
||||
"exit-fullscreen": "Exit fullscreen",
|
||||
"unsupported-format": "Media preview is not available for this file format:\n{{mime}}",
|
||||
"zoom-to-fit": "Zoom to fill",
|
||||
"zoom-reset": "Reset zoom to fill"
|
||||
"zoom-reset": "Reset zoom to fill",
|
||||
"more-options": "More options"
|
||||
},
|
||||
"protected_session": {
|
||||
"enter_password_instruction": "Showing protected note requires entering your password:",
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
import { t } from "../../services/i18n.js";
|
||||
import server from "../../services/server.js";
|
||||
import froca from "../../services/froca.js";
|
||||
import linkService from "../../services/link.js";
|
||||
import appContext from "../../components/app_context.js";
|
||||
import attributeAutocompleteService from "../../services/attribute_autocomplete.js";
|
||||
import type { Attribute } from "../../services/attribute_parser.js";
|
||||
import { isExperimentalFeatureEnabled } from "../../services/experimental_features.js";
|
||||
import { focusSavedElement, saveFocusedElement } from "../../services/focus.js";
|
||||
import froca from "../../services/froca.js";
|
||||
import { t } from "../../services/i18n.js";
|
||||
import linkService from "../../services/link.js";
|
||||
import noteAutocompleteService from "../../services/note_autocomplete.js";
|
||||
import promotedAttributeDefinitionParser from "../../services/promoted_attribute_definition_parser.js";
|
||||
import NoteContextAwareWidget from "../note_context_aware_widget.js";
|
||||
import server from "../../services/server.js";
|
||||
import shortcutService from "../../services/shortcuts.js";
|
||||
import SpacedUpdate from "../../services/spaced_update.js";
|
||||
import utils from "../../services/utils.js";
|
||||
import shortcutService from "../../services/shortcuts.js";
|
||||
import appContext from "../../components/app_context.js";
|
||||
import type { Attribute } from "../../services/attribute_parser.js";
|
||||
import { focusSavedElement, saveFocusedElement } from "../../services/focus.js";
|
||||
import { isExperimentalFeatureEnabled } from "../../services/experimental_features.js";
|
||||
import NoteContextAwareWidget from "../note_context_aware_widget.js";
|
||||
|
||||
const TPL = /*html*/`
|
||||
<div class="attr-detail tn-tool-dialog">
|
||||
@@ -29,6 +29,7 @@ const TPL = /*html*/`
|
||||
max-height: 600px;
|
||||
overflow: auto;
|
||||
box-shadow: 10px 10px 93px -25px black;
|
||||
contain: none;
|
||||
}
|
||||
|
||||
.attr-help td {
|
||||
@@ -343,6 +344,7 @@ export default class AttributeDetailWidget extends NoteContextAwareWidget {
|
||||
private $relatedNotesList!: JQuery<HTMLElement>;
|
||||
private $relatedNotesMoreNotes!: JQuery<HTMLElement>;
|
||||
private $attrHelp!: JQuery<HTMLElement>;
|
||||
private $statusBar?: JQuery<HTMLElement>;
|
||||
|
||||
private relatedNotesSpacedUpdate!: SpacedUpdate;
|
||||
private attribute!: Attribute;
|
||||
@@ -577,17 +579,24 @@ export default class AttributeDetailWidget extends NoteContextAwareWidget {
|
||||
return;
|
||||
}
|
||||
|
||||
this.$widget
|
||||
.css("left", detPosition.left)
|
||||
.css("right", detPosition.right)
|
||||
.css("top", y - offset.top + 70)
|
||||
.css("max-height", outerHeight + y > height - 50 ? height - y - 50 : 10000);
|
||||
|
||||
if (isNewLayout) {
|
||||
if (!this.$statusBar) {
|
||||
this.$statusBar = $(document.body).find(".component.status-bar");
|
||||
}
|
||||
|
||||
const statusBarHeight = this.$statusBar.outerHeight() ?? 0;
|
||||
const maxHeight = document.body.clientHeight - statusBarHeight;
|
||||
this.$widget
|
||||
.css("left", offset.left + (typeof detPosition.left === "number" ? detPosition.left : 0))
|
||||
.css("top", "unset")
|
||||
.css("bottom", 70)
|
||||
.css("max-height", "80vh");
|
||||
.css("bottom", statusBarHeight ?? 0)
|
||||
.css("max-height", maxHeight);
|
||||
} else {
|
||||
this.$widget
|
||||
.css("left", detPosition.left)
|
||||
.css("right", detPosition.right)
|
||||
.css("top", y - offset.top + 70)
|
||||
.css("max-height", outerHeight + y > height - 50 ? height - y - 50 : 10000);
|
||||
}
|
||||
|
||||
if (focus === "name") {
|
||||
@@ -695,14 +704,14 @@ export default class AttributeDetailWidget extends NoteContextAwareWidget {
|
||||
return "label-definition";
|
||||
} else if (attribute.name.startsWith("relation:")) {
|
||||
return "relation-definition";
|
||||
} else {
|
||||
return "label";
|
||||
}
|
||||
return "label";
|
||||
|
||||
} else if (attribute.type === "relation") {
|
||||
return "relation";
|
||||
} else {
|
||||
this.$title.text("");
|
||||
}
|
||||
this.$title.text("");
|
||||
|
||||
}
|
||||
|
||||
updateAttributeInEditor() {
|
||||
|
||||
@@ -12,6 +12,11 @@
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 10px;
|
||||
|
||||
body.mobile & {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
.note-list-bottom-pager {
|
||||
@@ -269,8 +274,9 @@
|
||||
overflow: hidden;
|
||||
user-select: none;
|
||||
|
||||
body.mobile & {
|
||||
flex-basis: 150px;
|
||||
body.mobile &.mobile-full-width {
|
||||
grid-column-start: 1;
|
||||
grid-column-end: 3;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
@@ -364,23 +370,19 @@
|
||||
mask-repeat: no-repeat;
|
||||
mask-size: 100% 100%;
|
||||
}
|
||||
|
||||
|
||||
.ck-content p {
|
||||
margin-bottom: 0.5em;
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
.ck-content figure.image {
|
||||
width: 25%;
|
||||
}
|
||||
|
||||
.ck-content .table {
|
||||
display: flex;
|
||||
flex-direction: column-reverse;
|
||||
overflow-x: scroll;
|
||||
--scrollbar-thickness: 0;
|
||||
scrollbar-width: none;
|
||||
|
||||
|
||||
table {
|
||||
width: max-content;
|
||||
table-layout: auto;
|
||||
@@ -435,4 +437,4 @@
|
||||
}
|
||||
}
|
||||
|
||||
/* #endregion */
|
||||
/* #endregion */
|
||||
|
||||
@@ -1,25 +1,25 @@
|
||||
import "./ListOrGridView.css";
|
||||
import { Card, CardFrame, CardSection } from "../../react/Card";
|
||||
|
||||
import { clsx } from "clsx";
|
||||
import { ComponentChildren, TargetedMouseEvent } from "preact";
|
||||
import { useCallback, useEffect, useRef, useState } from "preact/hooks";
|
||||
import { JSX } from "preact/jsx-runtime";
|
||||
|
||||
import FNote from "../../../entities/fnote";
|
||||
import linkContextMenuService from "../../../menus/link_context_menu";
|
||||
import attribute_renderer from "../../../services/attribute_renderer";
|
||||
import content_renderer from "../../../services/content_renderer";
|
||||
import { t } from "../../../services/i18n";
|
||||
import link from "../../../services/link";
|
||||
import CollectionProperties from "../../note_bars/CollectionProperties";
|
||||
import ActionButton from "../../react/ActionButton";
|
||||
import { Card, CardFrame, CardSection } from "../../react/Card";
|
||||
import { useImperativeSearchHighlighlighting, useNoteLabel, useNoteLabelBoolean, useNoteProperty } from "../../react/hooks";
|
||||
import Icon from "../../react/Icon";
|
||||
import NoteLink from "../../react/NoteLink";
|
||||
import { ViewModeProps } from "../interface";
|
||||
import { Pager, usePagination, PaginationContext } from "../Pagination";
|
||||
import { Pager, PaginationContext,usePagination } from "../Pagination";
|
||||
import { filterChildNotes, useFilteredNoteIds } from "./utils";
|
||||
import { JSX } from "preact/jsx-runtime";
|
||||
import { clsx } from "clsx";
|
||||
import ActionButton from "../../react/ActionButton";
|
||||
import linkContextMenuService from "../../../menus/link_context_menu";
|
||||
import { ComponentChildren, TargetedMouseEvent } from "preact";
|
||||
|
||||
const contentSizeObserver = new ResizeObserver(onContentResized);
|
||||
|
||||
@@ -53,13 +53,13 @@ export function GridView({ note, noteIds: unfilteredNoteIds, highlightedTokens }
|
||||
<div className={clsx("note-list-container use-tn-links", {"search-results": (noteType === "search")})}>
|
||||
{pageNotes?.map(childNote => (
|
||||
<GridNoteCard key={childNote.noteId}
|
||||
note={childNote}
|
||||
parentNote={note}
|
||||
highlightedTokens={highlightedTokens}
|
||||
includeArchived={includeArchived} />
|
||||
note={childNote}
|
||||
parentNote={note}
|
||||
highlightedTokens={highlightedTokens}
|
||||
includeArchived={includeArchived} />
|
||||
))}
|
||||
</div>
|
||||
</NoteList>
|
||||
</NoteList>;
|
||||
}
|
||||
|
||||
interface NoteListProps {
|
||||
@@ -82,13 +82,13 @@ function NoteList(props: NoteListProps) {
|
||||
|
||||
{props.noteIds.length > 0 && <div className="note-list-wrapper">
|
||||
{!hasCollectionProperties && <Pager {...props.pagination} />}
|
||||
|
||||
|
||||
{props.children}
|
||||
|
||||
<Pager className="note-list-bottom-pager" {...props.pagination} />
|
||||
</div>}
|
||||
|
||||
</div>
|
||||
</div>;
|
||||
}
|
||||
|
||||
function ListNoteCard({ note, parentNote, highlightedTokens, currentLevel, expandDepth, includeArchived }: {
|
||||
@@ -106,25 +106,25 @@ function ListNoteCard({ note, parentNote, highlightedTokens, currentLevel, expan
|
||||
// Reset expand state if switching to another note, or if user manually toggled expansion state.
|
||||
useEffect(() => setExpanded(currentLevel <= expandDepth), [ note, currentLevel, expandDepth ]);
|
||||
|
||||
let subSections: JSX.Element | undefined = undefined;
|
||||
let subSections: JSX.Element | undefined;
|
||||
if (isExpanded) {
|
||||
subSections = <>
|
||||
<CardSection className="note-content-preview">
|
||||
<NoteContent note={note}
|
||||
highlightedTokens={highlightedTokens}
|
||||
noChildrenList
|
||||
includeArchivedNotes={includeArchived} />
|
||||
highlightedTokens={highlightedTokens}
|
||||
noChildrenList
|
||||
includeArchivedNotes={includeArchived} />
|
||||
</CardSection>
|
||||
|
||||
<NoteChildren note={note}
|
||||
parentNote={parentNote}
|
||||
highlightedTokens={highlightedTokens}
|
||||
currentLevel={currentLevel}
|
||||
expandDepth={expandDepth}
|
||||
includeArchived={includeArchived} />
|
||||
</>
|
||||
parentNote={parentNote}
|
||||
highlightedTokens={highlightedTokens}
|
||||
currentLevel={currentLevel}
|
||||
expandDepth={expandDepth}
|
||||
includeArchived={includeArchived} />
|
||||
</>;
|
||||
}
|
||||
|
||||
|
||||
return (
|
||||
<CardSection
|
||||
className={clsx("nested-note-list-item", "no-tooltip-preview", note.getColorClass(), {
|
||||
@@ -137,14 +137,14 @@ function ListNoteCard({ note, parentNote, highlightedTokens, currentLevel, expan
|
||||
data-note-id={note.noteId}
|
||||
>
|
||||
<h5>
|
||||
<span className={`note-expander ${isExpanded ? "bx bx-chevron-down" : "bx bx-chevron-right"}`}
|
||||
onClick={() => setExpanded(!isExpanded)}/>
|
||||
<span className={`note-expander ${isExpanded ? "bx bx-chevron-down" : "bx bx-chevron-right"}`}
|
||||
onClick={() => setExpanded(!isExpanded)}/>
|
||||
<Icon className="note-icon" icon={note.getIcon()} />
|
||||
<NoteLink className="note-book-title"
|
||||
notePath={notePath}
|
||||
noPreview
|
||||
showNotePath={parentNote.type === "search"}
|
||||
highlightedTokens={highlightedTokens} />
|
||||
notePath={notePath}
|
||||
noPreview
|
||||
showNotePath={parentNote.type === "search"}
|
||||
highlightedTokens={highlightedTokens} />
|
||||
<NoteAttributes note={note} />
|
||||
<NoteMenuButton notePath={notePath} />
|
||||
</h5>
|
||||
@@ -164,27 +164,28 @@ function GridNoteCard(props: GridNoteCardProps) {
|
||||
|
||||
return (
|
||||
<CardFrame className={clsx("note-book-card", "no-tooltip-preview", "block-link", props.note.getColorClass(), {
|
||||
"archived": props.note.isArchived
|
||||
})}
|
||||
data-href={`#${notePath}`}
|
||||
data-note-id={props.note.noteId}
|
||||
onClick={(e) => link.goToLink(e)}
|
||||
"archived": props.note.isArchived,
|
||||
"mobile-full-width": props.note.type === "file"
|
||||
})}
|
||||
data-href={`#${notePath}`}
|
||||
data-note-id={props.note.noteId}
|
||||
onClick={(e) => link.goToLink(e)}
|
||||
>
|
||||
<h5 className={clsx("note-book-header")}>
|
||||
<Icon className="note-icon" icon={props.note.getIcon()} />
|
||||
<NoteLink className="note-book-title"
|
||||
notePath={notePath}
|
||||
noPreview
|
||||
showNotePath={props.parentNote.type === "search"}
|
||||
highlightedTokens={props.highlightedTokens}
|
||||
notePath={notePath}
|
||||
noPreview
|
||||
showNotePath={props.parentNote.type === "search"}
|
||||
highlightedTokens={props.highlightedTokens}
|
||||
/>
|
||||
{!props.note.isOptions() && <NoteMenuButton notePath={notePath} />}
|
||||
|
||||
|
||||
</h5>
|
||||
<NoteContent note={props.note}
|
||||
trim
|
||||
highlightedTokens={props.highlightedTokens}
|
||||
includeArchivedNotes={props.includeArchived}
|
||||
trim
|
||||
highlightedTokens={props.highlightedTokens}
|
||||
includeArchivedNotes={props.includeArchived}
|
||||
/>
|
||||
</CardFrame>
|
||||
);
|
||||
@@ -222,7 +223,7 @@ export function NoteContent({ note, trim, noChildrenList, highlightedTokens, inc
|
||||
|
||||
return () => {
|
||||
contentSizeObserver.unobserve(contentElement);
|
||||
}
|
||||
};
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -281,13 +282,13 @@ function NoteChildren({ note, parentNote, highlightedTokens, currentLevel, expan
|
||||
function NoteMenuButton(props: {notePath: string}) {
|
||||
const openMenu = useCallback((e: TargetedMouseEvent<HTMLElement>) => {
|
||||
linkContextMenuService.openContextMenu(props.notePath, e);
|
||||
e.stopPropagation()
|
||||
e.stopPropagation();
|
||||
}, [props.notePath]);
|
||||
|
||||
return <ActionButton className="note-book-item-menu"
|
||||
icon="bx bx-dots-vertical-rounded" text=""
|
||||
onClick={openMenu}
|
||||
/>
|
||||
icon="bx bx-dots-vertical-rounded" text=""
|
||||
onClick={openMenu}
|
||||
/>;
|
||||
}
|
||||
|
||||
function getNotePath(parentNote: FNote, childNote: FNote) {
|
||||
@@ -315,7 +316,7 @@ function useExpansionDepth(note: FNote) {
|
||||
|
||||
function onContentResized(entries: ResizeObserverEntry[], observer: ResizeObserver): void {
|
||||
for (const contentElement of entries) {
|
||||
const isOverflowing = ((contentElement.target.scrollHeight > contentElement.target.clientHeight))
|
||||
const isOverflowing = ((contentElement.target.scrollHeight > contentElement.target.clientHeight));
|
||||
contentElement.target.classList.toggle("note-book-content-overflowing", isOverflowing);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { createPortal } from "preact/compat";
|
||||
import { useEffect, useState } from "preact/hooks";
|
||||
|
||||
import FAttribute from "../../entities/fattribute";
|
||||
@@ -74,7 +75,7 @@ export default function InheritedAttributesTab({ note, componentId, emptyListStr
|
||||
)}
|
||||
</div>
|
||||
|
||||
{attributeDetailWidgetEl}
|
||||
{createPortal(attributeDetailWidgetEl, document.body)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { AttributeEditor as CKEditorAttributeEditor, MentionFeed, ModelElement, ModelNode, ModelPosition } from "@triliumnext/ckeditor5";
|
||||
import { AttributeType } from "@triliumnext/commons";
|
||||
import { createPortal } from "preact/compat";
|
||||
import { MutableRef, useEffect, useImperativeHandle, useMemo, useRef, useState } from "preact/hooks";
|
||||
|
||||
import type { CommandData, FilteredCommandNames } from "../../../components/app_context";
|
||||
@@ -336,7 +337,8 @@ export default function AttributeEditor({ api, note, componentId, notePath, ntxI
|
||||
let matchedAttr: Attribute | null = null;
|
||||
|
||||
for (const attr of parsedAttrs) {
|
||||
if (attr.startIndex && clickIndex > attr.startIndex && attr.endIndex && clickIndex <= attr.endIndex) {
|
||||
if (attr.startIndex !== undefined && clickIndex > attr.startIndex &&
|
||||
attr.endIndex !== undefined && clickIndex <= attr.endIndex) {
|
||||
matchedAttr = attr;
|
||||
break;
|
||||
}
|
||||
@@ -407,7 +409,7 @@ export default function AttributeEditor({ api, note, componentId, notePath, ntxI
|
||||
)}
|
||||
</div>}
|
||||
|
||||
{attributeDetailWidgetEl}
|
||||
{createPortal(attributeDetailWidgetEl, document.body)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -50,13 +50,21 @@
|
||||
}
|
||||
}
|
||||
|
||||
.media-volume-row {
|
||||
.media-volume-dropdown-content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.25em;
|
||||
padding: 0.5em;
|
||||
|
||||
.volume-mute-btn {
|
||||
padding: 0.25em;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.media-volume-slider {
|
||||
width: 80px;
|
||||
width: 100px;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,30 +102,47 @@ export function VolumeControl({ mediaRef }: { mediaRef: RefObject<HTMLVideoEleme
|
||||
}
|
||||
};
|
||||
|
||||
const toggleMute = () => {
|
||||
const toggleMute = (e: MouseEvent) => {
|
||||
e.stopPropagation();
|
||||
const media = mediaRef.current;
|
||||
if (!media) return;
|
||||
media.muted = !media.muted;
|
||||
setMuted(media.muted);
|
||||
};
|
||||
|
||||
const volumeIcon = muted || volume === 0
|
||||
? "bx bx-volume-mute"
|
||||
: volume < 0.5
|
||||
? "bx bx-volume-low"
|
||||
: "bx bx-volume-full";
|
||||
|
||||
return (
|
||||
<div class="media-volume-row">
|
||||
<ActionButton
|
||||
icon={muted || volume === 0 ? "bx bx-volume-mute" : volume < 0.5 ? "bx bx-volume-low" : "bx bx-volume-full"}
|
||||
text={muted ? t("media.unmute") : t("media.mute")}
|
||||
onClick={toggleMute}
|
||||
/>
|
||||
<input
|
||||
type="range"
|
||||
class="media-volume-slider"
|
||||
min={0}
|
||||
max={1}
|
||||
step={0.05}
|
||||
value={muted ? 0 : volume}
|
||||
onInput={onVolumeChange}
|
||||
/>
|
||||
</div>
|
||||
<Dropdown
|
||||
iconAction
|
||||
hideToggleArrow
|
||||
buttonClassName="volume-dropdown"
|
||||
text={<Icon icon={volumeIcon} />}
|
||||
title={t("media.volume")}
|
||||
>
|
||||
<li class="media-volume-dropdown-content">
|
||||
<button
|
||||
class="dropdown-item volume-mute-btn"
|
||||
onClick={toggleMute}
|
||||
title={muted ? t("media.unmute") : t("media.mute")}
|
||||
>
|
||||
<Icon icon={volumeIcon} />
|
||||
</button>
|
||||
<input
|
||||
type="range"
|
||||
class="media-volume-slider"
|
||||
min={0}
|
||||
max={1}
|
||||
step={0.05}
|
||||
value={muted ? 0 : volume}
|
||||
onInput={onVolumeChange}
|
||||
/>
|
||||
</li>
|
||||
</Dropdown>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
.note-detail-file > .video-preview-wrapper {
|
||||
.video-preview-wrapper {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
background-color: black;
|
||||
background-color: black;
|
||||
|
||||
.video-preview {
|
||||
background-color: black;
|
||||
|
||||
@@ -7,19 +7,29 @@ import FNote from "../../../entities/fnote";
|
||||
import { t } from "../../../services/i18n";
|
||||
import { getUrlForDownload } from "../../../services/open";
|
||||
import ActionButton from "../../react/ActionButton";
|
||||
import Dropdown from "../../react/Dropdown";
|
||||
import { FormListHeader, FormListItem } from "../../react/FormList";
|
||||
import Icon from "../../react/Icon";
|
||||
import NoItems from "../../react/NoItems";
|
||||
import { LoopButton, PlaybackSpeed, PlayPauseButton, SeekBar, SkipButton, VolumeControl } from "./MediaPlayer";
|
||||
import { PlayPauseButton, SeekBar, SkipButton, VolumeControl } from "./MediaPlayer";
|
||||
|
||||
const AUTO_HIDE_DELAY = 3000;
|
||||
|
||||
export default function VideoPreview({ note }: { note: FNote }) {
|
||||
return <VideoPreviewContent
|
||||
url={getUrlForDownload(`api/notes/${note.noteId}/open-partial`)}
|
||||
mime={note.mime}
|
||||
/>;
|
||||
}
|
||||
|
||||
export function VideoPreviewContent({ url, mime }: { url: string, mime: string }) {
|
||||
const wrapperRef = useRef<HTMLDivElement>(null);
|
||||
const videoRef = useRef<HTMLVideoElement>(null);
|
||||
const [playing, setPlaying] = useState(false);
|
||||
const [error, setError] = useState(false);
|
||||
const { visible: controlsVisible, onMouseMove, flash: flashControls } = useAutoHideControls(videoRef, playing);
|
||||
|
||||
useEffect(() => setError(false), [note.noteId]);
|
||||
useEffect(() => setError(false), [ url ]);
|
||||
const onError = useCallback(() => setError(true), []);
|
||||
|
||||
const togglePlayback = useCallback(() => {
|
||||
@@ -33,6 +43,7 @@ export default function VideoPreview({ note }: { note: FNote }) {
|
||||
}, []);
|
||||
|
||||
const onVideoClick = useCallback((e: MouseEvent) => {
|
||||
e.stopPropagation();
|
||||
if ((e.target as HTMLElement).closest(".media-preview-controls")) return;
|
||||
togglePlayback();
|
||||
}, [togglePlayback]);
|
||||
@@ -40,7 +51,7 @@ export default function VideoPreview({ note }: { note: FNote }) {
|
||||
const onKeyDown = useKeyboardShortcuts(videoRef, wrapperRef, togglePlayback, flashControls);
|
||||
|
||||
if (error) {
|
||||
return <NoItems icon="bx bx-video-off" text={t("media.unsupported-format", { mime: note.mime.replace("/", "-") })} />;
|
||||
return <NoItems icon="bx bx-video-off" text={t("media.unsupported-format", { mime: mime.replace("/", "-") })} />;
|
||||
}
|
||||
|
||||
return (
|
||||
@@ -48,8 +59,8 @@ export default function VideoPreview({ note }: { note: FNote }) {
|
||||
<video
|
||||
ref={videoRef}
|
||||
class="video-preview"
|
||||
src={getUrlForDownload(`api/notes/${note.noteId}/open-partial`)}
|
||||
datatype={note?.mime}
|
||||
src={url}
|
||||
datatype={mime}
|
||||
onPlay={() => setPlaying(true)}
|
||||
onPause={() => setPlaying(false)}
|
||||
onError={onError}
|
||||
@@ -59,19 +70,17 @@ export default function VideoPreview({ note }: { note: FNote }) {
|
||||
<SeekBar mediaRef={videoRef} />
|
||||
<div class="media-buttons-row">
|
||||
<div className="left">
|
||||
<PlaybackSpeed mediaRef={videoRef} />
|
||||
<RotateButton videoRef={videoRef} />
|
||||
<OverflowMenu videoRef={videoRef} />
|
||||
</div>
|
||||
<div className="center">
|
||||
<div className="spacer" />
|
||||
<SkipButton mediaRef={videoRef} seconds={-10} icon="bx bx-rewind" text={t("media.back-10s")} />
|
||||
<PlayPauseButton playing={playing} togglePlayback={togglePlayback} />
|
||||
<SkipButton mediaRef={videoRef} seconds={30} icon="bx bx-fast-forward" text={t("media.forward-30s")} />
|
||||
<LoopButton mediaRef={videoRef} />
|
||||
<div className="spacer" />
|
||||
</div>
|
||||
<div className="right">
|
||||
<VolumeControl mediaRef={videoRef} />
|
||||
<ZoomToFitButton videoRef={videoRef} />
|
||||
<PictureInPictureButton videoRef={videoRef} />
|
||||
<FullscreenButton targetRef={wrapperRef} />
|
||||
</div>
|
||||
@@ -171,8 +180,49 @@ function useAutoHideControls(videoRef: RefObject<HTMLVideoElement>, playing: boo
|
||||
return { visible, onMouseMove, flash: onMouseMove };
|
||||
}
|
||||
|
||||
function RotateButton({ videoRef }: { videoRef: RefObject<HTMLVideoElement> }) {
|
||||
const PLAYBACK_SPEEDS = [0.5, 1, 1.25, 1.5, 2];
|
||||
|
||||
function OverflowMenu({ videoRef }: { videoRef: RefObject<HTMLVideoElement> }) {
|
||||
const [speed, setSpeed] = useState(() => videoRef.current?.playbackRate ?? 1);
|
||||
const [loop, setLoop] = useState(() => videoRef.current?.loop ?? false);
|
||||
const [rotation, setRotation] = useState(0);
|
||||
const [fitted, setFitted] = useState(false);
|
||||
|
||||
// Sync playback rate
|
||||
useEffect(() => {
|
||||
const video = videoRef.current;
|
||||
if (!video) return;
|
||||
|
||||
setSpeed(video.playbackRate);
|
||||
const onRateChange = () => setSpeed(video.playbackRate);
|
||||
video.addEventListener("ratechange", onRateChange);
|
||||
return () => video.removeEventListener("ratechange", onRateChange);
|
||||
}, [videoRef]);
|
||||
|
||||
// Sync loop state
|
||||
useEffect(() => {
|
||||
const video = videoRef.current;
|
||||
if (!video) return;
|
||||
setLoop(video.loop);
|
||||
|
||||
const observer = new MutationObserver(() => setLoop(video.loop));
|
||||
observer.observe(video, { attributes: true, attributeFilter: ["loop"] });
|
||||
return () => observer.disconnect();
|
||||
}, [videoRef]);
|
||||
|
||||
const selectSpeed = (rate: number) => {
|
||||
const video = videoRef.current;
|
||||
if (!video) return;
|
||||
video.playbackRate = rate;
|
||||
setSpeed(rate);
|
||||
};
|
||||
|
||||
const toggleLoop = () => {
|
||||
const video = videoRef.current;
|
||||
if (!video) return;
|
||||
video.loop = !video.loop;
|
||||
setLoop(video.loop);
|
||||
};
|
||||
|
||||
const rotate = () => {
|
||||
const video = videoRef.current;
|
||||
@@ -182,7 +232,6 @@ function RotateButton({ videoRef }: { videoRef: RefObject<HTMLVideoElement> }) {
|
||||
|
||||
const isSideways = next === 90 || next === 270;
|
||||
if (isSideways) {
|
||||
// Scale down so the rotated video fits within its container.
|
||||
const container = video.parentElement;
|
||||
if (container) {
|
||||
const ratio = container.clientWidth / container.clientHeight;
|
||||
@@ -195,19 +244,7 @@ function RotateButton({ videoRef }: { videoRef: RefObject<HTMLVideoElement> }) {
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<ActionButton
|
||||
icon="bx bx-rotate-right"
|
||||
text={t("media.rotate")}
|
||||
onClick={rotate}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function ZoomToFitButton({ videoRef }: { videoRef: RefObject<HTMLVideoElement> }) {
|
||||
const [fitted, setFitted] = useState(false);
|
||||
|
||||
const toggle = () => {
|
||||
const toggleFit = () => {
|
||||
const video = videoRef.current;
|
||||
if (!video) return;
|
||||
const next = !fitted;
|
||||
@@ -216,12 +253,50 @@ function ZoomToFitButton({ videoRef }: { videoRef: RefObject<HTMLVideoElement> }
|
||||
};
|
||||
|
||||
return (
|
||||
<ActionButton
|
||||
className={fitted ? "active" : ""}
|
||||
icon={fitted ? "bx bx-collapse" : "bx bx-expand"}
|
||||
text={fitted ? t("media.zoom-reset") : t("media.zoom-to-fit")}
|
||||
onClick={toggle}
|
||||
/>
|
||||
<Dropdown
|
||||
iconAction
|
||||
hideToggleArrow
|
||||
noSelectButtonStyle
|
||||
noDropdownListStyle
|
||||
mobileBackdrop
|
||||
buttonClassName="overflow-menu-dropdown"
|
||||
dropdownContainerClassName="mobile-bottom-menu"
|
||||
text={<Icon icon="bx bx-dots-horizontal-rounded" />}
|
||||
title={t("media.more-options")}
|
||||
>
|
||||
<FormListHeader text={t("media.playback-speed")} />
|
||||
{PLAYBACK_SPEEDS.map((rate) => (
|
||||
<FormListItem
|
||||
key={rate}
|
||||
icon={rate === speed ? "bx bx-check" : "bx bx-empty"}
|
||||
active={rate === speed}
|
||||
onClick={() => selectSpeed(rate)}
|
||||
>
|
||||
{rate}x
|
||||
</FormListItem>
|
||||
))}
|
||||
<li class="dropdown-divider" />
|
||||
<FormListItem
|
||||
icon="bx bx-rotate-right"
|
||||
onClick={rotate}
|
||||
>
|
||||
{t("media.rotate")}
|
||||
</FormListItem>
|
||||
<FormListItem
|
||||
icon={loop ? "bx bx-check" : "bx bx-repeat"}
|
||||
active={loop}
|
||||
onClick={toggleLoop}
|
||||
>
|
||||
{loop ? t("media.disable-loop") : t("media.loop")}
|
||||
</FormListItem>
|
||||
<FormListItem
|
||||
icon={fitted ? "bx bx-collapse" : "bx bx-expand"}
|
||||
active={fitted}
|
||||
onClick={toggleFit}
|
||||
>
|
||||
{fitted ? t("media.zoom-reset") : t("media.zoom-to-fit")}
|
||||
</FormListItem>
|
||||
</Dropdown>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -14,11 +14,10 @@ import note_create from "../../../services/note_create";
|
||||
import options from "../../../services/options";
|
||||
import toast from "../../../services/toast";
|
||||
import utils, { hasTouchBar, isMobile } from "../../../services/utils";
|
||||
import { useEditorSpacedUpdate, useLegacyImperativeHandlers, useNoteLabel, useNoteProperty, useTriliumEvent, useTriliumOption, useTriliumOptionBool } from "../../react/hooks";
|
||||
import { useEditorSpacedUpdate, useLegacyImperativeHandlers, useNoteLabel, useTriliumEvent, useTriliumOption, useTriliumOptionBool } from "../../react/hooks";
|
||||
import TouchBar, { TouchBarButton, TouchBarGroup, TouchBarSegmentedControl } from "../../react/TouchBar";
|
||||
import { TypeWidgetProps } from "../type_widget";
|
||||
import CKEditorWithWatchdog, { CKEditorApi } from "./CKEditorWithWatchdog";
|
||||
import LexicalText from "./lexical";
|
||||
import getTemplates, { updateTemplateCache } from "./snippets.js";
|
||||
import { loadIncludedNote, refreshIncludedNote, setupImageOpening } from "./utils";
|
||||
|
||||
@@ -28,15 +27,7 @@ import { loadIncludedNote, refreshIncludedNote, setupImageOpening } from "./util
|
||||
* - Ballon block mode, in which there is a floating toolbar for the selected text, but another floating button for the entire block (i.e. paragraph).
|
||||
* - Decoupled mode, in which the editing toolbar is actually added on the client side (in {@link ClassicEditorToolbar}), see https://ckeditor.com/docs/ckeditor5/latest/examples/framework/bottom-toolbar-editor.html for an example on how the decoupled editor works.
|
||||
*/
|
||||
export default function EditableText(props: TypeWidgetProps) {
|
||||
const mime = useNoteProperty(props.note, "mime");
|
||||
if (mime === "application/json") {
|
||||
return <LexicalText {...props} />;
|
||||
}
|
||||
return <EditableTextCKEditor {...props} />;
|
||||
}
|
||||
|
||||
function EditableTextCKEditor({ note, parentComponent, ntxId, noteContext }: TypeWidgetProps) {
|
||||
export default function EditableText({ note, parentComponent, ntxId, noteContext }: TypeWidgetProps) {
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
const contentRef = useRef<string>("");
|
||||
const watchdogRef = useRef<EditorWatchdog>(null);
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
.note-detail-editable-text {
|
||||
.toolbar {
|
||||
display: flex;
|
||||
margin-bottom: 1px;
|
||||
background: var(--classic-toolbar-vert-layout-background-color);
|
||||
padding: 3px 6px;
|
||||
border-radius: 6px;
|
||||
margin: 20px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.toolbar .divider {
|
||||
width: 1px;
|
||||
background-color: var(--main-border-color);
|
||||
margin: 0 6px;
|
||||
}
|
||||
|
||||
.toolbar .toolbar-item .text {
|
||||
display: flex;
|
||||
line-height: 20px;
|
||||
width: 200px;
|
||||
vertical-align: middle;
|
||||
font-size: 14px;
|
||||
color: #777;
|
||||
text-overflow: ellipsis;
|
||||
width: 70px;
|
||||
overflow: hidden;
|
||||
height: 20px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.toolbar .toolbar-item .icon {
|
||||
display: flex;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
user-select: none;
|
||||
margin-right: 8px;
|
||||
line-height: 16px;
|
||||
background-size: contain;
|
||||
}
|
||||
}
|
||||
@@ -1,177 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
*/
|
||||
import "./ToolbarPlugin.css";
|
||||
|
||||
import {useLexicalComposerContext} from '@lexical/react/LexicalComposerContext';
|
||||
import {mergeRegister} from '@lexical/utils';
|
||||
import {
|
||||
$getSelection,
|
||||
$isRangeSelection,
|
||||
CAN_REDO_COMMAND,
|
||||
CAN_UNDO_COMMAND,
|
||||
COMMAND_PRIORITY_LOW,
|
||||
FORMAT_ELEMENT_COMMAND,
|
||||
FORMAT_TEXT_COMMAND,
|
||||
REDO_COMMAND,
|
||||
SELECTION_CHANGE_COMMAND,
|
||||
UNDO_COMMAND,
|
||||
} from 'lexical';
|
||||
import {useCallback, useEffect, useRef, useState} from 'react';
|
||||
|
||||
import ActionButton, { ActionButtonProps } from "../../../react/ActionButton";
|
||||
|
||||
function Divider() {
|
||||
return <div className="divider" />;
|
||||
}
|
||||
|
||||
export default function ToolbarPlugin() {
|
||||
const [editor] = useLexicalComposerContext();
|
||||
const toolbarRef = useRef(null);
|
||||
const [canUndo, setCanUndo] = useState(false);
|
||||
const [canRedo, setCanRedo] = useState(false);
|
||||
const [isBold, setIsBold] = useState(false);
|
||||
const [isItalic, setIsItalic] = useState(false);
|
||||
const [isUnderline, setIsUnderline] = useState(false);
|
||||
const [isStrikethrough, setIsStrikethrough] = useState(false);
|
||||
|
||||
const $updateToolbar = useCallback(() => {
|
||||
const selection = $getSelection();
|
||||
if ($isRangeSelection(selection)) {
|
||||
// Update text format
|
||||
setIsBold(selection.hasFormat('bold'));
|
||||
setIsItalic(selection.hasFormat('italic'));
|
||||
setIsUnderline(selection.hasFormat('underline'));
|
||||
setIsStrikethrough(selection.hasFormat('strikethrough'));
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
return mergeRegister(
|
||||
editor.registerUpdateListener(({editorState}) => {
|
||||
editorState.read(
|
||||
() => {
|
||||
$updateToolbar();
|
||||
},
|
||||
{editor},
|
||||
);
|
||||
}),
|
||||
editor.registerCommand(
|
||||
SELECTION_CHANGE_COMMAND,
|
||||
(_payload, _newEditor) => {
|
||||
$updateToolbar();
|
||||
return false;
|
||||
},
|
||||
COMMAND_PRIORITY_LOW,
|
||||
),
|
||||
editor.registerCommand(
|
||||
CAN_UNDO_COMMAND,
|
||||
(payload) => {
|
||||
setCanUndo(payload);
|
||||
return false;
|
||||
},
|
||||
COMMAND_PRIORITY_LOW,
|
||||
),
|
||||
editor.registerCommand(
|
||||
CAN_REDO_COMMAND,
|
||||
(payload) => {
|
||||
setCanRedo(payload);
|
||||
return false;
|
||||
},
|
||||
COMMAND_PRIORITY_LOW,
|
||||
),
|
||||
);
|
||||
}, [editor, $updateToolbar]);
|
||||
|
||||
return (
|
||||
<div className="toolbar" ref={toolbarRef}>
|
||||
<ToolbarButton
|
||||
disabled={!canUndo}
|
||||
onClick={() => {
|
||||
editor.dispatchCommand(UNDO_COMMAND, undefined);
|
||||
}}
|
||||
text="Undo"
|
||||
icon="bx bx-undo"
|
||||
/>
|
||||
<ToolbarButton
|
||||
disabled={!canRedo}
|
||||
onClick={() => {
|
||||
editor.dispatchCommand(REDO_COMMAND, undefined);
|
||||
}}
|
||||
text="Redo"
|
||||
icon="bx bx-redo"
|
||||
/>
|
||||
<Divider />
|
||||
<ToolbarButton
|
||||
onClick={() => {
|
||||
editor.dispatchCommand(FORMAT_TEXT_COMMAND, 'bold');
|
||||
}}
|
||||
active={isBold}
|
||||
text="Format Bold"
|
||||
icon="bx bx-bold"
|
||||
/>
|
||||
<ToolbarButton
|
||||
onClick={() => {
|
||||
editor.dispatchCommand(FORMAT_TEXT_COMMAND, 'italic');
|
||||
}}
|
||||
active={isItalic}
|
||||
text="Format Italics"
|
||||
icon="bx bx-italic"
|
||||
/>
|
||||
<ToolbarButton
|
||||
onClick={() => {
|
||||
editor.dispatchCommand(FORMAT_TEXT_COMMAND, 'underline');
|
||||
}}
|
||||
active={isUnderline}
|
||||
text="Format Underline"
|
||||
icon="bx bx-underline"
|
||||
/>
|
||||
<ToolbarButton
|
||||
onClick={() => {
|
||||
editor.dispatchCommand(FORMAT_TEXT_COMMAND, 'strikethrough');
|
||||
}}
|
||||
active={isStrikethrough}
|
||||
text="Format Strikethrough"
|
||||
icon="bx bx-strikethrough"
|
||||
/>
|
||||
<Divider />
|
||||
<ToolbarButton
|
||||
onClick={() => {
|
||||
editor.dispatchCommand(FORMAT_ELEMENT_COMMAND, 'left');
|
||||
}}
|
||||
text="Left Align"
|
||||
icon="bx bx-align-left"
|
||||
/>
|
||||
<ToolbarButton
|
||||
onClick={() => {
|
||||
editor.dispatchCommand(FORMAT_ELEMENT_COMMAND, 'center');
|
||||
}}
|
||||
text="Center Align"
|
||||
icon="bx bx-align-middle"
|
||||
/>
|
||||
<ToolbarButton
|
||||
onClick={() => editor.dispatchCommand(FORMAT_ELEMENT_COMMAND, 'right')}
|
||||
text="Right Align"
|
||||
icon="bx bx-align-right"
|
||||
/>
|
||||
<ToolbarButton
|
||||
onClick={() => editor.dispatchCommand(FORMAT_ELEMENT_COMMAND, 'justify')}
|
||||
text="Justify Align"
|
||||
icon="bx bx-align-justify"
|
||||
/>{' '}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function ToolbarButton(props: Pick<ActionButtonProps, "icon" | "disabled" | "onClick" | "text">) {
|
||||
return (
|
||||
<ActionButton
|
||||
className="toolbar-item"
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
.note-detail-editable-text .lexical-wrapper {
|
||||
color: var(--main-text-color);
|
||||
font-family: var(--main-font-family);
|
||||
font-size: var(--main-font-size);
|
||||
line-height: 1.5;
|
||||
word-break: break-word;
|
||||
position: relative;
|
||||
margin-inline: var(--content-margin-inline);
|
||||
|
||||
>div[contenteditable="true"] {
|
||||
outline: 0;
|
||||
}
|
||||
|
||||
.lexical-placeholder {
|
||||
opacity: 0.5;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
@@ -1,120 +0,0 @@
|
||||
import "./index.css";
|
||||
|
||||
import {AutoFocusPlugin} from '@lexical/react/LexicalAutoFocusPlugin';
|
||||
import {LexicalComposer} from '@lexical/react/LexicalComposer';
|
||||
import {useLexicalComposerContext} from '@lexical/react/LexicalComposerContext';
|
||||
import {ContentEditable} from '@lexical/react/LexicalContentEditable';
|
||||
import {LexicalErrorBoundary} from '@lexical/react/LexicalErrorBoundary';
|
||||
import {HistoryPlugin} from '@lexical/react/LexicalHistoryPlugin';
|
||||
import {RichTextPlugin} from '@lexical/react/LexicalRichTextPlugin';
|
||||
import {$createRangeSelection, $getRoot, $setSelection, CLEAR_HISTORY_COMMAND} from 'lexical';
|
||||
import { useEffect } from 'preact/hooks';
|
||||
|
||||
import { useEditorSpacedUpdate, useTriliumEvent } from '../../../react/hooks';
|
||||
import { TypeWidgetProps } from "../../type_widget";
|
||||
import ToolbarPlugin from "./ToolbarPlugin";
|
||||
|
||||
const theme = {
|
||||
// Theme styling goes here
|
||||
//...
|
||||
};
|
||||
|
||||
// Catch any errors that occur during Lexical updates and log them
|
||||
// or throw them as needed. If you don't throw them, Lexical will
|
||||
// try to recover gracefully without losing user data.
|
||||
function onError(error) {
|
||||
console.error(error);
|
||||
}
|
||||
|
||||
export default function LexicalText(props: TypeWidgetProps) {
|
||||
const initialConfig = {
|
||||
namespace: 'MyEditor',
|
||||
theme,
|
||||
onError,
|
||||
};
|
||||
|
||||
const placeholder = (
|
||||
<div className="lexical-placeholder">
|
||||
Enter some text...
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<LexicalComposer initialConfig={initialConfig}>
|
||||
<ToolbarPlugin />
|
||||
<div className="lexical-wrapper">
|
||||
<RichTextPlugin
|
||||
contentEditable={<ContentEditable /> as never}
|
||||
placeholder={placeholder as never}
|
||||
ErrorBoundary={LexicalErrorBoundary}
|
||||
/>
|
||||
</div>
|
||||
<HistoryPlugin />
|
||||
<AutoFocusPlugin />
|
||||
<ScrollToEndPlugin />
|
||||
<CustomEditorPersistencePlugin {...props} />
|
||||
</LexicalComposer>
|
||||
);
|
||||
}
|
||||
|
||||
function CustomEditorPersistencePlugin({ note, noteContext }: TypeWidgetProps) {
|
||||
const [editor] = useLexicalComposerContext();
|
||||
|
||||
const spacedUpdate = useEditorSpacedUpdate({
|
||||
note,
|
||||
noteContext,
|
||||
noteType: "text",
|
||||
getData() {
|
||||
return {
|
||||
content: JSON.stringify(editor.toJSON().editorState)
|
||||
};
|
||||
},
|
||||
onContentChange(newContent) {
|
||||
if (!newContent) {
|
||||
editor.update(() => {
|
||||
$getRoot().clear();
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const editorState = editor.parseEditorState(newContent);
|
||||
editor.setEditorState(editorState);
|
||||
} catch (err) {
|
||||
console.error("Error parsing Lexical content", err);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
// Clear the history whenever note changes.
|
||||
useEffect(() => {
|
||||
editor.dispatchCommand(CLEAR_HISTORY_COMMAND, undefined);
|
||||
}, [ editor, note ]);
|
||||
|
||||
// Detect changes in content.
|
||||
useEffect(() => {
|
||||
return editor.registerUpdateListener(() => {
|
||||
spacedUpdate.scheduleUpdate();
|
||||
});
|
||||
}, [ spacedUpdate, editor ]);
|
||||
}
|
||||
|
||||
function ScrollToEndPlugin() {
|
||||
const [editor] = useLexicalComposerContext();
|
||||
|
||||
useTriliumEvent("scrollToEnd", () => {
|
||||
editor.update(() => {
|
||||
const root = $getRoot();
|
||||
const lastChild = root.getLastDescendant();
|
||||
if (lastChild) {
|
||||
const selection = $createRangeSelection();
|
||||
selection.anchor.set(lastChild.getKey(), lastChild.getTextContentSize(), 'text');
|
||||
selection.focus.set(lastChild.getKey(), lastChild.getTextContentSize(), 'text');
|
||||
$setSelection(selection);
|
||||
}
|
||||
});
|
||||
editor.focus();
|
||||
});
|
||||
|
||||
return null;
|
||||
}
|
||||
@@ -22,8 +22,9 @@
|
||||
flex-direction: column;
|
||||
padding: var(--ck-spacing-standard);
|
||||
box-sizing: border-box;
|
||||
max-width: 80vw;
|
||||
max-height: 80vh;
|
||||
min-width: 400px;
|
||||
max-width: 60vw;
|
||||
max-height: 350px;
|
||||
overflow: visible;
|
||||
user-select: text;
|
||||
}
|
||||
@@ -63,8 +64,8 @@
|
||||
border-radius: var(--ck-border-radius);
|
||||
background: var(--ck-color-input-background) !important;
|
||||
transition: border-color 120ms ease;
|
||||
overflow: visible !important;
|
||||
clip-path: none !important;
|
||||
overflow: auto;
|
||||
clip-path: none;
|
||||
}
|
||||
.ck.ck-math-input .ck-mathlive-container:focus-within {
|
||||
border-color: var(--ck-color-focus-border);
|
||||
@@ -159,16 +160,12 @@
|
||||
.ck.ck-math-preview {
|
||||
width: 100%;
|
||||
min-height: 40px;
|
||||
max-height: none !important;
|
||||
height: auto !important;
|
||||
padding: var(--ck-spacing-small);
|
||||
background: transparent !important;
|
||||
border: none !important;
|
||||
display: block;
|
||||
text-align: left;
|
||||
overflow-x: auto !important;
|
||||
overflow-y: visible !important;
|
||||
flex-shrink: 0;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
/* Center equation when in display mode */
|
||||
@@ -213,8 +210,7 @@
|
||||
.ck.ck-balloon-panel .ck-balloon-panel__content,
|
||||
.ck.ck-math-form,
|
||||
.ck-math-view,
|
||||
.ck.ck-math-input,
|
||||
.ck.ck-math-input .ck-mathlive-container {
|
||||
.ck.ck-math-input {
|
||||
overflow: visible !important;
|
||||
clip-path: none !important;
|
||||
}
|
||||
|
||||
344
pnpm-lock.yaml
generated
344
pnpm-lock.yaml
generated
@@ -209,9 +209,6 @@ importers:
|
||||
'@fullcalendar/timegrid':
|
||||
specifier: 6.1.20
|
||||
version: 6.1.20(@fullcalendar/core@6.1.20)
|
||||
'@lexical/react':
|
||||
specifier: 0.42.0
|
||||
version: 0.42.0(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(yjs@13.6.30)
|
||||
'@maplibre/maplibre-gl-leaflet':
|
||||
specifier: 0.1.3
|
||||
version: 0.1.3(@types/leaflet@1.9.21)(leaflet@1.9.4)(maplibre-gl@5.6.1)
|
||||
@@ -323,9 +320,6 @@ importers:
|
||||
leaflet-gpx:
|
||||
specifier: 2.2.0
|
||||
version: 2.2.0
|
||||
lexical:
|
||||
specifier: 0.42.0
|
||||
version: 0.42.0
|
||||
mark.js:
|
||||
specifier: 8.11.1
|
||||
version: 8.11.1
|
||||
@@ -3117,18 +3111,6 @@ packages:
|
||||
react: '>=16.8.0'
|
||||
react-dom: '>=16.8.0'
|
||||
|
||||
'@floating-ui/react-dom@2.1.8':
|
||||
resolution: {integrity: sha512-cC52bHwM/n/CxS87FH0yWdngEZrjdtLW/qVruo68qg+prK7ZQ4YGdut2GyDVpoGeAYe/h899rVeOVm6Oi40k2A==}
|
||||
peerDependencies:
|
||||
react: '>=16.8.0'
|
||||
react-dom: '>=16.8.0'
|
||||
|
||||
'@floating-ui/react@0.27.19':
|
||||
resolution: {integrity: sha512-31B8h5mm8YxotlE7/AU/PhNAl8eWxAmjL/v2QOxroDNkTFLk3Uu82u63N3b6TXa4EGJeeZLVcd/9AlNlVqzeog==}
|
||||
peerDependencies:
|
||||
react: '>=17.0.0'
|
||||
react-dom: '>=17.0.0'
|
||||
|
||||
'@floating-ui/utils@0.2.11':
|
||||
resolution: {integrity: sha512-RiB/yIh78pcIxl6lLMG0CgBXAZ2Y0eVHqMPYugu+9U0AeT6YBeiJpf7lbdJNIugFP5SIjwNRgo4DhR1Qxi26Gg==}
|
||||
|
||||
@@ -3843,80 +3825,6 @@ packages:
|
||||
'@leichtgewicht/ip-codec@2.0.5':
|
||||
resolution: {integrity: sha512-Vo+PSpZG2/fmgmiNzYK9qWRh8h/CHrwD0mo1h1DzL4yzHNSfWYujGTYsWGreD000gcgmZ7K4Ys6Tx9TxtsKdDw==}
|
||||
|
||||
'@lexical/clipboard@0.42.0':
|
||||
resolution: {integrity: sha512-D3K2ID0zew/+CKpwxnUTTh/N46yU4IK8bFWV9Htz+g1vFhgUF9UnDOQCmqpJbdP7z+9U1F8rk3fzf9OmP2Fm2w==}
|
||||
|
||||
'@lexical/code-core@0.42.0':
|
||||
resolution: {integrity: sha512-vrZTUPWDJkHjAAvuV2+Qte4vYE80s7hIO7wxipiJmWojGx6lcmQjO+UqJ8AIrqI4Wjy8kXrK74kisApWmwxuCw==}
|
||||
|
||||
'@lexical/devtools-core@0.42.0':
|
||||
resolution: {integrity: sha512-8nP8eE9i8JImgSrvInkWFfMCmXVKp3w3VaOvbJysdlK/Zal6xd8EWJEi6elj0mUW5T/oycfipPs2Sfl7Z+n14A==}
|
||||
peerDependencies:
|
||||
react: '>=17.x'
|
||||
react-dom: '>=17.x'
|
||||
|
||||
'@lexical/dragon@0.42.0':
|
||||
resolution: {integrity: sha512-/TQzP+7PLJMqq9+MlgQWiJsxS9GOOa8Gp0svCD8vNIOciYmXfd28TR1Go+ZnBWwr7k/2W++3XUYVQU2KUcQsDQ==}
|
||||
|
||||
'@lexical/extension@0.42.0':
|
||||
resolution: {integrity: sha512-rkZq/h8d1BenKRqU4t/zQUVfY/RinMX1Tz7t+Ee3ss0sk+kzP4W+URXNAxpn7r39Vn6wrFBqmCziah3dLAIqPw==}
|
||||
|
||||
'@lexical/hashtag@0.42.0':
|
||||
resolution: {integrity: sha512-WOg5nFOfhabNBXzEIutdWDj+TUHtJEezj6w8jyYDGqZ31gu0cgrXSeV8UIynz/1oj+rpzEeEB7P6ODnwgjt7qA==}
|
||||
|
||||
'@lexical/history@0.42.0':
|
||||
resolution: {integrity: sha512-YfCZ1ICUt6BCg2ncJWFMuS4yftnB7FEHFRf3qqTSTf6oGZ4IZfzabMNEy47xybUuf7FXBbdaCKJrc/zOM+wGxw==}
|
||||
|
||||
'@lexical/html@0.42.0':
|
||||
resolution: {integrity: sha512-KgBUDLXehufCsXW3w0XsuoI2xecIhouOishnaNOH4zIA7dAtnNAfdPN/kWrWs0s83gz44OrnqccP+Bprw3UDEQ==}
|
||||
|
||||
'@lexical/link@0.42.0':
|
||||
resolution: {integrity: sha512-cdeM/+f+kn7aGwW/3FIi6USjl1gBNdEEwg0/ZS+KlYcsy8gxx2e4cyVjsomBu/WU17Qxa0NC0paSr7qEJ/1Fig==}
|
||||
|
||||
'@lexical/list@0.42.0':
|
||||
resolution: {integrity: sha512-TIezILnmIVuvfqEEbcMnsT4xQRlswI6ysHISqsvKL6l5EBhs1gqmNYjHa/Yrfzaq5y52TM1PAtxbFts+G7N6kg==}
|
||||
|
||||
'@lexical/mark@0.42.0':
|
||||
resolution: {integrity: sha512-H1aGjbMEcL4B8GT7bm/ePHm7j3Wema+wIRNPmxMtXGMz5gpVN3gZlvg2UcUHHJb00SrBA95OUVT5I2nu/KP06w==}
|
||||
|
||||
'@lexical/markdown@0.42.0':
|
||||
resolution: {integrity: sha512-+mOxgBiumlgVX8Acna+9HjJfSOw1jywufGcAQq3/8S11wZ4gE0u13AaR8LMmU8ydVeOQg09y8PNzGNQ/avZJbg==}
|
||||
|
||||
'@lexical/offset@0.42.0':
|
||||
resolution: {integrity: sha512-V+4af1KmTOnBZrR+kU3e6eD33W/g3QqMPPp3cpFwyXk/dKRc4K8HfyDsSDrjop1mPd9pl3lKSiEmX6uQG8K9XQ==}
|
||||
|
||||
'@lexical/overflow@0.42.0':
|
||||
resolution: {integrity: sha512-wlrHaM27rODJP5m+CTgfZGLg3qWlQ0ptGodcqoGdq6HSbV8nGFY6TvcLMaMtYQ1lm4v9G7Xe9LwjooR6xS3Gug==}
|
||||
|
||||
'@lexical/plain-text@0.42.0':
|
||||
resolution: {integrity: sha512-YWvBwIxLltrIaZDcv0rK4s44P6Yt17yhOb0E+g3+tjF8GGPrrocox+Pglu0m2RHR+G7zULN3isolmWIm/HhWiw==}
|
||||
|
||||
'@lexical/react@0.42.0':
|
||||
resolution: {integrity: sha512-ujWJXhvlFVVTpwDcnSgEYWRuqUbreZaMB+4bjIDT5r7hkAplUHQndlkeuFHKFiJBasSAreleV7zhXrLL5xa9eA==}
|
||||
peerDependencies:
|
||||
react: '>=17.x'
|
||||
react-dom: '>=17.x'
|
||||
|
||||
'@lexical/rich-text@0.42.0':
|
||||
resolution: {integrity: sha512-v4YgiM3oK3FZcRrfB+LetvLbQ5aee9MRO9tHf0EFweXg19XnSjHV0cfPAW7TyPxRELzB69+K0Q3AybRlTMjG4Q==}
|
||||
|
||||
'@lexical/selection@0.42.0':
|
||||
resolution: {integrity: sha512-iWTjLA5BSEuUnvWe9Xwu9FSdZFl3Yi0NqalabXKI+7KgCIlIVXE74y4NvWPUSLkSCB/Z1RPKiHmZqZ1vyu/yGQ==}
|
||||
|
||||
'@lexical/table@0.42.0':
|
||||
resolution: {integrity: sha512-GKiZyjQsHDXRckq5VBrOowyvds51WoVRECfDgcl8pqLMnKyEdCa58E7fkSJrr5LS80Scod+Cjn6SBRzOcdsrKg==}
|
||||
|
||||
'@lexical/text@0.42.0':
|
||||
resolution: {integrity: sha512-hT3EYVtBmONXyXe4TFVgtFcG1tf6JhLEuAf95+cOjgFGFSgvkZ/64BPbKLNTj2/9n6cU7EGPUNNwVigCSECJ2g==}
|
||||
|
||||
'@lexical/utils@0.42.0':
|
||||
resolution: {integrity: sha512-wGNdCW3QWEyVdFiSTLZfFPtiASPyYLcekIiYYZmoRVxVimT/jY+QPfnkO4JYgkO7Z70g/dsg9OhqyQSChQfvkQ==}
|
||||
|
||||
'@lexical/yjs@0.42.0':
|
||||
resolution: {integrity: sha512-DplzWnYhfFceGPR+UyDFpZdB287wF/vNOHFuDsBF/nGDdTezvr0Gf60opzyBEF3oXym6p3xTmGygxvO97LZ+vw==}
|
||||
peerDependencies:
|
||||
yjs: '>=13.5.22'
|
||||
|
||||
'@lezer/common@1.5.1':
|
||||
resolution: {integrity: sha512-6YRVG9vBkaY7p1IVxL4s44n5nUnaNnGM2/AckNgYOnxTG2kWh1vR8BMxPseWPjRNpb5VtXnMpeYAEAADoRV1Iw==}
|
||||
|
||||
@@ -10871,9 +10779,6 @@ packages:
|
||||
resolution: {integrity: sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
isomorphic.js@0.2.5:
|
||||
resolution: {integrity: sha512-PIeMbHqMt4DnUP3MA/Flc0HElYjMXArsw1qwJZcm9sqR8mq3l8NYizFMty0pWwE/tzIGH3EKK5+jes5mAr85yw==}
|
||||
|
||||
istanbul-lib-coverage@3.2.2:
|
||||
resolution: {integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==}
|
||||
engines: {node: '>=8'}
|
||||
@@ -11181,17 +11086,9 @@ packages:
|
||||
resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==}
|
||||
engines: {node: '>= 0.8.0'}
|
||||
|
||||
lexical@0.42.0:
|
||||
resolution: {integrity: sha512-GY9Lg3YEIU7nSFaiUlLspZ1fm4NfIcfABaxy9nT+fRVDkX7iV005T5Swil83gXUmxFUNKGal3j+hUxHOUDr+Aw==}
|
||||
|
||||
lezer-elixir@1.1.2:
|
||||
resolution: {integrity: sha512-K3yPMJcNhqCL6ugr5NkgOC1g37rcOM38XZezO9lBXy0LwWFd8zdWXfmRbY829vZVk0OGCQoI02yDWp9FF2OWZA==}
|
||||
|
||||
lib0@0.2.117:
|
||||
resolution: {integrity: sha512-DeXj9X5xDCjgKLU/7RR+/HQEVzuuEUiwldwOGsHK/sfAfELGWEyTcf0x+uOvCvK3O2zPmZePXWL85vtia6GyZw==}
|
||||
engines: {node: '>=16'}
|
||||
hasBin: true
|
||||
|
||||
lie@3.1.1:
|
||||
resolution: {integrity: sha512-RiNhHysUjhrDQntfYSfY4MU24coXXdEOgw9WGcKHNeEwffDYbF//u87M1EWaMGzuFoSbqW0C9C6lEEhDOAswfw==}
|
||||
|
||||
@@ -13326,11 +13223,6 @@ packages:
|
||||
peerDependencies:
|
||||
react: ^19.2.4
|
||||
|
||||
react-error-boundary@6.1.1:
|
||||
resolution: {integrity: sha512-BrYwPOdXi5mqkk5lw+Uvt0ThHx32rCt3BkukS4X23A2AIWDPSGX6iaWTc0y9TU/mHDA/6qOSGel+B2ERkOvD1w==}
|
||||
peerDependencies:
|
||||
react: ^18.0.0 || ^19.0.0
|
||||
|
||||
react-i18next@16.5.8:
|
||||
resolution: {integrity: sha512-2ABeHHlakxVY+LSirD+OiERxFL6+zip0PaHo979bgwzeHg27Sqc82xxXWIrSFmfWX0ZkrvXMHwhsi/NGUf5VQg==}
|
||||
peerDependencies:
|
||||
@@ -14650,9 +14542,6 @@ packages:
|
||||
resolution: {integrity: sha512-gAQ9qrUN/UCypHtGFbbe7Rc/f9bzO88IwrG8TDo/aMKAApKyD6E3W4Cm0EfhfBb6Z6SKt59tTCTfD+n1xmAvMg==}
|
||||
engines: {node: '>=16.0.0'}
|
||||
|
||||
tabbable@6.4.0:
|
||||
resolution: {integrity: sha512-05PUHKSNE8ou2dwIxTngl4EzcnsCDZGJ/iCLtDflR/SHB/ny14rXc+qU5P4mG9JkusiV7EivzY9Mhm55AzAvCg==}
|
||||
|
||||
table@6.9.0:
|
||||
resolution: {integrity: sha512-9kY+CygyYM6j02t5YFHbNz2FN5QmYGv9zAjVp4lCDjlCw7amdckXlEt/bjMhUIfj4ThGRE4gCUH5+yGnNuPo5A==}
|
||||
engines: {node: '>=10.0.0'}
|
||||
@@ -15949,10 +15838,6 @@ packages:
|
||||
resolution: {integrity: sha512-k1isifdbpNSFEHFJ1ZY4YDewv0IH9FR61lDetaRMD3j2ae3bIXGV+7c+LHCqtQGofSd8PIyV4X6+dHMAnSr60A==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
yjs@13.6.30:
|
||||
resolution: {integrity: sha512-vv/9h42eCMC81ZHDFswuu/MKzkl/vyq1BhaNGfHyOonwlG4CJbQF4oiBBJPvfdeCt/PlVDWh7Nov9D34YY09uQ==}
|
||||
engines: {node: '>=16.0.0', npm: '>=8.0.0'}
|
||||
|
||||
yn@3.1.1:
|
||||
resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==}
|
||||
engines: {node: '>=6'}
|
||||
@@ -16962,8 +16847,6 @@ snapshots:
|
||||
'@ckeditor/ckeditor5-utils': 47.6.1
|
||||
'@ckeditor/ckeditor5-widget': 47.6.1
|
||||
es-toolkit: 1.39.5
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@ckeditor/ckeditor5-cloud-services@47.6.1':
|
||||
dependencies:
|
||||
@@ -17240,6 +17123,8 @@ snapshots:
|
||||
'@ckeditor/ckeditor5-core': 47.6.1
|
||||
'@ckeditor/ckeditor5-engine': 47.6.1
|
||||
'@ckeditor/ckeditor5-utils': 47.6.1
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@ckeditor/ckeditor5-essentials@47.6.1':
|
||||
dependencies:
|
||||
@@ -17271,6 +17156,8 @@ snapshots:
|
||||
'@ckeditor/ckeditor5-ui': 47.6.1
|
||||
'@ckeditor/ckeditor5-utils': 47.6.1
|
||||
ckeditor5: 47.6.1
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@ckeditor/ckeditor5-export-word@47.6.1':
|
||||
dependencies:
|
||||
@@ -17295,8 +17182,6 @@ snapshots:
|
||||
'@ckeditor/ckeditor5-utils': 47.6.1
|
||||
ckeditor5: 47.6.1
|
||||
es-toolkit: 1.39.5
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@ckeditor/ckeditor5-font@47.6.1':
|
||||
dependencies:
|
||||
@@ -17433,6 +17318,8 @@ snapshots:
|
||||
'@ckeditor/ckeditor5-ui': 47.6.1
|
||||
'@ckeditor/ckeditor5-utils': 47.6.1
|
||||
ckeditor5: 47.6.1
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@ckeditor/ckeditor5-indent@47.6.1':
|
||||
dependencies:
|
||||
@@ -17547,8 +17434,6 @@ snapshots:
|
||||
'@ckeditor/ckeditor5-utils': 47.6.1
|
||||
'@ckeditor/ckeditor5-widget': 47.6.1
|
||||
ckeditor5: 47.6.1
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@ckeditor/ckeditor5-mention@47.6.1(patch_hash=5981fb59ba35829e4dff1d39cf771000f8a8fdfa7a34b51d8af9549541f2d62d)':
|
||||
dependencies:
|
||||
@@ -17558,6 +17443,8 @@ snapshots:
|
||||
'@ckeditor/ckeditor5-utils': 47.6.1
|
||||
ckeditor5: 47.6.1
|
||||
es-toolkit: 1.39.5
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@ckeditor/ckeditor5-merge-fields@47.6.1':
|
||||
dependencies:
|
||||
@@ -17570,6 +17457,8 @@ snapshots:
|
||||
'@ckeditor/ckeditor5-widget': 47.6.1
|
||||
ckeditor5: 47.6.1
|
||||
es-toolkit: 1.39.5
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@ckeditor/ckeditor5-minimap@47.6.1':
|
||||
dependencies:
|
||||
@@ -17578,6 +17467,8 @@ snapshots:
|
||||
'@ckeditor/ckeditor5-ui': 47.6.1
|
||||
'@ckeditor/ckeditor5-utils': 47.6.1
|
||||
ckeditor5: 47.6.1
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@ckeditor/ckeditor5-operations-compressor@47.6.1':
|
||||
dependencies:
|
||||
@@ -17630,6 +17521,8 @@ snapshots:
|
||||
'@ckeditor/ckeditor5-utils': 47.6.1
|
||||
'@ckeditor/ckeditor5-widget': 47.6.1
|
||||
ckeditor5: 47.6.1
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@ckeditor/ckeditor5-pagination@47.6.1':
|
||||
dependencies:
|
||||
@@ -17663,6 +17556,8 @@ snapshots:
|
||||
'@ckeditor/ckeditor5-core': 47.6.1
|
||||
'@ckeditor/ckeditor5-engine': 47.6.1
|
||||
ckeditor5: 47.6.1
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@ckeditor/ckeditor5-real-time-collaboration@47.6.1(bufferutil@4.0.9)(utf-8-validate@6.0.5)':
|
||||
dependencies:
|
||||
@@ -17693,6 +17588,8 @@ snapshots:
|
||||
'@ckeditor/ckeditor5-ui': 47.6.1
|
||||
'@ckeditor/ckeditor5-utils': 47.6.1
|
||||
ckeditor5: 47.6.1
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@ckeditor/ckeditor5-restricted-editing@47.6.1':
|
||||
dependencies:
|
||||
@@ -17737,6 +17634,8 @@ snapshots:
|
||||
'@ckeditor/ckeditor5-ui': 47.6.1
|
||||
'@ckeditor/ckeditor5-utils': 47.6.1
|
||||
ckeditor5: 47.6.1
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@ckeditor/ckeditor5-slash-command@47.6.1':
|
||||
dependencies:
|
||||
@@ -17749,6 +17648,8 @@ snapshots:
|
||||
'@ckeditor/ckeditor5-ui': 47.6.1
|
||||
'@ckeditor/ckeditor5-utils': 47.6.1
|
||||
ckeditor5: 47.6.1
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@ckeditor/ckeditor5-source-editing-enhanced@47.6.1':
|
||||
dependencies:
|
||||
@@ -17775,6 +17676,8 @@ snapshots:
|
||||
'@ckeditor/ckeditor5-ui': 47.6.1
|
||||
'@ckeditor/ckeditor5-utils': 47.6.1
|
||||
ckeditor5: 47.6.1
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@ckeditor/ckeditor5-special-characters@47.6.1':
|
||||
dependencies:
|
||||
@@ -17796,6 +17699,8 @@ snapshots:
|
||||
'@ckeditor/ckeditor5-utils': 47.6.1
|
||||
ckeditor5: 47.6.1
|
||||
es-toolkit: 1.39.5
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@ckeditor/ckeditor5-table@47.6.1':
|
||||
dependencies:
|
||||
@@ -17808,6 +17713,8 @@ snapshots:
|
||||
'@ckeditor/ckeditor5-widget': 47.6.1
|
||||
ckeditor5: 47.6.1
|
||||
es-toolkit: 1.39.5
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@ckeditor/ckeditor5-template@47.6.1':
|
||||
dependencies:
|
||||
@@ -17917,6 +17824,8 @@ snapshots:
|
||||
'@ckeditor/ckeditor5-engine': 47.6.1
|
||||
'@ckeditor/ckeditor5-utils': 47.6.1
|
||||
es-toolkit: 1.39.5
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@ckeditor/ckeditor5-widget@47.6.1':
|
||||
dependencies:
|
||||
@@ -17936,6 +17845,8 @@ snapshots:
|
||||
'@ckeditor/ckeditor5-utils': 47.6.1
|
||||
ckeditor5: 47.6.1
|
||||
es-toolkit: 1.39.5
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@codemirror/autocomplete@6.18.6':
|
||||
dependencies:
|
||||
@@ -19007,20 +18918,6 @@ snapshots:
|
||||
react: 19.2.4
|
||||
react-dom: 19.2.4(react@19.2.4)
|
||||
|
||||
'@floating-ui/react-dom@2.1.8(react-dom@19.2.4(react@19.2.4))(react@19.2.4)':
|
||||
dependencies:
|
||||
'@floating-ui/dom': 1.7.6
|
||||
react: 19.2.4
|
||||
react-dom: 19.2.4(react@19.2.4)
|
||||
|
||||
'@floating-ui/react@0.27.19(react-dom@19.2.4(react@19.2.4))(react@19.2.4)':
|
||||
dependencies:
|
||||
'@floating-ui/react-dom': 2.1.8(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
|
||||
'@floating-ui/utils': 0.2.11
|
||||
react: 19.2.4
|
||||
react-dom: 19.2.4(react@19.2.4)
|
||||
tabbable: 6.4.0
|
||||
|
||||
'@floating-ui/utils@0.2.11': {}
|
||||
|
||||
'@fsegurai/codemirror-theme-abcdef@6.2.3(@codemirror/language@6.12.2)(@codemirror/state@6.6.0)(@codemirror/view@6.40.0)(@lezer/highlight@1.2.3)':
|
||||
@@ -19768,163 +19665,6 @@ snapshots:
|
||||
|
||||
'@leichtgewicht/ip-codec@2.0.5': {}
|
||||
|
||||
'@lexical/clipboard@0.42.0':
|
||||
dependencies:
|
||||
'@lexical/html': 0.42.0
|
||||
'@lexical/list': 0.42.0
|
||||
'@lexical/selection': 0.42.0
|
||||
'@lexical/utils': 0.42.0
|
||||
lexical: 0.42.0
|
||||
|
||||
'@lexical/code-core@0.42.0':
|
||||
dependencies:
|
||||
lexical: 0.42.0
|
||||
|
||||
'@lexical/devtools-core@0.42.0(react-dom@19.2.4(react@19.2.4))(react@19.2.4)':
|
||||
dependencies:
|
||||
'@lexical/html': 0.42.0
|
||||
'@lexical/link': 0.42.0
|
||||
'@lexical/mark': 0.42.0
|
||||
'@lexical/table': 0.42.0
|
||||
'@lexical/utils': 0.42.0
|
||||
lexical: 0.42.0
|
||||
react: 19.2.4
|
||||
react-dom: 19.2.4(react@19.2.4)
|
||||
|
||||
'@lexical/dragon@0.42.0':
|
||||
dependencies:
|
||||
'@lexical/extension': 0.42.0
|
||||
lexical: 0.42.0
|
||||
|
||||
'@lexical/extension@0.42.0':
|
||||
dependencies:
|
||||
'@lexical/utils': 0.42.0
|
||||
'@preact/signals-core': 1.14.0
|
||||
lexical: 0.42.0
|
||||
|
||||
'@lexical/hashtag@0.42.0':
|
||||
dependencies:
|
||||
'@lexical/text': 0.42.0
|
||||
'@lexical/utils': 0.42.0
|
||||
lexical: 0.42.0
|
||||
|
||||
'@lexical/history@0.42.0':
|
||||
dependencies:
|
||||
'@lexical/extension': 0.42.0
|
||||
'@lexical/utils': 0.42.0
|
||||
lexical: 0.42.0
|
||||
|
||||
'@lexical/html@0.42.0':
|
||||
dependencies:
|
||||
'@lexical/selection': 0.42.0
|
||||
'@lexical/utils': 0.42.0
|
||||
lexical: 0.42.0
|
||||
|
||||
'@lexical/link@0.42.0':
|
||||
dependencies:
|
||||
'@lexical/extension': 0.42.0
|
||||
'@lexical/utils': 0.42.0
|
||||
lexical: 0.42.0
|
||||
|
||||
'@lexical/list@0.42.0':
|
||||
dependencies:
|
||||
'@lexical/extension': 0.42.0
|
||||
'@lexical/selection': 0.42.0
|
||||
'@lexical/utils': 0.42.0
|
||||
lexical: 0.42.0
|
||||
|
||||
'@lexical/mark@0.42.0':
|
||||
dependencies:
|
||||
'@lexical/utils': 0.42.0
|
||||
lexical: 0.42.0
|
||||
|
||||
'@lexical/markdown@0.42.0':
|
||||
dependencies:
|
||||
'@lexical/code-core': 0.42.0
|
||||
'@lexical/link': 0.42.0
|
||||
'@lexical/list': 0.42.0
|
||||
'@lexical/rich-text': 0.42.0
|
||||
'@lexical/text': 0.42.0
|
||||
'@lexical/utils': 0.42.0
|
||||
lexical: 0.42.0
|
||||
|
||||
'@lexical/offset@0.42.0':
|
||||
dependencies:
|
||||
lexical: 0.42.0
|
||||
|
||||
'@lexical/overflow@0.42.0':
|
||||
dependencies:
|
||||
lexical: 0.42.0
|
||||
|
||||
'@lexical/plain-text@0.42.0':
|
||||
dependencies:
|
||||
'@lexical/clipboard': 0.42.0
|
||||
'@lexical/dragon': 0.42.0
|
||||
'@lexical/selection': 0.42.0
|
||||
'@lexical/utils': 0.42.0
|
||||
lexical: 0.42.0
|
||||
|
||||
'@lexical/react@0.42.0(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(yjs@13.6.30)':
|
||||
dependencies:
|
||||
'@floating-ui/react': 0.27.19(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
|
||||
'@lexical/devtools-core': 0.42.0(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
|
||||
'@lexical/dragon': 0.42.0
|
||||
'@lexical/extension': 0.42.0
|
||||
'@lexical/hashtag': 0.42.0
|
||||
'@lexical/history': 0.42.0
|
||||
'@lexical/link': 0.42.0
|
||||
'@lexical/list': 0.42.0
|
||||
'@lexical/mark': 0.42.0
|
||||
'@lexical/markdown': 0.42.0
|
||||
'@lexical/overflow': 0.42.0
|
||||
'@lexical/plain-text': 0.42.0
|
||||
'@lexical/rich-text': 0.42.0
|
||||
'@lexical/table': 0.42.0
|
||||
'@lexical/text': 0.42.0
|
||||
'@lexical/utils': 0.42.0
|
||||
'@lexical/yjs': 0.42.0(yjs@13.6.30)
|
||||
lexical: 0.42.0
|
||||
react: 19.2.4
|
||||
react-dom: 19.2.4(react@19.2.4)
|
||||
react-error-boundary: 6.1.1(react@19.2.4)
|
||||
transitivePeerDependencies:
|
||||
- yjs
|
||||
|
||||
'@lexical/rich-text@0.42.0':
|
||||
dependencies:
|
||||
'@lexical/clipboard': 0.42.0
|
||||
'@lexical/dragon': 0.42.0
|
||||
'@lexical/selection': 0.42.0
|
||||
'@lexical/utils': 0.42.0
|
||||
lexical: 0.42.0
|
||||
|
||||
'@lexical/selection@0.42.0':
|
||||
dependencies:
|
||||
lexical: 0.42.0
|
||||
|
||||
'@lexical/table@0.42.0':
|
||||
dependencies:
|
||||
'@lexical/clipboard': 0.42.0
|
||||
'@lexical/extension': 0.42.0
|
||||
'@lexical/utils': 0.42.0
|
||||
lexical: 0.42.0
|
||||
|
||||
'@lexical/text@0.42.0':
|
||||
dependencies:
|
||||
lexical: 0.42.0
|
||||
|
||||
'@lexical/utils@0.42.0':
|
||||
dependencies:
|
||||
'@lexical/selection': 0.42.0
|
||||
lexical: 0.42.0
|
||||
|
||||
'@lexical/yjs@0.42.0(yjs@13.6.30)':
|
||||
dependencies:
|
||||
'@lexical/offset': 0.42.0
|
||||
'@lexical/selection': 0.42.0
|
||||
lexical: 0.42.0
|
||||
yjs: 13.6.30
|
||||
|
||||
'@lezer/common@1.5.1': {}
|
||||
|
||||
'@lezer/css@1.1.11':
|
||||
@@ -20877,7 +20617,7 @@ snapshots:
|
||||
|
||||
'@radix-ui/react-popper@1.2.8(@types/react-dom@19.1.6(@types/react@19.1.7))(@types/react@19.1.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)':
|
||||
dependencies:
|
||||
'@floating-ui/react-dom': 2.1.8(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
|
||||
'@floating-ui/react-dom': 2.1.2(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
|
||||
'@radix-ui/react-arrow': 1.1.7(@types/react-dom@19.1.6(@types/react@19.1.7))(@types/react@19.1.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
|
||||
'@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.7)(react@19.2.4)
|
||||
'@radix-ui/react-context': 1.1.2(@types/react@19.1.7)(react@19.2.4)
|
||||
@@ -28911,8 +28651,6 @@ snapshots:
|
||||
|
||||
isobject@3.0.1: {}
|
||||
|
||||
isomorphic.js@0.2.5: {}
|
||||
|
||||
istanbul-lib-coverage@3.2.2: {}
|
||||
|
||||
istanbul-lib-report@3.0.1:
|
||||
@@ -29295,17 +29033,11 @@ snapshots:
|
||||
prelude-ls: 1.2.1
|
||||
type-check: 0.4.0
|
||||
|
||||
lexical@0.42.0: {}
|
||||
|
||||
lezer-elixir@1.1.2:
|
||||
dependencies:
|
||||
'@lezer/highlight': 1.2.3
|
||||
'@lezer/lr': 1.4.2
|
||||
|
||||
lib0@0.2.117:
|
||||
dependencies:
|
||||
isomorphic.js: 0.2.5
|
||||
|
||||
lie@3.1.1:
|
||||
dependencies:
|
||||
immediate: 3.0.6
|
||||
@@ -31825,10 +31557,6 @@ snapshots:
|
||||
react: 19.2.4
|
||||
scheduler: 0.27.0
|
||||
|
||||
react-error-boundary@6.1.1(react@19.2.4):
|
||||
dependencies:
|
||||
react: 19.2.4
|
||||
|
||||
react-i18next@16.5.8(i18next@25.8.18(typescript@5.9.3))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3):
|
||||
dependencies:
|
||||
'@babel/runtime': 7.28.6
|
||||
@@ -33489,8 +33217,6 @@ snapshots:
|
||||
sync-message-port@1.2.0:
|
||||
optional: true
|
||||
|
||||
tabbable@6.4.0: {}
|
||||
|
||||
table@6.9.0:
|
||||
dependencies:
|
||||
ajv: 8.18.0
|
||||
@@ -35029,10 +34755,6 @@ snapshots:
|
||||
buffer-crc32: 0.2.13
|
||||
pend: 1.2.0
|
||||
|
||||
yjs@13.6.30:
|
||||
dependencies:
|
||||
lib0: 0.2.117
|
||||
|
||||
yn@3.1.1: {}
|
||||
|
||||
yocto-queue@0.1.0: {}
|
||||
|
||||
Reference in New Issue
Block a user