Skip to content

Commit

Permalink
feat(presets): chat with workspace (#5748)
Browse files Browse the repository at this point in the history
  • Loading branch information
zzj3720 authored Dec 15, 2023
1 parent 3a0d1e5 commit 5ea5de7
Show file tree
Hide file tree
Showing 10 changed files with 567 additions and 79 deletions.
98 changes: 22 additions & 76 deletions packages/playground/apps/starter/components/debug-menu.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,12 @@ import '@shoelace-style/shoelace/dist/components/tab/tab.js';
import '@shoelace-style/shoelace/dist/components/tooltip/tooltip.js';
import '@shoelace-style/shoelace/dist/themes/light.css';
import '@shoelace-style/shoelace/dist/themes/dark.css';
import './left-side-panel';
import './side-panel';

import {
BlocksUtils,
ColorVariables,
createDefaultPage,
extractCssVariables,
FontFamilyVariables,
HtmlTransformer,
Expand Down Expand Up @@ -51,6 +52,8 @@ import type { Pane } from 'tweakpane';
import { extendFormatBar } from './custom-format-bar.js';
import type { CustomFramePanel } from './custom-frame-panel.js';
import type { CustomTOCOutlinePanel } from './custom-toc-outline-panel.js';
import type { LeftSidePanel } from './left-side-panel';
import type { PagesPanel } from './pages-panel';
import type { SidePanel } from './side-panel';

export function getSurfaceElementFromEditor(editor: AffineEditorContainer) {
Expand Down Expand Up @@ -212,6 +215,10 @@ export class DebugMenu extends ShadowlessElement {

@property({ attribute: false })
sidePanel!: SidePanel;
@property({ attribute: false })
leftSidePanel!: LeftSidePanel;
@property({ attribute: false })
pagesPanel!: PagesPanel;

@state()
private _connected = true;
Expand Down Expand Up @@ -324,11 +331,10 @@ export class DebugMenu extends ShadowlessElement {
}

private _toggleCopilotPanel() {
if (this.sidePanel.currentContent === this.copilotPanel) {
this.sidePanel.hideContent();
} else {
this.sidePanel.showContent(this.copilotPanel);
}
this.sidePanel.toggle(this.copilotPanel);
}
private _togglePagesPanel() {
this.leftSidePanel.toggle(this.pagesPanel);
}

private _createMindMap() {
Expand Down Expand Up @@ -411,7 +417,9 @@ export class DebugMenu extends ShadowlessElement {
}

private async _exportSnapshot() {
const file = await ZipTransformer.exportPages(this.workspace, [this.page]);
const file = await ZipTransformer.exportPages(this.workspace, [
...this.workspace.pages.values(),
]);
const url = URL.createObjectURL(file);
const a = document.createElement('a');
a.setAttribute('href', url);
Expand Down Expand Up @@ -707,17 +715,6 @@ export class DebugMenu extends ShadowlessElement {
</sl-button>
</sl-tooltip>
<sl-tooltip content="Add New Page" placement="bottom" hoist>
<sl-button
size="small"
@click=${() => createPageBlock(this.workspace)}
>
<sl-icon name="file-earmark-plus"></sl-icon>
</sl-button>
</sl-tooltip>
${PageList(this.workspace, this.editor, () => this.requestUpdate())}
<sl-tooltip
content="🚧 Toggle Copilot Panel"
placement="bottom"
Expand All @@ -727,70 +724,19 @@ export class DebugMenu extends ShadowlessElement {
<sl-icon name="stars"></sl-icon>
</sl-button>
</sl-tooltip>
<sl-button
data-testid="pages-button"
size="small"
@click=${this._togglePagesPanel}
>
Pages
</sl-button>
</div>
</div>
`;
}
}

function createPageBlock(workspace: Workspace) {
const id = workspace.idGenerator('page');
createDefaultPage(workspace, { id }).catch(console.error);
}

function PageList(
workspace: Workspace,
editor: AffineEditorContainer,
requestUpdate: () => void
) {
workspace.meta.pageMetasUpdated.on(requestUpdate);

// This function is called when a delete option is clicked
const handleDeletePage = (pageId: string) => {
workspace.removePage(pageId);
// When delete a page, we need to set the editor page to the first remaining page
const pages = Array.from(workspace.pages.values());
editor.page = pages[0];
requestUpdate();
};

// Create a dropdown menu for each page with a delete option
return html`
<sl-dropdown hoist>
<sl-button size="small" slot="trigger" caret>Pages</sl-button>
<sl-menu>
${workspace.meta.pageMetas.map(
pageMeta => html`
<sl-menu-item>
${pageMeta.title || 'Untitled'}
<sl-menu slot="submenu">
<sl-menu-item
@click="${() => {
const newPage = workspace.getPage(pageMeta.id);
if (!newPage) return;
editor.page = newPage;
}}"
>
Open
</sl-menu-item>
<sl-menu-item
.disabled="${workspace.pages.size <= 1}"
@click="${() => {
if (workspace.pages.size <= 1) return;
handleDeletePage(pageMeta.id);
}}"
>
Delete
</sl-menu-item>
</sl-menu>
</sl-menu-item>
`
)}
</sl-menu>
</sl-dropdown>
`;
}

declare global {
interface HTMLElementTagNameMap {
'debug-menu': DebugMenu;
Expand Down
54 changes: 54 additions & 0 deletions packages/playground/apps/starter/components/left-side-panel.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { ShadowlessElement } from '@blocksuite/lit';
import { css, html } from 'lit';
import { customElement } from 'lit/decorators.js';

@customElement('left-side-panel')
export class LeftSidePanel extends ShadowlessElement {
static override styles = css`
left-side-panel {
padding-top: 50px;
width: 300px;
position: absolute;
top: 0;
left: 0;
height: 100%;
display: none;
}
`;
currentContent: HTMLElement | null = null;

showContent(ele: HTMLElement) {
if (this.currentContent) {
this.currentContent.remove();
}
this.style.display = 'block';
this.currentContent = ele;
this.append(ele);
}

hideContent() {
if (this.currentContent) {
this.style.display = 'none';
this.currentContent.remove();
this.currentContent = null;
}
}

toggle(ele: HTMLElement) {
if (this.currentContent !== ele) {
this.showContent(ele);
} else {
this.hideContent();
}
}

protected override render(): unknown {
return html``;
}
}

declare global {
interface HTMLElementTagNameMap {
'left-side-panel': LeftSidePanel;
}
}
123 changes: 123 additions & 0 deletions packages/playground/apps/starter/components/pages-panel.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
import { createDefaultPage } from '@blocksuite/blocks';
import { CloseIcon } from '@blocksuite/blocks/_common/icons';
import { ShadowlessElement, WithDisposable } from '@blocksuite/lit';
import type { AffineEditorContainer } from '@blocksuite/presets';
import type { Workspace } from '@blocksuite/store';
import { css, html } from 'lit';
import { customElement, property } from 'lit/decorators.js';
import { repeat } from 'lit/directives/repeat.js';
import { styleMap } from 'lit/directives/style-map.js';

@customElement('pages-panel')
export class PagesPanel extends WithDisposable(ShadowlessElement) {
static override styles = css`
pages-panel {
display: flex;
flex-direction: column;
width: 100%;
background-color: var(--affine-background-secondary-color);
font-family: var(--affine-font-family);
height: 100%;
padding: 12px;
gap: 4px;
}
.page-item:hover .delete-page-icon {
display: flex;
}
.delete-page-icon {
display: none;
padding: 2px;
border-radius: 4px;
}
.delete-page-icon:hover {
background-color: var(--affine-hover-color);
}
.delete-page-icon svg {
width: 14px;
height: 14px;
color: var(--affine-secondary-color);
fill: var(--affine-secondary-color);
}
.new-page-button {
margin-bottom: 16px;
border: 1px solid var(--affine-border-color);
border-radius: 4px;
height: 32px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
}
.new-page-button:hover {
background-color: var(--affine-hover-color);
}
`;
@property({ attribute: false })
editor!: AffineEditorContainer;

public override connectedCallback() {
super.connectedCallback();
this.disposables.add(
this.editor.page.workspace.slots.pagesUpdated.on(() => {
this.requestUpdate();
})
);
}

createPage = () => {
createPageBlock(this.editor.page.workspace);
};

protected override render(): unknown {
const workspace = this.editor.page.workspace;
const pages = [...workspace.pages.values()];
return html`
<div @click="${this.createPage}" class="new-page-button">New Page</div>
${repeat(
pages,
v => v.id,
page => {
const style = styleMap({
backgroundColor:
this.editor.page.id === page.id
? 'var(--affine-hover-color)'
: undefined,
padding: '4px 4px 4px 8px',
borderRadius: '4px',
cursor: 'pointer',
display: 'flex',
justifyContent: 'space-between',
});
const click = () => {
this.editor.page = page;
this.requestUpdate();
};
const deletePage = () => {
workspace.removePage(page.id);
// When delete a page, we need to set the editor page to the first remaining page
const pages = Array.from(workspace.pages.values());
this.editor.page = pages[0];
this.requestUpdate();
};
return html`<div class="page-item" @click="${click}" style="${style}">
${page.meta.title || 'Untitled'}
<div @click="${deletePage}" class="delete-page-icon">
${CloseIcon}
</div>
</div>`;
}
)}
`;
}
}

function createPageBlock(workspace: Workspace) {
const id = workspace.idGenerator('page');
createDefaultPage(workspace, { id }).catch(console.error);
}

declare global {
interface HTMLElementTagNameMap {
'pages-panel': PagesPanel;
}
}
8 changes: 8 additions & 0 deletions packages/playground/apps/starter/components/side-panel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,12 @@ export class SidePanel extends ShadowlessElement {
protected override render(): unknown {
return html``;
}

toggle(ele: HTMLElement) {
if (this.currentContent !== ele) {
this.showContent(ele);
} else {
this.hideContent();
}
}
}
8 changes: 8 additions & 0 deletions packages/playground/apps/starter/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ import { Job, Workspace } from '@blocksuite/store';
import { CustomFramePanel } from './components/custom-frame-panel';
import { CustomTOCOutlinePanel } from './components/custom-toc-outline-panel.js';
import { DebugMenu } from './components/debug-menu.js';
import { LeftSidePanel } from './components/left-side-panel';
import { PagesPanel } from './components/pages-panel';
import { SidePanel } from './components/side-panel';
import type { InitFn } from './data';
import {
Expand Down Expand Up @@ -50,6 +52,8 @@ function subscribePage(workspace: Workspace) {
const framePanel = new CustomFramePanel();
const copilotPanelPanel = new CopilotPanel();
const sidePanel = new SidePanel();
const leftSidePanel = new LeftSidePanel();
const pagesPanel = new PagesPanel();

debugMenu.workspace = workspace;
debugMenu.editor = editor;
Expand All @@ -59,14 +63,18 @@ function subscribePage(workspace: Workspace) {
debugMenu.framePanel = framePanel;
debugMenu.copilotPanel = copilotPanelPanel;
debugMenu.sidePanel = sidePanel;
debugMenu.leftSidePanel = leftSidePanel;
debugMenu.pagesPanel = pagesPanel;

tocOutlinePanel.editor = editor;
copilotPanelPanel.editor = editor;
framePanel.editor = editor;
pagesPanel.editor = editor;

document.body.appendChild(debugMenu);
document.body.appendChild(tocOutlinePanel);
document.body.appendChild(sidePanel);
document.body.appendChild(leftSidePanel);
document.body.appendChild(framePanel);

window.editor = editor;
Expand Down
Loading

1 comment on commit 5ea5de7

@vercel
Copy link

@vercel vercel bot commented on 5ea5de7 Dec 15, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

blocksuite – ./packages/playground

blocksuite-toeverything.vercel.app
try-blocksuite.vercel.app
blocksuite-git-master-toeverything.vercel.app

Please sign in to comment.