Skip to content
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
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,013 changes: 800 additions & 213 deletions package-lock.json

Large diffs are not rendered by default.

10 changes: 9 additions & 1 deletion packages/app/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,12 @@ TELEMETRY=off
TELEMETRY_DEBUG=on
TELEMETRY_SYNC_INTERVAL=10s
TELEMETRY_URL=https://uxt.vitonsky.net
TELEMETRY_APP_NAME=deepink
TELEMETRY_APP_NAME=deepink

# Used for translate locales
OPENAI_API_URL=https://openrouter.ai/api/v1
OPENAI_API_KEY=

# Tests
HEADLESS_BROWSER=on
#PLAYWRIGHT_BROWSER_PATH=/usr/bin/chromium-browser
3 changes: 2 additions & 1 deletion packages/app/.gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
.transly
chakra-config.json
chakra-config.json
.vitest-attachments
8 changes: 6 additions & 2 deletions packages/app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@
"@types/unbzip2-stream": "^1.4.3",
"@types/uuid": "^11.0.0",
"@types/webpack": "^5.28.5",
"@vitest/browser": "^4.1.10",
"@vitest/browser-playwright": "^4.1.10",
"@vitest/web-worker": "^4.1.1",
"blob-polyfill": "^9.0.20240710",
"copy-webpack-plugin": "^13.0.1",
Expand All @@ -83,6 +85,7 @@
"mini-css-extract-plugin": "^2.10.0",
"node-loader": "^2.1.0",
"p-limit": "^7.2.0",
"playwright": "^1.61.1",
"react-refresh": "^0.18.0",
"sharp": "^0.34.5",
"sharp-ico": "^0.1.5",
Expand All @@ -97,6 +100,7 @@
"typescript-eslint": "^8.54.0",
"unbzip2-stream": "^1.4.3",
"vite-tsconfig-paths": "^6.1.1",
"vitest": "^4.1.10",
"webpack": "^5.106.2",
"webpack-cli": "^6.0.1",
"webpack-dev-server": "^5.2.4",
Expand All @@ -109,7 +113,7 @@
"@headless-tree/core": "^1.7.0",
"@headless-tree/react": "^1.7.0",
"@hookform/resolvers": "^5.2.2",
"@lexical/react": "^0.39.0",
"@lexical/react": "^0.46.0",
"@msgpack/msgpack": "^3.1.3",
"@reduxjs/toolkit": "^2.11.2",
"@tanstack/react-virtual": "^3.14.2",
Expand Down Expand Up @@ -143,7 +147,7 @@
"i18next-http-backend": "^3.0.4",
"idb-keyval": "^6.2.2",
"leviathan-crypto": "^1.4.0",
"lexical": "^0.39.0",
"lexical": "^0.46.0",
"libsodium-wrappers-sumo": "^0.8.2",
"lodash": "^4.17.23",
"lru-cache": "^11.2.7",
Expand Down
20 changes: 8 additions & 12 deletions packages/app/src/components/theme/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,7 @@
============================================================
*/
/* eslint-disable @cspell/spellchecker */
import {
defineConfig,
defineRecipe,
defineSlotRecipe,
SystemStyleObject,
} from '@chakra-ui/react';
import { defineConfig, defineRecipe, defineSlotRecipe } from '@chakra-ui/react';
import {
alertAnatomy,
dialogAnatomy,
Expand Down Expand Up @@ -82,6 +77,13 @@ export const getScrollBarStyles = (): Record<string, any> => {

export default defineConfig({
globalCss: {
html: {
'& *::selection': {
color: 'selection.foreground',
backgroundColor: 'selection.background',
},
},

body: {
margin: 0,
backgroundColor: 'var(--chakra-colors-surface-background)',
Expand All @@ -97,12 +99,6 @@ export default defineConfig({
'Segoe UI Emoji'`,
},

':root': {
'::selection': {
color: 'selection.foreground',
backgroundColor: 'selection.background',
},
} as SystemStyleObject,
...getScrollBarStyles(),
},

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React, { memo } from 'react';
import { CodeHighlightNode, CodeNode } from '@lexical/code';
import { CodeHighlightNode, CodeNode } from '@lexical/code-core';
import { AutoLinkNode, LinkNode } from '@lexical/link';
import { ListItemNode, ListNode } from '@lexical/list';
import { MarkNode } from '@lexical/mark';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
import React, { Ref, useEffect, useMemo } from 'react';
import { $createRangeSelection, $getRoot, $getSelection, $setSelection } from 'lexical';
import React, { Ref, useEffect, useImperativeHandle, useMemo } from 'react';
import {
$createRangeSelection,
$getRoot,
$getSelection,
$setSelection,
LexicalEditor,
} from 'lexical';
import { Box, useSlotRecipe } from '@chakra-ui/react';
import { CheckListPlugin } from '@lexical/react/LexicalCheckListPlugin';
import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext';
Expand Down Expand Up @@ -43,6 +49,7 @@ export type RichEditorContentProps = RichTextContainerProps &
isReadOnly?: boolean;
search?: string;
apiRef?: Ref<RichEditorAPI>;
editorRef?: Ref<LexicalEditor>;
};

export const RichEditorContent = ({
Expand All @@ -52,6 +59,7 @@ export const RichEditorContent = ({
isReadOnly,
search,
apiRef,
editorRef,
...props
}: RichEditorContentProps) => {
const recipe = useSlotRecipe({ key: 'richEditor' });
Expand All @@ -61,6 +69,8 @@ export const RichEditorContent = ({

// Expose API
const [editor] = useLexicalComposerContext();
useImperativeHandle(editorRef, () => editor);

const api = useMemo(() => {
return {
focus() {
Expand Down Expand Up @@ -99,6 +109,7 @@ export const RichEditorContent = ({
width="100%"
height="100%"
overflow="auto"
className="RichEditor"
css={{
...styles.root,

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import React, { act, createRef } from 'react';
import { createRoot } from 'react-dom/client';
import { Provider } from 'react-redux';
import { createEvent } from 'effector';
import { LexicalEditor } from 'lexical';
import { FilesController } from '@core/features/files/FilesController';
import {
FilesRegistryContext,
NotesContext,
NotesRegistryContext,
} from '@features/App/Workspace/WorkspaceProvider';
import {
editorPanelContext,
InsertingPayload,
TextFormat,
} from '@features/NoteEditor/EditorPanel';
import { RichEditor } from '@features/NoteEditor/RichEditor/RichEditor';
import { RichEditorContentProps } from '@features/NoteEditor/RichEditor/RichEditorContent';
import { ThemeProvider } from '@features/ThemeProvider';
import { createTestStore } from '@tests/utils/redux';

// Mock useUrlOpener to avoid importing monaco-editor-core in tests,
// which causes Vite module resolution errors during test setup
vi.mock('@hooks/useUrlOpener', () => ({
useUrlOpener: () => vi.fn(),
}));

export const MockWorkspaceProvider = ({ children }: { children: React.ReactNode }) => {
const filesRegistry = {
add: vi.fn(),
get: vi.fn(),
delete: vi.fn(),
query: vi.fn(),
} as unknown as FilesController;

const notesControl = {
get: vi.fn(),
updateBatch: vi.fn(),
getById: vi.fn(),
getLength: vi.fn(),
query: vi.fn(),
add: vi.fn(),
update: vi.fn(),
updateMeta: vi.fn(),
delete: vi.fn(),
};

const notesApi = {
openNote: vi.fn(),
noteUpdated: vi.fn(),
noteClosed: vi.fn(),
};

return (
<NotesContext.Provider value={notesApi}>
<FilesRegistryContext.Provider value={filesRegistry}>
<NotesRegistryContext.Provider value={notesControl}>
{children}
</NotesRegistryContext.Provider>
</FilesRegistryContext.Provider>
</NotesContext.Provider>
);
};

export const renderRichEditorInDOM = async (props: RichEditorContentProps) => {
const { store } = createTestStore();
const onFormatting = createEvent<TextFormat>();
const onInserting = createEvent<InsertingPayload>();

const editorRef = createRef<LexicalEditor>();

const renderEditor = (props: RichEditorContentProps) => (
<Provider store={store}>
<ThemeProvider>
<MockWorkspaceProvider>
<editorPanelContext.Provider value={{ onInserting, onFormatting }}>
<RichEditor placeholder="Enter text" {...props} />
</editorPanelContext.Provider>
</MockWorkspaceProvider>
</ThemeProvider>
</Provider>
);

const container = document.createElement('div');
document.body.appendChild(container);

const root = createRoot(container);
act(() => root.render(renderEditor({ ...props, editorRef })));

return {
root,
container,

destroy() {
act(() => {
root.unmount();
});
container.remove();
},

getEditor() {
const editor = editorRef.current;
if (!editor) throw new Error('Error instance is not set');
return editor;
},
};
Comment thread
vitonsky marked this conversation as resolved.
};
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import React, { act } from 'react';
import React, { act, createRef } from 'react';
import { I18nextProvider, initReactI18next } from 'react-i18next';
import { Provider } from 'react-redux';
import { createEvent } from 'effector';
import i18n from 'i18next';
import { LexicalEditor } from 'lexical';
import { ChakraProvider, defaultSystem } from '@chakra-ui/react';
import { FilesController } from '@core/features/files/FilesController';
import {
Expand Down Expand Up @@ -77,19 +80,41 @@ export const renderRichEditor = async (props: RichEditorContentProps) => {
const onFormatting = createEvent<TextFormat>();
const onInserting = createEvent<InsertingPayload>();

const editorRef = createRef<LexicalEditor>();

i18n.use(initReactI18next) // passes i18n down to react-i18next
.init({
lng: 'en',
fallbackLng: 'en',

ns: [],

interpolation: {
escapeValue: false,
},

react: {
useSuspense: false, // important since we manually wait
},
});

const renderEditor = (props: RichEditorContentProps) => (
<Provider store={store}>
<ChakraProvider value={defaultSystem}>
<MockWorkspaceProvider>
<editorPanelContext.Provider value={{ onInserting, onFormatting }}>
<RichEditor {...props} />
</editorPanelContext.Provider>
</MockWorkspaceProvider>
<I18nextProvider i18n={i18n}>
<MockWorkspaceProvider>
<editorPanelContext.Provider
value={{ onInserting, onFormatting }}
>
<RichEditor placeholder="Enter text" {...props} />
</editorPanelContext.Provider>
</MockWorkspaceProvider>
</I18nextProvider>
</ChakraProvider>
</Provider>
);

const result = render(renderEditor(props));
const result = render(renderEditor({ ...props, editorRef }));

return {
...result,
Expand All @@ -113,5 +138,9 @@ export const renderRichEditor = async (props: RichEditorContentProps) => {
format: async (format: TextFormat) => {
await act(async () => onFormatting(format));
},

getEditor() {
return editorRef.current;
},
};
};
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { useEffect } from 'react';
import { registerCodeHighlighting } from '@lexical/code';
import { registerCodeIndentation } from '@lexical/code-core';
import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext';

export const CodeHighlightPlugin = () => {
const [editor] = useLexicalComposerContext();

// TODO: use registerCodeHighlighting
useEffect(() => {
return registerCodeHighlighting(editor);
return registerCodeIndentation(editor);
}, [editor]);

return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {
CONTROLLED_TEXT_INSERTION_COMMAND,
FORMAT_TEXT_COMMAND,
} from 'lexical';
import { $createCodeNode } from '@lexical/code';
import { $createCodeNode } from '@lexical/code-core';
import { TOGGLE_LINK_COMMAND } from '@lexical/link';
import {
INSERT_CHECK_LIST_COMMAND,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {
ElementNode,
LexicalNode,
} from 'lexical';
import { $isCodeNode } from '@lexical/code';
import { $isCodeNode } from '@lexical/code-core';
import { $findMatchingParent } from '@lexical/utils';

export const $canInsertElementsToNode = (node: LexicalNode) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {
ElementNode,
KEY_ENTER_COMMAND,
} from 'lexical';
import { $isCodeNode } from '@lexical/code';
import { $isCodeNode } from '@lexical/code-core';
import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext';
import { $isQuoteNode } from '@lexical/rich-text';
import { mergeRegister } from '@lexical/utils';
Expand Down
Loading
Loading