Skip to content

Commit fa369e5

Browse files
hspencerclaude
andcommitted
v0.2.3: UX refinements — delete confirmations, tooltip redesign
- Delete concept button: discrete trash icon instead of red button, custom confirm dialog showing section count - Excerpt tooltip: inverted colors (light in day, dark in night), trash icon instead of ✕, inline confirmation before deleting - Tooltip concept chips use accent color, border matches theme Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent cf00dd4 commit fa369e5

6 files changed

Lines changed: 166 additions & 22 deletions

File tree

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "constel",
3-
"version": "0.2.2",
3+
"version": "0.2.3",
44
"type": "module",
55
"description": "con§tel — herramienta de análisis temático de corpus textuales",
66
"scripts": {

public/css/tab-reader.css

Lines changed: 105 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,7 @@
212212
border-right: 1px solid var(--border);
213213
background: var(--surface);
214214
overflow: hidden;
215+
position: relative;
215216
}
216217

217218
.concept-detail-panel[hidden] {
@@ -360,15 +361,64 @@
360361
flex-shrink: 0;
361362
}
362363

363-
.btn-danger {
364-
background: transparent;
364+
.btn-delete-concept {
365+
color: var(--muted);
366+
opacity: 0.5;
367+
transition: opacity 0.2s, color 0.2s;
368+
padding: 4px;
369+
}
370+
371+
.btn-delete-concept:hover {
372+
opacity: 1;
365373
color: var(--danger, #c44);
366-
border: 1px solid var(--danger, #c44);
367374
}
368375

369-
.btn-danger:hover {
376+
/* ── Confirm delete dialog ── */
377+
.confirm-delete-overlay {
378+
display: none;
379+
position: absolute;
380+
inset: 0;
381+
background: rgba(0,0,0,0.25);
382+
z-index: 100;
383+
align-items: center;
384+
justify-content: center;
385+
}
386+
387+
.confirm-delete-overlay.visible {
388+
display: flex;
389+
}
390+
391+
.confirm-delete-dialog {
392+
background: var(--surface);
393+
border: 1px solid var(--border);
394+
border-radius: 8px;
395+
padding: 20px 24px;
396+
max-width: 320px;
397+
box-shadow: 0 4px 20px rgba(0,0,0,0.15);
398+
text-align: center;
399+
}
400+
401+
.confirm-delete-dialog p {
402+
margin: 0 0 16px;
403+
font-size: 14px;
404+
color: var(--ink);
405+
line-height: 1.4;
406+
}
407+
408+
.confirm-delete-buttons {
409+
display: flex;
410+
gap: 8px;
411+
justify-content: center;
412+
}
413+
414+
.btn-confirm-delete {
370415
background: var(--danger, #c44);
371416
color: white;
417+
border-color: var(--danger, #c44);
418+
}
419+
420+
.btn-confirm-delete:hover {
421+
background: #a33;
372422
}
373423

374424
/* ── Texto del lector (derecha) ──────────────────────────────────────── */
@@ -434,10 +484,11 @@
434484
display: none;
435485
align-items: center;
436486
gap: var(--space-xs);
437-
background: var(--ink);
438-
color: var(--surface);
487+
background: var(--surface);
488+
color: var(--ink);
439489
padding: var(--space-xs) var(--space-sm);
440490
border-radius: var(--radius);
491+
border: 1px solid var(--border);
441492
box-shadow: var(--shadow-lg);
442493
font-size: var(--font-size-sm);
443494
white-space: nowrap;
@@ -451,7 +502,7 @@
451502
}
452503

453504
.tooltip-concept {
454-
color: var(--accent-light, #8fc);
505+
color: var(--accent);
455506
cursor: pointer;
456507
font-weight: 500;
457508
}
@@ -464,39 +515,78 @@
464515
display: flex;
465516
gap: 2px;
466517
margin-left: var(--space-xs);
467-
border-left: 1px solid rgba(255,255,255,0.2);
518+
border-left: 1px solid var(--border);
468519
padding-left: var(--space-xs);
469520
}
470521

471522
.tooltip-btn {
472523
background: transparent;
473524
border: none;
474-
color: var(--surface);
525+
color: var(--muted);
475526
cursor: pointer;
476527
padding: 2px 4px;
477528
border-radius: var(--radius);
478529
font-size: 0.8125rem;
479-
opacity: 0.7;
480-
transition: opacity 0.15s;
530+
opacity: 0.6;
531+
transition: opacity 0.15s, color 0.15s;
481532
}
482533

483534
.tooltip-btn:hover {
484535
opacity: 1;
485-
background: rgba(255,255,255,0.15);
536+
color: var(--ink);
486537
}
487538

488539
.tooltip-btn.tooltip-delete {
489-
color: var(--danger, #c44);
490-
opacity: 0.8;
491-
font-weight: 600;
540+
opacity: 0.5;
492541
}
493542

494543
.tooltip-btn.tooltip-delete:hover {
495544
opacity: 1;
545+
color: var(--danger, #c44);
546+
}
547+
548+
/* ── Confirm delete toast (inline en tooltip) ── */
549+
.excerpt-tooltip.confirming .tooltip-concepts,
550+
.excerpt-tooltip.confirming .tooltip-actions {
551+
display: none;
552+
}
553+
554+
.tooltip-confirm {
555+
display: flex;
556+
align-items: center;
557+
gap: var(--space-sm);
558+
font-size: var(--font-size-sm);
559+
}
560+
561+
.tooltip-confirm-msg {
562+
color: var(--ink);
563+
}
564+
565+
.tooltip-confirm-yes {
566+
background: transparent;
567+
border: none;
568+
color: var(--danger, #c44);
569+
cursor: pointer;
570+
font-weight: 600;
571+
font-size: var(--font-size-sm);
572+
padding: 2px 6px;
573+
border-radius: var(--radius);
574+
}
575+
576+
.tooltip-confirm-yes:hover {
496577
background: var(--danger, #c44);
497578
color: white;
498579
}
499580

581+
.tooltip-confirm-no {
582+
background: transparent;
583+
border: none;
584+
color: var(--muted);
585+
cursor: pointer;
586+
font-size: var(--font-size-sm);
587+
padding: 2px 6px;
588+
}
589+
500590
/* ── Modo "agregar sección" ──────────────────────────────────────────── */
501591
.reader-content.add-section-mode {
502592
cursor: text;

public/index.html

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,9 +108,21 @@ <h2>Vista previa</h2>
108108
</div>
109109
<div class="concept-detail-excerpts" id="conceptDetailExcerpts"></div>
110110
<div class="concept-detail-actions">
111-
<button class="btn-sm btn-danger" id="conceptDetailDelete" data-i18n="reader.delete_concept">Eliminar concepto</button>
111+
<button class="btn-icon btn-delete-concept" id="conceptDetailDelete" title="Eliminar concepto">
112+
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="3 6 5 6 21 6"/><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/><line x1="10" y1="11" x2="10" y2="17"/><line x1="14" y1="11" x2="14" y2="17"/></svg>
113+
</button>
112114
<button class="btn-sm" id="conceptDetailAddExcerpt" data-i18n="reader.add_section">+ Agregar sección</button>
113115
</div>
116+
<!-- Confirm delete dialog -->
117+
<div class="confirm-delete-overlay" id="confirmDeleteOverlay">
118+
<div class="confirm-delete-dialog">
119+
<p id="confirmDeleteMsg"></p>
120+
<div class="confirm-delete-buttons">
121+
<button class="btn-sm" id="confirmDeleteCancel">Cancelar</button>
122+
<button class="btn-sm btn-confirm-delete" id="confirmDeleteOk">Eliminar</button>
123+
</div>
124+
</div>
125+
</div>
114126
</div>
115127
<!-- Derecha: texto -->
116128
<div class="reader-text-panel" id="readerText">

public/js/components/text-highlighter.js

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -121,16 +121,24 @@ function showExcerptTooltip(mark, onExcerptClick) {
121121
`<span class="tooltip-concept">${escapeHtml(c.label)}</span>`
122122
).join(" ");
123123

124+
const trashSvg = `<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="3 6 5 6 21 6"/><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/></svg>`;
125+
124126
_tooltip.innerHTML = `
125127
<div class="tooltip-concepts">${conceptChips}</div>
126128
<div class="tooltip-actions">
127-
<button class="tooltip-btn tooltip-delete" data-action="delete" title="Eliminar excerpt">✕</button>
129+
<button class="tooltip-btn tooltip-delete" data-action="delete" title="Eliminar sección">${trashSvg}</button>
130+
</div>
131+
<div class="tooltip-confirm" style="display:none">
132+
<span class="tooltip-confirm-msg">¿Eliminar sección?</span>
133+
<button class="tooltip-confirm-yes">Eliminar</button>
134+
<button class="tooltip-confirm-no">No</button>
128135
</div>
129136
`;
130137

131138
// posicionar
132139
const rect = mark.getBoundingClientRect();
133140
_tooltip.style.display = "flex";
141+
_tooltip.classList.remove("confirming");
134142

135143
// medir tooltip
136144
const tooltipRect = _tooltip.getBoundingClientRect();
@@ -147,13 +155,27 @@ function showExcerptTooltip(mark, onExcerptClick) {
147155
_tooltip.style.left = left + "px";
148156
_tooltip.style.top = top + "px";
149157

150-
// acciones
158+
// acciones: mostrar confirmación
151159
_tooltip.querySelector('[data-action="delete"]')?.addEventListener("click", (e) => {
160+
e.stopPropagation();
161+
_tooltip.classList.add("confirming");
162+
_tooltip.querySelector(".tooltip-confirm").style.display = "flex";
163+
});
164+
165+
// confirmar eliminación
166+
_tooltip.querySelector(".tooltip-confirm-yes")?.addEventListener("click", (e) => {
152167
e.stopPropagation();
153168
removeExcerpt(excId);
154169
hideExcerptTooltip();
155170
});
156171

172+
// cancelar
173+
_tooltip.querySelector(".tooltip-confirm-no")?.addEventListener("click", (e) => {
174+
e.stopPropagation();
175+
_tooltip.classList.remove("confirming");
176+
_tooltip.querySelector(".tooltip-confirm").style.display = "none";
177+
});
178+
157179
// click en un chip de concepto → abrir detalle en sidebar
158180
_tooltip.querySelectorAll(".tooltip-concept").forEach((chip, i) => {
159181
chip.addEventListener("click", (e) => {

public/js/tabs/reader.js

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,17 +51,37 @@ export function initReaderTab() {
5151
closeConceptDetail();
5252
});
5353

54-
// concept detail: delete
54+
// concept detail: delete (custom confirm dialog)
55+
const confirmOverlay = document.getElementById("confirmDeleteOverlay");
56+
const confirmMsg = document.getElementById("confirmDeleteMsg");
57+
const confirmOk = document.getElementById("confirmDeleteOk");
58+
const confirmCancel = document.getElementById("confirmDeleteCancel");
59+
5560
document.getElementById("conceptDetailDelete")?.addEventListener("click", () => {
5661
if (!selectedConceptId) return;
5762
const c = state.concepts[selectedConceptId];
5863
if (!c) return;
59-
if (confirm(`¿Eliminar concepto [${c.label}]? Se desvinculará de todos sus excerpts.`)) {
64+
const excerptCount = Object.values(state.excerpts).filter(e => e.conceptIds?.includes(selectedConceptId)).length;
65+
confirmMsg.textContent = `¿Eliminar «${c.label}»? Tiene ${excerptCount} sección${excerptCount !== 1 ? "es" : ""}.`;
66+
confirmOverlay.classList.add("visible");
67+
});
68+
69+
confirmOk?.addEventListener("click", () => {
70+
confirmOverlay.classList.remove("visible");
71+
if (selectedConceptId) {
6072
removeConcept(selectedConceptId);
6173
closeConceptDetail();
6274
}
6375
});
6476

77+
confirmCancel?.addEventListener("click", () => {
78+
confirmOverlay.classList.remove("visible");
79+
});
80+
81+
confirmOverlay?.addEventListener("click", (e) => {
82+
if (e.target === confirmOverlay) confirmOverlay.classList.remove("visible");
83+
});
84+
6585
// concept detail: add section
6686
document.getElementById("conceptDetailAddExcerpt")?.addEventListener("click", () => {
6787
if (!selectedConceptId || !currentSourceId) return;

public/version.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
{"version":"0.2.2"}
1+
{"version":"0.2.3"}

0 commit comments

Comments
 (0)