Improve insights and continue WhatsApp implementation
This commit is contained in:
@@ -500,6 +500,82 @@
|
||||
});
|
||||
};
|
||||
|
||||
window.giaPrepareWindowAnchor = function (trigger) {
|
||||
if (!trigger || !trigger.getBoundingClientRect) {
|
||||
window.giaWindowAnchor = null;
|
||||
return;
|
||||
}
|
||||
const rect = trigger.getBoundingClientRect();
|
||||
window.giaWindowAnchor = {
|
||||
left: rect.left,
|
||||
right: rect.right,
|
||||
top: rect.top,
|
||||
bottom: rect.bottom,
|
||||
width: rect.width,
|
||||
height: rect.height,
|
||||
ts: Date.now(),
|
||||
};
|
||||
};
|
||||
|
||||
window.giaPositionFloatingWindow = function (windowEl) {
|
||||
if (!windowEl) {
|
||||
return;
|
||||
}
|
||||
const isMobile = window.matchMedia("(max-width: 768px)").matches;
|
||||
const margin = 12;
|
||||
const rect = windowEl.getBoundingClientRect();
|
||||
const anchor = window.giaWindowAnchor || null;
|
||||
windowEl.style.position = "fixed";
|
||||
|
||||
if (isMobile) {
|
||||
const centeredLeftViewport = Math.max(
|
||||
margin,
|
||||
Math.round((window.innerWidth - rect.width) / 2)
|
||||
);
|
||||
const centeredTopViewport = Math.max(
|
||||
margin,
|
||||
Math.round((window.innerHeight - rect.height) / 2)
|
||||
);
|
||||
windowEl.style.left = centeredLeftViewport + "px";
|
||||
windowEl.style.top = centeredTopViewport + "px";
|
||||
windowEl.style.right = "auto";
|
||||
windowEl.style.bottom = "auto";
|
||||
windowEl.style.transform = "none";
|
||||
windowEl.setAttribute("tabindex", "-1");
|
||||
if (typeof windowEl.focus === "function") {
|
||||
windowEl.focus({preventScroll: true});
|
||||
}
|
||||
if (typeof windowEl.scrollIntoView === "function") {
|
||||
windowEl.scrollIntoView({block: "center", inline: "center", behavior: "smooth"});
|
||||
}
|
||||
window.giaWindowAnchor = null;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!anchor || (Date.now() - anchor.ts) > 10000) {
|
||||
return;
|
||||
}
|
||||
|
||||
const desiredLeftViewport = anchor.left;
|
||||
const desiredTopViewport = anchor.bottom + 6;
|
||||
const maxLeftViewport = window.innerWidth - rect.width - margin;
|
||||
const maxTopViewport = window.innerHeight - rect.height - margin;
|
||||
const boundedLeftViewport = Math.max(
|
||||
margin,
|
||||
Math.min(desiredLeftViewport, maxLeftViewport)
|
||||
);
|
||||
const boundedTopViewport = Math.max(
|
||||
margin,
|
||||
Math.min(desiredTopViewport, maxTopViewport)
|
||||
);
|
||||
windowEl.style.left = boundedLeftViewport + "px";
|
||||
windowEl.style.top = boundedTopViewport + "px";
|
||||
windowEl.style.right = "auto";
|
||||
windowEl.style.bottom = "auto";
|
||||
windowEl.style.transform = "none";
|
||||
window.giaWindowAnchor = null;
|
||||
};
|
||||
|
||||
document.addEventListener("click", function (event) {
|
||||
const trigger = event.target.closest(".js-widget-spawn-trigger");
|
||||
if (!trigger) {
|
||||
@@ -515,6 +591,15 @@
|
||||
document.body.addEventListener("htmx:afterSwap", function (event) {
|
||||
const target = (event && event.target) || document;
|
||||
window.giaEnableWidgetSpawnButtons(target);
|
||||
const targetId = (target && target.id) || "";
|
||||
if (targetId === "windows-here") {
|
||||
const floatingWindow = target.querySelector(".floating-window");
|
||||
if (floatingWindow) {
|
||||
window.setTimeout(function () {
|
||||
window.giaPositionFloatingWindow(floatingWindow);
|
||||
}, 0);
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
{% block outer_content %}
|
||||
@@ -527,7 +612,7 @@
|
||||
{% endblock %}
|
||||
<div id="modals-here">
|
||||
</div>
|
||||
<div id="windows-here">
|
||||
<div id="windows-here" style="z-index: 120;">
|
||||
</div>
|
||||
<div id="widgets-here" style="display: none;">
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
<div id="widget">
|
||||
<div id="widget-{{ unique }}" class="grid-stack-item" {% block widget_options %}{% if widget_options is None %}gs-w="6" gs-h="1" gs-y="20" gs-x="0"{% else %}{% autoescape off %}{{ widget_options }}{% endautoescape %}{% endif %}{% endblock %}>
|
||||
<div class="grid-stack-item-content">
|
||||
<span class="gia-widget-focus-indicator" aria-hidden="true">
|
||||
<i class="{{ widget_icon|default:'fa-solid fa-circle' }}"></i>
|
||||
</span>
|
||||
|
||||
<nav class="panel">
|
||||
<p class="panel-heading" style="padding: .2em; line-height: .5em;">
|
||||
@@ -27,6 +30,36 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
#widget-{{ unique }} .grid-stack-item-content {
|
||||
position: relative;
|
||||
}
|
||||
#widget-{{ unique }} .gia-widget-focus-indicator {
|
||||
position: absolute;
|
||||
left: 0.32rem;
|
||||
top: 50%;
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
border-radius: 50%;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 0.7rem;
|
||||
color: #2f5f9f;
|
||||
background: rgba(235, 244, 255, 0.92);
|
||||
border: 1px solid rgba(47, 95, 159, 0.3);
|
||||
opacity: 0;
|
||||
transform: translateY(-50%) scale(0.92);
|
||||
transition: opacity 120ms ease, transform 120ms ease;
|
||||
pointer-events: none;
|
||||
z-index: 3;
|
||||
}
|
||||
#widget-{{ unique }}.is-widget-active .gia-widget-focus-indicator {
|
||||
opacity: 1;
|
||||
transform: translateY(-50%) scale(1);
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
{% block custom_script %}
|
||||
{% endblock %}
|
||||
@@ -34,43 +67,22 @@
|
||||
document.dispatchEvent(widget_event);
|
||||
(function () {
|
||||
var widgetRoot = document.getElementById("widget-{{ unique }}");
|
||||
var iconClass = "{{ widget_icon|default:'fa-solid fa-arrows-minimize'|escapejs }}";
|
||||
function decorateHandle() {
|
||||
if (!widgetRoot) {
|
||||
return true;
|
||||
}
|
||||
var handles = widgetRoot.querySelectorAll(".ui-resizable-se");
|
||||
if (!handles.length) {
|
||||
handles = widgetRoot.querySelectorAll(".ui-resizable-handle");
|
||||
}
|
||||
if (!handles.length) {
|
||||
return false;
|
||||
}
|
||||
handles.forEach(function (handle) {
|
||||
if (handle.dataset.iconApplied === "1") {
|
||||
return;
|
||||
}
|
||||
handle.dataset.iconApplied = "1";
|
||||
handle.style.display = "flex";
|
||||
handle.style.alignItems = "center";
|
||||
handle.style.justifyContent = "center";
|
||||
handle.style.overflow = "hidden";
|
||||
var icon = document.createElement("i");
|
||||
icon.className = iconClass + " has-text-grey-light";
|
||||
icon.style.fontSize = "0.65rem";
|
||||
icon.style.opacity = "0.65";
|
||||
icon.style.pointerEvents = "none";
|
||||
handle.appendChild(icon);
|
||||
});
|
||||
return true;
|
||||
if (!widgetRoot) {
|
||||
return;
|
||||
}
|
||||
var attempts = 0;
|
||||
var timer = window.setInterval(function () {
|
||||
attempts += 1;
|
||||
if (decorateHandle() || attempts > 10) {
|
||||
window.clearInterval(timer);
|
||||
}
|
||||
}, 80);
|
||||
|
||||
function setActive() {
|
||||
document.querySelectorAll(".grid-stack-item.is-widget-active").forEach(function (node) {
|
||||
if (node !== widgetRoot) {
|
||||
node.classList.remove("is-widget-active");
|
||||
}
|
||||
});
|
||||
widgetRoot.classList.add("is-widget-active");
|
||||
}
|
||||
|
||||
widgetRoot.addEventListener("mousedown", setActive);
|
||||
widgetRoot.addEventListener("focusin", setActive);
|
||||
window.setTimeout(setActive, 0);
|
||||
})();
|
||||
</script>
|
||||
{% block custom_end %}
|
||||
|
||||
@@ -30,9 +30,22 @@
|
||||
{% if metric.group == group_key %}
|
||||
<article class="message is-light" style="margin-bottom: 0.6rem;">
|
||||
<div class="message-body">
|
||||
<p><strong>{{ metric.title }}</strong>: {{ metric.value|default:"-" }}</p>
|
||||
<p><strong>Calculation:</strong> {{ metric.calculation }}</p>
|
||||
<p><strong>Psychological Read:</strong> {{ metric.psychology }}</p>
|
||||
<h3 class="is-size-6 has-text-weight-semibold" style="margin-bottom: 0.45rem;">{{ metric.title }}</h3>
|
||||
|
||||
<p class="is-size-7 has-text-grey has-text-weight-semibold" style="margin-bottom: 0.15rem;">
|
||||
Current Value
|
||||
</p>
|
||||
<p style="margin-bottom: 0.55rem;">{{ metric.value|default:"-" }}</p>
|
||||
|
||||
<p class="is-size-7 has-text-grey has-text-weight-semibold" style="margin-bottom: 0.15rem;">
|
||||
How It Is Calculated
|
||||
</p>
|
||||
<p style="margin-bottom: 0.55rem;">{{ metric.calculation }}</p>
|
||||
|
||||
<p class="is-size-7 has-text-grey has-text-weight-semibold" style="margin-bottom: 0.15rem;">
|
||||
Psychological Interpretation
|
||||
</p>
|
||||
<p>{{ metric.psychology }}</p>
|
||||
</div>
|
||||
</article>
|
||||
{% endif %}
|
||||
|
||||
@@ -52,7 +52,7 @@
|
||||
{% if render_mode == "page" %}
|
||||
<a class="button is-light is-rounded" href="{{ compose_workspace_url }}">
|
||||
<span class="icon is-small"><i class="fa-solid fa-table-cells-large"></i></span>
|
||||
<span>Open In Workspace</span>
|
||||
<span>Chat Workspace</span>
|
||||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
@@ -204,6 +204,7 @@
|
||||
class="compose-image"
|
||||
src="{{ image_url }}"
|
||||
alt="Attachment"
|
||||
referrerpolicy="no-referrer"
|
||||
loading="lazy"
|
||||
decoding="async">
|
||||
</figure>
|
||||
@@ -214,6 +215,7 @@
|
||||
class="compose-image"
|
||||
src="{{ msg.image_url }}"
|
||||
alt="Attachment"
|
||||
referrerpolicy="no-referrer"
|
||||
loading="lazy"
|
||||
decoding="async">
|
||||
</figure>
|
||||
@@ -390,7 +392,7 @@
|
||||
#{{ panel_id }}-lightbox.compose-lightbox {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
z-index: 160;
|
||||
z-index: 12050;
|
||||
background: rgba(10, 12, 16, 0.82);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@@ -661,7 +663,7 @@
|
||||
width: min(40rem, calc(100% - 1rem));
|
||||
margin-top: 0;
|
||||
z-index: 35;
|
||||
overflow: auto;
|
||||
overflow: visible;
|
||||
}
|
||||
#{{ panel_id }} .compose-ai-popover-backdrop {
|
||||
position: absolute;
|
||||
@@ -687,6 +689,7 @@
|
||||
background: #fff;
|
||||
padding: 0.65rem;
|
||||
box-shadow: 0 12px 24px rgba(0, 0, 0, 0.08);
|
||||
overflow: visible;
|
||||
}
|
||||
#{{ panel_id }} .compose-ai-card.is-active {
|
||||
display: block;
|
||||
@@ -863,9 +866,9 @@
|
||||
word-break: break-word;
|
||||
}
|
||||
#{{ panel_id }} .compose-qi-doc-dot {
|
||||
width: 0.5rem;
|
||||
height: 0.5rem;
|
||||
min-width: 0.5rem;
|
||||
width: 0.64rem;
|
||||
height: 0.64rem;
|
||||
min-width: 0.64rem;
|
||||
border-radius: 50%;
|
||||
border: 0;
|
||||
padding: 0;
|
||||
@@ -874,13 +877,65 @@
|
||||
cursor: help;
|
||||
opacity: 0.85;
|
||||
transform: translateY(0.02rem);
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
#{{ panel_id }} .compose-qi-doc-dot::after {
|
||||
content: attr(data-tooltip);
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
bottom: calc(100% + 0.42rem);
|
||||
transform: translate(-50%, 0.18rem);
|
||||
width: min(21rem, 75vw);
|
||||
max-width: 21rem;
|
||||
padding: 0.42rem 0.5rem;
|
||||
border-radius: 7px;
|
||||
background: rgba(31, 39, 53, 0.96);
|
||||
color: #f5f8ff;
|
||||
font-size: 0.67rem;
|
||||
line-height: 1.3;
|
||||
text-align: left;
|
||||
white-space: normal;
|
||||
box-shadow: 0 8px 22px rgba(7, 10, 17, 0.28);
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
pointer-events: none;
|
||||
transition: opacity 85ms ease, transform 85ms ease, visibility 85ms linear;
|
||||
transition-delay: 30ms;
|
||||
}
|
||||
#{{ panel_id }} .compose-qi-doc-dot::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
bottom: calc(100% + 0.1rem);
|
||||
transform: translateX(-50%);
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-left: 0.3rem solid transparent;
|
||||
border-right: 0.3rem solid transparent;
|
||||
border-top: 0.36rem solid rgba(31, 39, 53, 0.96);
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
pointer-events: none;
|
||||
transition: opacity 85ms ease, visibility 85ms linear;
|
||||
transition-delay: 30ms;
|
||||
}
|
||||
#{{ panel_id }} .compose-qi-doc-dot:hover,
|
||||
#{{ panel_id }} .compose-qi-doc-dot:focus-visible {
|
||||
background: #9ab1cc;
|
||||
opacity: 1;
|
||||
outline: 1px solid rgba(52, 101, 164, 0.45);
|
||||
outline-offset: 1px;
|
||||
}
|
||||
#{{ panel_id }} .compose-qi-doc-dot:hover::after,
|
||||
#{{ panel_id }} .compose-qi-doc-dot:focus-visible::after,
|
||||
#{{ panel_id }} .compose-qi-doc-dot:hover::before,
|
||||
#{{ panel_id }} .compose-qi-doc-dot:focus-visible::before {
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
transform: translate(-50%, 0);
|
||||
transition-delay: 0ms;
|
||||
}
|
||||
#{{ panel_id }} .compose-qi-row-meta {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
@@ -1242,6 +1297,7 @@
|
||||
img.className = "compose-image";
|
||||
img.src = String(candidateUrl);
|
||||
img.alt = "Attachment";
|
||||
img.referrerPolicy = "no-referrer";
|
||||
img.loading = "lazy";
|
||||
img.decoding = "async";
|
||||
figure.appendChild(img);
|
||||
@@ -1290,11 +1346,7 @@
|
||||
}
|
||||
img.dataset.fallbackBound = "1";
|
||||
img.addEventListener("error", function () {
|
||||
const figure = img.closest(".compose-media");
|
||||
if (figure) {
|
||||
figure.remove();
|
||||
}
|
||||
refresh();
|
||||
img.classList.add("is-image-load-failed");
|
||||
});
|
||||
img.addEventListener("load", function () {
|
||||
if (fallback) {
|
||||
@@ -1921,7 +1973,7 @@
|
||||
const dot = document.createElement("button");
|
||||
dot.type = "button";
|
||||
dot.className = "compose-qi-doc-dot";
|
||||
dot.title = String(tooltipText || "");
|
||||
dot.setAttribute("data-tooltip", String(tooltipText || ""));
|
||||
dot.setAttribute("aria-label", "Explain " + String(titleText || "metric"));
|
||||
dot.addEventListener("click", function (ev) {
|
||||
ev.preventDefault();
|
||||
|
||||
@@ -168,6 +168,7 @@
|
||||
hx-get="{{ action.url }}"
|
||||
hx-target="{{ action.target }}"
|
||||
hx-swap="innerHTML"
|
||||
{% if action.target == "#windows-here" %}onclick="window.giaPrepareWindowAnchor(this);"{% endif %}
|
||||
title="{{ action.title }}">
|
||||
<span class="icon"><i class="{{ action.icon }}"></i></span>
|
||||
</button>
|
||||
|
||||
@@ -4,6 +4,13 @@
|
||||
{% if object.warning %}
|
||||
<p class="is-size-7" style="margin-top: 0.6rem;">{{ object.warning }}</p>
|
||||
{% endif %}
|
||||
{% if object.debug_lines %}
|
||||
<article class="notification is-light" style="margin-top: 0.6rem; margin-bottom: 0;">
|
||||
<p><strong>Runtime Debug</strong></p>
|
||||
<pre class="is-size-7" style="white-space: pre-wrap; margin: 0.4rem 0 0;">{% for line in object.debug_lines %}{{ line }}
|
||||
{% endfor %}</pre>
|
||||
</article>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<article class="notification is-warning is-light" style="margin-bottom: 0;">
|
||||
<p><strong>WhatsApp QR Not Ready.</strong></p>
|
||||
@@ -26,6 +33,13 @@
|
||||
<input type="hidden" name="refresh" value="1" />
|
||||
</form>
|
||||
{% endif %}
|
||||
{% if object.debug_lines %}
|
||||
<article class="notification is-light" style="margin-top: 0.6rem; margin-bottom: 0;">
|
||||
<p><strong>Runtime Debug</strong></p>
|
||||
<pre class="is-size-7" style="white-space: pre-wrap; margin: 0.4rem 0 0;">{% for line in object.debug_lines %}{{ line }}
|
||||
{% endfor %}</pre>
|
||||
</article>
|
||||
{% endif %}
|
||||
</article>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user