mirror of
https://github.com/appwrite/console.git
synced 2026-06-06 19:27:48 +00:00
Merge pull request #1561 from ItzNotABug/fix-ai-rendering
Fix: markdown and styles in ai assistant
This commit is contained in:
@@ -36,6 +36,7 @@
|
||||
"plausible-tracker": "^0.3.9",
|
||||
"pretty-bytes": "^6.1.1",
|
||||
"prismjs": "^1.29.0",
|
||||
"remarkable": "^2.0.1",
|
||||
"svelte-confetti": "^1.4.0",
|
||||
"tippy.js": "^6.3.7"
|
||||
},
|
||||
@@ -52,6 +53,7 @@
|
||||
"@testing-library/user-event": "^14.5.2",
|
||||
"@types/deep-equal": "^1.0.4",
|
||||
"@types/prismjs": "^1.26.5",
|
||||
"@types/remarkable": "^2.0.8",
|
||||
"@typescript-eslint/eslint-plugin": "^7.18.0",
|
||||
"@typescript-eslint/parser": "^7.18.0",
|
||||
"@vitest/ui": "^1.6.0",
|
||||
|
||||
Generated
+28
@@ -59,6 +59,9 @@ importers:
|
||||
prismjs:
|
||||
specifier: ^1.29.0
|
||||
version: 1.29.0
|
||||
remarkable:
|
||||
specifier: ^2.0.1
|
||||
version: 2.0.1
|
||||
svelte-confetti:
|
||||
specifier: ^1.4.0
|
||||
version: 1.4.0(svelte@4.2.19)
|
||||
@@ -102,6 +105,9 @@ importers:
|
||||
'@types/prismjs':
|
||||
specifier: ^1.26.5
|
||||
version: 1.26.5
|
||||
'@types/remarkable':
|
||||
specifier: ^2.0.8
|
||||
version: 2.0.8
|
||||
'@typescript-eslint/eslint-plugin':
|
||||
specifier: ^7.18.0
|
||||
version: 7.18.0(@typescript-eslint/parser@7.18.0(eslint@8.57.1)(typescript@5.6.3))(eslint@8.57.1)(typescript@5.6.3)
|
||||
@@ -1327,6 +1333,9 @@ packages:
|
||||
'@types/pug@2.0.10':
|
||||
resolution: {integrity: sha512-Sk/uYFOBAB7mb74XcpizmH0KOR2Pv3D2Hmrh1Dmy5BmK3MpdSa5kqZcg6EKBdklU0bFXX9gCfzvpnyUehrPIuA==}
|
||||
|
||||
'@types/remarkable@2.0.8':
|
||||
resolution: {integrity: sha512-eKXqPZfpQl1kOADjdKchHrp2gwn9qMnGXhH/AtZe0UrklzhGJkawJo/Y/D0AlWcdWoWamFNIum8+/nkAISQVGg==}
|
||||
|
||||
'@types/shimmer@1.2.0':
|
||||
resolution: {integrity: sha512-UE7oxhQLLd9gub6JKIAhDq06T0F6FnztwMNRvYgjeQSBeMc1ZG/tA47EwfduvkuQS8apbkM/lpLpWsaCeYsXVg==}
|
||||
|
||||
@@ -1559,6 +1568,9 @@ packages:
|
||||
asynckit@0.4.0:
|
||||
resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==}
|
||||
|
||||
autolinker@3.16.2:
|
||||
resolution: {integrity: sha512-JiYl7j2Z19F9NdTmirENSUUIIL/9MytEWtmzhfmsKPCp9E+G35Y0UNCMoM9tFigxT59qSc8Ml2dlZXOCVTYwuA==}
|
||||
|
||||
available-typed-arrays@1.0.7:
|
||||
resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==}
|
||||
engines: {node: '>= 0.4'}
|
||||
@@ -3062,6 +3074,11 @@ packages:
|
||||
resolution: {integrity: sha512-vqlC04+RQoFalODCbCumG2xIOvapzVMHwsyIGM/SIE8fRhFFsXeH8/QQ+s0T0kDAhKc4k30s73/0ydkHQz6HlQ==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
remarkable@2.0.1:
|
||||
resolution: {integrity: sha512-YJyMcOH5lrR+kZdmB0aJJ4+93bEojRZ1HGDn9Eagu6ibg7aVZhc3OWbbShRid+Q5eAfsEqWxpe+g5W5nYNfNiA==}
|
||||
engines: {node: '>= 6.0.0'}
|
||||
hasBin: true
|
||||
|
||||
require-directory@2.1.1:
|
||||
resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
@@ -5143,6 +5160,8 @@ snapshots:
|
||||
|
||||
'@types/pug@2.0.10': {}
|
||||
|
||||
'@types/remarkable@2.0.8': {}
|
||||
|
||||
'@types/shimmer@1.2.0': {}
|
||||
|
||||
'@types/stack-utils@2.0.3': {}
|
||||
@@ -5435,6 +5454,10 @@ snapshots:
|
||||
|
||||
asynckit@0.4.0: {}
|
||||
|
||||
autolinker@3.16.2:
|
||||
dependencies:
|
||||
tslib: 2.8.1
|
||||
|
||||
available-typed-arrays@1.0.7:
|
||||
dependencies:
|
||||
possible-typed-array-names: 1.0.0
|
||||
@@ -7144,6 +7167,11 @@ snapshots:
|
||||
es-errors: 1.3.0
|
||||
set-function-name: 2.0.2
|
||||
|
||||
remarkable@2.0.1:
|
||||
dependencies:
|
||||
argparse: 1.0.10
|
||||
autolinker: 3.16.2
|
||||
|
||||
require-directory@2.1.1: {}
|
||||
|
||||
require-in-the-middle@7.4.0:
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
<script lang="ts">
|
||||
import { Remarkable } from 'remarkable';
|
||||
import Template from './template.svelte';
|
||||
|
||||
const markdownInstance = new Remarkable();
|
||||
|
||||
import { Alert, AvatarInitials, Code, LoadingDots, SvgIcon } from '$lib/components';
|
||||
import { user } from '$lib/stores/user';
|
||||
import { useCompletion } from 'ai/svelte';
|
||||
@@ -19,8 +22,6 @@
|
||||
credentials: 'include'
|
||||
});
|
||||
|
||||
let question = $input;
|
||||
|
||||
const examples = [
|
||||
'How to add platform in the console?',
|
||||
'How can I manage users, permissions, and access control in Appwrite?',
|
||||
@@ -87,10 +88,47 @@
|
||||
|
||||
$: answer = parseCompletion($completion);
|
||||
|
||||
function renderMarkdown(answer: string): string {
|
||||
const trimmedAnswer = answer
|
||||
.trim()
|
||||
.replace(/[ \t]+/g, ' ')
|
||||
.replace(/\n[ \t]+/g, '\n')
|
||||
.replace(/\n+/g, '\n');
|
||||
|
||||
// targeting links in plain text.
|
||||
const processedAnswer = trimmedAnswer
|
||||
.replace(/(\[(.*?)]\((.*?)\))|https?:\/\/\S+/g, (match, fullMarkdownLink, _, __) =>
|
||||
fullMarkdownLink ? match : `[${match}](${match})`
|
||||
)
|
||||
.replace(/https?:\/\/\S+##/g, (url) => url.replace(/##/, '#'));
|
||||
|
||||
const formattedAnswer = processedAnswer.replace(
|
||||
/(^|\n)Sources:/g,
|
||||
(_, prefix) => `${prefix}\nSources:`
|
||||
);
|
||||
|
||||
let renderedHTML = markdownInstance.render(formattedAnswer);
|
||||
|
||||
// add target blank to open links in a new tab.
|
||||
renderedHTML = renderedHTML.replace(/<a\s+href="([^"]+)"/g, '<a href="$1" target="_blank"');
|
||||
|
||||
return renderedHTML;
|
||||
}
|
||||
|
||||
function getInitials(name: string) {
|
||||
const [first, last] = name.split(' ');
|
||||
return `${first?.[0] ?? ''}${last?.[0] ?? ''}`;
|
||||
}
|
||||
|
||||
let previousQuestion = '';
|
||||
$: if ($input) {
|
||||
previousQuestion = $input;
|
||||
}
|
||||
|
||||
$: if (!$isLoading && answer) {
|
||||
// reset input if answer received.
|
||||
$input = '';
|
||||
}
|
||||
</script>
|
||||
|
||||
<Template
|
||||
@@ -142,7 +180,7 @@
|
||||
<div class="content">
|
||||
<div class="u-flex u-gap-8 u-cross-center">
|
||||
<div class="avatar is-size-x-small">{getInitials($user.name)}</div>
|
||||
<p class="u-opacity-75">{question}</p>
|
||||
<p class="u-opacity-75">{previousQuestion}</p>
|
||||
</div>
|
||||
<div class="u-flex u-gap-8 u-margin-block-start-24">
|
||||
<div class="logo">
|
||||
@@ -154,7 +192,7 @@
|
||||
{:else}
|
||||
{#each answer as part}
|
||||
{#if part.type === 'text'}
|
||||
<p>{part.value.trimStart()}</p>
|
||||
<p>{@html renderMarkdown(part.value.trim())}</p>
|
||||
{:else if part.type === 'code'}
|
||||
{#key part.value}
|
||||
<div
|
||||
@@ -196,7 +234,6 @@
|
||||
class="input-text-wrapper u-width-full-line"
|
||||
style="--amount-of-buttons: 1;"
|
||||
on:submit|preventDefault={(e) => {
|
||||
question = $input;
|
||||
handleSubmit(e);
|
||||
}}>
|
||||
<!-- svelte-ignore a11y-autofocus -->
|
||||
@@ -269,10 +306,20 @@
|
||||
.answer {
|
||||
overflow: hidden;
|
||||
|
||||
p {
|
||||
p:first-of-type {
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
}
|
||||
|
||||
:global(.answer ul),
|
||||
:global(.answer ol) {
|
||||
gap: 1rem;
|
||||
display: grid;
|
||||
}
|
||||
|
||||
:global(.answer a) {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
|
||||
.footer {
|
||||
|
||||
Reference in New Issue
Block a user