mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-10 19:38:39 +00:00
Compare commits
8 Commits
v0.15.0-be
...
v0.15.1-be
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
824be0d4c1 | ||
|
|
fbf676002f | ||
|
|
e877f20955 | ||
|
|
f4f84d2793 | ||
|
|
34b6a3bf1f | ||
|
|
eca484dc28 | ||
|
|
855d555480 | ||
|
|
d51fd8b54b |
@@ -107,7 +107,6 @@ export function fromPromise<T>(
|
||||
.catch(error => {
|
||||
subscriber.error(error);
|
||||
});
|
||||
|
||||
return () => abortController.abort('Aborted');
|
||||
});
|
||||
}
|
||||
|
||||
@@ -432,7 +432,7 @@ export function buildAIImageItemGroups(): AIItemGroupConfig[] {
|
||||
items: [
|
||||
{
|
||||
name: 'Explain this image',
|
||||
icon: ExplainIcon,
|
||||
icon: AIImageIcon,
|
||||
showWhen: () => true,
|
||||
handler: actionToHandler(
|
||||
'explainImage',
|
||||
@@ -457,14 +457,6 @@ export function buildAIImageItemGroups(): AIItemGroupConfig[] {
|
||||
blockActionTrackerOptions
|
||||
),
|
||||
},
|
||||
{
|
||||
name: 'AI image filter',
|
||||
icon: ImproveWritingIcon,
|
||||
showWhen: () => true,
|
||||
subItem: createImageFilterSubItem(blockActionTrackerOptions),
|
||||
subItemOffset: [12, -4],
|
||||
beta: true,
|
||||
},
|
||||
{
|
||||
name: 'Image processing',
|
||||
icon: AIImageIcon,
|
||||
@@ -473,6 +465,14 @@ export function buildAIImageItemGroups(): AIItemGroupConfig[] {
|
||||
subItemOffset: [12, -6],
|
||||
beta: true,
|
||||
},
|
||||
{
|
||||
name: 'AI image filter',
|
||||
icon: ImproveWritingIcon,
|
||||
showWhen: () => true,
|
||||
subItem: createImageFilterSubItem(blockActionTrackerOptions),
|
||||
subItemOffset: [12, -4],
|
||||
beta: true,
|
||||
},
|
||||
{
|
||||
name: 'Generate a caption',
|
||||
icon: AIPenIcon,
|
||||
|
||||
@@ -119,6 +119,9 @@ export class ChatCards extends WithDisposable(LitElement) {
|
||||
@property({ attribute: false })
|
||||
accessor temporaryParams: AIChatParams | null = null;
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor isEmpty!: boolean;
|
||||
|
||||
@state()
|
||||
accessor cards: Card[] = [];
|
||||
|
||||
@@ -508,6 +511,8 @@ export class ChatCards extends WithDisposable(LitElement) {
|
||||
}
|
||||
|
||||
protected override render() {
|
||||
if (!this.isEmpty) return nothing;
|
||||
|
||||
return repeat(
|
||||
this.cards,
|
||||
card => card.id,
|
||||
|
||||
@@ -265,23 +265,18 @@ export class ChatPanelMessages extends WithDisposable(ShadowlessElement) {
|
||||
>
|
||||
${items.length === 0
|
||||
? html`<div class="chat-panel-messages-placeholder">
|
||||
${AffineIcon(
|
||||
isLoading
|
||||
? 'var(--affine-icon-secondary)'
|
||||
: 'var(--affine-primary-color)'
|
||||
)}
|
||||
<div>
|
||||
${this.isLoading
|
||||
? 'AFFiNE AI is loading history...'
|
||||
: 'What can I help you with?'}
|
||||
</div>
|
||||
${this._renderAIOnboarding()}
|
||||
${AffineIcon(
|
||||
isLoading
|
||||
? 'var(--affine-icon-secondary)'
|
||||
: 'var(--affine-primary-color)'
|
||||
)}
|
||||
<div>
|
||||
${this.isLoading
|
||||
? 'AFFiNE AI is loading history...'
|
||||
: 'What can I help you with?'}
|
||||
</div>
|
||||
<chat-cards
|
||||
.updateContext=${this.updateContext}
|
||||
.host=${this.host}
|
||||
?data-show=${this.showChatCards}
|
||||
></chat-cards>`
|
||||
${this._renderAIOnboarding()}
|
||||
</div> `
|
||||
: repeat(filteredItems, (item, index) => {
|
||||
const isLast = index === filteredItems.length - 1;
|
||||
return html`<div class="message">
|
||||
@@ -289,6 +284,12 @@ export class ChatPanelMessages extends WithDisposable(ShadowlessElement) {
|
||||
<div class="item-wrapper">${this.renderItem(item, isLast)}</div>
|
||||
</div>`;
|
||||
})}
|
||||
<chat-cards
|
||||
.updateContext=${this.updateContext}
|
||||
.host=${this.host}
|
||||
.isEmpty=${items.length === 0}
|
||||
?data-show=${this.showChatCards}
|
||||
></chat-cards>
|
||||
</div>
|
||||
${this.showDownIndicator
|
||||
? html`<div class="down-indicator" @click=${() => this.scrollToDown()}>
|
||||
|
||||
@@ -33,12 +33,15 @@ export class ChatPanel extends WithDisposable(ShadowlessElement) {
|
||||
}
|
||||
|
||||
.chat-panel-title {
|
||||
background: var(--affine-background-primary-color);
|
||||
position: relative;
|
||||
padding: 8px 0px;
|
||||
width: 100%;
|
||||
height: 36px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
z-index: 1;
|
||||
|
||||
div:first-child {
|
||||
font-size: 14px;
|
||||
|
||||
@@ -20,9 +20,9 @@ export function setupCodeToolbarEntry(codeToolbar: AffineCodeToolbarWidget) {
|
||||
const onAskAIClick = () => {
|
||||
const { host } = codeToolbar;
|
||||
const { selection } = host;
|
||||
const imageBlock = codeToolbar.blockElement;
|
||||
const codeBlock = codeToolbar.blockElement;
|
||||
selection.setGroup('note', [
|
||||
selection.create('block', { blockId: imageBlock.blockId }),
|
||||
selection.create('block', { blockId: codeBlock.blockId }),
|
||||
]);
|
||||
};
|
||||
codeToolbar.setupDefaultConfig();
|
||||
|
||||
@@ -139,6 +139,9 @@ export class AIAnswerText extends WithDisposable(LitElement) {
|
||||
editor-host * {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
editor-host {
|
||||
isolation: isolate;
|
||||
}
|
||||
}
|
||||
|
||||
${textBlockStyles}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { notify } from '@affine/component';
|
||||
import { authAtom, openSettingModalAtom } from '@affine/core/atoms';
|
||||
import { AIProvider } from '@affine/core/blocksuite/presets/ai';
|
||||
import { toggleGeneralAIOnboarding } from '@affine/core/components/affine/ai-onboarding/apis';
|
||||
import { mixpanel } from '@affine/core/utils';
|
||||
import { getBaseUrl } from '@affine/graphql';
|
||||
import { Trans } from '@affine/i18n';
|
||||
@@ -398,6 +399,8 @@ Could you make a new website based on these notes and send back just the html fi
|
||||
},
|
||||
});
|
||||
|
||||
AIProvider.provide('onboarding', toggleGeneralAIOnboarding);
|
||||
|
||||
AIProvider.slots.requestUpgradePlan.on(() => {
|
||||
getCurrentStore().set(openSettingModalAtom, {
|
||||
activeTab: 'billing',
|
||||
|
||||
@@ -13,7 +13,11 @@ import {
|
||||
borderAngle3,
|
||||
} from './styles.css';
|
||||
|
||||
if (typeof window !== 'undefined' && window.CSS) {
|
||||
if (
|
||||
typeof window !== 'undefined' &&
|
||||
window.CSS &&
|
||||
window.CSS.registerProperty
|
||||
) {
|
||||
const getName = (nameWithVar: string) => nameWithVar.slice(4, -1);
|
||||
const registerAngle = (varName: string, initialValue: number) => {
|
||||
window.CSS.registerProperty({
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
import { toast } from '@affine/component';
|
||||
import {
|
||||
pushGlobalLoadingEventAtom,
|
||||
resolveGlobalLoadingEventAtom,
|
||||
} from '@affine/component/global-loading';
|
||||
import { useI18n } from '@affine/i18n';
|
||||
import { ZipTransformer } from '@blocksuite/blocks';
|
||||
import { assertExists } from '@blocksuite/global/utils';
|
||||
import {
|
||||
@@ -9,8 +15,13 @@ import {
|
||||
useSensors,
|
||||
} from '@dnd-kit/core';
|
||||
import {
|
||||
type DocMode,
|
||||
DocsService,
|
||||
effect,
|
||||
fromPromise,
|
||||
GlobalContextService,
|
||||
onStart,
|
||||
throwIfAborted,
|
||||
useLiveData,
|
||||
useService,
|
||||
WorkspaceService,
|
||||
@@ -19,6 +30,14 @@ import { useAtomValue, useSetAtom } from 'jotai';
|
||||
import type { PropsWithChildren, ReactNode } from 'react';
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { createPortal } from 'react-dom';
|
||||
import {
|
||||
catchError,
|
||||
EMPTY,
|
||||
finalize,
|
||||
mergeMap,
|
||||
switchMap,
|
||||
timeout,
|
||||
} from 'rxjs';
|
||||
import { Map as YMap } from 'yjs';
|
||||
|
||||
import { openSettingModalAtom } from '../atoms';
|
||||
@@ -103,6 +122,9 @@ export const WorkspaceLayout = function WorkspaceLayout({
|
||||
};
|
||||
|
||||
export const WorkspaceLayoutInner = ({ children }: PropsWithChildren) => {
|
||||
const t = useI18n();
|
||||
const pushGlobalLoadingEvent = useSetAtom(pushGlobalLoadingEventAtom);
|
||||
const resolveGlobalLoadingEvent = useSetAtom(resolveGlobalLoadingEventAtom);
|
||||
const currentWorkspace = useService(WorkspaceService).workspace;
|
||||
const docsList = useService(DocsService).list;
|
||||
const { openPage } = useNavigateHelper();
|
||||
@@ -120,26 +142,60 @@ export const WorkspaceLayoutInner = ({ children }: PropsWithChildren) => {
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const disposable = AIProvider.slots.requestInsertTemplate.on(
|
||||
({ template, mode }) => {
|
||||
(async () => {
|
||||
const templateZip = await fetch(template);
|
||||
const insertTemplate = effect(
|
||||
switchMap(({ template, mode }: { template: string; mode: string }) => {
|
||||
return fromPromise(async abort => {
|
||||
const templateZip = await fetch(template, { signal: abort });
|
||||
const templateBlob = await templateZip.blob();
|
||||
throwIfAborted(abort);
|
||||
const [doc] = await ZipTransformer.importDocs(
|
||||
currentWorkspace.docCollection,
|
||||
templateBlob
|
||||
);
|
||||
doc.resetHistory();
|
||||
|
||||
docsList.setMode(doc.id, mode);
|
||||
workbench.openPage(doc.id);
|
||||
})().catch(err => {
|
||||
console.error(err);
|
||||
});
|
||||
return { doc, mode };
|
||||
}).pipe(
|
||||
timeout(10000 /* 10s */),
|
||||
mergeMap(({ mode, doc }) => {
|
||||
docsList.setMode(doc.id, mode as DocMode);
|
||||
workbench.openPage(doc.id);
|
||||
return EMPTY;
|
||||
}),
|
||||
onStart(() => {
|
||||
pushGlobalLoadingEvent({
|
||||
key: 'insert-template',
|
||||
});
|
||||
}),
|
||||
catchError(err => {
|
||||
console.error(err);
|
||||
toast(t['com.affine.ai.template-insert.failed']());
|
||||
return EMPTY;
|
||||
}),
|
||||
finalize(() => {
|
||||
resolveGlobalLoadingEvent('insert-template');
|
||||
})
|
||||
);
|
||||
})
|
||||
);
|
||||
|
||||
const disposable = AIProvider.slots.requestInsertTemplate.on(
|
||||
({ template, mode }) => {
|
||||
insertTemplate({ template, mode });
|
||||
}
|
||||
);
|
||||
return () => disposable.dispose();
|
||||
}, [currentWorkspace.docCollection, docsList, workbench]);
|
||||
return () => {
|
||||
disposable.dispose();
|
||||
insertTemplate.unsubscribe();
|
||||
};
|
||||
}, [
|
||||
currentWorkspace.docCollection,
|
||||
docsList,
|
||||
pushGlobalLoadingEvent,
|
||||
resolveGlobalLoadingEvent,
|
||||
t,
|
||||
workbench,
|
||||
]);
|
||||
|
||||
useRegisterWorkspaceCommands();
|
||||
useRegisterNavigationCommands();
|
||||
|
||||
@@ -22,6 +22,11 @@ const contentOptions: Dialog.DialogContentProps = {
|
||||
e.preventDefault();
|
||||
}
|
||||
},
|
||||
onEscapeKeyDown: e => {
|
||||
// prevent closing the modal when pressing escape key by default
|
||||
// this is because radix-ui register the escape key event on the document using capture, which is not possible to prevent in blocksuite
|
||||
e.preventDefault();
|
||||
},
|
||||
style: {
|
||||
padding: 0,
|
||||
backgroundColor: 'transparent',
|
||||
@@ -98,13 +103,29 @@ export const PeekViewModalContainer = forwardRef<
|
||||
) {
|
||||
const [vtOpen, setVtOpen] = useState(open);
|
||||
useEffect(() => {
|
||||
document.startViewTransition(() => {
|
||||
flushSync(() => {
|
||||
setVtOpen(open);
|
||||
if (document.startViewTransition) {
|
||||
document.startViewTransition(() => {
|
||||
flushSync(() => {
|
||||
setVtOpen(open);
|
||||
});
|
||||
});
|
||||
});
|
||||
} else {
|
||||
setVtOpen(open);
|
||||
}
|
||||
}, [open]);
|
||||
|
||||
useEffect(() => {
|
||||
const onKeyDown = (e: KeyboardEvent) => {
|
||||
if (e.key === 'Escape') {
|
||||
onOpenChange(false);
|
||||
}
|
||||
};
|
||||
document.addEventListener('keydown', onKeyDown);
|
||||
return () => {
|
||||
document.removeEventListener('keydown', onKeyDown);
|
||||
};
|
||||
}, [onOpenChange]);
|
||||
|
||||
useEffect(() => {
|
||||
const bondingBox = target ? getElementScreenPositionCenter(target) : null;
|
||||
const offsetLeft =
|
||||
|
||||
@@ -8,6 +8,7 @@ export const header = style({
|
||||
alignItems: 'center',
|
||||
flexShrink: 0,
|
||||
padding: '0 16px',
|
||||
zIndex: 1,
|
||||
gap: '12px',
|
||||
background: cssVar('backgroundPrimaryColor'),
|
||||
selectors: {
|
||||
|
||||
@@ -115,7 +115,7 @@ const DetailPageImpl = memo(function DetailPageImpl() {
|
||||
setTabOnLoad(null);
|
||||
}
|
||||
});
|
||||
return disposable.dispose();
|
||||
return () => disposable.dispose();
|
||||
}, [activeTabName, rightSidebar, setActiveTabName]);
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
@@ -1342,5 +1342,6 @@
|
||||
"will be moved to Trash": "{{title}} will be moved to Trash",
|
||||
"will delete member": "will delete member",
|
||||
"com.affine.user-info.usage.ai": "AI Usage",
|
||||
"com.affine.user-info.usage.cloud": "Cloud Storage"
|
||||
"com.affine.user-info.usage.cloud": "Cloud Storage",
|
||||
"com.affine.ai.template-insert.failed": "Failed to insert template, please try again."
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user