mirror of
https://github.com/streetwriters/notesnook.git
synced 2026-02-24 04:00:59 +01:00
Merge branch 'beta' into rn-81
This commit is contained in:
4
apps/desktop/package-lock.json
generated
4
apps/desktop/package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@notesnook/desktop",
|
||||
"version": "3.3.5",
|
||||
"version": "3.3.6-beta.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@notesnook/desktop",
|
||||
"version": "3.3.5",
|
||||
"version": "3.3.6-beta.0",
|
||||
"hasInstallScript": true,
|
||||
"license": "GPL-3.0-or-later",
|
||||
"dependencies": {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"name": "@notesnook/desktop",
|
||||
"productName": "Notesnook",
|
||||
"description": "Your private note taking space",
|
||||
"version": "3.3.5",
|
||||
"version": "3.3.6-beta.0",
|
||||
"appAppleId": "1544027013",
|
||||
"private": true,
|
||||
"main": "./dist/cjs/index.js",
|
||||
|
||||
4
apps/web/package-lock.json
generated
4
apps/web/package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@notesnook/web",
|
||||
"version": "3.3.5",
|
||||
"version": "3.3.6-beta.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@notesnook/web",
|
||||
"version": "3.3.5",
|
||||
"version": "3.3.6-beta.0",
|
||||
"hasInstallScript": true,
|
||||
"license": "GPL-3.0-or-later",
|
||||
"dependencies": {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@notesnook/web",
|
||||
"description": "Your private note taking space",
|
||||
"version": "3.3.5",
|
||||
"version": "3.3.6-beta.0",
|
||||
"private": true,
|
||||
"main": "./src/app.js",
|
||||
"homepage": "https://notesnook.com/",
|
||||
|
||||
@@ -30,13 +30,18 @@ export async function handleDrop(
|
||||
item:
|
||||
| ItemReference
|
||||
| Context
|
||||
| { type: "trash" | "notebooks" | "favorites" | undefined }
|
||||
| { type: "trash" | "notebooks" | "favorites" | "archive" | undefined }
|
||||
) {
|
||||
if (!item.type) return;
|
||||
|
||||
const noteIds = getDragData(dataTransfer, "note");
|
||||
const notebookIds = getDragData(dataTransfer, "notebook");
|
||||
const { setColor, favorite, delete: trashNotes } = useNoteStore.getState();
|
||||
const {
|
||||
setColor,
|
||||
favorite,
|
||||
delete: trashNotes,
|
||||
archive
|
||||
} = useNoteStore.getState();
|
||||
switch (item.type) {
|
||||
case "notebook":
|
||||
if (noteIds.length > 0) {
|
||||
@@ -83,5 +88,8 @@ export async function handleDrop(
|
||||
await useNoteStore.getState().refresh();
|
||||
}
|
||||
break;
|
||||
case "archive":
|
||||
archive(true, ...noteIds);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -118,7 +118,7 @@ function countCharacters(text: string) {
|
||||
function countParagraphs(fragment: Fragment) {
|
||||
let count = 0;
|
||||
fragment.nodesBetween(0, fragment.size, (node) => {
|
||||
if (node.type.name === "paragraph") {
|
||||
if (node.type.name === "paragraph" && node.content.size > 0) {
|
||||
count++;
|
||||
}
|
||||
return true;
|
||||
@@ -737,7 +737,7 @@ function toIEditor(editor: Editor): IEditor {
|
||||
function getSelectedParagraphs(editor: Editor, selection: Selection): number {
|
||||
let count = 0;
|
||||
editor.state.doc.nodesBetween(selection.from, selection.to, (node) => {
|
||||
if (node.type.name === "paragraph") {
|
||||
if (node.type.name === "paragraph" && node.content.size > 0) {
|
||||
count++;
|
||||
}
|
||||
return true;
|
||||
|
||||
@@ -506,6 +506,8 @@ function RouteItem({
|
||||
? "trash"
|
||||
: item.path === "/favorites"
|
||||
? "favorites"
|
||||
: item.path == "/archive"
|
||||
? "archive"
|
||||
: undefined
|
||||
});
|
||||
}}
|
||||
|
||||
@@ -91,18 +91,7 @@ const features: Record<FeatureKeys, Feature> = {
|
||||
)
|
||||
}
|
||||
]
|
||||
: [
|
||||
{
|
||||
title: "Notesnook Circle",
|
||||
subtitle:
|
||||
"Notesnook Circle brings together trusted partners who share our commitment to privacy, transparency, and user freedom. As a member you get exclusive discounts and offers from our partners. Check it out in Settings > Notesnook Circle."
|
||||
},
|
||||
{
|
||||
title: "Collapsible headings",
|
||||
subtitle:
|
||||
"You can now collapse and expand headings in your notes. This makes it easier to manage large notes and focus on specific sections."
|
||||
}
|
||||
],
|
||||
: [],
|
||||
cta: {
|
||||
title: strings.gotIt(),
|
||||
icon: Checkmark,
|
||||
|
||||
@@ -666,6 +666,15 @@ class UserManager {
|
||||
usesFallback: await this.usesFallbackPWHash(old_password)
|
||||
});
|
||||
|
||||
// retrieve user keys before deriving a new encryption key
|
||||
const oldUserKeys = {
|
||||
attachmentsKey: await this.getAttachmentsKey(),
|
||||
monographPasswordsKey: await this.getMonographPasswordsKey(),
|
||||
inboxKeys: (await this.hasInboxKeys())
|
||||
? await this.getInboxKeys()
|
||||
: undefined
|
||||
} as const;
|
||||
|
||||
await this.db.storage().deriveCryptoKey({
|
||||
password: new_password,
|
||||
salt
|
||||
@@ -678,27 +687,33 @@ class UserManager {
|
||||
const userEncryptionKey = await this.getEncryptionKey();
|
||||
if (userEncryptionKey) {
|
||||
const updateUserPayload: Partial<User> = {};
|
||||
const attachmentsKey = await this.getAttachmentsKey();
|
||||
if (attachmentsKey) {
|
||||
if (oldUserKeys.attachmentsKey) {
|
||||
user.attachmentsKey = await this.db
|
||||
.storage()
|
||||
.encrypt(userEncryptionKey, JSON.stringify(attachmentsKey));
|
||||
.encrypt(
|
||||
userEncryptionKey,
|
||||
JSON.stringify(oldUserKeys.attachmentsKey)
|
||||
);
|
||||
updateUserPayload.attachmentsKey = user.attachmentsKey;
|
||||
}
|
||||
const monographPasswordsKey = await this.getMonographPasswordsKey();
|
||||
if (monographPasswordsKey) {
|
||||
if (oldUserKeys.monographPasswordsKey) {
|
||||
user.monographPasswordsKey = await this.db
|
||||
.storage()
|
||||
.encrypt(userEncryptionKey, JSON.stringify(monographPasswordsKey));
|
||||
.encrypt(
|
||||
userEncryptionKey,
|
||||
JSON.stringify(oldUserKeys.monographPasswordsKey)
|
||||
);
|
||||
updateUserPayload.monographPasswordsKey = user.monographPasswordsKey;
|
||||
}
|
||||
const inboxKeys = await this.getInboxKeys();
|
||||
if (inboxKeys) {
|
||||
if (oldUserKeys.inboxKeys) {
|
||||
user.inboxKeys = {
|
||||
public: inboxKeys.publicKey,
|
||||
public: oldUserKeys.inboxKeys.publicKey,
|
||||
private: await this.db
|
||||
.storage()
|
||||
.encrypt(userEncryptionKey, JSON.stringify(inboxKeys.privateKey))
|
||||
.encrypt(
|
||||
userEncryptionKey,
|
||||
JSON.stringify(oldUserKeys.inboxKeys.privateKey)
|
||||
)
|
||||
};
|
||||
updateUserPayload.inboxKeys = user.inboxKeys;
|
||||
}
|
||||
|
||||
@@ -3,3 +3,13 @@
|
||||
exports[`collapse heading > heading collapsed 1`] = `"<h1 data-collapsed="true">Main Heading</h1><p>paragraph.</p><h2>Subheading</h2><p>subheading paragraph</p><h1>Main heading 2</h1><p>paragraph another</p>"`;
|
||||
|
||||
exports[`collapse heading > heading uncollapsed 1`] = `"<h1>Main Heading</h1><p>paragraph.</p><h2>Subheading</h2><p>subheading paragraph</p><h1>Main heading 2</h1><p>paragraph another</p>"`;
|
||||
|
||||
exports[`replacing collapsed heading with another heading level should not unhide content 1`] = `"<h2 data-collapsed="true">A collapsed heading</h2><p data-hidden="true">Hidden paragraph</p>"`;
|
||||
|
||||
exports[`replacing collapsed heading with another node (blockquote) should unhide content 1`] = `"<blockquote><h1 data-collapsed="true">A collpased heading</h1></blockquote><p>Hidden paragraph</p>"`;
|
||||
|
||||
exports[`replacing collapsed heading with another node (bulletList) should unhide content 1`] = `"<ul><li><p>A collpased heading</p></li></ul><p>Hidden paragraph</p>"`;
|
||||
|
||||
exports[`replacing collapsed heading with another node (codeBlock) should unhide content 1`] = `"<pre><code>A collpased heading</code></pre><p>Hidden paragraph</p>"`;
|
||||
|
||||
exports[`replacing collapsed heading with another node (paragraph) should unhide content 1`] = `"<p>A collpased heading</p><p>Hidden paragraph</p>"`;
|
||||
|
||||
@@ -18,8 +18,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { test, expect } from "vitest";
|
||||
import { createEditor } from "../../../../test-utils/index.js";
|
||||
import { createEditor, h } from "../../../../test-utils/index.js";
|
||||
import { Heading } from "../heading.js";
|
||||
import { Editor } from "@tiptap/core";
|
||||
|
||||
test("collapse heading", () => {
|
||||
const { editor } = createEditor({
|
||||
@@ -52,3 +53,59 @@ test("collapse heading", () => {
|
||||
|
||||
expect(editor.getHTML()).toMatchSnapshot("heading uncollapsed");
|
||||
});
|
||||
|
||||
test("replacing collapsed heading with another heading level should not unhide content", () => {
|
||||
const el = h("div", [
|
||||
h("h1", ["A collapsed heading"], { "data-collapsed": "true" }),
|
||||
h("p", ["Hidden paragraph"], { "data-hidden": "true" })
|
||||
]);
|
||||
const { editor } = createEditor({
|
||||
extensions: {
|
||||
heading: Heading.configure({ levels: [1, 2, 3, 4, 5, 6] })
|
||||
},
|
||||
initialContent: el.outerHTML
|
||||
});
|
||||
|
||||
editor.commands.setTextSelection(0);
|
||||
editor.commands.setHeading({ level: 2 });
|
||||
|
||||
expect(editor.getHTML()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
const nodes: { name: string; setNode: (editor: Editor) => void }[] = [
|
||||
{
|
||||
name: "paragraph",
|
||||
setNode: (editor) => editor.commands.setParagraph()
|
||||
},
|
||||
{
|
||||
name: "codeBlock",
|
||||
setNode: (editor) => editor.commands.setCodeBlock()
|
||||
},
|
||||
{
|
||||
name: "bulletList",
|
||||
setNode: (editor) => editor.commands.toggleList("bulletList", "listItem")
|
||||
},
|
||||
{
|
||||
name: "blockquote",
|
||||
setNode: (editor) => editor.commands.toggleBlockquote()
|
||||
}
|
||||
];
|
||||
for (const { name, setNode } of nodes) {
|
||||
test(`replacing collapsed heading with another node (${name}) should unhide content`, () => {
|
||||
const el = h("div", [
|
||||
h("h1", ["A collpased heading"], { "data-collapsed": "true" }),
|
||||
h("p", ["Hidden paragraph"], { "data-hidden": "true" })
|
||||
]);
|
||||
const { editor } = createEditor({
|
||||
extensions: {
|
||||
heading: Heading.configure({ levels: [1, 2, 3, 4, 5, 6] })
|
||||
},
|
||||
initialContent: el.outerHTML
|
||||
});
|
||||
|
||||
editor.commands.setTextSelection(0);
|
||||
setNode(editor);
|
||||
|
||||
expect(editor.getHTML()).toMatchSnapshot();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -23,10 +23,8 @@ import {
|
||||
textblockTypeInputRule
|
||||
} from "@tiptap/core";
|
||||
import { Heading as TiptapHeading } from "@tiptap/extension-heading";
|
||||
import { isClickWithinBounds } from "../../utils/prosemirror.js";
|
||||
import { Plugin, PluginKey, Selection, Transaction } from "@tiptap/pm/state";
|
||||
import { Node } from "@tiptap/pm/model";
|
||||
import { useToolbarStore } from "../../toolbar/stores/toolbar-store.js";
|
||||
|
||||
const COLLAPSIBLE_BLOCK_TYPES = [
|
||||
"paragraph",
|
||||
@@ -168,6 +166,14 @@ export const Heading = TiptapHeading.extend({
|
||||
addNodeView() {
|
||||
return ({ node, getPos, editor, HTMLAttributes }) => {
|
||||
const heading = document.createElement(`h${node.attrs.level}`);
|
||||
const contentWrapper = document.createElement("div");
|
||||
const icon = document.createElement("span");
|
||||
|
||||
// providing a minWidth so that empty headings show the blinking cursor
|
||||
contentWrapper.style.minWidth = "1px";
|
||||
|
||||
icon.className = "heading-collapse-icon";
|
||||
icon.contentEditable = "false";
|
||||
|
||||
for (const attr in HTMLAttributes) {
|
||||
heading.setAttribute(attr, HTMLAttributes[attr]);
|
||||
@@ -176,50 +182,46 @@ export const Heading = TiptapHeading.extend({
|
||||
if (node.attrs.collapsed) heading.dataset.collapsed = "true";
|
||||
else delete heading.dataset.collapsed;
|
||||
|
||||
function onClick(e: MouseEvent | TouchEvent) {
|
||||
if (e instanceof MouseEvent && e.button !== 0) return;
|
||||
if (!(e.target instanceof HTMLHeadingElement)) return;
|
||||
function onIconClick(e: MouseEvent | TouchEvent) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
e.stopImmediatePropagation();
|
||||
|
||||
const pos = typeof getPos === "function" ? getPos() : 0;
|
||||
if (typeof pos !== "number") return;
|
||||
|
||||
const resolvedPos = editor.state.doc.resolve(pos);
|
||||
const calloutAncestor = findParentNodeClosestToPos(
|
||||
resolvedPos,
|
||||
(node) => node.type.name === "callout"
|
||||
);
|
||||
if (calloutAncestor) return;
|
||||
|
||||
const forbiddenParents = ["callout"];
|
||||
if (
|
||||
isClickWithinBounds(
|
||||
e,
|
||||
resolvedPos,
|
||||
useToolbarStore.getState().isMobile ? "right" : "left"
|
||||
findParentNodeClosestToPos(resolvedPos, (node) =>
|
||||
forbiddenParents.includes(node.type.name)
|
||||
)
|
||||
) {
|
||||
e.preventDefault();
|
||||
e.stopImmediatePropagation();
|
||||
|
||||
editor.commands.command(({ tr }) => {
|
||||
const currentNode = tr.doc.nodeAt(pos);
|
||||
if (currentNode && currentNode.type.name === "heading") {
|
||||
const shouldCollapse = !currentNode.attrs.collapsed;
|
||||
const headingLevel = currentNode.attrs.level;
|
||||
|
||||
tr.setNodeAttribute(pos, "collapsed", shouldCollapse);
|
||||
toggleNodesUnderHeading(tr, pos, headingLevel, shouldCollapse);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
editor.commands.command(({ tr }) => {
|
||||
const currentNode = tr.doc.nodeAt(pos);
|
||||
if (currentNode && currentNode.type.name === "heading") {
|
||||
const shouldCollapse = !currentNode.attrs.collapsed;
|
||||
const headingLevel = currentNode.attrs.level;
|
||||
|
||||
tr.setNodeAttribute(pos, "collapsed", shouldCollapse);
|
||||
toggleNodesUnderPos(tr, pos, headingLevel, shouldCollapse);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
heading.onmousedown = onClick;
|
||||
heading.ontouchstart = onClick;
|
||||
icon.onmousedown = onIconClick;
|
||||
icon.ontouchend = onIconClick;
|
||||
|
||||
heading.appendChild(contentWrapper);
|
||||
heading.appendChild(icon);
|
||||
|
||||
return {
|
||||
dom: heading,
|
||||
contentDOM: heading,
|
||||
contentDOM: contentWrapper,
|
||||
update: (updatedNode) => {
|
||||
if (updatedNode.type !== this.type) {
|
||||
return false;
|
||||
@@ -253,17 +255,17 @@ export const Heading = TiptapHeading.extend({
|
||||
}
|
||||
});
|
||||
|
||||
function toggleNodesUnderHeading(
|
||||
function toggleNodesUnderPos(
|
||||
tr: Transaction,
|
||||
headingPos: number,
|
||||
pos: number,
|
||||
headingLevel: number,
|
||||
isCollapsing: boolean
|
||||
) {
|
||||
const { doc } = tr;
|
||||
const headingNode = doc.nodeAt(headingPos);
|
||||
if (!headingNode || headingNode.type.name !== "heading") return;
|
||||
const node = doc.nodeAt(pos);
|
||||
if (!node) return;
|
||||
|
||||
let nextPos = headingPos + headingNode.nodeSize;
|
||||
let nextPos = pos + node.nodeSize;
|
||||
const cursorPos = tr.selection.from;
|
||||
let shouldMoveCursor = false;
|
||||
let insideCollapsedHeading = false;
|
||||
@@ -318,8 +320,8 @@ function toggleNodesUnderHeading(
|
||||
}
|
||||
|
||||
if (shouldMoveCursor) {
|
||||
const headingEndPos = headingPos + headingNode.nodeSize - 1;
|
||||
tr.setSelection(Selection.near(tr.doc.resolve(headingEndPos)));
|
||||
const endPos = pos + node.nodeSize - 1;
|
||||
tr.setSelection(Selection.near(tr.doc.resolve(endPos)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -364,26 +366,27 @@ const headingUpdatePlugin = new Plugin({
|
||||
let modified = false;
|
||||
|
||||
newDoc.descendants((newNode, pos) => {
|
||||
if (newNode.type.name === "heading") {
|
||||
if (pos >= oldDoc.content.size) return;
|
||||
if (pos >= oldDoc.content.size) return;
|
||||
|
||||
const oldNode = oldDoc.nodeAt(pos);
|
||||
if (
|
||||
oldNode &&
|
||||
oldNode.type.name === "heading" &&
|
||||
oldNode.attrs.level !== newNode.attrs.level
|
||||
) {
|
||||
/**
|
||||
* if the level of a collapsed heading is changed,
|
||||
* we need to reset visibility of all the nodes under it as there
|
||||
* might be a heading of same or higher level previously
|
||||
* hidden under this heading
|
||||
*/
|
||||
if (newNode.attrs.collapsed) {
|
||||
toggleNodesUnderHeading(tr, pos, oldNode.attrs.level, false);
|
||||
toggleNodesUnderHeading(tr, pos, newNode.attrs.level, true);
|
||||
modified = true;
|
||||
}
|
||||
const oldNode = oldDoc.nodeAt(pos);
|
||||
if (
|
||||
oldNode &&
|
||||
oldNode.type.name === "heading" &&
|
||||
oldNode.attrs.level !== newNode.attrs.level
|
||||
) {
|
||||
/**
|
||||
* if the level of a collapsed heading is changed,
|
||||
* we need to reset visibility of all the nodes under it as there
|
||||
* might be a heading of same or higher level previously
|
||||
* hidden under this heading
|
||||
*/
|
||||
if (newNode.type.name === "heading" && newNode.attrs.collapsed) {
|
||||
toggleNodesUnderPos(tr, pos, oldNode.attrs.level, false);
|
||||
toggleNodesUnderPos(tr, pos, newNode.attrs.level, true);
|
||||
modified = true;
|
||||
} else if (newNode.type.name !== "heading" && oldNode.attrs.collapsed) {
|
||||
toggleNodesUnderPos(tr, pos, oldNode.attrs.level, false);
|
||||
modified = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -316,6 +316,31 @@ img.ProseMirror-separator {
|
||||
.ProseMirror table p {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.ProseMirror td > h1:first-child,
|
||||
.ProseMirror td > h2:first-child,
|
||||
.ProseMirror td > h3:first-child,
|
||||
.ProseMirror td > h4:first-child,
|
||||
.ProseMirror td > h5:first-child,
|
||||
.ProseMirror td > h6:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.ProseMirror td > ol,
|
||||
.ProseMirror td > ul {
|
||||
padding-left: 20px;
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.ProseMirror td > blockquote {
|
||||
margin-left: 0;
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.ProseMirror td > blockquote > :first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
/*
|
||||
.resize-cursor {
|
||||
|
||||
@@ -739,6 +764,10 @@ p > *::selection {
|
||||
mask-size: cover;
|
||||
}
|
||||
|
||||
.simple-checklist > li.checked p {
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
/* Callout */
|
||||
.ProseMirror div.callout {
|
||||
padding: 15px;
|
||||
@@ -866,150 +895,133 @@ del.diffdel {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.ProseMirror h1,
|
||||
.ProseMirror h2,
|
||||
.ProseMirror h3,
|
||||
.ProseMirror h4,
|
||||
.ProseMirror h5,
|
||||
.ProseMirror h1 ,
|
||||
.ProseMirror h2 ,
|
||||
.ProseMirror h3 ,
|
||||
.ProseMirror h4 ,
|
||||
.ProseMirror h5 ,
|
||||
.ProseMirror h6 {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.ProseMirror h1::before,
|
||||
.ProseMirror h2::before,
|
||||
.ProseMirror h3::before,
|
||||
.ProseMirror h4::before,
|
||||
.ProseMirror h5::before,
|
||||
.ProseMirror h6::before {
|
||||
position: absolute;
|
||||
.ProseMirror h1 .heading-collapse-icon,
|
||||
.ProseMirror h2 .heading-collapse-icon,
|
||||
.ProseMirror h3 .heading-collapse-icon,
|
||||
.ProseMirror h4 .heading-collapse-icon,
|
||||
.ProseMirror h5 .heading-collapse-icon,
|
||||
.ProseMirror h6 .heading-collapse-icon {
|
||||
cursor: pointer;
|
||||
content: "";
|
||||
background-size: 18px;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
margin-inline-start: 8px;
|
||||
|
||||
background-color: var(--icon);
|
||||
mask: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxZW0iIGhlaWdodD0iMWVtIiB2aWV3Qm94PSIwIDAgMjQgMjQiPjxwYXRoIGZpbGw9IiM4ODg4ODgiIGQ9Ik03LjQxIDguNThMMTIgMTMuMTdsNC41OS00LjU5TDE4IDEwbC02IDZsLTYtNmwxLjQxLTEuNDJaIi8+PC9zdmc+)
|
||||
no-repeat 50% 50%;
|
||||
mask-size: cover;
|
||||
border: 1px solid var(--background);
|
||||
|
||||
transform: rotate(0);
|
||||
transition: transform 250ms ease, opacity 200ms ease;
|
||||
left: -22px;
|
||||
opacity: 0;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.ProseMirror h1[dir="rtl"]::before,
|
||||
.ProseMirror h2[dir="rtl"]::before,
|
||||
.ProseMirror h3[dir="rtl"]::before,
|
||||
.ProseMirror h4[dir="rtl"]::before,
|
||||
.ProseMirror h5[dir="rtl"]::before,
|
||||
.ProseMirror h6[dir="rtl"]::before {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.ProseMirror h1[dir="rtl"]::after,
|
||||
.ProseMirror h2[dir="rtl"]::after,
|
||||
.ProseMirror h3[dir="rtl"]::after,
|
||||
.ProseMirror h4[dir="rtl"]::after,
|
||||
.ProseMirror h5[dir="rtl"]::after,
|
||||
.ProseMirror h6[dir="rtl"]::after {
|
||||
position: absolute;
|
||||
cursor: pointer;
|
||||
content: "";
|
||||
background-size: 18px;
|
||||
.ProseMirror h1 .heading-collapse-icon {
|
||||
margin-top: 3.5px;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
|
||||
background-color: var(--icon);
|
||||
mask: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxZW0iIGhlaWdodD0iMWVtIiB2aWV3Qm94PSIwIDAgMjQgMjQiPjxwYXRoIGZpbGw9IiM4ODg4ODgiIGQ9Ik03LjQxIDguNThMMTIgMTMuMTdsNC41OS00LjU5TDE4IDEwbC02IDZsLTYtNmwxLjQxLTEuNDJaIi8+PC9zdmc+)
|
||||
no-repeat 50% 50%;
|
||||
mask-size: cover;
|
||||
border: 1px solid var(--background);
|
||||
|
||||
transform: rotate(0deg);
|
||||
transition: transform 250ms ease, opacity 200ms ease;
|
||||
right: -22px;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.ProseMirror h1::before,
|
||||
.ProseMirror h1::after {
|
||||
top: 8px;
|
||||
.ProseMirror h2 .heading-collapse-icon {
|
||||
margin-top: 3px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
|
||||
.ProseMirror h2::before,
|
||||
.ProseMirror h2::after
|
||||
{
|
||||
top: 3px;
|
||||
.ProseMirror h3 .heading-collapse-icon {
|
||||
margin-top: 2.3px;
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
}
|
||||
|
||||
.ProseMirror h3::before,
|
||||
.ProseMirror h3::after {
|
||||
top: 0px;
|
||||
.ProseMirror h4 .heading-collapse-icon {
|
||||
margin-top: 1.8px;
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
}
|
||||
|
||||
.ProseMirror h4::before,
|
||||
.ProseMirror h4::after {
|
||||
top: -1px;
|
||||
.ProseMirror h5 .heading-collapse-icon {
|
||||
margin-top: 1.3px;
|
||||
width: 13px;
|
||||
height: 13px;
|
||||
}
|
||||
|
||||
.ProseMirror h5::before,
|
||||
.ProseMirror h5::after {
|
||||
top: -2px;
|
||||
.ProseMirror h6 .heading-collapse-icon {
|
||||
margin-top: 0.3px;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
}
|
||||
|
||||
.ProseMirror h6::before,
|
||||
.ProseMirror h6::after {
|
||||
top: -4px;
|
||||
}
|
||||
|
||||
.ProseMirror h1[data-collapsed="true"]::before,
|
||||
.ProseMirror h2[data-collapsed="true"]::before,
|
||||
.ProseMirror h3[data-collapsed="true"]::before,
|
||||
.ProseMirror h4[data-collapsed="true"]::before,
|
||||
.ProseMirror h5[data-collapsed="true"]::before,
|
||||
.ProseMirror h6[data-collapsed="true"]::before {
|
||||
.ProseMirror h1[data-collapsed="true"] .heading-collapse-icon,
|
||||
.ProseMirror h2[data-collapsed="true"] .heading-collapse-icon,
|
||||
.ProseMirror h3[data-collapsed="true"] .heading-collapse-icon,
|
||||
.ProseMirror h4[data-collapsed="true"] .heading-collapse-icon,
|
||||
.ProseMirror h5[data-collapsed="true"] .heading-collapse-icon,
|
||||
.ProseMirror h6[data-collapsed="true"] .heading-collapse-icon {
|
||||
transform: rotate(-90deg);
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.ProseMirror h1[data-collapsed="true"]::after,
|
||||
.ProseMirror h2[data-collapsed="true"]::after,
|
||||
.ProseMirror h3[data-collapsed="true"]::after,
|
||||
.ProseMirror h4[data-collapsed="true"]::after,
|
||||
.ProseMirror h5[data-collapsed="true"]::after,
|
||||
.ProseMirror h6[data-collapsed="true"]::after {
|
||||
.ProseMirror h1[data-collapsed="true"][dir="rtl"] .heading-collapse-icon,
|
||||
.ProseMirror h2[data-collapsed="true"][dir="rtl"] .heading-collapse-icon,
|
||||
.ProseMirror h3[data-collapsed="true"][dir="rtl"] .heading-collapse-icon,
|
||||
.ProseMirror h4[data-collapsed="true"][dir="rtl"] .heading-collapse-icon,
|
||||
.ProseMirror h5[data-collapsed="true"][dir="rtl"] .heading-collapse-icon,
|
||||
.ProseMirror h6[data-collapsed="true"][dir="rtl"] .heading-collapse-icon {
|
||||
transform: rotate(90deg);
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
|
||||
.ProseMirror h1:hover::before,
|
||||
.ProseMirror h2:hover::before,
|
||||
.ProseMirror h3:hover::before,
|
||||
.ProseMirror h4:hover::before,
|
||||
.ProseMirror h5:hover::before,
|
||||
.ProseMirror h6:hover::before,
|
||||
.ProseMirror h1:hover::after,
|
||||
.ProseMirror h2:hover::after,
|
||||
.ProseMirror h3:hover::after,
|
||||
.ProseMirror h4:hover::after,
|
||||
.ProseMirror h5:hover::after,
|
||||
.ProseMirror h6:hover::after {
|
||||
.ProseMirror h1:hover .heading-collapse-icon,
|
||||
.ProseMirror h2:hover .heading-collapse-icon,
|
||||
.ProseMirror h3:hover .heading-collapse-icon,
|
||||
.ProseMirror h4:hover .heading-collapse-icon,
|
||||
.ProseMirror h5:hover .heading-collapse-icon,
|
||||
.ProseMirror h6:hover .heading-collapse-icon {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.ProseMirror div.callout h1::before,
|
||||
.ProseMirror div.callout h2::before,
|
||||
.ProseMirror div.callout h3::before,
|
||||
.ProseMirror div.callout h4::before,
|
||||
.ProseMirror div.callout h5::before,
|
||||
.ProseMirror div.callout h6::before {
|
||||
.ProseMirror div.callout h1 .heading-collapse-icon,
|
||||
.ProseMirror div.callout h2 .heading-collapse-icon,
|
||||
.ProseMirror div.callout h3 .heading-collapse-icon,
|
||||
.ProseMirror div.callout h4 .heading-collapse-icon,
|
||||
.ProseMirror div.callout h5 .heading-collapse-icon,
|
||||
.ProseMirror div.callout h6 .heading-collapse-icon {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* hide collapse icon when heading is empty (only contains trailing break) */
|
||||
.ProseMirror h1:has(> div > br.ProseMirror-trailingBreak:only-child) .heading-collapse-icon,
|
||||
.ProseMirror h2:has(> div > br.ProseMirror-trailingBreak:only-child) .heading-collapse-icon,
|
||||
.ProseMirror h3:has(> div > br.ProseMirror-trailingBreak:only-child) .heading-collapse-icon,
|
||||
.ProseMirror h4:has(> div > br.ProseMirror-trailingBreak:only-child) .heading-collapse-icon,
|
||||
.ProseMirror h5:has(> div > br.ProseMirror-trailingBreak:only-child) .heading-collapse-icon,
|
||||
.ProseMirror h6:has(> div > br.ProseMirror-trailingBreak:only-child) .heading-collapse-icon {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 768px) {
|
||||
.ProseMirror h1 .heading-collapse-icon,
|
||||
.ProseMirror h2 .heading-collapse-icon,
|
||||
.ProseMirror h3 .heading-collapse-icon,
|
||||
.ProseMirror h4 .heading-collapse-icon,
|
||||
.ProseMirror h5 .heading-collapse-icon,
|
||||
.ProseMirror h6 .heading-collapse-icon {
|
||||
opacity: 1 !important;
|
||||
}
|
||||
}
|
||||
|
||||
[data-hidden="true"] {
|
||||
display: none !important;
|
||||
}
|
||||
@@ -1028,3 +1040,4 @@ del.diffdel {
|
||||
pre[class*="language-"] {
|
||||
overflow: initial !important;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user