/* ---------- Object Overlay Management ---------- */
/* DOM Elements */
let attributesObjectViewBtn, objectOverlay, ovTitle, ovBody, closeOverlayBtn;
/* ---------- Attribute Row Creation ---------- */
function makeAttrRow(scope, scopeId, idx, attr) {
const row = document.createElement('div');
row.className = 'attr-row';
const keyInput = document.createElement('input');
keyInput.type = 'text';
keyInput.placeholder = 'Attribute name';
keyInput.className = 'attr-key';
keyInput.value = attr.key ?? '';
keyInput.dataset.scope = scope;
keyInput.dataset.scopeId = scopeId;
keyInput.dataset.index = String(idx);
const valInput = document.createElement('input');
valInput.type = 'text';
valInput.placeholder = 'Value';
valInput.className = 'attr-val';
valInput.value = attr.value ?? '';
valInput.dataset.scope = scope;
valInput.dataset.scopeId = scopeId;
valInput.dataset.index = String(idx);
const delBtn = document.createElement('button');
delBtn.textContent = '🗑️';
delBtn.title = 'Remove attribute';
delBtn.className = 'attr-del';
delBtn.dataset.scope = scope;
delBtn.dataset.scopeId = scopeId;
delBtn.dataset.index = String(idx);
row.appendChild(keyInput);
row.appendChild(valInput);
row.appendChild(delBtn);
return row;
}
/* ---------- Object Attributes Block ---------- */
function buildObjectAttributesBlock(obj) {
const wrap = document.createElement('div');
wrap.className = 'ov-obj-attrs';
wrap.style.background = '#151515';
wrap.style.border = '1px solid #2a2a2a';
wrap.style.borderRadius = '.6rem';
wrap.style.padding = '.6rem';
wrap.style.marginBottom = '.8rem';
const head = document.createElement('div');
head.style.display = 'flex';
head.style.alignItems = 'center';
head.style.gap = '.6rem';
const title = document.createElement('h3');
title.textContent = 'Object Attributes';
title.style.margin = '.1rem 0';
title.style.color = '#ddd';
const addKey = document.createElement('input');
addKey.type = 'text';
addKey.placeholder = 'Attribute name';
addKey.id = 'ov-add-obj-key';
const addVal = document.createElement('input');
addVal.type = 'text';
addVal.placeholder = 'Value';
addVal.id = 'ov-add-obj-val';
const addBtn = document.createElement('button');
addBtn.textContent = '➕ Add';
addBtn.className = 'attr-add';
addBtn.dataset.scope = 'object';
addBtn.dataset.scopeId = obj.id;
head.appendChild(title);
head.appendChild(addKey);
head.appendChild(addVal);
head.appendChild(addBtn);
const list = document.createElement('div');
list.className = 'attr-list';
list.dataset.scope = 'object';
list.dataset.scopeId = obj.id;
for (let i = 0; i < (obj.attributes?.length || 0); i++) {
list.appendChild(makeAttrRow('object', obj.id, i, obj.attributes[i]));
}
wrap.appendChild(head);
wrap.appendChild(list);
return wrap;
}
/* ---------- Line Block ---------- */
function buildLineBlock(line) {
const block = document.createElement('div');
block.className = 'ov-line';
const h = document.createElement('h3');
h.textContent = line.name;
const attrHead = document.createElement('div');
attrHead.style.display = 'flex';
attrHead.style.alignItems = 'center';
attrHead.style.gap = '.6rem';
attrHead.style.marginBottom = '.3rem';
const attrTitle = document.createElement('div');
attrTitle.textContent = 'Line Attributes';
attrTitle.style.color = '#bbb';
const addKey = document.createElement('input');
addKey.type = 'text';
addKey.placeholder = 'Attribute name';
addKey.className = 'ov-add-line-key';
addKey.dataset.lineId = line.id;
const addVal = document.createElement('input');
addVal.type = 'text';
addVal.placeholder = 'Value';
addVal.className = 'ov-add-line-val';
addVal.dataset.lineId = line.id;
const addBtn = document.createElement('button');
addBtn.textContent = '➕ Add';
addBtn.className = 'attr-add';
addBtn.dataset.scope = 'line';
addBtn.dataset.scopeId = line.id;
attrHead.appendChild(attrTitle);
attrHead.appendChild(addKey);
attrHead.appendChild(addVal);
attrHead.appendChild(addBtn);
const list = document.createElement('div');
list.className = 'attr-list';
list.dataset.scope = 'line';
list.dataset.scopeId = line.id;
for (let i = 0; i < (line.attributes?.length || 0); i++) {
list.appendChild(makeAttrRow('line', line.id, i, line.attributes[i]));
}
const strip = document.createElement('div');
strip.className = 'ov-strip';
for (const t of (line.items || [])) {
const item = document.createElement('div');
item.className = 'ov-item';
const img = document.createElement('img');
img.src = t.thumbDataURL;
const badge = document.createElement('div');
badge.className = 'ov-badge';
badge.textContent = t.badge;
item.appendChild(img);
item.appendChild(badge);
strip.appendChild(item);
}
block.appendChild(h);
block.appendChild(attrHead);
block.appendChild(list);
block.appendChild(strip);
return block;
}
/* ---------- Overlay Rendering ---------- */
function renderObjectOverlay() {
const o = window.WorkspaceAPI.activeObject();
if(!o) return;
ovTitle.textContent = o.name;
ovBody.innerHTML = '';
ovBody.appendChild(buildObjectAttributesBlock(o));
for (const line of o.lines) {
ovBody.appendChild(buildLineBlock(line));
}
}
/* ---------- Overlay Controls ---------- */
function openObjectView() {
renderObjectOverlay();
objectOverlay.classList.add('open');
objectOverlay.setAttribute('aria-hidden', 'false');
}
function closeObjectView() {
objectOverlay.classList.remove('open');
objectOverlay.setAttribute('aria-hidden', 'true');
}
/* ---------- Attribute Event Handlers ---------- */
function initAttributeEvents() {
// Add/remove attribute handlers
ovBody.addEventListener('click', (e) => {
const btn = e.target.closest('.attr-add');
const del = e.target.closest('.attr-del');
if (btn) {
const scope = btn.dataset.scope;
const scopeId = btn.dataset.scopeId;
if (scope === 'object') {
const keyEl = document.getElementById('ov-add-obj-key');
const valEl = document.getElementById('ov-add-obj-val');
const key = (keyEl.value || '').trim();
const value = (valEl.value || '').trim();
if (!key) {
keyEl.focus();
return;
}
const o = window.WorkspaceAPI.activeObject();
if(!o) return;
o.attributes = o.attributes || [];
o.attributes.push({key, value});
keyEl.value = '';
valEl.value = '';
renderObjectOverlay();
return;
}
if (scope === 'line') {
const o = window.WorkspaceAPI.activeObject();
if(!o) return;
const line = o.lines.find(l => l.id === scopeId);
if(!line) return;
const head = btn.parentElement;
const keyEl = head.querySelector('.ov-add-line-key');
const valEl = head.querySelector('.ov-add-line-val');
const key = (keyEl?.value || '').trim();
const value = (valEl?.value || '').trim();
if (!key) {
keyEl?.focus();
return;
}
line.attributes = line.attributes || [];
line.attributes.push({key, value});
if (keyEl) keyEl.value = '';
if (valEl) valEl.value = '';
renderObjectOverlay();
return;
}
}
if (del) {
const scope = del.dataset.scope;
const scopeId = del.dataset.scopeId;
const idx = parseInt(del.dataset.index, 10);
const o = window.WorkspaceAPI.activeObject();
if(!o) return;
if (scope === 'object') {
if (o.attributes && o.attributes[idx]) {
o.attributes.splice(idx, 1);
}
renderObjectOverlay();
return;
}
if (scope === 'line') {
const line = o.lines.find(l => l.id === scopeId);
if (line && line.attributes && line.attributes[idx]) {
line.attributes.splice(idx, 1);
}
renderObjectOverlay();
return;
}
}
});
// Live editing of attributes
ovBody.addEventListener('input', (e) => {
const keyEl = e.target.closest('.attr-key');
const valEl = e.target.closest('.attr-val');
const o = window.WorkspaceAPI.activeObject();
if(!o) return;
function apply(el, field) {
const scope = el.dataset.scope;
const scopeId = el.dataset.scopeId;
const idx = parseInt(el.dataset.index, 10);
if (scope === 'object') {
if (!o.attributes || !o.attributes[idx]) return;
o.attributes[idx][field] = el.value;
} else if (scope === 'line') {
const line = o.lines.find(l => l.id === scopeId);
if (!line || !line.attributes || !line.attributes[idx]) return;
line.attributes[idx][field] = el.value;
}
}
if (keyEl) apply(keyEl, 'key');
if (valEl) apply(valEl, 'value');
});
}
/* ---------- Initialization ---------- */
function initializeAttributes() {
try {
// Get DOM elements
attributesObjectViewBtn = document.getElementById('objectViewBtn');
objectOverlay = document.getElementById('objectOverlay');
ovTitle = document.getElementById('ovTitle');
ovBody = document.getElementById('ovBody');
closeOverlayBtn = document.getElementById('closeOverlayBtn');
// Check required DOM elements and dependencies
if (!attributesObjectViewBtn || !objectOverlay || !ovBody) {
throw new Error('Required DOM elements not found');
}
if (!window.WorkspaceAPI || typeof window.WorkspaceAPI.activeObject !== 'function') {
throw new Error('WorkspaceAPI not available');
}
// Set up event handlers
attributesObjectViewBtn.addEventListener('click', openObjectView);
closeOverlayBtn.addEventListener('click', closeObjectView);
objectOverlay.addEventListener('click', (e) => {
if(e.target === objectOverlay) closeObjectView();
});
initAttributeEvents();
} catch (error) {
alert(`Attributes module failed to initialize: ${error.message}`);
throw error;
}
}
// Export API for other modules
window.AttributesAPI = {
renderObjectOverlay,
openObjectView,
closeObjectView
};