Compare commits

..

2 Commits

Author SHA1 Message Date
CrescentLeaf
185f5480fa feat: BlockQoute display in client 2025-12-05 21:27:05 +08:00
CrescentLeaf
b4a60bcbe2 ui: 以正确方式编写 chat-file 的自定义元素代码 2025-12-05 20:49:40 +08:00
5 changed files with 74 additions and 11 deletions

View File

@@ -11,6 +11,7 @@ import './ui/custom-elements/chat-file.ts'
import './ui/custom-elements/chat-text.ts' import './ui/custom-elements/chat-text.ts'
import './ui/custom-elements/chat-mention.ts' import './ui/custom-elements/chat-mention.ts'
import './ui/custom-elements/chat-text-container.ts' import './ui/custom-elements/chat-text-container.ts'
import './ui/custom-elements/chat-quote.ts'
import App from './ui/App.tsx' import App from './ui/App.tsx'
import AppMobile from './ui/AppMobile.tsx' import AppMobile from './ui/AppMobile.tsx'

View File

@@ -49,6 +49,7 @@ const sanitizeConfig = {
'chat-text', 'chat-text',
"chat-link", "chat-link",
'chat-mention', 'chat-mention',
'chat-quote',
], ],
ALLOWED_ATTR: [ ALLOWED_ATTR: [
'underline', 'underline',
@@ -64,6 +65,9 @@ const sanitizeConfig = {
const markedInstance = new marked.Marked({ const markedInstance = new marked.Marked({
renderer: { renderer: {
blockquote({ text }) {
return `<chat-quote>${escapeHTML(text)}</chat-quote>`
},
text({ text }) { text({ text }) {
return `<chat-text>${escapeHTML(text)}</chat-text>` return `<chat-text>${escapeHTML(text)}</chat-text>`
}, },

View File

@@ -25,6 +25,7 @@ function prettyFlatParsedMessage(html: string) {
const textElementTags = [ const textElementTags = [
'chat-text', 'chat-text',
'chat-mention', 'chat-mention',
'chat-quote',
] ]
function checkContinuousElement(tagName: string) { function checkContinuousElement(tagName: string) {
/* console.log('shangyige ', lastElementType) /* console.log('shangyige ', lastElementType)

View File

@@ -1,27 +1,39 @@
import { $ } from 'mdui/jq' import { $ } from 'mdui/jq'
customElements.define('chat-file', class extends HTMLElement { customElements.define('chat-file', class extends HTMLElement {
static observedAttributes = ['href', 'name']
declare anchor: HTMLAnchorElement
declare span: HTMLSpanElement
constructor() { constructor() {
super() super()
this.attachShadow({ mode: 'open' })
}
update() {
if (this.anchor == null) return
this.anchor.href = $(this).attr('href') as string
this.anchor.download = $(this).attr('href') as string
this.span.textContent = $(this).attr("name") as string
}
attributeChangedCallback(_name: string, _oldValue: unknown, _newValue: unknown) {
this.update()
} }
connectedCallback() { connectedCallback() {
const e = new DOMParser().parseFromString(` this.anchor = new DOMParser().parseFromString(`
<a style="width: 100%;height: 100%;"> <a style="width: 100%;height: 100%;">
<mdui-card clickable style="display: flex;align-items: center;box-shadow: inherit;border-radius: inherit;"> <mdui-card clickable style="display: flex;align-items: center;box-shadow: inherit;border-radius: inherit;">
<mdui-icon name="insert_drive_file" style="margin: 13px;font-size: 34px;"></mdui-icon> <mdui-icon name="insert_drive_file" style="margin: 13px;font-size: 34px;"></mdui-icon>
<span style="margin-right: 13px; word-wrap: break-word; word-break:break-all;white-space:normal; max-width :100%;"></span> <span style="margin-right: 13px; word-wrap: break-word; word-break:break-all;white-space:normal; max-width :100%;"></span>
</mdui-card> </mdui-card>
</a>`, 'text/html').body.firstChild as HTMLElement </a>`, 'text/html').body.firstChild as HTMLAnchorElement
$(e).find('span').text($(this).attr("name")) this.span = $(this.anchor).find('span').get(0)
const href = $(this).attr('href') this.anchor.style.textDecoration = 'none'
$(e).attr('href', href) this.anchor.style.color = 'inherit'
$(e).attr('target', '_blank') this.anchor.onclick = (e) => {
$(e).attr('download', href)
e.style.textDecoration = 'none'
e.style.color = 'inherit'
e.onclick = (e) => {
e.stopPropagation() e.stopPropagation()
} }
this.appendChild(e) this.shadowRoot!.appendChild(this.anchor)
this.update()
} }
}) })

View File

@@ -0,0 +1,45 @@
import { $ } from 'mdui/jq'
customElements.define('chat-quote', class extends HTMLElement {
declare container: HTMLAnchorElement
declare span: HTMLSpanElement
declare ellipsis: boolean
constructor() {
super()
this.attachShadow({ mode: 'open' })
}
update() {
if (this.container == null) return
this.span.textContent = this.textContent
this.updateStyle()
}
updateStyle() {
this.span.style.whiteSpace = this.ellipsis ? 'nowrap' : 'pre-wrap'
this.span.style.overflow = this.ellipsis ? 'hidden' : ''
this.span.style.textOverflow = this.ellipsis ? 'ellipsis' : ''
}
attributeChangedCallback(_name: string, _oldValue: unknown, _newValue: unknown) {
this.update()
}
connectedCallback() {
this.container = new DOMParser().parseFromString(`
<a style="width: 100%;height: 100%; color: rgb(var(--mdui-color-primary));" href="javascript:void(0)">
<span style="display: block; word-wrap: break-word; word-break:break-all;white-space:normal; max-width :100%;"></span>
</a>`, 'text/html').body.firstChild as HTMLAnchorElement
this.span = $(this.container).find('span').get(0)
this.container.style.textDecoration = 'none'
this.span.style.fontSynthesis = 'style weight'
this.container.onclick = (e) => {
this.ellipsis = !this.ellipsis
this.updateStyle()
e.stopPropagation()
}
this.ellipsis = true
this.shadowRoot!.appendChild(this.container)
this.update()
}
})