Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@blocksuite/virgo

Package Overview
Dependencies
Maintainers
5
Versions
509
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@blocksuite/virgo - npm Package Compare versions

Comparing version 0.0.0-20230907222710-102d1e99-nightly to 0.0.0-20230908185655-61117b50-nightly

4

dist/components/virgo-line.js

@@ -11,3 +11,3 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {

import { styleMap } from 'lit/directives/style-map.js';
import { ZERO_WIDTH_SPACE } from '../consts.js';
import { VIRGO_ROOT_ATTR, ZERO_WIDTH_SPACE } from '../consts.js';
import { EmbedGap } from './embed-gap.js';

@@ -40,3 +40,3 @@ let VirgoLine = class VirgoLine extends LitElement {

}
const rootElement = this.closest('[data-virgo-root="true"]');
const rootElement = this.closest(`[${VIRGO_ROOT_ATTR}]`);
assertExists(rootElement, 'v-line must be inside a v-root');

@@ -43,0 +43,0 @@ const virgoEditor = rootElement.virgoEditor;

export declare const ZERO_WIDTH_SPACE = "\u200B";
export declare const ZERO_WIDTH_NON_JOINER = "\u200C";
export declare const VIRGO_ROOT_ATTR = "data-virgo-root";
//# sourceMappingURL=consts.d.ts.map
export const ZERO_WIDTH_SPACE = '\u200B';
// see https://en.wikipedia.org/wiki/Zero-width_non-joiner
export const ZERO_WIDTH_NON_JOINER = '\u200C';
export const VIRGO_ROOT_ATTR = 'data-virgo-root';
//# sourceMappingURL=consts.js.map

@@ -7,3 +7,3 @@ import type { z, ZodTypeDef } from 'zod';

export declare class VirgoAttributeService<TextAttributes extends BaseTextAttributes> {
private readonly _editor;
readonly editor: VEditor<TextAttributes>;
private _marks;

@@ -10,0 +10,0 @@ private _attributeRenderer;

import { baseTextAttributes, getDefaultAttributeRenderer, } from '../utils/index.js';
export class VirgoAttributeService {
constructor(editor) {
this.editor = editor;
this._marks = null;

@@ -20,3 +21,3 @@ this._attributeRenderer = getDefaultAttributeRenderer();

this.getFormat = (vRange, loose = false) => {
const deltas = this._editor.deltaService
const deltas = this.editor.deltaService
.getDeltasByVRange(vRange)

@@ -64,3 +65,2 @@ .filter(([_, position]) => position.index + position.length > vRange.index &&

};
this._editor = editor;
}

@@ -67,0 +67,0 @@ get marks() {

@@ -6,3 +6,3 @@ import type { DeltaInsert } from '../types.js';

export declare class VirgoDeltaService<TextAttributes extends BaseTextAttributes> {
private readonly _editor;
readonly editor: VEditor<TextAttributes>;
constructor(editor: VEditor<TextAttributes>);

@@ -9,0 +9,0 @@ get deltas(): DeltaInsert<TextAttributes>[];

@@ -6,2 +6,3 @@ import { html, render } from 'lit';

constructor(editor) {
this.editor = editor;
this.mapDeltasInVRange = (vRange, callback, normalize = false) => {

@@ -125,3 +126,3 @@ const deltas = normalize ? this.normalizedDeltas : this.deltas;

this.render = async (syncVRange = true) => {
const rootElement = this._editor.rootElement;
const rootElement = this.editor.rootElement;
const normalizedDeltas = this.normalizedDeltas;

@@ -140,3 +141,3 @@ const chunks = deltaInsertsToChunks(normalizedDeltas);

let selected = false;
const vRange = this._editor.getVRange();
const vRange = this.editor.getVRange();
if (vRange) {

@@ -146,3 +147,3 @@ selected = this.isNormalizedDeltaSelected(normalizedDeltaIndex, vRange);

return [
renderElement(delta, this._editor.attributeService.normalizeAttributes, selected),
renderElement(delta, this.editor.attributeService.normalizeAttributes, selected),
delta,

@@ -162,17 +163,16 @@ ];

// Lit may be crashed by IME input and we need to rerender whole editor for it
this._editor.rerenderWholeEditor();
await this._editor.waitForUpdate();
this.editor.rerenderWholeEditor();
await this.editor.waitForUpdate();
}
await this._editor.waitForUpdate();
await this.editor.waitForUpdate();
if (syncVRange) {
// We need to synchronize the selection immediately after rendering is completed,
// otherwise there is a possibility of an error in the cursor position
this._editor.rangeService.syncVRange();
this.editor.rangeService.syncVRange();
}
this._editor.slots.updated.emit();
this.editor.slots.updated.emit();
};
this._editor = editor;
}
get deltas() {
return this._editor.yText.toDelta();
return this.editor.yText.toDelta();
}

@@ -185,3 +185,3 @@ get normalizedDeltas() {

for (const delta of this.deltas) {
if (this._editor.isEmbed(delta)) {
if (this.editor.isEmbed(delta)) {
const dividedDeltas = [...delta.insert].map(subInsert => ({

@@ -202,3 +202,3 @@ insert: subInsert,

if (vRange.length >= 1) {
this._editor.mapDeltasInVRange(vRange, (_, rangeIndex, deltaIndex) => {
this.editor.mapDeltasInVRange(vRange, (_, rangeIndex, deltaIndex) => {
if (deltaIndex === normalizedDeltaIndex &&

@@ -205,0 +205,0 @@ rangeIndex >= vRange.index) {

@@ -9,7 +9,8 @@ import { type BaseTextAttributes } from '../utils/index.js';

constructor(editor: VEditor<TextAttributes>);
get vRangeProvider(): import("../virgo.js").VRangeProvider | null;
mount: () => void;
private _isRangeCompletelyInRoot;
private _onSelectionChange;
private _onCompositionStart;
private _onCompositionEnd;
private _firstRecomputeInFrame;
private _onBeforeInput;

@@ -16,0 +17,0 @@ private _onScroll;

@@ -14,3 +14,5 @@ import { assertExists } from '@blocksuite/global/utils';

const rootElement = this.editor.rootElement;
this.editor.disposables.addFromEvent(document, 'selectionchange', this._onSelectionChange);
if (!this.vRangeProvider) {
this.editor.disposables.addFromEvent(document, 'selectionchange', this._onSelectionChange);
}
this.editor.disposables.addFromEvent(rootElement, 'beforeinput', this._onBeforeInput);

@@ -23,2 +25,21 @@ this.editor.disposables.addFromEvent(rootElement, 'compositionstart', this._onCompositionStart);

};
this._isRangeCompletelyInRoot = () => {
const selectionRoot = findDocumentOrShadowRoot(this.editor);
const selection = selectionRoot.getSelection();
if (!selection || selection.rangeCount === 0)
return false;
const range = selection.getRangeAt(0);
const rootElement = this.editor.rootElement;
const rootRange = document.createRange();
rootRange.selectNode(rootElement);
if (range.startContainer.compareDocumentPosition(range.endContainer) &
Node.DOCUMENT_POSITION_FOLLOWING) {
return (rootRange.comparePoint(range.startContainer, range.startOffset) >= 0 &&
rootRange.comparePoint(range.endContainer, range.endOffset) <= 0);
}
else {
return (rootRange.comparePoint(range.endContainer, range.startOffset) >= 0 &&
rootRange.comparePoint(range.startContainer, range.endOffset) <= 0);
}
};
this._onSelectionChange = () => {

@@ -36,3 +57,3 @@ const rootElement = this.editor.rootElement;

if (previousVRange !== null) {
this.editor.slots.vRangeUpdated.emit([null, 'native']);
this.editor.setVRange(null, false);
}

@@ -62,3 +83,3 @@ return;

if (previousVRange !== null) {
this.editor.slots.vRangeUpdated.emit([null, 'native']);
this.editor.setVRange(null, false);
}

@@ -72,3 +93,3 @@ return;

if (!isMaybeVRangeEqual(previousVRange, vRange)) {
this.editor.slots.vRangeUpdated.emit([vRange, 'native']);
this.editor.setVRange(vRange, false);
}

@@ -99,3 +120,3 @@ // avoid infinite syncVRange

await this.editor.waitForUpdate();
if (this.editor.isReadonly)
if (this.editor.isReadonly || !this._isRangeCompletelyInRoot())
return;

@@ -163,23 +184,29 @@ const vRange = this.editor.getVRange();

this.editor.insertText(newVRange, newData, ctx.attributes);
this.editor.slots.vRangeUpdated.emit([
{
index: newVRange.index + newData.length,
length: 0,
},
'input',
]);
this.editor.setVRange({
index: newVRange.index + newData.length,
length: 0,
}, false);
}
}
};
this._firstRecomputeInFrame = true;
this._onBeforeInput = (event) => {
event.preventDefault();
if (this.editor.isReadonly || this._isComposing)
if (this.editor.isReadonly ||
this._isComposing ||
!this._isRangeCompletelyInRoot())
return;
if (this._firstRecomputeInFrame) {
this._firstRecomputeInFrame = false;
this._onSelectionChange();
requestAnimationFrame(() => {
this._firstRecomputeInFrame = true;
});
if (!this.editor.getVRange())
return;
// Sometimes input event will directly come from some scripts (e.g. browser extension),
// so we need to resync the vRange.
const targetRanges = event.getTargetRanges();
if (targetRanges.length > 0) {
const staticRange = targetRanges[0];
const range = document.createRange();
range.setStart(staticRange.startContainer, staticRange.startOffset);
range.setEnd(staticRange.endContainer, staticRange.endOffset);
const vRange = this.editor.toVRange(range);
if (!isMaybeVRangeEqual(this.editor.getVRange(), vRange)) {
this.editor.setVRange(vRange, false);
}
}

@@ -280,3 +307,6 @@ const vRange = this.editor.getVRange();

}
get vRangeProvider() {
return this.editor.vRangeProvider;
}
}
//# sourceMappingURL=event.js.map

@@ -6,3 +6,3 @@ import type { VRange } from '../types.js';

export declare class VirgoRangeService<TextAttributes extends BaseTextAttributes> {
private readonly _editor;
readonly editor: VEditor<TextAttributes>;
private _prevVRange;

@@ -12,3 +12,4 @@ private _vRange;

constructor(editor: VEditor<TextAttributes>);
onVRangeUpdated: ([newVRange, origin]: VRangeUpdatedProp) => void;
get vRangeProvider(): import("../virgo.js").VRangeProvider | null;
onVRangeUpdated: ([newVRange, sync]: VRangeUpdatedProp) => void;
getVRange: () => VRange | null;

@@ -15,0 +16,0 @@ /**

@@ -7,8 +7,9 @@ import { VirgoLine } from '../components/index.js';

constructor(editor) {
this.editor = editor;
this._prevVRange = null;
this._vRange = null;
this._lastScrollLeft = 0;
this.onVRangeUpdated = ([newVRange, origin]) => {
this.onVRangeUpdated = ([newVRange, sync]) => {
this._vRange = newVRange;
if (this._editor.mounted &&
if (this.editor.mounted &&
newVRange &&

@@ -18,14 +19,18 @@ !isMaybeVRangeEqual(this._prevVRange, newVRange)) {

// be broken if we sync
this._editor.requestUpdate(false);
this.editor.requestUpdate(false);
}
this._prevVRange = newVRange;
if (origin !== 'other') {
if (this.vRangeProvider) {
this.vRangeProvider.setVRange(newVRange);
return;
}
if (!sync) {
return;
}
if (this._vRange === null) {
const selectionRoot = findDocumentOrShadowRoot(this._editor);
const selectionRoot = findDocumentOrShadowRoot(this.editor);
const selection = selectionRoot.getSelection();
if (selection && selection.rangeCount > 0) {
const range = selection.getRangeAt(0);
if (range.intersectsNode(this._editor.rootElement)) {
if (range.intersectsNode(this.editor.rootElement)) {
selection.removeAllRanges();

@@ -48,2 +53,5 @@ }

this.getVRange = () => {
if (this.vRangeProvider) {
return this.vRangeProvider.getVRange();
}
return this._vRange;

@@ -58,6 +66,6 @@ };

(vRange.index < 0 ||
vRange.index + vRange.length > this._editor.yText.length)) {
vRange.index + vRange.length > this.editor.yText.length)) {
throw new Error('invalid vRange');
}
this._editor.slots.vRangeUpdated.emit([vRange, sync ? 'other' : 'silent']);
this.editor.slots.vRangeUpdated.emit([vRange, sync]);
};

@@ -68,4 +76,5 @@ /**

this.syncVRange = () => {
if (this._vRange && this._editor.mounted) {
this._applyVRange(this._vRange);
const vRange = this.getVRange();
if (vRange && this.editor.mounted) {
this._applyVRange(vRange);
}

@@ -77,3 +86,3 @@ };

this.toDomRange = (vRange) => {
const rootElement = this._editor.rootElement;
const rootElement = this.editor.rootElement;
return virgoRangeToDomRange(rootElement, vRange);

@@ -108,3 +117,3 @@ };

this.toVRange = (range) => {
const { rootElement, yText } = this._editor;
const { rootElement, yText } = this.editor;
return domRangeToVirgoRange(range, rootElement, yText);

@@ -116,3 +125,3 @@ };

this._applyVRange = (vRange) => {
const selectionRoot = findDocumentOrShadowRoot(this._editor);
const selectionRoot = findDocumentOrShadowRoot(this.editor);
const selection = selectionRoot.getSelection();

@@ -130,6 +139,6 @@ if (!selection) {

this._scrollCursorIntoViewIfNeeded(newRange);
this._editor.slots.rangeUpdated.emit(newRange);
this.editor.slots.rangeUpdated.emit(newRange);
};
this._scrollLineIntoViewIfNeeded = (range) => {
if (this._editor.shouldLineScrollIntoView) {
if (this.editor.shouldLineScrollIntoView) {
let lineElement = range.endContainer.parentElement;

@@ -145,4 +154,4 @@ while (!(lineElement instanceof VirgoLine)) {

this._scrollCursorIntoViewIfNeeded = (range) => {
if (this._editor.shouldCursorScrollIntoView) {
const root = this._editor.rootElement;
if (this.editor.shouldCursorScrollIntoView) {
const root = this.editor.rootElement;
const rootRect = root.getBoundingClientRect();

@@ -158,5 +167,7 @@ const rangeRect = range.getBoundingClientRect();

};
this._editor = editor;
}
get vRangeProvider() {
return this.editor.vRangeProvider;
}
}
//# sourceMappingURL=range.js.map

@@ -12,6 +12,3 @@ import type { TemplateResult } from 'lit';

}
export type VRangeUpdatedProp = [
range: VRange | null,
type: 'native' | 'input' | 'other' | 'silent'
];
export type VRangeUpdatedProp = [range: VRange | null, sync: boolean];
export type DeltaEntry<TextAttributes extends BaseTextAttributes = BaseTextAttributes> = [delta: DeltaInsert<TextAttributes>, range: VRange];

@@ -18,0 +15,0 @@ export type NativePoint = readonly [node: Node, offset: number];

@@ -1,2 +0,2 @@

import { ZERO_WIDTH_SPACE } from '../consts.js';
import { VIRGO_ROOT_ATTR, ZERO_WIDTH_SPACE } from '../consts.js';
import { isNativeTextInVText, isVElement, isVLine, isVRoot } from './guard.js';

@@ -64,4 +64,4 @@ import { calculateTextLength, getTextNodesFromElement } from './text.js';

const container = node instanceof Element
? node.closest('[data-virgo-root="true"]')
: node.parentElement?.closest('[data-virgo-root="true"]');
? node.closest(`[${VIRGO_ROOT_ATTR}]`)
: node.parentElement?.closest(`[${VIRGO_ROOT_ATTR}]`);
if (container) {

@@ -68,0 +68,0 @@ return Array.from(container.querySelectorAll('v-line'));

import { assertExists } from '@blocksuite/global/utils';
import { VIRGO_ROOT_ATTR } from '../consts.js';
export function findDocumentOrShadowRoot(editor) {

@@ -15,3 +16,3 @@ const el = editor.rootElement;

export function getVEditorInsideRoot(element) {
const rootElement = element.closest('[data-virgo-root="true"]');
const rootElement = element.closest(`[${VIRGO_ROOT_ATTR}]`);
assertExists(rootElement, 'element must be inside a v-root');

@@ -18,0 +19,0 @@ const virgoEditor = rootElement.virgoEditor;

function handleInsertText(vRange, data, editor, attributes) {
if (vRange.index >= 0 && data) {
editor.slots.vRangeUpdated.emit([
{
index: vRange.index + data.length,
length: 0,
},
'input',
]);
editor.insertText(vRange, data, attributes);
editor.setVRange({
index: vRange.index + data.length,
length: 0,
}, false);
}

@@ -15,10 +12,7 @@ }

if (vRange.index >= 0) {
editor.slots.vRangeUpdated.emit([
{
index: vRange.index + 1,
length: 0,
},
'input',
]);
editor.insertLineBreak(vRange);
editor.setVRange({
index: vRange.index + 1,
length: 0,
}, false);
}

@@ -29,10 +23,7 @@ }

if (vRange.length > 0) {
editor.slots.vRangeUpdated.emit([
{
index: vRange.index,
length: 0,
},
'input',
]);
editor.deleteText(vRange);
editor.setVRange({
index: vRange.index,
length: 0,
}, false);
return;

@@ -44,9 +35,2 @@ }

const deletedLength = segments[segments.length - 1].segment.length;
editor.slots.vRangeUpdated.emit([
{
index: vRange.index - deletedLength,
length: 0,
},
'input',
]);
editor.deleteText({

@@ -56,2 +40,6 @@ index: vRange.index - deletedLength,

});
editor.setVRange({
index: vRange.index - deletedLength,
length: 0,
}, false);
}

@@ -63,10 +51,7 @@ }

if (vRange.length > 0) {
editor.slots.vRangeUpdated.emit([
{
index: vRange.index,
length: 0,
},
'input',
]);
editor.deleteText(vRange);
editor.setVRange({
index: vRange.index,
length: 0,
}, false);
}

@@ -79,9 +64,2 @@ else {

const deletedLength = segments[slicedSegments.length].segment.length;
editor.slots.vRangeUpdated.emit([
{
index: vRange.index,
length: 0,
},
'input',
]);
editor.deleteText({

@@ -91,2 +69,6 @@ index: vRange.index,

});
editor.setVRange({
index: vRange.index,
length: 0,
}, false);
}

@@ -99,9 +81,2 @@ }

const deleteLength = matches[0].length;
editor.slots.vRangeUpdated.emit([
{
index: vRange.index - deleteLength,
length: 0,
},
'input',
]);
editor.deleteText({

@@ -111,2 +86,6 @@ index: vRange.index - deleteLength,

});
editor.setVRange({
index: vRange.index - deleteLength,
length: 0,
}, false);
}

@@ -118,9 +97,2 @@ }

const deleteLength = matches[0].length;
editor.slots.vRangeUpdated.emit([
{
index: vRange.index,
length: 0,
},
'input',
]);
editor.deleteText({

@@ -130,2 +102,6 @@ index: vRange.index,

});
editor.setVRange({
index: vRange.index,
length: 0,
}, false);
}

@@ -135,10 +111,7 @@ }

if (vRange.length > 0) {
editor.slots.vRangeUpdated.emit([
{
index: vRange.index,
length: 0,
},
'input',
]);
editor.deleteText(vRange);
editor.setVRange({
index: vRange.index,
length: 0,
}, false);
return;

@@ -149,9 +122,2 @@ }

const deleteLength = vRange.index - Math.max(0, str.slice(0, vRange.index).lastIndexOf('\n'));
editor.slots.vRangeUpdated.emit([
{
index: vRange.index - deleteLength,
length: 0,
},
'input',
]);
editor.deleteText({

@@ -161,2 +127,6 @@ index: vRange.index - deleteLength,

});
editor.setVRange({
index: vRange.index - deleteLength,
length: 0,
}, false);
}

@@ -163,0 +133,0 @@ }

@@ -13,2 +13,6 @@ import type { NullablePartial } from '@blocksuite/global/utils';

};
export interface VRangeProvider {
getVRange(): VRange | null;
setVRange(vRange: VRange | null): void;
}
export declare class VEditor<TextAttributes extends BaseTextAttributes = BaseTextAttributes> {

@@ -32,2 +36,3 @@ static nativePointToTextPoint: typeof nativePointToTextPoint;

readonly isEmbed: (delta: DeltaInsert<TextAttributes>) => boolean;
readonly vRangeProvider: VRangeProvider | null;
slots: {

@@ -73,2 +78,3 @@ mounted: Slot;

hooks?: VirgoHookService<TextAttributes>['hooks'];
vRangeProvider?: VRangeProvider;
});

@@ -75,0 +81,0 @@ mount(rootElement: HTMLElement): void;

import { assertExists, DisposableGroup, Slot } from '@blocksuite/global/utils';
import { nothing, render } from 'lit';
import { VIRGO_ROOT_ATTR } from './consts.js';
import { VirgoHookService } from './services/hook.js';

@@ -93,6 +94,7 @@ import { VirgoAttributeService, VirgoDeltaService, VirgoEventService, VirgoRangeService, } from './services/index.js';

}
const { isEmbed = () => false, hooks = {} } = ops;
const { isEmbed = () => false, hooks = {}, vRangeProvider = null } = ops;
this._yText = yText;
this.isEmbed = isEmbed;
this._hooksService = new VirgoHookService(this, hooks);
this.vRangeProvider = vRangeProvider;
this.slots = {

@@ -124,2 +126,3 @@ mounted: new Slot(),

render(nothing, this.rootElement);
this.rootElement.removeAttribute(VIRGO_ROOT_ATTR);
this._rootElement = null;

@@ -126,0 +129,0 @@ this._mounted = false;

{
"name": "@blocksuite/virgo",
"version": "0.0.0-20230907222710-102d1e99-nightly",
"version": "0.0.0-20230908185655-61117b50-nightly",
"description": "A micro editor.",

@@ -27,3 +27,3 @@ "type": "module",

"zod": "^3.22.2",
"@blocksuite/global": "0.0.0-20230907222710-102d1e99-nightly"
"@blocksuite/global": "0.0.0-20230908185655-61117b50-nightly"
},

@@ -30,0 +30,0 @@ "scripts": {

@@ -6,3 +6,3 @@ import { assertExists } from '@blocksuite/global/utils';

import { ZERO_WIDTH_SPACE } from '../consts.js';
import { VIRGO_ROOT_ATTR, ZERO_WIDTH_SPACE } from '../consts.js';
import type { DeltaInsert } from '../types.js';

@@ -45,3 +45,3 @@ import type { VirgoRootElement } from '../virgo.js';

const rootElement = this.closest(
'[data-virgo-root="true"]'
`[${VIRGO_ROOT_ATTR}]`
) as VirgoRootElement;

@@ -48,0 +48,0 @@ assertExists(rootElement, 'v-line must be inside a v-root');

export const ZERO_WIDTH_SPACE = '\u200B';
// see https://en.wikipedia.org/wiki/Zero-width_non-joiner
export const ZERO_WIDTH_NON_JOINER = '\u200C';
export const VIRGO_ROOT_ATTR = 'data-virgo-root';

@@ -13,4 +13,2 @@ import type { z, ZodTypeDef } from 'zod';

export class VirgoAttributeService<TextAttributes extends BaseTextAttributes> {
private readonly _editor: VEditor<TextAttributes>;
private _marks: TextAttributes | null = null;

@@ -24,5 +22,3 @@

constructor(editor: VEditor<TextAttributes>) {
this._editor = editor;
}
constructor(public readonly editor: VEditor<TextAttributes>) {}

@@ -56,3 +52,3 @@ get marks() {

getFormat = (vRange: VRange, loose = false): TextAttributes => {
const deltas = this._editor.deltaService
const deltas = this.editor.deltaService
.getDeltasByVRange(vRange)

@@ -59,0 +55,0 @@ .filter(

@@ -12,10 +12,6 @@ import { html, render } from 'lit';

export class VirgoDeltaService<TextAttributes extends BaseTextAttributes> {
private readonly _editor: VEditor<TextAttributes>;
constructor(public readonly editor: VEditor<TextAttributes>) {}
constructor(editor: VEditor<TextAttributes>) {
this._editor = editor;
}
get deltas() {
return this._editor.yText.toDelta() as DeltaInsert<TextAttributes>[];
return this.editor.yText.toDelta() as DeltaInsert<TextAttributes>[];
}

@@ -29,3 +25,3 @@

for (const delta of this.deltas) {
if (this._editor.isEmbed(delta)) {
if (this.editor.isEmbed(delta)) {
const dividedDeltas = [...delta.insert].map(subInsert => ({

@@ -82,3 +78,3 @@ insert: subInsert,

if (vRange.length >= 1) {
this._editor.mapDeltasInVRange(
this.editor.mapDeltasInVRange(
vRange,

@@ -208,3 +204,3 @@ (_, rangeIndex, deltaIndex) => {

render = async (syncVRange = true) => {
const rootElement = this._editor.rootElement;
const rootElement = this.editor.rootElement;

@@ -227,3 +223,3 @@ const normalizedDeltas = this.normalizedDeltas;

let selected = false;
const vRange = this._editor.getVRange();
const vRange = this.editor.getVRange();
if (vRange) {

@@ -239,3 +235,3 @@ selected = this.isNormalizedDeltaSelected(

delta,
this._editor.attributeService.normalizeAttributes,
this.editor.attributeService.normalizeAttributes,
selected

@@ -265,7 +261,7 @@ ),

// Lit may be crashed by IME input and we need to rerender whole editor for it
this._editor.rerenderWholeEditor();
await this._editor.waitForUpdate();
this.editor.rerenderWholeEditor();
await this.editor.waitForUpdate();
}
await this._editor.waitForUpdate();
await this.editor.waitForUpdate();

@@ -275,7 +271,7 @@ if (syncVRange) {

// otherwise there is a possibility of an error in the cursor position
this._editor.rangeService.syncVRange();
this.editor.rangeService.syncVRange();
}
this._editor.slots.updated.emit();
this.editor.slots.updated.emit();
};
}

@@ -23,11 +23,18 @@ import { assertExists } from '@blocksuite/global/utils';

get vRangeProvider() {
return this.editor.vRangeProvider;
}
mount = () => {
const rootElement = this.editor.rootElement;
if (!this.vRangeProvider) {
this.editor.disposables.addFromEvent(
document,
'selectionchange',
this._onSelectionChange
);
}
this.editor.disposables.addFromEvent(
document,
'selectionchange',
this._onSelectionChange
);
this.editor.disposables.addFromEvent(
rootElement,

@@ -56,2 +63,28 @@ 'beforeinput',

private _isRangeCompletelyInRoot = () => {
const selectionRoot = findDocumentOrShadowRoot(this.editor);
const selection = selectionRoot.getSelection();
if (!selection || selection.rangeCount === 0) return false;
const range = selection.getRangeAt(0);
const rootElement = this.editor.rootElement;
const rootRange = document.createRange();
rootRange.selectNode(rootElement);
if (
range.startContainer.compareDocumentPosition(range.endContainer) &
Node.DOCUMENT_POSITION_FOLLOWING
) {
return (
rootRange.comparePoint(range.startContainer, range.startOffset) >= 0 &&
rootRange.comparePoint(range.endContainer, range.endOffset) <= 0
);
} else {
return (
rootRange.comparePoint(range.endContainer, range.startOffset) >= 0 &&
rootRange.comparePoint(range.startContainer, range.endOffset) <= 0
);
}
};
private _onSelectionChange = () => {

@@ -69,3 +102,3 @@ const rootElement = this.editor.rootElement;

if (previousVRange !== null) {
this.editor.slots.vRangeUpdated.emit([null, 'native']);
this.editor.setVRange(null, false);
}

@@ -105,3 +138,3 @@

if (previousVRange !== null) {
this.editor.slots.vRangeUpdated.emit([null, 'native']);
this.editor.setVRange(null, false);
}

@@ -117,3 +150,3 @@ return;

if (!isMaybeVRangeEqual(previousVRange, vRange)) {
this.editor.slots.vRangeUpdated.emit([vRange, 'native']);
this.editor.setVRange(vRange, false);
}

@@ -152,3 +185,3 @@

if (this.editor.isReadonly) return;
if (this.editor.isReadonly || !this._isRangeCompletelyInRoot()) return;

@@ -221,3 +254,3 @@ const vRange = this.editor.getVRange();

this.editor.slots.vRangeUpdated.emit([
this.editor.setVRange(
{

@@ -227,4 +260,4 @@ index: newVRange.index + newData.length,

},
'input',
]);
false
);
}

@@ -234,14 +267,29 @@ }

private _firstRecomputeInFrame = true;
private _onBeforeInput = (event: InputEvent) => {
event.preventDefault();
if (this.editor.isReadonly || this._isComposing) return;
if (this._firstRecomputeInFrame) {
this._firstRecomputeInFrame = false;
this._onSelectionChange();
requestAnimationFrame(() => {
this._firstRecomputeInFrame = true;
});
if (
this.editor.isReadonly ||
this._isComposing ||
!this._isRangeCompletelyInRoot()
)
return;
if (!this.editor.getVRange()) return;
// Sometimes input event will directly come from some scripts (e.g. browser extension),
// so we need to resync the vRange.
const targetRanges = event.getTargetRanges();
if (targetRanges.length > 0) {
const staticRange = targetRanges[0];
const range = document.createRange();
range.setStart(staticRange.startContainer, staticRange.startOffset);
range.setEnd(staticRange.endContainer, staticRange.endOffset);
const vRange = this.editor.toVRange(range);
if (!isMaybeVRangeEqual(this.editor.getVRange(), vRange)) {
this.editor.setVRange(vRange, false);
}
}
const vRange = this.editor.getVRange();

@@ -248,0 +296,0 @@ if (!vRange) return;

@@ -14,4 +14,2 @@ import { VirgoLine } from '../components/index.js';

export class VirgoRangeService<TextAttributes extends BaseTextAttributes> {
private readonly _editor: VEditor<TextAttributes>;
private _prevVRange: VRange | null = null;

@@ -21,11 +19,13 @@ private _vRange: VRange | null = null;

constructor(editor: VEditor<TextAttributes>) {
this._editor = editor;
constructor(public readonly editor: VEditor<TextAttributes>) {}
get vRangeProvider() {
return this.editor.vRangeProvider;
}
onVRangeUpdated = ([newVRange, origin]: VRangeUpdatedProp) => {
onVRangeUpdated = ([newVRange, sync]: VRangeUpdatedProp) => {
this._vRange = newVRange;
if (
this._editor.mounted &&
this.editor.mounted &&
newVRange &&

@@ -36,16 +36,21 @@ !isMaybeVRangeEqual(this._prevVRange, newVRange)

// be broken if we sync
this._editor.requestUpdate(false);
this.editor.requestUpdate(false);
}
this._prevVRange = newVRange;
if (origin !== 'other') {
if (this.vRangeProvider) {
this.vRangeProvider.setVRange(newVRange);
return;
}
if (!sync) {
return;
}
if (this._vRange === null) {
const selectionRoot = findDocumentOrShadowRoot(this._editor);
const selectionRoot = findDocumentOrShadowRoot(this.editor);
const selection = selectionRoot.getSelection();
if (selection && selection.rangeCount > 0) {
const range = selection.getRangeAt(0);
if (range.intersectsNode(this._editor.rootElement)) {
if (range.intersectsNode(this.editor.rootElement)) {
selection.removeAllRanges();

@@ -71,2 +76,6 @@ }

getVRange = (): VRange | null => {
if (this.vRangeProvider) {
return this.vRangeProvider.getVRange();
}
return this._vRange;

@@ -83,3 +92,3 @@ };

(vRange.index < 0 ||
vRange.index + vRange.length > this._editor.yText.length)
vRange.index + vRange.length > this.editor.yText.length)
) {

@@ -89,3 +98,3 @@ throw new Error('invalid vRange');

this._editor.slots.vRangeUpdated.emit([vRange, sync ? 'other' : 'silent']);
this.editor.slots.vRangeUpdated.emit([vRange, sync]);
};

@@ -97,4 +106,5 @@

syncVRange = (): void => {
if (this._vRange && this._editor.mounted) {
this._applyVRange(this._vRange);
const vRange = this.getVRange();
if (vRange && this.editor.mounted) {
this._applyVRange(vRange);
}

@@ -107,3 +117,3 @@ };

toDomRange = (vRange: VRange): Range | null => {
const rootElement = this._editor.rootElement;
const rootElement = this.editor.rootElement;
return virgoRangeToDomRange(rootElement, vRange);

@@ -139,3 +149,3 @@ };

toVRange = (range: Range): VRange | null => {
const { rootElement, yText } = this._editor;
const { rootElement, yText } = this.editor;

@@ -150,3 +160,3 @@ return domRangeToVirgoRange(range, rootElement, yText);

private _applyVRange = (vRange: VRange): void => {
const selectionRoot = findDocumentOrShadowRoot(this._editor);
const selectionRoot = findDocumentOrShadowRoot(this.editor);
const selection = selectionRoot.getSelection();

@@ -168,7 +178,7 @@ if (!selection) {

this._editor.slots.rangeUpdated.emit(newRange);
this.editor.slots.rangeUpdated.emit(newRange);
};
private _scrollLineIntoViewIfNeeded = (range: Range) => {
if (this._editor.shouldLineScrollIntoView) {
if (this.editor.shouldLineScrollIntoView) {
let lineElement: HTMLElement | null = range.endContainer.parentElement;

@@ -185,4 +195,4 @@ while (!(lineElement instanceof VirgoLine)) {

private _scrollCursorIntoViewIfNeeded = (range: Range) => {
if (this._editor.shouldCursorScrollIntoView) {
const root = this._editor.rootElement;
if (this.editor.shouldCursorScrollIntoView) {
const root = this.editor.rootElement;

@@ -189,0 +199,0 @@ const rootRect = root.getBoundingClientRect();

@@ -24,6 +24,3 @@ import type { TemplateResult } from 'lit';

export type VRangeUpdatedProp = [
range: VRange | null,
type: 'native' | 'input' | 'other' | 'silent',
];
export type VRangeUpdatedProp = [range: VRange | null, sync: boolean];

@@ -30,0 +27,0 @@ export type DeltaEntry<

@@ -11,4 +11,3 @@ import { z } from 'zod';

});
// .partial();
export type BaseTextAttributes = z.infer<typeof baseTextAttributes>;
import type { VirgoElement, VirgoLine } from '../components/index.js';
import { ZERO_WIDTH_SPACE } from '../consts.js';
import { VIRGO_ROOT_ATTR, ZERO_WIDTH_SPACE } from '../consts.js';
import type { DomPoint, TextPoint } from '../types.js';

@@ -101,4 +101,4 @@ import { isNativeTextInVText, isVElement, isVLine, isVRoot } from './guard.js';

node instanceof Element
? node.closest('[data-virgo-root="true"]')
: node.parentElement?.closest('[data-virgo-root="true"]');
? node.closest(`[${VIRGO_ROOT_ATTR}]`)
: node.parentElement?.closest(`[${VIRGO_ROOT_ATTR}]`);

@@ -105,0 +105,0 @@ if (container) {

import { assertExists } from '@blocksuite/global/utils';
import { VIRGO_ROOT_ATTR } from '../consts.js';
import type { VEditor, VirgoRootElement } from '../virgo.js';

@@ -29,3 +30,3 @@ import type { BaseTextAttributes } from './base-attributes.js';

const rootElement = element.closest(
'[data-virgo-root="true"]'
`[${VIRGO_ROOT_ATTR}]`
) as VirgoRootElement;

@@ -32,0 +33,0 @@ assertExists(rootElement, 'element must be inside a v-root');

@@ -12,3 +12,4 @@ import type { VRange } from '../types.js';

if (vRange.index >= 0 && data) {
editor.slots.vRangeUpdated.emit([
editor.insertText(vRange, data, attributes);
editor.setVRange(
{

@@ -18,5 +19,4 @@ index: vRange.index + data.length,

},
'input',
]);
editor.insertText(vRange, data, attributes);
false
);
}

@@ -27,3 +27,4 @@ }

if (vRange.index >= 0) {
editor.slots.vRangeUpdated.emit([
editor.insertLineBreak(vRange);
editor.setVRange(
{

@@ -33,5 +34,4 @@ index: vRange.index + 1,

},
'input',
]);
editor.insertLineBreak(vRange);
false
);
}

@@ -43,3 +43,4 @@ }

if (vRange.length > 0) {
editor.slots.vRangeUpdated.emit([
editor.deleteText(vRange);
editor.setVRange(
{

@@ -49,5 +50,4 @@ index: vRange.index,

},
'input',
]);
editor.deleteText(vRange);
false
);
return;

@@ -60,3 +60,7 @@ }

const deletedLength = segments[segments.length - 1].segment.length;
editor.slots.vRangeUpdated.emit([
editor.deleteText({
index: vRange.index - deletedLength,
length: deletedLength,
});
editor.setVRange(
{

@@ -66,8 +70,4 @@ index: vRange.index - deletedLength,

},
'input',
]);
editor.deleteText({
index: vRange.index - deletedLength,
length: deletedLength,
});
false
);
}

@@ -80,3 +80,4 @@ }

if (vRange.length > 0) {
editor.slots.vRangeUpdated.emit([
editor.deleteText(vRange);
editor.setVRange(
{

@@ -86,5 +87,4 @@ index: vRange.index,

},
'input',
]);
editor.deleteText(vRange);
false
);
} else {

@@ -96,3 +96,7 @@ const originalString = editor.yText.toString();

const deletedLength = segments[slicedSegments.length].segment.length;
editor.slots.vRangeUpdated.emit([
editor.deleteText({
index: vRange.index,
length: deletedLength,
});
editor.setVRange(
{

@@ -102,8 +106,4 @@ index: vRange.index,

},
'input',
]);
editor.deleteText({
index: vRange.index,
length: deletedLength,
});
false
);
}

@@ -120,3 +120,7 @@ }

editor.slots.vRangeUpdated.emit([
editor.deleteText({
index: vRange.index - deleteLength,
length: deleteLength,
});
editor.setVRange(
{

@@ -126,8 +130,4 @@ index: vRange.index - deleteLength,

},
'input',
]);
editor.deleteText({
index: vRange.index - deleteLength,
length: deleteLength,
});
false
);
}

@@ -141,3 +141,7 @@ }

editor.slots.vRangeUpdated.emit([
editor.deleteText({
index: vRange.index,
length: deleteLength,
});
editor.setVRange(
{

@@ -147,8 +151,4 @@ index: vRange.index,

},
'input',
]);
editor.deleteText({
index: vRange.index,
length: deleteLength,
});
false
);
}

@@ -159,3 +159,4 @@ }

if (vRange.length > 0) {
editor.slots.vRangeUpdated.emit([
editor.deleteText(vRange);
editor.setVRange(
{

@@ -165,5 +166,5 @@ index: vRange.index,

},
'input',
]);
editor.deleteText(vRange);
false
);
return;

@@ -177,3 +178,7 @@ }

editor.slots.vRangeUpdated.emit([
editor.deleteText({
index: vRange.index - deleteLength,
length: deleteLength,
});
editor.setVRange(
{

@@ -183,8 +188,4 @@ index: vRange.index - deleteLength,

},
'input',
]);
editor.deleteText({
index: vRange.index - deleteLength,
length: deleteLength,
});
false
);
}

@@ -191,0 +192,0 @@ }

@@ -7,2 +7,3 @@ import type { NullablePartial } from '@blocksuite/global/utils';

import type { VirgoLine } from './components/index.js';
import { VIRGO_ROOT_ATTR } from './consts.js';
import { VirgoHookService } from './services/hook.js';

@@ -36,2 +37,7 @@ import {

export interface VRangeProvider {
getVRange(): VRange | null;
setVRange(vRange: VRange | null): void;
}
export class VEditor<

@@ -73,2 +79,3 @@ TextAttributes extends BaseTextAttributes = BaseTextAttributes,

readonly isEmbed: (delta: DeltaInsert<TextAttributes>) => boolean;
readonly vRangeProvider: VRangeProvider | null;

@@ -158,2 +165,3 @@ slots: {

hooks?: VirgoHookService<TextAttributes>['hooks'];
vRangeProvider?: VRangeProvider;
} = {}

@@ -171,6 +179,7 @@ ) {

const { isEmbed = () => false, hooks = {} } = ops;
const { isEmbed = () => false, hooks = {}, vRangeProvider = null } = ops;
this._yText = yText;
this.isEmbed = isEmbed;
this._hooksService = new VirgoHookService(this, hooks);
this.vRangeProvider = vRangeProvider;

@@ -210,2 +219,3 @@ this.slots = {

render(nothing, this.rootElement);
this.rootElement.removeAttribute(VIRGO_ROOT_ATTR);
this._rootElement = null;

@@ -212,0 +222,0 @@ this._mounted = false;

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc