mirror of
https://github.com/PR0M3TH3AN/Artstr-Studio.git
synced 2026-11-08 22:50:09 +00:00
Linked Designs: Credits panel in metadata sidebar
Per LINKED_DESIGNS_FEATURE.md §10.1, surface each distinct
contributor reachable via the host's linked layers as a clickable
@npub entry in a "🔗 Credits" panel in the metadata sidebar.
Walks state.layers + every deck slide's layers, groups distinct
linked refs by pubkey (so 5 copies of the same source count as 1
design from that creator), excludes self, and renders one entry
per contributor sorted by descending design count. Each entry
links to the /u/<npub> profile page in a new tab.
Pubkey is parsed straight from design.ref's address coord, so the
panel populates without waiting for the resolver. Fieldset is
hidden entirely when there are no linked layers — non-collaborative
designs stay clutter-free.
Wired into render() so it stays current as the user adds, removes,
pins, replaces, or unlinks layers.
This commit is contained in:
1 parent
f4a858e88f
commit
4e1073bd7c
1 file changed
+74
@@ -2955,6 +2955,12 @@
|
||||
<p class="hint">Title and category are required to save or publish.</p>
|
||||
</fieldset>
|
||||
|
||||
<fieldset class="template-only" id="creditsFieldset" style="display:none;margin-top:10px">
|
||||
<legend style="font-size:.85rem;font-weight:600">🔗 Credits</legend>
|
||||
<ul id="creditsList" style="margin:6px 0 0;padding:0;list-style:none;font-size:.82rem"></ul>
|
||||
<p class="hint" style="margin:6px 0 0;font-size:.7rem">Distinct creators reachable via the linked layers in this design. Tagged on the published event for indexers.</p>
|
||||
</fieldset>
|
||||
|
||||
|
||||
<fieldset class="workflowStep template-only" id="exportFieldset">
|
||||
<legend data-step="4">Display options</legend>
|
||||
@@ -6681,6 +6687,73 @@
|
||||
return { contributorTags, linkTags };
|
||||
}
|
||||
|
||||
// Build the Credits panel that lives in the metadata sidebar
|
||||
// (LINKED_DESIGNS_FEATURE.md §10.1). Walks state.layers + every
|
||||
// deck slide's layers, groups distinct linked refs by pubkey,
|
||||
// and renders one anchor per contributor with a click-through to
|
||||
// the /u/<npub> profile page. Excludes the current user — they
|
||||
// already authored the host, so self-attribution is noise.
|
||||
//
|
||||
// The contributor pubkey comes straight from the address coord
|
||||
// in `design.ref`, so this can populate immediately without
|
||||
// waiting for the resolver. Hidden when there are no linked
|
||||
// layers so non-collaborative designs stay clutter-free.
|
||||
function renderCreditsPanel() {
|
||||
const fieldset = $('creditsFieldset');
|
||||
const list = $('creditsList');
|
||||
if (!fieldset || !list) return;
|
||||
const allLayers = [];
|
||||
if (Array.isArray(state.layers)) allLayers.push(...state.layers);
|
||||
if (Array.isArray(state.deck?.slides)) {
|
||||
for (const s of state.deck.slides) {
|
||||
if (Array.isArray(s?.layers)) allLayers.push(...s.layers);
|
||||
}
|
||||
}
|
||||
const myPubkey = state.community?.myPubkey || '';
|
||||
// pubkey → Set<ref> so multiple layers pointing at the same
|
||||
// source count as one design.
|
||||
const byPubkey = new Map();
|
||||
for (const l of allLayers) {
|
||||
if (l?.type !== 'design' || l.design?.kind !== 'linked' || !l.design.ref) continue;
|
||||
const coord = linkedDesigns._parseAddressCoord(l.design.ref);
|
||||
if (!coord?.pubkey) continue;
|
||||
if (coord.pubkey === myPubkey) continue;
|
||||
let refs = byPubkey.get(coord.pubkey);
|
||||
if (!refs) { refs = new Set(); byPubkey.set(coord.pubkey, refs); }
|
||||
refs.add(l.design.ref);
|
||||
}
|
||||
if (byPubkey.size === 0) {
|
||||
fieldset.style.display = 'none';
|
||||
list.innerHTML = '';
|
||||
return;
|
||||
}
|
||||
// Descending design count, then by pubkey for stable ordering.
|
||||
const entries = [...byPubkey.entries()].sort(
|
||||
(a, b) => b[1].size - a[1].size || a[0].localeCompare(b[0])
|
||||
);
|
||||
list.innerHTML = '';
|
||||
for (const [pk, refs] of entries) {
|
||||
const li = document.createElement('li');
|
||||
li.style.cssText = 'margin:4px 0;line-height:1.35';
|
||||
const a = document.createElement('a');
|
||||
a.href = profileUrlForPubkey(pk);
|
||||
a.target = '_blank';
|
||||
a.rel = 'noopener noreferrer';
|
||||
a.style.cssText = 'font-family:monospace;color:#1e40af;text-decoration:none';
|
||||
let np = '';
|
||||
try { np = encodeNpub(pk); } catch {}
|
||||
a.textContent = '@' + (np ? np.slice(0, 12) + '…' + np.slice(-6) : pk.slice(0, 10) + '…');
|
||||
a.title = np || pk;
|
||||
li.appendChild(a);
|
||||
const count = document.createElement('span');
|
||||
count.style.cssText = 'color:#666;margin-left:4px';
|
||||
count.textContent = ` (${refs.size} design${refs.size === 1 ? '' : 's'})`;
|
||||
li.appendChild(count);
|
||||
list.appendChild(li);
|
||||
}
|
||||
fieldset.style.display = '';
|
||||
}
|
||||
|
||||
// Pre-publish hook. Linked layers carry render-only fields
|
||||
// (lastResolvedAt, lastResolvedSize, transient fallbackPreview)
|
||||
// that are session-local and should not ship in the published
|
||||
@@ -9744,6 +9817,7 @@
|
||||
syncDesignerInputs();
|
||||
updateBodyClasses();
|
||||
renderDeckSorter();
|
||||
renderCreditsPanel();
|
||||
if (state.templateMode === 'pixelart') ensureStandalonePixelEditor();
|
||||
// After updateBodyClasses so the designer view is displayed/laid out —
|
||||
// the disc image's cover-pan math needs the wrapper's real size.
|
||||
|
||||
Reference in new issue
Block a user