Rebuild workspace widgets and behavioral graph views
This commit is contained in:
@@ -96,12 +96,12 @@
|
||||
.compose-shell .compose-thread {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.75rem;
|
||||
gap: 0.35rem;
|
||||
flex: 1 1 auto;
|
||||
min-height: 0;
|
||||
max-height: none;
|
||||
overflow-y: auto;
|
||||
padding: 0.75rem;
|
||||
padding: 0.5rem 0.625rem;
|
||||
border: 1px solid var(--bulma-border, #dbdbdb);
|
||||
background: var(--bulma-scheme-main-bis, #f7f8fa);
|
||||
}
|
||||
@@ -112,10 +112,19 @@
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.compose-shell .compose-history-loader {
|
||||
margin: 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.compose-shell .compose-history-loader.is-hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.compose-shell .compose-row {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.35rem;
|
||||
gap: 0.15rem;
|
||||
}
|
||||
|
||||
.compose-shell .compose-row.is-in {
|
||||
@@ -137,10 +146,10 @@
|
||||
|
||||
.compose-shell .compose-bubble {
|
||||
width: fit-content;
|
||||
max-width: min(42rem, 100%);
|
||||
padding: 0.75rem 0.875rem;
|
||||
max-width: min(38rem, 100%);
|
||||
padding: 0.4rem 0.55rem;
|
||||
border: 1px solid var(--bulma-border, #dbdbdb);
|
||||
border-radius: 1rem;
|
||||
border-radius: 0.8rem;
|
||||
background: var(--bulma-scheme-main, #fff);
|
||||
}
|
||||
|
||||
@@ -150,8 +159,8 @@
|
||||
}
|
||||
|
||||
.compose-shell .compose-reply-ref {
|
||||
margin-bottom: 0.5rem;
|
||||
padding-left: 0.75rem;
|
||||
margin-bottom: 0.3rem;
|
||||
padding-left: 0.45rem;
|
||||
border-left: 3px solid var(--bulma-border, #dbdbdb);
|
||||
}
|
||||
|
||||
@@ -160,7 +169,7 @@
|
||||
border: 0;
|
||||
background: transparent;
|
||||
color: var(--bulma-link, #3273dc);
|
||||
font-size: 0.75rem;
|
||||
font-size: 0.6875rem;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
@@ -168,35 +177,26 @@
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.compose-shell .compose-source-badge-wrap {
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.compose-shell .compose-source-badge {
|
||||
font-size: 0.6875rem;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.02em;
|
||||
}
|
||||
|
||||
.compose-shell .compose-media {
|
||||
margin: 0 0 0.5rem;
|
||||
margin: 0 0 0.3rem;
|
||||
}
|
||||
|
||||
.compose-shell .compose-media:last-of-type {
|
||||
margin-bottom: 0.625rem;
|
||||
margin-bottom: 0.35rem;
|
||||
}
|
||||
|
||||
.compose-shell .compose-image {
|
||||
display: block;
|
||||
max-width: min(26rem, 100%);
|
||||
max-height: 24rem;
|
||||
border-radius: 0.75rem;
|
||||
max-height: 22rem;
|
||||
border-radius: 0.6rem;
|
||||
}
|
||||
|
||||
.compose-shell .compose-body {
|
||||
margin: 0;
|
||||
white-space: pre-wrap;
|
||||
overflow-wrap: anywhere;
|
||||
line-height: 1.28;
|
||||
}
|
||||
|
||||
.compose-shell .compose-image-fallback.is-hidden {
|
||||
@@ -209,11 +209,11 @@
|
||||
.compose-shell .compose-reactions + .compose-msg-meta,
|
||||
.compose-shell .compose-edit-history + .compose-reactions,
|
||||
.compose-shell .compose-edit-history + .compose-msg-meta {
|
||||
margin-top: 0.5rem;
|
||||
margin-top: 0.3rem;
|
||||
}
|
||||
|
||||
.compose-shell .compose-edit-history {
|
||||
font-size: 0.75rem;
|
||||
font-size: 0.6875rem;
|
||||
}
|
||||
|
||||
.compose-shell .compose-edit-history summary {
|
||||
@@ -221,15 +221,15 @@
|
||||
}
|
||||
|
||||
.compose-shell .compose-edit-history ul {
|
||||
margin: 0.5rem 0 0;
|
||||
margin: 0.35rem 0 0;
|
||||
padding-left: 1rem;
|
||||
}
|
||||
|
||||
.compose-shell .compose-edit-diff {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.35rem;
|
||||
margin-top: 0.25rem;
|
||||
gap: 0.25rem;
|
||||
margin-top: 0.15rem;
|
||||
}
|
||||
|
||||
.compose-shell .compose-edit-old {
|
||||
@@ -244,21 +244,21 @@
|
||||
.compose-shell .compose-reactions {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.35rem;
|
||||
gap: 0.2rem;
|
||||
}
|
||||
|
||||
.compose-shell .compose-reaction-chip {
|
||||
min-height: 1.7rem;
|
||||
font-size: 0.875rem;
|
||||
min-height: 1.45rem;
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
|
||||
.compose-shell .compose-msg-meta {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
gap: 0.35rem;
|
||||
margin-top: 0.5rem;
|
||||
font-size: 0.75rem;
|
||||
gap: 0.25rem;
|
||||
margin-top: 0.3rem;
|
||||
font-size: 0.6875rem;
|
||||
}
|
||||
|
||||
.compose-shell .compose-msg-flag {
|
||||
@@ -275,7 +275,30 @@
|
||||
}
|
||||
|
||||
.compose-shell .compose-reply-btn {
|
||||
margin-top: 0.5rem;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.25rem;
|
||||
margin-top: 0.25rem;
|
||||
min-height: 1.75rem;
|
||||
padding-inline: 0.45rem;
|
||||
}
|
||||
|
||||
@media (hover: hover) {
|
||||
.compose-shell .compose-row .compose-reply-btn {
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
pointer-events: none;
|
||||
transition: opacity 0.12s ease-in-out;
|
||||
}
|
||||
|
||||
.compose-shell .compose-row:hover .compose-reply-btn,
|
||||
.compose-shell .compose-row.compose-reply-selected .compose-reply-btn,
|
||||
.compose-shell .compose-row:focus-within .compose-reply-btn,
|
||||
.compose-shell .compose-reply-btn:focus-visible {
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
pointer-events: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.compose-shell .compose-form {
|
||||
|
||||
@@ -130,6 +130,70 @@ body .has-text-grey-light {
|
||||
color: var(--gia-text);
|
||||
}
|
||||
|
||||
.gia-split-dropdown .dropdown-menu {
|
||||
min-width: 18rem;
|
||||
}
|
||||
|
||||
.gia-dropdown-nest {
|
||||
border-top: 1px solid var(--gia-border);
|
||||
}
|
||||
|
||||
.gia-dropdown-nest summary {
|
||||
list-style: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.gia-dropdown-nest summary::-webkit-details-marker {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.gia-dropdown-nest-body {
|
||||
padding: 0.25rem 0 0.35rem;
|
||||
}
|
||||
|
||||
.gia-dropdown-nest-body .dropdown-item {
|
||||
padding-left: 1.5rem;
|
||||
}
|
||||
|
||||
.gia-inline-tabs ul {
|
||||
border-bottom: 0;
|
||||
}
|
||||
|
||||
.gia-behavior-shell {
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.gia-behavior-summary-card,
|
||||
.gia-behavior-graph-card {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.gia-behavior-chart-shell {
|
||||
min-height: 11rem;
|
||||
}
|
||||
|
||||
.gia-behavior-chart {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 11rem;
|
||||
}
|
||||
|
||||
.gia-behavior-chart-area {
|
||||
fill: color-mix(in srgb, var(--bulma-link) 16%, transparent);
|
||||
}
|
||||
|
||||
.gia-behavior-chart-line {
|
||||
fill: none;
|
||||
stroke: var(--bulma-link);
|
||||
stroke-width: 1.6;
|
||||
stroke-linecap: round;
|
||||
stroke-linejoin: round;
|
||||
}
|
||||
|
||||
.gia-behavior-chart-point {
|
||||
fill: var(--bulma-link);
|
||||
}
|
||||
|
||||
.input,
|
||||
.textarea,
|
||||
.select select {
|
||||
@@ -554,6 +618,16 @@ html.gia-has-workspace-root {
|
||||
box-shadow: 0 0 0 2px rgba(50, 115, 220, 0.16);
|
||||
}
|
||||
|
||||
.grid-stack-item.is-gia-anchor .gia-widget-panel {
|
||||
border-color: rgba(255, 159, 28, 0.55);
|
||||
box-shadow: 0 0 0 2px rgba(255, 159, 28, 0.12);
|
||||
}
|
||||
|
||||
.grid-stack-item.is-gia-spawned .gia-widget-panel {
|
||||
border-color: rgba(72, 199, 142, 0.6);
|
||||
box-shadow: 0 0 0 3px rgba(72, 199, 142, 0.18);
|
||||
}
|
||||
|
||||
.floating-window {
|
||||
max-height: 300px;
|
||||
z-index: 9000;
|
||||
@@ -740,10 +814,11 @@ html.gia-has-workspace-root {
|
||||
|
||||
.gia-send-composer {
|
||||
margin: 0;
|
||||
padding: 0.75rem;
|
||||
border: 1px solid var(--bulma-border, #dbdbdb);
|
||||
border-radius: 0.875rem;
|
||||
background: var(--bulma-scheme-main-bis, #f7f8fa);
|
||||
padding: 0;
|
||||
border: 0;
|
||||
border-radius: 0;
|
||||
background: transparent;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.gia-send-composer-row {
|
||||
@@ -756,11 +831,12 @@ html.gia-has-workspace-root {
|
||||
}
|
||||
|
||||
.gia-send-composer-input {
|
||||
min-height: 2.75rem;
|
||||
max-height: 8rem;
|
||||
min-height: 2.5rem;
|
||||
max-height: 7rem;
|
||||
resize: none;
|
||||
border-top-right-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.gia-send-composer-action {
|
||||
@@ -769,7 +845,7 @@ html.gia-has-workspace-root {
|
||||
|
||||
.gia-send-composer-button {
|
||||
height: 100%;
|
||||
min-height: 2.75rem;
|
||||
min-height: 2.5rem;
|
||||
border-top-left-radius: 0;
|
||||
border-bottom-left-radius: 0;
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
const replyBanner = config.replyBanner;
|
||||
const replyBannerText = config.replyBannerText;
|
||||
const replyClearBtn = config.replyClearBtn;
|
||||
const historyLoader = config.historyLoader;
|
||||
const platformSelect = config.platformSelect;
|
||||
const contactSelect = config.contactSelect;
|
||||
const hiddenService = config.hiddenService;
|
||||
@@ -29,6 +30,9 @@
|
||||
let lastTs = core.toInt(thread.dataset.lastTs);
|
||||
let beforeContextReset = null;
|
||||
|
||||
state.loadingOlder = false;
|
||||
state.olderExhausted = false;
|
||||
|
||||
const nearBottom = function () {
|
||||
return thread.scrollHeight - thread.scrollTop - thread.clientHeight < 120;
|
||||
};
|
||||
@@ -39,6 +43,21 @@
|
||||
}
|
||||
};
|
||||
|
||||
const setHistoryLoader = function (message, hidden) {
|
||||
if (!historyLoader) {
|
||||
return;
|
||||
}
|
||||
historyLoader.textContent = String(
|
||||
message || "Scroll up to load older messages."
|
||||
);
|
||||
historyLoader.classList.toggle("is-hidden", !!hidden);
|
||||
};
|
||||
|
||||
const getOldestTs = function () {
|
||||
const firstRow = thread.querySelector(".compose-row");
|
||||
return core.toInt(firstRow && firstRow.dataset ? firstRow.dataset.ts : 0);
|
||||
};
|
||||
|
||||
const queryParams = function (extraParams) {
|
||||
const params = new URLSearchParams();
|
||||
params.set("service", thread.dataset.service || "");
|
||||
@@ -204,8 +223,28 @@
|
||||
});
|
||||
if (rows.length) {
|
||||
scrollToBottom(shouldStick);
|
||||
setHistoryLoader("", false);
|
||||
}
|
||||
ensureEmptyState();
|
||||
if (!thread.querySelector(".compose-row")) {
|
||||
setHistoryLoader("", true);
|
||||
}
|
||||
};
|
||||
|
||||
const prependMessageHtml = function (html) {
|
||||
const rows = parseMessageRows(html);
|
||||
if (!rows.length) {
|
||||
return 0;
|
||||
}
|
||||
const previousHeight = thread.scrollHeight;
|
||||
const previousTop = thread.scrollTop;
|
||||
rows.forEach(function (msg) {
|
||||
upsertMessageRow(msg);
|
||||
});
|
||||
thread.scrollTop = previousTop + (thread.scrollHeight - previousHeight);
|
||||
setHistoryLoader("", false);
|
||||
ensureEmptyState();
|
||||
return rows.length;
|
||||
};
|
||||
|
||||
const applyTyping = function (payload) {
|
||||
@@ -267,6 +306,47 @@
|
||||
}
|
||||
};
|
||||
|
||||
const loadOlder = async function () {
|
||||
if (state.loadingOlder || state.olderExhausted) {
|
||||
return;
|
||||
}
|
||||
const oldestTs = getOldestTs();
|
||||
if (!oldestTs) {
|
||||
state.olderExhausted = true;
|
||||
setHistoryLoader("Start of conversation.", false);
|
||||
return;
|
||||
}
|
||||
state.loadingOlder = true;
|
||||
setHistoryLoader("Loading older messages...", false);
|
||||
try {
|
||||
const response = await fetch(
|
||||
thread.dataset.pollUrl + "?" + queryParams({ before_ts: String(oldestTs) }),
|
||||
{
|
||||
method: "GET",
|
||||
credentials: "same-origin",
|
||||
headers: { Accept: "application/json" },
|
||||
}
|
||||
);
|
||||
if (!response.ok) {
|
||||
setHistoryLoader("Could not load older messages.", false);
|
||||
return;
|
||||
}
|
||||
const payload = await response.json();
|
||||
const inserted = prependMessageHtml(payload.messages_html || "");
|
||||
state.olderExhausted = !payload.has_older || inserted === 0;
|
||||
setHistoryLoader(
|
||||
state.olderExhausted
|
||||
? "Start of conversation."
|
||||
: "Scroll up to load older messages.",
|
||||
false
|
||||
);
|
||||
} catch (_err) {
|
||||
setHistoryLoader("Could not load older messages.", false);
|
||||
} finally {
|
||||
state.loadingOlder = false;
|
||||
}
|
||||
};
|
||||
|
||||
const setupWebSocket = function () {
|
||||
const wsPath = String(thread.dataset.wsUrl || "").trim();
|
||||
if (!wsPath || !window.WebSocket) {
|
||||
@@ -359,8 +439,14 @@
|
||||
clearReplyTarget();
|
||||
closeSocket();
|
||||
lastTs = 0;
|
||||
state.loadingOlder = false;
|
||||
state.olderExhausted = false;
|
||||
thread.dataset.lastTs = "0";
|
||||
thread.innerHTML = "";
|
||||
if (historyLoader) {
|
||||
thread.appendChild(historyLoader);
|
||||
}
|
||||
setHistoryLoader("Loading recent messages...", false);
|
||||
ensureEmptyState("Loading messages...");
|
||||
applyTyping({ typing: false });
|
||||
poll(true);
|
||||
@@ -466,6 +552,12 @@
|
||||
setReplyTarget(row.dataset.messageId || "", row.dataset.replySnippet || "");
|
||||
textarea.focus();
|
||||
});
|
||||
|
||||
thread.addEventListener("scroll", function () {
|
||||
if (thread.scrollTop <= 48) {
|
||||
loadOlder();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const init = function () {
|
||||
@@ -473,6 +565,7 @@
|
||||
bindContextSelectors();
|
||||
applyTyping(core.parseJsonSafe(panel.dataset.initialTyping || "{}", {}));
|
||||
ensureEmptyState();
|
||||
setHistoryLoader("", !thread.querySelector(".compose-row"));
|
||||
scrollToBottom(true);
|
||||
setupWebSocket();
|
||||
|
||||
@@ -492,6 +585,9 @@
|
||||
init: init,
|
||||
poll: poll,
|
||||
queryParams: queryParams,
|
||||
scrollToLatest: function () {
|
||||
scrollToBottom(true);
|
||||
},
|
||||
setBeforeContextReset: function (callback) {
|
||||
beforeContextReset = callback;
|
||||
},
|
||||
|
||||
@@ -119,6 +119,7 @@
|
||||
const threadController = threadModule.createController({
|
||||
contactSelect: document.getElementById(panelId + "-contact-select"),
|
||||
hiddenIdentifier: document.getElementById(panelId + "-input-identifier"),
|
||||
historyLoader: document.getElementById(panelId + "-history-loader"),
|
||||
hiddenPerson: document.getElementById(panelId + "-input-person"),
|
||||
hiddenReplyTo: form.querySelector('input[name="reply_to_message_id"]'),
|
||||
hiddenService: document.getElementById(panelId + "-input-service"),
|
||||
@@ -135,6 +136,7 @@
|
||||
thread: thread,
|
||||
typingNode: document.getElementById(panelId + "-typing"),
|
||||
});
|
||||
state.threadController = threadController;
|
||||
|
||||
const sendController = sendModule.createController({
|
||||
armInput: form.querySelector('input[name="failsafe_arm"]'),
|
||||
@@ -177,6 +179,25 @@
|
||||
},
|
||||
initAll: initAll,
|
||||
initPanel: initPanel,
|
||||
scrollWidgetToLatest: function (widgetId) {
|
||||
const widgetNode = widgetId ? document.getElementById(String(widgetId)) : null;
|
||||
if (!widgetNode) {
|
||||
return;
|
||||
}
|
||||
const panel = widgetNode.querySelector("[data-compose-panel]");
|
||||
const panelId = String(panel && panel.id ? panel.id : "").trim();
|
||||
if (!panelId) {
|
||||
return;
|
||||
}
|
||||
const state = window.giaComposePanels[panelId];
|
||||
if (
|
||||
state
|
||||
&& state.threadController
|
||||
&& typeof state.threadController.scrollToLatest === "function"
|
||||
) {
|
||||
state.threadController.scrollToLatest();
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
document.addEventListener("DOMContentLoaded", function () {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user