diff --git a/qt/aqt/deckbrowser.py b/qt/aqt/deckbrowser.py index a3641fd95..3b0b16666 100644 --- a/qt/aqt/deckbrowser.py +++ b/qt/aqt/deckbrowser.py @@ -65,7 +65,7 @@ class DeckBrowser: def _linkHandler(self, url): if ":" in url: - (cmd, arg) = url.split(":") + (cmd, arg) = url.split(":", 1) else: cmd = url if cmd == "open": diff --git a/rslib/backend.proto b/rslib/backend.proto index 392ed3388..1dcb9dac5 100644 --- a/rslib/backend.proto +++ b/rslib/backend.proto @@ -1092,7 +1092,6 @@ message GraphsOut { uint32 scheduler_version = 5; /// Seconds to add to UTC timestamps to get local time. int32 local_offset_secs = 7; - bool bridge_commands_supported = 8; } message GraphPreferences { @@ -1104,6 +1103,7 @@ message GraphPreferences { } Weekday calendar_first_day_of_week = 1; bool card_counts_separate_inactive = 2; + bool browser_links_supported = 3; } message RevlogEntry { diff --git a/rslib/src/stats/graphs.rs b/rslib/src/stats/graphs.rs index 73ae77836..704625575 100644 --- a/rslib/src/stats/graphs.rs +++ b/rslib/src/stats/graphs.rs @@ -44,7 +44,6 @@ impl Collection { next_day_at_secs: timing.next_day_at as u32, scheduler_version: self.sched_ver() as u32, local_offset_secs: local_offset_secs as i32, - bridge_commands_supported: true, }) } @@ -52,6 +51,7 @@ impl Collection { Ok(pb::GraphPreferences { calendar_first_day_of_week: self.get_first_day_of_week() as i32, card_counts_separate_inactive: self.get_card_counts_separate_inactive(), + browser_links_supported: true, }) } diff --git a/ts/graphs/AddedGraph.svelte b/ts/graphs/AddedGraph.svelte index 5ff7af8b9..ed30aec2b 100644 --- a/ts/graphs/AddedGraph.svelte +++ b/ts/graphs/AddedGraph.svelte @@ -9,6 +9,7 @@ import HistogramGraph from "./HistogramGraph.svelte"; import GraphRangeRadios from "./GraphRangeRadios.svelte"; import TableData from "./TableData.svelte"; + import { createEventDispatcher } from "svelte"; export let sourceData: pb.BackendProto.GraphsOut | null = null; export let i18n: I18n; @@ -17,13 +18,15 @@ let tableData: TableDatum[] = []; let graphRange: GraphRange = GraphRange.Month; + const dispatch = createEventDispatcher(); + let addedData: GraphData | null = null; $: if (sourceData) { addedData = gatherData(sourceData); } $: if (addedData) { - [histogramData, tableData] = buildHistogram(addedData, graphRange, i18n); + [histogramData, tableData] = buildHistogram(addedData, graphRange, i18n, dispatch); } const title = i18n.tr(i18n.TR.STATISTICS_ADDED_TITLE); diff --git a/ts/graphs/CardCounts.svelte b/ts/graphs/CardCounts.svelte index 55b1ea47f..6434de646 100644 --- a/ts/graphs/CardCounts.svelte +++ b/ts/graphs/CardCounts.svelte @@ -11,7 +11,7 @@ export let i18n: I18n; export let preferences: PreferenceStore; - let { cardCountsSeparateInactive } = preferences; + let { cardCountsSeparateInactive, browserLinksSupported } = preferences; const dispatch = createEventDispatcher(); let svg = null as HTMLElement | SVGElement | null; @@ -89,7 +89,7 @@ ■  - {#if sourceData.bridgeCommandsSupported} + {#if browserLinksSupported} dispatch('search', { query: d.query })}>{d.label} {:else} {d.label} diff --git a/ts/graphs/EaseGraph.svelte b/ts/graphs/EaseGraph.svelte index 89c728b44..559334e9f 100644 --- a/ts/graphs/EaseGraph.svelte +++ b/ts/graphs/EaseGraph.svelte @@ -6,15 +6,18 @@ import type { I18n } from "anki/i18n"; import type { TableDatum } from "./graph-helpers"; import TableData from "./TableData.svelte"; + import { createEventDispatcher } from "svelte"; export let sourceData: pb.BackendProto.GraphsOut | null = null; export let i18n: I18n; + const dispatch = createEventDispatcher(); + let histogramData = null as HistogramData | null; let tableData: TableDatum[] = []; $: if (sourceData) { - [histogramData, tableData] = prepareData(gatherData(sourceData), i18n); + [histogramData, tableData] = prepareData(gatherData(sourceData), i18n, dispatch); } const title = i18n.tr(i18n.TR.STATISTICS_CARD_EASE_TITLE); diff --git a/ts/graphs/FutureDue.svelte b/ts/graphs/FutureDue.svelte index a10560b8f..ffb27fe75 100644 --- a/ts/graphs/FutureDue.svelte +++ b/ts/graphs/FutureDue.svelte @@ -9,10 +9,13 @@ import HistogramGraph from "./HistogramGraph.svelte"; import GraphRangeRadios from "./GraphRangeRadios.svelte"; import TableData from "./TableData.svelte"; + import { createEventDispatcher } from "svelte"; export let sourceData: pb.BackendProto.GraphsOut | null = null; export let i18n: I18n; + const dispatch = createEventDispatcher(); + let graphData = null as GraphData | null; let histogramData = null as HistogramData | null; let tableData: TableDatum[] = [] as any; @@ -28,7 +31,8 @@ graphData, graphRange, backlog, - i18n + i18n, + dispatch, )); } diff --git a/ts/graphs/IntervalsGraph.svelte b/ts/graphs/IntervalsGraph.svelte index aca20542b..50d979be4 100644 --- a/ts/graphs/IntervalsGraph.svelte +++ b/ts/graphs/IntervalsGraph.svelte @@ -12,10 +12,13 @@ import HistogramGraph from "./HistogramGraph.svelte"; import type { TableDatum } from "./graph-helpers"; import TableData from "./TableData.svelte"; + import { createEventDispatcher } from "svelte"; export let sourceData: pb.BackendProto.GraphsOut | null = null; export let i18n: I18n; + const dispatch = createEventDispatcher(); + let intervalData: IntervalGraphData | null = null; let histogramData = null as HistogramData | null; let tableData: TableDatum[] = []; @@ -26,7 +29,7 @@ } $: if (intervalData) { - [histogramData, tableData] = prepareIntervalData(intervalData, range, i18n); + [histogramData, tableData] = prepareIntervalData(intervalData, range, i18n, dispatch); } const title = i18n.tr(i18n.TR.STATISTICS_INTERVALS_TITLE); diff --git a/ts/graphs/added.ts b/ts/graphs/added.ts index c22ea73a7..3574cf959 100644 --- a/ts/graphs/added.ts +++ b/ts/graphs/added.ts @@ -7,7 +7,7 @@ */ import type pb from "anki/backend_proto"; -import { extent, histogram, sum } from "d3-array"; +import { extent, histogram, sum, Bin } from "d3-array"; import { scaleLinear, scaleSequential } from "d3-scale"; import type { HistogramData } from "./histogram-graph"; import { interpolateBlues } from "d3-scale-chromatic"; @@ -28,10 +28,22 @@ export function gatherData(data: pb.BackendProto.GraphsOut): GraphData { return { daysAdded }; } +function makeQuery(start: number, end: number): string { + const include = `"added:${start}"`; + + if (start === 1) { + return include; + } + + const exclude = `-"added:${end}"`; + return `${include} AND ${exclude}`; +} + export function buildHistogram( data: GraphData, range: GraphRange, - i18n: I18n + i18n: I18n, + dispatch: any, ): [HistogramData | null, TableDatum[]] { // get min/max const total = data.daysAdded.length; @@ -102,17 +114,11 @@ export function buildHistogram( return `${day}:
${cards}
${total}: ${totalCards}`; } - function makeQuery(data: HistogramData, binIdx: number): string { - const start = Math.abs(data.bins[binIdx].x0!) + 1; - const include = `"added:${start}"`; - - if (start === 1) { - return include; - } - - const end = Math.abs(data.bins[binIdx].x1!) + 1; - const exclude = `-"added:${end}"`; - return `${include} AND ${exclude}`; + function onClick(bin: Bin): void { + const start = Math.abs(bin.x0!) + 1; + const end = Math.abs(bin.x1!) + 1; + const query = makeQuery(start, end); + dispatch("search", { query }); } return [ @@ -121,7 +127,7 @@ export function buildHistogram( bins, total: totalInPeriod, hoverText, - makeQuery, + onClick, colourScale, showArea: true, }, diff --git a/ts/graphs/calendar.ts b/ts/graphs/calendar.ts index fbc2fe964..0dae1a530 100644 --- a/ts/graphs/calendar.ts +++ b/ts/graphs/calendar.ts @@ -114,7 +114,6 @@ export function renderCalendar( maxCount = count; } } - console.log("sourceData", sourceData, dayMap); if (!maxCount) { setDataAvailable(svg, false); diff --git a/ts/graphs/ease.ts b/ts/graphs/ease.ts index 4f13e200f..a083960d4 100644 --- a/ts/graphs/ease.ts +++ b/ts/graphs/ease.ts @@ -7,7 +7,7 @@ */ import type pb from "anki/backend_proto"; -import { extent, histogram, sum } from "d3-array"; +import { extent, histogram, sum, Bin } from "d3-array"; import { scaleLinear, scaleSequential } from "d3-scale"; import { CardType } from "anki/cards"; import type { HistogramData } from "./histogram-graph"; @@ -26,9 +26,21 @@ export function gatherData(data: pb.BackendProto.GraphsOut): GraphData { return { eases }; } +function makeQuery(start: number, end: number): string { + if (start === end) { + return `"prop:ease=${start / 100}"`; + } + + const fromQuery = `"prop:ease>=${start / 100}"`; + const tillQuery = `"prop:ease<${end / 100}"`; + + return `${fromQuery} AND ${tillQuery}`; +} + export function prepareData( data: GraphData, - i18n: I18n + i18n: I18n, + dispatch: any, ): [HistogramData | null, TableDatum[]] { // get min/max const allEases = data.eases; @@ -61,19 +73,11 @@ export function prepareData( }); } - function makeQuery(data: HistogramData, binIdx: number): string { - const bin = data.bins[binIdx]; + function onClick(bin: Bin): void { const start = bin.x0!; const end = bin.x1! - 1; - - if (start === end) { - return `"prop:ease=${start / 100}"`; - } - - const fromQuery = `"prop:ease>=${start / 100}"`; - const tillQuery = `"prop:ease<${end / 100}"`; - - return `${fromQuery} AND ${tillQuery}`; + const query = makeQuery(start, end); + dispatch("search", { query }); } const xTickFormat = (num: number): string => `${num.toFixed(0)}%`; @@ -90,7 +94,7 @@ export function prepareData( bins, total, hoverText, - makeQuery, + onClick, colourScale, showArea: false, xTickFormat, diff --git a/ts/graphs/future-due.ts b/ts/graphs/future-due.ts index d1f09018e..498e8dff9 100644 --- a/ts/graphs/future-due.ts +++ b/ts/graphs/future-due.ts @@ -72,11 +72,23 @@ export interface FutureDueOut { tableData: TableDatum[]; } +function makeQuery(start: number, end: number): string { + if (start === end) { + return `"prop:due=${start}"`; + } + else { + const fromQuery = `"prop:due>=${start}"`; + const tillQuery = `"prop:due<=${end}"`; + return `${fromQuery} AND ${tillQuery}`; + } +} + export function buildHistogram( sourceData: GraphData, range: GraphRange, backlog: boolean, - i18n: I18n + i18n: I18n, + dispatch: any, ): FutureDueOut { const output = { histogramData: null, tableData: [] }; // get min/max @@ -145,19 +157,11 @@ export function buildHistogram( return `${days}:
${cards}
${totalLabel}: ${cumulative}`; } - function makeQuery(data: HistogramData, binIdx: number): string { - const bin = data.bins[binIdx]; + function onClick(bin: Bin): void { const start = bin.x0!; const end = bin.x1! - 1; - - if (start === end) { - return `"prop:due=${start}"`; - } - - const fromQuery = `"prop:due>=${start}"`; - const tillQuery = `"prop:due<=${end}"`; - - return `${fromQuery} AND ${tillQuery}`; + const query = makeQuery(start, end); + dispatch("search", { query }); } const periodDays = xMax! - xMin!; @@ -186,7 +190,7 @@ export function buildHistogram( bins, total, hoverText, - makeQuery, + onClick, showArea: true, colourScale, binValue, diff --git a/ts/graphs/histogram-graph.ts b/ts/graphs/histogram-graph.ts index 93f5ddad9..f22b31850 100644 --- a/ts/graphs/histogram-graph.ts +++ b/ts/graphs/histogram-graph.ts @@ -25,7 +25,7 @@ export interface HistogramData { cumulative: number, percent: number ) => string; - makeQuery?: (data: HistogramData, binIdx: number) => string; + onClick?: (data: Bin) => void; showArea: boolean; colourScale: ScaleSequential; binValue?: (bin: Bin) => number; @@ -36,7 +36,6 @@ export function histogramGraph( svgElem: SVGElement, bounds: GraphBounds, data: HistogramData | null, - dispatch: any ): void { const svg = select(svgElem); const trans = svg.transition().duration(600) as any; @@ -162,11 +161,9 @@ export function histogramGraph( }) .on("mouseout", hideTooltip); - if (data.makeQuery) { + if (data.onClick) { hoverzone .attr("class", "clickable") - .on("click", function (this: any, _d: any, idx: number) { - dispatch("search", { query: data.makeQuery!(data, idx) }); - }); + .on("click", data.onClick); } } diff --git a/ts/graphs/intervals.ts b/ts/graphs/intervals.ts index b31f88def..9d652eb2f 100644 --- a/ts/graphs/intervals.ts +++ b/ts/graphs/intervals.ts @@ -7,7 +7,7 @@ */ import type pb from "anki/backend_proto"; -import { extent, histogram, quantile, sum, mean } from "d3-array"; +import { extent, histogram, quantile, sum, mean, Bin } from "d3-array"; import { scaleLinear, scaleSequential } from "d3-scale"; import { CardType } from "anki/cards"; import type { HistogramData } from "./histogram-graph"; @@ -56,10 +56,22 @@ export function intervalLabel( } } +function makeQuery(start: number, end: number): string { + if (start === end) { + return `"prop:ivl=${start}"`; + } + + const fromQuery = `"prop:ivl>=${start}"`; + const tillQuery = `"prop:ivl<=${end}"`; + + return `${fromQuery} AND ${tillQuery}`; +} + export function prepareIntervalData( data: IntervalGraphData, range: IntervalRange, - i18n: I18n + i18n: I18n, + dispatch: any, ): [HistogramData | null, TableDatum[]] { // get min/max const allIntervals = data.intervals; @@ -134,19 +146,11 @@ export function prepareIntervalData( return `${interval}
${total}: \u200e${percent.toFixed(1)}%`; } - function makeQuery(data: HistogramData, binIdx: number): string { - const bin = data.bins[binIdx]; + function onClick(bin: Bin): void { const start = bin.x0!; const end = bin.x1! - 1; - - if (start === end) { - return `"prop:ivl=${start}"`; - } - - const fromQuery = `"prop:ivl>=${start}"`; - const tillQuery = `"prop:ivl<=${end}"`; - - return `${fromQuery} AND ${tillQuery}`; + const query = makeQuery(start, end); + dispatch("search", { query }); } const meanInterval = Math.round(mean(allIntervals) ?? 0); @@ -163,7 +167,7 @@ export function prepareIntervalData( bins, total: totalInPeriod, hoverText, - makeQuery, + onClick, colourScale, showArea: true, },