feat(core): block diff ui

This commit is contained in:
yoyoyohamapi
2025-06-19 11:30:58 +08:00
parent e8d774a2ad
commit 2dc69a3bef
6 changed files with 90 additions and 0 deletions
@@ -28,6 +28,7 @@ import { query, state } from 'lit/decorators.js';
import { classMap } from 'lit/directives/class-map.js';
import { styleMap } from 'lit/directives/style-map.js';
import { unsafeHTML } from 'lit/directives/unsafe-html.js';
import { repeat } from 'lit/directives/repeat.js';
import { ParagraphBlockConfigExtension } from './paragraph-block-config.js';
import { paragraphBlockStyles } from './styles.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
@@ -341,6 +348,7 @@ export class ParagraphBlockComponent extends CaptionedBlockComponent<ParagraphBl
</div>
${children}
${widgets}
</div>
`;
}
@@ -71,6 +71,7 @@ import {
} from './widgets/ai-panel/components';
import { AIFinishTip } from './widgets/ai-panel/components/finish-tip';
import { GeneratingPlaceholder } from './widgets/ai-panel/components/generating-placeholder';
import { AFFINE_BLOCK_DIFF_WIDGET, AffineBlockDiffWidget } from './widgets/block-diff/widget';
import {
AFFINE_EDGELESS_COPILOT_WIDGET,
EdgelessCopilotWidget,
@@ -158,6 +159,7 @@ export function registerAIEffects() {
customElements.define(AFFINE_AI_PANEL_WIDGET, AffineAIPanelWidget);
customElements.define(AFFINE_EDGELESS_COPILOT_WIDGET, EdgelessCopilotWidget);
customElements.define(AFFINE_BLOCK_DIFF_WIDGET, AffineBlockDiffWidget);
customElements.define('edgeless-copilot-panel', EdgelessCopilotPanel);
customElements.define(
@@ -0,0 +1,52 @@
import { createIdentifier } from '@blocksuite/global/di';
import type { PatchOp } from '../utils/apply-model/markdown-diff';
interface DiffMap {
// removed blocks
deletes: string[];
// inserted blocks
// key is the start block id, value is the blocks(markdowns) inserted
inserts: Record<string, string[]>;
// updated blocks
// key is the block id, value is the block(markdown)
updates: Record<string, string>;
}
export interface BlockDiffService {
/**
* Set the patches to the block diffs
* @param patches - The patches to set.
* @returns The diff map.
*/
setPatches(patches: PatchOp[]): DiffMap;
}
export const BlockDiffService = createIdentifier<BlockDiffService>(
'AffineBlockDiffService'
);
export class BlockDiffServiceImpl implements BlockDiffService {
setPatches(patches: PatchOp[]): DiffMap {
const diffMap: DiffMap = {
deletes: [],
inserts: {},
updates: {},
};
for (const patch of patches) {
switch (patch.op) {
case 'delete':
diffMap.deletes.push(patch.block_id);
break;
case 'insert_at':
diffMap.inserts[patch.new_block.id] = [patch.new_block.content];
break;
case 'replace':
diffMap.updates[patch.block_id] = patch.new_content;
break;
}
}
return diffMap;
}
}
@@ -505,6 +505,7 @@ export class AffineAIPanelWidget extends WidgetComponent {
}
override render() {
console.log
if (this.state === 'hidden') {
return nothing;
}
@@ -0,0 +1,23 @@
import { WidgetComponent, WidgetViewExtension } from '@blocksuite/affine/std';
import { html } from 'lit';
import { literal, unsafeStatic } from 'lit/static-html.js';
export const AFFINE_BLOCK_DIFF_WIDGET = 'affine-block-diff-widget';
export class AffineBlockDiffWidget extends WidgetComponent {
override render() {
const attached = this.block?.blockId;
return html`<div
class="ai-panel-container"
data-testid="ai-panel-container"
>
<h1>Block Diff</h1>
</div>`;
}
}
export const blockDiffWidget = WidgetViewExtension(
'affine:paragraph',
AFFINE_BLOCK_DIFF_WIDGET,
literal`${unsafeStatic(AFFINE_BLOCK_DIFF_WIDGET)}`
);
@@ -20,6 +20,7 @@ import { FrameworkProvider } from '@toeverything/infra';
import { z } from 'zod';
import { EdgelessClipboardAIChatConfig } from './edgeless-clipboard';
import { blockDiffWidget } from '../../ai/widgets/block-diff/widget';
const optionsSchema = z.object({
enable: z.boolean().optional(),
@@ -50,6 +51,8 @@ export class AIViewExtension extends ViewExtensionProvider<AIViewOptions> {
config: imageToolbarAIEntryConfig(),
})
);
if (context.scope === 'edgeless' || context.scope === 'page') {
context.register([
aiPanelWidget,
@@ -73,6 +76,7 @@ export class AIViewExtension extends ViewExtensionProvider<AIViewOptions> {
]);
}
if (context.scope === 'page') {
context.register(blockDiffWidget);
context.register(getAIPageRootWatcher(framework));
}
}