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.5.0-20230302104138-788dae4 to 0.5.0-20230302142916-8e090d3

2

dist/components/virgo-line.d.ts

@@ -5,2 +5,4 @@ import { LitElement } from 'lit';

elements: TextElement[];
get textLength(): number;
get textContent(): string;
render(): import("lit-html").TemplateResult<1>;

@@ -7,0 +9,0 @@ createRenderRoot(): this;

@@ -14,2 +14,8 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {

}
get textLength() {
return this.elements.reduce((acc, el) => acc + el.delta.insert.length, 0);
}
get textContent() {
return this.elements.reduce((acc, el) => acc + el.delta.insert, '');
}
render() {

@@ -16,0 +22,0 @@ return html `

@@ -9,2 +9,3 @@ import type { DeltaInsert, VRange } from '@blocksuite/virgo';

export declare function setVirgoRichTextRange(page: Page, vRange: VRange, index?: number): Promise<void>;
export declare function getVirgoRichTextLine(page: Page, index: number, i?: number): Promise<readonly [string, number]>;
//# sourceMappingURL=misc.d.ts.map

@@ -51,2 +51,16 @@ import { getDefaultPlaygroundURL } from '@blocksuite/global/utils';

}
export async function getVirgoRichTextLine(page, index, i = 0) {
return await page.evaluate(([index, i]) => {
const richTexts = document
.querySelector('test-page')
?.shadowRoot?.querySelectorAll('rich-text');
if (!richTexts) {
throw new Error('Cannot find rich-text');
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const editor = richTexts[i].vEditor;
const line = editor.getLine(index);
return [line[0].textContent, line[1]];
}, [index, i]);
}
//# sourceMappingURL=misc.js.map
import { expect, test } from '@playwright/test';
import { ZERO_WIDTH_SPACE } from '../constant.js';
import { enterVirgoPlayground, focusVirgoRichText, getDeltaFromVirgoRichText, press, setVirgoRichTextRange, type, } from './utils/misc.js';
import { enterVirgoPlayground, focusVirgoRichText, getDeltaFromVirgoRichText, getVirgoRichTextLine, press, setVirgoRichTextRange, type, } from './utils/misc.js';
test('basic input', async ({ page }) => {

@@ -23,2 +23,3 @@ await enterVirgoPlayground(page);

await focusVirgoRichText(page);
await page.waitForTimeout(50);
await press(page, 'Backspace');

@@ -37,2 +38,3 @@ await press(page, 'Backspace');

await focusVirgoRichText(page);
await page.waitForTimeout(50);
await press(page, 'Enter');

@@ -51,2 +53,3 @@ await press(page, 'Enter');

await focusVirgoRichText(page);
await page.waitForTimeout(50);
await press(page, 'Backspace');

@@ -65,2 +68,3 @@ await press(page, 'Backspace');

await focusVirgoRichText(page);
await page.waitForTimeout(50);
await press(page, 'ArrowLeft');

@@ -80,2 +84,3 @@ await press(page, 'ArrowLeft');

await focusVirgoRichText(page);
await page.waitForTimeout(50);
await press(page, 'ArrowLeft');

@@ -143,2 +148,3 @@ await press(page, 'ArrowLeft');

editorABold.click();
page.waitForTimeout(50);
delta = await getDeltaFromVirgoRichText(page);

@@ -160,2 +166,3 @@ expect(delta).toEqual([

editorAItalic.click();
page.waitForTimeout(50);
delta = await getDeltaFromVirgoRichText(page);

@@ -178,2 +185,3 @@ expect(delta).toEqual([

editorAUnderline.click();
page.waitForTimeout(50);
delta = await getDeltaFromVirgoRichText(page);

@@ -197,2 +205,3 @@ expect(delta).toEqual([

editorAStrike.click();
page.waitForTimeout(50);
delta = await getDeltaFromVirgoRichText(page);

@@ -217,2 +226,3 @@ expect(delta).toEqual([

editorACode.click();
page.waitForTimeout(50);
delta = await getDeltaFromVirgoRichText(page);

@@ -240,2 +250,3 @@ expect(delta).toEqual([

});
page.waitForTimeout(50);
delta = await getDeltaFromVirgoRichText(page);

@@ -250,2 +261,3 @@ expect(delta).toEqual([

});
page.waitForTimeout(50);
delta = await getDeltaFromVirgoRichText(page);

@@ -271,2 +283,3 @@ expect(delta).toEqual([

editorABold.click();
page.waitForTimeout(50);
delta = await getDeltaFromVirgoRichText(page);

@@ -291,2 +304,3 @@ expect(delta).toEqual([

editorAItalic.click();
page.waitForTimeout(50);
delta = await getDeltaFromVirgoRichText(page);

@@ -310,2 +324,3 @@ expect(delta).toEqual([

editorAUnderline.click();
page.waitForTimeout(50);
delta = await getDeltaFromVirgoRichText(page);

@@ -328,2 +343,3 @@ expect(delta).toEqual([

editorAStrike.click();
page.waitForTimeout(50);
delta = await getDeltaFromVirgoRichText(page);

@@ -345,2 +361,3 @@ expect(delta).toEqual([

editorACode.click();
page.waitForTimeout(50);
delta = await getDeltaFromVirgoRichText(page);

@@ -524,2 +541,3 @@ expect(delta).toEqual([

await focusVirgoRichText(page);
await page.waitForTimeout(50);
await press(page, 'ArrowLeft');

@@ -573,2 +591,35 @@ await press(page, 'ArrowLeft');

});
test('getLine', async ({ page }) => {
await enterVirgoPlayground(page);
await focusVirgoRichText(page);
const editorA = page.locator('[data-virgo-root="true"]').nth(0);
const editorB = page.locator('[data-virgo-root="true"]').nth(1);
expect(await editorA.innerText()).toBe(ZERO_WIDTH_SPACE);
expect(await editorB.innerText()).toBe(ZERO_WIDTH_SPACE);
await type(page, 'abc');
await press(page, 'Enter');
await type(page, 'def');
await press(page, 'Enter');
await type(page, 'ghi');
expect(await editorA.innerText()).toBe('abc\ndef\nghi');
expect(await editorB.innerText()).toBe('abc\ndef\nghi');
const [line1, offset1] = await getVirgoRichTextLine(page, 0);
const [line2, offset2] = await getVirgoRichTextLine(page, 1);
const [line3, offset3] = await getVirgoRichTextLine(page, 4);
const [line4, offset4] = await getVirgoRichTextLine(page, 5);
const [line5, offset5] = await getVirgoRichTextLine(page, 8);
const [line6, offset6] = await getVirgoRichTextLine(page, 11);
expect(line1).toEqual('abc');
expect(offset1).toEqual(0);
expect(line2).toEqual('abc');
expect(offset2).toEqual(1);
expect(line3).toEqual('def');
expect(offset3).toEqual(0);
expect(line4).toEqual('def');
expect(offset4).toEqual(1);
expect(line5).toEqual('ghi');
expect(offset5).toEqual(0);
expect(line6).toEqual('ghi');
expect(offset6).toEqual(3);
});
//# sourceMappingURL=virgo.spec.js.map

14

dist/virgo.d.ts
import { Signal } from '@blocksuite/global/utils';
import type * as Y from 'yjs';
import { VirgoLine } from './components/virgo-line.js';
import type { DeltaInsert, TextAttributes, TextElement } from './types.js';

@@ -10,2 +11,4 @@ export interface VRange {

export type DeltaEntry = [DeltaInsert, VRange];
export type NativePoint = readonly [node: Node, offset: number];
export type TextPoint = readonly [text: Text, offset: number];
export interface DomPoint {

@@ -16,3 +19,3 @@ text: Text;

export declare class VEditor {
static nativePointToTextPoint(node: unknown, offset: number): readonly [Text, number] | null;
static nativePointToTextPoint(node: unknown, offset: number): TextPoint | null;
static textPointToDomPoint(text: Text, offset: number, rootElement: HTMLElement): DomPoint | null;

@@ -24,2 +27,3 @@ private _rootElement;

private _isReadOnly;
private _yText;
private _renderElement;

@@ -30,7 +34,8 @@ private _onKeyDown;

};
yText: Y.Text;
get yText(): Y.YText;
get rootElement(): HTMLElement;
constructor(yText: VEditor['yText'], opts?: {
renderElement?: (delta: DeltaInsert) => TextElement;
onKeyDown?: (event: KeyboardEvent) => void;
});
bindKeyDownHandler(handler: (event: KeyboardEvent) => void): void;
mount(rootElement: HTMLElement): void;

@@ -40,2 +45,4 @@ unmount(): void;

getDeltaByRangeIndex(rangeIndex: VRange['index']): DeltaInsert | null;
getTextPoint(rangeIndex: VRange['index']): TextPoint;
getLine(rangeIndex: VRange['index']): readonly [VirgoLine, number];
/**

@@ -48,3 +55,2 @@ * In following example, the vRange is { index: 3, length: 3 } and

getDeltasByVRange(vRange: VRange): DeltaEntry[];
getRootElement(): HTMLElement | null;
getVRange(): VRange | null;

@@ -51,0 +57,0 @@ getReadOnly(): boolean;

@@ -67,2 +67,9 @@ import { assertExists, Signal } from '@blocksuite/global/utils';

}
get yText() {
return this._yText;
}
get rootElement() {
assertExists(this._rootElement);
return this._rootElement;
}
constructor(yText, opts = {}) {

@@ -143,3 +150,3 @@ this._rootElement = null;

// updates in lit are performed asynchronously
setTimeout(fn, 0);
requestAnimationFrame(fn);
};

@@ -149,10 +156,7 @@ if (!yText.doc) {

}
this.yText = yText;
const { renderElement, onKeyDown } = opts;
this._yText = yText;
const { renderElement } = opts;
if (renderElement) {
this._renderElement = renderElement;
}
if (onKeyDown) {
this._onKeyDown = onKeyDown;
}
this.signals = {

@@ -163,2 +167,5 @@ updateVRange: new Signal(),

}
bindKeyDownHandler(handler) {
this._onKeyDown = handler;
}
mount(rootElement) {

@@ -227,2 +234,41 @@ this._rootElement = rootElement;

}
getTextPoint(rangeIndex) {
assertExists(this._rootElement);
const textElements = Array.from(this._rootElement.querySelectorAll('[data-virgo-text="true"]'));
let index = 0;
for (const textElement of textElements) {
if (!textElement.textContent) {
throw new Error('text element should have textContent');
}
if (textElement.textContent === ZERO_WIDTH_SPACE) {
continue;
}
if (index + textElement.textContent.length >= rangeIndex) {
const text = getTextNodeFromElement(textElement);
if (!text) {
throw new Error('text node should have text content');
}
return [text, rangeIndex - index];
}
index += textElement.textContent.length;
}
throw new Error('failed to find leaf');
}
// the number is releated to the VirgoLine's textLength
getLine(rangeIndex) {
assertExists(this._rootElement);
const lineElements = Array.from(this._rootElement.querySelectorAll('virgo-line'));
let index = 0;
for (const lineElement of lineElements) {
if (rangeIndex >= index && rangeIndex < index + lineElement.textLength) {
return [lineElement, rangeIndex - index];
}
if (rangeIndex === index + lineElement.textLength &&
rangeIndex === this.yText.length) {
return [lineElement, rangeIndex - index];
}
index += lineElement.textLength + 1;
}
throw new Error('failed to find line');
}
/**

@@ -250,5 +296,2 @@ * In following example, the vRange is { index: 3, length: 3 } and

}
getRootElement() {
return this._rootElement;
}
getVRange() {

@@ -291,3 +334,3 @@ return this._vRange;

formatText(vRange, attributes, options = {}) {
const { match = () => true, mode = 'replace' } = options;
const { match = () => true, mode = 'merge' } = options;
const deltas = this.getDeltasByVRange(vRange);

@@ -633,3 +676,3 @@ for (const [delta, deltaVRange] of deltas) {

function findDocumentOrShadowRoot(editor) {
const el = editor.getRootElement();
const el = editor.rootElement;
if (!el) {

@@ -636,0 +679,0 @@ throw new Error('editor root element not found');

{
"name": "@blocksuite/virgo",
"version": "0.5.0-20230302104138-788dae4",
"version": "0.5.0-20230302142916-8e090d3",
"description": "A micro editor.",

@@ -26,3 +26,3 @@ "main": "dist/index.js",

"dependencies": {
"@blocksuite/global": "0.5.0-20230302104138-788dae4",
"@blocksuite/global": "0.5.0-20230302142916-8e090d3",
"zod": "^3.20.6"

@@ -29,0 +29,0 @@ },

@@ -11,2 +11,10 @@ import { html, LitElement } from 'lit';

get textLength() {
return this.elements.reduce((acc, el) => acc + el.delta.insert.length, 0);
}
get textContent() {
return this.elements.reduce((acc, el) => acc + el.delta.insert, '');
}
render() {

@@ -13,0 +21,0 @@ return html`

@@ -78,1 +78,25 @@ import { getDefaultPlaygroundURL } from '@blocksuite/global/utils';

}
export async function getVirgoRichTextLine(
page: Page,
index: number,
i = 0
): Promise<readonly [string, number]> {
return await page.evaluate(
([index, i]) => {
const richTexts = document
.querySelector('test-page')
?.shadowRoot?.querySelectorAll('rich-text');
if (!richTexts) {
throw new Error('Cannot find rich-text');
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const editor = (richTexts[i] as any).vEditor as VEditor;
const line = editor.getLine(index);
return [line[0].textContent, line[1]] as const;
},
[index, i]
);
}

@@ -8,2 +8,3 @@ import { expect, test } from '@playwright/test';

getDeltaFromVirgoRichText,
getVirgoRichTextLine,
press,

@@ -43,2 +44,3 @@ setVirgoRichTextRange,

await focusVirgoRichText(page);
await page.waitForTimeout(50);
await press(page, 'Backspace');

@@ -63,2 +65,3 @@ await press(page, 'Backspace');

await focusVirgoRichText(page);
await page.waitForTimeout(50);
await press(page, 'Enter');

@@ -84,2 +87,3 @@ await press(page, 'Enter');

await focusVirgoRichText(page);
await page.waitForTimeout(50);
await press(page, 'Backspace');

@@ -104,2 +108,3 @@ await press(page, 'Backspace');

await focusVirgoRichText(page);
await page.waitForTimeout(50);
await press(page, 'ArrowLeft');

@@ -125,2 +130,3 @@ await press(page, 'ArrowLeft');

await focusVirgoRichText(page);
await page.waitForTimeout(50);
await press(page, 'ArrowLeft');

@@ -213,2 +219,3 @@ await press(page, 'ArrowLeft');

editorABold.click();
page.waitForTimeout(50);
delta = await getDeltaFromVirgoRichText(page);

@@ -231,2 +238,3 @@ expect(delta).toEqual([

editorAItalic.click();
page.waitForTimeout(50);
delta = await getDeltaFromVirgoRichText(page);

@@ -250,2 +258,3 @@ expect(delta).toEqual([

editorAUnderline.click();
page.waitForTimeout(50);
delta = await getDeltaFromVirgoRichText(page);

@@ -270,2 +279,3 @@ expect(delta).toEqual([

editorAStrike.click();
page.waitForTimeout(50);
delta = await getDeltaFromVirgoRichText(page);

@@ -291,2 +301,3 @@ expect(delta).toEqual([

editorACode.click();
page.waitForTimeout(50);
delta = await getDeltaFromVirgoRichText(page);

@@ -315,2 +326,3 @@ expect(delta).toEqual([

});
page.waitForTimeout(50);
delta = await getDeltaFromVirgoRichText(page);

@@ -326,2 +338,3 @@ expect(delta).toEqual([

});
page.waitForTimeout(50);
delta = await getDeltaFromVirgoRichText(page);

@@ -348,2 +361,3 @@ expect(delta).toEqual([

editorABold.click();
page.waitForTimeout(50);
delta = await getDeltaFromVirgoRichText(page);

@@ -369,2 +383,3 @@ expect(delta).toEqual([

editorAItalic.click();
page.waitForTimeout(50);
delta = await getDeltaFromVirgoRichText(page);

@@ -389,2 +404,3 @@ expect(delta).toEqual([

editorAUnderline.click();
page.waitForTimeout(50);
delta = await getDeltaFromVirgoRichText(page);

@@ -408,2 +424,3 @@ expect(delta).toEqual([

editorAStrike.click();
page.waitForTimeout(50);
delta = await getDeltaFromVirgoRichText(page);

@@ -426,2 +443,3 @@ expect(delta).toEqual([

editorACode.click();
page.waitForTimeout(50);
delta = await getDeltaFromVirgoRichText(page);

@@ -627,2 +645,3 @@ expect(delta).toEqual([

await focusVirgoRichText(page);
await page.waitForTimeout(50);
await press(page, 'ArrowLeft');

@@ -687,1 +706,41 @@ await press(page, 'ArrowLeft');

});
test('getLine', async ({ page }) => {
await enterVirgoPlayground(page);
await focusVirgoRichText(page);
const editorA = page.locator('[data-virgo-root="true"]').nth(0);
const editorB = page.locator('[data-virgo-root="true"]').nth(1);
expect(await editorA.innerText()).toBe(ZERO_WIDTH_SPACE);
expect(await editorB.innerText()).toBe(ZERO_WIDTH_SPACE);
await type(page, 'abc');
await press(page, 'Enter');
await type(page, 'def');
await press(page, 'Enter');
await type(page, 'ghi');
expect(await editorA.innerText()).toBe('abc\ndef\nghi');
expect(await editorB.innerText()).toBe('abc\ndef\nghi');
const [line1, offset1] = await getVirgoRichTextLine(page, 0);
const [line2, offset2] = await getVirgoRichTextLine(page, 1);
const [line3, offset3] = await getVirgoRichTextLine(page, 4);
const [line4, offset4] = await getVirgoRichTextLine(page, 5);
const [line5, offset5] = await getVirgoRichTextLine(page, 8);
const [line6, offset6] = await getVirgoRichTextLine(page, 11);
expect(line1).toEqual('abc');
expect(offset1).toEqual(0);
expect(line2).toEqual('abc');
expect(offset2).toEqual(1);
expect(line3).toEqual('def');
expect(offset3).toEqual(0);
expect(line4).toEqual('def');
expect(offset4).toEqual(1);
expect(line5).toEqual('ghi');
expect(offset5).toEqual(0);
expect(line6).toEqual('ghi');
expect(offset6).toEqual(3);
});

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

// corresponding to [anchorNode/focusNode, anchorOffset/focusOffset]
export type NativePoint = readonly [node: Node, offset: number];
// the number here is relative to the text node
export type TextPoint = readonly [text: Text, offset: number];
export interface DomPoint {

@@ -32,3 +36,3 @@ // which text node this point is in

offset: number
): readonly [Text, number] | null {
): TextPoint | null {
let text: Text | null = null;

@@ -118,2 +122,3 @@ let textOffset = offset;

private _isReadOnly = false;
private _yText: Y.Text;
private _renderElement: (delta: DeltaInsert) => TextElement =

@@ -128,4 +133,11 @@ baseRenderElement;

};
yText: Y.Text;
get yText() {
return this._yText;
}
get rootElement() {
assertExists(this._rootElement);
return this._rootElement;
}
constructor(

@@ -135,3 +147,2 @@ yText: VEditor['yText'],

renderElement?: (delta: DeltaInsert) => TextElement;
onKeyDown?: (event: KeyboardEvent) => void;
} = {}

@@ -143,4 +154,4 @@ ) {

this.yText = yText;
const { renderElement, onKeyDown } = opts;
this._yText = yText;
const { renderElement } = opts;

@@ -151,6 +162,2 @@ if (renderElement) {

if (onKeyDown) {
this._onKeyDown = onKeyDown;
}
this.signals = {

@@ -163,2 +170,6 @@ updateVRange: new Signal<UpdateVRangeProp>(),

bindKeyDownHandler(handler: (event: KeyboardEvent) => void) {
this._onKeyDown = handler;
}
mount(rootElement: HTMLElement): void {

@@ -251,2 +262,53 @@ this._rootElement = rootElement;

getTextPoint(rangeIndex: VRange['index']): TextPoint {
assertExists(this._rootElement);
const textElements = Array.from(
this._rootElement.querySelectorAll('[data-virgo-text="true"]')
);
let index = 0;
for (const textElement of textElements) {
if (!textElement.textContent) {
throw new Error('text element should have textContent');
}
if (textElement.textContent === ZERO_WIDTH_SPACE) {
continue;
}
if (index + textElement.textContent.length >= rangeIndex) {
const text = getTextNodeFromElement(textElement);
if (!text) {
throw new Error('text node should have text content');
}
return [text, rangeIndex - index];
}
index += textElement.textContent.length;
}
throw new Error('failed to find leaf');
}
// the number is releated to the VirgoLine's textLength
getLine(rangeIndex: VRange['index']): readonly [VirgoLine, number] {
assertExists(this._rootElement);
const lineElements = Array.from(
this._rootElement.querySelectorAll('virgo-line')
);
let index = 0;
for (const lineElement of lineElements) {
if (rangeIndex >= index && rangeIndex < index + lineElement.textLength) {
return [lineElement, rangeIndex - index] as const;
}
if (
rangeIndex === index + lineElement.textLength &&
rangeIndex === this.yText.length
) {
return [lineElement, rangeIndex - index] as const;
}
index += lineElement.textLength + 1;
}
throw new Error('failed to find line');
}
/**

@@ -279,6 +341,2 @@ * In following example, the vRange is { index: 3, length: 3 } and

getRootElement(): HTMLElement | null {
return this._rootElement;
}
getVRange(): VRange | null {

@@ -337,3 +395,3 @@ return this._vRange;

): void {
const { match = () => true, mode = 'replace' } = options;
const { match = () => true, mode = 'merge' } = options;
const deltas = this.getDeltasByVRange(vRange);

@@ -802,3 +860,3 @@

// updates in lit are performed asynchronously
setTimeout(fn, 0);
requestAnimationFrame(fn);
};

@@ -878,3 +936,3 @@

function findDocumentOrShadowRoot(editor: VEditor): Document {
const el = editor.getRootElement();
const el = editor.rootElement;

@@ -881,0 +939,0 @@ if (!el) {

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