Merge branch 'main' into sassy-comments

This commit is contained in:
Matthias Metelka 2022-10-20 23:38:31 +02:00
commit 876f1670cc
30 changed files with 327 additions and 134 deletions

1
Cargo.lock generated
View file

@ -118,6 +118,7 @@ dependencies = [
"unicase",
"unicode-normalization",
"utime",
"which",
"zip",
"zstd",
]

View file

@ -579,6 +579,15 @@ alias(
],
)
alias(
name = "which",
actual = "@raze__which__4_3_0//:which",
tags = [
"cargo-raze",
"manual",
],
)
alias(
name = "zip",
actual = "@raze__zip__0_6_2//:zip",

View file

@ -60,7 +60,7 @@ editing-collapse-field = Collapse field
editing-underline-text = Underline text
editing-unordered-list = Unordered list
editing-warning-cloze-deletions-will-not-work = Warning, cloze deletions will not work until you switch the type at the top to Cloze.
editing-toggle-mathjax-rendering = Toggle MathJax Rendering
editing-mathjax-preview = MathJax Preview
editing-shrink-images = Shrink Images
editing-close-html-tags = Auto-close HTML tags

View file

@ -579,6 +579,15 @@ alias(
],
)
alias(
name = "which",
actual = "@raze__which__4_3_0//:which",
tags = [
"cargo-raze",
"manual",
],
)
alias(
name = "zip",
actual = "@raze__zip__0_6_2//:zip",

View file

@ -345,26 +345,19 @@ class AnkiApp(QApplication):
##################################################
def eventFilter(self, src: Any, evt: QEvent) -> bool:
if evt.type() == QEvent.Type.HoverEnter:
if (
(
isinstance(
src,
(
QPushButton,
QCheckBox,
QRadioButton,
# classes with PyQt5 compatibility proxy
without_qt5_compat_wrapper(QToolButton),
without_qt5_compat_wrapper(QTabBar),
),
)
)
and src.isEnabled()
or (
isinstance(src, without_qt5_compat_wrapper(QComboBox))
and not src.isEditable()
)
pointer_classes = (
QPushButton,
QCheckBox,
QRadioButton,
QMenu,
# classes with PyQt5 compatibility proxy
without_qt5_compat_wrapper(QToolButton),
without_qt5_compat_wrapper(QTabBar),
)
if evt.type() in [QEvent.Type.Enter, QEvent.Type.HoverEnter]:
if (isinstance(src, pointer_classes) and src.isEnabled()) or (
isinstance(src, without_qt5_compat_wrapper(QComboBox))
and not src.isEditable()
):
self.setOverrideCursor(QCursor(Qt.CursorShape.PointingHandCursor))
else:

View file

@ -226,6 +226,7 @@ def show(mw: aqt.AnkiQt) -> QDialog:
"Nicholas Flint",
"Daniel Vieira Memoria10X",
"Luka Warren",
"Christos Longros",
)
)

View file

@ -126,19 +126,22 @@ class Browser(QMainWindow):
self._closeEventHasCleanedUp = False
self.form = aqt.forms.browser.Ui_Dialog()
self.form.setupUi(self)
restoreGeom(self, "editor", 0)
restoreSplitter(self.form.splitter, "editor3")
self.form.splitter.setChildrenCollapsible(False)
# set if exactly 1 row is selected; used by the previewer
self.card: Card | None = None
self.current_card: Card | None = None
self.setupSidebar()
# make sure to call restoreState() after QDockWidget is attached to QMainWindow
restoreState(self, "editor")
self.setup_table()
self.setupMenus()
self.setupHooks()
self.setupEditor()
# restoreXXX() should be called after all child widgets have been created
# and attached to QMainWindow
restoreGeom(self, "editor", 0)
restoreSplitter(self.form.splitter, "editor3")
restoreState(self, "editor")
# responsive layout
self.aspect_ratio = self.width() / self.height()
self.set_layout(self.mw.pm.browser_layout(), True)

View file

@ -28,7 +28,8 @@ qlineargradient(
def general_styles(tm: ThemeManager) -> str:
return f"""
QFrame {{
QFrame,
QWidget {{
background: none;
}}
QPushButton,
@ -38,7 +39,7 @@ QLineEdit,
QListWidget,
QTreeWidget,
QListView {{
border: 1px solid {tm.var(colors.BORDER)};
border: 1px solid {tm.var(colors.BORDER_SUBTLE)};
border-radius: {tm.var(props.BORDER_RADIUS)};
}}
QLineEdit {{
@ -58,6 +59,47 @@ QSpinBox {{
"""
def menu_styles(tm: ThemeManager) -> str:
return f"""
QMenuBar {{
border-bottom: 1px solid {tm.var(colors.BORDER_FAINT)};
}}
QMenuBar::item {{
background-color: transparent;
padding: 2px 4px;
border-radius: {tm.var(props.BORDER_RADIUS)};
}}
QMenuBar::item:selected {{
background-color: {tm.var(colors.CANVAS_ELEVATED)};
}}
QMenu {{
background-color: {tm.var(colors.CANVAS_OVERLAY)};
border: 1px solid {tm.var(colors.BORDER_SUBTLE)};
padding: 4px;
}}
QMenu::item {{
background-color: transparent;
padding: 3px 14px;
margin-bottom: 4px;
}}
QMenu::item:selected {{
background-color: {tm.var(colors.CANVAS_INSET)};
color: {tm.var(colors.HIGHLIGHT_FG)};
border-radius: {tm.var(props.BORDER_RADIUS)};
}}
QMenu::separator {{
height: 1px;
background: {tm.var(colors.BORDER_SUBTLE)};
margin: 0 8px 4px 8px;
}}
QMenu::indicator {{
border: 1px solid {tm.var(colors.BORDER)};
margin-left: 6px;
margin-right: -6px;
}}
"""
def button_styles(tm: ThemeManager) -> str:
return f"""
QPushButton {{
@ -184,7 +226,7 @@ QTabWidget::pane {{
top: -15px;
padding-top: 1em;
background: {tm.var(colors.CANVAS_ELEVATED)};
border: 1px solid {tm.var(colors.BORDER)};
border: 1px solid {tm.var(colors.BORDER_SUBTLE)};
border-radius: {tm.var(props.BORDER_RADIUS)};
}}
QTabWidget::tab-bar {{
@ -196,7 +238,7 @@ QTabBar::tab {{
min-width: 8ex;
}}
QTabBar::tab {{
border: 1px solid {tm.var(colors.BORDER)};
border: 1px solid {tm.var(colors.BORDER_SUBTLE)};
}}
QTabBar::tab:first {{
border-top-{tm.left()}-radius: {tm.var(props.BORDER_RADIUS)};
@ -225,7 +267,7 @@ def table_styles(tm: ThemeManager) -> str:
return f"""
QTableView {{
border-radius: {tm.var(props.BORDER_RADIUS)};
gridline-color: {tm.var(colors.BORDER)};
gridline-color: {tm.var(colors.BORDER_SUBTLE)};
selection-background-color: {tm.var(colors.SELECTED_BG)};
selection-color: {tm.var(colors.SELECTED_FG)};
}}
@ -233,7 +275,7 @@ QHeaderView {{
background: {tm.var(colors.CANVAS)};
}}
QHeaderView::section {{
border: 1px solid {tm.var(colors.BORDER)};
border: 1px solid {tm.var(colors.BORDER_SUBTLE)};
background: {
button_gradient(
tm.var(colors.BUTTON_GRADIENT_START),
@ -261,19 +303,19 @@ QHeaderView::section:hover {{
};
}}
QHeaderView::section:first {{
border-left: 1px solid {tm.var(colors.BORDER)};
border-left: 1px solid {tm.var(colors.BORDER_SUBTLE)};
border-top-left-radius: {tm.var(props.BORDER_RADIUS)};
}}
QHeaderView::section:!first {{
border-left: none;
}}
QHeaderView::section:last {{
border-right: 1px solid {tm.var(colors.BORDER)};
border-right: 1px solid {tm.var(colors.BORDER_SUBTLE)};
border-top-right-radius: {tm.var(props.BORDER_RADIUS)};
}}
QHeaderView::section:only-one {{
border-left: 1px solid {tm.var(colors.BORDER)};
border-right: 1px solid {tm.var(colors.BORDER)};
border-left: 1px solid {tm.var(colors.BORDER_SUBTLE)};
border-right: 1px solid {tm.var(colors.BORDER_SUBTLE)};
border-top-left-radius: {tm.var(props.BORDER_RADIUS)};
border-top-right-radius: {tm.var(props.BORDER_RADIUS)};
}}
@ -374,16 +416,16 @@ QRadioButton {{
margin: 2px 0;
}}
QCheckBox::indicator,
QRadioButton::indicator {{
QRadioButton::indicator,
QMenu::indicator {{
border: 1px solid {tm.var(colors.BORDER_SUBTLE)};
border-radius: {tm.var(props.BORDER_RADIUS)};
background: {tm.var(colors.CANVAS_INSET)};
width: 16px;
height: 16px;
}}
QCheckBox::indicator {{
border-radius: {tm.var(props.BORDER_RADIUS)};
}}
QRadioButton::indicator {{
QRadioButton::indicator,
QMenu::indicator:exclusive {{
border-radius: 8px;
}}
QCheckBox::indicator:hover,
@ -395,7 +437,8 @@ QRadioButton::indicator:checked:hover {{
height: 14px;
}}
QCheckBox::indicator:checked,
QRadioButton::indicator:checked {{
QRadioButton::indicator:checked,
QMenu::indicator:checked {{
image: url({tm.themed_icon("mdi:check")});
}}
QCheckBox::indicator:indeterminate {{
@ -445,20 +488,3 @@ QScrollBar::sub-line {{
background: none;
}}
"""
def win10_styles(tm: ThemeManager) -> str:
return f"""
/* day mode is missing a bottom border; background must be
also set for border to apply */
QMenuBar {{
border-bottom: 1px solid {tm.var(colors.BORDER)};
background: {tm.var(colors.CANVAS) if tm.night_mode else "white"};
}}
/* qt bug? setting the above changes the browser sidebar
to white as well, so set it back */
QTreeWidget {{
background: {tm.var(colors.CANVAS)};
}}
"""

View file

@ -5,7 +5,6 @@ from __future__ import annotations
import enum
import os
import platform
import re
import subprocess
from dataclasses import dataclass
@ -227,29 +226,27 @@ class ThemeManager:
checkbox_styles,
combobox_styles,
general_styles,
menu_styles,
scrollbar_styles,
spinbox_styles,
table_styles,
tabwidget_styles,
win10_styles,
)
buf += "".join(
[
general_styles(self),
button_styles(self),
checkbox_styles(self),
menu_styles(self),
combobox_styles(self),
tabwidget_styles(self),
table_styles(self),
spinbox_styles(self),
checkbox_styles(self),
scrollbar_styles(self),
]
)
if is_win and platform.release() == "10":
buf += win10_styles(self)
# allow addons to modify the styling
buf = gui_hooks.style_did_init(buf)

View file

@ -115,12 +115,12 @@ def register_repos():
################
core_i18n_repo = "anki-core-i18n"
core_i18n_commit = "71bfeda9ef7667f3454e7341969835168768bd54"
core_i18n_zip_csum = "4b4203d862aa1d90c0c391fac00bdf8ba0a8bffe00e4ba5fd36b2c7b288613c9"
core_i18n_commit = "b9d5c896f22fe6e79810194f41222d8638c13e16"
core_i18n_zip_csum = "8ef7888373cacf682c17f41056dc1f5348f60a15e1809c8db0f66f4072e7d5fb"
qtftl_i18n_repo = "anki-desktop-ftl"
qtftl_i18n_commit = "aa0e656fa4b0b9c926fc7436cb62418d8995e666"
qtftl_i18n_zip_csum = "5949b0e19b92f8699e9f1a3ca17abf9249103232aa408b4bb21bb6c5d1ff28bc"
qtftl_i18n_commit = "a8bd0e284e2785421180af2ce10dd1d534b0033d"
qtftl_i18n_zip_csum = "f88398324a64be99521bd5cd7e79e7dda64c31a2cd4e568328a211c7765b23ac"
i18n_build_content = """
filegroup(

View file

@ -31,6 +31,7 @@ cargo_build_script(
],
deps = [
"//rslib/cargo:prost_build",
"//rslib/cargo:which",
],
)

View file

@ -23,6 +23,7 @@ required-features = ["bench"]
[build-dependencies]
prost-build = "0.11.1"
which = "4.3.0"
[dev-dependencies]
env_logger = "0.9.1"

View file

@ -1,7 +1,11 @@
// Copyright: Ankitects Pty Ltd and contributors
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
use std::{env, fmt::Write, path::PathBuf};
use std::{
env,
fmt::Write,
path::{Path, PathBuf},
};
struct CustomGenerator {}
@ -71,6 +75,7 @@ fn service_generator() -> Box<dyn prost_build::ServiceGenerator> {
}
pub fn write_backend_proto_rs() {
maybe_add_protobuf_to_path();
let proto_dir = if let Ok(proto) = env::var("PROTO_TOP") {
PathBuf::from(proto).parent().unwrap().to_owned()
} else {
@ -122,3 +127,27 @@ pub fn write_backend_proto_rs() {
.compile_protos(paths.as_slice(), &[proto_dir])
.unwrap();
}
/// If PROTOC is not defined, and protoc is not on path, use the protoc
/// fetched by Bazel so that Rust Analyzer does not fail.
fn maybe_add_protobuf_to_path() {
if std::env::var("PROTOC").is_ok() {
return;
}
if which::which("protoc").is_ok() {
return;
}
let path = if cfg!(target_os = "windows") {
let base = std::fs::read_link("../.bazel/out").unwrap();
base.join("../external/protoc_bin_windows/bin/protoc.exe")
} else {
let base = Path::new("../.bazel/out/../external");
let subpath = if cfg!(target_os = "macos") {
"protoc_bin_macos/bin/protoc"
} else {
"protoc_bin_linux_x86_64/bin/protoc"
};
base.join(subpath)
};
std::env::set_var("PROTOC", path.to_str().unwrap());
}

View file

@ -579,6 +579,15 @@ alias(
],
)
alias(
name = "which",
actual = "@raze__which__4_3_0//:which",
tags = [
"cargo-raze",
"manual",
],
)
alias(
name = "zip",
actual = "@raze__zip__0_6_2//:zip",

View file

@ -579,6 +579,15 @@ alias(
],
)
alias(
name = "which",
actual = "@raze__which__4_3_0//:which",
tags = [
"cargo-raze",
"manual",
],
)
alias(
name = "zip",
actual = "@raze__zip__0_6_2//:zip",

View file

@ -579,6 +579,15 @@ alias(
],
)
alias(
name = "which",
actual = "@raze__which__4_3_0//:which",
tags = [
"cargo-raze",
"manual",
],
)
alias(
name = "zip",
actual = "@raze__zip__0_6_2//:zip",

View file

@ -579,6 +579,15 @@ alias(
],
)
alias(
name = "which",
actual = "@raze__which__4_3_0//:which",
tags = [
"cargo-raze",
"manual",
],
)
alias(
name = "zip",
actual = "@raze__zip__0_6_2//:zip",

View file

@ -89,11 +89,14 @@ $vars: (
),
),
overlay: (
"Background of floating elements (menus, tooltips)",
<<<<<<< HEAD "Background of floating elements (menus, tooltips)",
(
light: white,
dark: black,
),
======= light: palette(lightgray, 0),
dark: palette(darkgray, 5),
>>>>>>> main,
),
code: (
"Background of code editors",
@ -105,7 +108,8 @@ $vars: (
),
border: (
default: (
"Border color with medium contrast against window background",
<<<<<<< HEAD
"Border color with medium contrast against window background",
(
light: palette(lightgray, 6),
dark: palette(darkgray, 7),
@ -117,6 +121,9 @@ $vars: (
light: palette(lightgray, 5),
dark: palette(darkgray, 6),
),
======= light: palette(lightgray, 6),
dark: palette(darkgray, 4),
>>>>>>> main,
),
strong: (
"Border color with high contrast against window background",
@ -125,6 +132,14 @@ $vars: (
dark: palette(darkgray, 1),
),
),
subtle: (
light: palette(lightgray, 5),
dark: palette(darkgray, 7),
),
faint: (
light: palette(lightgray, 4),
dark: palette(darkgray, 6),
),
focus: (
"Border color of focused input elements",
(
@ -402,8 +417,8 @@ $vars: (
bg: (
"Background color of highlighted items",
(
light: palette(blue, 3),
dark: palette(blue, 4),
light: color.scale(palette(blue, 3), $alpha: -33%),
dark: color.scale(palette(blue, 4), $alpha: -33%),
),
),
fg: (

View file

@ -3,6 +3,9 @@ Copyright: Ankitects Pty Ltd and contributors
License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
-->
<script lang="ts">
import { createEventDispatcher } from "svelte";
import { fly } from "svelte/transition";
import { on } from "../lib/events";
import { Callback, singleCallback } from "../lib/typing";
import IconConstrain from "./IconConstrain.svelte";
@ -12,8 +15,13 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
export let panes: ResizablePane[];
export let index = 0;
export let tip = "";
export let showIndicator = false;
export let clientHeight: number;
const rtl = window.getComputedStyle(document.body).direction == "rtl";
const dispatch = createEventDispatcher();
let destroy: Callback;
let before: ResizablePane;
@ -77,18 +85,31 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
destroy = singleCallback(
on(window, "pointermove", onMove),
on(window, "pointerup", releasePointer),
on(window, "pointerup", () => {
releasePointer.call(window);
dispatch("release");
}),
);
}
</script>
<div
class="horizontal-resizer"
class:rtl
title={tip}
bind:clientHeight={resizerHeight}
on:pointerdown|preventDefault={lockPointer}
on:dblclick
on:dblclick|preventDefault
>
{#if showIndicator}
<div
class="resize-indicator"
transition:fly={{ x: rtl ? 25 : -25, duration: 200 }}
>
<slot />
</div>
{/if}
<div class="drag-handle">
<IconConstrain iconSize={80}>{@html horizontalHandle}</IconConstrain>
</div>
@ -99,7 +120,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
width: 100%;
cursor: row-resize;
position: relative;
height: 10px;
height: 25px;
border-top: 1px solid var(--border);
z-index: 20;
@ -113,5 +134,15 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
&:hover .drag-handle {
opacity: 0.8;
}
.resize-indicator {
position: absolute;
font-size: small;
bottom: 0;
}
&.rtl .resize-indicator {
padding: 0.5rem 0 0 0.5rem;
right: 0;
}
}
</style>

View file

@ -59,5 +59,6 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
.pane {
@include panes.resizable(column, true, true);
opacity: var(--opacity, 1);
}
</style>

View file

@ -6,7 +6,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
import IconConstrain from "./IconConstrain.svelte";
import { chevronLeft, chevronRight } from "./icons";
export let value = 1;
export let value: number;
export let step = 1;
export let min = 1;
export let max = 9999;
@ -22,7 +22,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
}
let stringValue: string;
$: stringValue = value.toFixed(decimalPlaces(step));
$: if (value) stringValue = value.toFixed(decimalPlaces(step));
function update(this: HTMLInputElement): void {
value = Math.min(max, Math.max(min, parseFloat(this.value)));

View file

@ -355,12 +355,28 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
$: tagAmount = $tags.length;
let snapTags = $tagsCollapsed;
function collapseTags(): void {
lowerResizer.move([tagsPane, fieldsPane], tagsPane.minHeight);
$tagsCollapsed = snapTags = true;
}
function expandTags(): void {
lowerResizer.move([tagsPane, fieldsPane], tagsPane.maxHeight);
$tagsCollapsed = snapTags = false;
}
window.addEventListener("resize", () => snapResizer(snapTags));
function snapResizer(collapse: boolean): void {
if (collapse) {
collapseTags();
bridgeCommand("collapseTags");
} else {
expandTags();
bridgeCommand("expandTags");
}
}
</script>
@ -390,7 +406,9 @@ the AddCards dialog) should be implemented in the user of this component.
<Pane
bind:this={fieldsPane.resizable}
on:resize={(e) => (fieldsPane.height = e.detail.height)}
on:resize={(e) => {
fieldsPane.height = e.detail.height;
}}
>
<PaneContent>
<Fields>
@ -553,7 +571,17 @@ the AddCards dialog) should be implemented in the user of this component.
</PaneContent>
</Pane>
{#if $tagsCollapsed}
<HorizontalResizer
panes={[fieldsPane, tagsPane]}
showIndicator={$tagsCollapsed || snapTags}
tip={`Double click to ${$tagsCollapsed ? "expand" : "collapse"} tag editor`}
{clientHeight}
bind:this={lowerResizer}
on:dblclick={() => snapResizer(!$tagsCollapsed)}
on:release={() => {
snapResizer(snapTags);
}}
>
<div class="tags-expander">
<TagAddButton
on:tagappend={() => {
@ -564,36 +592,28 @@ the AddCards dialog) should be implemented in the user of this component.
{@html tagAmount > 0 ? `${tagAmount} Tags` : ""}
</TagAddButton>
</div>
{/if}
<HorizontalResizer
panes={[fieldsPane, tagsPane]}
tip={`Double click to ${$tagsCollapsed ? "expand" : "collapse"} tag editor`}
{clientHeight}
bind:this={lowerResizer}
on:dblclick={() => {
if ($tagsCollapsed) {
expandTags();
bridgeCommand("expandTags");
$tagsCollapsed = false;
} else {
collapseTags();
bridgeCommand("collapseTags");
$tagsCollapsed = true;
}
}}
/>
</HorizontalResizer>
<Pane
bind:this={tagsPane.resizable}
on:resize={(e) => {
tagsPane.height = e.detail.height;
$tagsCollapsed = tagsPane.height == 0;
if (tagsPane.maxHeight > 0) {
snapTags = tagsPane.height < tagsPane.maxHeight / 2;
}
}}
--opacity={(() => {
if (!$tagsCollapsed) {
return 1;
} else {
return snapTags ? tagsPane.height / tagsPane.maxHeight : 1;
}
})()}
>
<PaneContent scroll={false}>
<TagEditor
{tags}
--button-opacity={snapTags ? 0 : 1}
bind:this={tagEditor}
on:tagsupdate={saveTags}
on:tagsFocused={() => {
@ -617,7 +637,4 @@ the AddCards dialog) should be implemented in the user of this component.
flex-direction: column;
height: 100%;
}
.tags-expander {
margin-top: 0.5rem;
}
</style>

View file

@ -9,7 +9,6 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
import Shortcut from "../../components/Shortcut.svelte";
import WithFloating from "../../components/WithFloating.svelte";
import { mathjaxConfig } from "../../editable/mathjax-element";
import { bridgeCommand } from "../../lib/bridgecommand";
import * as tr from "../../lib/ftl";
import { getPlatformString } from "../../lib/shortcuts";
import { wrapInternal } from "../../lib/wrap";
@ -27,15 +26,27 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
}
function onMathjaxInline(): void {
surround("<anki-mathjax focusonmount>", "</anki-mathjax>");
if (mathjaxConfig.enabled) {
surround("<anki-mathjax focusonmount>", "</anki-mathjax>");
} else {
surround("\\(", "\\)");
}
}
function onMathjaxBlock(): void {
surround('<anki-mathjax block="true" focusonmount>', "</anki-matjax>");
if (mathjaxConfig.enabled) {
surround('<anki-mathjax block="true" focusonmount>', "</anki-matjax>");
} else {
surround("\\[", "\\]");
}
}
function onMathjaxChemistry(): void {
surround('<anki-mathjax focusonmount="0,4">\\ce{', "}</anki-mathjax>");
if (mathjaxConfig.enabled) {
surround('<anki-mathjax focusonmount="0,4">\\ce{', "}</anki-mathjax>");
} else {
surround("\\(\\ce{", "}\\)");
}
}
function onLatex(): void {
@ -50,11 +61,6 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
surround("[$$]", "[/$$]");
}
function toggleShowMathjax(): void {
mathjaxConfig.enabled = !mathjaxConfig.enabled;
bridgeCommand("toggleMathjax");
}
type LatexItem = [() => void, string, string];
const dropdownItems: LatexItem[] = [
@ -94,10 +100,6 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
>
</DropdownItem>
{/each}
<DropdownItem on:click={toggleShowMathjax}>
<span>{tr.editingToggleMathjaxRendering()}</span>
</DropdownItem>
</Popover>
</WithFloating>

View file

@ -8,6 +8,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
import IconButton from "../../components/IconButton.svelte";
import Popover from "../../components/Popover.svelte";
import WithFloating from "../../components/WithFloating.svelte";
import { mathjaxConfig } from "../../editable/mathjax-element";
import { bridgeCommand } from "../../lib/bridgecommand";
import * as tr from "../../lib/ftl";
import { shrinkImagesByDefault } from "../image-overlay/ImageOverlay.svelte";
@ -22,6 +23,11 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
showFloating = false;
}
function toggleShowMathjax(_evt: MouseEvent): void {
mathjaxConfig.enabled = !mathjaxConfig.enabled;
bridgeCommand("toggleMathjax");
}
function toggleCloseHTMLTags(_evt: MouseEvent): void {
$closeHTMLTags = !$closeHTMLTags;
bridgeCommand("toggleCloseHTMLTags");
@ -50,6 +56,10 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
<CheckBox value={$shrinkImagesByDefault} />
<span class="d-flex-inline ps-3">{tr.editingShrinkImages()}</span>
</DropdownItem>
<DropdownItem on:click={toggleShowMathjax}>
<CheckBox value={mathjaxConfig.enabled} />
<span class="d-flex-inline ps-3">{tr.editingMathjaxPreview()}</span>
</DropdownItem>
<DropdownItem on:click={toggleCloseHTMLTags}>
<CheckBox value={$closeHTMLTags} />
<span class="d-flex-inline ps-3">{tr.editingCloseHtmlTags()}</span>

View file

@ -99,7 +99,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
{configuration}
bind:api={codeMirror}
on:change={({ detail: mathjaxText }) => code.set(mathjaxText)}
on:tab
on:blur
/>
</div>

View file

@ -65,14 +65,18 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
}
}
function clear(): void {
unsubscribe();
activeImage = null;
mathjaxElement = null;
}
async function resetHandle(): Promise<void> {
selectAll = false;
position = undefined;
if (activeImage && mathjaxElement) {
unsubscribe();
activeImage = null;
mathjaxElement = null;
clear();
}
allow();
@ -188,10 +192,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
placeHandle(true);
await resetHandle();
}}
on:tab={async () => {
// Instead of resetting on blur, we reset on tab
// Otherwise, when clicking from Mathjax element to another,
// the user has to click twice (focus is called before blur?)
on:blur={async () => {
await resetHandle();
}}
let:editor={mathjaxEditor}
@ -220,8 +221,8 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
}}
on:delete={async () => {
placeCaretAfter(activeImage);
activeImage.remove();
await resetHandle();
mathjaxElement?.remove();
clear();
}}
on:surround={async ({ detail }) => {
const editor = await mathjaxEditor.editor;

View file

@ -6,7 +6,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
export let selected = false;
export let active = false;
let buttonRef: HTMLButtonElement;
let buttonRef: HTMLElement;
$: if (selected && buttonRef) {
/* buttonRef.scrollIntoView({ behavior: "smooth", block: "start" }); */
@ -18,7 +18,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
}
</script>
<button
<div
bind:this={buttonRef}
tabindex="-1"
class="autocomplete-item"
@ -30,20 +30,26 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
on:mouseleave
>
<slot />
</button>
</div>
<style lang="scss">
@use "sass/button-mixins" as button;
.autocomplete-item {
@include button.base($with-disabled: false, $active-class: active);
padding: 1px 7px 2px;
padding: 4px 8px;
text-align: start;
white-space: nowrap;
flex-grow: 1;
border-radius: 0;
border: 1px solid transparent;
&:not(:first-child) {
border-top-color: var(--border-subtle);
}
&:hover {
@include button.base($with-disabled: false, $active-class: active);
}
&.selected {
@include button.base(
$primary: true,

View file

@ -490,6 +490,9 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
display: flex;
flex-flow: row wrap;
align-items: flex-end;
background: var(--canvas-inset);
border-radius: var(--border-radius);
padding: 6px;
}
.tag-relative {

View file

@ -186,9 +186,9 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
flex-flow: column nowrap;
width: 80vw;
max-height: 7rem;
max-height: 30vh;
font-size: 11px;
font-size: 13px;
overflow-x: hidden;
text-overflow: ellipsis;
overflow-y: auto;

View file

@ -25,6 +25,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
<style lang="scss">
.tag-options-button {
padding: 6px 3px 0;
transition: opacity 0.2s linear;
opacity: var(--button-opacity, 1);
}
</style>