mirror of
https://github.com/ankitects/anki.git
synced 2025-09-18 14:02:21 -04:00
Compare commits
14 commits
5f837b81f6
...
0ed5fe67c8
Author | SHA1 | Date | |
---|---|---|---|
![]() |
0ed5fe67c8 | ||
![]() |
3890e12c9e | ||
![]() |
80cff16250 | ||
![]() |
75d9026be5 | ||
![]() |
6854d13b88 | ||
![]() |
29072654db | ||
![]() |
ec6f09958a | ||
![]() |
c2957746f4 | ||
![]() |
9e415869b8 | ||
![]() |
7e8a1076c1 | ||
![]() |
b97fb45e06 | ||
![]() |
61094d387a | ||
![]() |
90ed4cc115 | ||
![]() |
3726435906 |
20 changed files with 152 additions and 40 deletions
2
.version
2
.version
|
@ -1 +1 @@
|
||||||
25.09
|
25.09.2
|
||||||
|
|
|
@ -1 +1,2 @@
|
||||||
nodeLinker: node-modules
|
nodeLinker: node-modules
|
||||||
|
enableScripts: false
|
||||||
|
|
|
@ -49,6 +49,7 @@ Sander Santema <github.com/sandersantema/>
|
||||||
Thomas Brownback <https://github.com/brownbat/>
|
Thomas Brownback <https://github.com/brownbat/>
|
||||||
Andrew Gaul <andrew@gaul.org>
|
Andrew Gaul <andrew@gaul.org>
|
||||||
kenden
|
kenden
|
||||||
|
Emil Hamrin <github.com/e-hamrin>
|
||||||
Nickolay Yudin <kelciour@gmail.com>
|
Nickolay Yudin <kelciour@gmail.com>
|
||||||
neitrinoweb <github.com/neitrinoweb/>
|
neitrinoweb <github.com/neitrinoweb/>
|
||||||
Andreas Reis <github.com/nwwt>
|
Andreas Reis <github.com/nwwt>
|
||||||
|
|
|
@ -28,7 +28,11 @@ pub fn setup_yarn(args: YarnArgs) {
|
||||||
.arg("--ignore-scripts"),
|
.arg("--ignore-scripts"),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
run_command(Command::new(&args.yarn_bin).arg("install"));
|
run_command(
|
||||||
|
Command::new(&args.yarn_bin)
|
||||||
|
.arg("install")
|
||||||
|
.arg("--immutable"),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::fs::write(args.stamp, b"").unwrap();
|
std::fs::write(args.stamp, b"").unwrap();
|
||||||
|
|
|
@ -1,35 +1,78 @@
|
||||||
# This Dockerfile uses three stages.
|
# This is a user-contributed Dockerfile. No official support is available.
|
||||||
# 1. Compile anki (and dependencies) and build python wheels.
|
|
||||||
# 2. Create a virtual environment containing anki and its dependencies.
|
|
||||||
# 3. Create a final image that only includes anki's virtual environment and required
|
|
||||||
# system packages.
|
|
||||||
|
|
||||||
ARG PYTHON_VERSION="3.9"
|
|
||||||
ARG DEBIAN_FRONTEND="noninteractive"
|
ARG DEBIAN_FRONTEND="noninteractive"
|
||||||
|
|
||||||
# Build anki.
|
FROM ubuntu:24.04 AS build
|
||||||
FROM python:$PYTHON_VERSION AS build
|
|
||||||
RUN curl -fsSL https://github.com/bazelbuild/bazelisk/releases/download/v1.7.4/bazelisk-linux-amd64 \
|
|
||||||
> /usr/local/bin/bazel \
|
|
||||||
&& chmod +x /usr/local/bin/bazel \
|
|
||||||
# Bazel expects /usr/bin/python
|
|
||||||
&& ln -s /usr/local/bin/python /usr/bin/python
|
|
||||||
WORKDIR /opt/anki
|
WORKDIR /opt/anki
|
||||||
COPY . .
|
ENV PYTHON_VERSION="3.13"
|
||||||
# Build python wheels.
|
|
||||||
|
|
||||||
|
# System deps
|
||||||
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||||
|
curl \
|
||||||
|
git \
|
||||||
|
build-essential \
|
||||||
|
pkg-config \
|
||||||
|
libssl-dev \
|
||||||
|
libbz2-dev \
|
||||||
|
libreadline-dev \
|
||||||
|
libsqlite3-dev \
|
||||||
|
libffi-dev \
|
||||||
|
zlib1g-dev \
|
||||||
|
liblzma-dev \
|
||||||
|
ca-certificates \
|
||||||
|
ninja-build \
|
||||||
|
rsync \
|
||||||
|
libglib2.0-0 \
|
||||||
|
libgl1 \
|
||||||
|
libx11-6 \
|
||||||
|
libxext6 \
|
||||||
|
libxrender1 \
|
||||||
|
libxkbcommon0 \
|
||||||
|
libxkbcommon-x11-0 \
|
||||||
|
libxcb1 \
|
||||||
|
libxcb-render0 \
|
||||||
|
libxcb-shm0 \
|
||||||
|
libxcb-icccm4 \
|
||||||
|
libxcb-image0 \
|
||||||
|
libxcb-keysyms1 \
|
||||||
|
libxcb-randr0 \
|
||||||
|
libxcb-shape0 \
|
||||||
|
libxcb-xfixes0 \
|
||||||
|
libxcb-xinerama0 \
|
||||||
|
libxcb-xinput0 \
|
||||||
|
libsm6 \
|
||||||
|
libice6 \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
# install rust with rustup
|
||||||
|
RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
|
||||||
|
ENV PATH="/root/.cargo/bin:${PATH}"
|
||||||
|
|
||||||
|
# Install uv and Python 3.13 with uv
|
||||||
|
RUN curl -LsSf https://astral.sh/uv/install.sh | sh \
|
||||||
|
&& ln -s /root/.local/bin/uv /usr/local/bin/uv
|
||||||
|
ENV PATH="/root/.local/bin:${PATH}"
|
||||||
|
|
||||||
|
RUN uv python install ${PYTHON_VERSION} --default
|
||||||
|
|
||||||
|
COPY . .
|
||||||
|
|
||||||
RUN ./tools/build
|
RUN ./tools/build
|
||||||
|
|
||||||
|
|
||||||
# Install pre-compiled Anki.
|
# Install pre-compiled Anki.
|
||||||
FROM python:${PYTHON_VERSION}-slim as installer
|
FROM python:3.13-slim AS installer
|
||||||
WORKDIR /opt/anki/
|
WORKDIR /opt/anki/
|
||||||
COPY --from=build /opt/anki/wheels/ wheels/
|
COPY --from=build /opt/anki/out/wheels/ wheels/
|
||||||
# Use virtual environment.
|
# Use virtual environment.
|
||||||
RUN python -m venv venv \
|
RUN python -m venv venv \
|
||||||
&& ./venv/bin/python -m pip install --no-cache-dir setuptools wheel \
|
&& ./venv/bin/python -m pip install --no-cache-dir setuptools wheel \
|
||||||
&& ./venv/bin/python -m pip install --no-cache-dir /opt/anki/wheels/*.whl
|
&& ./venv/bin/python -m pip install --no-cache-dir /opt/anki/wheels/*.whl
|
||||||
|
|
||||||
|
|
||||||
# We use another build stage here so we don't include the wheels in the final image.
|
# We use another build stage here so we don't include the wheels in the final image.
|
||||||
FROM python:${PYTHON_VERSION}-slim as final
|
FROM python:3.13-slim AS final
|
||||||
COPY --from=installer /opt/anki/venv /opt/anki/venv
|
COPY --from=installer /opt/anki/venv /opt/anki/venv
|
||||||
ENV PATH=/opt/anki/venv/bin:$PATH
|
ENV PATH=/opt/anki/venv/bin:$PATH
|
||||||
# Install run-time dependencies.
|
# Install run-time dependencies.
|
||||||
|
@ -59,9 +102,9 @@ RUN apt-get update \
|
||||||
libxrender1 \
|
libxrender1 \
|
||||||
libxtst6 \
|
libxtst6 \
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
# Add non-root user.
|
# Add non-root user.
|
||||||
RUN useradd --create-home anki
|
RUN useradd --create-home anki
|
||||||
USER anki
|
USER anki
|
||||||
WORKDIR /work
|
WORKDIR /work
|
||||||
ENTRYPOINT ["/opt/anki/venv/bin/anki"]
|
ENTRYPOINT ["/opt/anki/venv/bin/anki"]
|
||||||
LABEL maintainer="Jakub Kaczmarzyk <jakub.kaczmarzyk@gmail.com>"
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit 6552c95a81d162422b2a50126547cc7f1b50c2fd
|
Subproject commit 480ef0da728c7ea3485c58529ae7ee02be3e5dba
|
|
@ -46,6 +46,20 @@ studying-type-answer-unknown-field = Type answer: unknown field { $val }
|
||||||
studying-unbury = Unbury
|
studying-unbury = Unbury
|
||||||
studying-what-would-you-like-to-unbury = What would you like to unbury?
|
studying-what-would-you-like-to-unbury = What would you like to unbury?
|
||||||
studying-you-havent-recorded-your-voice-yet = You haven't recorded your voice yet.
|
studying-you-havent-recorded-your-voice-yet = You haven't recorded your voice yet.
|
||||||
|
studying-card-studied-in-minute =
|
||||||
|
{ $cards ->
|
||||||
|
[one] { $cards } card
|
||||||
|
*[other] { $cards } cards
|
||||||
|
} studied in
|
||||||
|
{ $minutes ->
|
||||||
|
[one] { $minutes } minute.
|
||||||
|
*[other] { $minutes } minutes.
|
||||||
|
}
|
||||||
|
studying-question-time-elapsed = Question time elapsed
|
||||||
|
studying-answer-time-elapsed = Answer time elapsed
|
||||||
|
|
||||||
|
## OBSOLETE; you do not need to translate this
|
||||||
|
|
||||||
studying-card-studied-in =
|
studying-card-studied-in =
|
||||||
{ $count ->
|
{ $count ->
|
||||||
[one] { $count } card studied in
|
[one] { $count } card studied in
|
||||||
|
@ -56,5 +70,3 @@ studying-minute =
|
||||||
[one] { $count } minute.
|
[one] { $count } minute.
|
||||||
*[other] { $count } minutes.
|
*[other] { $count } minutes.
|
||||||
}
|
}
|
||||||
studying-question-time-elapsed = Question time elapsed
|
|
||||||
studying-answer-time-elapsed = Answer time elapsed
|
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit dad4e2736a2b53dcdb52d79b5703dd464c05d666
|
Subproject commit fd5f984785ad07a0d3dbd893ee3d7e3671eaebd6
|
|
@ -27,6 +27,9 @@ service FrontendService {
|
||||||
rpc deckOptionsRequireClose(generic.Empty) returns (generic.Empty);
|
rpc deckOptionsRequireClose(generic.Empty) returns (generic.Empty);
|
||||||
// Warns python that the deck option web view is ready to receive requests.
|
// Warns python that the deck option web view is ready to receive requests.
|
||||||
rpc deckOptionsReady(generic.Empty) returns (generic.Empty);
|
rpc deckOptionsReady(generic.Empty) returns (generic.Empty);
|
||||||
|
|
||||||
|
// Save colour picker's custom colour palette
|
||||||
|
rpc SaveCustomColours(generic.Empty) returns (generic.Empty);
|
||||||
}
|
}
|
||||||
|
|
||||||
service BackendFrontendService {}
|
service BackendFrontendService {}
|
||||||
|
|
|
@ -13,7 +13,7 @@ import aqt.browser
|
||||||
from anki.cards import Card
|
from anki.cards import Card
|
||||||
from anki.collection import Config
|
from anki.collection import Config
|
||||||
from anki.tags import MARKED_TAG
|
from anki.tags import MARKED_TAG
|
||||||
from aqt import AnkiQt, gui_hooks
|
from aqt import AnkiQt, gui_hooks, is_mac
|
||||||
from aqt.qt import (
|
from aqt.qt import (
|
||||||
QCheckBox,
|
QCheckBox,
|
||||||
QDialog,
|
QDialog,
|
||||||
|
@ -81,10 +81,15 @@ class Previewer(QDialog):
|
||||||
qconnect(self.finished, self._on_finished)
|
qconnect(self.finished, self._on_finished)
|
||||||
self.silentlyClose = True
|
self.silentlyClose = True
|
||||||
self.vbox = QVBoxLayout()
|
self.vbox = QVBoxLayout()
|
||||||
|
spacing = 6
|
||||||
self.vbox.setContentsMargins(0, 0, 0, 0)
|
self.vbox.setContentsMargins(0, 0, 0, 0)
|
||||||
|
self.vbox.setSpacing(spacing)
|
||||||
self._web: AnkiWebView | None = AnkiWebView(kind=AnkiWebViewKind.PREVIEWER)
|
self._web: AnkiWebView | None = AnkiWebView(kind=AnkiWebViewKind.PREVIEWER)
|
||||||
self.vbox.addWidget(self._web)
|
self.vbox.addWidget(self._web)
|
||||||
self.bbox = QDialogButtonBox()
|
self.bbox = QDialogButtonBox()
|
||||||
|
self.bbox.setContentsMargins(
|
||||||
|
spacing, spacing if is_mac else 0, spacing, spacing
|
||||||
|
)
|
||||||
self.bbox.setLayoutDirection(Qt.LayoutDirection.LeftToRight)
|
self.bbox.setLayoutDirection(Qt.LayoutDirection.LeftToRight)
|
||||||
|
|
||||||
gui_hooks.card_review_webview_did_init(self._web, AnkiWebViewKind.PREVIEWER)
|
gui_hooks.card_review_webview_did_init(self._web, AnkiWebViewKind.PREVIEWER)
|
||||||
|
|
|
@ -151,6 +151,7 @@ class Editor:
|
||||||
self.add_webview()
|
self.add_webview()
|
||||||
self.setupWeb()
|
self.setupWeb()
|
||||||
self.setupShortcuts()
|
self.setupShortcuts()
|
||||||
|
self.setupColourPalette()
|
||||||
gui_hooks.editor_did_init(self)
|
gui_hooks.editor_did_init(self)
|
||||||
|
|
||||||
# Initial setup
|
# Initial setup
|
||||||
|
@ -349,6 +350,14 @@ require("anki/ui").loaded.then(() => require("anki/NoteEditor").instances[0].too
|
||||||
keys, fn, _ = row
|
keys, fn, _ = row
|
||||||
QShortcut(QKeySequence(keys), self.widget, activated=fn) # type: ignore
|
QShortcut(QKeySequence(keys), self.widget, activated=fn) # type: ignore
|
||||||
|
|
||||||
|
def setupColourPalette(self) -> None:
|
||||||
|
if not (colors := self.mw.col.get_config("customColorPickerPalette")):
|
||||||
|
return
|
||||||
|
for i, colour in enumerate(colors[: QColorDialog.customCount()]):
|
||||||
|
if not QColor.isValidColorName(colour):
|
||||||
|
continue
|
||||||
|
QColorDialog.setCustomColor(i, QColor.fromString(colour))
|
||||||
|
|
||||||
def _addFocusCheck(self, fn: Callable) -> Callable:
|
def _addFocusCheck(self, fn: Callable) -> Callable:
|
||||||
def checkFocus() -> None:
|
def checkFocus() -> None:
|
||||||
if self.currentField is None:
|
if self.currentField is None:
|
||||||
|
|
|
@ -599,6 +599,15 @@ def deck_options_ready() -> bytes:
|
||||||
return b""
|
return b""
|
||||||
|
|
||||||
|
|
||||||
|
def save_custom_colours() -> bytes:
|
||||||
|
colors = [
|
||||||
|
QColorDialog.customColor(i).name(QColor.NameFormat.HexArgb)
|
||||||
|
for i in range(QColorDialog.customCount())
|
||||||
|
]
|
||||||
|
aqt.mw.col.set_config("customColorPickerPalette", colors)
|
||||||
|
return b""
|
||||||
|
|
||||||
|
|
||||||
post_handler_list = [
|
post_handler_list = [
|
||||||
congrats_info,
|
congrats_info,
|
||||||
get_deck_configs_for_update,
|
get_deck_configs_for_update,
|
||||||
|
@ -614,6 +623,7 @@ post_handler_list = [
|
||||||
search_in_browser,
|
search_in_browser,
|
||||||
deck_options_require_close,
|
deck_options_require_close,
|
||||||
deck_options_ready,
|
deck_options_ready,
|
||||||
|
save_custom_colours,
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,7 @@ import aqt.browser
|
||||||
import aqt.operations
|
import aqt.operations
|
||||||
from anki.cards import Card, CardId
|
from anki.cards import Card, CardId
|
||||||
from anki.collection import Config, OpChanges, OpChangesWithCount
|
from anki.collection import Config, OpChanges, OpChangesWithCount
|
||||||
|
from anki.lang import with_collapsed_whitespace
|
||||||
from anki.scheduler.base import ScheduleCardsAsNew
|
from anki.scheduler.base import ScheduleCardsAsNew
|
||||||
from anki.scheduler.v3 import (
|
from anki.scheduler.v3 import (
|
||||||
CardAnswer,
|
CardAnswer,
|
||||||
|
@ -966,11 +967,15 @@ timerStopped = false;
|
||||||
elapsed = self.mw.col.timeboxReached()
|
elapsed = self.mw.col.timeboxReached()
|
||||||
if elapsed:
|
if elapsed:
|
||||||
assert not isinstance(elapsed, bool)
|
assert not isinstance(elapsed, bool)
|
||||||
part1 = tr.studying_card_studied_in(count=elapsed[1])
|
cards_val = elapsed[1]
|
||||||
mins = int(round(elapsed[0] / 60))
|
minutes_val = int(round(elapsed[0] / 60))
|
||||||
part2 = tr.studying_minute(count=mins)
|
message = with_collapsed_whitespace(
|
||||||
|
tr.studying_card_studied_in_minute(
|
||||||
|
cards=cards_val, minutes=str(minutes_val)
|
||||||
|
)
|
||||||
|
)
|
||||||
fin = tr.studying_finish()
|
fin = tr.studying_finish()
|
||||||
diag = askUserDialog(f"{part1} {part2}", [tr.studying_continue(), fin])
|
diag = askUserDialog(message, [tr.studying_continue(), fin])
|
||||||
diag.setIcon(QMessageBox.Icon.Information)
|
diag.setIcon(QMessageBox.Icon.Information)
|
||||||
if diag.run() == fin:
|
if diag.run() == fin:
|
||||||
self.mw.moveToState("deckBrowser")
|
self.mw.moveToState("deckBrowser")
|
||||||
|
|
|
@ -161,6 +161,7 @@ fn run() -> Result<()> {
|
||||||
|
|
||||||
print!("\x1B[2J\x1B[H"); // Clear screen and move cursor to top
|
print!("\x1B[2J\x1B[H"); // Clear screen and move cursor to top
|
||||||
println!("\x1B[1mAnki Launcher\x1B[0m\n");
|
println!("\x1B[1mAnki Launcher\x1B[0m\n");
|
||||||
|
println!("This is the Anki Launcher. If you're running it for the first time, press Enter to install the latest version. Otherwise, pressing Enter will check for updates and then open Anki.\n");
|
||||||
|
|
||||||
ensure_os_supported()?;
|
ensure_os_supported()?;
|
||||||
|
|
||||||
|
@ -440,8 +441,8 @@ fn file_timestamp_secs(path: &std::path::Path) -> i64 {
|
||||||
|
|
||||||
fn get_main_menu_choice(state: &State) -> Result<MainMenuChoice> {
|
fn get_main_menu_choice(state: &State) -> Result<MainMenuChoice> {
|
||||||
loop {
|
loop {
|
||||||
println!("1) Latest Anki (press Enter)");
|
println!("1) Install Latest Anki (press Enter)");
|
||||||
println!("2) Choose a version");
|
println!("2) Choose a specific version");
|
||||||
|
|
||||||
if let Some(current_version) = &state.current_version {
|
if let Some(current_version) = &state.current_version {
|
||||||
let normalized_current = normalize_version(current_version);
|
let normalized_current = normalize_version(current_version);
|
||||||
|
@ -477,7 +478,7 @@ fn get_main_menu_choice(state: &State) -> Result<MainMenuChoice> {
|
||||||
if mirror_enabled { "on" } else { "off" }
|
if mirror_enabled { "on" } else { "off" }
|
||||||
);
|
);
|
||||||
println!();
|
println!();
|
||||||
println!("8) Uninstall");
|
println!("8) Uninstall Anki");
|
||||||
print!("> ");
|
print!("> ");
|
||||||
let _ = stdout().flush();
|
let _ = stdout().flush();
|
||||||
|
|
||||||
|
|
|
@ -174,7 +174,7 @@ impl Collection {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let health_check_passed = if health_check {
|
let health_check_passed = if health_check && input.train_set.len() > 300 {
|
||||||
let fsrs = FSRS::new(None)?;
|
let fsrs = FSRS::new(None)?;
|
||||||
fsrs.evaluate_with_time_series_splits(input, |_| true)
|
fsrs.evaluate_with_time_series_splits(input, |_| true)
|
||||||
.ok()
|
.ok()
|
||||||
|
|
|
@ -4,6 +4,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
-->
|
-->
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Shortcut from "$lib/components/Shortcut.svelte";
|
import Shortcut from "$lib/components/Shortcut.svelte";
|
||||||
|
import { saveCustomColours } from "@generated/backend";
|
||||||
|
|
||||||
export let keyCombination: string | null = null;
|
export let keyCombination: string | null = null;
|
||||||
export let value: string;
|
export let value: string;
|
||||||
|
@ -11,7 +12,15 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
let inputRef: HTMLInputElement;
|
let inputRef: HTMLInputElement;
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<input bind:this={inputRef} tabindex="-1" type="color" bind:value on:input on:change />
|
<input
|
||||||
|
bind:this={inputRef}
|
||||||
|
tabindex="-1"
|
||||||
|
type="color"
|
||||||
|
bind:value
|
||||||
|
on:input
|
||||||
|
on:change
|
||||||
|
on:click={() => saveCustomColours({})}
|
||||||
|
/>
|
||||||
|
|
||||||
{#if keyCombination}
|
{#if keyCombination}
|
||||||
<Shortcut {keyCombination} on:action={() => inputRef.click()} />
|
<Shortcut {keyCombination} on:action={() => inputRef.click()} />
|
||||||
|
|
|
@ -19,6 +19,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
import ColorPicker from "./ColorPicker.svelte";
|
import ColorPicker from "./ColorPicker.svelte";
|
||||||
import { context as editorToolbarContext } from "./EditorToolbar.svelte";
|
import { context as editorToolbarContext } from "./EditorToolbar.svelte";
|
||||||
import WithColorHelper from "./WithColorHelper.svelte";
|
import WithColorHelper from "./WithColorHelper.svelte";
|
||||||
|
import { saveCustomColours } from "@generated/backend";
|
||||||
|
|
||||||
export let color: string;
|
export let color: string;
|
||||||
|
|
||||||
|
@ -134,7 +135,10 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
color = setColor(event);
|
color = setColor(event);
|
||||||
bridgeCommand(`lastHighlightColor:${color}`);
|
bridgeCommand(`lastHighlightColor:${color}`);
|
||||||
}}
|
}}
|
||||||
on:change={() => setTextColor()}
|
on:change={() => {
|
||||||
|
setTextColor();
|
||||||
|
saveCustomColours({});
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</WithColorHelper>
|
</WithColorHelper>
|
||||||
|
|
|
@ -22,6 +22,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
import ColorPicker from "./ColorPicker.svelte";
|
import ColorPicker from "./ColorPicker.svelte";
|
||||||
import { context as editorToolbarContext } from "./EditorToolbar.svelte";
|
import { context as editorToolbarContext } from "./EditorToolbar.svelte";
|
||||||
import WithColorHelper from "./WithColorHelper.svelte";
|
import WithColorHelper from "./WithColorHelper.svelte";
|
||||||
|
import { saveCustomColours } from "@generated/backend";
|
||||||
|
|
||||||
export let color: string;
|
export let color: string;
|
||||||
|
|
||||||
|
@ -158,6 +159,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
setTextColor();
|
setTextColor();
|
||||||
}, 200);
|
}, 200);
|
||||||
|
saveCustomColours({});
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</IconButton>
|
</IconButton>
|
||||||
|
|
|
@ -10,6 +10,9 @@ export function allImagesLoaded(): Promise<void[]> {
|
||||||
}
|
}
|
||||||
|
|
||||||
function imageLoaded(img: HTMLImageElement): Promise<void> {
|
function imageLoaded(img: HTMLImageElement): Promise<void> {
|
||||||
|
if (!img.getAttribute("decoding")) {
|
||||||
|
img.decoding = "async";
|
||||||
|
}
|
||||||
return img.complete
|
return img.complete
|
||||||
? Promise.resolve()
|
? Promise.resolve()
|
||||||
: new Promise((resolve) => {
|
: new Promise((resolve) => {
|
||||||
|
|
|
@ -6939,8 +6939,8 @@ __metadata:
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"vite@npm:6":
|
"vite@npm:6":
|
||||||
version: 6.3.5
|
version: 6.3.6
|
||||||
resolution: "vite@npm:6.3.5"
|
resolution: "vite@npm:6.3.6"
|
||||||
dependencies:
|
dependencies:
|
||||||
esbuild: "npm:^0.25.0"
|
esbuild: "npm:^0.25.0"
|
||||||
fdir: "npm:^6.4.4"
|
fdir: "npm:^6.4.4"
|
||||||
|
@ -6989,7 +6989,7 @@ __metadata:
|
||||||
optional: true
|
optional: true
|
||||||
bin:
|
bin:
|
||||||
vite: bin/vite.js
|
vite: bin/vite.js
|
||||||
checksum: 10c0/df70201659085133abffc6b88dcdb8a57ef35f742a01311fc56a4cfcda6a404202860729cc65a2c401a724f6e25f9ab40ce4339ed4946f550541531ced6fe41c
|
checksum: 10c0/add701f1e72596c002275782e38d0389ab400c1be330c93a3009804d62db68097a936ca1c53c3301df3aaacfe5e328eab547060f31ef9c49a277ae50df6ad4fb
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue