mirror of
https://github.com/ankitects/anki.git
synced 2025-09-23 08:22:24 -04:00
Merge branch 'main' into sassy-comments
This commit is contained in:
commit
876f1670cc
30 changed files with 327 additions and 134 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -118,6 +118,7 @@ dependencies = [
|
|||
"unicase",
|
||||
"unicode-normalization",
|
||||
"utime",
|
||||
"which",
|
||||
"zip",
|
||||
"zstd",
|
||||
]
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -226,6 +226,7 @@ def show(mw: aqt.AnkiQt) -> QDialog:
|
|||
"Nicholas Flint",
|
||||
"Daniel Vieira Memoria10X",
|
||||
"Luka Warren",
|
||||
"Christos Longros",
|
||||
)
|
||||
)
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)};
|
||||
}}
|
||||
"""
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -31,6 +31,7 @@ cargo_build_script(
|
|||
],
|
||||
deps = [
|
||||
"//rslib/cargo:prost_build",
|
||||
"//rslib/cargo:which",
|
||||
],
|
||||
)
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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: (
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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)));
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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>
|
||||
|
|
Loading…
Reference in a new issue