mirror of
https://github.com/sune-org/store.git
synced 2026-01-13 16:17:58 +00:00
27 lines
18 KiB
JSON
27 lines
18 KiB
JSON
[
|
|
{
|
|
"id": "e1yibwd",
|
|
"name": "Marketplace",
|
|
"pinned": false,
|
|
"avatar": "data:image/webp;base64,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",
|
|
"url": "gh://sune-org/store@main/marketplace.sune",
|
|
"updatedAt": 1757019424249,
|
|
"settings": {
|
|
"model": "openai/gpt-5",
|
|
"temperature": 1,
|
|
"top_p": 0.97,
|
|
"top_k": 0,
|
|
"frequency_penalty": 0,
|
|
"presence_penalty": 0,
|
|
"repetition_penalty": 1,
|
|
"min_p": 0,
|
|
"top_a": 0,
|
|
"max_tokens": 0,
|
|
"verbosity": "",
|
|
"reasoning_effort": "default",
|
|
"system_prompt": "",
|
|
"html": "<!--\n Sune: Marketplace\n Version: 1.8.1\n Description: Discover and install new sunes from the official sune-org/store repository.\n-->\n<div id=\"suneMarketplaceContainer\" class=\"p-4 md:p-6 bg-slate-50\">\n <!-- Header -->\n <div class=\"mb-6 pb-4 border-b border-slate-200\">\n <div class=\"flex justify-between items-center\">\n <h1 class=\"text-2xl font-bold text-slate-800\">Sune Marketplace</h1>\n <span class=\"text-xs font-mono text-slate-400\">v1.8.1</span>\n </div>\n <p class=\"mt-1 text-sm text-slate-600\">Discover and install new capabilities for your Sune.</p>\n </div>\n\n <!-- Content Area: Populated by script -->\n <div id=\"marketplaceContent\">\n <div class=\"flex justify-center items-center py-20\">\n <div class=\"animate-spin rounded-full h-12 w-12 border-b-2 border-slate-500\"></div>\n </div>\n </div>\n\n <!-- Log Viewer -->\n <div class=\"mt-8\">\n <div class=\"flex justify-between items-center mb-2\">\n <h3 class=\"text-sm font-semibold text-slate-600\">Installation Log</h3>\n <button id=\"marketplaceClearLogBtn\" class=\"text-xs text-slate-500 hover:text-slate-800 hover:underline\">Clear Log</button>\n </div>\n <div id=\"marketplaceLogContainer\" class=\"bg-slate-800 text-white font-mono text-xs rounded-lg p-4 h-40 overflow-y-auto\" style=\"scroll-behavior: smooth;\">\n </div>\n </div>\n\n <!-- Contribution Snippet -->\n <div class=\"mt-8 pt-6 border-t border-slate-200\">\n <div class=\"text-center bg-white p-6 rounded-lg border border-slate-200 shadow-sm\">\n <div class=\"flex items-center justify-center gap-2\">\n <i data-lucide=\"git-pull-request-arrow\" class=\"h-5 w-5 text-slate-600\"></i>\n <h3 class=\"text-lg font-semibold text-slate-800\">Contribute your Sune</h3>\n </div>\n <p class=\"mt-2 text-sm text-slate-600 max-w-lg mx-auto\">\n Have a sune you'd like to share? Add it by opening a pull request in the\n <a href=\"https://github.com/sune-org/store\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"font-medium text-sky-600 hover:underline\">sune-org/store</a> repository.\n </p>\n </div>\n </div>\n</div>\n\n<script>\n(() => {\n const rootElement = document.getElementById('suneMarketplaceContainer');\n if (!rootElement) {\n console.error(\"Marketplace Sune: Root element #suneMarketplaceContainer not found.\");\n return;\n }\n\n const marketplaceContent = rootElement.querySelector('#marketplaceContent');\n const logContainer = rootElement.querySelector('#marketplaceLogContainer');\n\n window.SuneMarketplace = window.SuneMarketplace || { catalog: null, isLoading: false };\n\n const logMessage = (message, type = 'info') => {\n if (!logContainer) return;\n const entry = document.createElement('div');\n const sanitizedMessage = message.toString().replace(/</g, '<').replace(/>/g, '>');\n let prefix = '»';\n let colorClass = 'text-slate-300';\n switch (type) {\n case 'success': prefix = '✔'; colorClass = 'text-green-400'; break;\n case 'error': prefix = '✖'; colorClass = 'text-red-400'; break;\n case 'warn': prefix = '⚠'; colorClass = 'text-yellow-400'; break;\n }\n entry.className = `flex items-start gap-2 ${colorClass}`;\n entry.innerHTML = `<span class=\"flex-shrink-0 pt-0.5\">${prefix}</span><span class=\"flex-grow\">${sanitizedMessage}</span>`;\n logContainer.appendChild(entry);\n logContainer.scrollTop = logContainer.scrollHeight;\n };\n\n const clearLog = () => {\n if (!logContainer) return;\n logContainer.innerHTML = '';\n logMessage(\"Log cleared.\");\n };\n\n const convertToJsDelivrUrl = (rawUrl) => {\n if (!rawUrl || typeof rawUrl !== 'string' || !rawUrl.startsWith('https://raw.githubusercontent.com/')) return rawUrl;\n try {\n const url = new URL(rawUrl);\n const [, user, repo, branch, ...filePath] = url.pathname.split('/');\n return `https://cdn.jsdelivr.net/gh/${user}/${repo}@${branch}/${filePath.join('/')}`;\n } catch (e) { return rawUrl; }\n };\n\n const convertToShortUrl = (rawUrl) => {\n if (!rawUrl || !rawUrl.startsWith('https://raw.githubusercontent.com/')) return rawUrl;\n try {\n const path = rawUrl.substring('https://raw.githubusercontent.com/'.length);\n const parts = path.split('/');\n const user = parts.shift();\n const repo = parts.shift();\n const branch = parts.shift();\n return `${user}/${repo}@${branch}/${parts.join('/')}`;\n } catch {\n return rawUrl;\n }\n };\n\n const renderMarketplace = (items) => {\n const installedSunes = window.SUNE?.list || [];\n const html = items.map(item => {\n const rawUrl = item.raw;\n const shortUrl = convertToShortUrl(rawUrl);\n const ghUrl = `gh://${shortUrl}`;\n const isInstalled = installedSunes.some(s => s.url === rawUrl || s.url === shortUrl || s.url === ghUrl);\n\n const buttonText = isInstalled ? 'Installed' : 'Install';\n const buttonIcon = isInstalled ? 'check-circle' : 'download-cloud';\n const buttonClasses = isInstalled \n ? \"bg-slate-100 text-slate-500 cursor-default\" \n : \"bg-slate-600 text-white hover:bg-slate-700 active:bg-slate-800 focus:ring-2 focus:ring-offset-2 focus:ring-slate-500\";\n \n const displayName = (item.name || 'Untitled Sune').replace(/\\.sune$/, '').replace(/[-_]/g, ' ').replace(/\\b\\w/g, l => l.toUpperCase());\n const description = item.description || \"Official utility sune to enhance workflow.\";\n\n return `\n <div class=\"bg-white border border-slate-200 rounded-lg shadow-sm p-5 flex flex-col justify-between transition-shadow hover:shadow-md\">\n <div>\n <h3 class=\"text-base font-semibold text-slate-800\">${displayName}</h3>\n <p class=\"text-sm text-slate-500 mt-1 h-10 overflow-hidden\">${description}</p>\n </div>\n <button \n data-name=\"${displayName}\" \n data-short-url=\"${shortUrl}\" \n data-raw-url=\"${rawUrl}\"\n class=\"install-btn mt-4 w-full px-4 py-2 text-sm font-medium rounded-md transition-colors duration-200 flex items-center justify-center gap-2 ${buttonClasses}\" \n ${isInstalled ? 'disabled' : ''}>\n <i data-lucide=\"${buttonIcon}\" class=\"h-4 w-4\"></i>\n <span>${buttonText}</span>\n </button>\n </div>`;\n }).join('');\n marketplaceContent.innerHTML = `<div class=\"grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-4\">${html}</div>`;\n window.lucide?.createIcons();\n };\n\n const handleInstallClick = async (button) => {\n if (button.disabled) return;\n \n const displayName = button.dataset.name || 'Unknown Sune';\n const rawUrl = button.dataset.rawUrl;\n const shortUrl = button.dataset.shortUrl;\n const ghUrl = `gh://${shortUrl}`;\n const fetchUrl = convertToJsDelivrUrl(rawUrl);\n\n clearLog();\n logMessage(`Starting installation for '${displayName}'...`);\n\n button.disabled = true;\n button.innerHTML = `<i data-lucide=\"loader-circle\" class=\"h-4 w-4 animate-spin\"></i><span>Installing...</span>`;\n window.lucide?.createIcons();\n\n try {\n logMessage(`Fetching from: ${fetchUrl}`);\n const response = await fetch(fetchUrl);\n if (!response.ok) throw new Error(`Fetch failed with status: ${response.status}`);\n\n const suneDataArray = await response.json();\n if (!Array.isArray(suneDataArray)) throw new Error('Invalid or empty sune file format.');\n \n logMessage(`Parsed ${suneDataArray.length} sune(s) from file.`);\n logMessage(`Assigning sync URL: ${ghUrl}`);\n\n const allSunes = window.SUNE.list;\n let added = 0, updated = 0, skipped = 0;\n\n for (const suneObject of suneDataArray) {\n if (!suneObject || !suneObject.id) {\n logMessage('Skipping invalid sune entry without an ID.', 'warn');\n skipped++;\n continue;\n }\n \n suneObject.url = ghUrl; // Assign the correct gh:// prefixed URL\n const existing = allSunes.find(s => s.id === suneObject.id);\n\n if (!existing) {\n window.SUNE.create(suneObject);\n added++;\n logMessage(`Installed: '${suneObject.name || suneObject.id}'.`, 'success');\n } else if (+(suneObject.updatedAt || 0) > +(existing.updatedAt || 0)) {\n Object.assign(existing, suneObject);\n updated++;\n logMessage(`Updated: '${suneObject.name || suneObject.id}'.`, 'success');\n } else {\n skipped++;\n }\n }\n\n if (updated > 0) {\n window.SUNE.save(); // Persist changes from Object.assign\n }\n if (skipped > 0 && added === 0 && updated === 0) {\n logMessage('All sunes are already up to date.', 'info');\n }\n if (added > 0 || updated > 0) {\n logMessage('Refreshing sune list...', 'success');\n window.renderSidebar();\n }\n\n button.innerHTML = `<i data-lucide=\"check-circle\" class=\"h-4 w-4\"></i><span>${updated > 0 ? 'Updated' : 'Installed'}</span>`;\n button.classList.remove('bg-slate-600', 'hover:bg-slate-700');\n button.classList.add('bg-slate-100', 'text-slate-500', 'cursor-default');\n\n } catch (error) {\n logMessage(`Installation failed: ${error.message}`, 'error');\n button.disabled = false;\n button.innerHTML = `<i data-lucide=\"alert-triangle\" class=\"h-4 w-4\"></i><span>Retry Install</span>`;\n } finally {\n window.lucide?.createIcons();\n }\n };\n \n const initMarketplace = async () => {\n clearLog();\n if (window.SuneMarketplace.catalog) {\n renderMarketplace(window.SuneMarketplace.catalog);\n logMessage(\"Loaded cached catalog.\");\n return;\n }\n if (window.SuneMarketplace.isLoading) return;\n \n try {\n window.SuneMarketplace.isLoading = true;\n logMessage(\"Fetching sune catalog...\");\n \n const catalogUrl = 'https://cdn.jsdelivr.net/gh/sune-org/store@main/catalog.json';\n const response = await fetch(catalogUrl, { cache: 'no-store' });\n if (!response.ok) throw new Error(`Catalog fetch failed: Status ${response.status}`);\n \n const catalogData = (await response.json()).filter(i => i.name && i.name.endsWith('.sune'));\n window.SuneMarketplace.catalog = catalogData;\n logMessage(`Catalog loaded with ${catalogData.length} items.`);\n renderMarketplace(catalogData);\n } catch (error) {\n logMessage(`Error initializing marketplace: ${error.message}`, 'error');\n marketplaceContent.innerHTML = `<div class=\"text-center py-20 bg-red-50 text-red-700 rounded-lg\"><p>Could not load Marketplace.</p><p class=\"text-sm mt-2\">${error.message}</p></div>`;\n } finally {\n window.SuneMarketplace.isLoading = false;\n }\n };\n \n rootElement.addEventListener('click', (event) => {\n const installButton = event.target.closest('.install-btn');\n if (installButton) {\n handleInstallClick(installButton);\n return;\n }\n if (event.target.closest('#marketplaceClearLogBtn')) {\n clearLog();\n return;\n }\n });\n\n initMarketplace();\n window.lucide?.createIcons();\n})();\n</script>\n",
|
|
"extension_html": "<sune src='https://raw.githubusercontent.com/sune-org/store/refs/heads/main/sync.sune' private />"
|
|
}
|
|
}
|
|
] |