feat: support markdown mermaid (#117)

This commit is contained in:
Zhang Minghan 2024-03-16 10:33:10 +08:00
parent 93c18bb34c
commit 865d5d056f
13 changed files with 500 additions and 29 deletions

View File

@ -25,7 +25,7 @@ English | [简体中文](https://github.com/Deeptrain-Community/chatnio/blob/mas
- Supports drawing with DALL-E models
- Supports **Imagine** / **Upscale** / **Variant** / **Reroll** operations for Midjourney / Niji models
![Midjourney](/screenshot/code.png)
2. **Markdown Support / Theme Switching Support**, supports light and dark modes, code highlighting, LaTeX formulas, tables, progress bars, Virtual Message, etc.
2. **Markdown Support / Theme Switching Support**, supports light and dark modes, code highlighting, Mermaid, LaTeX formulas, tables, progress bars, Virtual Message, etc.
![Markdown Message](/screenshot/latex.jpg)
3. **Support for Message Menu**, including re-answering, copying messages, using messages, editing messages, deleting messages, saving as a file, and more operations...
4. **Support for Multi-platform Adaptation**, supports PWA apps, desktop platforms (desktop is based on [Tauri](https://github.com/tauri-apps/tauri)).

View File

@ -25,7 +25,7 @@ _🚀 **Next Generation AI One-Stop Solution**_
- 支持 DALL-E 模型绘图
- 支持 Midjourney / Niji 模型的 **Imagine** / **Upscale** / **Variant** / **Reroll** 操作
![Midjourney 绘图](/screenshot/code.png)
2. **丰富 Markdown 支持和主题切换**, 支持明暗模式, 代码高亮, LaTeX 公式, 表格, 进度条, Virtual Message 等
2. **丰富 Markdown 支持和主题切换**, 支持明暗模式, 代码高亮, Mermaid, LaTeX 公式, 表格, 进度条, Virtual Message 等
![Markdown 消息](/screenshot/latex.jpg)
3. **支持消息菜单**, 支持重新回答, 复制消息, 使用消息, 编辑消息, 删除消息, 保存为文件等操作
![Vision 支持](/screenshot/vision.png)

View File

@ -48,6 +48,7 @@
"localforage": "^1.10.0",
"lucide-react": "^0.309.0",
"match-sorter": "^6.3.1",
"mermaid": "^10.9.0",
"next-themes": "^0.2.1",
"react": "^18.2.0",
"react-beautiful-dnd": "^13.1.1",
@ -66,6 +67,7 @@
"sort-by": "^1.2.0",
"tailwind-merge": "^1.14.0",
"tailwindcss-animate": "^1.0.7",
"use-debounce": "^10.0.0",
"vaul": "^0.9.0",
"workbox-window": "^7.0.0"
},

357
app/pnpm-lock.yaml generated
View File

@ -113,6 +113,9 @@ dependencies:
match-sorter:
specifier: ^6.3.1
version: 6.3.1
mermaid:
specifier: ^10.9.0
version: 10.9.0
next-themes:
specifier: ^0.2.1
version: 0.2.1(next@14.0.4)(react-dom@18.2.0)(react@18.2.0)
@ -167,6 +170,9 @@ dependencies:
tailwindcss-animate:
specifier: ^1.0.7
version: 1.0.7(tailwindcss@3.3.5)
use-debounce:
specifier: ^10.0.0
version: 10.0.0(react@18.2.0)
vaul:
specifier: ^0.9.0
version: 0.9.0(@types/react-dom@18.2.14)(@types/react@18.2.33)(react-dom@18.2.0)(react@18.2.0)
@ -260,6 +266,10 @@ packages:
regenerator-runtime: 0.14.0
dev: false
/@braintree/sanitize-url@6.0.4:
resolution: {integrity: sha512-s3jaWicZd0pkP0jf5ysyHUI/RE7MHos6qlToFcGWXVp+ykHOy77OUMrfbgJ9it2C5bow7OIQwYYaHjk9XlBQ2A==}
dev: false
/@esbuild/android-arm64@0.18.20:
resolution: {integrity: sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==}
engines: {node: '>=12'}
@ -2340,6 +2350,10 @@ packages:
resolution: {integrity: sha512-P2dlU/q51fkOc/Gfl3Ul9kicV7l+ra934qBFXCFhrZMOL6du1TM0pm1ThYvENukyOn5h9v+yMJ9Fn5JK4QozrQ==}
dev: false
/@types/d3-scale-chromatic@3.0.3:
resolution: {integrity: sha512-laXM4+1o5ImZv3RpFAsTRn3TEkzqkytiOY0Dz0sq5cnd1dtNlk6sHLon4OvqaiJb28T0S/TdsBI3Sjsy+keJrw==}
dev: false
/@types/d3-scale@4.0.8:
resolution: {integrity: sha512-gkK1VVTr5iNiYJ7vWDI+yUFFlszhNMtVeneJ6lUTKPjprsvLLI9/tgEGiXJOnlINJA8FyA88gfnQsHbybVZrYQ==}
dependencies:
@ -3099,6 +3113,11 @@ packages:
resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==}
engines: {node: '>= 6'}
/commander@7.2.0:
resolution: {integrity: sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==}
engines: {node: '>= 10'}
dev: false
/commander@8.3.0:
resolution: {integrity: sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==}
engines: {node: '>= 12'}
@ -3121,6 +3140,12 @@ packages:
is-what: 3.14.1
dev: true
/cose-base@1.0.3:
resolution: {integrity: sha512-s9whTXInMSgAp/NVXVNuVxVKzGH2qck3aQlVHxDCdAEPgtMKwc4Wq6/QKhgdEdgbLSi9rBTAcPoRa6JpiG4ksg==}
dependencies:
layout-base: 1.0.2
dev: false
/cross-spawn@7.0.3:
resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==}
engines: {node: '>= 8'}
@ -3159,6 +3184,29 @@ packages:
/csstype@3.1.2:
resolution: {integrity: sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==}
/cytoscape-cose-bilkent@4.1.0(cytoscape@3.28.1):
resolution: {integrity: sha512-wgQlVIUJF13Quxiv5e1gstZ08rnZj2XaLHGoFMYXz7SkNfCDOOteKBE6SYRfA9WxxI/iBc3ajfDoc6hb/MRAHQ==}
peerDependencies:
cytoscape: ^3.2.0
dependencies:
cose-base: 1.0.3
cytoscape: 3.28.1
dev: false
/cytoscape@3.28.1:
resolution: {integrity: sha512-xyItz4O/4zp9/239wCcH8ZcFuuZooEeF8KHRmzjDfGdXsj3OG9MFSMA0pJE0uX3uCN/ygof6hHf4L7lst+JaDg==}
engines: {node: '>=0.10'}
dependencies:
heap: 0.2.7
lodash: 4.17.21
dev: false
/d3-array@2.12.1:
resolution: {integrity: sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ==}
dependencies:
internmap: 1.0.1
dev: false
/d3-array@3.2.4:
resolution: {integrity: sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==}
engines: {node: '>=12'}
@ -3166,21 +3214,109 @@ packages:
internmap: 2.0.3
dev: false
/d3-axis@3.0.0:
resolution: {integrity: sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw==}
engines: {node: '>=12'}
dev: false
/d3-brush@3.0.0:
resolution: {integrity: sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==}
engines: {node: '>=12'}
dependencies:
d3-dispatch: 3.0.1
d3-drag: 3.0.0
d3-interpolate: 3.0.1
d3-selection: 3.0.0
d3-transition: 3.0.1(d3-selection@3.0.0)
dev: false
/d3-chord@3.0.1:
resolution: {integrity: sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g==}
engines: {node: '>=12'}
dependencies:
d3-path: 3.1.0
dev: false
/d3-color@3.1.0:
resolution: {integrity: sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==}
engines: {node: '>=12'}
dev: false
/d3-contour@4.0.2:
resolution: {integrity: sha512-4EzFTRIikzs47RGmdxbeUvLWtGedDUNkTcmzoeyg4sP/dvCexO47AaQL7VKy/gul85TOxw+IBgA8US2xwbToNA==}
engines: {node: '>=12'}
dependencies:
d3-array: 3.2.4
dev: false
/d3-delaunay@6.0.4:
resolution: {integrity: sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A==}
engines: {node: '>=12'}
dependencies:
delaunator: 5.0.1
dev: false
/d3-dispatch@3.0.1:
resolution: {integrity: sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==}
engines: {node: '>=12'}
dev: false
/d3-drag@3.0.0:
resolution: {integrity: sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==}
engines: {node: '>=12'}
dependencies:
d3-dispatch: 3.0.1
d3-selection: 3.0.0
dev: false
/d3-dsv@3.0.1:
resolution: {integrity: sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==}
engines: {node: '>=12'}
hasBin: true
dependencies:
commander: 7.2.0
iconv-lite: 0.6.3
rw: 1.3.3
dev: false
/d3-ease@3.0.1:
resolution: {integrity: sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==}
engines: {node: '>=12'}
dev: false
/d3-fetch@3.0.1:
resolution: {integrity: sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==}
engines: {node: '>=12'}
dependencies:
d3-dsv: 3.0.1
dev: false
/d3-force@3.0.0:
resolution: {integrity: sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==}
engines: {node: '>=12'}
dependencies:
d3-dispatch: 3.0.1
d3-quadtree: 3.0.1
d3-timer: 3.0.1
dev: false
/d3-format@3.1.0:
resolution: {integrity: sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==}
engines: {node: '>=12'}
dev: false
/d3-geo@3.1.1:
resolution: {integrity: sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q==}
engines: {node: '>=12'}
dependencies:
d3-array: 3.2.4
dev: false
/d3-hierarchy@3.1.2:
resolution: {integrity: sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==}
engines: {node: '>=12'}
dev: false
/d3-interpolate@3.0.1:
resolution: {integrity: sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==}
engines: {node: '>=12'}
@ -3188,11 +3324,45 @@ packages:
d3-color: 3.1.0
dev: false
/d3-path@1.0.9:
resolution: {integrity: sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg==}
dev: false
/d3-path@3.1.0:
resolution: {integrity: sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==}
engines: {node: '>=12'}
dev: false
/d3-polygon@3.0.1:
resolution: {integrity: sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg==}
engines: {node: '>=12'}
dev: false
/d3-quadtree@3.0.1:
resolution: {integrity: sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==}
engines: {node: '>=12'}
dev: false
/d3-random@3.0.1:
resolution: {integrity: sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==}
engines: {node: '>=12'}
dev: false
/d3-sankey@0.12.3:
resolution: {integrity: sha512-nQhsBRmM19Ax5xEIPLMY9ZmJ/cDvd1BG3UVvt5h3WRxKg5zGRbvnteTyWAbzeSvlh3tW7ZEmq4VwR5mB3tutmQ==}
dependencies:
d3-array: 2.12.1
d3-shape: 1.3.7
dev: false
/d3-scale-chromatic@3.1.0:
resolution: {integrity: sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ==}
engines: {node: '>=12'}
dependencies:
d3-color: 3.1.0
d3-interpolate: 3.0.1
dev: false
/d3-scale@4.0.2:
resolution: {integrity: sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==}
engines: {node: '>=12'}
@ -3204,6 +3374,17 @@ packages:
d3-time-format: 4.1.0
dev: false
/d3-selection@3.0.0:
resolution: {integrity: sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==}
engines: {node: '>=12'}
dev: false
/d3-shape@1.3.7:
resolution: {integrity: sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw==}
dependencies:
d3-path: 1.0.9
dev: false
/d3-shape@3.2.0:
resolution: {integrity: sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==}
engines: {node: '>=12'}
@ -3230,6 +3411,74 @@ packages:
engines: {node: '>=12'}
dev: false
/d3-transition@3.0.1(d3-selection@3.0.0):
resolution: {integrity: sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==}
engines: {node: '>=12'}
peerDependencies:
d3-selection: 2 - 3
dependencies:
d3-color: 3.1.0
d3-dispatch: 3.0.1
d3-ease: 3.0.1
d3-interpolate: 3.0.1
d3-selection: 3.0.0
d3-timer: 3.0.1
dev: false
/d3-zoom@3.0.0:
resolution: {integrity: sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==}
engines: {node: '>=12'}
dependencies:
d3-dispatch: 3.0.1
d3-drag: 3.0.0
d3-interpolate: 3.0.1
d3-selection: 3.0.0
d3-transition: 3.0.1(d3-selection@3.0.0)
dev: false
/d3@7.9.0:
resolution: {integrity: sha512-e1U46jVP+w7Iut8Jt8ri1YsPOvFpg46k+K8TpCb0P+zjCkjkPnV7WzfDJzMHy1LnA+wj5pLT1wjO901gLXeEhA==}
engines: {node: '>=12'}
dependencies:
d3-array: 3.2.4
d3-axis: 3.0.0
d3-brush: 3.0.0
d3-chord: 3.0.1
d3-color: 3.1.0
d3-contour: 4.0.2
d3-delaunay: 6.0.4
d3-dispatch: 3.0.1
d3-drag: 3.0.0
d3-dsv: 3.0.1
d3-ease: 3.0.1
d3-fetch: 3.0.1
d3-force: 3.0.0
d3-format: 3.1.0
d3-geo: 3.1.1
d3-hierarchy: 3.1.2
d3-interpolate: 3.0.1
d3-path: 3.1.0
d3-polygon: 3.0.1
d3-quadtree: 3.0.1
d3-random: 3.0.1
d3-scale: 4.0.2
d3-scale-chromatic: 3.1.0
d3-selection: 3.0.0
d3-shape: 3.2.0
d3-time: 3.1.0
d3-time-format: 4.1.0
d3-timer: 3.0.1
d3-transition: 3.0.1(d3-selection@3.0.0)
d3-zoom: 3.0.0
dev: false
/dagre-d3-es@7.0.10:
resolution: {integrity: sha512-qTCQmEhcynucuaZgY5/+ti3X/rnszKZhEQH/ZdWdtP1tA/y3VoHJzcVrO9pjjJCNpigfscAtoUB5ONcd2wNn0A==}
dependencies:
d3: 7.9.0
lodash-es: 4.17.21
dev: false
/date-fns@2.30.0:
resolution: {integrity: sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==}
engines: {node: '>=0.11'}
@ -3237,6 +3486,10 @@ packages:
'@babel/runtime': 7.23.2
dev: false
/dayjs@1.11.10:
resolution: {integrity: sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ==}
dev: false
/debug@4.3.4:
resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==}
engines: {node: '>=6.0'}
@ -3262,6 +3515,12 @@ packages:
resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==}
dev: true
/delaunator@5.0.1:
resolution: {integrity: sha512-8nvh+XBe96aCESrGOqMp/84b13H9cdKbG5P2ejQCh4d4sK9RL4371qou9drQjMhvnPmhWl5hnmqbEE0fXr9Xnw==}
dependencies:
robust-predicates: 3.0.2
dev: false
/delayed-stream@1.0.0:
resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==}
engines: {node: '>=0.4.0'}
@ -3333,6 +3592,10 @@ packages:
domelementtype: 2.3.0
dev: true
/dompurify@3.0.9:
resolution: {integrity: sha512-uyb4NDIvQ3hRn6NiC+SIFaP4mJ/MdXlvtunaqK9Bn6dD3RuB/1S/gasEjDHD8eiaqdSael2vBv+hOs7Y+jhYOQ==}
dev: false
/domutils@2.8.0:
resolution: {integrity: sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==}
dependencies:
@ -3374,6 +3637,10 @@ packages:
resolution: {integrity: sha512-g73CJB/rMPjdxpiNJYmV1homV7mLVUNe/R0z/HhqMfpjkt58FpYmkTjbtuv3zymdbTTJ+VOEqe1c+lkTjSOhmQ==}
dev: true
/elkjs@0.9.2:
resolution: {integrity: sha512-2Y/RaA1pdgSHpY0YG4TYuYCD2wh97CRvu22eLG3Kz0pgQ/6KbIFTxsTnDc4MH/6hFlg2L/9qXrDMG0nMjP63iw==}
dev: false
/emoji-picker-react@4.7.18(react@18.2.0):
resolution: {integrity: sha512-ZYTUl9KdSFfrdmPmAhcVwSv6/fBuhIw6LxqOJ8jz5min8sezjEArGs25LGjf6VABNE4895HBsuJ5uVKIb0lJuw==}
engines: {node: '>=10'}
@ -3961,6 +4228,10 @@ packages:
hasBin: true
dev: true
/heap@0.2.7:
resolution: {integrity: sha512-2bsegYkkHO+h/9MGbn6KWcE45cHZgPANo5LXF7EvWdT0yT2EguSVO1nDgU5c8+ZOPwp2vMNa7YFsJhVcDR9Sdg==}
dev: false
/highlight.js@10.7.3:
resolution: {integrity: sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==}
dev: false
@ -4011,8 +4282,6 @@ packages:
requiresBuild: true
dependencies:
safer-buffer: 2.1.2
dev: true
optional: true
/ignore@5.2.4:
resolution: {integrity: sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==}
@ -4061,6 +4330,10 @@ packages:
resolution: {integrity: sha512-7NXolsK4CAS5+xvdj5OMMbI962hU/wvwoxk+LWR9Ek9bVtyuuYScDN6eS0rUm6TxApFpw7CX1o4uJzcd4AyD3Q==}
dev: false
/internmap@1.0.1:
resolution: {integrity: sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw==}
dev: false
/internmap@2.0.3:
resolution: {integrity: sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==}
engines: {node: '>=12'}
@ -4211,11 +4484,19 @@ packages:
json-buffer: 3.0.1
dev: true
/khroma@2.1.0:
resolution: {integrity: sha512-Ls993zuzfayK269Svk9hzpeGUKob/sIgZzyHYdjQoAdQetRKpOLj+k/QQQ/6Qi0Yz65mlROrfd+Ev+1+7dz9Kw==}
dev: false
/kleur@4.1.5:
resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==}
engines: {node: '>=6'}
dev: false
/layout-base@1.0.2:
resolution: {integrity: sha512-8h2oVEZNktL4BH2JCOI90iD1yXwL6iNW7KcCKT2QZgQJR2vbqDsldCTPRU9NifTCqHZci57XvQQ15YTu+sTYPg==}
dev: false
/less-loader@11.1.3(less@4.2.0)(webpack@5.89.0):
resolution: {integrity: sha512-A5b7O8dH9xpxvkosNrP0dFp2i/dISOJa9WwGF3WJflfqIERE2ybxh1BFDj5CovC2+jCE4M354mk90hN6ziXlVw==}
engines: {node: '>= 14.15.0'}
@ -4284,6 +4565,10 @@ packages:
p-locate: 5.0.0
dev: true
/lodash-es@4.17.21:
resolution: {integrity: sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==}
dev: false
/lodash.merge@4.6.2:
resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==}
dev: true
@ -4533,6 +4818,33 @@ packages:
resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==}
engines: {node: '>= 8'}
/mermaid@10.9.0:
resolution: {integrity: sha512-swZju0hFox/B/qoLKK0rOxxgh8Cf7rJSfAUc1u8fezVihYMvrJAS45GzAxTVf4Q+xn9uMgitBcmWk7nWGXOs/g==}
dependencies:
'@braintree/sanitize-url': 6.0.4
'@types/d3-scale': 4.0.8
'@types/d3-scale-chromatic': 3.0.3
cytoscape: 3.28.1
cytoscape-cose-bilkent: 4.1.0(cytoscape@3.28.1)
d3: 7.9.0
d3-sankey: 0.12.3
dagre-d3-es: 7.0.10
dayjs: 1.11.10
dompurify: 3.0.9
elkjs: 0.9.2
katex: 0.16.9
khroma: 2.1.0
lodash-es: 4.17.21
mdast-util-from-markdown: 1.3.1
non-layered-tidy-tree-layout: 2.0.2
stylis: 4.3.1
ts-dedent: 2.2.0
uuid: 9.0.1
web-worker: 1.3.0
transitivePeerDependencies:
- supports-color
dev: false
/micromark-core-commonmark@1.1.0:
resolution: {integrity: sha512-BgHO1aRbolh2hcrzL2d1La37V0Aoz73ymF8rAcKnohLy93titmv62E0gP8Hrx9PKcKrqCZ1BbLGbP3bEhoXYlw==}
dependencies:
@ -4974,6 +5286,10 @@ packages:
resolution: {integrity: sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==}
dev: true
/non-layered-tidy-tree-layout@2.0.2:
resolution: {integrity: sha512-gkXMxRzUH+PB0ax9dUN0yYF0S25BqeAYqhgMaLUFmpXLEk7Fcu8f4emJuOAY0V8kjDICxROIKsTAKsV/v355xw==}
dev: false
/normalize-path@3.0.0:
resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==}
engines: {node: '>=0.10.0'}
@ -5724,6 +6040,10 @@ packages:
glob: 7.2.3
dev: true
/robust-predicates@3.0.2:
resolution: {integrity: sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg==}
dev: false
/rollup@3.29.4:
resolution: {integrity: sha512-oWzmBZwvYrU0iJHtDmhsm662rC15FRXmcjCk1xD771dFDx5jJ02ufAQQTn0etB2emNk4J9EZg/yWKpsn9BWGRw==}
engines: {node: '>=14.18.0', npm: '>=8.0.0'}
@ -5737,6 +6057,10 @@ packages:
dependencies:
queue-microtask: 1.2.3
/rw@1.3.3:
resolution: {integrity: sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==}
dev: false
/sade@1.8.1:
resolution: {integrity: sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==}
engines: {node: '>=6'}
@ -5751,8 +6075,6 @@ packages:
/safer-buffer@2.1.2:
resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==}
requiresBuild: true
dev: true
optional: true
/sax@1.3.0:
resolution: {integrity: sha512-0s+oAmw9zLl1V1cS9BtZN7JAd0cW5e0QH4W3LWEK6a4LaLEA2OTpGYWDY+6XasBLtz6wkm3u1xRw95mRuJ59WA==}
@ -5893,6 +6215,10 @@ packages:
react: 18.2.0
dev: false
/stylis@4.3.1:
resolution: {integrity: sha512-EQepAV+wMsIaGVGX1RECzgrcqRRU/0sYOHkeLsZ3fzHaHXZy4DaOOX0vOlGQdlsjkh3mFHAIlVimpwAs4dslyQ==}
dev: false
/sucrase@3.34.0:
resolution: {integrity: sha512-70/LQEZ07TEcxiU2dz51FKaE6hCTWC6vr7FOk3Gr0U60C3shtAN+H+BFr9XlYe5xqf3RA8nrc+VIwzCfnxuXJw==}
engines: {node: '>=8'}
@ -6063,6 +6389,11 @@ packages:
typescript: 5.2.2
dev: true
/ts-dedent@2.2.0:
resolution: {integrity: sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ==}
engines: {node: '>=6.10'}
dev: false
/ts-interface-checker@0.1.13:
resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==}
@ -6247,6 +6578,15 @@ packages:
tslib: 2.6.2
dev: false
/use-debounce@10.0.0(react@18.2.0):
resolution: {integrity: sha512-XRjvlvCB46bah9IBXVnq/ACP2lxqXyZj0D9hj4K5OzNroMDpTEBg8Anuh1/UfRTRs7pLhQ+RiNxxwZu9+MVl1A==}
engines: {node: '>= 16.0.0'}
peerDependencies:
react: '>=16.8.0'
dependencies:
react: 18.2.0
dev: false
/use-memo-one@1.1.3(react@18.2.0):
resolution: {integrity: sha512-g66/K7ZQGYrI6dy8GLpVcMsBp4s17xNkYJVSMvTEevGy3nDxHOfE6z8BVE22+5G5x7t3+bhzrlTDB7ObrEE0cQ==}
peerDependencies:
@ -6282,6 +6622,11 @@ packages:
/util-deprecate@1.0.2:
resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
/uuid@9.0.1:
resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==}
hasBin: true
dev: false
/uvu@0.5.6:
resolution: {integrity: sha512-+g8ENReyr8YsOc6fv/NVJs2vFdHBnBNdfE49rshrTzDWOlUx4Gq7KOS2GD8eqhy2j+Ejq29+SbKH8yjkAqXqoA==}
engines: {node: '>=8'}
@ -6444,6 +6789,10 @@ packages:
resolution: {integrity: sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==}
dev: false
/web-worker@1.3.0:
resolution: {integrity: sha512-BSR9wyRsy/KOValMgd5kMyr3JzpdeoR9KVId8u5GVlTTAtNChlsE4yTxeY7zMdNSyOmoKBv8NH2qeRY9Tg+IaA==}
dev: false
/webpack-sources@3.2.3:
resolution: {integrity: sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==}
engines: {node: '>=10.13.0'}

View File

@ -658,7 +658,6 @@
.chat-input {
flex-shrink: 0;
height: max-content;
width: 100%;
overflow: hidden;
padding: 0.5rem 1rem 1rem;

View File

@ -16,6 +16,7 @@ type MarkdownProps = {
className?: string;
acceptHtml?: boolean;
codeStyle?: string;
loading?: boolean;
};
function MarkdownContent({
@ -23,6 +24,7 @@ function MarkdownContent({
className,
acceptHtml,
codeStyle,
loading,
}: MarkdownProps) {
useEffect(() => {
document.querySelectorAll(".file-instance").forEach((el) => {
@ -41,7 +43,9 @@ function MarkdownContent({
return {
p: Label,
a: Link,
code: (props: CodeProps) => <Code {...props} codeStyle={codeStyle} />,
code: (props: CodeProps) => (
<Code {...props} loading={loading} codeStyle={codeStyle} />
),
};
}, [codeStyle]);
@ -62,6 +66,7 @@ function Markdown({
acceptHtml,
codeStyle,
className,
loading,
}: MarkdownProps) {
// memoize the component
return useMemo(
@ -71,9 +76,10 @@ function Markdown({
acceptHtml={acceptHtml}
codeStyle={codeStyle}
className={className}
loading={loading}
/>
),
[children, acceptHtml, codeStyle, className],
[children, acceptHtml, codeStyle, className, loading],
);
}

View File

@ -272,7 +272,11 @@ function MessageContent({
</div>
<div className={`message-content`}>
{message.content.length ? (
<Markdown children={message.content} acceptHtml={false} />
<Markdown
loading={message.end === false}
children={message.content}
acceptHtml={false}
/>
) : message.end === true ? (
<CircleSlash className={`h-5 w-5 m-1`} />
) : (

View File

@ -45,6 +45,7 @@ import { Separator } from "@/components/ui/separator.tsx";
import { goAuth } from "@/utils/app.ts";
import Avatar from "@/components/Avatar.tsx";
import { cn } from "@/components/ui/lib/utils.ts";
import { getNumberMemory } from "@/utils/memory.ts";
type Operation = {
target: ConversationInstance | null;
@ -343,14 +344,23 @@ function SidebarMenu() {
}
function SideBar() {
const { t } = useTranslation();
const { refresh } = useConversationActions();
const { refresh, toggle } = useConversationActions();
const current = useSelector(selectCurrent);
const open = useSelector(selectMenu);
const auth = useSelector(selectAuthenticated);
const [operateConversation, setOperateConversation] = useState<Operation>({
target: null,
type: "",
});
useEffectAsync(async () => await refresh(true), []);
useEffectAsync(async () => {
const resp = await refresh();
const store = getNumberMemory("history_conversation", -1);
if (current === store) return; // no need to dispatch current
if (store === -1) return; // -1 is default, no need to dispatch
if (!resp.map((item) => item.id).includes(store)) return; // not in the list, no need to dispatch
await toggle(store);
}, []);
return (
<div className={cn("sidebar", open && "open")}>

View File

@ -1,11 +1,12 @@
import { parseFile } from "@/components/plugins/file.tsx";
import { parseProgressbar } from "@/components/plugins/progress.tsx";
import { MarkdownFile } from "@/components/plugins/file.tsx";
import { MarkdownProgressbar } from "@/components/plugins/progress.tsx";
import { cn } from "@/components/ui/lib/utils.ts";
import { copyClipboard } from "@/utils/dom.ts";
import { Check, Copy } from "lucide-react";
import { LightAsync as SyntaxHighlighter } from "react-syntax-highlighter";
import { atomOneDark as style } from "react-syntax-highlighter/dist/esm/styles/hljs";
import React from "react";
import React, { useMemo } from "react";
import { MarkdownMermaid } from "@/components/plugins/mermaid.tsx";
const LanguageMap: Record<string, string> = {
html: "htmlbars",
@ -21,20 +22,24 @@ export type CodeProps = {
className?: string;
children: React.ReactNode;
codeStyle?: string;
loading?: boolean;
};
export default function ({
function Code({
inline,
className,
children,
loading,
codeStyle,
...props
}: CodeProps) {
const [copied, setCopied] = React.useState(false);
const match = /language-(\w+)/.exec(className || "");
const language = match ? match[1].toLowerCase() : "unknown";
if (language === "file") return parseFile(children);
if (language === "progress") return parseProgressbar(children);
if (language === "file") return <MarkdownFile children={children} />;
if (language === "progress")
return <MarkdownProgressbar children={children} />;
if (language === "mermaid") return <MarkdownMermaid children={children} />;
if (inline)
return (
@ -73,3 +78,25 @@ export default function ({
</div>
);
}
export default function ({
inline,
className,
children,
codeStyle,
loading,
...props
}: CodeProps) {
return useMemo(() => {
return (
<Code
inline={inline}
className={className}
children={children}
codeStyle={codeStyle}
loading={loading}
{...props}
/>
);
}, [inline, className, children, codeStyle, loading, props]);
}

View File

@ -12,7 +12,12 @@ import FileViewer from "@/components/FileViewer.tsx";
* ```
*/
export function parseFile(children: React.ReactNode, acceptDownload?: boolean) {
type MarkdownFileProps = {
children: React.ReactNode;
acceptDownload?: boolean;
};
export function MarkdownFile({ children, acceptDownload }: MarkdownFileProps) {
const data = children?.toString() || "";
const filename = data.split("\n")[0].replace("[[", "").replace("]]", "");
const content = data.replace(`[[${filename}]]\n`, "");

View File

@ -0,0 +1,71 @@
import React, { useEffect } from "react";
import mermaid from "mermaid";
import { cn } from "@/components/ui/lib/utils.ts";
import { Loader2 } from "lucide-react";
import { useDebouncedCallback } from "use-debounce";
mermaid.initialize({
theme: "dark",
themeCSS: `
.node rect {
stroke: #fff;
}
`,
fontFamily:
'Andika,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji"',
});
type MermaidProps = {
children: string | React.ReactNode;
};
export function Mermaid({ children }: MermaidProps) {
const chart = React.useRef<HTMLDivElement>(null);
const [error, setError] = React.useState<boolean>(false);
const [loading, setLoading] = React.useState<boolean>(false);
const createRenderTask = useDebouncedCallback(() => {
if (!chart.current) return;
// preflight check syntax is valid, if not, suppress the error message
console.debug(`[mermaid] create render task`);
mermaid
.run({
nodes: [chart.current],
suppressErrors: true, // suppresses the error message
})
.catch((e) => {
setError(true);
console.warn(`[mermaid] render failed: ${e}`);
});
setLoading(false);
}, 500);
useEffect(() => {
createRenderTask();
setLoading(true);
}, [children]);
return (
<div className={`whitespace-pre-wrap p-2 border rounded-md border-input`}>
<div
className={cn(
"flex flex-row items-center justify-center text-primary select-none mb-4",
!loading && "hidden",
)}
>
<Loader2 className={`h-4 w-4 mr-2 animate-spin shrink-0`} />
{error ? "Failed to render" : "Rendering..."}
</div>
<div className={cn("mermaid", loading && "mt-2")} ref={chart}>
{children}
</div>
</div>
);
}
export function MarkdownMermaid({ children }: { children: React.ReactNode }) {
return <Mermaid children={children} />;
}

View File

@ -2,7 +2,11 @@ import React, { useMemo } from "react";
import { parseNumber } from "@/utils/base.ts";
import { Check, Loader2 } from "lucide-react";
export function parseProgressbar(children: React.ReactNode) {
type MarkdownProgressbarProps = {
children: React.ReactNode;
};
export function MarkdownProgressbar({ children }: MarkdownProgressbarProps) {
const data = children?.toString() || "";
const progress = useMemo(() => {
const arr = data.split("\n").filter((line) => line.trim().length > 0);

View File

@ -11,7 +11,6 @@ import {
getArrayMemory,
getBooleanMemory,
getMemory,
getNumberMemory,
setArrayMemory,
setMemory,
setNumberMemory,
@ -472,17 +471,11 @@ export function useConversationActions() {
return state;
},
refresh: async (init?: boolean) => {
refresh: async () => {
const resp = await getConversationList();
dispatch(setHistory(resp));
if (init) {
const store = getNumberMemory("history_conversation", -1);
if (current === store) return; // no need to dispatch current
if (store === -1) return; // -1 is default, no need to dispatch
if (!resp.map((item) => item.id).includes(store)) return; // not in the list, no need to dispatch
dispatch(setCurrent(store));
}
return resp;
},
mask: (mask: Mask) => {
dispatch(setMaskItem(mask));
@ -609,6 +602,7 @@ export function useMessageActions() {
if (id === -1 && message.conversation) {
const target: number = message.conversation;
dispatch(raiseConversation(target));
setNumberMemory("history_conversation", target);
stack.raiseConnection(target);
await refresh();
}