Reimplement compose and add tiling windows
This commit is contained in:
@@ -4,17 +4,29 @@
|
||||
{% load accessibility %}
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="en-GB">
|
||||
<html lang="en-GB" class="{% block html_class %}{% endblock %}">
|
||||
<head>
|
||||
<script>
|
||||
(function () {
|
||||
var storedTheme = null;
|
||||
var resolvedTheme = null;
|
||||
try {
|
||||
storedTheme = localStorage.getItem("theme");
|
||||
} catch (error) {
|
||||
}
|
||||
if (storedTheme === "dark" || storedTheme === "light") {
|
||||
document.documentElement.dataset.theme = storedTheme;
|
||||
resolvedTheme = storedTheme;
|
||||
} else {
|
||||
try {
|
||||
resolvedTheme = window.matchMedia("(prefers-color-scheme: dark)").matches
|
||||
? "dark"
|
||||
: "light";
|
||||
} catch (error) {
|
||||
}
|
||||
}
|
||||
if (resolvedTheme === "dark" || resolvedTheme === "light") {
|
||||
document.documentElement.dataset.theme = resolvedTheme;
|
||||
document.documentElement.style.colorScheme = resolvedTheme;
|
||||
} else {
|
||||
document.documentElement.removeAttribute("data-theme");
|
||||
}
|
||||
@@ -30,23 +42,11 @@
|
||||
<link rel="preload" href="{% static 'vendor/fontawesome/webfonts/fa-solid-900.woff2' %}" as="font" type="font/woff2" integrity="sha512-Ph1xTLhfMycYSW+wUN8oL3Ggl56nGIS95EHiKWggcL/GbMNjPdib1Hreb1D4COlMxdiGCkk43nspQnpDuTjgQg==" crossorigin="anonymous">
|
||||
<link rel="preload" href="{% static 'vendor/fontawesome/webfonts/fa-regular-400.woff2' %}" as="font" type="font/woff2" integrity="sha512-qioT43fXB5q4Bbpn8sPQE9OIZLjKD0c0lVmpm6KmT8k34LM6gkRcOOMi1BOl2lohFG/7p9tzKfTP5G563BQq1g==" crossorigin="anonymous">
|
||||
<link rel="stylesheet" href="{% static 'css/bulma.min.css' %}" integrity="sha512-yh2RE0wZCVZeysGiqTwDTO/dKelCbS9bP2L94UvOFtl/FKXcNAje3Y2oBg/ZMZ3LS1sicYk4dYVGtDex75fvvA==" crossorigin="anonymous">
|
||||
<link rel="stylesheet" href="{% static 'css/bulma-tooltip.min.css' %}" integrity="sha512-SNDNIUvSYhnqDV9FFXaH/e0xZ6NzkG4Qm5dafLLf0PCMkzICKaOmMTgI3y2t2jZK+hAtP6A7UBcFqjWMhsujIg==" crossorigin="anonymous">
|
||||
<link rel="stylesheet" href="{% static 'vendor/fontawesome/css/all.css' %}" integrity="sha512-UKBBxJ5N3/MYiSsYTlEsARsp4vELKVRIklED4Mb6wpuVFOgy5Blt+sXUdz1TDReqWsm64xxBA2QoBJRCxI0x5Q==" crossorigin="anonymous">
|
||||
<link rel="stylesheet" href="{% static 'css/bulma-slider.min.css' %}" integrity="sha512-9o5SkCRCA9thttRH3Gb5QXLxKdRiuRLdO6ToEPwRHGLXjrhTZwFj0rEHjrCcJvDN9/aNaWMpGOIEA2vZsHmEqw==" crossorigin="anonymous">
|
||||
<link rel="stylesheet" href="{% static 'css/bulma-calendar.min.css' %}" integrity="sha512-IOnJQkgQpezPDPTJcRiWD7YVI3sF2RYzYDl4isbDT2geSaEHRQ615UN/8GhJbSkvqkKRZu8SBCQ7XwKMqsqLFQ==" crossorigin="anonymous">
|
||||
<link rel="stylesheet" href="{% static 'css/bulma-tagsinput.min.css' %}" integrity="sha512-NWTkcDRubZ3pyXbZZLQBILuVsRFs8c6QGgnfe4dm5/d6yp50U+xdoCDLIcSo51fFy/GXH0O2Oed1Z1sF1faxDA==" crossorigin="anonymous">
|
||||
<link rel="stylesheet" href="{% static 'css/bulma-switch.min.css' %}" integrity="sha512-zjrHYubQoNgDVqVKTyGjKcvIeQlduZTvXCvcBwQ0iqJYKLKiz9cuFAN7e98zfKqCTpI/EgFRBRcTwJw20yAFuw==" crossorigin="anonymous">
|
||||
<link rel="stylesheet" href="{% static 'css/gridstack.min.css' %}" integrity="sha512-ttQfsDTO64bamkJHeLDf0kzMP1NKfkootudPWS2V8Pwy+9z1wexSYjIT6/HXGg/bmtD+DRwsUnQoYEB0yePjbw==" crossorigin="anonymous">
|
||||
<link rel="stylesheet" href="{% static 'css/gia-theme.css' %}" integrity="sha512-6BpiCie64b2f+fUAgX2EWY95FVBcGsSSnSJchQPwV5zFOR/A4sklIPC4HNXcT/NyOAY7PpNol6efClbh/NSGIA==" crossorigin="anonymous">
|
||||
<script src="{% static 'js/bulma-calendar.min.js' %}" integrity="sha512-kkEtEtypXzruevjkoxhyEkqkZBtlhK7s8zt7IV2yPabgBwy5xbKL9uWeCS37ldS9AaNTSnveWTu4ivUvGMJUWA==" crossorigin="anonymous"></script>
|
||||
<script src="{% static 'js/bulma-slider.min.js' %}" integrity="sha512-WLKXHCsMXTSIPsmQShJRE6K4IzwvNkhwxr/Oo8N3z+kzjhGleHibspmWLTawNMdl2z9E23XK20+yvUTDZ+zeNQ==" crossorigin="anonymous"></script>
|
||||
<link rel="stylesheet" href="{% static 'css/gia-theme.css' %}" integrity="sha512-ySzeXoQreOo29Fv+kBiggvr2yLJEj4LO+Srcdw6rAENl1cAl6fzjVITZld1grkwMb+Hxe1jczo623coGQr0jsw==" crossorigin="anonymous">
|
||||
{% block extra_head_assets %}{% endblock %}
|
||||
<script src="{% static 'js/htmx.min.js' %}" integrity="sha512-CGXFnDNv5q48ciFeIyWFcfZhqYW0sSBiPO+HZDO3XLM+p8xjhezz5CCxtkXVDKfCbvF+iUhel7xoeSp19o7x7g==" crossorigin="anonymous"></script>
|
||||
<script defer src="{% static 'js/remove-me.js' %}" integrity="sha512-uhE4kDw2+tXdJPDKSttOEYhVnwYq310+d5DMQnTjafJ58QLlYPyx0RTCNbjcrTiGfCjAhaQob4AumEUa2m3TaQ==" crossorigin="anonymous"></script>
|
||||
<script defer src="{% static 'js/hyperscript.min.js' %}" integrity="sha512-l43sZzpnAddmYhJyfPrgv46XhJvA95gsA28/+eW4XZLSekQ8wlP68i9f22KGkRjY0HNiZrLc5MXGo4z/tM2QNA==" crossorigin="anonymous"></script>
|
||||
<script src="{% static 'js/bulma-tagsinput.min.js' %}" integrity="sha512-Je6J++MjmmpxF30JCmRwM2KiK3uWQBQtqiNCjwzEMJKExLaa0BqerlYNa/fJAl5Rra4hMgRZF2fzg+V2vjE4Kw==" crossorigin="anonymous"></script>
|
||||
<script src="{% static 'js/jquery.min.js' %}" integrity="sha512-v2CJ7UaYy4JwqLDIrZUI/4hqeoQieOmAZNXBeQyjo21dadnwR+8ZaIJVT8EE2iyI61OV8e6M8PP2/4hpQINQ/g==" crossorigin="anonymous"></script>
|
||||
<script src="{% static 'js/gridstack-all.js' %}" integrity="sha512-djBPxwvBhDep1SvOhliatweHMORhVO3HabrfBjaW6nYsa7UcJYHty31x42m4HBSJXcJSQdoEgRPLVYGGIuIaDQ==" crossorigin="anonymous"></script>
|
||||
<script defer src="{% static 'js/magnet.min.js' %}" integrity="sha512-aoQ3V4iCM8zTcdMDSUTRG1K9wqZzmDSisuaCLQexk9DdFy92oWvTUoAfCVLnGzzJClst8PmtasZg219REwyNkw==" crossorigin="anonymous"></script>
|
||||
<script>
|
||||
(function () {
|
||||
var FAVICON_VERSION = "sq3";
|
||||
@@ -110,6 +110,7 @@
|
||||
function applyTheme(mode, shouldPersist) {
|
||||
var validMode = mode === THEME_DARK ? THEME_DARK : THEME_LIGHT;
|
||||
root.dataset.theme = validMode;
|
||||
root.style.colorScheme = validMode;
|
||||
applyFavicon();
|
||||
updateToggleUI(validMode);
|
||||
if (shouldPersist !== false) {
|
||||
@@ -159,7 +160,40 @@
|
||||
if (target) {
|
||||
target.classList.toggle("is-active");
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
document.body.addEventListener("htmx:afterRequest", function (event) {
|
||||
const detail = (event && event.detail) || null;
|
||||
const source = detail && detail.elt ? detail.elt : null;
|
||||
if (!detail || !detail.successful || !source || !source.dataset) {
|
||||
return;
|
||||
}
|
||||
const queueAfter = String(source.dataset.queueAfter || "");
|
||||
if (queueAfter === "remove-card") {
|
||||
const cardId = String(source.dataset.queueCardId || "").trim();
|
||||
const refreshEvent = String(source.dataset.queueRefreshEvent || "").trim();
|
||||
if (cardId) {
|
||||
const card = document.getElementById(cardId);
|
||||
if (card) {
|
||||
card.remove();
|
||||
}
|
||||
}
|
||||
if (refreshEvent) {
|
||||
htmx.trigger(document.body, refreshEvent);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (queueAfter === "show-inline-editor") {
|
||||
const editorId = String(source.dataset.queueEditorId || "").trim();
|
||||
if (!editorId) {
|
||||
return;
|
||||
}
|
||||
const editor = document.getElementById(editorId);
|
||||
if (editor) {
|
||||
editor.style.display = "block";
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
var composeLink = document.getElementById("nav-compose-link");
|
||||
@@ -201,14 +235,14 @@
|
||||
</script>
|
||||
</head>
|
||||
{% get_accessibility_settings request.user as a11y_settings %}
|
||||
<body{% if a11y_settings and a11y_settings.disable_animations %} class="reduced-motion"{% endif %}>
|
||||
<body class="{% if a11y_settings and a11y_settings.disable_animations %}reduced-motion {% endif %}{% block body_class %}{% endblock %}">
|
||||
|
||||
<nav class="navbar" role="navigation" aria-label="main navigation">
|
||||
<div class="navbar-brand">
|
||||
<div class="navbar-item">
|
||||
<span class="gia-brand-shell">
|
||||
{% if user.is_authenticated %}
|
||||
<button class="button is-light theme-toggle-button gia-brand-logo brand-theme-toggle js-theme-toggle" type="button" data-theme-mode="light" aria-label="Theme toggle">
|
||||
<button class="gia-brand-logo brand-theme-toggle js-theme-toggle" type="button" data-theme-mode="light" aria-label="Theme toggle">
|
||||
<svg class="brand-theme-logo" viewBox="0 0 213.35 150.85" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false">
|
||||
<g transform="translate(38.831 -7.4316)">
|
||||
<g transform="matrix(.99287 0 0 .99911 1.2367 -30.308)">
|
||||
@@ -291,7 +325,7 @@
|
||||
Services
|
||||
</a>
|
||||
|
||||
<div class="navbar-dropdown">
|
||||
<div class="navbar-dropdown is-right gia-navbar-dropdown">
|
||||
<a class="navbar-item" href="{% url 'signal' %}">
|
||||
Signal
|
||||
</a>
|
||||
@@ -309,7 +343,7 @@
|
||||
Data
|
||||
</a>
|
||||
|
||||
<div class="navbar-dropdown">
|
||||
<div class="navbar-dropdown is-right gia-navbar-dropdown">
|
||||
<a class="navbar-item" href="{% url 'sessions' type='page' %}">
|
||||
Sessions
|
||||
</a>
|
||||
@@ -324,7 +358,7 @@
|
||||
Settings
|
||||
</a>
|
||||
|
||||
<div class="navbar-dropdown">
|
||||
<div class="navbar-dropdown is-right gia-navbar-dropdown">
|
||||
<div class="navbar-item has-text-weight-semibold is-size-7 has-text-grey">
|
||||
General
|
||||
</div>
|
||||
@@ -391,28 +425,29 @@
|
||||
<a class="navbar-item{% if request.resolver_match.url_name == 'osint_workspace' %} is-current-route{% endif %}" href="{% url 'osint_workspace' %}">
|
||||
OSINT Workspace
|
||||
</a>
|
||||
<hr class="navbar-divider">
|
||||
<a class="navbar-item" href="{% url 'logout' %}">
|
||||
Logout
|
||||
</a>
|
||||
<button class="navbar-item button is-light add-button" type="button" style="display:none;">
|
||||
Install App
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="navbar-item">
|
||||
<div class="buttons">
|
||||
{% if not user.is_authenticated %}
|
||||
{% if not user.is_authenticated %}
|
||||
<div class="navbar-item">
|
||||
<div class="buttons">
|
||||
<a class="button is-info" href="{% url 'signup' %}">
|
||||
<strong>Sign up</strong>
|
||||
</a>
|
||||
<a class="button" href="{% url 'two_factor:login' %}">
|
||||
Log in
|
||||
</a>
|
||||
{% endif %}
|
||||
|
||||
{% if user.is_authenticated %}
|
||||
<button class="button is-light add-button" type="button" style="display:none;">Install App</button>
|
||||
<a class="button is-dark" href="{% url 'logout' %}">Logout</a>
|
||||
{% endif %}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
@@ -449,218 +484,26 @@
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
window.giaPrepareWidgetTarget = function () {
|
||||
const target = document.getElementById("widgets-here");
|
||||
if (target) {
|
||||
target.style.display = "block";
|
||||
}
|
||||
};
|
||||
|
||||
window.giaCanSpawnWidgets = function () {
|
||||
return !!(
|
||||
window.grid &&
|
||||
typeof window.grid.addWidget === "function" &&
|
||||
document.getElementById("grid-stack-main") &&
|
||||
document.getElementById("widgets-here")
|
||||
);
|
||||
};
|
||||
|
||||
window.giaEnableWidgetSpawnButtons = function (root) {
|
||||
const scope = root && root.querySelectorAll ? root : document;
|
||||
const canSpawn = window.giaCanSpawnWidgets();
|
||||
scope.querySelectorAll(".js-widget-spawn-trigger").forEach(function (button) {
|
||||
const widgetUrl = String(
|
||||
button.getAttribute("data-widget-url")
|
||||
|| button.getAttribute("hx-get")
|
||||
|| ""
|
||||
).trim();
|
||||
const visible = canSpawn && !!widgetUrl;
|
||||
button.classList.toggle("is-hidden", !visible);
|
||||
button.setAttribute("aria-hidden", visible ? "false" : "true");
|
||||
});
|
||||
};
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
window.giaEnableFloatingWindowInteractions = function (windowEl) {
|
||||
if (!windowEl || windowEl.dataset.giaWindowInteractive === "1") {
|
||||
return;
|
||||
}
|
||||
windowEl.dataset.giaWindowInteractive = "1";
|
||||
|
||||
// Disable magnet-block global drag so text inputs remain editable.
|
||||
windowEl.setAttribute("unmovable", "");
|
||||
|
||||
const heading = windowEl.querySelector(".panel-heading");
|
||||
if (!heading) {
|
||||
return;
|
||||
}
|
||||
|
||||
let dragging = false;
|
||||
let startX = 0;
|
||||
let startY = 0;
|
||||
let startLeft = 0;
|
||||
let startTop = 0;
|
||||
|
||||
const onMove = function (event) {
|
||||
if (!dragging) {
|
||||
return;
|
||||
}
|
||||
const deltaX = event.clientX - startX;
|
||||
const deltaY = event.clientY - startY;
|
||||
windowEl.style.left = (startLeft + deltaX) + "px";
|
||||
windowEl.style.top = (startTop + deltaY) + "px";
|
||||
windowEl.style.right = "auto";
|
||||
windowEl.style.bottom = "auto";
|
||||
};
|
||||
|
||||
const stopDrag = function () {
|
||||
dragging = false;
|
||||
document.removeEventListener("pointermove", onMove);
|
||||
document.removeEventListener("pointerup", stopDrag);
|
||||
};
|
||||
|
||||
heading.addEventListener("pointerdown", function (event) {
|
||||
if (event.button !== 0) {
|
||||
return;
|
||||
}
|
||||
const interactive = event.target.closest(
|
||||
"button, a, input, textarea, select, label, .delete, .icon"
|
||||
);
|
||||
if (interactive) {
|
||||
return;
|
||||
}
|
||||
const rect = windowEl.getBoundingClientRect();
|
||||
windowEl.style.position = "fixed";
|
||||
startLeft = rect.left;
|
||||
startTop = rect.top;
|
||||
startX = event.clientX;
|
||||
startY = event.clientY;
|
||||
dragging = true;
|
||||
document.addEventListener("pointermove", onMove);
|
||||
document.addEventListener("pointerup", stopDrag);
|
||||
event.preventDefault();
|
||||
});
|
||||
};
|
||||
|
||||
document.addEventListener("click", function (event) {
|
||||
const trigger = event.target.closest(".js-widget-spawn-trigger");
|
||||
if (!trigger) {
|
||||
return;
|
||||
}
|
||||
window.giaPrepareWidgetTarget();
|
||||
});
|
||||
|
||||
document.addEventListener("DOMContentLoaded", function () {
|
||||
window.giaEnableWidgetSpawnButtons(document);
|
||||
});
|
||||
|
||||
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 floatingWindows = target.querySelectorAll(".floating-window");
|
||||
floatingWindows.forEach(function (floatingWindow) {
|
||||
window.setTimeout(function () {
|
||||
window.giaPositionFloatingWindow(floatingWindow);
|
||||
window.giaEnableFloatingWindowInteractions(floatingWindow);
|
||||
}, 0);
|
||||
});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
{% block outer_content %}
|
||||
{% endblock %}
|
||||
<div>
|
||||
<div class="container">
|
||||
{% include "partials/settings-hierarchy-nav.html" %}
|
||||
{% block content_wrapper %}
|
||||
{% block content %}
|
||||
{% block standard_page_shell %}
|
||||
<div class="gia-standard-page-shell">
|
||||
<div class="container">
|
||||
{% include "partials/settings-hierarchy-nav.html" %}
|
||||
{% block content_wrapper %}
|
||||
{% block content %}
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
<div id="modals-here">
|
||||
</div>
|
||||
<div id="windows-here" style="z-index: 120;">
|
||||
</div>
|
||||
<div id="widgets-here" style="display: none;">
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
<div id="modals-here">
|
||||
</div>
|
||||
<div id="windows-here" style="z-index: 120;">
|
||||
</div>
|
||||
<div id="widgets-here" style="display: none;">
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user