mirror of
https://codeberg.org/catask-org/catask.git
synced 2025-04-20 13:53:42 -05:00
481 lines
22 KiB
HTML
481 lines
22 KiB
HTML
{% extends 'admin/base.html' %}
|
|
{% block _title %}{{ _('Customize') }}{% endblock %}
|
|
{% set custom_link = 'active' %}
|
|
{% block _additionalHeadItems %}
|
|
<style>
|
|
.cm-content, .cm-gutter {
|
|
min-height: 200px !important;
|
|
}
|
|
.cm-editor, .cm-scroller {
|
|
border-radius: var(--bs-border-radius);
|
|
}
|
|
.cm-editor {
|
|
font-size: 14px;
|
|
}
|
|
.ͼ1.cm-focused {
|
|
box-shadow: 0 0 0 .25rem color-mix(in srgb, var(--bs-primary) 25%, transparent);
|
|
outline: 0 !important;
|
|
}
|
|
#settings-dropdown {
|
|
min-width: 25rem;
|
|
}
|
|
@media screen and (max-width: 600px) {
|
|
#settings-dropdown {
|
|
min-width: 100vw;
|
|
}
|
|
}
|
|
</style>
|
|
{% endblock %}
|
|
{% block _content %}
|
|
<h2 id="customize" class="mb-2 fw-normal">{{ _('Customize') }}</h2>
|
|
<p class="fs-5 h3 text-body-secondary mb-3">{{ _('Customize {} to your liking').format(const.appName) }}</p>
|
|
<h3 class="fw-light">{{ _('Favicon') }}</h3>
|
|
<form hx-post="{{ url_for('api.uploadFavicon') }}" hx-target="#response-container" hx-swap="none" hx-encoding="multipart/form-data">
|
|
<p class="m-0">{{ _('Current favicon:') }} <img src="{{ url_for('static', filename='icons/favicon/apple-touch-icon.png') }}" width="32" height="32" alt="{{ cfg.instance.title }}'s icon" class="rounded"></p>
|
|
<div class="mt-2">
|
|
<label for="favicon" class="form-label">{{ _('Upload favicon') }}</label>
|
|
<input class="form-control" type="file" id="favicon" name="favicon">
|
|
</div>
|
|
<div class="mb-4">
|
|
<button type="submit" class="btn btn-primary mt-3" id="saveFavicon">
|
|
<span class="spinner-border spinner-border-sm htmx-indicator" aria-hidden="true"></span>
|
|
<span class="visually-hidden" role="status">{{ _('Loading...') }}</span>
|
|
<i class="bi bi-file-earmark-arrow-up me-1"></i> {{ _('Upload') }}
|
|
</button>
|
|
</div>
|
|
</form>
|
|
<form hx-post="{{ url_for('api.updateConfig') }}" hx-target="#response-container" hx-swap="none" hx-on::before-request="setCmTextareaValue()">
|
|
<h3 class="fw-light">{{ _('Accent color') }}</h3>
|
|
{# <h4 class="fw-light">Color</h4> #}
|
|
<div class="form-group d-flex flex-column">
|
|
<label class="form-label" for="style.accentLight">{{ _('Light theme') }}</label>
|
|
<input type="text" name="style.accentLight" id="style.accentLight" value="{{ cfg.style.accentLight }}" class="form-control" data-coloris title="{{ _('Click to open the color picker') }}">
|
|
<p class="form-text">{{ _('Default') }}: <b>#6345d9</b></p>
|
|
</div>
|
|
<div class="form-group d-flex flex-column">
|
|
<label class="form-label" for="style.accentDark">{{ _('Dark theme') }}</label>
|
|
<input type="text" name="style.accentDark" id="style.accentDark" value="{{ cfg.style.accentDark }}" class="form-control" data-coloris title="{{ _('Click to open the color picker') }}">
|
|
<p class="form-text">{{ _('Default') }}: <b>#7a63e3</b></p>
|
|
</div>
|
|
{# brain doesn't feel like implementing this rn (9/27/24)
|
|
<h4 class="fw-light mt-2">Background</h4>
|
|
<div class="form-group d-flex flex-column">
|
|
<label class="form-label" for="style.bgLight">Light theme</label>
|
|
<input type="text" name="style.bgLight" id="style.bgLight" value="{{ cfg.style.bgLight }}" class="form-control" data-coloris>
|
|
<p class="form-text">Default: <b>#ffffff</b></p>
|
|
</div>
|
|
<div class="form-group d-flex flex-column">
|
|
<label class="form-label" for="style.bgDark">Dark theme</label>
|
|
<input type="text" name="style.bgDark" id="style.bgDark" value="{{ cfg.style.bgDark }}" class="form-control" data-coloris>
|
|
<p class="form-text">Default: <b>#202020</b></p>
|
|
</div>
|
|
#}
|
|
<div class="form-check form-switch mb-2">
|
|
<input
|
|
class="form-check-input"
|
|
type="checkbox"
|
|
name="_style.tintColors"
|
|
id="_style.tintColors"
|
|
value="{{ cfg.style.tintColors }}"
|
|
role="switch"
|
|
{% if cfg.style.tintColors == true %}checked{% endif %}>
|
|
<input type="hidden" id="style.tintColors" name="style.tintColors" value="{{ cfg.style.tintColors }}">
|
|
<label for="_style.tintColors" class="form-check-label">{{ _('Tint all colors with accent color') }}</label>
|
|
</div>
|
|
<div class="form-check form-switch mb-1">
|
|
<input
|
|
class="form-check-input nav-icons-checkbox"
|
|
type="checkbox"
|
|
name="_style.navIcons"
|
|
id="_style.navIcons"
|
|
value="{{ cfg.style.navIcons }}"
|
|
role="switch"
|
|
{% if cfg.style.navIcons == true %}checked{% endif %}>
|
|
<input type="hidden" id="style.navIcons" name="style.navIcons" value="{{ cfg.style.navIcons }}">
|
|
<label for="_style.navIcons" class="form-check-label">{{ _('Include icons in nav links') }}</label>
|
|
</div>
|
|
<p class="form-text mb-2"><b>{{ _('Note') }}:</b> {{ _('on mobile screens text will always be hidden if this option is enabled') }}</p>
|
|
<div class="form-check form-switch mb-4 ms-3">
|
|
<input
|
|
class="form-check-input nav-icons-only-checkbox"
|
|
type="checkbox"
|
|
name="_style.navIconsOnly"
|
|
id="_style.navIconsOnly"
|
|
value="{{ cfg.style.navIconsOnly }}"
|
|
role="switch"
|
|
{% if cfg.style.navIconsOnly == true %}checked{% endif %}>
|
|
<input type="hidden" id="style.navIconsOnly" name="style.navIconsOnly" value="{{ cfg.style.navIconsOnly }}">
|
|
<label for="_style.navIconsOnly" class="form-check-label">{{ _('Include only icons in nav links') }}</label>
|
|
</div>
|
|
{#
|
|
<div class="form-check form-switch mb-3">
|
|
<input
|
|
class="form-check-input"
|
|
type="checkbox"
|
|
name="_style.tintBackground"
|
|
id="_style.tintBackground"
|
|
value="{{ cfg.style.tintBackground }}"
|
|
role="switch"
|
|
{% if cfg.style.tintBackground == true %}checked{% endif %}>
|
|
<input type="hidden" id="style.tintBackground" name="style.tintBackground" value="{{ cfg.style.tintBackground }}">
|
|
<label for="_style.tintBackground" class="form-check-label">Tint background with accent color</label>
|
|
</div>
|
|
#}
|
|
<h3 class="fw-light">{{ _('Question card layout') }}</h3>
|
|
<div class="btn-group mb-4 w-100" role="group" aria-label="{{ _('Navbar link style group') }}">
|
|
<input class="btn-check" type="radio" name="style.cardStyle" id="style.cardStyle.default" value="default" {% if cfg.style.cardStyle == 'default' %}checked{% endif %}>
|
|
<label class="btn btn-outline-secondary w-50" for="style.cardStyle.default">
|
|
{{ _('Default') }}
|
|
</label>
|
|
<input class="btn-check" type="radio" name="style.cardStyle" id="style.cardStyle.compact" value="compact" {% if cfg.style.cardStyle == 'compact' %}checked{% endif %}>
|
|
<label class="btn btn-outline-secondary w-50" for="style.cardStyle.compact">
|
|
{{ _('Compact') }}
|
|
</label>
|
|
</div>
|
|
<h3 class="fw-light">{{ _('Navbar link style') }}</h3>
|
|
<div class="btn-group mb-4 w-100" role="group" aria-label="{{ _('Navbar link style group') }}">
|
|
<input class="btn-check" type="radio" name="style.navStyle" id="style.navStyle.underline" value="underline" {% if cfg.style.navStyle == 'underline' %}checked{% endif %}>
|
|
<label class="btn btn-outline-secondary w-50" for="style.navStyle.underline">
|
|
{{ _('Underline') }}
|
|
</label>
|
|
<input class="btn-check" type="radio" name="style.navStyle" id="style.navStyle.pills" value="pills" {% if cfg.style.navStyle == 'pills' %}checked{% endif %}>
|
|
<label class="btn btn-outline-secondary w-50" for="style.navStyle.pills">
|
|
{{ _('Pills') }}
|
|
</label>
|
|
</div>
|
|
<h3 class="fw-light">{{ _('Homepage layout') }}</h3>
|
|
<div class="btn-group mb-4 w-100" role="group" aria-label="{{ _('Homepage layout group') }}">
|
|
<input class="btn-check" type="radio" name="style.homepageLayout" id="style.homepageLayout.catask" value="catask" {% if cfg.style.homepageLayout == 'catask' %}checked{% endif %}>
|
|
<label class="btn btn-outline-secondary w-50" for="style.homepageLayout.catask">
|
|
CatAsk
|
|
</label>
|
|
<input class="btn-check" type="radio" name="style.homepageLayout" id="style.homepageLayout.retrospring" value="retrospring" {% if cfg.style.homepageLayout == 'retrospring' %}checked{% endif %}>
|
|
<label class="btn btn-outline-secondary w-50" for="style.homepageLayout.retrospring">
|
|
Retrospring
|
|
</label>
|
|
</div>
|
|
<h3 class="fw-light">{{ _('Info box layout') }}</h3>
|
|
<div class="btn-group mb-4 w-100" role="group" aria-label="Info box layout group">
|
|
<input class="btn-check" type="radio" name="style.infoBoxLayout" id="style.infoBoxLayout.column" value="column" {% if cfg.style.infoBoxLayout == 'column' %}checked{% endif %}>
|
|
<label class="btn btn-outline-secondary w-50" for="style.infoBoxLayout.column">
|
|
{{ _('Column') }}
|
|
</label>
|
|
<input class="btn-check" type="radio" name="style.infoBoxLayout" id="style.infoBoxLayout.row" value="row" {% if cfg.style.infoBoxLayout == 'row' %}checked{% endif %}>
|
|
<label class="btn btn-outline-secondary w-50" for="style.infoBoxLayout.row">
|
|
{{ _('Row') }}
|
|
</label>
|
|
</div>
|
|
<h3 class="fw-light">{{ _('Trimmed content') }}</h3>
|
|
<div class="form-group mb-3">
|
|
<label class="form-label" for="trimContentAfter">{{ _('Trim content after (characters)') }}</label>
|
|
<input type="number" id="trimContentAfter" name="trimContentAfter" value="{{ cfg.trimContentAfter }}" class="form-control">
|
|
<p class="form-text">{{ _('Maximum length of content before it gets trimmed (used in sharing options); set to 0 to disable') }}</p>
|
|
</div>
|
|
|
|
<h3 class="fw-light">{{ _('Advanced') }}</h3>
|
|
<h4 class="fw-light">{{ _('Custom CSS') }}</h4>
|
|
<button class="btn btn-secondary mb-3" id="theme-store-open-btn" type="button" data-bs-toggle="modal" data-bs-target="#theme-store-modal"><i class="bi bi-palette me-1"></i> {{ _("Open Theme Store") }}</button>
|
|
<div id="theme-store-modals" hx-get="{{ url_for('api.themeStore_themeModals') }}" data-dontshowtoast hx-target="this" hx-swap="innerHTML" hx-trigger="click from:#theme-store-open-btn">
|
|
<!-- htmx will replace this message with theme modals -->
|
|
</div>
|
|
<div class="modal fade" id="theme-store-modal" tabindex="-1" aria-labelledby="ts-modal-label" aria-hidden="true">
|
|
<div class="modal-dialog modal-dialog-scrollable modal-fullscreen-md-down modal-xl modal-dialog-centered">
|
|
<div class="modal-content">
|
|
<div class="modal-header justify-content-between border-0">
|
|
<h1 class="d-flex align-items-center gap-2 modal-title fs-5 fw-normal" id="q-modal-label"><img src="{{ url_for('static', filename='icons/catask-theme-store.svg') }}" width="32" height="32" alt="CatAsk Theme Store icon"> {{ _('Theme Store') }}</h1>
|
|
<div class="d-flex align-items-center">
|
|
<div class="dropdown me-2">
|
|
<button class="btn btn-basic px-3 btn-sm fs-5 dropdown-toggle no-arrow" type="button" data-bs-toggle="dropdown" data-bs-auto-close="false" aria-expanded="false">
|
|
<i class="bi bi-gear"></i>
|
|
<span class="visually-hidden">{{ _('Settings') }}</span>
|
|
</button>
|
|
<div class="dropdown-menu dropdown-menu-end p-3" id="settings-dropdown">
|
|
<h5 class="fw-light mb-3">{{ _('Settings') }}</h5>
|
|
<label for="themeStoreUrl" class="form-label">{{ _('Theme Store URL') }}</label>
|
|
<div class="input-group mb-3">
|
|
<input type="text" id="themeStoreUrl" name="themeStoreUrl" class="form-control" placeholder="{{ cfg.themeStoreUrl }}" value="{{ cfg.themeStoreUrl }}" aria-label="{{ _('Theme Store URL') }}">
|
|
<button type="button" class="btn btn-primary" hx-post="{{ url_for('api.updateConfig') }}" hx-target="#response-container" hx-swap="none" hx-on::before-request="setCmTextareaValue()" hx-indicator="#spinner-2">
|
|
<span class="spinner-border spinner-border-sm htmx-indicator" id="spinner-2" aria-hidden="true"></span>
|
|
<span class="visually-hidden" role="status">{{ _('Loading...') }}</span>
|
|
{{ _('Save') }}
|
|
</button>
|
|
</div>
|
|
<p class="mb-0">{{ _('Refresh the page to load themes from new instance') }}</p>
|
|
</div>
|
|
</div>
|
|
<button type="button" class="btn-close d-flex align-items-center fs-5" data-bs-dismiss="modal" aria-label="Close"><i class="bi bi-x-lg"></i></button>
|
|
</div>
|
|
</div>
|
|
<div class="modal-body py-0">
|
|
<div hx-get="{{ url_for('api.themeStore_themes') }}" hx-trigger="intersect once" hx-indicator="#ts-indicator" data-dontshowtoast hx-target="this" hx-swap="innerHTML" id="modal_loader">
|
|
<div class="d-flex justify-content-center my-5" id="ts-indicator">
|
|
<div class="spinner-border" role="status">
|
|
<span class="visually-hidden">{{ _('Loading...') }}</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<!-- <div class="modal-footer pt-1 flex-row align-items-stretch w-100 border-0">
|
|
<button type="button" class="btn btn-outline-secondary flex-fill flex-lg-grow-0" data-bs-dismiss="modal">Close</button>
|
|
</div>-->
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="form-check form-switch mb-2">
|
|
<input
|
|
class="form-check-input"
|
|
type="checkbox"
|
|
name="_style.useCustomCss"
|
|
id="_style.useCustomCss"
|
|
value="{{ cfg.style.useCustomCss }}"
|
|
role="switch"
|
|
{% if cfg.style.useCustomCss %}checked{% endif %}>
|
|
<input type="hidden" id="style.useCustomCss" name="style.useCustomCss" value="{{ cfg.style.useCustomCss }}">
|
|
<label for="_style.useCustomCss" class="form-check-label">{{ _("Use custom CSS") }}</label>
|
|
</div>
|
|
<div class="form-check form-check-inline mb-2">
|
|
<input
|
|
class="form-check-input"
|
|
type="checkbox"
|
|
name="_style.overrideBaseStyles"
|
|
id="_style.overrideBaseStyles"
|
|
value="{{ cfg.style.overrideBaseStyles }}"
|
|
role="switch"
|
|
{% if cfg.style.overrideBaseStyles %}checked{% endif %}>
|
|
<input type="hidden" id="style.overrideBaseStyles" name="style.overrideBaseStyles" value="{{ cfg.style.overrideBaseStyles }}">
|
|
<label for="_style.overrideBaseStyles" class="form-check-label">{{ _("Override base styles") }}</label>
|
|
</div>
|
|
<div class="form-check form-check-inline mb-2">
|
|
<input
|
|
class="form-check-input"
|
|
type="checkbox"
|
|
name="_style.overrideCatAskStyles"
|
|
id="_style.overrideCatAskStyles"
|
|
value="{{ cfg.style.overrideCatAskStyles }}"
|
|
role="switch"
|
|
{% if cfg.style.overrideCatAskStyles %}checked{% endif %}>
|
|
<input type="hidden" id="style.overrideCatAskStyles" name="style.overrideCatAskStyles" value="{{ cfg.style.overrideCatAskStyles }}">
|
|
<label for="_style.overrideCatAskStyles" class="form-check-label">{{ _("Override {} styles").format(const.appName) }}</label>
|
|
</div>
|
|
<div class="d-flex flex-column flex-sm-row gap-2 my-2 justify-content-sm-between">
|
|
<div>
|
|
<button class="btn btn-sm btn-secondary square-btn" type="button" onclick="clearCmText(true)"><i class="bi bi-eraser me-1"></i> <span>{{ _('Clear') }}</span></button>
|
|
</div>
|
|
<div>
|
|
<div class="btn-group" role="group">
|
|
<label id="import" for="css-file" class="btn btn-sm btn-secondary"><i class="bi bi-file-earmark-arrow-up me-1"></i> {{ _('Import...') }}</label>
|
|
<label id="append" for="css-file" class="btn btn-sm btn-secondary"><i class="bi bi-file-earmark-arrow-up me-1"></i> {{ _('Append...') }}</label>
|
|
</div>
|
|
<input class="d-none" type="file" id="css-file" name="css-file" accept=".css,text/css">
|
|
<button class="btn btn-sm btn-secondary" type="button" data-bs-toggle="modal" data-bs-target="#theme-export-modal"><i class="bi bi-filetype-css me-1"></i> {{ _('Export...') }}</button>
|
|
</div>
|
|
</div>
|
|
<div class="modal fade" id="theme-export-modal" tabindex="-1" aria-hidden="true">
|
|
<div class="modal-dialog modal-dialog-centered">
|
|
<div class="modal-content">
|
|
<div class="modal-header justify-content-between border-0">
|
|
<h1 class="modal-title fs-5 fw-normal" id="q-modal-label">{{ _('Export CSS') }}</h1>
|
|
<button type="button" class="btn-close d-flex align-items-center fs-5" data-bs-dismiss="modal" aria-label="Close"><i class="bi bi-x-lg"></i></button>
|
|
</div>
|
|
<div class="modal-body py-0">
|
|
<div class="mb-3">
|
|
<label for="cssFilename" class="form-label">{{ _('Filename') }}</label>
|
|
<div class="input-group">
|
|
<input type="text" id="cssFilename" class="form-control" placeholder="catask_theme" aria-label="Filename">
|
|
<span class="input-group-text">.css</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="modal-footer pt-1 flex-row align-items-stretch w-100 border-0">
|
|
<button type="button" class="btn btn-primary" onclick="exportCSS()">
|
|
<i class="bi bi-download me-1"></i> {{ _('Export') }}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<!-- <p class="form-text mb-1">Use <code>Escape</code> and then <code>Tab</code> or <code>Shift + Tab</code> to move out of the editor</p> -->
|
|
<div id="codemirror-editor"></div>
|
|
<textarea class="d-none" id="style.customCss" name="style.customCss">{{ cfg.style.customCss | safe }}</textarea>
|
|
{% include 'snippets/admin/saveBtn.html' %}
|
|
</form>
|
|
{% endblock %}
|
|
{% block _scripts %}
|
|
<script src="{{ url_for('static', filename='js/codemirror-css.min.js') }}"></script>
|
|
<script>
|
|
let cmTextarea = document.getElementById("style.customCss");
|
|
|
|
function exportCSS() {
|
|
const cssContent = cmTextarea.value;
|
|
const filenameInput = document.getElementById("cssFilename").value.trim();
|
|
const filename = filenameInput ? filenameInput + ".css" : "catask_theme.css";
|
|
|
|
const blob = new Blob([cssContent], { type: "text/css" });
|
|
const a = document.createElement("a");
|
|
a.href = URL.createObjectURL(blob);
|
|
a.download = filename;
|
|
a.class = "d-none";
|
|
document.body.appendChild(a);
|
|
a.click();
|
|
document.body.removeChild(a);
|
|
}
|
|
|
|
document.body.addEventListener("htmx:beforeRequest", function(evt) {
|
|
console.log("HTMX is making a request to:", evt.detail.pathInfo.finalRequestPath);
|
|
});
|
|
|
|
document.body.addEventListener("htmx:afterSwap", function(evt) {
|
|
console.log("HTMX swap completed on:", evt.detail.target);
|
|
});
|
|
|
|
function clearCmText(toast = false) {
|
|
cmTextarea.value = "";
|
|
view.update([view.state.update({changes: {from: 0, to: view.state.doc.length, insert: ""}})]);
|
|
if (toast) {
|
|
showToast("{{ _('Cleared') }}", "success", 'bottom');
|
|
}
|
|
}
|
|
|
|
const fileSelector = document.getElementById('css-file');
|
|
const importBtn = document.getElementById('import');
|
|
const appendBtn = document.getElementById('append');
|
|
|
|
let replaceCmValue = true;
|
|
|
|
importBtn.addEventListener('click', () => {
|
|
replaceCmValue = true;
|
|
});
|
|
|
|
appendBtn.addEventListener('click', () => {
|
|
replaceCmValue = false;
|
|
});
|
|
|
|
fileSelector.addEventListener('change', (event) => readFile(event, replaceCmValue));
|
|
|
|
function readFile(event, replaceCmValue) {
|
|
const file = event.target.files[0];
|
|
|
|
if (!file || file.type !== "text/css") {
|
|
showToast("{{ _('Invalid file type. Only .css files are allowed.') }}", "danger");
|
|
event.target.value = "";
|
|
return;
|
|
}
|
|
|
|
const reader = new FileReader();
|
|
|
|
reader.onload = () => {
|
|
const newContent = reader.result;
|
|
if (replaceCmValue) {
|
|
view.update([view.state.update({changes: {from: 0, to: view.state.doc.length, insert: newContent}})]);
|
|
} else {
|
|
view.update([view.state.update({changes: {from: view.state.doc.length, to: view.state.doc.length, insert: "\n" + newContent}})]);
|
|
}
|
|
};
|
|
|
|
reader.onerror = () => {
|
|
console.error("Error reading the file. Please try again.");
|
|
};
|
|
|
|
reader.readAsText(file);
|
|
saveConfig();
|
|
}
|
|
|
|
function useTheme(theme_id, theme_name) {
|
|
cmTextarea.value = document.getElementById(`theme-source-code-${theme_id}`).innerHTML;
|
|
// codemirror 6 at its finest
|
|
view.update([view.state.update({changes: {from: 0, to: view.state.doc.length, insert: document.getElementById(`theme-source-code-${theme_id}`).innerHTML}})]);
|
|
|
|
Toastify({
|
|
text: `{{ _("Theme") }} "${theme_name}" {{ _("applied! Enable the \"Use custom CSS\" checkbox if it's off and reload the page to see it.") }}`,
|
|
duration: 3000,
|
|
gravity: "top",
|
|
position: "right",
|
|
stopOnFocus: true,
|
|
className: `alert alert-success shadow alert-dismissible`,
|
|
close: true
|
|
}).showToast();
|
|
saveConfig();
|
|
}
|
|
|
|
const initialState = cm6.createEditorState(`{{ cfg.style.customCss | safe }}`, cmTextarea);
|
|
const view = cm6.createEditorView(initialState, document.getElementById("codemirror-editor"));
|
|
|
|
async function setCmTextareaValue() {
|
|
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
setCmTextareaValue2();
|
|
}
|
|
function setCmTextareaValue2() {
|
|
console.log(`cm value: ${view.state.doc.toString()}`);
|
|
cmTextarea.value = view.state.doc.toString();
|
|
}
|
|
function copyFull(text) {
|
|
navigator.clipboard.writeText(text);
|
|
Toastify({
|
|
text: "{{ _('Successfully copied text to clipboard!') }}",
|
|
duration: 3000,
|
|
gravity: "top",
|
|
position: "right",
|
|
stopOnFocus: true,
|
|
className: `alert alert-success shadow alert-dismissible`,
|
|
close: true
|
|
}).showToast();
|
|
};
|
|
</script>
|
|
<script>
|
|
document.addEventListener("DOMContentLoaded", function () {
|
|
|
|
// fix handling checkboxes
|
|
document.querySelectorAll('.form-check-input[type=checkbox]').forEach(function(checkbox) {
|
|
checkbox.addEventListener('change', function() {
|
|
checkbox.nextElementSibling.value = this.checked ? 'True' : 'False';
|
|
});
|
|
});
|
|
// Get the first checkbox and the second checkbox
|
|
const navIconsCheckbox = document.querySelector(".nav-icons-checkbox");
|
|
const navIconsOnlyCheckbox = document.querySelector(".nav-icons-only-checkbox");
|
|
|
|
// Define a function to update the second checkbox
|
|
function updateNavIconsOnlyCheckbox() {
|
|
if (!navIconsCheckbox.checked) {
|
|
// Disable the second checkbox and uncheck it
|
|
navIconsOnlyCheckbox.checked = false;
|
|
navIconsOnlyCheckbox.disabled = true;
|
|
|
|
// Update the hidden input for the second checkbox
|
|
navIconsOnlyCheckbox.nextElementSibling.value = "False";
|
|
} else {
|
|
// Enable the second checkbox
|
|
navIconsOnlyCheckbox.disabled = false;
|
|
}
|
|
}
|
|
|
|
// Perform the check on page load
|
|
updateNavIconsOnlyCheckbox();
|
|
|
|
// Add an event listener to the first checkbox to handle changes
|
|
navIconsCheckbox.addEventListener("change", updateNavIconsOnlyCheckbox);
|
|
});
|
|
</script>
|
|
|
|
<script src="{{ url_for('static', filename='js/coloris.min.js') }}"></script>
|
|
<script>
|
|
Coloris({
|
|
theme: 'square',
|
|
themeMode: 'auto',
|
|
formatToggle: true,
|
|
alpha: false,
|
|
swatches: [
|
|
'#c70f0f', // Red
|
|
'#db5d0e', // Orange
|
|
'#968829', // Yellow
|
|
'#217d1a', // Green
|
|
'#28b59b', // Turquoise
|
|
'#338FFF', // Blue
|
|
'#3358ff',
|
|
'#7a63e3', // Indigo
|
|
'#A833FF', // Purple
|
|
'#d1298b' // Pink
|
|
],
|
|
});
|
|
</script>
|
|
{% endblock %}
|