286 lines
8.2 KiB
TypeScript
286 lines
8.2 KiB
TypeScript
/* eslint-disable @typescript-eslint/no-restricted-imports */
|
|
import '@shoelace-style/shoelace/dist/components/tab-panel/tab-panel.js';
|
|
|
|
import { ShadowlessElement } from '@blocksuite/affine/block-std';
|
|
import { defaultImageProxyMiddleware } from '@blocksuite/affine/blocks/image';
|
|
import { WithDisposable } from '@blocksuite/affine/global/lit';
|
|
import {
|
|
docLinkBaseURLMiddlewareBuilder,
|
|
embedSyncedDocMiddleware,
|
|
type HtmlAdapter,
|
|
HtmlAdapterFactoryIdentifier,
|
|
type MarkdownAdapter,
|
|
MarkdownAdapterFactoryIdentifier,
|
|
type PlainTextAdapter,
|
|
PlainTextAdapterFactoryIdentifier,
|
|
titleMiddleware,
|
|
} from '@blocksuite/affine/shared/adapters';
|
|
import type { DocSnapshot } from '@blocksuite/affine/store';
|
|
import type { TestAffineEditorContainer } from '@blocksuite/integration-test';
|
|
import { effect } from '@preact/signals-core';
|
|
import type SlTabPanel from '@shoelace-style/shoelace/dist/components/tab-panel/tab-panel.js';
|
|
import { css, html, type PropertyValues } from 'lit';
|
|
import { customElement, property, query, state } from 'lit/decorators.js';
|
|
|
|
@customElement('adapters-panel')
|
|
export class AdaptersPanel extends WithDisposable(ShadowlessElement) {
|
|
static override styles = css`
|
|
adapters-panel {
|
|
width: 36vw;
|
|
}
|
|
.adapters-container {
|
|
border: 1px solid var(--affine-border-color, #e3e2e4);
|
|
background-color: var(--affine-background-primary-color);
|
|
box-sizing: border-box;
|
|
position: relative;
|
|
}
|
|
.adapter-container {
|
|
padding: 0px 16px;
|
|
width: 100%;
|
|
height: calc(100vh - 80px);
|
|
white-space: pre-wrap;
|
|
color: var(--affine-text-primary-color);
|
|
overflow: auto;
|
|
}
|
|
.update-button {
|
|
position: absolute;
|
|
top: 8px;
|
|
right: 12px;
|
|
padding: 8px 12px;
|
|
border-radius: 4px;
|
|
font-size: 12px;
|
|
font-weight: 500;
|
|
cursor: pointer;
|
|
border: 1px solid var(--affine-border-color);
|
|
font-family: var(--affine-font-family);
|
|
color: var(--affine-text-primary-color);
|
|
background-color: var(--affine-background-primary-color);
|
|
}
|
|
.update-button:hover {
|
|
background-color: var(--affine-hover-color);
|
|
}
|
|
.html-panel {
|
|
display: flex;
|
|
gap: 8px;
|
|
flex-direction: column;
|
|
}
|
|
.html-preview-container,
|
|
.html-panel-content {
|
|
width: 100%;
|
|
flex: 1;
|
|
border: none;
|
|
box-sizing: border-box;
|
|
color: var(--affine-text-primary-color);
|
|
overflow: auto;
|
|
}
|
|
.html-panel-footer {
|
|
width: 100%;
|
|
height: 32px;
|
|
display: flex;
|
|
justify-content: flex-end;
|
|
|
|
span {
|
|
cursor: pointer;
|
|
padding: 4px 8px;
|
|
font-size: 12px;
|
|
font-weight: 500;
|
|
border: 1px solid var(--affine-border-color);
|
|
font-family: var(--affine-font-family);
|
|
color: var(--affine-text-primary-color);
|
|
background-color: var(--affine-background-primary-color);
|
|
line-height: 20px;
|
|
}
|
|
span[active] {
|
|
background-color: var(--affine-hover-color);
|
|
}
|
|
}
|
|
`;
|
|
|
|
get doc() {
|
|
return this.editor.doc;
|
|
}
|
|
|
|
private _createJob() {
|
|
return this.doc.getTransformer([
|
|
docLinkBaseURLMiddlewareBuilder(
|
|
'https://example.com',
|
|
this.doc.workspace.id
|
|
).get(),
|
|
titleMiddleware(this.doc.workspace.meta.docMetas),
|
|
embedSyncedDocMiddleware('content'),
|
|
defaultImageProxyMiddleware,
|
|
]);
|
|
}
|
|
|
|
private _getDocSnapshot() {
|
|
const job = this._createJob();
|
|
const result = job.docToSnapshot(this.doc);
|
|
return result;
|
|
}
|
|
|
|
private async _getHtmlContent() {
|
|
const job = this._createJob();
|
|
const htmlAdapterFactory = this.editor.std.provider.get(
|
|
HtmlAdapterFactoryIdentifier
|
|
);
|
|
const htmlAdapter = htmlAdapterFactory.get(job) as HtmlAdapter;
|
|
const result = await htmlAdapter.fromDoc(this.doc);
|
|
return result?.file;
|
|
}
|
|
|
|
private async _getMarkdownContent() {
|
|
const job = this._createJob();
|
|
const markdownAdapterFactory = this.editor.std.provider.get(
|
|
MarkdownAdapterFactoryIdentifier
|
|
);
|
|
const markdownAdapter = markdownAdapterFactory.get(job) as MarkdownAdapter;
|
|
const result = await markdownAdapter.fromDoc(this.doc);
|
|
return result?.file;
|
|
}
|
|
|
|
private async _getPlainTextContent() {
|
|
const job = this._createJob();
|
|
const plainTextAdapterFactory = this.editor.std.provider.get(
|
|
PlainTextAdapterFactoryIdentifier
|
|
);
|
|
const plainTextAdapter = plainTextAdapterFactory.get(
|
|
job
|
|
) as PlainTextAdapter;
|
|
const result = await plainTextAdapter.fromDoc(this.doc);
|
|
return result?.file;
|
|
}
|
|
|
|
private async _handleTabShow(name: string) {
|
|
switch (name) {
|
|
case 'markdown':
|
|
this._markdownContent = (await this._getMarkdownContent()) || '';
|
|
break;
|
|
case 'html':
|
|
this._htmlContent = (await this._getHtmlContent()) || '';
|
|
break;
|
|
case 'plaintext':
|
|
this._plainTextContent = (await this._getPlainTextContent()) || '';
|
|
break;
|
|
case 'snapshot':
|
|
this._docSnapshot = this._getDocSnapshot() || null;
|
|
break;
|
|
}
|
|
}
|
|
|
|
private _renderHtmlPanel() {
|
|
return html`
|
|
${this._isHtmlPreview
|
|
? html`<iframe
|
|
class="html-preview-container"
|
|
.srcdoc=${this._htmlContent}
|
|
></iframe>`
|
|
: html`<div class="html-panel-content">${this._htmlContent}</div>`}
|
|
<div class="html-panel-footer">
|
|
<span
|
|
class="html-panel-footer-item"
|
|
?active=${!this._isHtmlPreview}
|
|
@click=${() => (this._isHtmlPreview = false)}
|
|
>Source</span
|
|
>
|
|
<span
|
|
class="html-panel-footer-item"
|
|
?active=${this._isHtmlPreview}
|
|
@click=${() => (this._isHtmlPreview = true)}
|
|
>Preview</span
|
|
>
|
|
</div>
|
|
`;
|
|
}
|
|
|
|
private async _updateActiveTabContent() {
|
|
if (!this._activeTab) return;
|
|
const activeTabName = this._activeTab.name;
|
|
await this._handleTabShow(activeTabName);
|
|
}
|
|
|
|
override firstUpdated() {
|
|
this.disposables.add(
|
|
effect(() => {
|
|
const doc = this.doc;
|
|
if (doc) {
|
|
this._updateActiveTabContent().catch(console.error);
|
|
}
|
|
})
|
|
);
|
|
}
|
|
|
|
override render() {
|
|
const snapshotString = this._docSnapshot
|
|
? JSON.stringify(this._docSnapshot, null, 4)
|
|
: '';
|
|
return html`
|
|
<div class="adapters-container">
|
|
<sl-tab-group
|
|
activation="auto"
|
|
@sl-tab-show=${(e: CustomEvent) => this._handleTabShow(e.detail.name)}
|
|
>
|
|
<sl-tab slot="nav" panel="markdown">Markdown</sl-tab>
|
|
<sl-tab slot="nav" panel="plaintext">PlainText</sl-tab>
|
|
<sl-tab slot="nav" panel="html">HTML</sl-tab>
|
|
<sl-tab slot="nav" panel="snapshot">Snapshot</sl-tab>
|
|
|
|
<sl-tab-panel name="markdown">
|
|
<div class="adapter-container">${this._markdownContent}</div>
|
|
</sl-tab-panel>
|
|
<sl-tab-panel name="html">
|
|
<div class="adapter-container html-panel">
|
|
${this._renderHtmlPanel()}
|
|
</div>
|
|
</sl-tab-panel>
|
|
<sl-tab-panel name="plaintext">
|
|
<div class="adapter-container">${this._plainTextContent}</div>
|
|
</sl-tab-panel>
|
|
<sl-tab-panel name="snapshot">
|
|
<div class="adapter-container">${snapshotString}</div>
|
|
</sl-tab-panel>
|
|
</sl-tab-group>
|
|
<sl-tooltip content="Update Adapter Content" placement="left" hoist>
|
|
<div class="update-button" @click="${this._updateActiveTabContent}">
|
|
Update
|
|
</div>
|
|
</sl-tooltip>
|
|
</div>
|
|
`;
|
|
}
|
|
|
|
override willUpdate(_changedProperties: PropertyValues) {
|
|
if (_changedProperties.has('editor')) {
|
|
requestIdleCallback(() => {
|
|
this._updateActiveTabContent().catch(console.error);
|
|
});
|
|
}
|
|
}
|
|
|
|
@query('sl-tab-panel[active]')
|
|
private accessor _activeTab!: SlTabPanel;
|
|
|
|
@state()
|
|
private accessor _docSnapshot: DocSnapshot | null = null;
|
|
|
|
@state()
|
|
private accessor _htmlContent = '';
|
|
|
|
@state()
|
|
private accessor _isHtmlPreview = false;
|
|
|
|
@state()
|
|
private accessor _markdownContent = '';
|
|
|
|
@state()
|
|
private accessor _plainTextContent = '';
|
|
|
|
@property({ attribute: false })
|
|
accessor editor!: TestAffineEditorContainer;
|
|
}
|
|
|
|
declare global {
|
|
interface HTMLElementTagNameMap {
|
|
'adapters-panel': AdaptersPanel;
|
|
}
|
|
}
|