mirror of
https://github.com/willmiao/ComfyUI-Lora-Manager.git
synced 2026-03-24 22:52:12 -03:00
feat: add clickable version rows with Civitai links
- Add CSS class `is-clickable` to make version rows appear interactive - Implement URL builder function for Civitai model version links - Make version names clickable links that open Civitai pages in new tabs - Add tooltips and data attributes for enhanced user experience - Pass modelId to version rendering to support external linking This improves user navigation by allowing direct access to model versions on Civitai without leaving the application.
This commit is contained in:
@@ -107,6 +107,10 @@
|
|||||||
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.08);
|
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.08);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.model-version-row.is-clickable {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
.model-version-row.is-current {
|
.model-version-row.is-current {
|
||||||
border-color: var(--lora-accent);
|
border-color: var(--lora-accent);
|
||||||
box-shadow: 0 0 0 1px color-mix(in oklch, var(--lora-accent) 65%, transparent),
|
box-shadow: 0 0 0 1px color-mix(in oklch, var(--lora-accent) 65%, transparent),
|
||||||
|
|||||||
@@ -7,6 +7,20 @@ import { formatFileSize } from './utils.js';
|
|||||||
|
|
||||||
const VIDEO_EXTENSIONS = ['.mp4', '.webm', '.mov', '.mkv'];
|
const VIDEO_EXTENSIONS = ['.mp4', '.webm', '.mov', '.mkv'];
|
||||||
|
|
||||||
|
function buildCivitaiVersionUrl(modelId, versionId) {
|
||||||
|
if (modelId == null || versionId == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const normalizedModelId = String(modelId).trim();
|
||||||
|
const normalizedVersionId = String(versionId).trim();
|
||||||
|
if (!normalizedModelId || !normalizedVersionId) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const encodedModelId = encodeURIComponent(normalizedModelId);
|
||||||
|
const encodedVersionId = encodeURIComponent(normalizedVersionId);
|
||||||
|
return `https://civitai.com/models/${encodedModelId}?modelVersionId=${encodedVersionId}`;
|
||||||
|
}
|
||||||
|
|
||||||
function escapeHtml(value) {
|
function escapeHtml(value) {
|
||||||
if (value == null) {
|
if (value == null) {
|
||||||
return '';
|
return '';
|
||||||
@@ -176,7 +190,7 @@ function renderMediaMarkup(version) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function renderRow(version, options) {
|
function renderRow(version, options) {
|
||||||
const { latestLibraryVersionId, currentVersionId } = options;
|
const { latestLibraryVersionId, currentVersionId, modelId: parentModelId } = options;
|
||||||
const isCurrent = currentVersionId && version.versionId === currentVersionId;
|
const isCurrent = currentVersionId && version.versionId === currentVersionId;
|
||||||
const isNewer =
|
const isNewer =
|
||||||
typeof latestLibraryVersionId === 'number' &&
|
typeof latestLibraryVersionId === 'number' &&
|
||||||
@@ -201,8 +215,8 @@ function renderRow(version, options) {
|
|||||||
const deleteLabel = translate('modals.model.versions.actions.delete', {}, 'Delete');
|
const deleteLabel = translate('modals.model.versions.actions.delete', {}, 'Delete');
|
||||||
const ignoreLabel = translate(
|
const ignoreLabel = translate(
|
||||||
version.shouldIgnore
|
version.shouldIgnore
|
||||||
? 'modals.model.versions.actions.unignore'
|
? 'modals.model.versions.actions.unignore'
|
||||||
: 'modals.model.versions.actions.ignore',
|
: 'modals.model.versions.actions.ignore',
|
||||||
{},
|
{},
|
||||||
version.shouldIgnore ? 'Unignore' : 'Ignore'
|
version.shouldIgnore ? 'Unignore' : 'Ignore'
|
||||||
);
|
);
|
||||||
@@ -223,12 +237,37 @@ function renderRow(version, options) {
|
|||||||
}">${escapeHtml(ignoreLabel)}</button>`
|
}">${escapeHtml(ignoreLabel)}</button>`
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const versionName =
|
||||||
|
version.name ||
|
||||||
|
translate('modals.model.versions.labels.unnamed', {}, 'Untitled Version');
|
||||||
|
const linkTarget = buildCivitaiVersionUrl(
|
||||||
|
version.modelId || parentModelId,
|
||||||
|
version.versionId
|
||||||
|
);
|
||||||
|
const civitaiTooltip = translate(
|
||||||
|
'modals.model.actions.viewOnCivitai',
|
||||||
|
{},
|
||||||
|
'View on Civitai'
|
||||||
|
);
|
||||||
|
const versionNameMarkup = linkTarget
|
||||||
|
? `<a class="versions-tab-version-name" href="${escapeHtml(linkTarget)}" target="_blank" rel="noopener noreferrer" title="${escapeHtml(civitaiTooltip)}">${escapeHtml(versionName)}</a>`
|
||||||
|
: `<span class="versions-tab-version-name">${escapeHtml(versionName)}</span>`;
|
||||||
|
|
||||||
|
const rowAttributes = [
|
||||||
|
`class="model-version-row${isCurrent ? ' is-current' : ''}${linkTarget ? ' is-clickable' : ''}"`,
|
||||||
|
`data-version-id="${escapeHtml(version.versionId)}"`,
|
||||||
|
];
|
||||||
|
if (linkTarget) {
|
||||||
|
rowAttributes.push(`data-civitai-url="${escapeHtml(linkTarget)}"`);
|
||||||
|
rowAttributes.push(`title="${escapeHtml(civitaiTooltip)}"`);
|
||||||
|
}
|
||||||
|
|
||||||
return `
|
return `
|
||||||
<div class="model-version-row${isCurrent ? ' is-current' : ''}" data-version-id="${escapeHtml(version.versionId)}">
|
<div ${rowAttributes.join(' ')}>
|
||||||
${renderMediaMarkup(version)}
|
${renderMediaMarkup(version)}
|
||||||
<div class="version-details">
|
<div class="version-details">
|
||||||
<div class="version-title">
|
<div class="version-title">
|
||||||
<span class="versions-tab-version-name">${escapeHtml(version.name || translate('modals.model.versions.labels.unnamed', {}, 'Untitled Version'))}</span>
|
${versionNameMarkup}
|
||||||
</div>
|
</div>
|
||||||
<div class="version-badges">${badges.join('')}</div>
|
<div class="version-badges">${badges.join('')}</div>
|
||||||
<div class="version-meta">
|
<div class="version-meta">
|
||||||
@@ -413,6 +452,7 @@ export function initVersionsTab({
|
|||||||
markup += renderRow(version, {
|
markup += renderRow(version, {
|
||||||
latestLibraryVersionId,
|
latestLibraryVersionId,
|
||||||
currentVersionId: normalizedCurrentVersionId,
|
currentVersionId: normalizedCurrentVersionId,
|
||||||
|
modelId: record?.modelId ?? modelId,
|
||||||
});
|
});
|
||||||
return markup;
|
return markup;
|
||||||
})
|
})
|
||||||
@@ -608,32 +648,53 @@ export function initVersionsTab({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const actionButton = event.target.closest('[data-version-action]');
|
const actionButton = event.target.closest('[data-version-action]');
|
||||||
if (!actionButton) {
|
if (actionButton) {
|
||||||
|
const row = actionButton.closest('.model-version-row');
|
||||||
|
if (!row) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const versionId = Number(row.dataset.versionId);
|
||||||
|
const action = actionButton.dataset.versionAction;
|
||||||
|
|
||||||
|
switch (action) {
|
||||||
|
case 'download':
|
||||||
|
event.preventDefault();
|
||||||
|
await handleDownloadVersion(actionButton, versionId);
|
||||||
|
break;
|
||||||
|
case 'delete':
|
||||||
|
event.preventDefault();
|
||||||
|
await handleDeleteVersion(actionButton, versionId);
|
||||||
|
break;
|
||||||
|
case 'toggle-ignore':
|
||||||
|
event.preventDefault();
|
||||||
|
await handleToggleVersionIgnore(actionButton, versionId);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const row = actionButton.closest('.model-version-row');
|
|
||||||
|
const row = event.target.closest('.model-version-row.is-clickable');
|
||||||
if (!row) {
|
if (!row) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const versionId = Number(row.dataset.versionId);
|
if (event.target.closest('button')) {
|
||||||
const action = actionButton.dataset.versionAction;
|
return;
|
||||||
|
|
||||||
switch (action) {
|
|
||||||
case 'download':
|
|
||||||
event.preventDefault();
|
|
||||||
await handleDownloadVersion(actionButton, versionId);
|
|
||||||
break;
|
|
||||||
case 'delete':
|
|
||||||
event.preventDefault();
|
|
||||||
await handleDeleteVersion(actionButton, versionId);
|
|
||||||
break;
|
|
||||||
case 'toggle-ignore':
|
|
||||||
event.preventDefault();
|
|
||||||
await handleToggleVersionIgnore(actionButton, versionId);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
if (event.target.closest('.version-actions')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (event.target.closest('a')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const targetUrl = row.dataset.civitaiUrl;
|
||||||
|
if (!targetUrl) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
event.preventDefault();
|
||||||
|
window.open(targetUrl, '_blank', 'noopener,noreferrer');
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
Reference in New Issue
Block a user