mirror of
https://github.com/appwrite/console.git
synced 2026-04-07 19:17:46 +00:00
Merge branch 'main' into fix-sms-message-id
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 {
|
||||
|
||||
@@ -140,6 +140,7 @@
|
||||
<InputText
|
||||
id="taxId"
|
||||
label="Tax ID"
|
||||
autofocus
|
||||
placeholder="Tax ID"
|
||||
bind:value={taxId} />
|
||||
</div>
|
||||
|
||||
@@ -56,9 +56,9 @@
|
||||
|
||||
<style lang="scss">
|
||||
// TODO: remove once pink is updated
|
||||
.collapsible-item {
|
||||
.collapsible-item:not(.is-info) {
|
||||
.collapsible-wrapper {
|
||||
padding-left: 0;
|
||||
padding-left: 0.5rem;
|
||||
}
|
||||
.collapsible-wrapper.is-disabled {
|
||||
cursor: not-allowed;
|
||||
|
||||
@@ -24,11 +24,17 @@
|
||||
let error: string;
|
||||
|
||||
onMount(() => {
|
||||
if (element && autofocus) {
|
||||
element.focus();
|
||||
if (autofocus) {
|
||||
addInputFocus();
|
||||
}
|
||||
});
|
||||
|
||||
export function addInputFocus() {
|
||||
if (element) {
|
||||
element.focus();
|
||||
}
|
||||
}
|
||||
|
||||
const handleInvalid = (event: Event) => {
|
||||
event.preventDefault();
|
||||
|
||||
|
||||
@@ -71,6 +71,7 @@
|
||||
</div>
|
||||
{:else}
|
||||
<CustomId
|
||||
autofocus
|
||||
bind:show={showCustomId}
|
||||
name="Function"
|
||||
bind:id={$createFunction.$id}
|
||||
|
||||
@@ -48,6 +48,7 @@
|
||||
</div>
|
||||
{:else}
|
||||
<CustomId
|
||||
autofocus
|
||||
bind:show={showCustomId}
|
||||
name="Function"
|
||||
bind:id={$createFunction.$id}
|
||||
|
||||
@@ -73,6 +73,7 @@
|
||||
</div>
|
||||
{:else}
|
||||
<CustomId
|
||||
autofocus
|
||||
bind:show={showCustomId}
|
||||
name="Function"
|
||||
bind:id={$templateConfig.$id}
|
||||
|
||||
@@ -75,7 +75,7 @@
|
||||
</Pill>
|
||||
</div>
|
||||
{:else}
|
||||
<CustomId bind:show={showCustomId} name="Organization" bind:id />
|
||||
<CustomId autofocus bind:show={showCustomId} name="Organization" bind:id />
|
||||
{/if}
|
||||
</FormList>
|
||||
<svelte:fragment slot="footer">
|
||||
|
||||
@@ -125,7 +125,12 @@
|
||||
</Pill>
|
||||
</div>
|
||||
{:else}
|
||||
<CustomId bind:show={showCustomId} name="Organization" isProject bind:id />
|
||||
<CustomId
|
||||
autofocus
|
||||
bind:show={showCustomId}
|
||||
name="Organization"
|
||||
isProject
|
||||
bind:id />
|
||||
{/if}
|
||||
{#if isCloud}
|
||||
<div class="u-margin-block-start-8">
|
||||
|
||||
@@ -40,7 +40,12 @@
|
||||
Apply Appwrite credits to your organization.
|
||||
|
||||
<FormList>
|
||||
<InputText placeholder="Promo code" id="code" label="Add promo code" bind:value={coupon} />
|
||||
<InputText
|
||||
placeholder="Promo code"
|
||||
id="code"
|
||||
autofocus
|
||||
label="Add promo code"
|
||||
bind:value={coupon} />
|
||||
</FormList>
|
||||
|
||||
<svelte:fragment slot="footer">
|
||||
|
||||
@@ -84,6 +84,7 @@
|
||||
<InputNumber
|
||||
placeholder="Add budget cap"
|
||||
id="cap"
|
||||
autofocus
|
||||
label="Budget cap (USD)"
|
||||
bind:value={budget} />
|
||||
{/if}
|
||||
|
||||
@@ -61,7 +61,7 @@
|
||||
</Pill>
|
||||
</div>
|
||||
{:else}
|
||||
<CustomId bind:show={showCustomId} name="Project" isProject bind:id />
|
||||
<CustomId autofocus bind:show={showCustomId} name="Project" isProject bind:id />
|
||||
{/if}
|
||||
</FormList>
|
||||
<svelte:fragment slot="footer">
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
</div>
|
||||
{:else}
|
||||
<CustomId
|
||||
autofocus
|
||||
bind:show={showCustomId}
|
||||
name="Project"
|
||||
isProject={true}
|
||||
|
||||
@@ -58,7 +58,7 @@
|
||||
</Pill>
|
||||
</div>
|
||||
{:else}
|
||||
<CustomId bind:show={showCustomId} name="Team" bind:id />
|
||||
<CustomId autofocus bind:show={showCustomId} name="Team" bind:id />
|
||||
{/if}
|
||||
</FormList>
|
||||
<svelte:fragment slot="footer">
|
||||
|
||||
@@ -84,7 +84,7 @@
|
||||
</Pill>
|
||||
</div>
|
||||
{:else}
|
||||
<CustomId bind:show={showDropdown} name="User" bind:id />
|
||||
<CustomId autofocus bind:show={showDropdown} name="User" bind:id />
|
||||
{/if}
|
||||
</FormList>
|
||||
<svelte:fragment slot="footer">
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
import { addNotification } from '$lib/stores/notifications';
|
||||
import { sdk } from '$lib/stores/sdk';
|
||||
import { project } from '../../store';
|
||||
import { tick } from 'svelte';
|
||||
|
||||
let passwordHistory = $project?.authPasswordHistory < 1 ? 5 : $project?.authPasswordHistory;
|
||||
let passwordHistoryEnabled = ($project?.authPasswordHistory ?? 0) !== 0;
|
||||
@@ -34,6 +35,14 @@
|
||||
trackError(error, Submit.AuthPasswordHistoryUpdate);
|
||||
}
|
||||
}
|
||||
|
||||
let maxSessionInputField: InputNumber | null = null;
|
||||
|
||||
$: if (passwordHistoryEnabled && maxSessionInputField) {
|
||||
tick().then(() => {
|
||||
maxSessionInputField.addInputFocus();
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<Form onSubmit={updatePasswordHistoryLimit}>
|
||||
@@ -59,6 +68,7 @@
|
||||
min={1}
|
||||
id="max-session"
|
||||
label="Limit"
|
||||
bind:this={maxSessionInputField}
|
||||
disabled={!passwordHistoryEnabled}
|
||||
bind:value={passwordHistory} />
|
||||
</FormList>
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
import { addNotification } from '$lib/stores/notifications';
|
||||
import { sdk } from '$lib/stores/sdk';
|
||||
import { project } from '../../store';
|
||||
import { tick } from 'svelte';
|
||||
|
||||
let isLimited = $project?.authLimit !== 0;
|
||||
let newLimit = isLimited ? $project?.authLimit : 100;
|
||||
@@ -41,6 +42,14 @@
|
||||
trackError(error, Submit.AuthLimitUpdate);
|
||||
}
|
||||
}
|
||||
|
||||
let maxUsersInputField: HTMLInputElement | null = null;
|
||||
|
||||
$: if (isLimited && maxUsersInputField) {
|
||||
tick().then(() => {
|
||||
maxUsersInputField.focus();
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<CardGrid>
|
||||
@@ -96,7 +105,8 @@
|
||||
class="input-text"
|
||||
max="10000"
|
||||
disabled={!isLimited}
|
||||
bind:value={newLimit} />
|
||||
bind:value={newLimit}
|
||||
bind:this={maxUsersInputField} />
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
+1
-1
@@ -29,7 +29,7 @@
|
||||
</Pill>
|
||||
</div>
|
||||
{:else}
|
||||
<CustomId bind:show={showCustomId} name="Document" bind:id={customId} />
|
||||
<CustomId autofocus bind:show={showCustomId} name="Document" bind:id={customId} />
|
||||
{/if}
|
||||
{/if}
|
||||
</ul>
|
||||
|
||||
+1
-1
@@ -71,10 +71,10 @@
|
||||
</div>
|
||||
{:else}
|
||||
<CustomId
|
||||
autofocus
|
||||
bind:show={showCustomId}
|
||||
name="Collection"
|
||||
bind:id
|
||||
autofocus={false}
|
||||
fullWidth={true} />
|
||||
{/if}
|
||||
</FormList>
|
||||
|
||||
@@ -52,6 +52,7 @@
|
||||
</div>
|
||||
{:else}
|
||||
<CustomId
|
||||
autofocus
|
||||
bind:show={showCustomId}
|
||||
name="Function"
|
||||
bind:id={$createFunction.id}
|
||||
|
||||
+3
-3
@@ -63,7 +63,7 @@
|
||||
</script>
|
||||
|
||||
<Form onSubmit={update}>
|
||||
<CardGrid hideFooter={message.status != 'draft'}>
|
||||
<CardGrid hideFooter={message.status !== 'draft'}>
|
||||
<div class="grid-1-2-col-1 u-flex u-cross-center u-gap-16">
|
||||
<Heading tag="h6" size="7">Message</Heading>
|
||||
</div>
|
||||
@@ -72,12 +72,12 @@
|
||||
<InputText
|
||||
id="subject"
|
||||
label="Subject"
|
||||
disabled={message.status != 'draft'}
|
||||
disabled={message.status !== 'draft'}
|
||||
bind:value={subject}></InputText>
|
||||
<InputTextarea
|
||||
id="message"
|
||||
label="Message"
|
||||
disabled={message.status != 'draft'}
|
||||
disabled={message.status !== 'draft'}
|
||||
bind:value={content}></InputTextarea>
|
||||
<InputSwitch label="HTML mode" id="html" bind:value={html}>
|
||||
<svelte:fragment slot="description">
|
||||
|
||||
@@ -58,7 +58,7 @@
|
||||
</Pill>
|
||||
</div>
|
||||
{:else}
|
||||
<CustomId bind:show={showCustomId} name="Topic" bind:id />
|
||||
<CustomId autofocus bind:show={showCustomId} name="Topic" bind:id />
|
||||
{/if}
|
||||
</FormList>
|
||||
<svelte:fragment slot="footer">
|
||||
|
||||
@@ -115,6 +115,7 @@
|
||||
</div>
|
||||
{:else}
|
||||
<CustomId
|
||||
autofocus
|
||||
bind:show={showCustomId}
|
||||
name="Message"
|
||||
bind:id={$messageParams[$providerType].messageId}
|
||||
|
||||
+1
-1
@@ -62,7 +62,7 @@
|
||||
</div>
|
||||
{:else}
|
||||
<div class="custom-id-wrapper">
|
||||
<CustomId bind:show={showCustomId} name="File" bind:id={$createFile.id} />
|
||||
<CustomId autofocus bind:show={showCustomId} name="File" bind:id={$createFile.id} />
|
||||
</div>
|
||||
{/if}
|
||||
</FormList>
|
||||
|
||||
@@ -63,7 +63,7 @@
|
||||
</Pill>
|
||||
</div>
|
||||
{:else}
|
||||
<CustomId bind:show={showCustomId} name="Bucket" bind:id />
|
||||
<CustomId autofocus bind:show={showCustomId} name="Bucket" bind:id />
|
||||
{/if}
|
||||
</FormList>
|
||||
<svelte:fragment slot="footer">
|
||||
|
||||
Reference in New Issue
Block a user