Skip to content

[WIKI-400] feat: page navigation pane #7206

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 27 commits into from
Jul 2, 2025
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
f434365
init: page navigation pane
aaryan610 Jun 6, 2025
40a723b
chore: outline and info tabs
aaryan610 Jun 10, 2025
cf463a4
chore: asset download endpoint
aaryan610 Jun 12, 2025
aab6c50
chore: realtime document info updates
aaryan610 Jun 12, 2025
8b74388
fix: merge conflicts resolved from preview
aaryan610 Jun 12, 2025
78a4e38
chore: add support for code splitting
aaryan610 Jun 13, 2025
00cd4ab
fix: merge conflicts resolved from preview
aaryan610 Jun 13, 2025
aa8ae78
fix: formatting
aaryan610 Jun 13, 2025
3a7f84b
refactor: image block id generation
aaryan610 Jun 13, 2025
553aa62
chore: implement translation
aaryan610 Jun 13, 2025
37bd7d4
refactor: assets list storage logic
aaryan610 Jun 13, 2025
6640b57
fix: merge conflicts resolved from preview
aaryan610 Jun 16, 2025
ab2e5ff
fix: build errors
aaryan610 Jun 16, 2025
cfb5ae6
fix: image extension name
aaryan610 Jun 17, 2025
69565b0
Merge branch 'preview' of https://github.com/makeplane/plane into fea…
aaryan610 Jun 17, 2025
68f902c
refactor: add support for additional asset items
aaryan610 Jun 17, 2025
28b867c
refactor: asset extraction logic
aaryan610 Jun 17, 2025
d6fc5cc
chore: add translations
aaryan610 Jun 17, 2025
a9979b2
fix: merge conflicts resolved from preview
aaryan610 Jun 19, 2025
71b8b30
fix: merge conflicts resolved from preview
aaryan610 Jun 19, 2025
2b251b6
chore: remove version history option from the dropdown
aaryan610 Jun 19, 2025
68df2a7
fix: merge conflicts resolved from preview
aaryan610 Jun 23, 2025
8d0e1e1
chore: query params handling
aaryan610 Jun 23, 2025
8b73186
fix: merge conflicts resolved from preview
aaryan610 Jun 24, 2025
441f237
chore: remove unnecessary logic
aaryan610 Jun 24, 2025
2f6b42d
refactor: empty state components
aaryan610 Jun 24, 2025
b5f41eb
fix: empty state asset path
aaryan610 Jun 24, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions admin/styles/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,7 @@
text-rendering: optimizeLegibility;
-moz-osx-font-smoothing: grayscale;
-webkit-font-smoothing: antialiased;
scroll-behavior: smooth;
}

body {
Expand Down
6 changes: 6 additions & 0 deletions packages/editor/src/ce/constants/assets.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// helpers
import { TAssetMetaDataRecord } from "@/helpers/assets";
// local imports
import { ADDITIONAL_EXTENSIONS } from "./extensions";

export const ADDITIONAL_ASSETS_META_DATA_RECORD: Partial<Record<ADDITIONAL_EXTENSIONS, TAssetMetaDataRecord>> = {};
1 change: 1 addition & 0 deletions packages/editor/src/ce/constants/extensions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export enum ADDITIONAL_EXTENSIONS {}
1 change: 1 addition & 0 deletions packages/editor/src/ce/types/asset.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export type TAdditionalEditorAsset = never;
2 changes: 2 additions & 0 deletions packages/editor/src/ce/types/storage.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { CharacterCountStorage } from "@tiptap/extension-character-count";
// constants
import { CORE_EXTENSIONS } from "@/constants/extension";
// extensions
Expand All @@ -15,6 +16,7 @@ export type ExtensionStorageMap = {
[CORE_EXTENSIONS.HEADINGS_LIST]: HeadingExtensionStorage;
[CORE_EXTENSIONS.MENTION]: MentionExtensionStorage;
[CORE_EXTENSIONS.UTILITY]: UtilityExtensionStorage;
[CORE_EXTENSIONS.CHARACTER_COUNT]: CharacterCountStorage;
};

export type ExtensionFileSetStorageKey = Extract<keyof ImageExtensionStorage, "deletedImageSet">;
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ import { EditorRefApi, ICollaborativeDocumentEditor } from "@/types";

const CollaborativeDocumentEditor = (props: ICollaborativeDocumentEditor) => {
const {
onTransaction,
aiHandler,
bubbleMenuEnabled = true,
containerClassName,
Expand All @@ -31,6 +30,8 @@ const CollaborativeDocumentEditor = (props: ICollaborativeDocumentEditor) => {
handleEditorReady,
id,
mentionHandler,
onAssetChange,
onTransaction,
placeholder,
realtimeConfig,
serverHandler,
Expand Down Expand Up @@ -60,6 +61,7 @@ const CollaborativeDocumentEditor = (props: ICollaborativeDocumentEditor) => {
handleEditorReady,
id,
mentionHandler,
onAssetChange,
onTransaction,
placeholder,
realtimeConfig,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ const ensurePixelString = <TDefault,>(value: Pixel | TDefault | number | undefin
return value;
};

export const getImageBlockId = (id: string) => `editor-image-block-${id}`;

type CustomImageBlockProps = CustomBaseImageNodeViewProps & {
imageFromFileSystem: string | undefined;
setFailedToLoadImage: (isError: boolean) => void;
Expand Down Expand Up @@ -222,6 +224,7 @@ export const CustomImageBlock: React.FC<CustomImageBlockProps> = (props) => {

return (
<div
id={getImageBlockId(node.attrs.id ?? "")}
ref={containerRef}
className="group/image-component relative inline-block max-w-full"
onMouseDown={handleImageMouseDown}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { CORE_EXTENSIONS } from "@/constants/extension";
// extensions
import { CustomBaseImageNodeViewProps, getImageComponentImageFileMap } from "@/extensions/custom-image";
// helpers
import { CORE_ASSETS_META_DATA_RECORD } from "@/helpers/assets";
import { EFileError } from "@/helpers/file";
import { getExtensionStorage } from "@/helpers/get-extension-storage";
// hooks
Expand Down Expand Up @@ -49,6 +50,16 @@ export const CustomImageUploader = (props: CustomImageUploaderProps) => {
src: url,
});
imageComponentImageFileMap?.delete(imageEntityId);
const updatedAttrs: CustomImageUploaderProps["node"]["attrs"] = {
...node.attrs,
src: url,
};
const assetMetaData = CORE_ASSETS_META_DATA_RECORD[CORE_EXTENSIONS.CUSTOM_IMAGE]?.(updatedAttrs);
if (assetMetaData) {
editor.commands.updateAssetsList?.({
asset: assetMetaData,
});
}

const pos = getPos();
// get current node
Expand Down Expand Up @@ -163,7 +174,7 @@ export const CustomImageUploader = (props: CustomImageUploaderProps) => {
}

return "Add an image";
}, [draggedInside, failedToLoadImage, isImageBeingUploaded]);
}, [draggedInside, editor.isEditable, failedToLoadImage, isImageBeingUploaded]);

return (
<div
Expand Down
29 changes: 27 additions & 2 deletions packages/editor/src/core/extensions/utility.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { Extension } from "@tiptap/core";
// prosemirror plugins
import codemark from "prosemirror-codemark";
// helpers
import { restorePublicImages } from "@/helpers/image-helpers";
Expand All @@ -8,17 +7,27 @@ import { DropHandlerPlugin } from "@/plugins/drop";
import { FilePlugins } from "@/plugins/file/root";
import { MarkdownClipboardPlugin } from "@/plugins/markdown-clipboard";
// types
import { TExtensions, TFileHandler, TReadOnlyFileHandler } from "@/types";
import { TEditorAsset, TExtensions, TFileHandler, TReadOnlyFileHandler } from "@/types";

declare module "@tiptap/core" {
interface Commands {
utility: {
updateAssetsUploadStatus: (updatedStatus: TFileHandler["assetsUploadStatus"]) => () => void;
updateAssetsList: (
args:
| {
asset: TEditorAsset;
}
| {
idToRemove: string;
}
) => () => void;
};
}
}

export interface UtilityExtensionStorage {
assetsList: TEditorAsset[];
assetsUploadStatus: TFileHandler["assetsUploadStatus"];
uploadInProgress: boolean;
}
Expand Down Expand Up @@ -59,6 +68,7 @@ export const UtilityExtension = (props: Props) => {

addStorage() {
return {
assetsList: [],
assetsUploadStatus: isEditable && "assetsUploadStatus" in fileHandler ? fileHandler.assetsUploadStatus : {},
uploadInProgress: false,
};
Expand All @@ -69,6 +79,21 @@ export const UtilityExtension = (props: Props) => {
updateAssetsUploadStatus: (updatedStatus) => () => {
this.storage.assetsUploadStatus = updatedStatus;
},
updateAssetsList: (args) => () => {
const uniqueAssets = new Set(this.storage.assetsList);
if ("asset" in args) {
const alreadyExists = this.storage.assetsList.find((asset) => asset.id === args.asset.id);
if (!alreadyExists) {
uniqueAssets.add(args.asset);
}
} else if ("idToRemove" in args) {
const asset = this.storage.assetsList.find((asset) => asset.id === args.idToRemove);
if (asset) {
uniqueAssets.delete(asset);
}
}
this.storage.assetsList = Array.from(uniqueAssets);
},
};
},
});
Expand Down
37 changes: 37 additions & 0 deletions packages/editor/src/core/helpers/assets.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { Node as ProseMirrorNode } from "@tiptap/pm/model";
// constants
import { CORE_EXTENSIONS } from "@/constants/extension";
// extensions
import { getImageBlockId } from "@/extensions/custom-image/components/image-block";
// plane editor imports
import { ADDITIONAL_ASSETS_META_DATA_RECORD } from "@/plane-editor/constants/assets";
// types
import { TEditorAsset } from "@/types";

export type TAssetMetaDataRecord = (attrs: ProseMirrorNode["attrs"]) => TEditorAsset | undefined;

export const CORE_ASSETS_META_DATA_RECORD: Partial<Record<CORE_EXTENSIONS, TAssetMetaDataRecord>> = {
[CORE_EXTENSIONS.IMAGE]: (attrs) => {
if (!attrs?.src) return;
return {
href: `#${getImageBlockId(attrs?.id ?? "")}`,
id: attrs?.id,
name: `image-${attrs?.id}`,
size: 0,
src: attrs?.src,
type: CORE_EXTENSIONS.IMAGE,
};
},
[CORE_EXTENSIONS.CUSTOM_IMAGE]: (attrs) => {
if (!attrs?.src) return;
return {
href: `#${getImageBlockId(attrs?.id ?? "")}`,
id: attrs?.id,
name: `image-${attrs?.id}`,
size: 0,
src: attrs?.src,
type: CORE_EXTENSIONS.CUSTOM_IMAGE,
};
},
...ADDITIONAL_ASSETS_META_DATA_RECORD,
};
55 changes: 55 additions & 0 deletions packages/editor/src/core/helpers/editor-ref.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { HocuspocusProvider } from "@hocuspocus/provider";
import { Editor } from "@tiptap/core";
import * as Y from "yjs";
// constants
import { CORE_EXTENSIONS } from "@/constants/extension";
import { CORE_EDITOR_META } from "@/constants/meta";
// types
import { EditorReadOnlyRefApi } from "@/types";
// local imports
import { getParagraphCount } from "./common";
import { getExtensionStorage } from "./get-extension-storage";
import { scrollSummary } from "./scroll-to-node";

type TArgs = {
editor: Editor | null;
provider: HocuspocusProvider | undefined;
};

export const getEditorRefHelpers = (args: TArgs): EditorReadOnlyRefApi => {
const { editor, provider } = args;

return {
clearEditor: (emitUpdate = false) => {
editor?.chain().setMeta(CORE_EDITOR_META.SKIP_FILE_DELETION, true).clearContent(emitUpdate).run();
},
getDocument: () => {
const documentBinary = provider?.document ? Y.encodeStateAsUpdate(provider?.document) : null;
const documentHTML = editor?.getHTML() ?? "<p></p>";
const documentJSON = editor?.getJSON() ?? null;

return {
binary: documentBinary,
html: documentHTML,
json: documentJSON,
};
},
getDocumentInfo: () => ({
characters: editor ? getExtensionStorage(editor, CORE_EXTENSIONS.CHARACTER_COUNT)?.characters?.() : 0,
paragraphs: getParagraphCount(editor?.state),
words: editor ? getExtensionStorage(editor, CORE_EXTENSIONS.CHARACTER_COUNT)?.words?.() : 0,
}),
getHeadings: () => (editor ? getExtensionStorage(editor, CORE_EXTENSIONS.HEADINGS_LIST)?.headings : []),
getMarkDown: () => {
const markdownOutput = editor?.storage.markdown.getMarkdown();
return markdownOutput;
},
scrollSummary: (marking) => {
if (!editor) return;
scrollSummary(editor, marking);
},
setEditorValue: (content, emitUpdate = false) => {
editor?.commands.setContent(content, emitUpdate, { preserveWhitespace: true });
},
};
};
2 changes: 2 additions & 0 deletions packages/editor/src/core/hooks/use-collaborative-editor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { TCollaborativeEditorProps } from "@/types";

export const useCollaborativeEditor = (props: TCollaborativeEditorProps) => {
const {
onAssetChange,
onTransaction,
disabledExtensions,
editable,
Expand Down Expand Up @@ -102,6 +103,7 @@ export const useCollaborativeEditor = (props: TCollaborativeEditorProps) => {
forwardedRef,
handleEditorReady,
mentionHandler,
onAssetChange,
onTransaction,
placeholder,
provider,
Expand Down
Loading