Compare commits

...
Author SHA1 Message Date
Philipinho 92e2278d49 remove stale overrides 2026-08-06 20:30:00 +01:00
Philipinho f3d6b4b2a7 chore: package updates 2026-08-06 18:57:03 +01:00
Philip Okugbe 56ac42767a fix: stale placeholder decorations on empty paragraphs (#2374) 2026-08-02 21:07:50 +01:00
Philipinho 38380211a5 fix: dedupe duplicate packages 2026-08-01 11:57:05 +01:00
Philip Okugbe cd34594b5d fix: placeholder bug (#2373)
* fix placeholder check

* tiptap v3.29.2

* add s3 max socket env
2026-08-01 11:45:25 +01:00
Philip Okugbe 70d2ff8685 fix: increase s3 client max socket limit (#2355) 2026-07-21 22:59:25 +01:00
Philipinho 0ba2d78660 chore: update pnpm and axios 2026-07-20 20:46:02 +01:00
Philip Okugbe 3ed505d2be fix: toolbar flicker on navigation (#2352) 2026-07-20 20:36:03 +01:00
13 changed files with 2087 additions and 2510 deletions
+1 -1
View File
@@ -1,7 +1,7 @@
FROM node:26-slim AS base
LABEL org.opencontainers.image.source="https://github.com/docmost/docmost"
RUN npm install -g pnpm@11.13.0
RUN npm install -g pnpm@11.15.1
FROM base AS builder
+3 -3
View File
@@ -36,7 +36,7 @@
"@tanstack/react-table": "8.21.3",
"@tanstack/react-virtual": "3.14.3",
"alfaaz": "1.1.0",
"axios": "1.16.0",
"axios": "1.18.1",
"blueimp-load-image": "5.16.0",
"clsx": "2.1.1",
"file-saver": "2.0.5",
@@ -61,7 +61,7 @@
"react-error-boundary": "6.1.1",
"react-helmet-async": "3.0.0",
"react-i18next": "16.5.8",
"react-router-dom": "7.18.0",
"react-router-dom": "7.18.2",
"semver": "7.7.4",
"socket.io-client": "4.8.3",
"zod": "4.3.6"
@@ -86,7 +86,7 @@
"globals": "15.13.0",
"jsdom": "25.0.0",
"optics-ts": "2.4.1",
"postcss": "8.5.14",
"postcss": "8.5.25",
"postcss-preset-mantine": "1.18.0",
"postcss-simple-vars": "7.0.1",
"prettier": "3.8.1",
@@ -6,6 +6,7 @@
z-index: 99;
display: flex;
align-items: center;
min-height: 45px;
background: var(--mantine-color-body);
border-bottom: 1px solid
light-dark(var(--mantine-color-gray-2), var(--mantine-color-dark-4));
@@ -31,8 +31,6 @@ export const FixedToolbar: FC<FixedToolbarProps> = ({
const workspace = useAtomValue(workspaceAtom);
const isGenerativeAiEnabled = workspace?.settings?.ai?.generative === true;
if (!editor || !state) return null;
return (
<>
<div
@@ -49,22 +47,26 @@ export const FixedToolbar: FC<FixedToolbarProps> = ({
<div className={classes.divider} />
</>
)} */}
<BlockTypeGroup editor={editor} />
<div className={classes.divider} />
<InlineMarksGroup editor={editor} state={state} />
<div className={classes.divider} />
<ColorGroup editor={editor} />
<div className={classes.divider} />
<ListsGroup editor={editor} state={state} />
<div className={classes.divider} />
<AlignmentGroup editor={editor} />
<div className={classes.divider} />
<MediaGroup editor={editor} templateMode={templateMode} />
<div className={classes.divider} />
<QuickInsertsGroup editor={editor} />
<MoreInsertsGroup editor={editor} templateMode={templateMode} />
<div className={classes.divider} />
<HistoryGroup editor={editor} state={state} />
{editor && state && (
<>
<BlockTypeGroup editor={editor} />
<div className={classes.divider} />
<InlineMarksGroup editor={editor} state={state} />
<div className={classes.divider} />
<ColorGroup editor={editor} />
<div className={classes.divider} />
<ListsGroup editor={editor} state={state} />
<div className={classes.divider} />
<AlignmentGroup editor={editor} />
<div className={classes.divider} />
<MediaGroup editor={editor} templateMode={templateMode} />
<div className={classes.divider} />
<QuickInsertsGroup editor={editor} />
<MoreInsertsGroup editor={editor} templateMode={templateMode} />
<div className={classes.divider} />
<HistoryGroup editor={editor} state={state} />
</>
)}
</div>
</div>
<div className={classes.spacer} aria-hidden />
@@ -3,7 +3,8 @@ import { StarterKit } from "@tiptap/starter-kit";
import { Code } from "@tiptap/extension-code";
import { TextAlign } from "@tiptap/extension-text-align";
import { TaskList, TaskItem } from "@tiptap/extension-list";
import { Placeholder, CharacterCount, UndoRedo } from "@tiptap/extensions";
import { CharacterCount, UndoRedo } from "@tiptap/extensions";
import { Placeholder } from "@/features/editor/extensions/placeholder";
import { Superscript } from "@tiptap/extension-superscript";
import SubScript from "@tiptap/extension-subscript";
import { Typography } from "@tiptap/extension-typography";
@@ -194,16 +195,18 @@ export const mainExtensions = [
return i18n.t("Toggle title");
}
if (node.type.name === "paragraph") {
const $pos = editor.state.doc.resolve(pos);
const parentName = $pos.parent.type.name;
if (
parentName === "column" ||
parentName === "tableCell" ||
parentName === "tableHeader" ||
parentName === "callout" ||
parentName === "blockquote"
) {
return i18n.t("Write...");
const doc = editor.state.doc;
if (pos >= 0 && pos <= doc.content.size) {
const parentName = doc.resolve(pos).parent.type.name;
if (
parentName === "column" ||
parentName === "tableCell" ||
parentName === "tableHeader" ||
parentName === "callout" ||
parentName === "blockquote"
) {
return i18n.t("Write...");
}
}
return i18n.t('Write anything. Enter "/" for commands');
}
@@ -0,0 +1,64 @@
import { isNodeEmpty } from "@tiptap/core";
import { Plugin, PluginKey } from "@tiptap/pm/state";
import { Decoration, DecorationSet } from "@tiptap/pm/view";
import { Placeholder as TiptapPlaceholder } from "@tiptap/extensions";
export const Placeholder = TiptapPlaceholder.extend({
addProseMirrorPlugins() {
const editor = this.editor;
const options = this.options;
const dataAttribute = `data-${options.dataAttribute || "placeholder"}`;
return [
new Plugin({
key: new PluginKey("docmostPlaceholder"),
props: {
decorations: (state) => {
if (options.showOnlyWhenEditable && !editor.isEditable) {
return null;
}
const { doc, selection } = state;
const { anchor } = selection;
const decorations: Decoration[] = [];
const isEmptyDoc = editor.isEmpty;
doc.descendants((node, pos) => {
if (!node.type.isTextblock) {
return options.includeChildren;
}
const hasAnchor = anchor >= pos && anchor <= pos + node.nodeSize;
const isEmpty = !node.isLeaf && isNodeEmpty(node);
if ((hasAnchor || !options.showOnlyCurrent) && isEmpty) {
const emptyNodeClass =
typeof options.emptyNodeClass === "function"
? options.emptyNodeClass({ editor, node, pos, hasAnchor })
: options.emptyNodeClass;
const classes = [emptyNodeClass];
if (isEmptyDoc) {
classes.push(options.emptyEditorClass);
}
decorations.push(
Decoration.node(pos, pos + node.nodeSize, {
class: classes.join(" "),
[dataAttribute]:
typeof options.placeholder === "function"
? options.placeholder({ editor, node, pos, hasAnchor })
: options.placeholder,
}),
);
}
return options.includeChildren;
});
return DecorationSet.create(doc, decorations);
},
},
}),
];
},
});
@@ -2,6 +2,7 @@ import "@/features/editor/styles/index.css";
import React, {
useCallback,
useEffect,
useLayoutEffect,
useMemo,
useRef,
useState,
@@ -100,11 +101,13 @@ export default function PageEditor({
const { pageSlug } = useParams();
const slugId = extractPageSlugId(pageSlug);
const [socket] = useState(getCollabSocket);
const hasCollabToken = !!collabQuery?.token;
useEffect(() => {
if (!hasCollabToken) return;
acquireCollabSocket();
return () => releaseCollabSocket();
}, []);
}, [hasCollabToken]);
const handleStateless = ({ payload }: onStatelessParameters) => {
try {
@@ -322,6 +325,16 @@ function CollabPageEditor({
[pageId, editable, extensions],
);
useLayoutEffect(() => {
if (editor && !editor.isDestroyed) {
// @ts-ignore
setEditor(editor);
// @ts-ignore
editor.storage.pageId = pageId;
editorRef.current = editor;
}
}, [editor, pageId, setEditor]);
const editorIsEditable = useEditorState({
editor,
selector: (ctx) => {
+7 -7
View File
@@ -40,14 +40,14 @@
"@clickhouse/client": "1.18.2",
"@docmost/base-formula": "workspace:*",
"@docmost/pdf-inspector": "1.9.6",
"@fastify/cookie": "^11.0.2",
"@fastify/multipart": "^10.0.0",
"@fastify/static": "^9.1.3",
"@keyv/redis": "^5.1.6",
"@fastify/cookie": "11.0.2",
"@fastify/multipart": "10.0.0",
"@fastify/static": "10.1.2",
"@keyv/redis": "5.1.6",
"@langchain/core": "1.1.46",
"@langchain/textsplitters": "1.0.1",
"@modelcontextprotocol/sdk": "1.29.0",
"@nest-lab/throttler-storage-redis": "^1.2.0",
"@modelcontextprotocol/sdk": "1.30.0",
"@nest-lab/throttler-storage-redis": "1.2.0",
"@nestjs-labs/nestjs-ioredis": "11.0.4",
"@nestjs/bullmq": "11.0.4",
"@nestjs/cache-manager": "3.1.3",
@@ -118,7 +118,7 @@
"tlds": "1.261.0",
"tmp-promise": "3.0.3",
"typesense": "3.0.5",
"undici": "7.28.0",
"undici": "7.29.0",
"ws": "8.21.0",
"yauzl": "3.4.0",
"zod": "4.3.6"
@@ -15,13 +15,21 @@ import { getMimeType } from '../../../common/helpers';
import { Upload } from '@aws-sdk/lib-storage';
import { Logger } from '@nestjs/common';
const S3_MAX_SOCKETS = parseInt(process.env.AWS_S3_MAX_SOCKETS) || 200;
export class S3Driver implements StorageDriver {
private readonly s3Client: S3Client;
private readonly config: S3StorageConfig;
constructor(config: S3StorageConfig) {
this.config = config;
this.s3Client = new S3Client(config as any);
this.config = {
...config,
requestHandler: {
httpAgent: { maxSockets: S3_MAX_SOCKETS },
httpsAgent: { maxSockets: S3_MAX_SOCKETS },
},
};
this.s3Client = new S3Client(this.config as any);
}
async upload(filePath: string, file: Buffer | Readable): Promise<void> {
+33 -33
View File
@@ -31,41 +31,41 @@
"@joplin/turndown": "4.0.82",
"@joplin/turndown-plugin-gfm": "1.0.64",
"@sindresorhus/slugify": "3.0.0",
"@tiptap/core": "3.28.0",
"@tiptap/extension-audio": "3.28.0",
"@tiptap/extension-code-block": "3.28.0",
"@tiptap/extension-collaboration": "3.28.0",
"@tiptap/extension-collaboration-caret": "3.28.0",
"@tiptap/extension-color": "3.28.0",
"@tiptap/extension-document": "3.28.0",
"@tiptap/extension-heading": "3.28.0",
"@tiptap/extension-highlight": "3.28.0",
"@tiptap/extension-history": "3.28.0",
"@tiptap/extension-image": "3.28.0",
"@tiptap/extension-link": "3.28.0",
"@tiptap/extension-list": "3.28.0",
"@tiptap/extension-placeholder": "3.28.0",
"@tiptap/extension-subscript": "3.28.0",
"@tiptap/extension-superscript": "3.28.0",
"@tiptap/extension-table": "3.28.0",
"@tiptap/extension-text": "3.28.0",
"@tiptap/extension-text-align": "3.28.0",
"@tiptap/extension-text-style": "3.28.0",
"@tiptap/extension-typography": "3.28.0",
"@tiptap/extension-unique-id": "3.28.0",
"@tiptap/extension-youtube": "3.28.0",
"@tiptap/html": "3.28.0",
"@tiptap/pm": "3.28.0",
"@tiptap/react": "3.28.0",
"@tiptap/starter-kit": "3.28.0",
"@tiptap/suggestion": "3.28.0",
"@tiptap/core": "3.29.2",
"@tiptap/extension-audio": "3.29.2",
"@tiptap/extension-code-block": "3.29.2",
"@tiptap/extension-collaboration": "3.29.2",
"@tiptap/extension-collaboration-caret": "3.29.2",
"@tiptap/extension-color": "3.29.2",
"@tiptap/extension-document": "3.29.2",
"@tiptap/extension-heading": "3.29.2",
"@tiptap/extension-highlight": "3.29.2",
"@tiptap/extension-history": "3.29.2",
"@tiptap/extension-image": "3.29.2",
"@tiptap/extension-link": "3.29.2",
"@tiptap/extension-list": "3.29.2",
"@tiptap/extension-placeholder": "3.29.2",
"@tiptap/extension-subscript": "3.29.2",
"@tiptap/extension-superscript": "3.29.2",
"@tiptap/extension-table": "3.29.2",
"@tiptap/extension-text": "3.29.2",
"@tiptap/extension-text-align": "3.29.2",
"@tiptap/extension-text-style": "3.29.2",
"@tiptap/extension-typography": "3.29.2",
"@tiptap/extension-unique-id": "3.29.2",
"@tiptap/extension-youtube": "3.29.2",
"@tiptap/html": "3.29.2",
"@tiptap/pm": "3.29.2",
"@tiptap/react": "3.29.2",
"@tiptap/starter-kit": "3.29.2",
"@tiptap/suggestion": "3.29.2",
"@tiptap/y-tiptap": "3.0.7",
"bytes": "3.1.2",
"cross-env": "10.1.0",
"date-fns": "4.1.0",
"diff": "8.0.3",
"docx": "9.7.1",
"dompurify": "3.4.11",
"dompurify": "3.4.12",
"fractional-indexing-jittered": "1.0.0",
"highlight.js": "11.11.1",
"image-dimensions": "2.5.0",
@@ -81,12 +81,12 @@
"yjs": "^13.6.30"
},
"devDependencies": {
"@nx/js": "22.6.1",
"@nx/js": "22.7.2",
"@types/bytes": "3.1.5",
"@types/qrcode": "1.5.6",
"@types/turndown": "5.0.6",
"concurrently": "9.2.3",
"nx": "22.6.1",
"concurrently": "10.0.4",
"nx": "22.7.2",
"tsx": "^4.21.0"
},
"workspaces": {
@@ -95,5 +95,5 @@
"packages/*"
]
},
"packageManager": "pnpm@11.13.0"
"packageManager": "pnpm@11.15.1"
}
+1910 -2396
View File
File diff suppressed because it is too large Load Diff
+9 -37
View File
@@ -5,56 +5,28 @@ patchedDependencies:
scimmy@1.3.5: patches/scimmy@1.3.5.patch
overrides:
prosemirror-changeset: 2.4.0
y-prosemirror: 1.3.7
glob: 13.0.6
ws: 8.21.0
dompurify: 3.4.11
tmp: 0.2.7
hono: 4.12.25
dompurify: 3.4.12
mermaid: 11.15.0
undici: 7.29.0
tmp: 0.2.7
nanoid@^3: 3.3.8
socket.io-parser: 4.2.6
serialize-javascript: 7.0.3
lodash-es: 4.18.1
lodash: 4.18.1
'@hono/node-server': 1.19.13
undici: 7.28.0
ajv@^6: 6.14.0
ajv@^8: 8.18.0
underscore: 1.13.8
immutable: 4.3.8
express-rate-limit: 8.2.2
minimatch@^3: 3.1.5
minimatch@^5: 5.1.8
flatted: 3.4.2
picomatch@<2.3.2: 2.3.2
picomatch@>=4.0.0 <4.0.4: 4.0.4
fastify: 5.8.5
yaml@>=1.0.0 <1.10.3: 1.10.3
find-my-way: 9.7.0
yaml@>=2.0.0 <2.8.3: 2.8.3
path-to-regexp@^8: 8.4.0
brace-expansion@^5: 5.0.6
'@xmldom/xmldom': 0.8.13
handlebars: 4.7.9
axios: 1.16.0
langsmith: 0.7.0
follow-redirects: 1.16.0
protobufjs: 7.5.8
ip-address: 10.1.1
fast-uri: 3.1.3
brace-expansion@^5: 5.0.9
axios: 1.18.1
ip-address: 10.3.1
fast-uri: 3.1.5
form-data@>=4.0.0 <4.0.6: 4.0.6
nanoid@>=4.0.0 <5.0.9: 5.1.16
qs: 6.15.3
esbuild@>=0.27.3 <0.28.1: 0.28.1
'@babel/core@<=7.29.0': 7.29.7
'@opentelemetry/core@>=2.0.0 <2.8.0': 2.9.0
'@babel/plugin-transform-modules-systemjs@<=7.29.3': 7.29.7
brace-expansion@<1.1.13: 1.1.15
brace-expansion@>=2.0.0 <2.0.3: 2.0.3
js-yaml@>=3.0.0 <3.15.0: 3.15.0
js-yaml@>=4.0.0 <=4.1.1: 4.3.0
shamefullyHoist: true
minimumReleaseAge: 5760
minimumReleaseAge: 4320
allowBuilds:
'@swc/core': true
bcrypt: true