mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-07-05 03:25:10 +08:00
Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8024172569 | |||
| b434b95548 |
@@ -23,7 +23,7 @@
|
||||
"@floating-ui/dom": "^1.6.13",
|
||||
"@lit/context": "^1.1.2",
|
||||
"@preact/signals-core": "^1.8.0",
|
||||
"@toeverything/theme": "^1.1.15",
|
||||
"@toeverything/theme": "^1.1.16",
|
||||
"file-type": "^21.0.0",
|
||||
"lit": "^3.2.0",
|
||||
"minimatch": "^10.0.1",
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
"@blocksuite/store": "workspace:*",
|
||||
"@lit/context": "^1.1.2",
|
||||
"@preact/signals-core": "^1.8.0",
|
||||
"@toeverything/theme": "^1.1.15",
|
||||
"@toeverything/theme": "^1.1.16",
|
||||
"lit": "^3.2.0",
|
||||
"minimatch": "^10.0.1",
|
||||
"rxjs": "^7.8.1",
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
"@floating-ui/dom": "^1.6.10",
|
||||
"@lit/context": "^1.1.2",
|
||||
"@preact/signals-core": "^1.8.0",
|
||||
"@toeverything/theme": "^1.1.15",
|
||||
"@toeverything/theme": "^1.1.16",
|
||||
"@types/mdast": "^4.0.4",
|
||||
"emoji-mart": "^5.6.0",
|
||||
"lit": "^3.2.0",
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
"@floating-ui/dom": "^1.6.13",
|
||||
"@lit/context": "^1.1.2",
|
||||
"@preact/signals-core": "^1.8.0",
|
||||
"@toeverything/theme": "^1.1.15",
|
||||
"@toeverything/theme": "^1.1.16",
|
||||
"@types/mdast": "^4.0.4",
|
||||
"lit": "^3.2.0",
|
||||
"minimatch": "^10.0.1",
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
"@floating-ui/dom": "^1.6.13",
|
||||
"@lit/context": "^1.1.2",
|
||||
"@preact/signals-core": "^1.8.0",
|
||||
"@toeverything/theme": "^1.1.15",
|
||||
"@toeverything/theme": "^1.1.16",
|
||||
"@types/mdast": "^4.0.4",
|
||||
"lit": "^3.2.0",
|
||||
"minimatch": "^10.0.1",
|
||||
|
||||
@@ -42,6 +42,7 @@ import { computed, signal } from '@preact/signals-core';
|
||||
import { css, nothing, unsafeCSS } from 'lit';
|
||||
import { html } from 'lit/static-html.js';
|
||||
|
||||
import { repeat } from 'lit/directives/repeat.js';
|
||||
import { BlockQueryDataSource } from './data-source.js';
|
||||
import type { DataViewBlockModel } from './data-view-model.js';
|
||||
|
||||
@@ -303,9 +304,16 @@ export class DataViewBlockComponent extends CaptionedBlockComponent<DataViewBloc
|
||||
},
|
||||
});
|
||||
override renderBlock() {
|
||||
const widgets = html`${repeat(
|
||||
Object.entries(this.widgets),
|
||||
([id]) => id,
|
||||
([_, widget]) => widget
|
||||
)}`;
|
||||
|
||||
return html`
|
||||
<div contenteditable="false" style="position: relative">
|
||||
${this.dataViewRootLogic.render()}
|
||||
${widgets}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
"@floating-ui/dom": "^1.6.13",
|
||||
"@lit/context": "^1.1.2",
|
||||
"@preact/signals-core": "^1.8.0",
|
||||
"@toeverything/theme": "^1.1.15",
|
||||
"@toeverything/theme": "^1.1.16",
|
||||
"@types/mdast": "^4.0.4",
|
||||
"date-fns": "^4.0.0",
|
||||
"lit": "^3.2.0",
|
||||
|
||||
@@ -45,6 +45,7 @@ import { autoUpdate } from '@floating-ui/dom';
|
||||
import { computed, signal } from '@preact/signals-core';
|
||||
import { html, nothing } from 'lit';
|
||||
|
||||
import { repeat } from 'lit/directives/repeat.js';
|
||||
import { popSideDetail } from './components/layout.js';
|
||||
import { DatabaseConfigExtension } from './config.js';
|
||||
import { EditorHostKey } from './context/host-context.js';
|
||||
@@ -428,9 +429,15 @@ export class DatabaseBlockComponent extends CaptionedBlockComponent<DatabaseBloc
|
||||
})
|
||||
);
|
||||
override renderBlock() {
|
||||
const widgets = html`${repeat(
|
||||
Object.entries(this.widgets),
|
||||
([id]) => id,
|
||||
([_, widget]) => widget
|
||||
)}`;
|
||||
|
||||
return html`
|
||||
<div contenteditable="false" class="${databaseContentStyles}">
|
||||
${this.dataViewRootLogic.value.render()}
|
||||
${this.dataViewRootLogic.value.render()} ${widgets}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
"@floating-ui/dom": "^1.6.13",
|
||||
"@lit/context": "^1.1.2",
|
||||
"@preact/signals-core": "^1.8.0",
|
||||
"@toeverything/theme": "^1.1.15",
|
||||
"@toeverything/theme": "^1.1.16",
|
||||
"@types/mdast": "^4.0.4",
|
||||
"lit": "^3.2.0",
|
||||
"minimatch": "^10.0.1",
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
"@floating-ui/dom": "^1.6.13",
|
||||
"@lit/context": "^1.1.2",
|
||||
"@preact/signals-core": "^1.8.0",
|
||||
"@toeverything/theme": "^1.1.15",
|
||||
"@toeverything/theme": "^1.1.16",
|
||||
"lit": "^3.2.0",
|
||||
"minimatch": "^10.0.1",
|
||||
"rxjs": "^7.8.1",
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
"@floating-ui/dom": "^1.6.13",
|
||||
"@lit/context": "^1.1.2",
|
||||
"@preact/signals-core": "^1.8.0",
|
||||
"@toeverything/theme": "^1.1.15",
|
||||
"@toeverything/theme": "^1.1.16",
|
||||
"@types/lodash-es": "^4.17.12",
|
||||
"lit": "^3.2.0",
|
||||
"lodash-es": "^4.17.21",
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
"@floating-ui/dom": "^1.6.13",
|
||||
"@lit/context": "^1.1.2",
|
||||
"@preact/signals-core": "^1.8.0",
|
||||
"@toeverything/theme": "^1.1.15",
|
||||
"@toeverything/theme": "^1.1.16",
|
||||
"@types/lodash-es": "^4.17.12",
|
||||
"lit": "^3.2.0",
|
||||
"lodash-es": "^4.17.21",
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
"@floating-ui/dom": "^1.6.13",
|
||||
"@lit/context": "^1.1.2",
|
||||
"@preact/signals-core": "^1.8.0",
|
||||
"@toeverything/theme": "^1.1.15",
|
||||
"@toeverything/theme": "^1.1.16",
|
||||
"@types/mdast": "^4.0.4",
|
||||
"lit": "^3.2.0",
|
||||
"minimatch": "^10.0.1",
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
"@floating-ui/dom": "^1.6.13",
|
||||
"@lit/context": "^1.1.2",
|
||||
"@preact/signals-core": "^1.8.0",
|
||||
"@toeverything/theme": "^1.1.15",
|
||||
"@toeverything/theme": "^1.1.16",
|
||||
"file-type": "^21.0.0",
|
||||
"lit": "^3.2.0",
|
||||
"minimatch": "^10.0.1",
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
"@floating-ui/dom": "^1.6.13",
|
||||
"@lit/context": "^1.1.2",
|
||||
"@preact/signals-core": "^1.8.0",
|
||||
"@toeverything/theme": "^1.1.15",
|
||||
"@toeverything/theme": "^1.1.16",
|
||||
"@types/katex": "^0.16.7",
|
||||
"@types/mdast": "^4.0.4",
|
||||
"katex": "^0.16.11",
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
"@floating-ui/dom": "^1.6.13",
|
||||
"@lit/context": "^1.1.2",
|
||||
"@preact/signals-core": "^1.8.0",
|
||||
"@toeverything/theme": "^1.1.15",
|
||||
"@toeverything/theme": "^1.1.16",
|
||||
"@types/mdast": "^4.0.4",
|
||||
"lit": "^3.2.0",
|
||||
"minimatch": "^10.0.1",
|
||||
|
||||
@@ -23,6 +23,7 @@ import { effect } from '@preact/signals-core';
|
||||
import { html, nothing, type TemplateResult } from 'lit';
|
||||
import { query, state } from 'lit/decorators.js';
|
||||
import { classMap } from 'lit/directives/class-map.js';
|
||||
import { repeat } from 'lit/directives/repeat.js';
|
||||
import { styleMap } from 'lit/directives/style-map.js';
|
||||
|
||||
import { correctNumberedListsOrderToPrev } from './commands/utils.js';
|
||||
@@ -138,6 +139,11 @@ export class ListBlockComponent extends CaptionedBlockComponent<ListBlockModel>
|
||||
|
||||
override renderBlock(): TemplateResult<1> {
|
||||
const { model, _onClickIcon } = this;
|
||||
const widgets = html`${repeat(
|
||||
Object.entries(this.widgets),
|
||||
([id]) => id,
|
||||
([_, widget]) => widget
|
||||
)}`;
|
||||
const collapsed = this.store.readonly
|
||||
? this._readonlyCollapsed
|
||||
: model.props.collapsed;
|
||||
@@ -199,7 +205,7 @@ export class ListBlockComponent extends CaptionedBlockComponent<ListBlockModel>
|
||||
></rich-text>
|
||||
</div>
|
||||
|
||||
${children}
|
||||
${children} ${widgets}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
"@blocksuite/store": "workspace:*",
|
||||
"@lit/context": "^1.1.2",
|
||||
"@preact/signals-core": "^1.8.0",
|
||||
"@toeverything/theme": "^1.1.15",
|
||||
"@toeverything/theme": "^1.1.16",
|
||||
"@types/lodash-es": "^4.17.12",
|
||||
"@types/mdast": "^4.0.4",
|
||||
"@vanilla-extract/css": "^1.17.0",
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
"@floating-ui/dom": "^1.6.13",
|
||||
"@lit/context": "^1.1.2",
|
||||
"@preact/signals-core": "^1.8.0",
|
||||
"@toeverything/theme": "^1.1.15",
|
||||
"@toeverything/theme": "^1.1.16",
|
||||
"@types/mdast": "^4.0.4",
|
||||
"lit": "^3.2.0",
|
||||
"minimatch": "^10.0.1",
|
||||
|
||||
@@ -26,6 +26,7 @@ import { computed, effect, signal } from '@preact/signals-core';
|
||||
import { html, nothing, type TemplateResult } from 'lit';
|
||||
import { query, state } from 'lit/decorators.js';
|
||||
import { classMap } from 'lit/directives/class-map.js';
|
||||
import { repeat } from 'lit/directives/repeat.js';
|
||||
import { styleMap } from 'lit/directives/style-map.js';
|
||||
import { unsafeHTML } from 'lit/directives/unsafe-html.js';
|
||||
|
||||
@@ -227,6 +228,12 @@ export class ParagraphBlockComponent extends CaptionedBlockComponent<ParagraphBl
|
||||
}
|
||||
|
||||
override renderBlock(): TemplateResult<1> {
|
||||
const widgets = html`${repeat(
|
||||
Object.entries(this.widgets),
|
||||
([id]) => id,
|
||||
([_, widget]) => widget
|
||||
)}`;
|
||||
|
||||
const { type$ } = this.model.props;
|
||||
const collapsed = this.store.readonly
|
||||
? this._readonlyCollapsed
|
||||
@@ -340,7 +347,7 @@ export class ParagraphBlockComponent extends CaptionedBlockComponent<ParagraphBl
|
||||
`}
|
||||
</div>
|
||||
|
||||
${children}
|
||||
${children} ${widgets}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@
|
||||
"@floating-ui/dom": "^1.6.13",
|
||||
"@lit/context": "^1.1.2",
|
||||
"@preact/signals-core": "^1.8.0",
|
||||
"@toeverything/theme": "^1.1.15",
|
||||
"@toeverything/theme": "^1.1.16",
|
||||
"@types/lodash-es": "^4.17.12",
|
||||
"dompurify": "^3.2.4",
|
||||
"html2canvas": "^1.4.1",
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
"@floating-ui/dom": "^1.6.13",
|
||||
"@lit/context": "^1.1.2",
|
||||
"@preact/signals-core": "^1.8.0",
|
||||
"@toeverything/theme": "^1.1.15",
|
||||
"@toeverything/theme": "^1.1.16",
|
||||
"@types/lodash-es": "^4.17.12",
|
||||
"fractional-indexing": "^3.2.0",
|
||||
"lit": "^3.2.0",
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
"@blocksuite/store": "workspace:*",
|
||||
"@lit/context": "^1.1.2",
|
||||
"@preact/signals-core": "^1.8.0",
|
||||
"@toeverything/theme": "^1.1.15",
|
||||
"@toeverything/theme": "^1.1.16",
|
||||
"@types/lodash-es": "^4.17.12",
|
||||
"fractional-indexing": "^3.2.0",
|
||||
"html2canvas": "^1.4.1",
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
"@lit/context": "^1.1.2",
|
||||
"@lottiefiles/dotlottie-wc": "^0.5.0",
|
||||
"@preact/signals-core": "^1.8.0",
|
||||
"@toeverything/theme": "^1.1.15",
|
||||
"@toeverything/theme": "^1.1.16",
|
||||
"@types/hast": "^3.0.4",
|
||||
"@types/katex": "^0.16.7",
|
||||
"@types/lodash-es": "^4.17.12",
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
"@floating-ui/dom": "^1.6.13",
|
||||
"@lit/context": "^1.1.2",
|
||||
"@preact/signals-core": "^1.8.0",
|
||||
"@toeverything/theme": "^1.1.15",
|
||||
"@toeverything/theme": "^1.1.16",
|
||||
"@types/lodash-es": "^4.17.12",
|
||||
"clsx": "^2.1.1",
|
||||
"date-fns": "^4.0.0",
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
"@floating-ui/dom": "^1.6.13",
|
||||
"@lit/context": "^1.1.2",
|
||||
"@preact/signals-core": "^1.8.0",
|
||||
"@toeverything/theme": "^1.1.15",
|
||||
"@toeverything/theme": "^1.1.16",
|
||||
"@types/lodash-es": "^4.17.12",
|
||||
"lit": "^3.2.0",
|
||||
"lodash-es": "^4.17.21",
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
"@floating-ui/dom": "^1.6.13",
|
||||
"@lit/context": "^1.1.2",
|
||||
"@preact/signals-core": "^1.8.0",
|
||||
"@toeverything/theme": "^1.1.15",
|
||||
"@toeverything/theme": "^1.1.16",
|
||||
"lit": "^3.2.0",
|
||||
"rxjs": "^7.8.1"
|
||||
},
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
"@floating-ui/dom": "^1.6.13",
|
||||
"@lit/context": "^1.1.2",
|
||||
"@preact/signals-core": "^1.8.0",
|
||||
"@toeverything/theme": "^1.1.15",
|
||||
"@toeverything/theme": "^1.1.16",
|
||||
"lit": "^3.2.0",
|
||||
"minimatch": "^10.0.1",
|
||||
"rxjs": "^7.8.1",
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
"@floating-ui/dom": "^1.6.13",
|
||||
"@lit/context": "^1.1.2",
|
||||
"@preact/signals-core": "^1.8.0",
|
||||
"@toeverything/theme": "^1.1.15",
|
||||
"@toeverything/theme": "^1.1.16",
|
||||
"@types/lodash-es": "^4.17.12",
|
||||
"lit": "^3.2.0",
|
||||
"lodash-es": "^4.17.21",
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
"@floating-ui/dom": "^1.6.13",
|
||||
"@lit/context": "^1.1.2",
|
||||
"@preact/signals-core": "^1.8.0",
|
||||
"@toeverything/theme": "^1.1.15",
|
||||
"@toeverything/theme": "^1.1.16",
|
||||
"@vanilla-extract/css": "^1.17.0",
|
||||
"lit": "^3.2.0",
|
||||
"minimatch": "^10.0.1",
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
"@blocksuite/store": "workspace:*",
|
||||
"@lit/context": "^1.1.2",
|
||||
"@preact/signals-core": "^1.8.0",
|
||||
"@toeverything/theme": "^1.1.15",
|
||||
"@toeverything/theme": "^1.1.16",
|
||||
"@types/lodash-es": "^4.17.12",
|
||||
"lit": "^3.2.0",
|
||||
"lodash-es": "^4.17.21",
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
"@blocksuite/store": "workspace:*",
|
||||
"@lit/context": "^1.1.2",
|
||||
"@preact/signals-core": "^1.8.0",
|
||||
"@toeverything/theme": "^1.1.15",
|
||||
"@toeverything/theme": "^1.1.16",
|
||||
"@types/lodash-es": "^4.17.12",
|
||||
"lit": "^3.2.0",
|
||||
"lodash-es": "^4.17.21",
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
"@blocksuite/store": "workspace:*",
|
||||
"@lit/context": "^1.1.2",
|
||||
"@preact/signals-core": "^1.8.0",
|
||||
"@toeverything/theme": "^1.1.15",
|
||||
"@toeverything/theme": "^1.1.16",
|
||||
"@types/lodash-es": "^4.17.12",
|
||||
"lit": "^3.2.0",
|
||||
"lodash-es": "^4.17.21",
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
"@blocksuite/store": "workspace:*",
|
||||
"@lit/context": "^1.1.2",
|
||||
"@preact/signals-core": "^1.8.0",
|
||||
"@toeverything/theme": "^1.1.15",
|
||||
"@toeverything/theme": "^1.1.16",
|
||||
"@types/lodash-es": "^4.17.12",
|
||||
"lit": "^3.2.0",
|
||||
"lodash-es": "^4.17.21",
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
"@blocksuite/store": "workspace:*",
|
||||
"@lit/context": "^1.1.2",
|
||||
"@preact/signals-core": "^1.8.0",
|
||||
"@toeverything/theme": "^1.1.15",
|
||||
"@toeverything/theme": "^1.1.16",
|
||||
"@types/lodash-es": "^4.17.12",
|
||||
"lit": "^3.2.0",
|
||||
"lodash-es": "^4.17.21",
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
"@blocksuite/store": "workspace:*",
|
||||
"@lit/context": "^1.1.2",
|
||||
"@preact/signals-core": "^1.8.0",
|
||||
"@toeverything/theme": "^1.1.15",
|
||||
"@toeverything/theme": "^1.1.16",
|
||||
"@types/lodash-es": "^4.17.12",
|
||||
"lit": "^3.2.0",
|
||||
"lodash-es": "^4.17.21",
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
"@blocksuite/store": "workspace:*",
|
||||
"@lit/context": "^1.1.2",
|
||||
"@preact/signals-core": "^1.8.0",
|
||||
"@toeverything/theme": "^1.1.15",
|
||||
"@toeverything/theme": "^1.1.16",
|
||||
"@types/lodash-es": "^4.17.12",
|
||||
"lit": "^3.2.0",
|
||||
"lodash-es": "^4.17.21",
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
"@blocksuite/store": "workspace:*",
|
||||
"@lit/context": "^1.1.2",
|
||||
"@preact/signals-core": "^1.8.0",
|
||||
"@toeverything/theme": "^1.1.15",
|
||||
"@toeverything/theme": "^1.1.16",
|
||||
"@types/lodash-es": "^4.17.12",
|
||||
"lit": "^3.2.0",
|
||||
"lodash-es": "^4.17.21",
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
"@floating-ui/dom": "^1.6.13",
|
||||
"@lit/context": "^1.1.2",
|
||||
"@preact/signals-core": "^1.8.0",
|
||||
"@toeverything/theme": "^1.1.15",
|
||||
"@toeverything/theme": "^1.1.16",
|
||||
"@types/lodash-es": "^4.17.12",
|
||||
"lit": "^3.2.0",
|
||||
"lodash-es": "^4.17.21",
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
"@blocksuite/store": "workspace:*",
|
||||
"@lit/context": "^1.1.2",
|
||||
"@preact/signals-core": "^1.8.0",
|
||||
"@toeverything/theme": "^1.1.15",
|
||||
"@toeverything/theme": "^1.1.16",
|
||||
"@types/lodash-es": "^4.17.12",
|
||||
"lit": "^3.2.0",
|
||||
"lodash-es": "^4.17.21",
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
"@floating-ui/dom": "^1.6.13",
|
||||
"@lit/context": "^1.1.2",
|
||||
"@preact/signals-core": "^1.8.0",
|
||||
"@toeverything/theme": "^1.1.15",
|
||||
"@toeverything/theme": "^1.1.16",
|
||||
"@types/lodash-es": "^4.17.12",
|
||||
"collapse-white-space": "^2.1.0",
|
||||
"date-fns": "^4.0.0",
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
"@floating-ui/dom": "^1.6.13",
|
||||
"@lit/context": "^1.1.2",
|
||||
"@preact/signals-core": "^1.8.0",
|
||||
"@toeverything/theme": "^1.1.15",
|
||||
"@toeverything/theme": "^1.1.16",
|
||||
"@types/hast": "^3.0.4",
|
||||
"@types/katex": "^0.16.7",
|
||||
"@types/lodash-es": "^4.17.12",
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
"@floating-ui/dom": "^1.6.13",
|
||||
"@lit/context": "^1.1.2",
|
||||
"@preact/signals-core": "^1.8.0",
|
||||
"@toeverything/theme": "^1.1.15",
|
||||
"@toeverything/theme": "^1.1.16",
|
||||
"@types/lodash-es": "^4.17.12",
|
||||
"collapse-white-space": "^2.1.0",
|
||||
"date-fns": "^4.0.0",
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
"@floating-ui/dom": "^1.6.13",
|
||||
"@lit/context": "^1.1.2",
|
||||
"@preact/signals-core": "^1.8.0",
|
||||
"@toeverything/theme": "^1.1.15",
|
||||
"@toeverything/theme": "^1.1.16",
|
||||
"@types/lodash-es": "^4.17.12",
|
||||
"collapse-white-space": "^2.1.0",
|
||||
"date-fns": "^4.0.0",
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
"@floating-ui/dom": "^1.6.13",
|
||||
"@lit/context": "^1.1.2",
|
||||
"@preact/signals-core": "^1.8.0",
|
||||
"@toeverything/theme": "^1.1.15",
|
||||
"@toeverything/theme": "^1.1.16",
|
||||
"@types/hast": "^3.0.4",
|
||||
"@types/katex": "^0.16.7",
|
||||
"@types/lodash-es": "^4.17.12",
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
"@floating-ui/dom": "^1.6.13",
|
||||
"@lit/context": "^1.1.2",
|
||||
"@preact/signals-core": "^1.8.0",
|
||||
"@toeverything/theme": "^1.1.15",
|
||||
"@toeverything/theme": "^1.1.16",
|
||||
"@types/lodash-es": "^4.17.12",
|
||||
"collapse-white-space": "^2.1.0",
|
||||
"date-fns": "^4.0.0",
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
"@blocksuite/global": "workspace:*",
|
||||
"@blocksuite/std": "workspace:*",
|
||||
"@blocksuite/store": "workspace:*",
|
||||
"@toeverything/theme": "^1.1.15",
|
||||
"@toeverything/theme": "^1.1.16",
|
||||
"@types/lodash-es": "^4.17.12",
|
||||
"fractional-indexing": "^3.2.0",
|
||||
"lodash-es": "^4.17.21",
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
"@floating-ui/dom": "^1.6.13",
|
||||
"@lit/context": "^1.1.2",
|
||||
"@preact/signals-core": "^1.8.0",
|
||||
"@toeverything/theme": "^1.1.15",
|
||||
"@toeverything/theme": "^1.1.16",
|
||||
"@types/lodash-es": "^4.17.12",
|
||||
"collapse-white-space": "^2.1.0",
|
||||
"date-fns": "^4.0.0",
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
"@floating-ui/dom": "^1.6.13",
|
||||
"@lit/context": "^1.1.2",
|
||||
"@preact/signals-core": "^1.8.0",
|
||||
"@toeverything/theme": "^1.1.15",
|
||||
"@toeverything/theme": "^1.1.16",
|
||||
"@types/bytes": "^3.1.5",
|
||||
"@types/hast": "^3.0.4",
|
||||
"@types/lodash-es": "^4.17.12",
|
||||
@@ -63,7 +63,8 @@
|
||||
"./theme": "./src/theme/index.ts",
|
||||
"./styles": "./src/styles/index.ts",
|
||||
"./services": "./src/services/index.ts",
|
||||
"./adapters": "./src/adapters/index.ts"
|
||||
"./adapters": "./src/adapters/index.ts",
|
||||
"./test-utils": "./src/test-utils/index.ts"
|
||||
},
|
||||
"files": [
|
||||
"src",
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
import { getFirstBlockCommand } from '../../../commands/block-crud/get-first-content-block';
|
||||
import { affine } from '../../helpers/affine-template';
|
||||
import { affine } from '../../../test-utils';
|
||||
|
||||
describe('commands/block-crud', () => {
|
||||
describe('getFirstBlockCommand', () => {
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
import { getLastBlockCommand } from '../../../commands/block-crud/get-last-content-block';
|
||||
import { affine } from '../../helpers/affine-template';
|
||||
import { affine } from '../../../test-utils';
|
||||
|
||||
describe('commands/block-crud', () => {
|
||||
describe('getLastBlockCommand', () => {
|
||||
|
||||
+1
-3
@@ -1,13 +1,11 @@
|
||||
/**
|
||||
* @vitest-environment happy-dom
|
||||
*/
|
||||
import '../../helpers/affine-test-utils';
|
||||
|
||||
import type { TextSelection } from '@blocksuite/std';
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
import { replaceSelectedTextWithBlocksCommand } from '../../../commands/model-crud/replace-selected-text-with-blocks';
|
||||
import { affine, block } from '../../helpers/affine-template';
|
||||
import { affine, block } from '../../../test-utils';
|
||||
|
||||
describe('commands/model-crud', () => {
|
||||
describe('replaceSelectedTextWithBlocksCommand', () => {
|
||||
|
||||
+1
-1
@@ -6,7 +6,7 @@ import { describe, expect, it, vi } from 'vitest';
|
||||
|
||||
import { isNothingSelectedCommand } from '../../../commands/selection/is-nothing-selected';
|
||||
import { ImageSelection } from '../../../selection';
|
||||
import { affine } from '../../helpers/affine-template';
|
||||
import { affine } from '../../../test-utils';
|
||||
|
||||
describe('commands/selection', () => {
|
||||
describe('isNothingSelectedCommand', () => {
|
||||
|
||||
@@ -1,298 +0,0 @@
|
||||
import {
|
||||
CodeBlockSchemaExtension,
|
||||
DatabaseBlockSchemaExtension,
|
||||
ImageBlockSchemaExtension,
|
||||
ListBlockSchemaExtension,
|
||||
NoteBlockSchemaExtension,
|
||||
ParagraphBlockSchemaExtension,
|
||||
RootBlockSchemaExtension,
|
||||
} from '@blocksuite/affine-model';
|
||||
import { TextSelection } from '@blocksuite/std';
|
||||
import { type Block, type Store } from '@blocksuite/store';
|
||||
import { Text } from '@blocksuite/store';
|
||||
import { TestWorkspace } from '@blocksuite/store/test';
|
||||
|
||||
import { createTestHost } from './create-test-host';
|
||||
|
||||
// Extensions array
|
||||
const extensions = [
|
||||
RootBlockSchemaExtension,
|
||||
NoteBlockSchemaExtension,
|
||||
ParagraphBlockSchemaExtension,
|
||||
ListBlockSchemaExtension,
|
||||
ImageBlockSchemaExtension,
|
||||
DatabaseBlockSchemaExtension,
|
||||
CodeBlockSchemaExtension,
|
||||
];
|
||||
|
||||
// Mapping from tag names to flavours
|
||||
const tagToFlavour: Record<string, string> = {
|
||||
'affine-page': 'affine:page',
|
||||
'affine-note': 'affine:note',
|
||||
'affine-paragraph': 'affine:paragraph',
|
||||
'affine-list': 'affine:list',
|
||||
'affine-image': 'affine:image',
|
||||
'affine-database': 'affine:database',
|
||||
'affine-code': 'affine:code',
|
||||
};
|
||||
|
||||
interface SelectionInfo {
|
||||
anchorBlockId?: string;
|
||||
anchorOffset?: number;
|
||||
focusBlockId?: string;
|
||||
focusOffset?: number;
|
||||
cursorBlockId?: string;
|
||||
cursorOffset?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse template strings and build BlockSuite document structure,
|
||||
* then create a host object with the document
|
||||
*
|
||||
* Example:
|
||||
* ```
|
||||
* const host = affine`
|
||||
* <affine-page id="page">
|
||||
* <affine-note id="note">
|
||||
* <affine-paragraph id="paragraph-1">Hello, world<anchor /></affine-paragraph>
|
||||
* <affine-paragraph id="paragraph-2">Hello, world<focus /></affine-paragraph>
|
||||
* </affine-note>
|
||||
* </affine-page>
|
||||
* `;
|
||||
* ```
|
||||
*/
|
||||
export function affine(strings: TemplateStringsArray, ...values: any[]) {
|
||||
// Merge template strings and values
|
||||
let htmlString = '';
|
||||
strings.forEach((str, i) => {
|
||||
htmlString += str;
|
||||
if (i < values.length) {
|
||||
htmlString += values[i];
|
||||
}
|
||||
});
|
||||
|
||||
// Create a new doc
|
||||
const workspace = new TestWorkspace({});
|
||||
workspace.meta.initialize();
|
||||
const doc = workspace.createDoc('test-doc');
|
||||
const store = doc.getStore({ extensions });
|
||||
|
||||
let selectionInfo: SelectionInfo = {};
|
||||
|
||||
// Use DOMParser to parse HTML string
|
||||
doc.load(() => {
|
||||
const parser = new DOMParser();
|
||||
const dom = parser.parseFromString(htmlString.trim(), 'text/html');
|
||||
const root = dom.body.firstElementChild;
|
||||
|
||||
if (!root) {
|
||||
throw new Error('Template must contain a root element');
|
||||
}
|
||||
|
||||
buildDocFromElement(store, root, null, selectionInfo);
|
||||
});
|
||||
|
||||
// Create host object
|
||||
const host = createTestHost(store);
|
||||
|
||||
// Set selection if needed
|
||||
if (selectionInfo.anchorBlockId && selectionInfo.focusBlockId) {
|
||||
const anchorBlock = store.getBlock(selectionInfo.anchorBlockId);
|
||||
const anchorTextLength = anchorBlock?.model?.text?.length ?? 0;
|
||||
const focusOffset = selectionInfo.focusOffset ?? 0;
|
||||
const anchorOffset = selectionInfo.anchorOffset ?? 0;
|
||||
|
||||
if (selectionInfo.anchorBlockId === selectionInfo.focusBlockId) {
|
||||
const selection = host.selection.create(TextSelection, {
|
||||
from: {
|
||||
blockId: selectionInfo.anchorBlockId,
|
||||
index: anchorOffset,
|
||||
length: focusOffset,
|
||||
},
|
||||
to: null,
|
||||
});
|
||||
host.selection.setGroup('note', [selection]);
|
||||
} else {
|
||||
const selection = host.selection.create(TextSelection, {
|
||||
from: {
|
||||
blockId: selectionInfo.anchorBlockId,
|
||||
index: anchorOffset,
|
||||
length: anchorTextLength - anchorOffset,
|
||||
},
|
||||
to: {
|
||||
blockId: selectionInfo.focusBlockId,
|
||||
index: 0,
|
||||
length: focusOffset,
|
||||
},
|
||||
});
|
||||
host.selection.setGroup('note', [selection]);
|
||||
}
|
||||
} else if (selectionInfo.cursorBlockId) {
|
||||
const selection = host.selection.create(TextSelection, {
|
||||
from: {
|
||||
blockId: selectionInfo.cursorBlockId,
|
||||
index: selectionInfo.cursorOffset ?? 0,
|
||||
length: 0,
|
||||
},
|
||||
to: null,
|
||||
});
|
||||
host.selection.setGroup('note', [selection]);
|
||||
}
|
||||
|
||||
return host;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a single block from template string
|
||||
*
|
||||
* Example:
|
||||
* ```
|
||||
* const block = block`<affine-note />`
|
||||
* ```
|
||||
*/
|
||||
export function block(
|
||||
strings: TemplateStringsArray,
|
||||
...values: any[]
|
||||
): Block | null {
|
||||
// Merge template strings and values
|
||||
let htmlString = '';
|
||||
strings.forEach((str, i) => {
|
||||
htmlString += str;
|
||||
if (i < values.length) {
|
||||
htmlString += values[i];
|
||||
}
|
||||
});
|
||||
|
||||
// Create a temporary doc to hold the block
|
||||
const workspace = new TestWorkspace({});
|
||||
workspace.meta.initialize();
|
||||
const doc = workspace.createDoc('temp-doc');
|
||||
const store = doc.getStore({ extensions });
|
||||
|
||||
let blockId: string | null = null;
|
||||
const selectionInfo: SelectionInfo = {};
|
||||
|
||||
// Use DOMParser to parse HTML string
|
||||
doc.load(() => {
|
||||
const parser = new DOMParser();
|
||||
const dom = parser.parseFromString(htmlString.trim(), 'text/html');
|
||||
const root = dom.body.firstElementChild;
|
||||
|
||||
if (!root) {
|
||||
throw new Error('Template must contain a root element');
|
||||
}
|
||||
|
||||
// Create a root block if needed
|
||||
const flavour = tagToFlavour[root.tagName.toLowerCase()];
|
||||
if (
|
||||
flavour === 'affine:paragraph' ||
|
||||
flavour === 'affine:list' ||
|
||||
flavour === 'affine:code'
|
||||
) {
|
||||
const pageId = store.addBlock('affine:page', {});
|
||||
const noteId = store.addBlock('affine:note', {}, pageId);
|
||||
blockId = buildDocFromElement(store, root, noteId, selectionInfo);
|
||||
} else {
|
||||
blockId = buildDocFromElement(store, root, null, selectionInfo);
|
||||
}
|
||||
});
|
||||
|
||||
// Return the created block
|
||||
return blockId ? (store.getBlock(blockId) ?? null) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively build document structure
|
||||
* @param doc
|
||||
* @param element
|
||||
* @param parentId
|
||||
* @param selectionInfo
|
||||
* @returns
|
||||
*/
|
||||
function buildDocFromElement(
|
||||
doc: Store,
|
||||
element: Element,
|
||||
parentId: string | null,
|
||||
selectionInfo: SelectionInfo
|
||||
): string {
|
||||
const tagName = element.tagName.toLowerCase();
|
||||
|
||||
// Handle selection tags
|
||||
if (tagName === 'anchor') {
|
||||
if (!parentId) return '';
|
||||
const parentBlock = doc.getBlock(parentId);
|
||||
if (parentBlock) {
|
||||
const textBeforeCursor = element.previousSibling?.textContent ?? '';
|
||||
selectionInfo.anchorBlockId = parentId;
|
||||
selectionInfo.anchorOffset = textBeforeCursor.length;
|
||||
}
|
||||
return parentId;
|
||||
} else if (tagName === 'focus') {
|
||||
if (!parentId) return '';
|
||||
const parentBlock = doc.getBlock(parentId);
|
||||
if (parentBlock) {
|
||||
const textBeforeCursor = element.previousSibling?.textContent ?? '';
|
||||
selectionInfo.focusBlockId = parentId;
|
||||
selectionInfo.focusOffset = textBeforeCursor.length;
|
||||
}
|
||||
return parentId;
|
||||
} else if (tagName === 'cursor') {
|
||||
if (!parentId) return '';
|
||||
const parentBlock = doc.getBlock(parentId);
|
||||
if (parentBlock) {
|
||||
const textBeforeCursor = element.previousSibling?.textContent ?? '';
|
||||
selectionInfo.cursorBlockId = parentId;
|
||||
selectionInfo.cursorOffset = textBeforeCursor.length;
|
||||
}
|
||||
return parentId;
|
||||
}
|
||||
|
||||
const flavour = tagToFlavour[tagName];
|
||||
|
||||
if (!flavour) {
|
||||
throw new Error(`Unknown tag name: ${tagName}`);
|
||||
}
|
||||
|
||||
const props: Record<string, any> = {};
|
||||
|
||||
const customId = element.getAttribute('id');
|
||||
|
||||
// If ID is specified, add it to props
|
||||
if (customId) {
|
||||
props.id = customId;
|
||||
}
|
||||
|
||||
// Process element attributes
|
||||
Array.from(element.attributes).forEach(attr => {
|
||||
if (attr.name !== 'id') {
|
||||
// Skip id attribute, we already handled it
|
||||
props[attr.name] = attr.value;
|
||||
}
|
||||
});
|
||||
|
||||
// Special handling for different block types based on their flavours
|
||||
switch (flavour) {
|
||||
case 'affine:paragraph':
|
||||
case 'affine:list':
|
||||
if (element.textContent) {
|
||||
props.text = new Text(element.textContent);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Create block
|
||||
const blockId = doc.addBlock(flavour, props, parentId);
|
||||
|
||||
// Process all child nodes, including text nodes
|
||||
Array.from(element.children).forEach(child => {
|
||||
if (child.nodeType === Node.ELEMENT_NODE) {
|
||||
// Handle element nodes
|
||||
buildDocFromElement(doc, child as Element, blockId, selectionInfo);
|
||||
} else if (child.nodeType === Node.TEXT_NODE) {
|
||||
// Handle text nodes
|
||||
console.log('buildDocFromElement text node:', child.textContent);
|
||||
}
|
||||
});
|
||||
|
||||
return blockId;
|
||||
}
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
import { TextSelection } from '@blocksuite/std';
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
import { affine } from './affine-template';
|
||||
import { affine } from '../../test-utils';
|
||||
|
||||
describe('helpers/affine-template', () => {
|
||||
it('should create a basic document structure from template', () => {
|
||||
+1
-1
@@ -7,7 +7,7 @@
|
||||
### Basic Usage
|
||||
|
||||
```typescript
|
||||
import { affine } from '../__tests__/utils/affine-template';
|
||||
import { affine } from '@blocksuite/affine-shared/test-utils';
|
||||
|
||||
// Create a simple document
|
||||
const doc = affine`
|
||||
@@ -0,0 +1,316 @@
|
||||
import {
|
||||
CodeBlockSchemaExtension,
|
||||
DatabaseBlockSchemaExtension,
|
||||
ImageBlockSchemaExtension,
|
||||
ListBlockSchemaExtension,
|
||||
NoteBlockSchemaExtension,
|
||||
ParagraphBlockSchemaExtension,
|
||||
RootBlockSchemaExtension,
|
||||
} from '@blocksuite/affine-model';
|
||||
import { Container } from '@blocksuite/global/di';
|
||||
import { TextSelection } from '@blocksuite/std';
|
||||
import {
|
||||
type Block,
|
||||
type ExtensionType,
|
||||
type Store,
|
||||
Text,
|
||||
} from '@blocksuite/store';
|
||||
import { TestWorkspace } from '@blocksuite/store/test';
|
||||
|
||||
import { createTestHost } from './create-test-host';
|
||||
|
||||
const DEFAULT_EXTENSIONS = [
|
||||
RootBlockSchemaExtension,
|
||||
NoteBlockSchemaExtension,
|
||||
ParagraphBlockSchemaExtension,
|
||||
ListBlockSchemaExtension,
|
||||
ImageBlockSchemaExtension,
|
||||
DatabaseBlockSchemaExtension,
|
||||
CodeBlockSchemaExtension,
|
||||
];
|
||||
|
||||
// Mapping from tag names to flavours
|
||||
const tagToFlavour: Record<string, string> = {
|
||||
'affine-page': 'affine:page',
|
||||
'affine-note': 'affine:note',
|
||||
'affine-paragraph': 'affine:paragraph',
|
||||
'affine-list': 'affine:list',
|
||||
'affine-image': 'affine:image',
|
||||
'affine-database': 'affine:database',
|
||||
'affine-code': 'affine:code',
|
||||
};
|
||||
|
||||
interface SelectionInfo {
|
||||
anchorBlockId?: string;
|
||||
anchorOffset?: number;
|
||||
focusBlockId?: string;
|
||||
focusOffset?: number;
|
||||
cursorBlockId?: string;
|
||||
cursorOffset?: number;
|
||||
}
|
||||
|
||||
export function createAffineTemplate(
|
||||
extensions: ExtensionType[] = DEFAULT_EXTENSIONS
|
||||
) {
|
||||
/**
|
||||
* Parse template strings and build BlockSuite document structure,
|
||||
* then create a host object with the document
|
||||
*
|
||||
* Example:
|
||||
* ```
|
||||
* const host = affine`
|
||||
* <affine-page id="page">
|
||||
* <affine-note id="note">
|
||||
* <affine-paragraph id="paragraph-1">Hello, world<anchor /></affine-paragraph>
|
||||
* <affine-paragraph id="paragraph-2">Hello, world<focus /></affine-paragraph>
|
||||
* </affine-note>
|
||||
* </affine-page>
|
||||
* `;
|
||||
* ```
|
||||
*/
|
||||
function affine(strings: TemplateStringsArray, ...values: any[]) {
|
||||
// Merge template strings and values
|
||||
let htmlString = '';
|
||||
strings.forEach((str, i) => {
|
||||
htmlString += str;
|
||||
if (i < values.length) {
|
||||
htmlString += values[i];
|
||||
}
|
||||
});
|
||||
|
||||
// Create a new doc
|
||||
const workspace = new TestWorkspace({});
|
||||
workspace.meta.initialize();
|
||||
const doc = workspace.createDoc('test-doc');
|
||||
const container = new Container();
|
||||
extensions.forEach(extension => {
|
||||
extension.setup(container);
|
||||
});
|
||||
const store = doc.getStore({ extensions, provider: container.provider() });
|
||||
let selectionInfo: SelectionInfo = {};
|
||||
|
||||
// Use DOMParser to parse HTML string
|
||||
doc.load(() => {
|
||||
const parser = new DOMParser();
|
||||
const dom = parser.parseFromString(htmlString.trim(), 'text/html');
|
||||
const root = dom.body.firstElementChild;
|
||||
|
||||
if (!root) {
|
||||
throw new Error('Template must contain a root element');
|
||||
}
|
||||
|
||||
buildDocFromElement(store, root, null, selectionInfo);
|
||||
});
|
||||
|
||||
// Create host object
|
||||
const host = createTestHost(store);
|
||||
|
||||
// Set selection if needed
|
||||
if (selectionInfo.anchorBlockId && selectionInfo.focusBlockId) {
|
||||
const anchorBlock = store.getBlock(selectionInfo.anchorBlockId);
|
||||
const anchorTextLength = anchorBlock?.model?.text?.length ?? 0;
|
||||
const focusOffset = selectionInfo.focusOffset ?? 0;
|
||||
const anchorOffset = selectionInfo.anchorOffset ?? 0;
|
||||
|
||||
if (selectionInfo.anchorBlockId === selectionInfo.focusBlockId) {
|
||||
const selection = host.selection.create(TextSelection, {
|
||||
from: {
|
||||
blockId: selectionInfo.anchorBlockId,
|
||||
index: anchorOffset,
|
||||
length: focusOffset,
|
||||
},
|
||||
to: null,
|
||||
});
|
||||
host.selection.setGroup('note', [selection]);
|
||||
} else {
|
||||
const selection = host.selection.create(TextSelection, {
|
||||
from: {
|
||||
blockId: selectionInfo.anchorBlockId,
|
||||
index: anchorOffset,
|
||||
length: anchorTextLength - anchorOffset,
|
||||
},
|
||||
to: {
|
||||
blockId: selectionInfo.focusBlockId,
|
||||
index: 0,
|
||||
length: focusOffset,
|
||||
},
|
||||
});
|
||||
host.selection.setGroup('note', [selection]);
|
||||
}
|
||||
} else if (selectionInfo.cursorBlockId) {
|
||||
const selection = host.selection.create(TextSelection, {
|
||||
from: {
|
||||
blockId: selectionInfo.cursorBlockId,
|
||||
index: selectionInfo.cursorOffset ?? 0,
|
||||
length: 0,
|
||||
},
|
||||
to: null,
|
||||
});
|
||||
host.selection.setGroup('note', [selection]);
|
||||
}
|
||||
|
||||
return host;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a single block from template string
|
||||
*
|
||||
* Example:
|
||||
* ```
|
||||
* const block = block`<affine-note />`
|
||||
* ```
|
||||
*/
|
||||
function block(
|
||||
strings: TemplateStringsArray,
|
||||
...values: any[]
|
||||
): Block | null {
|
||||
// Merge template strings and values
|
||||
let htmlString = '';
|
||||
strings.forEach((str, i) => {
|
||||
htmlString += str;
|
||||
if (i < values.length) {
|
||||
htmlString += values[i];
|
||||
}
|
||||
});
|
||||
|
||||
// Create a temporary doc to hold the block
|
||||
const workspace = new TestWorkspace({});
|
||||
workspace.meta.initialize();
|
||||
const doc = workspace.createDoc('temp-doc');
|
||||
const store = doc.getStore({ extensions });
|
||||
|
||||
let blockId: string | null = null;
|
||||
const selectionInfo: SelectionInfo = {};
|
||||
|
||||
// Use DOMParser to parse HTML string
|
||||
doc.load(() => {
|
||||
const parser = new DOMParser();
|
||||
const dom = parser.parseFromString(htmlString.trim(), 'text/html');
|
||||
const root = dom.body.firstElementChild;
|
||||
|
||||
if (!root) {
|
||||
throw new Error('Template must contain a root element');
|
||||
}
|
||||
|
||||
// Create a root block if needed
|
||||
const flavour = tagToFlavour[root.tagName.toLowerCase()];
|
||||
if (
|
||||
flavour === 'affine:paragraph' ||
|
||||
flavour === 'affine:list' ||
|
||||
flavour === 'affine:code'
|
||||
) {
|
||||
const pageId = store.addBlock('affine:page', {});
|
||||
const noteId = store.addBlock('affine:note', {}, pageId);
|
||||
blockId = buildDocFromElement(store, root, noteId, selectionInfo);
|
||||
} else {
|
||||
blockId = buildDocFromElement(store, root, null, selectionInfo);
|
||||
}
|
||||
});
|
||||
|
||||
// Return the created block
|
||||
return blockId ? (store.getBlock(blockId) ?? null) : null;
|
||||
}
|
||||
|
||||
return {
|
||||
affine,
|
||||
block,
|
||||
};
|
||||
}
|
||||
|
||||
export const { affine, block } = createAffineTemplate();
|
||||
|
||||
/**
|
||||
* Recursively build document structure
|
||||
* @param doc
|
||||
* @param element
|
||||
* @param parentId
|
||||
* @param selectionInfo
|
||||
* @returns
|
||||
*/
|
||||
function buildDocFromElement(
|
||||
doc: Store,
|
||||
element: Element,
|
||||
parentId: string | null,
|
||||
selectionInfo: SelectionInfo
|
||||
): string {
|
||||
const tagName = element.tagName.toLowerCase();
|
||||
|
||||
// Handle selection tags
|
||||
if (tagName === 'anchor') {
|
||||
if (!parentId) return '';
|
||||
const parentBlock = doc.getBlock(parentId);
|
||||
if (parentBlock) {
|
||||
const textBeforeCursor = element.previousSibling?.textContent ?? '';
|
||||
selectionInfo.anchorBlockId = parentId;
|
||||
selectionInfo.anchorOffset = textBeforeCursor.length;
|
||||
}
|
||||
return parentId;
|
||||
} else if (tagName === 'focus') {
|
||||
if (!parentId) return '';
|
||||
const parentBlock = doc.getBlock(parentId);
|
||||
if (parentBlock) {
|
||||
const textBeforeCursor = element.previousSibling?.textContent ?? '';
|
||||
selectionInfo.focusBlockId = parentId;
|
||||
selectionInfo.focusOffset = textBeforeCursor.length;
|
||||
}
|
||||
return parentId;
|
||||
} else if (tagName === 'cursor') {
|
||||
if (!parentId) return '';
|
||||
const parentBlock = doc.getBlock(parentId);
|
||||
if (parentBlock) {
|
||||
const textBeforeCursor = element.previousSibling?.textContent ?? '';
|
||||
selectionInfo.cursorBlockId = parentId;
|
||||
selectionInfo.cursorOffset = textBeforeCursor.length;
|
||||
}
|
||||
return parentId;
|
||||
}
|
||||
|
||||
const flavour = tagToFlavour[tagName];
|
||||
|
||||
if (!flavour) {
|
||||
throw new Error(`Unknown tag name: ${tagName}`);
|
||||
}
|
||||
|
||||
const props: Record<string, any> = {};
|
||||
|
||||
const customId = element.getAttribute('id');
|
||||
|
||||
// If ID is specified, add it to props
|
||||
if (customId) {
|
||||
props.id = customId;
|
||||
}
|
||||
|
||||
// Process element attributes
|
||||
Array.from(element.attributes).forEach(attr => {
|
||||
if (attr.name !== 'id') {
|
||||
// Skip id attribute, we already handled it
|
||||
props[attr.name] = attr.value;
|
||||
}
|
||||
});
|
||||
|
||||
// Special handling for different block types based on their flavours
|
||||
switch (flavour) {
|
||||
case 'affine:paragraph':
|
||||
case 'affine:list':
|
||||
if (element.textContent) {
|
||||
props.text = new Text(element.textContent);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Create block
|
||||
const blockId = doc.addBlock(flavour, props, parentId);
|
||||
|
||||
// Process all child nodes, including text nodes
|
||||
Array.from(element.children).forEach(child => {
|
||||
if (child.nodeType === Node.ELEMENT_NODE) {
|
||||
// Handle element nodes
|
||||
buildDocFromElement(doc, child as Element, blockId, selectionInfo);
|
||||
} else if (child.nodeType === Node.TEXT_NODE) {
|
||||
// Handle text nodes
|
||||
console.log('buildDocFromElement text node:', child.textContent);
|
||||
}
|
||||
});
|
||||
|
||||
return blockId;
|
||||
}
|
||||
+2
-4
@@ -63,10 +63,8 @@ function compareBlocks(
|
||||
if (JSON.stringify(actualProps) !== JSON.stringify(expectedProps))
|
||||
return false;
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/prefer-for-of
|
||||
for (let i = 0; i < actual.children.length; i++) {
|
||||
if (!compareBlocks(actual.children[i], expected.children[i], compareId))
|
||||
return false;
|
||||
for (const [i, child] of actual.children.entries()) {
|
||||
if (!compareBlocks(child, expected.children[i], compareId)) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
+1
-1
@@ -240,7 +240,7 @@ export function createTestHost(doc: Store): EditorHost {
|
||||
std.selection = new MockSelectionStore();
|
||||
|
||||
std.command = new CommandManager(std as any);
|
||||
// @ts-expect-error
|
||||
// @ts-expect-error dev-only
|
||||
host.command = std.command;
|
||||
host.selection = std.selection;
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
export * from './affine-template';
|
||||
export * from './affine-test-utils';
|
||||
export * from './create-test-host';
|
||||
@@ -27,7 +27,7 @@
|
||||
"@floating-ui/dom": "^1.6.13",
|
||||
"@lit/context": "^1.1.2",
|
||||
"@preact/signals-core": "^1.8.0",
|
||||
"@toeverything/theme": "^1.1.15",
|
||||
"@toeverything/theme": "^1.1.16",
|
||||
"@types/lodash-es": "^4.17.12",
|
||||
"lit": "^3.2.0",
|
||||
"lodash-es": "^4.17.21",
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
"@blocksuite/icons": "^2.2.12",
|
||||
"@blocksuite/std": "workspace:*",
|
||||
"@preact/signals-core": "^1.8.0",
|
||||
"@toeverything/theme": "^1.1.15",
|
||||
"@toeverything/theme": "^1.1.16",
|
||||
"lit": "^3.2.0",
|
||||
"rxjs": "^7.8.1"
|
||||
},
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
"@blocksuite/std": "workspace:*",
|
||||
"@lit/context": "^1.1.2",
|
||||
"@preact/signals-core": "^1.8.0",
|
||||
"@toeverything/theme": "^1.1.15",
|
||||
"@toeverything/theme": "^1.1.16",
|
||||
"lit": "^3.2.0",
|
||||
"rxjs": "^7.8.1",
|
||||
"yjs": "^13.6.21"
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
"@blocksuite/std": "workspace:*",
|
||||
"@lit/context": "^1.1.2",
|
||||
"@preact/signals-core": "^1.8.0",
|
||||
"@toeverything/theme": "^1.1.15",
|
||||
"@toeverything/theme": "^1.1.16",
|
||||
"lit": "^3.2.0",
|
||||
"rxjs": "^7.8.1",
|
||||
"yjs": "^13.6.21"
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
"@floating-ui/dom": "^1.6.13",
|
||||
"@lit/context": "^1.1.2",
|
||||
"@preact/signals-core": "^1.8.0",
|
||||
"@toeverything/theme": "^1.1.15",
|
||||
"@toeverything/theme": "^1.1.16",
|
||||
"@types/lodash-es": "^4.17.12",
|
||||
"lit": "^3.2.0",
|
||||
"lodash-es": "^4.17.21",
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
"@blocksuite/std": "workspace:*",
|
||||
"@floating-ui/dom": "^1.6.13",
|
||||
"@preact/signals-core": "^1.8.0",
|
||||
"@toeverything/theme": "^1.1.15",
|
||||
"@toeverything/theme": "^1.1.16",
|
||||
"@types/lodash-es": "^4.17.12",
|
||||
"lit": "^3.2.0",
|
||||
"lodash-es": "^4.17.21",
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
"@blocksuite/std": "workspace:*",
|
||||
"@lit/context": "^1.1.2",
|
||||
"@preact/signals-core": "^1.8.0",
|
||||
"@toeverything/theme": "^1.1.15",
|
||||
"@toeverything/theme": "^1.1.16",
|
||||
"lit": "^3.2.0",
|
||||
"rxjs": "^7.8.1"
|
||||
},
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
"@blocksuite/store": "workspace:*",
|
||||
"@lit/context": "^1.1.2",
|
||||
"@preact/signals-core": "^1.8.0",
|
||||
"@toeverything/theme": "^1.1.15",
|
||||
"@toeverything/theme": "^1.1.16",
|
||||
"@types/lodash-es": "^4.17.12",
|
||||
"fflate": "^0.8.2",
|
||||
"lit": "^3.2.0",
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
"@blocksuite/store": "workspace:*",
|
||||
"@lit/context": "^1.1.2",
|
||||
"@preact/signals-core": "^1.8.0",
|
||||
"@toeverything/theme": "^1.1.15",
|
||||
"@toeverything/theme": "^1.1.16",
|
||||
"@types/lodash-es": "^4.17.12",
|
||||
"fflate": "^0.8.2",
|
||||
"lit": "^3.2.0",
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
"@blocksuite/std": "workspace:*",
|
||||
"@lit/context": "^1.1.2",
|
||||
"@preact/signals-core": "^1.8.0",
|
||||
"@toeverything/theme": "^1.1.15",
|
||||
"@toeverything/theme": "^1.1.16",
|
||||
"lit": "^3.2.0",
|
||||
"rxjs": "^7.8.1",
|
||||
"yjs": "^13.6.21"
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
"@blocksuite/store": "workspace:*",
|
||||
"@lit/context": "^1.1.2",
|
||||
"@preact/signals-core": "^1.8.0",
|
||||
"@toeverything/theme": "^1.1.15",
|
||||
"@toeverything/theme": "^1.1.16",
|
||||
"@types/lodash-es": "^4.17.12",
|
||||
"fflate": "^0.8.2",
|
||||
"lit": "^3.2.0",
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
"@blocksuite/icons": "^2.2.12",
|
||||
"@blocksuite/std": "workspace:*",
|
||||
"@preact/signals-core": "^1.8.0",
|
||||
"@toeverything/theme": "^1.1.15",
|
||||
"@toeverything/theme": "^1.1.16",
|
||||
"@types/lodash-es": "^4.17.12",
|
||||
"lit": "^3.2.0",
|
||||
"lodash-es": "^4.17.21",
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
"@blocksuite/global": "workspace:*",
|
||||
"@blocksuite/std": "workspace:*",
|
||||
"@preact/signals-core": "^1.8.0",
|
||||
"@toeverything/theme": "^1.1.15",
|
||||
"@toeverything/theme": "^1.1.16",
|
||||
"lit": "^3.2.0",
|
||||
"rxjs": "^7.8.1"
|
||||
},
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
"@blocksuite/store": "workspace:*",
|
||||
"@floating-ui/dom": "^1.6.13",
|
||||
"@preact/signals-core": "^1.8.0",
|
||||
"@toeverything/theme": "^1.1.15",
|
||||
"@toeverything/theme": "^1.1.16",
|
||||
"@types/lodash-es": "^4.17.12",
|
||||
"lit": "^3.2.0",
|
||||
"lodash-es": "^4.17.21",
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
"@blocksuite/std": "workspace:*",
|
||||
"@floating-ui/dom": "^1.6.13",
|
||||
"@preact/signals-core": "^1.8.0",
|
||||
"@toeverything/theme": "^1.1.15",
|
||||
"@toeverything/theme": "^1.1.16",
|
||||
"@types/lodash-es": "^4.17.12",
|
||||
"lit": "^3.2.0",
|
||||
"lodash-es": "^4.17.21",
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
"@blocksuite/std": "workspace:*",
|
||||
"@floating-ui/dom": "^1.6.13",
|
||||
"@preact/signals-core": "^1.8.0",
|
||||
"@toeverything/theme": "^1.1.15",
|
||||
"@toeverything/theme": "^1.1.16",
|
||||
"@types/lodash-es": "^4.17.12",
|
||||
"lit": "^3.2.0",
|
||||
"lodash-es": "^4.17.21",
|
||||
|
||||
@@ -28,6 +28,21 @@ import { ShadowlessElement } from './shadowless-element.js';
|
||||
export const storeContext = createContext<Store>('store');
|
||||
export const stdContext = createContext<BlockStdScope>('std');
|
||||
|
||||
function isMatchFlavour(widgetFlavour: string, block: BlockModel) {
|
||||
if (widgetFlavour.endsWith('/*')) {
|
||||
const path = widgetFlavour.slice(0, -2).split('/');
|
||||
let current: BlockModel | null = block.parent;
|
||||
for (let i = path.length - 1; i >= 0; i--) {
|
||||
if (!current || current.flavour !== path[i]) {
|
||||
return false;
|
||||
}
|
||||
current = current.parent;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return block.flavour === widgetFlavour;
|
||||
}
|
||||
|
||||
@requiredProperties({
|
||||
store: PropTypes.instanceOf(Store),
|
||||
std: PropTypes.object,
|
||||
@@ -61,7 +76,7 @@ export class EditorHost extends SignalWatcher(
|
||||
const widgets = Array.from(widgetViews.entries()).reduce(
|
||||
(mapping, [key, tag]) => {
|
||||
const [widgetFlavour, id] = key.split('|');
|
||||
if (widgetFlavour === flavour) {
|
||||
if (isMatchFlavour(widgetFlavour, model)) {
|
||||
const template = html`<${tag} ${unsafeStatic(WIDGET_ID_ATTR)}=${id}></${tag}>`;
|
||||
mapping[id] = template;
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
"@lit/context": "^1.1.3",
|
||||
"@lottiefiles/dotlottie-wc": "^0.5.0",
|
||||
"@preact/signals-core": "^1.8.0",
|
||||
"@toeverything/theme": "^1.1.15",
|
||||
"@toeverything/theme": "^1.1.16",
|
||||
"@vanilla-extract/css": "^1.17.0",
|
||||
"lit": "^3.2.0",
|
||||
"rxjs": "^7.8.1",
|
||||
|
||||
-10
@@ -1,10 +0,0 @@
|
||||
-- AlterEnum
|
||||
-- This migration adds more than one value to an enum.
|
||||
-- With PostgreSQL versions 11 and earlier, this is not possible
|
||||
-- in a single migration. This can be worked around by creating
|
||||
-- multiple migrations, each migration adding only one value to
|
||||
-- the enum.
|
||||
|
||||
|
||||
ALTER TYPE "NotificationType" ADD VALUE 'Comment';
|
||||
ALTER TYPE "NotificationType" ADD VALUE 'CommentMention';
|
||||
@@ -822,8 +822,6 @@ enum NotificationType {
|
||||
InvitationReviewRequest
|
||||
InvitationReviewApproved
|
||||
InvitationReviewDeclined
|
||||
Comment
|
||||
CommentMention
|
||||
}
|
||||
|
||||
enum NotificationLevel {
|
||||
|
||||
@@ -16,7 +16,6 @@ Generated by [AVA](https://avajs.dev).
|
||||
role: 'assistant',
|
||||
},
|
||||
],
|
||||
pinned: false,
|
||||
tokens: 8,
|
||||
},
|
||||
]
|
||||
@@ -31,7 +30,6 @@ Generated by [AVA](https://avajs.dev).
|
||||
role: 'assistant',
|
||||
},
|
||||
],
|
||||
pinned: false,
|
||||
tokens: 8,
|
||||
},
|
||||
]
|
||||
|
||||
Binary file not shown.
@@ -1513,179 +1513,6 @@ Generated by [AVA](https://avajs.dev).
|
||||
<!--/$-->␊
|
||||
`
|
||||
|
||||
> test@test.com commented on Test Doc
|
||||
|
||||
`<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">␊
|
||||
<!--$-->␊
|
||||
<table␊
|
||||
align="center"␊
|
||||
width="100%"␊
|
||||
border="0"␊
|
||||
cellpadding="0"␊
|
||||
cellspacing="0"␊
|
||||
role="presentation">␊
|
||||
<tbody>␊
|
||||
<tr>␊
|
||||
<td>␊
|
||||
<p␊
|
||||
style="font-size:20px;line-height:28px;font-weight:600;font-family:Inter, Arial, Helvetica, sans-serif;margin-top:24px;margin-bottom:0;color:#141414">␊
|
||||
You have a new comment␊
|
||||
</p>␊
|
||||
</td>␊
|
||||
</tr>␊
|
||||
</tbody>␊
|
||||
</table>␊
|
||||
<table␊
|
||||
align="center"␊
|
||||
width="100%"␊
|
||||
border="0"␊
|
||||
cellpadding="0"␊
|
||||
cellspacing="0"␊
|
||||
role="presentation">␊
|
||||
<tbody>␊
|
||||
<tr>␊
|
||||
<td>␊
|
||||
<table␊
|
||||
align="center"␊
|
||||
width="100%"␊
|
||||
border="0"␊
|
||||
cellpadding="0"␊
|
||||
cellspacing="0"␊
|
||||
role="presentation">␊
|
||||
<tbody style="width:100%">␊
|
||||
<tr style="width:100%">␊
|
||||
<p␊
|
||||
style="font-size:15px;line-height:24px;font-weight:400;font-family:Inter, Arial, Helvetica, sans-serif;margin-top:24px;margin-bottom:0;color:#141414">␊
|
||||
<span style="font-weight:600">test@test.com</span> commented on␊
|
||||
<a␊
|
||||
href="https://app.affine.pro"␊
|
||||
style="color:#067df7;text-decoration-line:none"␊
|
||||
target="_blank"␊
|
||||
><span style="font-weight:600">Test Doc</span></a␊
|
||||
>.␊
|
||||
</p>␊
|
||||
</tr>␊
|
||||
</tbody>␊
|
||||
</table>␊
|
||||
<table␊
|
||||
align="center"␊
|
||||
width="100%"␊
|
||||
border="0"␊
|
||||
cellpadding="0"␊
|
||||
cellspacing="0"␊
|
||||
role="presentation">␊
|
||||
<tbody style="width:100%">␊
|
||||
<tr style="width:100%">␊
|
||||
<a␊
|
||||
href="https://app.affine.pro"␊
|
||||
style="line-height:24px;text-decoration:none;display:inline-block;max-width:100%;mso-padding-alt:0px;font-size:15px;font-weight:600;font-family:Inter, Arial, Helvetica, sans-serif;margin-top:24px;margin-bottom:0;color:#FFFFFF;background-color:#1E96EB;padding:8px 18px 8px 18px;border-radius:8px;border:1px solid rgba(0,0,0,.1);margin-right:4px"␊
|
||||
target="_blank"␊
|
||||
><span␊
|
||||
><!--[if mso]><i style="mso-font-width:450%;mso-text-raise:12" hidden>  </i><![endif]--></span␊
|
||||
><span␊
|
||||
style="max-width:100%;display:inline-block;line-height:120%;mso-padding-alt:0px;mso-text-raise:6px"␊
|
||||
>View Comment</span␊
|
||||
><span␊
|
||||
><!--[if mso]><i style="mso-font-width:450%" hidden>  ​</i><![endif]--></span␊
|
||||
></a␊
|
||||
>␊
|
||||
</tr>␊
|
||||
</tbody>␊
|
||||
</table>␊
|
||||
</td>␊
|
||||
</tr>␊
|
||||
</tbody>␊
|
||||
</table>␊
|
||||
<!--/$-->␊
|
||||
`
|
||||
|
||||
> test@test.com mentioned you in a comment on Test Doc
|
||||
|
||||
`<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">␊
|
||||
<!--$-->␊
|
||||
<table␊
|
||||
align="center"␊
|
||||
width="100%"␊
|
||||
border="0"␊
|
||||
cellpadding="0"␊
|
||||
cellspacing="0"␊
|
||||
role="presentation">␊
|
||||
<tbody>␊
|
||||
<tr>␊
|
||||
<td>␊
|
||||
<p␊
|
||||
style="font-size:20px;line-height:28px;font-weight:600;font-family:Inter, Arial, Helvetica, sans-serif;margin-top:24px;margin-bottom:0;color:#141414">␊
|
||||
You are mentioned in a comment␊
|
||||
</p>␊
|
||||
</td>␊
|
||||
</tr>␊
|
||||
</tbody>␊
|
||||
</table>␊
|
||||
<table␊
|
||||
align="center"␊
|
||||
width="100%"␊
|
||||
border="0"␊
|
||||
cellpadding="0"␊
|
||||
cellspacing="0"␊
|
||||
role="presentation">␊
|
||||
<tbody>␊
|
||||
<tr>␊
|
||||
<td>␊
|
||||
<table␊
|
||||
align="center"␊
|
||||
width="100%"␊
|
||||
border="0"␊
|
||||
cellpadding="0"␊
|
||||
cellspacing="0"␊
|
||||
role="presentation">␊
|
||||
<tbody style="width:100%">␊
|
||||
<tr style="width:100%">␊
|
||||
<p␊
|
||||
style="font-size:15px;line-height:24px;font-weight:400;font-family:Inter, Arial, Helvetica, sans-serif;margin-top:24px;margin-bottom:0;color:#141414">␊
|
||||
<span style="font-weight:600">test@test.com</span> mentioned you␊
|
||||
in a comment on␊
|
||||
<a␊
|
||||
href="https://app.affine.pro"␊
|
||||
style="color:#067df7;text-decoration-line:none"␊
|
||||
target="_blank"␊
|
||||
><span style="font-weight:600">Test Doc</span></a␊
|
||||
>.␊
|
||||
</p>␊
|
||||
</tr>␊
|
||||
</tbody>␊
|
||||
</table>␊
|
||||
<table␊
|
||||
align="center"␊
|
||||
width="100%"␊
|
||||
border="0"␊
|
||||
cellpadding="0"␊
|
||||
cellspacing="0"␊
|
||||
role="presentation">␊
|
||||
<tbody style="width:100%">␊
|
||||
<tr style="width:100%">␊
|
||||
<a␊
|
||||
href="https://app.affine.pro"␊
|
||||
style="line-height:24px;text-decoration:none;display:inline-block;max-width:100%;mso-padding-alt:0px;font-size:15px;font-weight:600;font-family:Inter, Arial, Helvetica, sans-serif;margin-top:24px;margin-bottom:0;color:#FFFFFF;background-color:#1E96EB;padding:8px 18px 8px 18px;border-radius:8px;border:1px solid rgba(0,0,0,.1);margin-right:4px"␊
|
||||
target="_blank"␊
|
||||
><span␊
|
||||
><!--[if mso]><i style="mso-font-width:450%;mso-text-raise:12" hidden>  </i><![endif]--></span␊
|
||||
><span␊
|
||||
style="max-width:100%;display:inline-block;line-height:120%;mso-padding-alt:0px;mso-text-raise:6px"␊
|
||||
>View Comment</span␊
|
||||
><span␊
|
||||
><!--[if mso]><i style="mso-font-width:450%" hidden>  ​</i><![endif]--></span␊
|
||||
></a␊
|
||||
>␊
|
||||
</tr>␊
|
||||
</tbody>␊
|
||||
</table>␊
|
||||
</td>␊
|
||||
</tr>␊
|
||||
</tbody>␊
|
||||
</table>␊
|
||||
<!--/$-->␊
|
||||
`
|
||||
|
||||
> Your workspace has been upgraded to team workspace! 🎉
|
||||
|
||||
`<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">␊
|
||||
|
||||
Binary file not shown.
@@ -53,10 +53,7 @@ import {
|
||||
createWorkspaceCopilotSession,
|
||||
forkCopilotSession,
|
||||
getCopilotSession,
|
||||
getDocSessions,
|
||||
getHistories,
|
||||
getPinnedSessions,
|
||||
getWorkspaceSessions,
|
||||
listContext,
|
||||
listContextDocAndFiles,
|
||||
matchFiles,
|
||||
@@ -1143,94 +1140,31 @@ test('should list histories for different session types correctly', async t => {
|
||||
]);
|
||||
|
||||
const testHistoryQuery = async (
|
||||
queryFn: () => Promise<any[]>,
|
||||
opts: {
|
||||
sessionIds?: string[];
|
||||
sessionId?: string;
|
||||
pinned?: boolean;
|
||||
isEmpty?: boolean;
|
||||
},
|
||||
queryDocId: string | undefined,
|
||||
expectedSessionId: string,
|
||||
description: string
|
||||
) => {
|
||||
const s = await queryFn();
|
||||
|
||||
if (opts.isEmpty) {
|
||||
t.is(s.length, 0, `should return ${description}`);
|
||||
return;
|
||||
}
|
||||
|
||||
if (opts.sessionIds) {
|
||||
t.is(s.length, opts.sessionIds.length, `should return ${description}`);
|
||||
const ids = s.map(h => h.sessionId).sort((a, b) => a.localeCompare(b));
|
||||
const expectedIds = opts.sessionIds.sort((a, b) => a.localeCompare(b));
|
||||
t.deepEqual(ids, expectedIds, `should return correct ${description}`);
|
||||
} else if (opts.sessionId) {
|
||||
t.is(s.length, 1, `should return ${description}`);
|
||||
t.is(
|
||||
s[0].sessionId,
|
||||
opts.sessionId,
|
||||
`should return correct ${description}`
|
||||
);
|
||||
if (opts.pinned !== undefined) {
|
||||
t.is(s[0].pinned, opts.pinned, `pinned status for ${description}`);
|
||||
}
|
||||
}
|
||||
const histories = await getHistories(app, {
|
||||
workspaceId,
|
||||
docId: queryDocId,
|
||||
});
|
||||
t.is(histories.length, 1, `should return ${description}`);
|
||||
t.is(
|
||||
histories[0].sessionId,
|
||||
expectedSessionId,
|
||||
`should return correct ${description}`
|
||||
);
|
||||
};
|
||||
|
||||
// test for getHistories
|
||||
await testHistoryQuery(
|
||||
() => getHistories(app, { workspaceId, docId: null }),
|
||||
{ sessionId: workspaceSessionId },
|
||||
undefined,
|
||||
workspaceSessionId,
|
||||
'workspace session history'
|
||||
);
|
||||
await testHistoryQuery(
|
||||
() => getHistories(app, { workspaceId, docId: pinnedDocId }),
|
||||
{ sessionId: pinnedSessionId },
|
||||
pinnedDocId,
|
||||
pinnedSessionId,
|
||||
'pinned session history'
|
||||
);
|
||||
await testHistoryQuery(
|
||||
() => getHistories(app, { workspaceId, docId }),
|
||||
{ sessionId: docSessionId },
|
||||
'doc session history'
|
||||
);
|
||||
|
||||
// test for getWorkspaceSessions
|
||||
await testHistoryQuery(
|
||||
() => getWorkspaceSessions(app, { workspaceId }),
|
||||
{ sessionId: workspaceSessionId, pinned: false },
|
||||
'workspace-level sessions'
|
||||
);
|
||||
|
||||
// test for getDocSessions
|
||||
await testHistoryQuery(
|
||||
() =>
|
||||
getDocSessions(app, { workspaceId, docId, options: { pinned: false } }),
|
||||
{ sessionId: docSessionId, pinned: false },
|
||||
'doc sessions'
|
||||
);
|
||||
|
||||
await testHistoryQuery(
|
||||
() => getDocSessions(app, { workspaceId, docId: pinnedDocId }),
|
||||
{ sessionId: pinnedSessionId, pinned: true },
|
||||
'pinned doc sessions'
|
||||
);
|
||||
|
||||
// test for getPinnedSessions
|
||||
await testHistoryQuery(
|
||||
() => getPinnedSessions(app, { workspaceId }),
|
||||
{ sessionId: pinnedSessionId, pinned: true },
|
||||
'pinned sessions'
|
||||
);
|
||||
|
||||
await testHistoryQuery(
|
||||
() => getPinnedSessions(app, { workspaceId, docId: pinnedDocId }),
|
||||
{ sessionId: pinnedSessionId, pinned: true },
|
||||
'pinned session for specific doc'
|
||||
);
|
||||
|
||||
await testHistoryQuery(
|
||||
() => getPinnedSessions(app, { workspaceId, docId }),
|
||||
{ isEmpty: true },
|
||||
'no pinned sessions for non-pinned doc'
|
||||
);
|
||||
await testHistoryQuery(docId, docSessionId, 'doc session history');
|
||||
});
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -231,7 +231,7 @@ export async function createApp(
|
||||
app.useBodyParser('raw', { limit: 1 * OneMB });
|
||||
app.use(
|
||||
graphqlUploadExpress({
|
||||
maxFileSize: 100 * OneMB,
|
||||
maxFileSize: 10 * OneMB,
|
||||
maxFiles: 5,
|
||||
})
|
||||
);
|
||||
|
||||
+108
-88
@@ -262,53 +262,16 @@ Generated by [AVA](https://avajs.dev).
|
||||
|
||||
{
|
||||
all_workspace_sessions: {
|
||||
count: 7,
|
||||
count: 2,
|
||||
sessionTypes: [
|
||||
{
|
||||
hasMessages: false,
|
||||
isAction: false,
|
||||
isFork: true,
|
||||
messageCount: 0,
|
||||
type: 'doc',
|
||||
},
|
||||
{
|
||||
hasMessages: false,
|
||||
isAction: false,
|
||||
isFork: false,
|
||||
messageCount: 0,
|
||||
type: 'doc',
|
||||
},
|
||||
{
|
||||
hasMessages: false,
|
||||
isAction: false,
|
||||
isFork: false,
|
||||
messageCount: 0,
|
||||
type: 'doc',
|
||||
},
|
||||
{
|
||||
hasMessages: false,
|
||||
isAction: true,
|
||||
isFork: false,
|
||||
messageCount: 0,
|
||||
type: 'doc',
|
||||
},
|
||||
{
|
||||
hasMessages: false,
|
||||
isAction: false,
|
||||
isFork: false,
|
||||
messageCount: 0,
|
||||
type: 'doc',
|
||||
},
|
||||
{
|
||||
hasMessages: false,
|
||||
isAction: false,
|
||||
isFork: false,
|
||||
messageCount: 0,
|
||||
type: 'pinned',
|
||||
},
|
||||
{
|
||||
hasMessages: false,
|
||||
isAction: false,
|
||||
isFork: false,
|
||||
messageCount: 0,
|
||||
type: 'workspace',
|
||||
@@ -320,35 +283,30 @@ Generated by [AVA](https://avajs.dev).
|
||||
sessionTypes: [
|
||||
{
|
||||
hasMessages: false,
|
||||
isAction: false,
|
||||
isFork: true,
|
||||
messageCount: 0,
|
||||
type: 'doc',
|
||||
},
|
||||
{
|
||||
hasMessages: true,
|
||||
isAction: false,
|
||||
isFork: false,
|
||||
messageCount: 1,
|
||||
type: 'doc',
|
||||
},
|
||||
{
|
||||
hasMessages: true,
|
||||
isAction: false,
|
||||
isFork: false,
|
||||
messageCount: 1,
|
||||
type: 'doc',
|
||||
},
|
||||
{
|
||||
hasMessages: false,
|
||||
isAction: true,
|
||||
isFork: false,
|
||||
messageCount: 0,
|
||||
type: 'doc',
|
||||
},
|
||||
{
|
||||
hasMessages: true,
|
||||
isAction: false,
|
||||
isFork: false,
|
||||
messageCount: 1,
|
||||
type: 'doc',
|
||||
@@ -360,7 +318,6 @@ Generated by [AVA](https://avajs.dev).
|
||||
sessionTypes: [
|
||||
{
|
||||
hasMessages: false,
|
||||
isAction: false,
|
||||
isFork: false,
|
||||
messageCount: 0,
|
||||
type: 'doc',
|
||||
@@ -368,39 +325,28 @@ Generated by [AVA](https://avajs.dev).
|
||||
],
|
||||
},
|
||||
non_action_sessions: {
|
||||
count: 5,
|
||||
count: 4,
|
||||
sessionTypes: [
|
||||
{
|
||||
hasMessages: false,
|
||||
isAction: false,
|
||||
isFork: true,
|
||||
messageCount: 0,
|
||||
type: 'doc',
|
||||
},
|
||||
{
|
||||
hasMessages: false,
|
||||
isAction: false,
|
||||
isFork: false,
|
||||
messageCount: 0,
|
||||
type: 'doc',
|
||||
},
|
||||
{
|
||||
hasMessages: false,
|
||||
isAction: false,
|
||||
isFork: false,
|
||||
messageCount: 0,
|
||||
type: 'doc',
|
||||
},
|
||||
{
|
||||
hasMessages: false,
|
||||
isAction: true,
|
||||
isFork: false,
|
||||
messageCount: 0,
|
||||
type: 'doc',
|
||||
},
|
||||
{
|
||||
hasMessages: false,
|
||||
isAction: false,
|
||||
isFork: false,
|
||||
messageCount: 0,
|
||||
type: 'doc',
|
||||
@@ -408,25 +354,28 @@ Generated by [AVA](https://avajs.dev).
|
||||
],
|
||||
},
|
||||
non_fork_sessions: {
|
||||
count: 3,
|
||||
count: 4,
|
||||
sessionTypes: [
|
||||
{
|
||||
hasMessages: false,
|
||||
isAction: false,
|
||||
isFork: false,
|
||||
messageCount: 0,
|
||||
type: 'doc',
|
||||
},
|
||||
{
|
||||
hasMessages: false,
|
||||
isAction: false,
|
||||
isFork: false,
|
||||
messageCount: 0,
|
||||
type: 'doc',
|
||||
},
|
||||
{
|
||||
hasMessages: false,
|
||||
isAction: false,
|
||||
isFork: false,
|
||||
messageCount: 0,
|
||||
type: 'doc',
|
||||
},
|
||||
{
|
||||
hasMessages: false,
|
||||
isFork: false,
|
||||
messageCount: 0,
|
||||
type: 'doc',
|
||||
@@ -434,44 +383,16 @@ Generated by [AVA](https://avajs.dev).
|
||||
],
|
||||
},
|
||||
recent_top3_sessions: {
|
||||
count: 3,
|
||||
sessionTypes: [
|
||||
{
|
||||
hasMessages: false,
|
||||
isAction: false,
|
||||
isFork: true,
|
||||
messageCount: 0,
|
||||
type: 'doc',
|
||||
},
|
||||
{
|
||||
hasMessages: false,
|
||||
isAction: false,
|
||||
isFork: false,
|
||||
messageCount: 0,
|
||||
type: 'doc',
|
||||
},
|
||||
{
|
||||
hasMessages: false,
|
||||
isAction: false,
|
||||
isFork: false,
|
||||
messageCount: 0,
|
||||
type: 'doc',
|
||||
},
|
||||
],
|
||||
},
|
||||
workspace_sessions_with_messages: {
|
||||
count: 2,
|
||||
sessionTypes: [
|
||||
{
|
||||
hasMessages: false,
|
||||
isAction: false,
|
||||
isFork: false,
|
||||
messageCount: 0,
|
||||
type: 'pinned',
|
||||
},
|
||||
{
|
||||
hasMessages: false,
|
||||
isAction: false,
|
||||
isFork: false,
|
||||
messageCount: 0,
|
||||
type: 'workspace',
|
||||
@@ -565,3 +486,102 @@ Generated by [AVA](https://avajs.dev).
|
||||
workspaceSessionExists: true,
|
||||
},
|
||||
}
|
||||
|
||||
## should handle session updates and validations
|
||||
|
||||
> should unpin existing when pinning new session
|
||||
|
||||
[
|
||||
{
|
||||
docId: null,
|
||||
id: 'session-update-id',
|
||||
pinned: true,
|
||||
},
|
||||
{
|
||||
docId: null,
|
||||
id: 'existing-pinned-session-id',
|
||||
pinned: false,
|
||||
},
|
||||
]
|
||||
|
||||
> session type conversion steps
|
||||
|
||||
[
|
||||
{
|
||||
session: {
|
||||
docId: 'doc-update-id',
|
||||
pinned: false,
|
||||
},
|
||||
step: 'workspace_to_doc',
|
||||
type: 'doc',
|
||||
},
|
||||
{
|
||||
session: {
|
||||
docId: null,
|
||||
pinned: false,
|
||||
},
|
||||
step: 'doc_to_workspace',
|
||||
type: 'workspace',
|
||||
},
|
||||
{
|
||||
session: {
|
||||
docId: null,
|
||||
pinned: true,
|
||||
},
|
||||
step: 'workspace_to_pinned',
|
||||
type: 'pinned',
|
||||
},
|
||||
]
|
||||
|
||||
## should create multiple doc sessions and query latest
|
||||
|
||||
> multiple doc sessions for same document with order verification
|
||||
|
||||
[
|
||||
{
|
||||
docId: 'multi-session-doc',
|
||||
hasMessages: true,
|
||||
isFirstSession: false,
|
||||
isSecondSession: false,
|
||||
isThirdSession: true,
|
||||
messageCount: 1,
|
||||
},
|
||||
{
|
||||
docId: 'multi-session-doc',
|
||||
hasMessages: true,
|
||||
isFirstSession: false,
|
||||
isSecondSession: true,
|
||||
isThirdSession: false,
|
||||
messageCount: 1,
|
||||
},
|
||||
{
|
||||
docId: 'multi-session-doc',
|
||||
hasMessages: true,
|
||||
isFirstSession: true,
|
||||
isSecondSession: false,
|
||||
isThirdSession: false,
|
||||
messageCount: 1,
|
||||
},
|
||||
]
|
||||
|
||||
## should query recent topK sessions of different types
|
||||
|
||||
> should include different session types in recent topK query
|
||||
|
||||
[
|
||||
{
|
||||
docId: null,
|
||||
pinned: false,
|
||||
type: 'workspace',
|
||||
},
|
||||
{
|
||||
docId: null,
|
||||
pinned: true,
|
||||
type: 'pinned',
|
||||
},
|
||||
{
|
||||
docId: null,
|
||||
pinned: false,
|
||||
type: 'workspace',
|
||||
},
|
||||
]
|
||||
|
||||
BIN
Binary file not shown.
@@ -169,7 +169,6 @@ test('should list and filter session type', async t => {
|
||||
const workspaceSessions = await copilotSession.list({
|
||||
userId: user.id,
|
||||
workspaceId: workspace.id,
|
||||
docId: null,
|
||||
});
|
||||
|
||||
t.snapshot(
|
||||
@@ -576,10 +575,6 @@ test('should handle session queries, ordering, and filtering', async t => {
|
||||
const docParams = { ...baseParams, docId };
|
||||
const queryTestCases = [
|
||||
{ name: 'all_workspace_sessions', params: baseParams },
|
||||
{
|
||||
name: 'workspace_sessions_with_messages',
|
||||
params: { ...baseParams, docId: null, withMessages: true },
|
||||
},
|
||||
{
|
||||
name: 'doc_sessions_with_messages',
|
||||
params: { ...docParams, withMessages: true },
|
||||
@@ -614,7 +609,6 @@ test('should handle session queries, ordering, and filtering', async t => {
|
||||
type: copilotSession.getSessionType(s),
|
||||
hasMessages: !!s.messages?.length,
|
||||
messageCount: s.messages?.length || 0,
|
||||
isAction: s.promptName === TEST_PROMPTS.ACTION,
|
||||
isFork: !!s.parentSessionId,
|
||||
})),
|
||||
};
|
||||
|
||||
@@ -709,30 +709,26 @@ type ChatMessage = {
|
||||
|
||||
type History = {
|
||||
sessionId: string;
|
||||
pinned: boolean;
|
||||
tokens: number;
|
||||
action: string | null;
|
||||
createdAt: string;
|
||||
messages: ChatMessage[];
|
||||
};
|
||||
|
||||
type HistoryOptions = {
|
||||
action?: boolean;
|
||||
fork?: boolean;
|
||||
pinned?: boolean;
|
||||
limit?: number;
|
||||
skip?: number;
|
||||
sessionOrder?: 'asc' | 'desc';
|
||||
messageOrder?: 'asc' | 'desc';
|
||||
sessionId?: string;
|
||||
};
|
||||
|
||||
export async function getHistories(
|
||||
app: TestingApp,
|
||||
variables: {
|
||||
workspaceId: string;
|
||||
docId?: string | null;
|
||||
options?: HistoryOptions;
|
||||
docId?: string;
|
||||
options?: {
|
||||
action?: boolean;
|
||||
fork?: boolean;
|
||||
limit?: number;
|
||||
skip?: number;
|
||||
sessionOrder?: 'asc' | 'desc';
|
||||
messageOrder?: 'asc' | 'desc';
|
||||
sessionId?: string;
|
||||
};
|
||||
}
|
||||
): Promise<History[]> {
|
||||
const res = await app.gql(
|
||||
@@ -746,7 +742,6 @@ export async function getHistories(
|
||||
copilot(workspaceId: $workspaceId) {
|
||||
histories(docId: $docId, options: $options) {
|
||||
sessionId
|
||||
pinned
|
||||
tokens
|
||||
action
|
||||
createdAt
|
||||
@@ -768,152 +763,6 @@ export async function getHistories(
|
||||
return res.currentUser?.copilot?.histories || [];
|
||||
}
|
||||
|
||||
export async function getWorkspaceSessions(
|
||||
app: TestingApp,
|
||||
variables: {
|
||||
workspaceId: string;
|
||||
options?: HistoryOptions;
|
||||
}
|
||||
): Promise<History[]> {
|
||||
const res = await app.gql(
|
||||
`query getCopilotWorkspaceSessions(
|
||||
$workspaceId: String!
|
||||
$options: QueryChatHistoriesInput
|
||||
) {
|
||||
currentUser {
|
||||
copilot(workspaceId: $workspaceId) {
|
||||
histories(docId: null, options: $options) {
|
||||
sessionId
|
||||
pinned
|
||||
tokens
|
||||
action
|
||||
createdAt
|
||||
messages {
|
||||
id
|
||||
role
|
||||
content
|
||||
streamObjects {
|
||||
type
|
||||
textDelta
|
||||
toolCallId
|
||||
toolName
|
||||
args
|
||||
result
|
||||
}
|
||||
attachments
|
||||
createdAt
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}`,
|
||||
variables
|
||||
);
|
||||
|
||||
return res.currentUser?.copilot?.histories || [];
|
||||
}
|
||||
|
||||
export async function getDocSessions(
|
||||
app: TestingApp,
|
||||
variables: {
|
||||
workspaceId: string;
|
||||
docId: string;
|
||||
options?: HistoryOptions;
|
||||
}
|
||||
): Promise<History[]> {
|
||||
const res = await app.gql(
|
||||
`query getCopilotDocSessions(
|
||||
$workspaceId: String!
|
||||
$docId: String!
|
||||
$options: QueryChatHistoriesInput
|
||||
) {
|
||||
currentUser {
|
||||
copilot(workspaceId: $workspaceId) {
|
||||
histories(docId: $docId, options: $options) {
|
||||
sessionId
|
||||
pinned
|
||||
tokens
|
||||
action
|
||||
createdAt
|
||||
messages {
|
||||
id
|
||||
role
|
||||
content
|
||||
streamObjects {
|
||||
type
|
||||
textDelta
|
||||
toolCallId
|
||||
toolName
|
||||
args
|
||||
result
|
||||
}
|
||||
attachments
|
||||
createdAt
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}`,
|
||||
variables
|
||||
);
|
||||
|
||||
return res.currentUser?.copilot?.histories || [];
|
||||
}
|
||||
|
||||
export async function getPinnedSessions(
|
||||
app: TestingApp,
|
||||
variables: {
|
||||
workspaceId: string;
|
||||
docId?: string;
|
||||
messageOrder?: 'asc' | 'desc';
|
||||
withPrompt?: boolean;
|
||||
}
|
||||
): Promise<History[]> {
|
||||
const res = await app.gql(
|
||||
`query getCopilotPinnedSessions(
|
||||
$workspaceId: String!
|
||||
$docId: String
|
||||
$messageOrder: ChatHistoryOrder
|
||||
$withPrompt: Boolean
|
||||
) {
|
||||
currentUser {
|
||||
copilot(workspaceId: $workspaceId) {
|
||||
histories(docId: $docId, options: {
|
||||
limit: 1,
|
||||
pinned: true,
|
||||
messageOrder: $messageOrder,
|
||||
withPrompt: $withPrompt
|
||||
}) {
|
||||
sessionId
|
||||
pinned
|
||||
tokens
|
||||
action
|
||||
createdAt
|
||||
messages {
|
||||
id
|
||||
role
|
||||
content
|
||||
streamObjects {
|
||||
type
|
||||
textDelta
|
||||
toolCallId
|
||||
toolName
|
||||
args
|
||||
result
|
||||
}
|
||||
attachments
|
||||
createdAt
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}`,
|
||||
variables
|
||||
);
|
||||
|
||||
return res.currentUser?.copilot?.histories || [];
|
||||
}
|
||||
|
||||
type Prompt = {
|
||||
name: string;
|
||||
model: string;
|
||||
|
||||
@@ -29,7 +29,6 @@ import { StorageProviderModule } from './base/storage';
|
||||
import { RateLimiterModule } from './base/throttler';
|
||||
import { WebSocketModule } from './base/websocket';
|
||||
import { AuthModule } from './core/auth';
|
||||
import { CommentModule } from './core/comment';
|
||||
import { ServerConfigModule, ServerConfigResolverModule } from './core/config';
|
||||
import { DocStorageModule } from './core/doc';
|
||||
import { DocRendererModule } from './core/doc-renderer';
|
||||
@@ -187,8 +186,7 @@ export function buildAppModule(env: Env) {
|
||||
CopilotModule,
|
||||
CaptchaModule,
|
||||
OAuthModule,
|
||||
CustomerIoModule,
|
||||
CommentModule
|
||||
CustomerIoModule
|
||||
)
|
||||
// doc service only
|
||||
.useIf(() => env.flavors.doc, DocServiceModule)
|
||||
|
||||
@@ -921,8 +921,4 @@ export const USER_FRIENDLY_ERRORS = {
|
||||
type: 'resource_not_found',
|
||||
message: 'Comment attachment not found.',
|
||||
},
|
||||
comment_attachment_quota_exceeded: {
|
||||
type: 'quota_exceeded',
|
||||
message: 'You have exceeded the comment attachment size quota.',
|
||||
},
|
||||
} satisfies Record<string, UserFriendlyErrorOptions>;
|
||||
|
||||
@@ -1085,12 +1085,6 @@ export class CommentAttachmentNotFound extends UserFriendlyError {
|
||||
super('resource_not_found', 'comment_attachment_not_found', message);
|
||||
}
|
||||
}
|
||||
|
||||
export class CommentAttachmentQuotaExceeded extends UserFriendlyError {
|
||||
constructor(message?: string) {
|
||||
super('quota_exceeded', 'comment_attachment_quota_exceeded', message);
|
||||
}
|
||||
}
|
||||
export enum ErrorNames {
|
||||
INTERNAL_SERVER_ERROR,
|
||||
NETWORK_ERROR,
|
||||
@@ -1229,8 +1223,7 @@ export enum ErrorNames {
|
||||
INVALID_INDEXER_INPUT,
|
||||
COMMENT_NOT_FOUND,
|
||||
REPLY_NOT_FOUND,
|
||||
COMMENT_ATTACHMENT_NOT_FOUND,
|
||||
COMMENT_ATTACHMENT_QUOTA_EXCEEDED
|
||||
COMMENT_ATTACHMENT_NOT_FOUND
|
||||
}
|
||||
registerEnumType(ErrorNames, {
|
||||
name: 'ErrorNames'
|
||||
|
||||
-85
@@ -79,88 +79,3 @@ Generated by [AVA](https://avajs.dev).
|
||||
},
|
||||
totalCount: 105,
|
||||
}
|
||||
|
||||
## should return encode pageInfo with custom cursor
|
||||
|
||||
> Snapshot 1
|
||||
|
||||
{
|
||||
edges: [
|
||||
{
|
||||
cursor: '',
|
||||
node: {
|
||||
id: 11,
|
||||
},
|
||||
},
|
||||
{
|
||||
cursor: '',
|
||||
node: {
|
||||
id: 12,
|
||||
},
|
||||
},
|
||||
{
|
||||
cursor: '',
|
||||
node: {
|
||||
id: 13,
|
||||
},
|
||||
},
|
||||
{
|
||||
cursor: '',
|
||||
node: {
|
||||
id: 14,
|
||||
},
|
||||
},
|
||||
{
|
||||
cursor: '',
|
||||
node: {
|
||||
id: 15,
|
||||
},
|
||||
},
|
||||
{
|
||||
cursor: '',
|
||||
node: {
|
||||
id: 16,
|
||||
},
|
||||
},
|
||||
{
|
||||
cursor: '',
|
||||
node: {
|
||||
id: 17,
|
||||
},
|
||||
},
|
||||
{
|
||||
cursor: '',
|
||||
node: {
|
||||
id: 18,
|
||||
},
|
||||
},
|
||||
{
|
||||
cursor: '',
|
||||
node: {
|
||||
id: 19,
|
||||
},
|
||||
},
|
||||
{
|
||||
cursor: '',
|
||||
node: {
|
||||
id: 20,
|
||||
},
|
||||
},
|
||||
],
|
||||
pageInfo: {
|
||||
endCursor: 'eyJpZCI6MjAsIm5hbWUiOiJ0ZXN0MiJ9',
|
||||
hasNextPage: true,
|
||||
hasPreviousPage: false,
|
||||
startCursor: 'eyJpZCI6MTAsIm5hbWUiOiJ0ZXN0In0=',
|
||||
},
|
||||
totalCount: 105,
|
||||
}
|
||||
|
||||
## should decode with json
|
||||
|
||||
> Snapshot 1
|
||||
|
||||
{
|
||||
id: 10,
|
||||
name: 'test',
|
||||
}
|
||||
|
||||
BIN
Binary file not shown.
@@ -4,13 +4,7 @@ import Sinon from 'sinon';
|
||||
|
||||
import { createTestingApp } from '../../../__tests__/utils';
|
||||
import { Public } from '../../../core/auth';
|
||||
import {
|
||||
decodeWithJson,
|
||||
paginate,
|
||||
Paginated,
|
||||
paginateWithCustomCursor,
|
||||
PaginationInput,
|
||||
} from '../pagination';
|
||||
import { paginate, Paginated, PaginationInput } from '../pagination';
|
||||
|
||||
const TOTAL_COUNT = 105;
|
||||
const ITEMS = Array.from({ length: TOTAL_COUNT }, (_, i) => ({ id: i + 1 }));
|
||||
@@ -110,24 +104,3 @@ test('should return encode pageInfo', async t => {
|
||||
|
||||
t.snapshot(result);
|
||||
});
|
||||
|
||||
test('should return encode pageInfo with custom cursor', async t => {
|
||||
const result = paginateWithCustomCursor(
|
||||
ITEMS.slice(10, 20),
|
||||
TOTAL_COUNT,
|
||||
{ id: 10, name: 'test' },
|
||||
{ id: 20, name: 'test2' }
|
||||
);
|
||||
|
||||
t.snapshot(result);
|
||||
});
|
||||
|
||||
test('should decode with json', async t => {
|
||||
const result = decodeWithJson<{ id: number; name: string }>(
|
||||
'eyJpZCI6MTAsIm5hbWUiOiJ0ZXN0In0='
|
||||
);
|
||||
t.snapshot(result);
|
||||
|
||||
const result2 = decodeWithJson<{ id: number; name: string }>('');
|
||||
t.is(result2, null);
|
||||
});
|
||||
|
||||
@@ -65,15 +65,6 @@ const encode = (input: unknown) => {
|
||||
const decode = (base64String?: string | null) =>
|
||||
base64String ? Buffer.from(base64String, 'base64').toString('utf-8') : null;
|
||||
|
||||
function encodeWithJson(input: unknown) {
|
||||
return encode(JSON.stringify(input ?? null));
|
||||
}
|
||||
|
||||
export function decodeWithJson<T>(base64String?: string | null): T | null {
|
||||
const str = decode(base64String);
|
||||
return str ? (JSON.parse(str) as T) : null;
|
||||
}
|
||||
|
||||
export function paginate<T>(
|
||||
list: T[],
|
||||
cursorField: keyof T,
|
||||
@@ -97,31 +88,6 @@ export function paginate<T>(
|
||||
};
|
||||
}
|
||||
|
||||
export function paginateWithCustomCursor<T>(
|
||||
list: T[],
|
||||
total: number,
|
||||
startCursor: unknown,
|
||||
endCursor: unknown,
|
||||
hasPreviousPage = false
|
||||
): PaginatedType<T> {
|
||||
const edges = list.map(item => ({
|
||||
node: item,
|
||||
// set cursor to empty string for ignore it
|
||||
cursor: '',
|
||||
}));
|
||||
|
||||
return {
|
||||
totalCount: total,
|
||||
edges,
|
||||
pageInfo: {
|
||||
hasNextPage: list.length > 0,
|
||||
hasPreviousPage,
|
||||
endCursor: encodeWithJson(endCursor),
|
||||
startCursor: encodeWithJson(startCursor),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export interface PaginatedType<T> {
|
||||
totalCount: number;
|
||||
edges: {
|
||||
|
||||
@@ -60,11 +60,3 @@ export async function readBufferWithLimit(
|
||||
: undefined
|
||||
);
|
||||
}
|
||||
|
||||
export async function readableToBuffer(readable: Readable) {
|
||||
const chunks: Buffer[] = [];
|
||||
for await (const chunk of readable) {
|
||||
chunks.push(chunk);
|
||||
}
|
||||
return Buffer.concat(chunks);
|
||||
}
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
# Snapshot report for `src/core/comment/__tests__/service.spec.ts`
|
||||
|
||||
The actual snapshot is saved in `service.spec.ts.snap`.
|
||||
|
||||
Generated by [AVA](https://avajs.dev).
|
||||
|
||||
## should update a comment
|
||||
|
||||
> Snapshot 1
|
||||
|
||||
{
|
||||
content: [
|
||||
{
|
||||
text: 'test2',
|
||||
type: 'text',
|
||||
},
|
||||
],
|
||||
type: 'paragraph',
|
||||
}
|
||||
|
||||
## should update a reply
|
||||
|
||||
> Snapshot 1
|
||||
|
||||
{
|
||||
content: [
|
||||
{
|
||||
text: 'test2',
|
||||
type: 'text',
|
||||
},
|
||||
],
|
||||
type: 'paragraph',
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user