@ -83,8 +83,9 @@ import {
getPageContent ,
getUserConfigFromBackend ,
saveData ,
// markdown2html,
html2markdown
markdown2html ,
html2markdown ,
getUserAILayoutConfig
} from './utils' ;
import mitt from 'mitt' ;
// 导出为docx插件
@ -384,22 +385,162 @@ class SaveButton extends Plugin {
}
}
// AI 自动排版
function aiformat ( ) {
async function aiformat ( ) {
console . log ( "ai formatting" )
const editor = window . editor ;
cons t doc _content = editor . getData ( )
le t doc _content = editor . getData ( )
console . log ( doc _content ) ;
// TODO 处理html文件
// step 1 - split images and insert text tag
// match <figure ... </figure>
const img _tag = /<figure.*?>(.*?)<\/figure>/g
const img _list = doc _content . match ( img _tag )
console . log ( img _list )
// replace img tag with text tag
if ( img _list ) {
console . log ( "replace img tag" )
for ( let i = 0 ; i < img _list . length ; i ++ ) {
const img = img _list [ i ]
const text = ` <text>图片 ${ i + 1 } </text> `
doc _content = doc _content . replace ( img , text )
}
}
const markdown _content = html2markdown ( doc _content )
console . log ( markdown _content )
// TODO 请求大模型
// step 2 - convert markdown response to html text and setData
// 向后端调用API并接受response
var result = ''
try {
// const response = await fetch('/web_api/admin/ai_layout/layout_generate', {
const response = await fetch ( 'http://localhost:14514/admin/ai_layout/layout_generate' , {
method : 'POST' ,
headers : {
'Content-Type' : 'application/json'
} ,
body : JSON . stringify ( {
doc _content : markdown _content ,
} )
} ) ;
if ( ! response . body ) {
throw new Error ( 'No response body' ) ;
}
const reader = response . body . getReader ( ) ;
const decoder = new TextDecoder ( 'utf-8' ) ;
/* eslint-disable no-constant-condition */
let i = 0 ;
while ( true ) {
const { done , value } = await reader . read ( ) ;
if ( done ) break ;
const slice = decoder . decode ( value , { stream : true } ) ;
result += slice ;
i ++ ;
if ( i % 10 == 0 ) {
// 流式展示ai排版后的内容
const html _content = markdown2html ( result )
editor . setData ( html _content )
}
}
/* eslint-enable no-constant-condition */
} catch ( error ) {
console . error ( 'Error:' , error ) ;
}
// step 3 - recover original images
const markdown _response = result
let html _content = markdown2html ( markdown _response )
console . log ( "html_content:\n\n" , html _content )
// insert original img tag
if ( img _list ) {
console . log ( "insert img tag" )
for ( let i = 0 ; i < img _list . length ; i ++ ) {
const img = img _list [ i ]
const text = ` 图片 ${ i + 1 } `
html _content = html _content . replace ( text , img )
}
}
editor . setData ( html _content )
// step 4 - fetch users config
const user _config = getUserAILayoutConfig ( )
// step 5 - apply title styles of user
// step 6 - apply others styles of user
// get document element
const pageContent = document . querySelector ( "#app > div > div > div.editor-container.editor-container_document-editor.editor-container_include-style > div.editor-container__editor-wrapper > div > div > div" )
// 处理title样式
let title = pageContent . querySelector ( "h1" )
if ( title ) {
title . classList . add ( user _config . titleStyle . option )
}
// 处理一二三级heading样式
for ( let i = 0 ; i < 3 ; i ++ ) {
const heading = pageContent . querySelectorAll ( ` h ${ i + 2 } ` ) ;
if ( heading . length > 0 ) {
const headingTag = user _config . headingStyle . option [ i ] [ 0 ] ;
const headingClass = user _config . headingStyle . option [ i ] [ 1 ] ;
console . log ( "headingTag:\n\n" , headingTag )
console . log ( "headingClass:\n\n" , headingClass )
// for each element
heading . forEach ( ( element ) => {
// reset counter for heading
const parentNode = element . parentNode ;
let currentCounterReset = parentNode . style . counterReset ;
if ( currentCounterReset ) {
// currentCounterReset 不存在该counterreset
if ( currentCounterReset . indexOf ( headingClass + "counter" ) == - 1 ) {
currentCounterReset += " " + headingClass + "counter" ;
}
} else {
currentCounterReset = headingClass + "counter" ;
}
parentNode . style . setProperty ( 'counter-reset' , currentCounterReset ) ;
element . classList . add ( headingTag , headingClass ) ;
} )
}
}
// 处理正文样式
const paragraph = pageContent . querySelectorAll ( "p" )
if ( paragraph . length > 0 ) {
for ( let i = 0 ; i < paragraph . length ; i ++ ) {
const element = paragraph [ i ] ;
for ( let i = 0 ; i < user _config . bodyStyle . option . length ; i ++ ) {
element . classList . add ( user _config . bodyStyle . option [ i ] ) ;
}
}
}
// 处理块引用样式
const blockquote = pageContent . querySelectorAll ( "blockquote" )
if ( blockquote . length > 0 ) {
for ( let i = 0 ; i < blockquote . length ; i ++ ) {
const element = blockquote [ i ] ;
for ( let i = 0 ; i < user _config . blockquote . option . length ; i ++ ) {
element . classList . add ( user _config . blockquote . option [ i ] ) ;
}
}
}
// 处理代码块样式
const pre = pageContent . querySelectorAll ( "pre" )
if ( pre . length > 0 ) {
for ( let i = 0 ; i < pre . length ; i ++ ) {
const element = pre [ i ] ;
for ( let i = 0 ; i < user _config . codeBlockStyle . option . length ; i ++ ) {
element . classList . add ( user _config . codeBlockStyle . option [ i ] ) ;
}
}
}
// 处理列表样式
const ul = pageContent . querySelectorAll ( "ul" )
if ( ul . length > 0 ) {
for ( let i = 0 ; i < ul . length ; i ++ ) {
ul [ i ] . classList . add ( user _config . listStyle . option ) ;
}
}
}
class AiFormat extends Plugin {