PROMPT.md-based skill injection, scroll-on-image-load fix, skill availability UI
- SkillService: read PROMPT.md instead of SKILL.md for system prompt injection - SkillInfo: add HasPromptMd field, skills without PROMPT.md are not injectable - Agent form: disable checkbox + warning for skills missing PROMPT.md - Thumbnail size: 320px (was 300px) - Chat: _scrollOnImages() re-scrolls after lazy-loaded images finish loading - CSS: skill-unavailable/warning styling, adjusted thumb dimensions
This commit is contained in:
1 parent
8d86406037
commit
40e56acc30
6 files changed
+46
-15
No files matched your search
@@ -193,8 +193,8 @@ public static class SkillEndpoint
|
|||||||
{
|
{
|
||||||
using var image = await Image.LoadAsync(originalPath);
|
using var image = await Image.LoadAsync(originalPath);
|
||||||
|
|
||||||
// Max 300px auf der längsten Seite
|
// Max 320px auf der längsten Seite
|
||||||
var maxSize = 300;
|
var maxSize = 320;
|
||||||
image.Mutate(x => x.Resize(new ResizeOptions
|
image.Mutate(x => x.Resize(new ResizeOptions
|
||||||
{
|
{
|
||||||
Size = new Size(maxSize, maxSize),
|
Size = new Size(maxSize, maxSize),
|
||||||
|
|||||||
@@ -46,13 +46,14 @@ public class SkillService
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Liest die SKILL.md eines Skills. Gibt null zurück wenn nicht vorhanden.
|
/// Liest die PROMPT.md eines Skills. Gibt null zurück wenn nicht vorhanden.
|
||||||
|
/// Nur Skills mit PROMPT.md können im System-Prompt verwendet werden.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string GetSkillPrompt(string slug)
|
public string GetSkillPrompt(string slug)
|
||||||
{
|
{
|
||||||
var skillMd = Path.Combine(_skillsDir, slug, "SKILL.md");
|
var promptMd = Path.Combine(_skillsDir, slug, "PROMPT.md");
|
||||||
if (!File.Exists(skillMd)) return null;
|
if (!File.Exists(promptMd)) return null;
|
||||||
return File.ReadAllText(skillMd);
|
return File.ReadAllText(promptMd);
|
||||||
}
|
}
|
||||||
|
|
||||||
private SkillInfo LoadSkill(string dir)
|
private SkillInfo LoadSkill(string dir)
|
||||||
@@ -61,11 +62,14 @@ public class SkillService
|
|||||||
var metaFile = Path.Combine(dir, "_meta.json");
|
var metaFile = Path.Combine(dir, "_meta.json");
|
||||||
var skillMd = Path.Combine(dir, "SKILL.md");
|
var skillMd = Path.Combine(dir, "SKILL.md");
|
||||||
|
|
||||||
|
var promptMd = Path.Combine(dir, "PROMPT.md");
|
||||||
|
|
||||||
var info = new SkillInfo
|
var info = new SkillInfo
|
||||||
{
|
{
|
||||||
Slug = slug,
|
Slug = slug,
|
||||||
Path = Path.GetFullPath(dir),
|
Path = Path.GetFullPath(dir),
|
||||||
HasSkillMd = File.Exists(skillMd)
|
HasSkillMd = File.Exists(skillMd),
|
||||||
|
HasPromptMd = File.Exists(promptMd)
|
||||||
};
|
};
|
||||||
|
|
||||||
// Name/Description aus SKILL.md Frontmatter lesen
|
// Name/Description aus SKILL.md Frontmatter lesen
|
||||||
@@ -135,4 +139,5 @@ public class SkillInfo
|
|||||||
public string Version { get; set; }
|
public string Version { get; set; }
|
||||||
public string Path { get; set; }
|
public string Path { get; set; }
|
||||||
public bool HasSkillMd { get; set; }
|
public bool HasSkillMd { get; set; }
|
||||||
|
public bool HasPromptMd { get; set; }
|
||||||
}
|
}
|
||||||
@@ -627,17 +627,13 @@
|
|||||||
|
|
||||||
.krea-thumb {
|
.krea-thumb {
|
||||||
display: block;
|
display: block;
|
||||||
max-width: 300px;
|
max-width: 320px !important;
|
||||||
max-height: 200px;
|
max-height: 240px;
|
||||||
object-fit: contain;
|
object-fit: contain;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Krea Image Modal */
|
/* Krea Image Modal */
|
||||||
|
|
||||||
.krea-overlay {
|
|
||||||
backdrop-filter: blur(4px);
|
|
||||||
}
|
|
||||||
|
|
||||||
.krea-modal {
|
.krea-modal {
|
||||||
max-width: 90vw;
|
max-width: 90vw;
|
||||||
max-height: 90vh;
|
max-height: 90vh;
|
||||||
|
|||||||
@@ -180,6 +180,21 @@
|
|||||||
border-radius: var(--border-radius);
|
border-radius: var(--border-radius);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.skill-unavailable {
|
||||||
|
opacity: 0.45;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.skill-unavailable:hover {
|
||||||
|
border-color: var(--f2-border);
|
||||||
|
}
|
||||||
|
|
||||||
|
.skill-warning {
|
||||||
|
color: var(--dangerous);
|
||||||
|
font-size: var(--font-size-sm, 0.8em);
|
||||||
|
margin-left: auto;
|
||||||
|
}
|
||||||
|
|
||||||
.danger-btn {
|
.danger-btn {
|
||||||
color: var(--dangerous);
|
color: var(--dangerous);
|
||||||
border-color: var(--dangerous);
|
border-color: var(--dangerous);
|
||||||
|
|||||||
@@ -185,14 +185,17 @@ function renderSkillCheckboxes(container, allSkills, assignedSlugs) {
|
|||||||
}
|
}
|
||||||
for (const skill of allSkills) {
|
for (const skill of allSkills) {
|
||||||
const checked = assignedSlugs.includes(skill.slug) ? 'checked' : '';
|
const checked = assignedSlugs.includes(skill.slug) ? 'checked' : '';
|
||||||
|
const disabled = !skill.hasPromptMd ? 'disabled' : '';
|
||||||
const version = skill.version ? ` <span class="skill-version">v${skill.version}</span>` : '';
|
const version = skill.version ? ` <span class="skill-version">v${skill.version}</span>` : '';
|
||||||
const desc = skill.description ? `<span class="skill-desc">${skill.description}</span>` : '';
|
const desc = skill.description ? `<span class="skill-desc">${skill.description}</span>` : '';
|
||||||
|
const warning = !skill.hasPromptMd ? '<span class="skill-warning">⚠ PROMPT.md fehlt – Skill nicht verwendbar</span>' : '';
|
||||||
const row = document.createElement('label');
|
const row = document.createElement('label');
|
||||||
row.className = 'skill-item';
|
row.className = 'skill-item' + (!skill.hasPromptMd ? ' skill-unavailable' : '');
|
||||||
row.innerHTML = `
|
row.innerHTML = `
|
||||||
<input type="checkbox" value="${skill.slug}" ${checked}>
|
<input type="checkbox" value="${skill.slug}" ${checked} ${disabled}>
|
||||||
<span class="skill-name">${skill.name}${version}</span>
|
<span class="skill-name">${skill.name}${version}</span>
|
||||||
${desc}
|
${desc}
|
||||||
|
${warning}
|
||||||
`;
|
`;
|
||||||
container.appendChild(row);
|
container.appendChild(row);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -79,6 +79,16 @@ function scrollDown() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function _scrollOnImages(container) {
|
||||||
|
var imgs = container.querySelectorAll('img');
|
||||||
|
for (var i = 0; i < imgs.length; i++) {
|
||||||
|
if (!imgs[i].complete) {
|
||||||
|
imgs[i].addEventListener('load', scrollDown);
|
||||||
|
imgs[i].addEventListener('error', scrollDown);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function esc(s) {
|
function esc(s) {
|
||||||
const d = document.createElement('div');
|
const d = document.createElement('div');
|
||||||
d.textContent = s;
|
d.textContent = s;
|
||||||
@@ -234,6 +244,7 @@ function addAssistantBubble(text, timestamp = '', messageId = null, isMarked = f
|
|||||||
if (messageId) div.dataset.messageId = messageId;
|
if (messageId) div.dataset.messageId = messageId;
|
||||||
div.innerHTML = renderMarkdown(text);
|
div.innerHTML = renderMarkdown(text);
|
||||||
wrapKreaGalleries(div);
|
wrapKreaGalleries(div);
|
||||||
|
_scrollOnImages(div);
|
||||||
_addReplyButton(div);
|
_addReplyButton(div);
|
||||||
_addMarkButton(div, messageId, isMarked);
|
_addMarkButton(div, messageId, isMarked);
|
||||||
chatEl().appendChild(div);
|
chatEl().appendChild(div);
|
||||||
@@ -299,6 +310,7 @@ function endStreamBubble() {
|
|||||||
else {
|
else {
|
||||||
_streamDiv.innerHTML = renderMarkdown(_streamText);
|
_streamDiv.innerHTML = renderMarkdown(_streamText);
|
||||||
wrapKreaGalleries(_streamDiv);
|
wrapKreaGalleries(_streamDiv);
|
||||||
|
_scrollOnImages(_streamDiv);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_streamDiv = null;
|
_streamDiv = null;
|
||||||
|
|||||||
Reference in new issue
Block a user