feat(editor): by default render code iframe for html preview (#12848)

#### PR Dependency Tree


* **PR #12848** 👈

This tree was auto-generated by
[Charcoal](https://github.com/danerwilliams/charcoal)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

- **New Features**
- Introduced a feature flag to enable or disable web container
functionality for code block previews.
- **Improvements**
- Code block HTML previews now support an alternative rendering method
based on the new feature flag, enhancing flexibility.
- **Chores**
- Updated feature flag settings by removing an obsolete flag and adding
the new web container flag.
- **Tests**
- Simplified code block preview tests for faster and more direct
validation of HTML preview content.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
Peng Xiao
2025-06-19 09:12:33 +08:00
committed by GitHub
parent e046260deb
commit ce951ec316
6 changed files with 46 additions and 51 deletions

View File

@@ -2,12 +2,14 @@ import track from '@affine/track';
import { CodeBlockPreviewExtension } from '@blocksuite/affine/blocks/code';
import { SignalWatcher, WithDisposable } from '@blocksuite/affine/global/lit';
import type { CodeBlockModel } from '@blocksuite/affine/model';
import { FeatureFlagService } from '@blocksuite/affine/shared/services';
import { unsafeCSSVarV2 } from '@blocksuite/affine/shared/theme';
import { css, html, LitElement, type PropertyValues } from 'lit';
import { property, query, state } from 'lit/decorators.js';
import { choose } from 'lit/directives/choose.js';
import { styleMap } from 'lit/directives/style-map.js';
import { linkIframe } from './iframe-container';
import { linkWebContainer } from './web-container';
export const CodeBlockHtmlPreview = CodeBlockPreviewExtension(
@@ -83,20 +85,36 @@ export class HTMLPreview extends SignalWatcher(WithDisposable(LitElement)) {
private _link() {
this.state = 'loading';
linkWebContainer(this.iframe, this.model)
.then(() => {
this.state = 'finish';
})
.catch(error => {
const errorMessage = `Failed to link WebContainer: ${error}`;
console.error(errorMessage);
track.doc.editor.codeBlock.htmlBlockPreviewFailed({
type: errorMessage,
const featureFlagService = this.model.store.get(FeatureFlagService);
const isWebContainerEnabled = featureFlagService.getFlag(
'enable_web_container'
);
if (isWebContainerEnabled) {
linkWebContainer(this.iframe, this.model)
.then(() => {
this.state = 'finish';
})
.catch(error => {
const errorMessage = `Failed to link WebContainer: ${error}`;
console.error(errorMessage);
track.doc.editor.codeBlock.htmlBlockPreviewFailed({
type: errorMessage,
});
this.state = 'error';
});
} else {
try {
linkIframe(this.iframe, this.model);
this.state = 'finish';
} catch (error) {
console.error('HTML preview iframe failed:', error);
this.state = 'error';
});
}
}
}
override render() {

View File

@@ -0,0 +1,7 @@
import type { CodeBlockModel } from '@blocksuite/affine/model';
export function linkIframe(iframe: HTMLIFrameElement, model: CodeBlockModel) {
const html = model.props.text.toString();
iframe.srcdoc = html;
iframe.sandbox.add('allow-scripts');
}

View File

@@ -1,5 +1,3 @@
import { FeatureFlagService } from '@affine/core/modules/feature-flag';
import track from '@affine/track';
import {
type ViewExtensionContext,
ViewExtensionProvider,
@@ -32,22 +30,6 @@ export class CodeBlockPreviewViewExtension extends ViewExtensionProvider {
options?: z.infer<typeof optionsSchema>
) {
super.setup(context, options);
const framework = options?.framework;
if (!framework) return;
const flag =
framework.get(FeatureFlagService).flags.enable_code_block_html_preview.$
.value;
if (!flag) return;
if (!window.crossOriginIsolated) {
track.doc.editor.codeBlock.htmlBlockPreviewFailed({
type: 'cross-origin-isolated not supported',
});
return;
}
context.register(CodeBlockHtmlPreview);
}
}