mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-14 13:25:12 +00:00
@@ -10,24 +10,15 @@ export class LitTranscriptionBlock extends BlockComponent<TranscriptionBlockMode
|
||||
css`
|
||||
transcription-block {
|
||||
outline: none;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
}
|
||||
`,
|
||||
];
|
||||
|
||||
get lastCalloutBlock() {
|
||||
for (const child of this.model.children.toReversed()) {
|
||||
if (child.flavour === 'affine:callout') {
|
||||
return child;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
override render() {
|
||||
return this.std.host.renderChildren(this.model, model => {
|
||||
// if model is the last transcription block, we should render it
|
||||
return model === this.lastCalloutBlock;
|
||||
});
|
||||
return this.std.host.renderChildren(this.model);
|
||||
}
|
||||
|
||||
@property({ type: String, attribute: 'data-block-id' })
|
||||
|
||||
@@ -56,7 +56,7 @@ export const PPTBuilder = (host: EditorHost) => {
|
||||
return {
|
||||
process: async (text: string) => {
|
||||
try {
|
||||
const snapshot = await markdownToSnapshot(text, host);
|
||||
const snapshot = await markdownToSnapshot(text, host.doc, host);
|
||||
|
||||
const block = snapshot.snapshot?.content[0];
|
||||
if (!block) {
|
||||
@@ -73,7 +73,7 @@ export const PPTBuilder = (host: EditorHost) => {
|
||||
return { contents, images: allImages };
|
||||
},
|
||||
done: async (text: string) => {
|
||||
const snapshot = await markdownToSnapshot(text, host);
|
||||
const snapshot = await markdownToSnapshot(text, host.doc, host);
|
||||
const block = snapshot.snapshot?.content[0];
|
||||
if (!block) {
|
||||
return;
|
||||
|
||||
@@ -110,7 +110,11 @@ export const replace = async (
|
||||
|
||||
if (textSelection) {
|
||||
host.std.command.exec(deleteTextCommand, { textSelection });
|
||||
const { snapshot, transformer } = await markdownToSnapshot(content, host);
|
||||
const { snapshot, transformer } = await markdownToSnapshot(
|
||||
content,
|
||||
host.doc,
|
||||
host
|
||||
);
|
||||
if (snapshot) {
|
||||
await transformer.snapshotToSlice(
|
||||
snapshot,
|
||||
|
||||
@@ -102,18 +102,19 @@ export async function getContentFromSlice(
|
||||
|
||||
export const markdownToSnapshot = async (
|
||||
markdown: string,
|
||||
host: EditorHost
|
||||
store: Store,
|
||||
host?: EditorHost
|
||||
) => {
|
||||
const transformer = host.std.store.getTransformer([
|
||||
defaultImageProxyMiddleware,
|
||||
pasteMiddleware(host.std),
|
||||
]);
|
||||
const markdownAdapter = new MixTextAdapter(transformer, host.std.provider);
|
||||
const middlewares = host
|
||||
? [defaultImageProxyMiddleware, pasteMiddleware(host.std)]
|
||||
: [defaultImageProxyMiddleware];
|
||||
const transformer = store.getTransformer(middlewares);
|
||||
const markdownAdapter = new MixTextAdapter(transformer, store.provider);
|
||||
const payload = {
|
||||
file: markdown,
|
||||
assets: transformer.assetsManager,
|
||||
workspaceId: host.std.store.workspace.id,
|
||||
pageId: host.std.store.id,
|
||||
workspaceId: store.workspace.id,
|
||||
pageId: store.id,
|
||||
};
|
||||
|
||||
const snapshot = await markdownAdapter.toSliceSnapshot(payload);
|
||||
@@ -125,13 +126,17 @@ export const markdownToSnapshot = async (
|
||||
};
|
||||
|
||||
export async function insertFromMarkdown(
|
||||
host: EditorHost,
|
||||
host: EditorHost | undefined,
|
||||
markdown: string,
|
||||
doc: Store,
|
||||
parent?: string,
|
||||
index?: number
|
||||
) {
|
||||
const { snapshot, transformer } = await markdownToSnapshot(markdown, host);
|
||||
const { snapshot, transformer } = await markdownToSnapshot(
|
||||
markdown,
|
||||
doc,
|
||||
host
|
||||
);
|
||||
|
||||
const snapshots = snapshot?.content.flatMap(x => x.children) ?? [];
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { insertFromMarkdown } from '@affine/core/blocksuite/utils';
|
||||
import { encodeAudioBlobToOpusSlices } from '@affine/core/utils/webm-encoding';
|
||||
import { DebugLogger } from '@affine/debug';
|
||||
import { AiJobStatus } from '@affine/graphql';
|
||||
@@ -26,6 +27,18 @@ function sanitizeText(text: string) {
|
||||
return text.replace(/\r/g, '');
|
||||
}
|
||||
|
||||
const colorOptions = [
|
||||
cssVarV2.text.highlight.fg.red,
|
||||
cssVarV2.text.highlight.fg.green,
|
||||
cssVarV2.text.highlight.fg.blue,
|
||||
cssVarV2.text.highlight.fg.yellow,
|
||||
cssVarV2.text.highlight.fg.purple,
|
||||
cssVarV2.text.highlight.fg.orange,
|
||||
cssVarV2.text.highlight.fg.teal,
|
||||
cssVarV2.text.highlight.fg.grey,
|
||||
cssVarV2.text.highlight.fg.magenta,
|
||||
];
|
||||
|
||||
export class AudioAttachmentBlock extends Entity<AttachmentBlockModel> {
|
||||
private readonly refCount$ = new LiveData<number>(0);
|
||||
readonly audioMedia: AudioMedia;
|
||||
@@ -142,7 +155,7 @@ export class AudioAttachmentBlock extends Entity<AttachmentBlockModel> {
|
||||
}
|
||||
const status = await this.transcriptionJob.start();
|
||||
if (status.status === AiJobStatus.claimed) {
|
||||
this.fillTranscriptionResult(status.result);
|
||||
await this.fillTranscriptionResult(status.result);
|
||||
}
|
||||
} catch (error) {
|
||||
track.doc.editor.audioBlock.transcribeRecording({
|
||||
@@ -154,55 +167,77 @@ export class AudioAttachmentBlock extends Entity<AttachmentBlockModel> {
|
||||
}
|
||||
};
|
||||
|
||||
private readonly fillTranscriptionResult = (result: TranscriptionResult) => {
|
||||
private readonly fillTranscriptionResult = async (
|
||||
result: TranscriptionResult
|
||||
) => {
|
||||
this.props.props.caption = result.title ?? '';
|
||||
|
||||
const calloutId = this.props.doc.addBlock(
|
||||
'affine:callout',
|
||||
{
|
||||
emoji: '💬',
|
||||
},
|
||||
this.transcriptionBlock$.value?.id
|
||||
);
|
||||
|
||||
// todo: refactor
|
||||
const speakerToColors = new Map<string, string>();
|
||||
for (const segment of result.segments) {
|
||||
let color = speakerToColors.get(segment.speaker);
|
||||
const colorOptions = [
|
||||
cssVarV2.text.highlight.fg.red,
|
||||
cssVarV2.text.highlight.fg.green,
|
||||
cssVarV2.text.highlight.fg.blue,
|
||||
cssVarV2.text.highlight.fg.yellow,
|
||||
cssVarV2.text.highlight.fg.purple,
|
||||
cssVarV2.text.highlight.fg.orange,
|
||||
cssVarV2.text.highlight.fg.teal,
|
||||
cssVarV2.text.highlight.fg.grey,
|
||||
cssVarV2.text.highlight.fg.magenta,
|
||||
];
|
||||
if (!color) {
|
||||
color = colorOptions[speakerToColors.size % colorOptions.length];
|
||||
speakerToColors.set(segment.speaker, color);
|
||||
}
|
||||
const deltaInserts: DeltaInsert<AffineTextAttributes>[] = [
|
||||
const addCalloutBlock = (emoji: string, title: string) => {
|
||||
const calloutId = this.props.doc.addBlock(
|
||||
'affine:callout',
|
||||
{
|
||||
insert: sanitizeText(segment.start + ' ' + segment.speaker),
|
||||
attributes: {
|
||||
color,
|
||||
bold: true,
|
||||
},
|
||||
emoji,
|
||||
},
|
||||
{
|
||||
insert: ': ' + sanitizeText(segment.transcription),
|
||||
},
|
||||
];
|
||||
this.transcriptionBlock$.value?.id
|
||||
);
|
||||
this.props.doc.addBlock(
|
||||
'affine:paragraph',
|
||||
{
|
||||
text: new Text(deltaInserts),
|
||||
type: 'h6',
|
||||
text: new Text([
|
||||
{
|
||||
insert: title,
|
||||
},
|
||||
]),
|
||||
},
|
||||
calloutId
|
||||
);
|
||||
}
|
||||
return calloutId;
|
||||
};
|
||||
const fillTranscription = (segments: TranscriptionResult['segments']) => {
|
||||
const calloutId = addCalloutBlock('💬', 'Transcript');
|
||||
|
||||
const speakerToColors = new Map<string, string>();
|
||||
for (const segment of segments) {
|
||||
let color = speakerToColors.get(segment.speaker);
|
||||
if (!color) {
|
||||
color = colorOptions[speakerToColors.size % colorOptions.length];
|
||||
speakerToColors.set(segment.speaker, color);
|
||||
}
|
||||
const deltaInserts: DeltaInsert<AffineTextAttributes>[] = [
|
||||
{
|
||||
insert: sanitizeText(segment.start + ' ' + segment.speaker),
|
||||
attributes: {
|
||||
color,
|
||||
bold: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
insert: ': ' + sanitizeText(segment.transcription),
|
||||
},
|
||||
];
|
||||
this.props.doc.addBlock(
|
||||
'affine:paragraph',
|
||||
{
|
||||
text: new Text(deltaInserts),
|
||||
},
|
||||
calloutId
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const fillSummary = async (summary: TranscriptionResult['summary']) => {
|
||||
const calloutId = addCalloutBlock('📑', 'Summary');
|
||||
await insertFromMarkdown(
|
||||
undefined,
|
||||
summary,
|
||||
this.props.doc,
|
||||
calloutId,
|
||||
1
|
||||
);
|
||||
};
|
||||
|
||||
fillTranscription(result.segments);
|
||||
await fillSummary(result.summary);
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user