oki692's picture
Upload folder using huggingface_hub
cfb0fa4 verified
<script lang="ts">
import { onMount, tick, getContext } from 'svelte';
import Textarea from '$lib/components/common/Textarea.svelte';
import { toast } from 'svelte-sonner';
import Tooltip from '$lib/components/common/Tooltip.svelte';
import LockClosed from '$lib/components/icons/LockClosed.svelte';
import ChevronLeft from '$lib/components/icons/ChevronLeft.svelte';
import AccessControlModal from '../common/AccessControlModal.svelte';
import { user } from '$lib/stores';
import { slugify, parseFrontmatter, formatSkillName } from '$lib/utils';
import Spinner from '$lib/components/common/Spinner.svelte';
import { updateSkillAccessGrants } from '$lib/apis/skills';
import { goto } from '$app/navigation';
export let onSubmit: Function;
export let edit = false;
export let skill = null;
export let clone = false;
export let disabled = false;
const i18n = getContext('i18n');
let loading = false;
let name = '';
let id = '';
let description = '';
let content = '';
let accessGrants = [];
let showAccessControlModal = false;
let hasManualEdit = false;
let hasManualName = false;
let hasManualDescription = false;
let isFrontmatterDetected = false;
// Auto-detect frontmatter and fill name/description in create mode
$: if (!edit && content) {
const fm = parseFrontmatter(content);
if (fm.name) {
isFrontmatterDetected = true;
if (!hasManualName) {
name = formatSkillName(fm.name);
}
if (!hasManualEdit) {
id = fm.name;
}
} else {
isFrontmatterDetected = false;
}
if (fm.description && !hasManualDescription) {
description = fm.description;
}
} else if (!edit && !content) {
isFrontmatterDetected = false;
}
$: if (!edit && !hasManualEdit && !isFrontmatterDetected) {
id = name !== '' ? slugify(name) : '';
}
function handleIdInput(e: Event) {
hasManualEdit = true;
}
function handleNameInput(e: Event) {
hasManualName = true;
}
function handleDescriptionInput(e: Event) {
hasManualDescription = true;
}
const submitHandler = async () => {
if (disabled) {
toast.error($i18n.t('You do not have permission to edit this skill.'));
return;
}
loading = true;
await onSubmit({
id,
name,
description,
content,
is_active: true,
meta: { tags: [] },
access_grants: accessGrants
});
loading = false;
};
onMount(async () => {
if (skill) {
name = skill.name || '';
await tick();
id = skill.id || '';
description = skill.description || '';
content = skill.content || '';
accessGrants = skill?.access_grants === undefined ? [] : skill?.access_grants;
if (name) hasManualName = true;
if (description) hasManualDescription = true;
if (id) hasManualEdit = true;
}
});
</script>
<AccessControlModal
bind:show={showAccessControlModal}
bind:accessGrants
accessRoles={['read', 'write']}
share={$user?.permissions?.sharing?.skills || $user?.role === 'admin'}
sharePublic={$user?.permissions?.sharing?.public_skills || $user?.role === 'admin'}
onChange={async () => {
if (edit && skill?.id) {
try {
await updateSkillAccessGrants(localStorage.token, skill.id, accessGrants);
toast.success($i18n.t('Saved'));
} catch (error) {
toast.error(`${error}`);
}
}
}}
/>
<div class=" flex flex-col justify-between w-full overflow-y-auto h-full">
<div class="mx-auto w-full md:px-0 h-full">
<form class=" flex flex-col max-h-[100dvh] h-full" on:submit|preventDefault={submitHandler}>
<div class="flex flex-col flex-1 overflow-auto h-0 rounded-lg">
<div class="w-full mb-2 flex flex-col gap-0.5">
<div class="flex w-full items-center">
<div class=" shrink-0 mr-2">
<Tooltip content={$i18n.t('Back')}>
<button
class="w-full text-left text-sm py-1.5 px-1 rounded-lg dark:text-gray-300 dark:hover:text-white hover:bg-black/5 dark:hover:bg-gray-850"
on:click={() => {
goto('/workspace/skills');
}}
type="button"
>
<ChevronLeft strokeWidth="2.5" />
</button>
</Tooltip>
</div>
<div class="flex-1">
<Tooltip content={$i18n.t('e.g. Code Review Guidelines')} placement="top-start">
<input
class="w-full text-2xl bg-transparent outline-hidden"
type="text"
placeholder={$i18n.t('Skill Name')}
bind:value={name}
on:input={handleNameInput}
required
{disabled}
/>
</Tooltip>
</div>
<div class="self-center shrink-0">
{#if !disabled}
<button
class="bg-gray-50 hover:bg-gray-100 text-black dark:bg-gray-850 dark:hover:bg-gray-800 dark:text-white transition px-2 py-1 rounded-full flex gap-1 items-center"
type="button"
on:click={() => (showAccessControlModal = true)}
>
<LockClosed strokeWidth="2.5" className="size-3.5" />
<div class="text-sm font-medium shrink-0">
{$i18n.t('Access')}
</div>
</button>
{:else}
<span
class="text-xs text-gray-500 bg-gray-100 dark:bg-gray-800 px-2 py-1 rounded-full"
>{$i18n.t('Read Only')}</span
>
{/if}
</div>
</div>
<div class=" flex gap-2 px-1 items-center">
{#if edit}
<div class="text-sm text-gray-500 shrink-0">
{id}
</div>
{:else}
<Tooltip
className="w-full"
content={$i18n.t('e.g. code-review-guidelines')}
placement="top-start"
>
<input
class="w-full text-sm disabled:text-gray-500 bg-transparent outline-hidden"
type="text"
placeholder={$i18n.t('Skill ID')}
bind:value={id}
on:input={handleIdInput}
required
disabled={edit}
/>
</Tooltip>
{/if}
<Tooltip
className="w-full self-center items-center flex"
content={$i18n.t('e.g. Step-by-step instructions for code reviews')}
placement="top-start"
>
<input
class="w-full text-sm bg-transparent outline-hidden"
type="text"
placeholder={$i18n.t('Skill Description')}
bind:value={description}
on:input={handleDescriptionInput}
{disabled}
/>
</Tooltip>
</div>
</div>
<div class="mb-2 flex-1 overflow-auto h-0 rounded-lg">
<div class="h-full flex flex-col">
<div
class="bg-gray-50 dark:bg-gray-900 rounded-xl border border-gray-100/50 dark:border-gray-850/50 flex-1 min-h-0 overflow-hidden flex flex-col"
>
{#if disabled}
<div class="px-4 py-3 overflow-y-auto flex-1">
<pre class="text-xs whitespace-pre-wrap font-mono">{content}</pre>
</div>
{:else}
<textarea
class="w-full flex-1 text-xs bg-transparent outline-hidden resize-none font-mono px-4 py-3"
bind:value={content}
placeholder={$i18n.t('Enter skill instructions in markdown...')}
required
/>
{/if}
</div>
</div>
</div>
<div class="pb-3 flex justify-end">
{#if !disabled}
<button
class="px-3.5 py-1.5 text-sm font-medium bg-black hover:bg-gray-900 text-white dark:bg-white dark:text-black dark:hover:bg-gray-100 transition rounded-full flex items-center"
type="submit"
disabled={loading}
>
{$i18n.t(edit ? 'Save' : 'Save & Create')}
{#if loading}
<div class="ml-1.5">
<Spinner />
</div>
{/if}
</button>
{/if}
</div>
</div>
</form>
</div>
</div>