593 lines
24 KiB
HTML
593 lines
24 KiB
HTML
<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">
|
|
<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);">
|
|
AI {{ operation_label }}
|
|
</span>
|
|
</div>
|
|
|
|
{% if error %}
|
|
<div class="notification is-danger is-light" style="padding: 0.6rem;">
|
|
{{ result_text }}
|
|
</div>
|
|
{% else %}
|
|
{% if ai_request_status %}
|
|
<div class="tags" style="margin-bottom: 0.45rem;">
|
|
<span class="tag is-light">Request {{ ai_request_status|title }}</span>
|
|
<span class="tag is-light">Messages {{ ai_request_message_count|default:0 }}</span>
|
|
{% if ai_result_created_at %}
|
|
<span class="tag is-light">Result {{ ai_result_created_at }}</span>
|
|
{% endif %}
|
|
{% if ai_request_started_at %}
|
|
<span class="tag is-light">Started {{ ai_request_started_at }}</span>
|
|
{% endif %}
|
|
{% if ai_request_finished_at %}
|
|
<span class="tag is-light">Finished {{ ai_request_finished_at }}</span>
|
|
{% endif %}
|
|
</div>
|
|
{% if ai_request_window_spec %}
|
|
<div class="tags" style="margin-bottom: 0.45rem;">
|
|
{% if ai_request_window_tags %}
|
|
{% for item in ai_request_window_tags %}
|
|
<span class="tag is-light">{{ item }}</span>
|
|
{% endfor %}
|
|
{% else %}
|
|
<span class="tag is-light">Window selected</span>
|
|
{% endif %}
|
|
{% if ai_request_policy_tags %}
|
|
{% for item in ai_request_policy_tags %}
|
|
<span class="tag is-light">{{ item }}</span>
|
|
{% endfor %}
|
|
{% endif %}
|
|
</div>
|
|
{% endif %}
|
|
{% endif %}
|
|
{% if operation == "artifacts" %}
|
|
<details
|
|
id="ai-mitigation-collapsible-{{ person.id }}"
|
|
class="ai-mitigation-collapsible"
|
|
open
|
|
style="margin-top: 0.35rem;">
|
|
<summary class="ai-mitigation-summary">
|
|
<span class="icon is-small"><i class="fa-solid fa-shield-heart"></i></span>
|
|
<span>
|
|
Mitigation Protocol
|
|
{% if latest_plan %}
|
|
· {{ latest_plan.status|title }}
|
|
{% else %}
|
|
· Not Created
|
|
{% endif %}
|
|
</span>
|
|
</summary>
|
|
<div class="ai-mitigation-collapsible-body">
|
|
{% if latest_plan %}
|
|
{% include "partials/ai-workspace-mitigation-panel.html" with person=person plan=latest_plan rules=latest_plan_rules games=latest_plan_games corrections=latest_plan_corrections fundamentals_text=latest_plan.fundamental_items|join:"\n" mitigation_messages=latest_plan_messages latest_export=latest_plan_export notice_message=mitigation_notice_message notice_level=mitigation_notice_level auto_settings=latest_auto_settings active_tab="plan_board" %}
|
|
{% else %}
|
|
<div id="mitigation-shell-{{ person.id }}" class="box" style="padding: 0.65rem; margin-top: 0.2rem; border: 1px dashed rgba(0, 0, 0, 0.25); box-shadow: none;">
|
|
<p class="is-size-7 has-text-grey">No mitigation plan yet. Use the Patterns tab to generate one.</p>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</details>
|
|
{% elif operation == "draft_reply" and draft_replies %}
|
|
<div id="draft-host-{{ person.id }}-{{ operation }}" data-selected="0">
|
|
<div class="columns is-multiline" style="margin: 0 -0.35rem;">
|
|
{% for option in draft_replies %}
|
|
<div class="column is-12-mobile is-4-tablet" style="padding: 0.35rem;">
|
|
<article
|
|
class="draft-option-card {% if forloop.first %}is-selected{% endif %}"
|
|
data-index="{{ forloop.counter0 }}"
|
|
onclick="giaWorkspaceUseDraft('{{ person.id }}', '{{ operation }}', {{ forloop.counter0 }}); return false;"
|
|
style="height: 100%; padding: 0.6rem; border-radius: 9px; border: 1px solid rgba(0, 0, 0, 0.16); background: #fff; cursor: pointer; transition: border-color 120ms ease, box-shadow 120ms ease, background-color 120ms ease;">
|
|
<p class="draft-tone-line">
|
|
{% with tone=option.label|default:""|lower %}
|
|
{% if tone == "soft" %}
|
|
<span class="icon is-small has-text-success"><i class="fa-solid fa-leaf"></i></span>
|
|
{% elif tone == "neutral" %}
|
|
<span class="icon is-small has-text-info"><i class="fa-solid fa-scale-balanced"></i></span>
|
|
{% elif tone == "firm" %}
|
|
<span class="icon is-small has-text-danger"><i class="fa-solid fa-shield-heart"></i></span>
|
|
{% else %}
|
|
<span class="icon is-small has-text-grey"><i class="fa-solid fa-comment-dots"></i></span>
|
|
{% endif %}
|
|
{% endwith %}
|
|
<span>{{ option.label|default:"Option" }}</span>
|
|
</p>
|
|
<p class="draft-text" style="white-space: pre-wrap; margin-bottom: 0;">{{ option.text }}</p>
|
|
</article>
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
</div>
|
|
|
|
<div id="draft-send-shell-{{ person.id }}-{{ operation }}" style="margin-top: 0.5rem; padding: 0.6rem; border: 1px solid rgba(0, 0, 0, 0.16); border-radius: 8px;">
|
|
<form
|
|
hx-headers='{"X-CSRFToken": "{{ csrf_token }}"}'
|
|
hx-post="{% url 'ai_workspace_send' type='widget' person_id=person.id %}"
|
|
hx-target="#draft-send-status-{{ person.id }}-{{ operation }}"
|
|
hx-swap="innerHTML">
|
|
<input type="hidden" id="draft-send-input-{{ person.id }}-{{ operation }}" name="draft_text" value="">
|
|
<input type="hidden" id="draft-send-force-{{ person.id }}-{{ operation }}" name="force_send" value="0">
|
|
<input type="hidden" name="target_identifier_id" value="{{ send_target_bundle.selected_id }}">
|
|
<div class="field">
|
|
<label class="label is-small">Draft Preview</label>
|
|
<div class="control">
|
|
<textarea id="draft-send-preview-{{ person.id }}-{{ operation }}" class="textarea is-small" rows="4" readonly></textarea>
|
|
</div>
|
|
</div>
|
|
<div class="field" style="margin-bottom: 0.4rem;">
|
|
<div class="control buttons are-small" style="margin: 0; gap: 0.35rem;">
|
|
<button id="draft-send-btn-{{ person.id }}-{{ operation }}" class="button is-small is-link" {% if not send_state.can_send %}disabled{% endif %}>
|
|
Send Draft
|
|
</button>
|
|
<button
|
|
type="button"
|
|
class="button is-small is-info is-light"
|
|
onclick="giaWorkspaceQueueSelectedDraft('{{ person.id }}'); return false;">
|
|
<span class="icon is-small"><i class="fa-solid fa-inbox-in"></i></span>
|
|
<span>Add To Queue</span>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<div id="draft-send-status-{{ person.id }}-{{ operation }}"></div>
|
|
</form>
|
|
</div>
|
|
{% else %}
|
|
{% if operation == "extract_patterns" %}
|
|
<div class="columns is-multiline" style="margin: 0 -0.35rem;">
|
|
{% for section in result_sections %}
|
|
<div class="column is-12-mobile is-6-tablet" style="padding: 0.35rem;">
|
|
<article class="box ai-section-box" style="height: 100%; padding: 0.65rem; margin-bottom: 0; border: 1px solid rgba(0, 0, 0, 0.14); box-shadow: none;">
|
|
{% if section.level <= 2 %}
|
|
<h3 class="title is-6" style="margin-bottom: 0.45rem;">{{ section.title }}</h3>
|
|
{% elif section.level == 3 %}
|
|
<h4 class="title is-6" style="margin-bottom: 0.45rem;">{{ section.title }}</h4>
|
|
{% else %}
|
|
<h5 class="subtitle is-7 has-text-weight-semibold" style="margin-bottom: 0.45rem;">{{ section.title }}</h5>
|
|
{% endif %}
|
|
|
|
{% for block in section.blocks %}
|
|
{% if block.type == "ul" %}
|
|
<ul style="margin: 0 0 0.45rem 1.15rem;">
|
|
{% for item in block.items %}
|
|
<li style="margin-bottom: 0.25rem;">{{ item }}</li>
|
|
{% endfor %}
|
|
</ul>
|
|
{% else %}
|
|
{% for item in block.items %}
|
|
<p style="margin-bottom: 0.45rem; white-space: pre-wrap;">{{ item }}</p>
|
|
{% endfor %}
|
|
{% endif %}
|
|
{% endfor %}
|
|
</article>
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
{% else %}
|
|
<div class="ai-section-stack">
|
|
{% for section in result_sections %}
|
|
<article class="box ai-section-box" style="padding: 0.65rem; margin-bottom: 0.5rem; border: 1px solid rgba(0, 0, 0, 0.14); box-shadow: none;">
|
|
{% if section.level <= 2 %}
|
|
<h3 class="title is-6" style="margin-bottom: 0.45rem;">{{ section.title }}</h3>
|
|
{% elif section.level == 3 %}
|
|
<h4 class="title is-6" style="margin-bottom: 0.45rem;">{{ section.title }}</h4>
|
|
{% else %}
|
|
<h5 class="subtitle is-7 has-text-weight-semibold" style="margin-bottom: 0.45rem;">{{ section.title }}</h5>
|
|
{% endif %}
|
|
|
|
{% for block in section.blocks %}
|
|
{% if block.type == "ul" %}
|
|
<ul style="margin: 0 0 0.45rem 1.15rem;">
|
|
{% for item in block.items %}
|
|
<li style="margin-bottom: 0.25rem;">{{ item }}</li>
|
|
{% endfor %}
|
|
</ul>
|
|
{% else %}
|
|
{% for item in block.items %}
|
|
<p style="margin-bottom: 0.45rem; white-space: pre-wrap;">{{ item }}</p>
|
|
{% endfor %}
|
|
{% endif %}
|
|
{% endfor %}
|
|
</article>
|
|
{% endfor %}
|
|
</div>
|
|
{% endif %}
|
|
{% endif %}
|
|
|
|
{% if interaction_signals %}
|
|
<article
|
|
id="ai-signals-{{ person.id }}-{{ operation }}"
|
|
class="box"
|
|
style="padding: 0.55rem; margin-top: 0.55rem; border: 1px solid rgba(0, 0, 0, 0.14); box-shadow: none;">
|
|
<p class="is-size-7 has-text-weight-semibold" style="margin-bottom: 0.35rem;">Interaction Signals</p>
|
|
<p class="is-size-7 has-text-grey" style="margin-bottom: 0.35rem;">
|
|
Click a signal to highlight related memory proposals and evidence citations.
|
|
</p>
|
|
<div class="tags">
|
|
{% for signal in interaction_signals %}
|
|
<button
|
|
type="button"
|
|
class="tag is-light js-ai-signal-tag ai-linkable"
|
|
data-signal-key="{{ signal.signal_key|default:signal.label }}"
|
|
data-citation-ids="{{ signal.message_event_ids|join:',' }}"
|
|
title="{{ signal.meaning|default:'Linked conversational signal from this AI run.' }}">
|
|
{{ signal.display_label|default:signal.label|title }} ({{ signal.valence|title }})
|
|
</button>
|
|
{% endfor %}
|
|
</div>
|
|
</article>
|
|
{% endif %}
|
|
|
|
{% if memory_proposals %}
|
|
<section id="ai-memory-proposals-{{ person.id }}-{{ operation }}" style="margin-top: 0.55rem;">
|
|
<p class="is-size-7 has-text-weight-semibold" style="margin-bottom: 0.35rem;">Memory Proposals</p>
|
|
<p class="is-size-7 has-text-grey" style="margin-bottom: 0.35rem;">
|
|
Candidate memory extracts grouped by theme. Click a group to reveal linked signals and citations.
|
|
</p>
|
|
{% if memory_proposal_groups %}
|
|
<div class="columns is-multiline" style="margin: 0 -0.25rem;">
|
|
{% for group in memory_proposal_groups %}
|
|
<div class="column is-12-mobile is-6-tablet" style="padding: 0.25rem;">
|
|
<div
|
|
class="js-ai-memory-group ai-linkable"
|
|
tabindex="0"
|
|
role="button"
|
|
data-memory-key="{{ group.key|default:group.title }}"
|
|
data-signal-keys="{{ group.signal_keys|join:',' }}"
|
|
title="Highlight related interaction signals and citations."
|
|
style="padding: 0.1rem 0.15rem; border-radius: 8px;">
|
|
<p class="is-size-7 has-text-weight-semibold" style="margin-bottom: 0.3rem;">
|
|
{{ group.title }}
|
|
</p>
|
|
<ul style="margin: 0 0 0.25rem 1.1rem;">
|
|
{% for proposal in group.items %}
|
|
<li class="is-size-7" style="margin-bottom: 0.22rem; white-space: pre-wrap;">
|
|
{{ proposal.content }}
|
|
</li>
|
|
{% endfor %}
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
{% else %}
|
|
{% for proposal in memory_proposals %}
|
|
<p class="is-size-7" style="margin-bottom: 0.3rem; white-space: pre-wrap;">
|
|
<strong>{{ proposal.kind|title }}</strong>: {{ proposal.content }}
|
|
</p>
|
|
{% endfor %}
|
|
{% endif %}
|
|
</section>
|
|
{% endif %}
|
|
|
|
{% if citations %}
|
|
<article
|
|
id="ai-citations-{{ person.id }}-{{ operation }}"
|
|
class="box"
|
|
style="padding: 0.55rem; margin-top: 0.55rem; border: 1px solid rgba(0, 0, 0, 0.14); box-shadow: none;">
|
|
<p class="is-size-7 has-text-weight-semibold" style="margin-bottom: 0.35rem;">Citations</p>
|
|
<p class="is-size-7 has-text-grey" style="margin-bottom: 0.35rem;">
|
|
These are source messages used as evidence for this AI result.
|
|
</p>
|
|
{% if citation_rows %}
|
|
<div class="content is-small" style="margin-bottom: 0;">
|
|
{% for row in citation_rows %}
|
|
<p class="is-size-7 js-ai-citation-row" data-citation-id="{{ row.id }}" style="margin-bottom: 0.3rem;">
|
|
<span class="tag is-light">{{ row.source_system|default:"event" }}</span>
|
|
{% if row.ts_label %}
|
|
<span class="has-text-grey">{{ row.ts_label }}</span>
|
|
{% endif %}
|
|
{% if row.text %}
|
|
<span> {{ row.text|truncatechars:140 }}</span>
|
|
{% else %}
|
|
<code>{{ row.id }}</code>
|
|
{% endif %}
|
|
</p>
|
|
{% endfor %}
|
|
</div>
|
|
{% else %}
|
|
<p class="is-size-7" style="margin: 0;">{{ citations|join:", " }}</p>
|
|
{% endif %}
|
|
</article>
|
|
{% endif %}
|
|
|
|
{% if operation == "extract_patterns" %}
|
|
<article class="box" style="padding: 0.7rem; margin-top: 0.65rem; border: 1px solid rgba(0, 0, 0, 0.14); box-shadow: none;">
|
|
<p class="is-size-7 has-text-weight-semibold" style="margin-bottom: 0.4rem;">Create Framework / Rules / Games</p>
|
|
<form
|
|
hx-headers='{"X-CSRFToken": "{{ csrf_token }}"}'
|
|
hx-post="{% url 'ai_workspace_mitigation_create' type='widget' person_id=person.id %}"
|
|
hx-target="#mitigation-shell-{{ person.id }}"
|
|
hx-swap="outerHTML"
|
|
hx-indicator="#mitigation-create-loading-{{ person.id }}"
|
|
style="margin-bottom: 0;">
|
|
<input type="hidden" name="ai_result_id" value="{{ ai_result_id|default:'' }}">
|
|
<textarea name="source_text" style="display: none;">{{ result_text }}</textarea>
|
|
|
|
<div class="columns is-multiline" style="margin: 0 -0.3rem;">
|
|
<div class="column is-12-mobile is-6-tablet" style="padding: 0.3rem;">
|
|
<label class="label is-small" style="margin-bottom: 0.25rem;">Output</label>
|
|
<div class="select is-fullwidth is-small">
|
|
<select name="output_profile" required>
|
|
<option value="" selected disabled>Choose one</option>
|
|
<option value="framework">Framework (balanced)</option>
|
|
<option value="rule">Rule (minimal + strict)</option>
|
|
<option value="game">Game (engaging)</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="column is-12-mobile is-6-tablet" style="padding: 0.3rem;">
|
|
<label class="label is-small" style="margin-bottom: 0.25rem;">Context (single freeform input)</label>
|
|
<textarea class="textarea is-small" rows="2" name="user_context" placeholder="Optional context or constraints"></textarea>
|
|
</div>
|
|
</div>
|
|
|
|
<button class="button is-small is-primary is-light">
|
|
<span class="icon is-small"><i class="fa-solid fa-chess-board"></i></span>
|
|
<span>Create Mitigation Plan</span>
|
|
</button>
|
|
<span id="mitigation-create-loading-{{ person.id }}" class="tag is-info is-light htmx-indicator" style="margin-left: 0.45rem;">
|
|
<span class="icon is-small"><i class="fa-solid fa-spinner fa-spin"></i></span>
|
|
<span>Building mitigation plan...</span>
|
|
</span>
|
|
</form>
|
|
</article>
|
|
|
|
<div class="box" style="padding: 0.65rem; margin-top: 0.65rem; border: 1px dashed rgba(0, 0, 0, 0.25); box-shadow: none;">
|
|
<p class="is-size-7 has-text-grey">
|
|
Plan editing is consolidated in the <strong>Plan</strong> tab.
|
|
</p>
|
|
<div class="buttons are-small" style="margin-top: 0.4rem; margin-bottom: 0;">
|
|
<button
|
|
type="button"
|
|
class="button is-small is-light"
|
|
onclick="giaWorkspaceOpenTab('{{ person.id }}', 'plan_board', false); return false;">
|
|
<span class="icon is-small"><i class="fa-solid fa-shield-heart"></i></span>
|
|
<span>Open Plan Tab</span>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
{% endif %}
|
|
</div>
|
|
|
|
<script>
|
|
(function () {
|
|
const root = document.getElementById(
|
|
"ai-result-links-{{ person.id }}-{{ operation }}-{{ ai_result_id|default:'new' }}"
|
|
);
|
|
if (!root || root.dataset.linkedBound === "1") {
|
|
return;
|
|
}
|
|
root.dataset.linkedBound = "1";
|
|
|
|
const signalTags = Array.from(root.querySelectorAll(".js-ai-signal-tag"));
|
|
const memoryGroups = Array.from(root.querySelectorAll(".js-ai-memory-group"));
|
|
const citationRows = Array.from(root.querySelectorAll(".js-ai-citation-row"));
|
|
const citationsBox = root.querySelector(
|
|
"#ai-citations-{{ person.id }}-{{ operation }}"
|
|
);
|
|
const memorySection = root.querySelector(
|
|
"#ai-memory-proposals-{{ person.id }}-{{ operation }}"
|
|
);
|
|
const signalsSection = root.querySelector(
|
|
"#ai-signals-{{ person.id }}-{{ operation }}"
|
|
);
|
|
|
|
const citationById = new Map();
|
|
citationRows.forEach(function (row) {
|
|
const key = String(row.dataset.citationId || "").trim();
|
|
if (key) {
|
|
citationById.set(key, row);
|
|
}
|
|
});
|
|
|
|
const normalize = function (value) {
|
|
return String(value || "")
|
|
.toLowerCase()
|
|
.replace(/[^a-z0-9]+/g, "_")
|
|
.replace(/^_+|_+$/g, "")
|
|
.replace("open_loops", "open_loop");
|
|
};
|
|
|
|
const listFromCsv = function (value) {
|
|
return String(value || "")
|
|
.split(",")
|
|
.map(function (item) {
|
|
return item.trim();
|
|
})
|
|
.filter(Boolean);
|
|
};
|
|
|
|
const flashNode = function (node) {
|
|
if (!node) {
|
|
return;
|
|
}
|
|
node.classList.remove("ai-link-flash");
|
|
void node.offsetWidth;
|
|
node.classList.add("ai-link-flash");
|
|
window.setTimeout(function () {
|
|
node.classList.remove("ai-link-flash");
|
|
}, 900);
|
|
};
|
|
|
|
const flashCitationsByIds = function (ids) {
|
|
let matched = false;
|
|
ids.forEach(function (id) {
|
|
const node = citationById.get(String(id || "").trim());
|
|
if (node) {
|
|
matched = true;
|
|
flashNode(node);
|
|
}
|
|
});
|
|
if (matched) {
|
|
flashNode(citationsBox);
|
|
} else {
|
|
flashNode(citationsBox);
|
|
}
|
|
};
|
|
|
|
const signalKeyFromNode = function (node) {
|
|
return normalize(node.dataset.signalKey || node.textContent || "");
|
|
};
|
|
|
|
const handleSignalClick = function (sourceTag) {
|
|
const key = signalKeyFromNode(sourceTag);
|
|
const ids = listFromCsv(sourceTag.dataset.citationIds || "");
|
|
|
|
signalTags.forEach(function (tag) {
|
|
if (signalKeyFromNode(tag) === key) {
|
|
flashNode(tag);
|
|
}
|
|
});
|
|
|
|
let matchedGroup = false;
|
|
memoryGroups.forEach(function (group) {
|
|
const memoryKey = normalize(group.dataset.memoryKey || "");
|
|
const signalKeys = listFromCsv(group.dataset.signalKeys || "").map(normalize);
|
|
const linked = signalKeys.includes(key) || memoryKey === key;
|
|
if (linked) {
|
|
matchedGroup = true;
|
|
flashNode(group);
|
|
}
|
|
});
|
|
if (!matchedGroup) {
|
|
flashNode(memorySection);
|
|
}
|
|
|
|
flashCitationsByIds(ids);
|
|
};
|
|
|
|
const handleMemoryClick = function (group) {
|
|
const signalKeys = listFromCsv(group.dataset.signalKeys || "").map(normalize);
|
|
const memoryKey = normalize(group.dataset.memoryKey || "");
|
|
const allKeys = Array.from(new Set(signalKeys.concat([memoryKey])));
|
|
|
|
flashNode(group);
|
|
|
|
let matchedSignal = false;
|
|
const ids = [];
|
|
signalTags.forEach(function (tag) {
|
|
const key = signalKeyFromNode(tag);
|
|
if (allKeys.includes(key)) {
|
|
matchedSignal = true;
|
|
flashNode(tag);
|
|
ids.push.apply(ids, listFromCsv(tag.dataset.citationIds || ""));
|
|
}
|
|
});
|
|
if (!matchedSignal) {
|
|
flashNode(signalsSection);
|
|
}
|
|
|
|
flashCitationsByIds(ids);
|
|
};
|
|
|
|
signalTags.forEach(function (tag) {
|
|
tag.addEventListener("click", function () {
|
|
handleSignalClick(tag);
|
|
});
|
|
});
|
|
|
|
memoryGroups.forEach(function (group) {
|
|
group.addEventListener("click", function () {
|
|
handleMemoryClick(group);
|
|
});
|
|
group.addEventListener("keydown", function (event) {
|
|
if (event.key === "Enter" || event.key === " ") {
|
|
event.preventDefault();
|
|
handleMemoryClick(group);
|
|
}
|
|
});
|
|
});
|
|
})();
|
|
</script>
|
|
|
|
<style>
|
|
.ai-mitigation-collapsible {
|
|
border: 1px solid rgba(0, 0, 0, 0.14);
|
|
border-radius: 8px;
|
|
background: rgba(255, 255, 255, 0.75);
|
|
padding: 0.3rem 0.45rem;
|
|
}
|
|
.ai-mitigation-summary {
|
|
list-style: none;
|
|
cursor: pointer;
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 0.32rem;
|
|
font-size: 0.76rem;
|
|
font-weight: 600;
|
|
color: #35475f;
|
|
}
|
|
.ai-mitigation-summary::-webkit-details-marker {
|
|
display: none;
|
|
}
|
|
.ai-mitigation-collapsible .ai-mitigation-collapsible-body {
|
|
margin-top: 0.35rem;
|
|
}
|
|
.js-ai-signal-tag {
|
|
cursor: pointer;
|
|
border: 1px solid rgba(0, 0, 0, 0.16);
|
|
background: #fff;
|
|
}
|
|
.ai-linkable {
|
|
cursor: pointer;
|
|
transition: background-color 120ms ease, box-shadow 120ms ease;
|
|
}
|
|
.ai-linkable:hover {
|
|
background: rgba(54, 114, 206, 0.06);
|
|
}
|
|
.ai-linkable:focus {
|
|
outline: none;
|
|
box-shadow: 0 0 0 2px rgba(50, 115, 220, 0.25);
|
|
}
|
|
.ai-link-flash {
|
|
animation: aiLinkFlash 0.9s ease-out;
|
|
}
|
|
@keyframes aiLinkFlash {
|
|
0% {
|
|
background-color: rgba(50, 115, 220, 0.12);
|
|
box-shadow: 0 0 0 0 rgba(50, 115, 220, 0.24);
|
|
}
|
|
40% {
|
|
background-color: rgba(50, 115, 220, 0.2);
|
|
box-shadow: 0 0 0 2px rgba(50, 115, 220, 0.2);
|
|
}
|
|
100% {
|
|
background-color: transparent;
|
|
box-shadow: 0 0 0 0 rgba(50, 115, 220, 0);
|
|
}
|
|
}
|
|
.draft-option-card.is-selected {
|
|
border-color: rgba(54, 54, 54, 0.85) !important;
|
|
border-width: 2px !important;
|
|
box-shadow: inset 0 0 0 1px rgba(54, 54, 54, 0.18);
|
|
background-color: rgba(54, 54, 54, 0.06) !important;
|
|
}
|
|
.draft-tone-line {
|
|
margin-bottom: 0.26rem;
|
|
color: #6f7680;
|
|
font-size: 0.73rem;
|
|
font-weight: 600;
|
|
letter-spacing: 0.01em;
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 0.25rem;
|
|
white-space: nowrap;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
max-width: 100%;
|
|
}
|
|
.htmx-indicator {
|
|
display: none;
|
|
}
|
|
.htmx-request.htmx-indicator {
|
|
display: inline-flex;
|
|
}
|
|
</style>
|