Reimplement compose and add tiling windows
This commit is contained in:
@@ -1,11 +1,11 @@
|
||||
<div
|
||||
id="ai-result-links-{{ person.id }}-{{ operation }}-{{ ai_result_id|default:'new' }}"
|
||||
style="margin-bottom: 0.5rem;">
|
||||
<div class="tags has-addons" style="display: inline-flex; margin-bottom: 0.4rem;">
|
||||
<span class="tag is-dark">
|
||||
<div class="tags has-addons gia-tag-ribbon" style="margin-bottom: 0.4rem;">
|
||||
<span class="tag is-dark gia-badge">
|
||||
<i class="fa-solid fa-wand-magic-sparkles" aria-hidden="true"></i>
|
||||
</span>
|
||||
<span class="tag is-white" style="border: 1px solid rgba(0, 0, 0, 0.2);">
|
||||
<span class="tag is-white gia-badge gia-tag-ribbon-main">
|
||||
AI {{ operation_label }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@@ -85,13 +85,28 @@
|
||||
hx-headers='{"X-CSRFToken": "{{ csrf_token }}"}'
|
||||
hx-get="{{ compose_widget_url }}"
|
||||
hx-target="#widgets-here"
|
||||
hx-swap="afterend"
|
||||
hx-swap="beforeend"
|
||||
title="Open Manual Text widget here"
|
||||
aria-label="Open Manual Text widget here">
|
||||
<span class="icon is-small"><i class="fa-solid fa-table-cells-large"></i></span>
|
||||
<span>Widget</span>
|
||||
</button>
|
||||
{% endif %}
|
||||
{% if history_widget_url %}
|
||||
<button
|
||||
type="button"
|
||||
class="button is-light is-small js-widget-spawn-trigger is-hidden"
|
||||
data-widget-url="{{ history_widget_url }}"
|
||||
hx-headers='{"X-CSRFToken": "{{ csrf_token }}"}'
|
||||
hx-get="{{ history_widget_url }}"
|
||||
hx-target="#widgets-here"
|
||||
hx-swap="beforeend"
|
||||
title="Open message history widget here"
|
||||
aria-label="Open message history widget here">
|
||||
<span class="icon is-small"><i class="fa-solid fa-clock-rotate-left"></i></span>
|
||||
<span>History</span>
|
||||
</button>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
@@ -28,12 +28,12 @@
|
||||
hx-get="{% url 'ai_workspace_person' type='widget' person_id=row.person.id %}"
|
||||
hx-include="#ai-window-form"
|
||||
hx-target="#widgets-here"
|
||||
hx-swap="afterend">
|
||||
<span class="tags has-addons" style="display: inline-flex; width: 100%; margin: 0; white-space: nowrap;">
|
||||
<span class="tag is-dark" style="min-width: 2.5rem; justify-content: center;">
|
||||
hx-swap="beforeend">
|
||||
<span class="tags has-addons gia-tag-ribbon">
|
||||
<span class="tag is-dark gia-badge" style="min-width: 2.5rem; justify-content: center;">
|
||||
<i class="fa-solid fa-comment-dots" aria-hidden="true"></i>
|
||||
</span>
|
||||
<span class="tag is-white" style="flex: 1; display: inline-flex; align-items: center; justify-content: space-between; gap: 0.75rem; padding-left: 0.7rem; padding-right: 0.7rem; border-top: 1px solid rgba(0, 0, 0, 0.2); border-bottom: 1px solid rgba(0, 0, 0, 0.2);">
|
||||
<span class="tag is-white gia-badge gia-tag-ribbon-main">
|
||||
<span style="display: inline-flex; align-items: baseline; gap: 0.35rem; min-width: 0;">
|
||||
<strong>{{ row.person.name }}</strong>
|
||||
</span>
|
||||
@@ -41,7 +41,7 @@
|
||||
<small style="padding-left: 0.5rem;">{{ row.last_ts_label }}</small>
|
||||
{% endif %}
|
||||
</span>
|
||||
<span class="tag is-dark" style="min-width: 3.25rem; justify-content: center;">{{ row.message_count }}</span>
|
||||
<span class="tag is-dark gia-badge" style="min-width: 3.25rem; justify-content: center;">{{ row.message_count }}</span>
|
||||
</span>
|
||||
</button>
|
||||
{% else %}
|
||||
@@ -49,11 +49,11 @@
|
||||
class="button is-fullwidth"
|
||||
style="border-radius: 8px; border: 0; background: transparent; box-shadow: none; padding: 0;"
|
||||
disabled>
|
||||
<span class="tags has-addons" style="display: inline-flex; width: 100%; margin: 0; white-space: nowrap;">
|
||||
<span class="tag is-info is-light" style="min-width: 2.5rem; justify-content: center;">
|
||||
<span class="tags has-addons gia-tag-ribbon">
|
||||
<span class="tag is-info is-light gia-badge" style="min-width: 2.5rem; justify-content: center;">
|
||||
<i class="fa-solid fa-users" aria-hidden="true"></i>
|
||||
</span>
|
||||
<span class="tag is-white" style="flex: 1; display: inline-flex; align-items: center; justify-content: space-between; gap: 0.75rem; padding-left: 0.7rem; padding-right: 0.7rem; border-top: 1px solid rgba(0, 0, 0, 0.2); border-bottom: 1px solid rgba(0, 0, 0, 0.2);">
|
||||
<span class="tag is-white gia-badge gia-tag-ribbon-main">
|
||||
<span style="display: inline-flex; align-items: baseline; gap: 0.35rem; min-width: 0;">
|
||||
<strong>{{ row.chat_name }}</strong>
|
||||
<small class="has-text-grey">{{ row.service }}</small>
|
||||
|
||||
24
core/templates/partials/bulma-send-composer.html
Normal file
24
core/templates/partials/bulma-send-composer.html
Normal file
@@ -0,0 +1,24 @@
|
||||
<div class="box is-shadowless gia-send-composer{% if composer_class %} {{ composer_class }}{% endif %}">
|
||||
<div class="field has-addons gia-send-composer-row">
|
||||
<div class="control is-expanded gia-send-composer-input-wrap">
|
||||
<textarea
|
||||
id="{{ textarea_id }}"
|
||||
class="textarea gia-send-composer-input{% if textarea_class %} {{ textarea_class }}{% endif %}"
|
||||
name="{{ textarea_name|default:'text' }}"
|
||||
rows="{{ textarea_rows|default:'1' }}"
|
||||
{% if textarea_placeholder %}placeholder="{{ textarea_placeholder }}"{% endif %}></textarea>
|
||||
</div>
|
||||
<div class="control gia-send-composer-action">
|
||||
<button
|
||||
class="button gia-send-composer-button{% if button_class %} {{ button_class }}{% endif %}"
|
||||
type="{{ button_type|default:'submit' }}"
|
||||
{% if button_disabled %}disabled{% endif %}
|
||||
{% if button_title %}title="{{ button_title }}"{% endif %}>
|
||||
{% if button_icon_class %}
|
||||
<span class="icon is-small"><i class="{{ button_icon_class }}"></i></span>
|
||||
{% endif %}
|
||||
<span>{{ button_label|default:"Send" }}</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
88
core/templates/partials/compose-message-row.html
Normal file
88
core/templates/partials/compose-message-row.html
Normal file
@@ -0,0 +1,88 @@
|
||||
<div class="compose-row {% if msg.outgoing %}is-out{% else %}is-in{% endif %}{% if msg.is_deleted %} is-deleted{% endif %}" data-ts="{{ msg.ts }}" data-message-id="{{ msg.id }}"{% if msg.reply_to_id %} data-reply-to-id="{{ msg.reply_to_id }}"{% endif %} data-reply-snippet="{{ msg.display_text|default:msg.text|default:''|truncatechars:120|escape }}">
|
||||
<article class="compose-bubble {% if msg.outgoing %}is-out{% else %}is-in{% endif %}">
|
||||
{% if msg.reply_to_id %}
|
||||
<div class="compose-reply-ref" data-reply-target-id="{{ msg.reply_to_id }}">
|
||||
<button type="button" class="compose-reply-link" title="Jump to referenced message">
|
||||
Reply to: {{ msg.reply_preview|default:"Referenced message"|escape }}
|
||||
</button>
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="compose-source-badge-wrap">
|
||||
<span class="tag is-light gia-badge compose-source-badge source-{{ msg.source_service|default:'web'|lower }}">{{ msg.source_label }}</span>
|
||||
</div>
|
||||
{% if msg.image_urls %}
|
||||
{% for image_url in msg.image_urls %}
|
||||
<figure class="compose-media">
|
||||
<img
|
||||
class="compose-image"
|
||||
src="{{ image_url }}"
|
||||
alt="Attachment"
|
||||
referrerpolicy="no-referrer"
|
||||
loading="lazy"
|
||||
decoding="async">
|
||||
</figure>
|
||||
{% endfor %}
|
||||
{% elif msg.image_url %}
|
||||
<figure class="compose-media">
|
||||
<img
|
||||
class="compose-image"
|
||||
src="{{ msg.image_url }}"
|
||||
alt="Attachment"
|
||||
referrerpolicy="no-referrer"
|
||||
loading="lazy"
|
||||
decoding="async">
|
||||
</figure>
|
||||
{% endif %}
|
||||
{% if not msg.hide_text %}
|
||||
<p class="compose-body">{{ msg.display_text|default:"(no text)" }}</p>
|
||||
{% else %}
|
||||
<p class="compose-body compose-image-fallback is-hidden">(no text)</p>
|
||||
{% endif %}
|
||||
{% if msg.edit_count %}
|
||||
<details class="compose-edit-history">
|
||||
<summary>Edited {{ msg.edit_count }} time{% if msg.edit_count != 1 %}s{% endif %}</summary>
|
||||
<ul>
|
||||
{% for edit in msg.edit_history %}
|
||||
<li>
|
||||
{% if edit.edited_display %}{{ edit.edited_display }}{% else %}Unknown time{% endif %}
|
||||
{% if edit.actor %} · {{ edit.actor }}{% endif %}
|
||||
{% if edit.source_service %} · {{ edit.source_service|upper }}{% endif %}
|
||||
<div class="compose-edit-diff">
|
||||
<span class="compose-edit-old">{{ edit.previous_text|default:"(empty)" }}</span>
|
||||
<span class="compose-edit-arrow">→</span>
|
||||
<span class="compose-edit-new">{{ edit.new_text|default:"(empty)" }}</span>
|
||||
</div>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</details>
|
||||
{% endif %}
|
||||
{% if msg.reactions %}
|
||||
<div class="compose-reactions" aria-label="Message reactions">
|
||||
{% for reaction in msg.reactions %}
|
||||
<span
|
||||
class="tag is-light gia-badge compose-reaction-chip"
|
||||
data-emoji="{{ reaction.emoji|escape }}"
|
||||
data-actor="{{ reaction.actor|default:''|escape }}"
|
||||
data-source-service="{{ reaction.source_service|default:''|escape }}"
|
||||
title="{{ reaction.actor|default:'Unknown' }} via {{ reaction.source_service|default:'unknown'|upper }}">
|
||||
{{ reaction.emoji }}
|
||||
</span>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
<p class="compose-msg-meta">
|
||||
{{ msg.display_ts }}{% if msg.author %} · {{ msg.author }}{% endif %}
|
||||
{% if msg.is_edited %}
|
||||
<span class="tag is-light gia-badge compose-msg-flag is-edited" title="Message edited{% if msg.last_edit_display %} at {{ msg.last_edit_display }}{% endif %}">edited</span>
|
||||
{% endif %}
|
||||
{% if msg.is_deleted %}
|
||||
<span class="tag is-light gia-badge compose-msg-flag is-deleted" title="Deleted{% if msg.deleted_display %} at {{ msg.deleted_display }}{% endif %}{% if msg.deleted_actor %} by {{ msg.deleted_actor }}{% endif %}">deleted</span>
|
||||
{% endif %}
|
||||
</p>
|
||||
<button type="button" class="button is-white is-small compose-reply-btn" title="Reply to this message" aria-label="Reply to this message">
|
||||
<span class="icon is-small"><i class="fa-solid fa-reply"></i></span>
|
||||
<span class="compose-reply-btn-label">Reply</span>
|
||||
</button>
|
||||
</article>
|
||||
</div>
|
||||
7
core/templates/partials/compose-message-rows.html
Normal file
7
core/templates/partials/compose-message-rows.html
Normal file
@@ -0,0 +1,7 @@
|
||||
{% if message_rows %}
|
||||
{% for msg in message_rows %}
|
||||
{% include "partials/compose-message-row.html" with msg=msg only %}
|
||||
{% endfor %}
|
||||
{% elif show_empty_state %}
|
||||
<p class="compose-empty">{{ empty_message|default:"No stored messages for this contact yet." }}</p>
|
||||
{% endif %}
|
||||
6
core/templates/partials/compose-panel-assets.html
Normal file
6
core/templates/partials/compose-panel-assets.html
Normal file
@@ -0,0 +1,6 @@
|
||||
{% load static %}
|
||||
<link rel="stylesheet" href="{% static 'css/compose-panel.css' %}">
|
||||
<script defer src="{% static 'js/compose-panel-core.js' %}"></script>
|
||||
<script defer src="{% static 'js/compose-panel-thread.js' %}"></script>
|
||||
<script defer src="{% static 'js/compose-panel-send.js' %}"></script>
|
||||
<script defer src="{% static 'js/compose-panel.js' %}"></script>
|
||||
@@ -1,5 +1,3 @@
|
||||
{% load static %}
|
||||
<link rel="stylesheet" href="{% static 'css/compose-panel.css' %}">
|
||||
<div
|
||||
id="{{ panel_id }}"
|
||||
class="compose-shell box"
|
||||
@@ -10,57 +8,63 @@
|
||||
data-initial-typing="{{ typing_state_json|default:'{}'|escape }}"
|
||||
data-cancel-send-url="{% url 'compose_cancel_send' %}"
|
||||
data-command-result-url="{% url 'compose_command_result' %}">
|
||||
<div class="compose-shell-head is-flex is-justify-content-space-between is-align-items-flex-start is-flex-wrap-wrap is-gap-2 mb-3">
|
||||
<div class="compose-shell-head is-flex is-justify-content-space-between is-align-items-flex-start is-flex-wrap-wrap is-gap-2 mb-2">
|
||||
<div>
|
||||
<p class="compose-shell-eyebrow is-size-7 has-text-weight-semibold mb-1">Manual Text Mode</p>
|
||||
{% if recent_contacts %}
|
||||
<div class="compose-contact-switch">
|
||||
<div class="select is-small">
|
||||
<select id="{{ panel_id }}-contact-select" class="compose-contact-select">
|
||||
{% for option in recent_contacts %}
|
||||
<option
|
||||
value="{{ option.identifier }}"
|
||||
data-service="{{ option.service }}"
|
||||
{% if option.person_id %}data-person="{{ option.person_id }}"{% endif %}
|
||||
data-service-map='{{ option.service_identifiers_json|escape }}'
|
||||
{% if option.is_active %}selected{% endif %}>
|
||||
{{ option.person_name }}
|
||||
</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
<p class="is-size-6 mb-0">
|
||||
{% if person %}
|
||||
{{ person.name }}
|
||||
{% elif group_name %}
|
||||
{{ group_name }}
|
||||
<div class="compose-context-row">
|
||||
<div class="compose-context-primary">
|
||||
{% if recent_contacts %}
|
||||
<div class="compose-contact-switch">
|
||||
<div class="select is-small is-fullwidth">
|
||||
<select id="{{ panel_id }}-contact-select" class="compose-contact-select">
|
||||
{% for option in recent_contacts %}
|
||||
<option
|
||||
value="{{ option.identifier }}"
|
||||
data-service="{{ option.service }}"
|
||||
{% if option.person_id %}data-person="{{ option.person_id }}"{% endif %}
|
||||
data-service-map='{{ option.service_identifiers_json|escape }}'
|
||||
{% if option.is_active %}selected{% endif %}>
|
||||
{{ option.person_name }}
|
||||
</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
{{ identifier }}
|
||||
<p class="is-size-6 mb-0">
|
||||
{% if person %}
|
||||
{{ person.name }}
|
||||
{% elif group_name %}
|
||||
{{ group_name }}
|
||||
{% else %}
|
||||
{{ identifier }}
|
||||
{% endif %}
|
||||
</p>
|
||||
{% endif %}
|
||||
</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% if platform_options %}
|
||||
<div class="compose-context-secondary">
|
||||
<div class="compose-platform-switch">
|
||||
<div class="select is-small is-fullwidth">
|
||||
<select id="{{ panel_id }}-platform-select" class="compose-platform-select">
|
||||
{% for option in platform_options %}
|
||||
<option
|
||||
value="{{ option.service }}"
|
||||
data-identifier="{{ option.identifier }}"
|
||||
data-person="{{ option.person_id }}"
|
||||
{% if option.is_active %}selected{% endif %}>
|
||||
{{ option.service_label }}
|
||||
</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
<p id="{{ panel_id }}-meta-line" class="is-size-7 compose-meta-line mb-0">
|
||||
{{ service|title }} · {{ identifier }}
|
||||
</p>
|
||||
{% if platform_options %}
|
||||
<div class="compose-platform-switch">
|
||||
<div class="select is-small">
|
||||
<select id="{{ panel_id }}-platform-select" class="compose-platform-select">
|
||||
{% for option in platform_options %}
|
||||
<option
|
||||
value="{{ option.service }}"
|
||||
data-identifier="{{ option.identifier }}"
|
||||
data-person="{{ option.person_id }}"
|
||||
{% if option.is_active %}selected{% endif %}>
|
||||
{{ option.service_label }}
|
||||
</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -86,96 +90,7 @@
|
||||
data-limit="{{ limit }}"
|
||||
data-last-ts="{{ last_ts }}"
|
||||
data-ws-url="{{ compose_ws_url }}">
|
||||
{% for msg in serialized_messages %}
|
||||
<div class="compose-row {% if msg.outgoing %}is-out{% else %}is-in{% endif %}{% if msg.is_deleted %} is-deleted{% endif %}" data-ts="{{ msg.ts }}" data-message-id="{{ msg.id }}"{% if msg.reply_to_id %} data-reply-to-id="{{ msg.reply_to_id }}"{% endif %} data-reply-snippet="{{ msg.display_text|default:msg.text|default:''|truncatechars:120|escape }}">
|
||||
<article class="compose-bubble {% if msg.outgoing %}is-out{% else %}is-in{% endif %}">
|
||||
{% if msg.reply_to_id %}
|
||||
<div class="compose-reply-ref" data-reply-target-id="{{ msg.reply_to_id }}" data-reply-preview="{{ msg.reply_preview|default:''|escape }}">
|
||||
<button type="button" class="compose-reply-link" title="Jump to referenced message"></button>
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="compose-source-badge-wrap">
|
||||
<span class="tag is-light compose-source-badge source-{{ msg.source_service|default:'web'|lower }}">{{ msg.source_label }}</span>
|
||||
</div>
|
||||
{% if msg.image_urls %}
|
||||
{% for image_url in msg.image_urls %}
|
||||
<figure class="compose-media">
|
||||
<img
|
||||
class="compose-image"
|
||||
src="{{ image_url }}"
|
||||
alt="Attachment"
|
||||
referrerpolicy="no-referrer"
|
||||
loading="lazy"
|
||||
decoding="async">
|
||||
</figure>
|
||||
{% endfor %}
|
||||
{% elif msg.image_url %}
|
||||
<figure class="compose-media">
|
||||
<img
|
||||
class="compose-image"
|
||||
src="{{ msg.image_url }}"
|
||||
alt="Attachment"
|
||||
referrerpolicy="no-referrer"
|
||||
loading="lazy"
|
||||
decoding="async">
|
||||
</figure>
|
||||
{% endif %}
|
||||
{% if not msg.hide_text %}
|
||||
<p class="compose-body">{{ msg.display_text|default:"(no text)" }}</p>
|
||||
{% else %}
|
||||
<p class="compose-body compose-image-fallback is-hidden">(no text)</p>
|
||||
{% endif %}
|
||||
{% if msg.edit_count %}
|
||||
<details class="compose-edit-history">
|
||||
<summary>Edited {{ msg.edit_count }} time{% if msg.edit_count != 1 %}s{% endif %}</summary>
|
||||
<ul>
|
||||
{% for edit in msg.edit_history %}
|
||||
<li>
|
||||
{% if edit.edited_display %}{{ edit.edited_display }}{% else %}Unknown time{% endif %}
|
||||
{% if edit.actor %} · {{ edit.actor }}{% endif %}
|
||||
{% if edit.source_service %} · {{ edit.source_service|upper }}{% endif %}
|
||||
<div class="compose-edit-diff">
|
||||
<span class="compose-edit-old">{{ edit.previous_text|default:"(empty)" }}</span>
|
||||
<span class="compose-edit-arrow">→</span>
|
||||
<span class="compose-edit-new">{{ edit.new_text|default:"(empty)" }}</span>
|
||||
</div>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</details>
|
||||
{% endif %}
|
||||
{% if msg.reactions %}
|
||||
<div class="compose-reactions" aria-label="Message reactions">
|
||||
{% for reaction in msg.reactions %}
|
||||
<span
|
||||
class="tag is-light compose-reaction-chip"
|
||||
data-emoji="{{ reaction.emoji|escape }}"
|
||||
data-actor="{{ reaction.actor|default:''|escape }}"
|
||||
data-source-service="{{ reaction.source_service|default:''|escape }}"
|
||||
title="{{ reaction.actor|default:'Unknown' }} via {{ reaction.source_service|default:'unknown'|upper }}">
|
||||
{{ reaction.emoji }}
|
||||
</span>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
<p class="compose-msg-meta">
|
||||
{{ msg.display_ts }}{% if msg.author %} · {{ msg.author }}{% endif %}
|
||||
{% if msg.is_edited %}
|
||||
<span class="tag is-light compose-msg-flag is-edited" title="Message edited{% if msg.last_edit_display %} at {{ msg.last_edit_display }}{% endif %}">edited</span>
|
||||
{% endif %}
|
||||
{% if msg.is_deleted %}
|
||||
<span class="tag is-light compose-msg-flag is-deleted" title="Deleted{% if msg.deleted_display %} at {{ msg.deleted_display }}{% endif %}{% if msg.deleted_actor %} by {{ msg.deleted_actor }}{% endif %}">deleted</span>
|
||||
{% endif %}
|
||||
</p>
|
||||
<button type="button" class="button is-white is-small compose-reply-btn" title="Reply to this message" aria-label="Reply to this message">
|
||||
<span class="icon is-small"><i class="fa-solid fa-reply"></i></span>
|
||||
<span class="compose-reply-btn-label">Reply</span>
|
||||
</button>
|
||||
</article>
|
||||
</div>
|
||||
{% empty %}
|
||||
<p class="compose-empty">No stored messages for this contact yet.</p>
|
||||
{% endfor %}
|
||||
{% include "partials/compose-message-rows.html" with message_rows=serialized_messages show_empty_state=True empty_message="No stored messages for this contact yet." %}
|
||||
</div>
|
||||
<p id="{{ panel_id }}-typing" class="compose-typing is-hidden">
|
||||
{% if person %}{{ person.name }}{% else %}Contact{% endif %} is typing...
|
||||
@@ -198,8 +113,15 @@
|
||||
<input type="hidden" name="failsafe_arm" value="0">
|
||||
<input type="hidden" name="failsafe_confirm" value="0">
|
||||
<div class="compose-send-safety">
|
||||
<label class="checkbox is-size-7">
|
||||
<input type="checkbox" class="manual-confirm"{% if not capability_send %} disabled{% endif %}> Confirm Send
|
||||
<label class="checkbox is-size-7" for="{{ panel_id }}-manual-confirm">
|
||||
<input
|
||||
id="{{ panel_id }}-manual-confirm"
|
||||
type="checkbox"
|
||||
class="manual-confirm"
|
||||
name="manual_confirm"
|
||||
value="1"
|
||||
{% if not capability_send %}disabled{% endif %}>
|
||||
Confirm Send
|
||||
</label>
|
||||
{% if not capability_send %}
|
||||
<p class="help is-size-7 has-text-grey">Send disabled: {{ capability_send_reason }}</p>
|
||||
@@ -210,19 +132,6 @@
|
||||
<span id="{{ panel_id }}-reply-text" class="compose-reply-banner-text"></span>
|
||||
<button type="button" id="{{ panel_id }}-reply-clear" class="button is-white is-small compose-reply-clear-btn">Clear</button>
|
||||
</div>
|
||||
<div class="compose-composer-capsule box is-shadowless">
|
||||
<textarea
|
||||
id="{{ panel_id }}-textarea"
|
||||
class="textarea compose-textarea"
|
||||
name="text"
|
||||
rows="1"
|
||||
placeholder="Type a message. Enter to send, Shift+Enter for newline."></textarea>
|
||||
<button class="button is-link is-light compose-send-btn" type="submit" disabled{% if not capability_send %} title="{{ capability_send_reason }}"{% endif %}>
|
||||
<span class="icon is-small"><i class="{{ manual_icon_class }}"></i></span>
|
||||
<span>Send</span>
|
||||
</button>
|
||||
</div>
|
||||
{% include "partials/bulma-send-composer.html" with composer_class="compose-composer-capsule" textarea_id=panel_id|add:"-textarea" textarea_class="compose-textarea" textarea_name="text" textarea_rows="1" textarea_placeholder="Type a message. Enter to send, Shift+Enter for newline." button_class="is-link is-light compose-send-btn" button_type="submit" button_disabled=True button_title=capability_send_reason|default_if_none:"" button_label="Send" button_icon_class=manual_icon_class %}
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<script src="{% static 'js/compose-panel.js' %}"></script>
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
<div id="{{ results_id }}">
|
||||
<p class="is-size-7 has-text-grey mb-3">
|
||||
{% if total_matches %}
|
||||
{% if is_search_results %}
|
||||
Showing {{ visible_count }} of {{ total_matches }} contacts.
|
||||
{% elif result_mode == "active_chats" %}
|
||||
Recent chats. Search to see all {{ total_contacts }} contacts.
|
||||
{% else %}
|
||||
Showing {{ visible_count }} contacts.
|
||||
{% endif %}
|
||||
{% else %}
|
||||
{% if is_search_results %}
|
||||
No contacts found.
|
||||
{% else %}
|
||||
No contacts yet.
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</p>
|
||||
|
||||
{% if contact_rows %}
|
||||
<nav class="panel">
|
||||
{% for row in contact_rows %}
|
||||
<a
|
||||
class="panel-block"
|
||||
hx-get="{{ row.compose_widget_url }}"
|
||||
hx-target="#widgets-here"
|
||||
hx-swap="beforeend">
|
||||
<span class="is-flex is-justify-content-space-between is-align-items-center is-flex-grow-1" style="min-width: 0; gap: 0.75rem;">
|
||||
<span style="min-width: 0;">
|
||||
<span class="has-text-weight-semibold">{{ row.person_name }}</span>
|
||||
<span class="is-size-7 has-text-grey is-block" style="overflow-wrap: anywhere;">
|
||||
{{ row.identifier }}
|
||||
</span>
|
||||
</span>
|
||||
<span class="tag is-light gia-badge">{{ row.service|title }}</span>
|
||||
</span>
|
||||
</a>
|
||||
{% endfor %}
|
||||
</nav>
|
||||
{% endif %}
|
||||
|
||||
{% if has_more %}
|
||||
<div class="mt-3">
|
||||
<button
|
||||
class="button is-small is-fullwidth is-light"
|
||||
hx-get="{{ results_url }}"
|
||||
hx-include="#{{ launcher_form_id }}"
|
||||
hx-vals='{"page": "{{ next_page }}"}'
|
||||
hx-target="#{{ results_id }}"
|
||||
hx-swap="outerHTML">
|
||||
Show {{ next_count }} more
|
||||
</button>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
@@ -1,117 +1,31 @@
|
||||
<div class="compose-workspace-widget">
|
||||
<div class="columns is-mobile is-gapless">
|
||||
<div class="column is-12-mobile is-12-tablet">
|
||||
<div
|
||||
style="
|
||||
margin-bottom: 0.75rem;
|
||||
padding: 0.5rem 0.25rem;
|
||||
border-bottom: 1px solid rgba(0, 0, 0, 0.12);
|
||||
">
|
||||
<p class="is-size-7 has-text-weight-semibold">Manual Workspace</p>
|
||||
<h3 class="title is-6" style="margin-bottom: 0.5rem;">Choose A Contact</h3>
|
||||
<p class="is-size-7">
|
||||
Open one or more direct chat widgets and keep them live in this workspace.
|
||||
</p>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<h3 class="title is-6 mb-1">Contacts</h3>
|
||||
<p class="is-size-7 has-text-grey">
|
||||
See all your contacts. Search to narrow the list.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<form
|
||||
id="compose-workspace-window-form"
|
||||
style="
|
||||
margin-bottom: 0.75rem;
|
||||
padding: 0.5rem 0.25rem;
|
||||
border-bottom: 1px solid rgba(0, 0, 0, 0.12);
|
||||
">
|
||||
<label class="label is-small" for="compose-workspace-limit">Window</label>
|
||||
<div class="select is-fullwidth is-small">
|
||||
<select id="compose-workspace-limit" name="limit">
|
||||
{% for option in limit_options %}
|
||||
<option value="{{ option }}" {% if option == limit %}selected{% endif %}>
|
||||
{{ option }} messages
|
||||
</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
<p class="help">
|
||||
How many recent messages to load in each new message widget.
|
||||
</p>
|
||||
</form>
|
||||
|
||||
<div>
|
||||
{% if contact_rows %}
|
||||
<div class="buttons are-small" style="display: grid; gap: 0.5rem;">
|
||||
{% for row in contact_rows %}
|
||||
<button
|
||||
class="button is-fullwidth"
|
||||
style="
|
||||
border-radius: 8px;
|
||||
border: 0;
|
||||
background: transparent;
|
||||
box-shadow: none;
|
||||
padding: 0;
|
||||
"
|
||||
hx-get="{{ row.compose_widget_url }}"
|
||||
hx-include="#compose-workspace-window-form"
|
||||
hx-target="#widgets-here"
|
||||
hx-swap="afterend">
|
||||
<span
|
||||
class="tags has-addons"
|
||||
style="
|
||||
display: inline-flex;
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
white-space: nowrap;
|
||||
">
|
||||
<span
|
||||
class="tag is-white"
|
||||
style="
|
||||
flex: 1;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 0.75rem;
|
||||
padding-left: 0.7rem;
|
||||
padding-right: 0.7rem;
|
||||
border: 1px solid rgba(0, 0, 0, 0.2);
|
||||
min-width: 0;
|
||||
">
|
||||
<span
|
||||
style="
|
||||
display: inline-flex;
|
||||
align-items: baseline;
|
||||
gap: 0.45rem;
|
||||
min-width: 0;
|
||||
">
|
||||
<strong>{{ row.person_name }}</strong>
|
||||
<small class="has-text-grey">{{ row.service|title }}</small>
|
||||
</span>
|
||||
<small
|
||||
class="has-text-grey"
|
||||
style="
|
||||
min-width: 0;
|
||||
overflow-wrap: anywhere;
|
||||
word-break: break-all;
|
||||
text-align: right;
|
||||
">
|
||||
{{ row.identifier }}
|
||||
</small>
|
||||
</span>
|
||||
</span>
|
||||
</button>
|
||||
{% if not row.linked_person %}
|
||||
<a
|
||||
class="button is-small is-light"
|
||||
href="{{ row.match_url }}"
|
||||
title="Link this identifier to a person">
|
||||
<span class="icon is-small"><i class="fa-solid fa-link"></i></span>
|
||||
<span>Match</span>
|
||||
</a>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% else %}
|
||||
<p class="has-text-grey">No contacts available yet.</p>
|
||||
{% endif %}
|
||||
<form id="{{ launcher_form_id }}">
|
||||
<div class="field">
|
||||
<label class="label is-small" for="{{ search_input_id }}">Search</label>
|
||||
<div class="control has-icons-left">
|
||||
<input
|
||||
id="{{ search_input_id }}"
|
||||
class="input is-small"
|
||||
type="search"
|
||||
name="q"
|
||||
value="{{ search_query }}"
|
||||
placeholder="Name, identifier, or service"
|
||||
hx-get="{{ results_url }}"
|
||||
hx-trigger="input changed delay:250ms, search"
|
||||
hx-target="#{{ results_id }}"
|
||||
hx-include="#{{ launcher_form_id }}"
|
||||
hx-swap="outerHTML">
|
||||
<span class="icon is-small is-left"><i class="fa-solid fa-magnifying-glass"></i></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
{% include "partials/compose-workspace-contact-results.html" %}
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
<div id="{{ results_id }}">
|
||||
<p class="is-size-7 has-text-grey mb-3">
|
||||
{% if history_rows %}
|
||||
Showing {{ result_start }}-{{ result_end }} recent matches.
|
||||
{% else %}
|
||||
No persisted messages match this search.
|
||||
{% endif %}
|
||||
</p>
|
||||
|
||||
{% if history_rows %}
|
||||
<div class="is-flex is-flex-direction-column" style="gap: 0.5rem;">
|
||||
{% for row in history_rows %}
|
||||
<div class="box is-shadowless p-3 m-0">
|
||||
<div class="is-flex is-justify-content-space-between is-align-items-flex-start" style="gap: 0.75rem;">
|
||||
<div style="min-width: 0;">
|
||||
<p class="has-text-weight-semibold mb-1">
|
||||
{{ row.person_name }}
|
||||
<span class="tag is-light gia-badge ml-2">{{ row.service_label }}</span>
|
||||
<span class="tag {% if row.outgoing %}is-warning{% else %}is-info{% endif %} is-light gia-badge ml-1">{{ row.direction_label }}</span>
|
||||
</p>
|
||||
<p class="is-size-7 has-text-grey mb-1" style="overflow-wrap: anywhere;">
|
||||
{{ row.identifier }} · {{ row.display_ts }}
|
||||
</p>
|
||||
<p class="mb-0" style="overflow-wrap: anywhere;">
|
||||
{{ row.text_preview }}
|
||||
</p>
|
||||
</div>
|
||||
<div class="buttons are-small m-0">
|
||||
<button
|
||||
class="button is-small is-link is-light"
|
||||
hx-get="{{ row.compose_widget_url }}"
|
||||
hx-include="#{{ browser_form_id }}"
|
||||
hx-target="#widgets-here"
|
||||
hx-swap="beforeend">
|
||||
<span class="icon is-small"><i class="fa-solid fa-table-cells-large"></i></span>
|
||||
<span>Open</span>
|
||||
</button>
|
||||
<a class="button is-small is-light" href="{{ row.compose_page_url }}">
|
||||
<span class="icon is-small"><i class="fa-solid fa-arrow-up-right-from-square"></i></span>
|
||||
<span>Page</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if has_more %}
|
||||
<div class="mt-3">
|
||||
<button
|
||||
class="button is-small is-fullwidth is-light"
|
||||
hx-get="{{ results_url }}"
|
||||
hx-include="#{{ browser_form_id }}"
|
||||
hx-vals='{"page": "{{ next_page }}"}'
|
||||
hx-target="#{{ results_id }}"
|
||||
hx-swap="outerHTML">
|
||||
Show more history
|
||||
</button>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
108
core/templates/partials/compose-workspace-history-widget.html
Normal file
108
core/templates/partials/compose-workspace-history-widget.html
Normal file
@@ -0,0 +1,108 @@
|
||||
<div class="compose-workspace-widget">
|
||||
<div class="mb-4">
|
||||
<p class="is-size-7 has-text-weight-semibold">Manual Workspace</p>
|
||||
<h3 class="title is-6 mb-2">Browse Message History</h3>
|
||||
<p class="is-size-7">
|
||||
Filter persisted messages across contacts, then reopen the matching live thread widget.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<form id="{{ browser_form_id }}">
|
||||
{% if person_scope_id %}
|
||||
<input type="hidden" name="person" value="{{ person_scope_id }}">
|
||||
{% endif %}
|
||||
|
||||
<div class="field">
|
||||
<label class="label is-small" for="{{ search_input_id }}">Find Messages</label>
|
||||
<div class="control has-icons-left">
|
||||
<input
|
||||
id="{{ search_input_id }}"
|
||||
class="input is-small"
|
||||
type="search"
|
||||
name="q"
|
||||
value="{{ search_query }}"
|
||||
placeholder="Person, identifier, or message text"
|
||||
hx-get="{{ results_url }}"
|
||||
hx-trigger="input changed delay:250ms, search"
|
||||
hx-target="#{{ results_id }}"
|
||||
hx-include="#{{ browser_form_id }}"
|
||||
hx-swap="outerHTML">
|
||||
<span class="icon is-small is-left"><i class="fa-solid fa-magnifying-glass"></i></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="columns is-mobile is-multiline mb-1">
|
||||
<div class="column is-half pt-0 pb-2">
|
||||
<label class="label is-small" for="{{ service_input_id }}">Service</label>
|
||||
<div class="select is-small is-fullwidth">
|
||||
<select
|
||||
id="{{ service_input_id }}"
|
||||
name="service"
|
||||
hx-get="{{ results_url }}"
|
||||
hx-trigger="change"
|
||||
hx-target="#{{ results_id }}"
|
||||
hx-include="#{{ browser_form_id }}"
|
||||
hx-swap="outerHTML">
|
||||
{% for value, label in service_options %}
|
||||
<option value="{{ value }}" {% if value == service %}selected{% endif %}>{{ label }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="column is-half pt-0 pb-2">
|
||||
<label class="label is-small" for="{{ direction_input_id }}">Direction</label>
|
||||
<div class="select is-small is-fullwidth">
|
||||
<select
|
||||
id="{{ direction_input_id }}"
|
||||
name="direction"
|
||||
hx-get="{{ results_url }}"
|
||||
hx-trigger="change"
|
||||
hx-target="#{{ results_id }}"
|
||||
hx-include="#{{ browser_form_id }}"
|
||||
hx-swap="outerHTML">
|
||||
{% for value, label in direction_options %}
|
||||
<option value="{{ value }}" {% if value == direction %}selected{% endif %}>{{ label }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="column is-half pt-0 pb-2">
|
||||
<label class="label is-small" for="{{ days_input_id }}">Range</label>
|
||||
<div class="select is-small is-fullwidth">
|
||||
<select
|
||||
id="{{ days_input_id }}"
|
||||
name="days"
|
||||
hx-get="{{ results_url }}"
|
||||
hx-trigger="change"
|
||||
hx-target="#{{ results_id }}"
|
||||
hx-include="#{{ browser_form_id }}"
|
||||
hx-swap="outerHTML">
|
||||
{% for value, label in days_options %}
|
||||
<option value="{{ value }}" {% if value == days %}selected{% endif %}>{{ label }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="column is-half pt-0 pb-2">
|
||||
<label class="label is-small" for="{{ thread_limit_input_id }}">Thread Window</label>
|
||||
<div class="select is-small is-fullwidth">
|
||||
<select id="{{ thread_limit_input_id }}" name="limit">
|
||||
{% for option in thread_limit_options %}
|
||||
<option value="{{ option }}" {% if option == limit %}selected{% endif %}>
|
||||
{{ option }} messages
|
||||
</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if person_scope %}
|
||||
<p class="help mb-3">
|
||||
Scoped to <strong>{{ person_scope.name }}</strong>.
|
||||
</p>
|
||||
{% endif %}
|
||||
</form>
|
||||
|
||||
{% include "partials/compose-workspace-history-results.html" %}
|
||||
</div>
|
||||
@@ -23,7 +23,7 @@
|
||||
<a
|
||||
class="has-text-grey button nowrap-child"
|
||||
onclick="window.prompt('Copy to clipboard: Ctrl+C, Enter', '{{ item.id }}');">
|
||||
<span class="icon" data-tooltip="Copy to clipboard">
|
||||
<span class="icon" title="Copy to clipboard" aria-label="Copy to clipboard">
|
||||
<i class="fa-solid fa-copy" aria-hidden="true"></i>
|
||||
</span>
|
||||
</a>
|
||||
@@ -66,4 +66,4 @@
|
||||
{% endfor %}
|
||||
|
||||
</table>
|
||||
{% endcache %}
|
||||
{% endcache %}
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
<a
|
||||
class="has-text-grey button nowrap-child"
|
||||
onclick="window.prompt('Copy to clipboard: Ctrl+C, Enter', '{{ item.id }}');">
|
||||
<span class="icon" data-tooltip="Copy to clipboard">
|
||||
<span class="icon" title="Copy to clipboard" aria-label="Copy to clipboard">
|
||||
<i class="fa-solid fa-copy" aria-hidden="true"></i>
|
||||
</span>
|
||||
</a>
|
||||
@@ -94,4 +94,4 @@
|
||||
{% endfor %}
|
||||
|
||||
</table>
|
||||
{% endcache %}
|
||||
{% endcache %}
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
<a
|
||||
class="has-text-grey button nowrap-child"
|
||||
onclick="window.prompt('Copy to clipboard: Ctrl+C, Enter', '{{ item.id }}');">
|
||||
<span class="icon" data-tooltip="Copy to clipboard">
|
||||
<span class="icon" title="Copy to clipboard" aria-label="Copy to clipboard">
|
||||
<i class="fa-solid fa-copy" aria-hidden="true"></i>
|
||||
</span>
|
||||
</a>
|
||||
@@ -40,7 +40,7 @@
|
||||
<a
|
||||
class="has-text-grey button nowrap-child"
|
||||
onclick="window.prompt('Copy to clipboard: Ctrl+C, Enter', '{{ item.sender_uuid }}');">
|
||||
<span class="icon" data-tooltip="Copy to clipboard">
|
||||
<span class="icon" title="Copy to clipboard" aria-label="Copy to clipboard">
|
||||
<i class="fa-solid fa-copy" aria-hidden="true"></i>
|
||||
</span>
|
||||
</a>
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
<a class="navbar-item" href="{% url 'compose_workspace' %}">
|
||||
<span class="icon is-small"><i class="fa-solid fa-table-cells-large"></i></span>
|
||||
<span style="margin-left: 0.35rem;">Compose Workspace</span>
|
||||
</a>
|
||||
<hr class="navbar-divider" style="margin: 0.2rem 0;">
|
||||
{% if items %}
|
||||
{% for item in items %}
|
||||
<a class="navbar-item" href="{{ item.compose_url }}">
|
||||
|
||||
@@ -13,8 +13,7 @@
|
||||
class="button osint-capsule-tab"
|
||||
hx-get="{{ tab.widget_url }}"
|
||||
hx-target="#widgets-here"
|
||||
hx-swap="afterend"
|
||||
onclick="document.getElementById('widgets-here').style.display='block';"
|
||||
hx-swap="beforeend"
|
||||
title="Open {{ tab.label }} setup widget">
|
||||
<span class="icon is-small"><i class="{{ tab.icon }}"></i></span>
|
||||
<span>{{ tab.label }}</span>
|
||||
|
||||
@@ -198,7 +198,7 @@
|
||||
hx-get="{{ action.url }}"
|
||||
hx-target="{{ action.target }}"
|
||||
hx-swap="innerHTML"
|
||||
{% if action.target == "#windows-here" %}onclick="window.giaPrepareWindowAnchor(this);"{% endif %}
|
||||
{% if action.target == "#windows-here" %}onclick="if (window.giaPrepareWindowAnchor) { window.giaPrepareWindowAnchor(this); }"{% endif %}
|
||||
title="{{ action.title }}">
|
||||
<span class="icon"><i class="{{ action.icon }}"></i></span>
|
||||
</button>
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
<a
|
||||
class="has-text-grey button nowrap-child"
|
||||
onclick="window.prompt('Copy to clipboard: Ctrl+C, Enter', '{{ item.id }}');">
|
||||
<span class="icon" data-tooltip="Copy to clipboard">
|
||||
<span class="icon" title="Copy to clipboard" aria-label="Copy to clipboard">
|
||||
<i class="fa-solid fa-copy" aria-hidden="true"></i>
|
||||
</span>
|
||||
</a>
|
||||
@@ -77,4 +77,4 @@
|
||||
{% endfor %}
|
||||
|
||||
</table>
|
||||
{% endcache %}
|
||||
{% endcache %}
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
<a
|
||||
class="has-text-grey button nowrap-child"
|
||||
onclick="window.prompt('Copy to clipboard: Ctrl+C, Enter', '{{ item.id }}');">
|
||||
<span class="icon" data-tooltip="Copy to clipboard">
|
||||
<span class="icon" title="Copy to clipboard" aria-label="Copy to clipboard">
|
||||
<i class="fa-solid fa-copy" aria-hidden="true"></i>
|
||||
</span>
|
||||
</a>
|
||||
@@ -74,4 +74,4 @@
|
||||
{% endfor %}
|
||||
|
||||
</table>
|
||||
{% endcache %}
|
||||
{% endcache %}
|
||||
|
||||
@@ -37,7 +37,9 @@
|
||||
class="button is-success is-light"
|
||||
hx-get="{% url 'message_accept_api' message_id=item.id %}"
|
||||
hx-swap="none"
|
||||
_="on htmx:afterRequest if event.detail.successful remove #queue-card-{{ item.id }} then trigger {{ context_object_name_singular }}Event on body end">
|
||||
data-queue-after="remove-card"
|
||||
data-queue-card-id="queue-card-{{ item.id }}"
|
||||
data-queue-refresh-event="{{ context_object_name_singular }}Event">
|
||||
<span class="icon is-small"><i class="fa-solid fa-check"></i></span>
|
||||
<span>Approve</span>
|
||||
</button>
|
||||
@@ -45,7 +47,9 @@
|
||||
class="button is-danger is-light"
|
||||
hx-get="{% url 'message_reject_api' message_id=item.id %}"
|
||||
hx-swap="none"
|
||||
_="on htmx:afterRequest if event.detail.successful remove #queue-card-{{ item.id }} then trigger {{ context_object_name_singular }}Event on body end">
|
||||
data-queue-after="remove-card"
|
||||
data-queue-card-id="queue-card-{{ item.id }}"
|
||||
data-queue-refresh-event="{{ context_object_name_singular }}Event">
|
||||
<span class="icon is-small"><i class="fa-solid fa-xmark"></i></span>
|
||||
<span>Reject</span>
|
||||
</button>
|
||||
@@ -65,7 +69,8 @@
|
||||
hx-trigger="click"
|
||||
hx-target="#queue-inline-editor-{{ item.id }}"
|
||||
hx-swap="innerHTML"
|
||||
_="on htmx:afterRequest if event.detail.successful set #queue-inline-editor-{{ item.id }}.style.display to 'block' end"
|
||||
data-queue-after="show-inline-editor"
|
||||
data-queue-editor-id="queue-inline-editor-{{ item.id }}"
|
||||
class="button is-light">
|
||||
<span class="icon is-small"><i class="fa-solid fa-pen"></i></span>
|
||||
<span>Edit</span>
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
<a
|
||||
class="has-text-grey button nowrap-child"
|
||||
onclick="window.prompt('Copy to clipboard: Ctrl+C, Enter', '{{ item.id }}');">
|
||||
<span class="icon" data-tooltip="Copy to clipboard">
|
||||
<span class="icon" title="Copy to clipboard" aria-label="Copy to clipboard">
|
||||
<i class="fa-solid fa-copy" aria-hidden="true"></i>
|
||||
</span>
|
||||
</a>
|
||||
|
||||
@@ -1,12 +1,28 @@
|
||||
{% if settings_nav %}
|
||||
<h1 class="title is-4">{{ settings_nav.title }}</h1>
|
||||
<div class="tabs is-boxed is-small mb-4 security-page-tabs">
|
||||
<ul>
|
||||
{% for tab in settings_nav.tabs %}
|
||||
<li class="{% if tab.active %}is-active{% endif %}">
|
||||
<a href="{{ tab.href }}">{{ tab.label }}</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
<nav class="gia-settings-nav" aria-label="Settings navigation">
|
||||
{% if settings_nav.groups %}
|
||||
<div class="tabs is-boxed is-small mb-2 security-page-tabs gia-settings-nav-groups">
|
||||
<ul>
|
||||
{% for tab in settings_nav.groups %}
|
||||
<li class="{% if tab.active %}is-active{% endif %}">
|
||||
<a href="{{ tab.href }}">{{ tab.label }}</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% for row in settings_nav.rows %}
|
||||
{% if row.tabs|length > 1 %}
|
||||
<div class="tabs is-toggle is-toggle-rounded is-small security-page-tabs gia-settings-nav-row {% if forloop.last %}mb-4{% else %}mb-2{% endif %}">
|
||||
<ul>
|
||||
{% for tab in row.tabs %}
|
||||
<li class="{% if tab.active %}is-active{% endif %}">
|
||||
<a href="{{ tab.href }}">{{ tab.label }}</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</nav>
|
||||
{% endif %}
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
<a
|
||||
class="has-text-grey button nowrap-child"
|
||||
onclick="window.prompt('Copy to clipboard: Ctrl+C, Enter', '{{ item.chat.source_uuid }}');">
|
||||
<span class="icon" data-tooltip="Copy to clipboard">
|
||||
<span class="icon" title="Copy to clipboard" aria-label="Copy to clipboard">
|
||||
<i class="fa-solid fa-copy" aria-hidden="true"></i>
|
||||
</span>
|
||||
</a>
|
||||
@@ -112,7 +112,7 @@
|
||||
hx-get="{{ item.compose_widget_url }}"
|
||||
hx-trigger="click"
|
||||
hx-target="#widgets-here"
|
||||
hx-swap="afterend"
|
||||
hx-swap="beforeend"
|
||||
class="button">
|
||||
<span class="icon-text">
|
||||
<span class="icon">
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
<a
|
||||
class="has-text-grey button nowrap-child"
|
||||
onclick="window.prompt('Copy to clipboard: Ctrl+C, Enter', '{{ item.uuid }}');">
|
||||
<span class="icon" data-tooltip="Copy to clipboard">
|
||||
<span class="icon" title="Copy to clipboard" aria-label="Copy to clipboard">
|
||||
<i class="fa-solid fa-copy" aria-hidden="true"></i>
|
||||
</span>
|
||||
</a>
|
||||
@@ -55,4 +55,4 @@
|
||||
|
||||
</table>
|
||||
{% endif %}
|
||||
{% endcache %}
|
||||
{% endcache %}
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
<a
|
||||
class="has-text-grey button nowrap-child"
|
||||
onclick="window.prompt('Copy to clipboard: Ctrl+C, Enter', '{{ item.identifier }}');">
|
||||
<span class="icon" data-tooltip="Copy identifier">
|
||||
<span class="icon" title="Copy identifier" aria-label="Copy identifier">
|
||||
<i class="fa-solid fa-copy" aria-hidden="true"></i>
|
||||
</span>
|
||||
</a>
|
||||
@@ -46,7 +46,7 @@
|
||||
hx-get="{{ item.compose_widget_url }}"
|
||||
hx-trigger="click"
|
||||
hx-target="#widgets-here"
|
||||
hx-swap="afterend"
|
||||
hx-swap="beforeend"
|
||||
class="button"
|
||||
title="Manual text mode widget">
|
||||
<span class="icon"><i class="fa-solid fa-paper-plane"></i></span>
|
||||
|
||||
@@ -100,7 +100,7 @@
|
||||
hx-get="{{ item.compose_widget_url }}"
|
||||
hx-trigger="click"
|
||||
hx-target="#widgets-here"
|
||||
hx-swap="afterend"
|
||||
hx-swap="beforeend"
|
||||
class="button"
|
||||
title="Open manual chat widget">
|
||||
<span class="icon"><i class="fa-solid fa-paper-plane"></i></span>
|
||||
|
||||
7
core/templates/partials/workspace-widget-loader.html
Normal file
7
core/templates/partials/workspace-widget-loader.html
Normal file
@@ -0,0 +1,7 @@
|
||||
<div
|
||||
hx-headers='{"X-CSRFToken": "{{ csrf_token }}"}'
|
||||
hx-get="{{ widget_url }}"
|
||||
hx-target="{% firstof target_selector '#widgets-here' %}"
|
||||
hx-trigger="{% firstof trigger_name 'load' %}{% if trigger_delay %} delay:{{ trigger_delay }}{% endif %}"
|
||||
hx-swap="{% firstof swap_strategy 'beforeend' %}"
|
||||
style="display: none;"></div>
|
||||
Reference in New Issue
Block a user