Compare commits

...

39 commits

Author SHA1 Message Date
Luc Mcgrady
3307fdaf11
Merge 1e67a773c6 into 4506ad0c97 2025-09-15 10:28:26 +02:00
Luc Mcgrady
1e67a773c6
./check 2025-09-04 00:26:10 +01:00
Luc Mcgrady
c7fd7a0965
re-add reviewer-bottom to build script 2025-09-04 00:25:22 +01:00
Luc Mcgrady
d775efcb06
hide bottomweb 2025-09-03 23:40:37 +01:00
Luc Mcgrady
2e0a75ed83
fix: "card" class included bottombar 2025-09-03 23:38:28 +01:00
Luc Mcgrady
45793b3b64
Added: Todo 2025-09-03 23:36:29 +01:00
Luc Mcgrady
a08bca2673
dumb initial _showQuestion call 2025-09-03 23:24:45 +01:00
Luc Mcgrady
0acc8d14e0
Fix: Card style 2025-09-03 23:14:19 +01:00
Luc Mcgrady
f35b2cf5d2
bodyclass 2025-09-03 23:07:04 +01:00
Luc Mcgrady
d0d1c519e6
Naive reviewer 2025-09-03 22:57:49 +01:00
Luc Mcgrady
eac356139c
Added: Reviewer framework 2025-09-03 22:48:51 +01:00
Luc Mcgrady
7805b1b426
Fix: Wrong function name 2025-09-03 22:34:26 +01:00
Luc Mcgrady
c64dd6c959
Neaten bottom code 2025-09-03 22:29:23 +01:00
Luc Mcgrady
1ec9f4902e
Merge branch 'main' into svelte-reviewer-bottom 2025-09-03 22:08:20 +01:00
Luc Mcgrady
9d3451f97b
fix py issues 2025-09-03 22:07:44 +01:00
Luc Mcgrady
e46d98e2e0
./check 2025-09-03 22:05:35 +01:00
Luc Mcgrady
f01e0f8d0b
toggle svelte reviewer on 2025-09-03 22:01:41 +01:00
Luc Mcgrady
7cb8e62254
revert random rust changes 2025-09-03 21:59:46 +01:00
Luc Mcgrady
d81ec73205
Use inheritance for reviewer 2025-09-03 21:59:34 +01:00
Luc Mcgrady
4bf38ec2af
Added: Reviewer entrypoint 2025-09-03 18:40:37 +01:00
Luc Mcgrady
fac5d64558
Manually specify height 2025-08-26 16:12:03 +01:00
Luc Mcgrady
d49f1eb430
Fix: Update remaining on answer shown 2025-08-26 02:23:23 +01:00
Luc Mcgrady
860a8b4295
./check 2025-08-26 02:08:24 +01:00
Luc Mcgrady
6c540c89f1
./check 2025-08-26 02:03:13 +01:00
Luc Mcgrady
a365369562
align items: center 2025-08-26 01:58:52 +01:00
Luc Mcgrady
28402c548d
Fix: Id not class 2025-08-26 01:58:00 +01:00
Luc Mcgrady
b256e88b1d
Fix: Large font size 2025-08-26 01:56:30 +01:00
Luc Mcgrady
9dbb7abdbb
Remove unneeded globals 2025-08-26 01:46:27 +01:00
Luc Mcgrady
8a57d1c5e1
Fix: showQuestion issues 2025-08-26 01:36:17 +01:00
Luc Mcgrady
992c8ad731
Added: Remaining 2025-08-26 00:45:58 +01:00
Luc Mcgrady
7e92c40169
Added: More bridge command 2025-08-25 23:35:22 +01:00
Luc Mcgrady
f4eb7e0ff9
Use sveltekit 2025-08-25 23:32:33 +01:00
Luc Mcgrady
8c0d1d1720
More i18n 2025-08-25 22:30:28 +01:00
Luc Mcgrady
5d536f2f8e
Added: edit button 2025-08-25 22:01:48 +01:00
Luc Mcgrady
7788aa7785
Answer buttons 2025-08-25 21:55:21 +01:00
Luc Mcgrady
758cfa2693
Buttons template 2025-08-25 21:03:40 +01:00
Luc Mcgrady
34c1dfd849
Added: Svelte component 2025-08-25 20:34:26 +01:00
Luc Mcgrady
244aade836
Fix: Showquestion is not defined 2025-08-25 18:46:35 +01:00
Luc Mcgrady
6869e9fd36
reviewer-bottom entrypoint 2025-08-25 18:42:20 +01:00
15 changed files with 372 additions and 5 deletions

View file

@ -1075,9 +1075,9 @@ title="{}" {}>{}</button>""".format(
self.overview = Overview(self)
def setupReviewer(self) -> None:
from aqt.reviewer import Reviewer
from aqt.reviewer import Reviewer, SvelteReviewer
self.reviewer = Reviewer(self)
self.reviewer = SvelteReviewer(self) if True else Reviewer(self)
# Syncing
##########################################################################

View file

@ -334,6 +334,7 @@ def is_sveltekit_page(path: str) -> bool:
"import-csv",
"import-page",
"image-occlusion",
"reviewer",
]

View file

@ -1228,6 +1228,69 @@ timerStopped = false;
setFlag = set_flag_on_current_card
class SvelteReviewer(Reviewer):
def _answerButtons(self) -> str:
default = self._defaultEase()
assert isinstance(self.mw.col.sched, V3Scheduler)
labels = self.mw.col.sched.describe_next_states(self._v3.states)
def but(i: int, label: str):
if i == default:
id = "defease"
else:
id = ""
due = self._buttonTime(i, v3_labels=labels)
key = (
tr.actions_shortcut_key(val=aqt.mw.pm.get_answer_key(i))
if aqt.mw.pm.get_answer_key(i)
else ""
)
return {
"id": id,
"key": key,
"i": i,
"label": label,
"due": due,
}
return [but(ease, label) for ease, label in self._answerButtonList()] # type: ignore
def _remaining(self) -> str:
if not self.mw.col.conf["dueCounts"]:
return ""
idx, counts = self._v3.counts()
self.bottom.web.eval(f"_updateRemaining({json.dumps(counts)},{idx})")
return ""
def _showAnswerButton(self) -> None:
if self.card.should_show_timer():
maxTime = self.card.time_limit() / 1000
else:
maxTime = 0
self._remaining()
self.bottom.web.eval("_showQuestion(%s,%d);" % ("", maxTime))
def _linkHandler(self, url: str) -> None:
if url == "bottomReady":
self._showQuestion()
self._remaining()
return
super()._linkHandler(url)
def _initWeb(self) -> None:
self._reps = 0
# main window
self.web.load_sveltekit_page("reviewer")
# block default drag & drop behavior while allowing drop events to be received by JS handlers
self.web.allow_drops = True
self.web.eval("_blockDefaultDragDropBehavior();")
# ensure bottom web functions trigger
self.bottom.web = self.web
self.mw.bottomWeb.hide()
# if the last element is a comment, then the RUN_STATE_MUTATION code
# breaks due to the comment wrongly commenting out python code.
# To prevent this we put the js code on a separate line

View file

@ -0,0 +1,17 @@
<script>
import ReviewerBottomOuter from "./reviewer-bottom/ReviewerBottomOuter.svelte";
import ReviewerOuter from "./reviewerOuter.svelte";
</script>
<div>
<ReviewerOuter></ReviewerOuter>
<ReviewerBottomOuter></ReviewerBottomOuter>
</div>
<style>
div {
height: calc(100vh - 40px);
display: flex;
flex-direction: column;
}
</style>

View file

View file

@ -0,0 +1,14 @@
<!--
Copyright: Ankitects Pty Ltd and contributors
License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
-->
<script lang="ts">
import { bridgeCommand } from "@tslib/bridgecommand";
import type { AnswerButtonInfo } from "./types";
export let info: AnswerButtonInfo;
</script>
<button on:click={() => bridgeCommand(`ease${info.i}`)}>
{info.label}
</button>

View file

@ -0,0 +1,16 @@
<!--
Copyright: Ankitects Pty Ltd and contributors
License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
-->
<script lang="ts">
export let underlined: boolean;
export let cls: string;
</script>
<span class={cls}>
{#if underlined}
<u><slot /></u>
{:else}
<slot />
{/if}
</span>

View file

@ -0,0 +1,79 @@
<!--
Copyright: Ankitects Pty Ltd and contributors
License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
-->
<script lang="ts">
import type { Writable } from "svelte/store";
import AnswerButton from "./AnswerButton.svelte";
import { bridgeCommand } from "@tslib/bridgecommand";
import * as tr from "@generated/ftl";
import RemainingNumber from "./RemainingNumber.svelte";
import type { AnswerButtonInfo } from "./types";
export let answerButtons: Writable<AnswerButtonInfo[]>;
export let remaining: Writable<number[]>;
export let remainingIndex: Writable<number>;
$: console.log($remaining);
</script>
<div id="outer" class="fancy">
<div id="tableinner">
<div>
<button
title={tr.actionsShortcutKey({ val: "E" })}
on:click={() => bridgeCommand("edit")}
>
{tr.studyingEdit()}
</button>
</div>
<div class="review-buttons">
<span>
<RemainingNumber cls="new-count" underlined={$remainingIndex === 0}>
{$remaining[0]}
</RemainingNumber> +
<RemainingNumber cls="learn-count" underlined={$remainingIndex === 1}>
{$remaining[1]}
</RemainingNumber> +
<RemainingNumber cls="review-count" underlined={$remainingIndex === 2}>
{$remaining[2]}
</RemainingNumber>
</span>
<div>
{#if $answerButtons.length}
{#each $answerButtons as answerButton}
<AnswerButton info={answerButton}></AnswerButton>
{/each}
{:else}
<button on:click={() => bridgeCommand("ans")}>
{tr.studyingShowAnswer()}
</button>
{/if}
</div>
</div>
<div>
<button
on:click={() => bridgeCommand("more")}
title={tr.actionsShortcutKey({ val: "M" })}
>
{tr.studyingMore()}&#8615
</button>
</div>
</div>
</div>
<style lang="scss">
#tableinner {
width: 100%;
display: grid;
grid-template-columns: auto 1fr auto;
justify-items: center;
align-items: center;
}
.review-buttons {
display: flex;
flex-direction: column;
align-items: center;
}
</style>

View file

@ -0,0 +1,20 @@
<!--
Copyright: Ankitects Pty Ltd and contributors
License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
-->
<script lang="ts">
import { onMount } from "svelte";
import ReviewerBottom from "./ReviewerBottom.svelte";
import "./index.scss";
import { setupBottomBar } from "./reviewer-bottom";
let reviewerInfo: null | ReturnType<typeof setupBottomBar> = null;
onMount(() => {
reviewerInfo = setupBottomBar();
});
</script>
{#if reviewerInfo}
<ReviewerBottom {...reviewerInfo}></ReviewerBottom>
{/if}

View file

@ -1,9 +1,10 @@
/* Copyright: Ankitects Pty Ltd and contributors
* License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html */
@use "../../../../../ts/lib/sass/root-vars";
@use "../../../../../ts/lib/sass/vars" as *;
@use "../../../../../ts/lib/sass/card-counts";
@use "../../../lib/sass/root-vars";
@use "../../../lib/sass/vars" as *;
@use "../../../lib/sass/card-counts";
@use "../../../lib/sass/buttons";
:root {
--focus-color: #{palette-of(border-focus)};
@ -16,6 +17,8 @@
body {
margin: 0;
padding: 0;
font-size: 12px;
height: 72px;
}
#middle td[align="center"] {

View file

@ -0,0 +1,90 @@
import { bridgeCommand } from "@tslib/bridgecommand";
import { writable } from "svelte/store";
import type { AnswerButtonInfo } from "./types";
export function setupBottomBar() {
/*
let timerStopped = false;
let maxTime = 0;
function updateTime(): void {
const timeNode = document.getElementById("time");
if (maxTime === 0) {
timeNode.textContent = "";
return;
}
globalThis.time = Math.min(maxTime, globalThis.time);
const m = Math.floor(globalThis.time / 60);
const s = globalThis.time % 60;
const sStr = String(s).padStart(2, "0");
const timeString = `${m}:${sStr}`;
if (maxTime === time) {
timeNode.innerHTML = `<font color=red>${timeString}</font>`;
} else {
timeNode.textContent = timeString;
}
}*/
const answerButtons = writable<AnswerButtonInfo[]>([]);
const remaining = writable<[number, number, number]>([0, 0, 0]);
const remainingIndex = writable<number>(-1);
let intervalId: number | undefined;
function _showQuestion(_txt: string, _maxTime_: number): void {
_showAnswer([]);
globalThis.time = 0;
// maxTime = maxTime_;
// updateTime();
if (intervalId !== undefined) {
clearInterval(intervalId);
}
/*
intervalId = setInterval(function() {
if (!timerStopped) {
globalThis.time += 1;
//updateTime();
}
}, 1000);*/
}
function _showAnswer(info: AnswerButtonInfo[], _stopTimer = false): void {
console.log(info);
answerButtons.set(info);
// timerStopped = stopTimer;
}
function _updateRemaining(counts: [number, number, number], idx: number) {
remaining.set(counts);
remainingIndex.set(idx);
}
globalThis.showQuestion = _showQuestion;
globalThis.showAnswer = _showAnswer;
globalThis._updateRemaining = _updateRemaining;
/*
function selectedAnswerButton(): string | undefined {
const node = document.activeElement as HTMLElement;
if (!node) {
return;
}
return node.dataset.ease;
}
*/
// TODO This should probably be a "ready" command now that it is part of the actual reviewer,
// Currently this depends on this component mounting after the reviewer which it should but seems hacky.
// Maybe use a counter with a counter.subscribe($counter == 2 then call("ready"))
bridgeCommand("bottomReady");
return {
answerButtons,
remaining,
remainingIndex,
};
}

View file

@ -0,0 +1,9 @@
// Copyright: Ankitects Pty Ltd and contributors
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
export interface AnswerButtonInfo {
"extra": string;
"key": string;
"i": number;
"label": string;
"due": string;
}

View file

@ -0,0 +1,16 @@
<script lang="ts">
import type { Writable } from "svelte/store";
export let html: Writable<string>;
export let cardClass: Writable<string>;
</script>
<div id="qa" class={$cardClass}>
{@html $html}
</div>
<style lang="scss">
#qa {
flex: 1;
}
</style>

View file

@ -0,0 +1,18 @@
import { writable } from "svelte/store";
import { preloadAnswerImages } from "../../reviewer/images";
export function setupReviewer() {
const html = writable("");
const cardClass = writable("");
function showQuestion(q, a, cc) {
html.set(q);
cardClass.set(cc);
preloadAnswerImages(a);
}
globalThis._showAnswer = html.set;
globalThis._showQuestion = showQuestion;
return { html, cardClass };
}

View file

@ -0,0 +1,21 @@
<!--
Copyright: Ankitects Pty Ltd and contributors
License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
-->
<script lang="ts">
import { onMount } from "svelte";
import { setupReviewer } from "./reviewer";
import Reviewer from "./reviewer.svelte";
import "../../reviewer/reviewer.scss";
let reviewerInfo: null | ReturnType<typeof setupReviewer> = null;
onMount(() => {
reviewerInfo = setupReviewer();
});
</script>
{#if reviewerInfo}
<Reviewer {...reviewerInfo}></Reviewer>
{/if}