diff --git a/backend/open_webui/utils/skill_files.py b/backend/open_webui/utils/skill_files.py
index 9ec201dfbc..c75e24c7ae 100644
--- a/backend/open_webui/utils/skill_files.py
+++ b/backend/open_webui/utils/skill_files.py
@@ -237,7 +237,7 @@ def parse_import(
'name': str(name),
'description': package.get('description', fm.get('description', '')),
'meta': package.get('meta') or {},
- 'is_active': package.get('is_active', True),
+ **({'is_active': package['is_active']} if 'is_active' in package else {}),
'files': files,
}
)
diff --git a/src/lib/components/workspace/Skills.svelte b/src/lib/components/workspace/Skills.svelte
index 307125613d..6358c2228b 100644
--- a/src/lib/components/workspace/Skills.svelte
+++ b/src/lib/components/workspace/Skills.svelte
@@ -43,6 +43,40 @@
import Badge from '$lib/components/common/Badge.svelte';
import Switch from '../common/Switch.svelte';
import SkillMenu from './Skills/SkillMenu.svelte';
+ import CommunityDiscover from './common/CommunityDiscover.svelte';
+ import { config } from '$lib/stores';
+ let stopSkillShare = () => {};
+ const shareHandler = (skill: { id: string }) => {
+ stopSkillShare();
+ const tab = window.open('https://openwebui.com/post?type=skill', '_blank');
+ if (!tab) {
+ toast.error($i18n.t('Please allow popups to share your skill.'));
+ return;
+ }
+ const receiveLoaded = async (event: MessageEvent) => {
+ if (
+ event.origin !== 'https://openwebui.com' ||
+ event.source !== tab ||
+ event.data !== 'loaded'
+ )
+ return;
+ stopSkillShare();
+ try {
+ const exported = JSON.parse(
+ await (await exportSkillBundle(localStorage.token, 'json', [skill.id])).text()
+ );
+ const { id, name, description, files } = Array.isArray(exported) ? exported[0] : exported;
+ const data = JSON.stringify({ id, name, description, files });
+ if (new Blob([data]).size > 15 * 1024 * 1024)
+ throw new Error('Community skill JSON exceeds 15 MiB');
+ tab.postMessage(data, 'https://openwebui.com');
+ } catch (error) {
+ toast.error(skillError(error));
+ }
+ };
+ window.addEventListener('message', receiveLoaded);
+ stopSkillShare = () => window.removeEventListener('message', receiveLoaded);
+ };
import SkillImport from './Skills/SkillImport.svelte';
import ImportModal from '$lib/components/ImportModal.svelte';
import { cloneSkill, exportSkillBundle, loadSkillByUrl, skillError } from '$lib/apis/skills';
@@ -289,6 +323,7 @@
});
onDestroy(() => {
+ stopSkillShare();
searchController?.abort();
clearTimeout(searchDebounceTimer);
});
@@ -564,6 +599,7 @@
{:else}
shareHandler(skill)}
accessHandler={() => accessModal.open(skill.id)}
show={openSkillMenuId === skill.id}
editHandler={() => {
@@ -655,6 +691,13 @@
{resolveLocalizedResource(selectedSkill, $i18n.language)}.
+ {#if $config?.features?.enable_community_sharing}
+
+ {/if}
{:else}
diff --git a/src/lib/components/workspace/Skills/SkillMenu.svelte b/src/lib/components/workspace/Skills/SkillMenu.svelte
index 4c344e3ff8..5c1e62e134 100644
--- a/src/lib/components/workspace/Skills/SkillMenu.svelte
+++ b/src/lib/components/workspace/Skills/SkillMenu.svelte
@@ -8,13 +8,14 @@
import Tooltip from '$lib/components/common/Tooltip.svelte';
import DocumentDuplicate from '$lib/components/icons/DocumentDuplicate.svelte';
import Download from '$lib/components/icons/Download.svelte';
- import { user } from '$lib/stores';
+ import { user, config } from '$lib/stores';
const i18n = getContext
('i18n');
export let editHandler: Function;
export let cloneHandler: Function;
export let exportHandler: Function;
+ export let shareHandler: (() => void) | null = null;
export let deleteHandler: Function;
export let accessHandler: (() => void) | null = null;
export let onClose: Function;
@@ -90,6 +91,16 @@
{$i18n.t('Clone')}
+ {#if shareHandler && $config?.features?.enable_community_sharing && ($user?.role === 'admin' || $user?.permissions?.workspace?.skills_export)}
+
+ {/if}
{#if $user?.role === 'admin' || $user?.permissions?.workspace?.skills_export}