Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7ce913997a | ||
|
|
63f5097a6d |
+127
-15
@@ -334,6 +334,44 @@
|
||||
display: none;
|
||||
}
|
||||
|
||||
.docPreviewMeta {
|
||||
border: 1px solid var(--muted-color);
|
||||
border-radius: 8px;
|
||||
padding: 10px;
|
||||
margin-bottom: 10px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
background: color-mix(in srgb, var(--secondary-color) 88%, var(--muted-color) 12%);
|
||||
}
|
||||
|
||||
.docPreviewMetaRow {
|
||||
font-size: 84%;
|
||||
line-height: 1.4;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.docPreviewMetaLabel {
|
||||
font-weight: 700;
|
||||
color: var(--primary-color);
|
||||
}
|
||||
|
||||
.docPreviewImageWrap {
|
||||
margin-top: 6px;
|
||||
}
|
||||
|
||||
.docPreviewImage {
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
border-radius: 8px;
|
||||
border: 1px solid var(--muted-color);
|
||||
display: block;
|
||||
}
|
||||
|
||||
.docPreviewContent {
|
||||
min-height: 40px;
|
||||
}
|
||||
|
||||
#divDocumentInfo {
|
||||
font-size: 72%;
|
||||
color: var(--muted-color);
|
||||
@@ -1215,12 +1253,47 @@ user:
|
||||
return window.DOMPurify ? window.DOMPurify.sanitize(html) : html;
|
||||
}
|
||||
|
||||
function normalizePreviewImageUrl(rawUrl) {
|
||||
const value = String(rawUrl || '').trim();
|
||||
if (!value) return '';
|
||||
try {
|
||||
const parsed = new URL(value, window.location.origin);
|
||||
if (parsed.protocol === 'http:' || parsed.protocol === 'https:') {
|
||||
return parsed.href;
|
||||
}
|
||||
} catch (_error) {
|
||||
// ignore invalid urls
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
function renderDocumentMetaPreview() {
|
||||
const title = String(inpDocumentTitle?.value || '').trim();
|
||||
const summary = String(inpDocumentSummary?.value || '').trim();
|
||||
const tags = String(inpDocumentTags?.value || '').trim();
|
||||
const imageUrl = normalizePreviewImageUrl(inpDocumentImage?.value || '');
|
||||
const imageBlock = imageUrl
|
||||
? `<div class="docPreviewImageWrap"><img class="docPreviewImage" src="${escapeHtml(imageUrl)}" alt="Document image" loading="lazy" /></div>`
|
||||
: '';
|
||||
|
||||
return `
|
||||
<div class="docPreviewMeta">
|
||||
<div class="docPreviewMetaRow"><span class="docPreviewMetaLabel">Title:</span> ${escapeHtml(title || '—')}</div>
|
||||
<div class="docPreviewMetaRow"><span class="docPreviewMetaLabel">Summary:</span> ${escapeHtml(summary || '—')}</div>
|
||||
<div class="docPreviewMetaRow"><span class="docPreviewMetaLabel">Tags:</span> ${escapeHtml(tags || '—')}</div>
|
||||
${imageBlock}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
function updateDocumentView() {
|
||||
if (!taDocument || !divDocumentPreview) return;
|
||||
if (documentViewMode === 'markdown') {
|
||||
taDocument.style.display = 'none';
|
||||
divDocumentPreview.style.display = 'block';
|
||||
divDocumentPreview.innerHTML = renderDocumentPreview(taDocument.value || '');
|
||||
const metaHtml = renderDocumentMetaPreview();
|
||||
const bodyHtml = renderDocumentPreview(taDocument.value || '');
|
||||
divDocumentPreview.innerHTML = `${metaHtml}<div class="docPreviewContent">${bodyHtml}</div>`;
|
||||
return;
|
||||
}
|
||||
taDocument.style.display = 'block';
|
||||
@@ -1803,13 +1876,21 @@ user:
|
||||
}
|
||||
|
||||
function enforceDefaultSkillSelection() {
|
||||
const convo = getCurrentConversation();
|
||||
if (!convo) return;
|
||||
const defaultKey = getDefaultDocumentSkillKey();
|
||||
if (!defaultKey) return;
|
||||
const hasDefaultSkill = skills.some((s) => s.key === defaultKey);
|
||||
if (!hasDefaultSkill) return;
|
||||
|
||||
const convo = getCurrentConversation();
|
||||
if (!convo) {
|
||||
if (!selectedSkillKeys.includes(defaultKey)) {
|
||||
selectedSkillKeys = [...selectedSkillKeys, defaultKey];
|
||||
renderSkillsList();
|
||||
renderSkillsEditor();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const removed = new Set(getConversationRemovedSkillKeys(convo));
|
||||
const selected = new Set(getConversationSkillKeys(convo));
|
||||
if (!removed.has(defaultKey) && !selected.has(defaultKey)) {
|
||||
@@ -3518,6 +3599,7 @@ user:
|
||||
inputEl.addEventListener('input', () => {
|
||||
if (CURRENT_NOTE) setDocumentInfo(`Editing draft • d:${CURRENT_NOTE} • UNSAVED`);
|
||||
scheduleDocumentAutosave();
|
||||
updateDocumentView();
|
||||
renderCurrentConversation();
|
||||
});
|
||||
inputEl.addEventListener('blur', () => {
|
||||
@@ -4061,15 +4143,40 @@ user:
|
||||
});
|
||||
}
|
||||
|
||||
function upsertDefaultDocumentSkillDraft() {
|
||||
if (!currentPubkey) return;
|
||||
const key = getDefaultDocumentSkillKey();
|
||||
if (!key) return;
|
||||
if (skills.some((s) => s.key === key)) return;
|
||||
|
||||
const llm = String(aiConfig.model || 'default').trim() || 'default';
|
||||
const temperature = Number(aiConfig.temperature ?? 0);
|
||||
const maxTokens = Math.max(1, Math.floor(Number(aiConfig.max_tokens ?? 10000)));
|
||||
const evt = {
|
||||
kind: 31123,
|
||||
created_at: Math.floor(Date.now() / 1000),
|
||||
pubkey: currentPubkey,
|
||||
id: uid(),
|
||||
tags: [
|
||||
['d', DEFAULT_DOCUMENT_SKILL_SLUG],
|
||||
['description', DEFAULT_DOCUMENT_SKILL_DESCRIPTION],
|
||||
['llm', llm],
|
||||
['temperature', String(temperature)],
|
||||
['max_tokens', String(maxTokens)]
|
||||
],
|
||||
content: DEFAULT_DOCUMENT_SKILL_TEMPLATE
|
||||
};
|
||||
upsertSkillFromEvent(evt);
|
||||
}
|
||||
|
||||
async function ensureDefaultDocumentSkillExists() {
|
||||
if (!currentPubkey) return;
|
||||
const slug = DEFAULT_DOCUMENT_SKILL_SLUG;
|
||||
const key = getDefaultDocumentSkillKey();
|
||||
const existingInMemory = skills.find((s) => s.key === key);
|
||||
if (existingInMemory) {
|
||||
enforceDefaultSkillSelection();
|
||||
return;
|
||||
}
|
||||
|
||||
// Ensure visibility/editability immediately, even before network round-trips.
|
||||
upsertDefaultDocumentSkillDraft();
|
||||
enforceDefaultSkillSelection();
|
||||
|
||||
const filter = {
|
||||
kinds: [31123],
|
||||
@@ -4128,13 +4235,18 @@ user:
|
||||
};
|
||||
|
||||
const skillEvent = buildSkillEventFromEditor(draftSkill);
|
||||
await publishEvent(skillEvent);
|
||||
upsertSkillFromEvent({
|
||||
...skillEvent,
|
||||
pubkey: currentPubkey,
|
||||
id: uid()
|
||||
});
|
||||
setStatus('Created default-document-skill.');
|
||||
try {
|
||||
await publishEvent(skillEvent);
|
||||
upsertSkillFromEvent({
|
||||
...skillEvent,
|
||||
pubkey: currentPubkey,
|
||||
id: uid()
|
||||
});
|
||||
setStatus('Created default-document-skill.');
|
||||
} catch (error) {
|
||||
console.warn('[document.html] failed to publish default-document-skill, keeping local draft:', error);
|
||||
setStatus('Default document skill loaded locally (publish pending).');
|
||||
}
|
||||
enforceDefaultSkillSelection();
|
||||
}
|
||||
|
||||
|
||||
+3
-3
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"VERSION": "v0.7.8",
|
||||
"VERSION_NUMBER": "0.7.8",
|
||||
"BUILD_DATE": "2026-04-28T14:39:51.733Z"
|
||||
"VERSION": "v0.7.10",
|
||||
"VERSION_NUMBER": "0.7.10",
|
||||
"BUILD_DATE": "2026-04-28T16:33:28.943Z"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user