|
|
@@ -67,9 +67,75 @@ let currentPipelineStatus = {};
|
|
|
async function init() {
|
|
|
await fetchRequirements();
|
|
|
setupEventListeners();
|
|
|
+ setupFloatingApplyToTooltips();
|
|
|
startStatusPolling();
|
|
|
}
|
|
|
|
|
|
+function setupFloatingApplyToTooltips() {
|
|
|
+ if (window.__applyToTooltipReady) return;
|
|
|
+ window.__applyToTooltipReady = true;
|
|
|
+
|
|
|
+ let floatingTooltip = null;
|
|
|
+ let activeTarget = null;
|
|
|
+
|
|
|
+ const hideTooltip = () => {
|
|
|
+ activeTarget = null;
|
|
|
+ if (floatingTooltip) {
|
|
|
+ floatingTooltip.remove();
|
|
|
+ floatingTooltip = null;
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ const positionTooltip = () => {
|
|
|
+ if (!floatingTooltip || !activeTarget) return;
|
|
|
+ const rect = activeTarget.getBoundingClientRect();
|
|
|
+ const tipRect = floatingTooltip.getBoundingClientRect();
|
|
|
+ const gap = 8;
|
|
|
+ const margin = 12;
|
|
|
+
|
|
|
+ let left = rect.left + rect.width / 2 - tipRect.width / 2;
|
|
|
+ left = Math.max(margin, Math.min(left, window.innerWidth - tipRect.width - margin));
|
|
|
+
|
|
|
+ let top = rect.bottom + gap;
|
|
|
+ if (top + tipRect.height > window.innerHeight - margin) {
|
|
|
+ top = rect.top - tipRect.height - gap;
|
|
|
+ }
|
|
|
+ top = Math.max(margin, top);
|
|
|
+
|
|
|
+ floatingTooltip.style.left = `${left}px`;
|
|
|
+ floatingTooltip.style.top = `${top}px`;
|
|
|
+ floatingTooltip.style.visibility = 'visible';
|
|
|
+ floatingTooltip.style.opacity = '1';
|
|
|
+ };
|
|
|
+
|
|
|
+ document.addEventListener('pointerover', (event) => {
|
|
|
+ const target = event.target.closest('.apply-to-path-item.has-tooltip');
|
|
|
+ if (!target) return;
|
|
|
+ const sourceTooltip = target.querySelector('.apply-to-tooltip');
|
|
|
+ if (!sourceTooltip) return;
|
|
|
+
|
|
|
+ activeTarget = target;
|
|
|
+ if (!floatingTooltip) {
|
|
|
+ floatingTooltip = document.createElement('div');
|
|
|
+ floatingTooltip.className = 'floating-apply-to-tooltip';
|
|
|
+ document.body.appendChild(floatingTooltip);
|
|
|
+ }
|
|
|
+ floatingTooltip.innerHTML = sourceTooltip.innerHTML;
|
|
|
+ floatingTooltip.style.visibility = 'hidden';
|
|
|
+ floatingTooltip.style.opacity = '0';
|
|
|
+ requestAnimationFrame(positionTooltip);
|
|
|
+ });
|
|
|
+
|
|
|
+ document.addEventListener('pointerout', (event) => {
|
|
|
+ const target = event.target.closest('.apply-to-path-item.has-tooltip');
|
|
|
+ if (!target || target.contains(event.relatedTarget)) return;
|
|
|
+ hideTooltip();
|
|
|
+ });
|
|
|
+
|
|
|
+ window.addEventListener('scroll', hideTooltip, true);
|
|
|
+ window.addEventListener('resize', hideTooltip);
|
|
|
+}
|
|
|
+
|
|
|
// Fetch Data
|
|
|
async function fetchRequirements() {
|
|
|
try {
|
|
|
@@ -2519,11 +2585,16 @@ window.renderStructuredData = function (items, type, parentItem = null) {
|
|
|
}
|
|
|
|
|
|
let tooltipHtml = '';
|
|
|
- if (typeof pathObj === 'object' && pathObj !== null && pathObj.rationale) {
|
|
|
+ if (
|
|
|
+ typeof pathObj === 'object'
|
|
|
+ && pathObj !== null
|
|
|
+ && (pathObj.rationale || pathObj.body_excerpt || pathObj.category_id)
|
|
|
+ ) {
|
|
|
tooltipHtml = `
|
|
|
<div class="apply-to-tooltip">
|
|
|
${pathObj.category_id ? `<span class="tooltip-id">id: ${pathObj.category_id}</span>` : ''}
|
|
|
- <span class="tooltip-rationale">${escapeApplyToText(pathObj.rationale)}</span>
|
|
|
+ ${pathObj.rationale ? `<span class="tooltip-rationale">${escapeApplyToText(pathObj.rationale)}</span>` : ''}
|
|
|
+ ${pathObj.body_excerpt ? `<span class="tooltip-body-excerpt">${escapeApplyToText(pathObj.body_excerpt)}</span>` : ''}
|
|
|
</div>
|
|
|
`;
|
|
|
}
|
|
|
@@ -2830,7 +2901,7 @@ window.renderStructuredData = function (items, type, parentItem = null) {
|
|
|
<td class="capability-cell">${capability && capability.inputs && capability.inputs.length > 0 ? renderDataObjList(capability.inputs) : '-'}</td>
|
|
|
<td class="capability-cell">${renderAction(capability)}</td>
|
|
|
<td class="capability-cell">${capability && capability.outputs && capability.outputs.length > 0 ? renderDataObjList(capability.outputs) : '-'}</td>
|
|
|
- <td class="capability-cell" style="font-size:0.9em;"><div class="capability-clamp">${applyTo ? renderApplyToVal(applyTo, suggestApplyTo) : '-'}</div></td>
|
|
|
+ <td class="capability-cell" style="font-size:0.9em;"><div class="capability-clamp apply-to-clamp">${applyTo ? renderApplyToVal(applyTo, suggestApplyTo) : '-'}</div></td>
|
|
|
<td class="capability-cell"><div class="capability-clamp capability-text">${capability && capability.body ? escapeHtml(capability.body) : '-'}</div></td>
|
|
|
<td class="capability-cell"><div class="capability-clamp">${capability ? renderEffects(capability.effects) : '-'}</div></td>
|
|
|
<td class="capability-cell"><div class="capability-clamp">${capability ? renderTools(capability.tools) : '-'}</div></td>
|
|
|
@@ -2882,7 +2953,7 @@ window.renderStructuredData = function (items, type, parentItem = null) {
|
|
|
.steps-table .effect-method { background:#e0e7ff; color:#3730a3; font-weight:normal; margin-right:6px; margin-bottom:2px; display:inline-block; transform:translateY(-1px); }
|
|
|
.steps-table .effect-negative { background:#f8fafc; color:#64748b; border:1px solid #cbd5e1; border-radius:999px; padding:1px 7px; font-size:0.78em; }
|
|
|
</style>
|
|
|
- <table class="steps-table" style="width: 100%; min-width: ${minWidth + 220}px; border-collapse: collapse; margin-top: 8px; font-size: 0.9em; background: white; border-radius: 8px; overflow: hidden; box-shadow: 0 1px 3px rgba(0,0,0,0.05);">
|
|
|
+ <table class="steps-table" style="width: 100%; min-width: ${minWidth + 220}px; border-collapse: collapse; margin-top: 8px; font-size: 0.9em; background: white; border-radius: 8px; overflow: visible; box-shadow: 0 1px 3px rgba(0,0,0,0.05);">
|
|
|
<thead>
|
|
|
<tr style="background: rgba(0,0,0,0.03); border-bottom: 2px solid rgba(0,0,0,0.1); text-align: left;">
|
|
|
<th style="padding: 12px 10px; width: 60px;">序号</th>
|