Skip to content

Commit 5f5c604

Browse files
authored
Add rich text editor supports for comment widget (#187)
<img width="931" height="321" alt="image" src="https://github.com/user-attachments/assets/2391e35d-38a7-4229-a9cc-aad6b97b6db2" /> Fixes #66 Fixes #102 Fixes #142 ```release-note 评论输入框支持富文本内容 ```
1 parent b4a419d commit 5f5c604

File tree

20 files changed

+3749
-365
lines changed

20 files changed

+3749
-365
lines changed

.gitignore

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,9 +62,6 @@ nbdist/
6262
*.tar.gz
6363
*.rar
6464

65-
### VSCode
66-
.vscode
67-
6865
### Local file
6966
application-local.yml
7067
application-local.yaml

.vscode/settings.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"[typescript]": {
3+
"editor.defaultFormatter": "biomejs.biome",
4+
"editor.codeActionsOnSave": {
5+
"source.fixAll.biome": "explicit",
6+
"source.organizeImports.biome": "explicit"
7+
}
8+
}
9+
}

package.json

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,18 @@
3030
"husky": "^9.1.7",
3131
"lint-staged": "^16.1.2",
3232
"typescript": "~5.3.3",
33-
"vite": "^5.1.4"
33+
"vite": "^7.0.6",
34+
"vite-plugin-dts": "^4.5.4"
3435
},
35-
"packageManager": "[email protected]+sha512.d615db246fe70f25dcfea6d8d73dee782ce23e2245e3c4f6f888249fb568149318637dca73c2c5c8ef2a4ca0d5657fb9567188bfab47f566d1ee6ce987815c39"
36+
"packageManager": "[email protected]+sha512.d615db246fe70f25dcfea6d8d73dee782ce23e2245e3c4f6f888249fb568149318637dca73c2c5c8ef2a4ca0d5657fb9567188bfab47f566d1ee6ce987815c39",
37+
"pnpm": {
38+
"overrides": {
39+
"vite": "npm:[email protected]"
40+
}
41+
},
42+
"dependencies": {
43+
"@tiptap/extensions": "^3.0.9",
44+
"shiki": "^3.9.2",
45+
"tiptap-extension-code-block-shiki": "^0.5.1"
46+
}
3647
}

packages/comment-widget/package.json

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,24 +27,29 @@
2727
"var.css"
2828
],
2929
"scripts": {
30-
"build": "tsc",
31-
"dev": "tsc -w",
32-
"locale:extract": "lit-localize extract",
33-
"locale:build": "lit-localize build"
30+
"build": "vite build",
31+
"dev": "vite build --watch",
32+
"locale:build": "lit-localize build",
33+
"locale:extract": "lit-localize extract"
3434
},
3535
"dependencies": {
3636
"@emoji-mart/data": "^1.2.1",
3737
"@halo-dev/api-client": "^2.21.1",
3838
"@lit/context": "^1.1.6",
3939
"@lit/localize": "^0.12.2",
40+
"@tiptap/core": "^3.0.9",
41+
"@tiptap/pm": "^3.0.9",
42+
"@tiptap/starter-kit": "^3.0.9",
4043
"dayjs": "^1.11.13",
4144
"emoji-mart": "^5.6.0",
4245
"es-toolkit": "^1.39.8",
4346
"javascript-time-ago": "^2.5.11",
4447
"lit": "^3.3.1"
4548
},
4649
"devDependencies": {
50+
"@iconify/json": "^2.2.367",
4751
"@lit/localize-tools": "^0.8.0",
48-
"lit-analyzer": "^2.0.3"
52+
"lit-analyzer": "^2.0.3",
53+
"unocss": "^66.4.1"
4954
}
5055
}

packages/comment-widget/src/base-comment-item.ts

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
import './user-avatar';
22
import { msg } from '@lit/localize';
3-
import { css, html, LitElement } from 'lit';
3+
import { css, html, LitElement, unsafeCSS } from 'lit';
44
import { property } from 'lit/decorators.js';
5+
import { unsafeHTML } from 'lit/directives/unsafe-html.js';
56
import baseStyles from './styles/base';
7+
import contentStyles from './styles/content.css?inline';
68
import varStyles from './styles/var';
79
import { formatDate, timeAgo } from './utils/date';
810

@@ -40,19 +42,27 @@ export class BaseCommentItem extends LitElement {
4042
<div class="item__meta">
4143
${
4244
this.userWebsite
43-
? html`<a class="item__author" target="_blank" href=${this.userWebsite}>
45+
? html`<a
46+
class="item__author"
47+
target="_blank"
48+
href=${this.userWebsite}
49+
>
4450
${this.userDisplayName}
4551
</a>`
4652
: html`<div class="item__author">${this.userDisplayName}</div>`
4753
}
4854
<div class="item__meta-info" title=${formatDate(this.creationTime)}>
4955
${timeAgo(this.creationTime)}
5056
</div>
51-
${!this.approved ? html`<div class="item__meta-info">${msg('Reviewing')}</div>` : ''}
57+
${
58+
!this.approved
59+
? html`<div class="item__meta-info">${msg('Reviewing')}</div>`
60+
: ''
61+
}
5262
</div>
5363
54-
<div class="item__content">
55-
<pre><slot name="pre-content"></slot>${this.content}</pre>
64+
<div class="item__content markdown-body">
65+
<div><slot name="pre-content"></slot>${unsafeHTML(this.content)}</div>
5666
</div>
5767
5868
<div class="item__actions">
@@ -67,6 +77,7 @@ export class BaseCommentItem extends LitElement {
6777
static override styles = [
6878
varStyles,
6979
baseStyles,
80+
unsafeCSS(contentStyles),
7081
css`
7182
.item {
7283
display: flex;

packages/comment-widget/src/base-form.ts

Lines changed: 21 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,14 @@ import {
1414
nameContext,
1515
toastContext,
1616
} from './context';
17-
import './emoji-button';
1817
import './icons/icon-loading';
1918
import { msg } from '@lit/localize';
2019
import type { ToastManager } from './lit-toast';
2120
import baseStyles from './styles/base';
2221
import varStyles from './styles/var';
2322
import type { ConfigMapData } from './types';
23+
import './comment-editor';
24+
import type { CommentEditor } from './comment-editor';
2425

2526
export class BaseForm extends LitElement {
2627
@consume({ context: baseUrlContext })
@@ -64,6 +65,8 @@ export class BaseForm extends LitElement {
6465

6566
textareaRef: Ref<HTMLTextAreaElement> = createRef<HTMLTextAreaElement>();
6667

68+
editorRef: Ref<CommentEditor> = createRef<CommentEditor>();
69+
6770
get customAccount() {
6871
return JSON.parse(
6972
localStorage.getItem('halo-comment-custom-account') || '{}'
@@ -168,14 +171,6 @@ export class BaseForm extends LitElement {
168171
target.style.height = `${target.scrollHeight}px`;
169172
}
170173

171-
onEmojiSelect(e: CustomEvent) {
172-
const data = e.detail;
173-
if (this.textareaRef.value) {
174-
this.textareaRef.value.value += data.native;
175-
this.textareaRef.value.focus();
176-
}
177-
}
178-
179174
onKeydown(e: KeyboardEvent) {
180175
if (e.key === 'Enter' && (e.metaKey || e.ctrlKey)) {
181176
const form = this.shadowRoot?.querySelector('form');
@@ -197,15 +192,7 @@ export class BaseForm extends LitElement {
197192
override render() {
198193
return html`
199194
<form class="form" @submit="${this.onSubmit}">
200-
<textarea
201-
class="form__editor"
202-
${ref(this.textareaRef)}
203-
placeholder=${msg('Write a comment')}
204-
rows="4"
205-
name="content"
206-
required
207-
@input=${this.onContentInput}
208-
></textarea>
195+
<comment-editor ${ref(this.editorRef)}></comment-editor>
209196
210197
${
211198
!this.currentUser && this.allowAnonymousComments
@@ -269,7 +256,6 @@ export class BaseForm extends LitElement {
269256
: ''
270257
}
271258
272-
<emoji-button @emoji-select=${this.onEmojiSelect}></emoji-button>
273259
<button
274260
.disabled=${this.submitting}
275261
type="submit"
@@ -299,8 +285,21 @@ export class BaseForm extends LitElement {
299285
}
300286

301287
private debouncedSubmit = debounce((data: Record<string, unknown>) => {
288+
const content = this.editorRef.value?.editor?.getHTML() || '';
289+
const characterCount =
290+
this.editorRef.value?.editor?.storage.characterCount.characters();
291+
292+
if (!characterCount) {
293+
this.toastManager?.warn(msg('Please enter content'));
294+
this.editorRef.value?.setFocus();
295+
return;
296+
}
297+
302298
const event = new CustomEvent('submit', {
303-
detail: data,
299+
detail: {
300+
...data,
301+
content,
302+
},
304303
});
305304
this.dispatchEvent(event);
306305
}, 300);
@@ -327,10 +326,12 @@ export class BaseForm extends LitElement {
327326
resetForm() {
328327
const form = this.shadowRoot?.querySelector('form');
329328
form?.reset();
329+
this.editorRef.value?.reset();
330330
}
331331

332332
setFocus() {
333333
this.textareaRef.value?.focus();
334+
this.editorRef.value?.setFocus();
334335
}
335336

336337
static override styles = [

0 commit comments

Comments
 (0)