langcoach 0.3.0 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +156 -169
- package/dist/index.js +704 -6
- package/extension/assets/{sidepanel-DMjVhmRR.css → sidepanel-BBkB7IY_.css} +300 -41
- package/extension/background.js +2 -2
- package/extension/chunks/{translate-CM6Phtg_.js → translate-DvfZknmj.js} +59 -4
- package/extension/manifest.json +1 -1
- package/extension/sidepanel.html +2 -2
- package/extension/sidepanel.js +114 -150
- package/package.json +1 -1
|
@@ -1,39 +1,68 @@
|
|
|
1
1
|
:root {
|
|
2
|
-
|
|
2
|
+
/* Notebook tokens: exact copy of NOTEBOOK_TOKENS (@langcoach/core theme.ts); test/theme-tokens.test.ts keeps them in sync. */
|
|
3
|
+
--lc-paper: #faf7f2;
|
|
4
|
+
--lc-rule: #efe7da;
|
|
5
|
+
--lc-border: #eadfcd;
|
|
6
|
+
--lc-shadow: 0 3px 0 #eadfcd;
|
|
7
|
+
--lc-streak-tile: #fff1e0;
|
|
8
|
+
--lc-words-tile: #eef6e8;
|
|
9
|
+
--lc-sticker: #fde68a;
|
|
10
|
+
--lc-sticker-rotate: -6deg;
|
|
11
|
+
--lc-xp-from: #fb923c;
|
|
12
|
+
--lc-xp-to: #facc15;
|
|
13
|
+
--lc-learned-bg: #dcfce7;
|
|
14
|
+
--lc-learned-fg: #166534;
|
|
15
|
+
--lc-weak-bg: #ffe4e6;
|
|
16
|
+
--lc-weak-fg: #9f1239;
|
|
17
|
+
--lc-radius: 14px;
|
|
18
|
+
--lc-serif: Georgia, "Times New Roman", serif;
|
|
19
|
+
--lc-sans: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
|
|
20
|
+
|
|
21
|
+
--bg: var(--lc-paper);
|
|
3
22
|
--surface: #ffffff;
|
|
4
|
-
--surface-muted: #
|
|
5
|
-
--border:
|
|
6
|
-
--text: #
|
|
7
|
-
--text-muted: #
|
|
8
|
-
--primary: #
|
|
9
|
-
--primary-hover: #
|
|
10
|
-
--primary-soft:
|
|
11
|
-
--
|
|
12
|
-
--success
|
|
23
|
+
--surface-muted: #f6f0e6;
|
|
24
|
+
--border: var(--lc-border);
|
|
25
|
+
--text: #292524;
|
|
26
|
+
--text-muted: #6b5d4f;
|
|
27
|
+
--primary: #c2410c;
|
|
28
|
+
--primary-hover: #9a3412;
|
|
29
|
+
--primary-soft: var(--lc-streak-tile);
|
|
30
|
+
--on-primary: #ffffff;
|
|
31
|
+
--success: var(--lc-learned-fg);
|
|
32
|
+
--success-soft: var(--lc-learned-bg);
|
|
13
33
|
--warning: #b45309;
|
|
14
|
-
--danger:
|
|
15
|
-
--danger-soft:
|
|
16
|
-
--radius:
|
|
17
|
-
--shadow:
|
|
34
|
+
--danger: var(--lc-weak-fg);
|
|
35
|
+
--danger-soft: var(--lc-weak-bg);
|
|
36
|
+
--radius: var(--lc-radius);
|
|
37
|
+
--shadow: var(--lc-shadow);
|
|
38
|
+
--sticker-edge: #e5c76b;
|
|
39
|
+
--sticker-ink: #292524;
|
|
18
40
|
}
|
|
19
41
|
|
|
42
|
+
/* Warm dark variant of the same notebook: ink-stained paper, same roles. */
|
|
20
43
|
@media (prefers-color-scheme: dark) {
|
|
21
44
|
:root {
|
|
22
|
-
--
|
|
23
|
-
--
|
|
24
|
-
--
|
|
25
|
-
--
|
|
26
|
-
--
|
|
27
|
-
--
|
|
28
|
-
--
|
|
29
|
-
--
|
|
30
|
-
--
|
|
31
|
-
--
|
|
32
|
-
--
|
|
45
|
+
--lc-paper: #1c1814;
|
|
46
|
+
--lc-rule: #29221b;
|
|
47
|
+
--lc-border: #3d3329;
|
|
48
|
+
--lc-shadow: 0 3px 0 #0f0c09;
|
|
49
|
+
--lc-streak-tile: #33241a;
|
|
50
|
+
--lc-words-tile: #1f2a1d;
|
|
51
|
+
--lc-sticker: #fcd34d;
|
|
52
|
+
--lc-learned-bg: #173322;
|
|
53
|
+
--lc-learned-fg: #86efac;
|
|
54
|
+
--lc-weak-bg: #3b1a22;
|
|
55
|
+
--lc-weak-fg: #fda4af;
|
|
56
|
+
|
|
57
|
+
--surface: #26201a;
|
|
58
|
+
--surface-muted: #2f281f;
|
|
59
|
+
--text: #f3ece2;
|
|
60
|
+
--text-muted: #bcae9b;
|
|
61
|
+
--primary: #fb923c;
|
|
62
|
+
--primary-hover: #fdba74;
|
|
63
|
+
--on-primary: #1c1814;
|
|
33
64
|
--warning: #fbbf24;
|
|
34
|
-
--
|
|
35
|
-
--danger-soft: #3b1c22;
|
|
36
|
-
--shadow: 0 2px 12px rgba(0, 0, 0, 0.35);
|
|
65
|
+
--sticker-edge: #b45309;
|
|
37
66
|
}
|
|
38
67
|
}
|
|
39
68
|
|
|
@@ -609,8 +638,6 @@ button:focus-visible, select:focus-visible, input:focus-visible {
|
|
|
609
638
|
padding: 12px 16px;
|
|
610
639
|
border: none;
|
|
611
640
|
border-radius: 16px;
|
|
612
|
-
background: linear-gradient(135deg, #1a56db, #7c3aed);
|
|
613
|
-
color: #fff;
|
|
614
641
|
text-align: start;
|
|
615
642
|
box-shadow: 0 4px 0 rgba(0, 0, 0, 0.18);
|
|
616
643
|
transition: transform 150ms var(--px-ease), box-shadow 150ms var(--px-ease);
|
|
@@ -1233,7 +1260,6 @@ button:focus-visible, select:focus-visible, input:focus-visible {
|
|
|
1233
1260
|
--accent-violet-soft: #ede9fe;
|
|
1234
1261
|
--star: #b45309;
|
|
1235
1262
|
--focus: var(--primary);
|
|
1236
|
-
--brand-gradient: linear-gradient(135deg, #1a56db 0%, #7c3aed 100%);
|
|
1237
1263
|
}
|
|
1238
1264
|
@media (prefers-color-scheme: dark) {
|
|
1239
1265
|
:root {
|
|
@@ -1244,7 +1270,6 @@ button:focus-visible, select:focus-visible, input:focus-visible {
|
|
|
1244
1270
|
--accent-violet: #a78bfa;
|
|
1245
1271
|
--accent-violet-soft: #2a2148;
|
|
1246
1272
|
--star: #fbbf24;
|
|
1247
|
-
--brand-gradient: linear-gradient(135deg, #6b93f0 0%, #a78bfa 100%);
|
|
1248
1273
|
}
|
|
1249
1274
|
}
|
|
1250
1275
|
|
|
@@ -1264,13 +1289,6 @@ button:focus-visible, select:focus-visible, input:focus-visible {
|
|
|
1264
1289
|
.card {
|
|
1265
1290
|
border-radius: var(--radius-lg);
|
|
1266
1291
|
}
|
|
1267
|
-
.brand {
|
|
1268
|
-
background: var(--brand-gradient);
|
|
1269
|
-
-webkit-background-clip: text;
|
|
1270
|
-
background-clip: text;
|
|
1271
|
-
color: transparent;
|
|
1272
|
-
font-weight: 800;
|
|
1273
|
-
}
|
|
1274
1292
|
button,
|
|
1275
1293
|
.tab-btn,
|
|
1276
1294
|
.chip {
|
|
@@ -1563,8 +1581,6 @@ button:disabled {
|
|
|
1563
1581
|
border-radius: 20px;
|
|
1564
1582
|
display: grid;
|
|
1565
1583
|
place-items: center;
|
|
1566
|
-
background: var(--brand-gradient);
|
|
1567
|
-
color: #fff;
|
|
1568
1584
|
font-size: 28px;
|
|
1569
1585
|
font-weight: 800;
|
|
1570
1586
|
box-shadow: var(--shadow);
|
|
@@ -1727,3 +1743,246 @@ ul.ach-grid {
|
|
|
1727
1743
|
animation: none;
|
|
1728
1744
|
}
|
|
1729
1745
|
}
|
|
1746
|
+
|
|
1747
|
+
/* ───────────── Notebook look (sticker notebook, shared with the agent dashboard) ───────────── */
|
|
1748
|
+
:root {
|
|
1749
|
+
--radius-lg: var(--lc-radius);
|
|
1750
|
+
--focus: #c2410c;
|
|
1751
|
+
}
|
|
1752
|
+
@media (prefers-color-scheme: dark) {
|
|
1753
|
+
:root {
|
|
1754
|
+
--focus: #fdba74;
|
|
1755
|
+
}
|
|
1756
|
+
}
|
|
1757
|
+
|
|
1758
|
+
body {
|
|
1759
|
+
font-family: var(--lc-sans);
|
|
1760
|
+
background-color: var(--lc-paper);
|
|
1761
|
+
background-image: repeating-linear-gradient(transparent 0 27px, var(--lc-rule) 27px 28px);
|
|
1762
|
+
background-attachment: local;
|
|
1763
|
+
}
|
|
1764
|
+
|
|
1765
|
+
/* Header and tabs sit on the paper like a notebook's top strip. */
|
|
1766
|
+
.panel-header,
|
|
1767
|
+
.tab-bar {
|
|
1768
|
+
background: var(--lc-paper);
|
|
1769
|
+
border-bottom-color: var(--lc-border);
|
|
1770
|
+
}
|
|
1771
|
+
.brand {
|
|
1772
|
+
background: none;
|
|
1773
|
+
color: var(--text);
|
|
1774
|
+
font: italic 400 19px/1.2 var(--lc-serif);
|
|
1775
|
+
letter-spacing: 0;
|
|
1776
|
+
}
|
|
1777
|
+
.pill {
|
|
1778
|
+
background: var(--surface);
|
|
1779
|
+
border: 1px solid var(--lc-border);
|
|
1780
|
+
}
|
|
1781
|
+
/* One-line tabs: they share the width and scroll sideways only when a locale's labels are too long. */
|
|
1782
|
+
.tab-bar {
|
|
1783
|
+
display: flex;
|
|
1784
|
+
gap: 2px;
|
|
1785
|
+
padding: 6px 8px;
|
|
1786
|
+
overflow-x: auto;
|
|
1787
|
+
scrollbar-width: thin;
|
|
1788
|
+
}
|
|
1789
|
+
.tab-btn {
|
|
1790
|
+
flex: 1 0 auto;
|
|
1791
|
+
padding: 8px 6px;
|
|
1792
|
+
white-space: nowrap;
|
|
1793
|
+
overflow-wrap: normal;
|
|
1794
|
+
}
|
|
1795
|
+
.tab-btn[aria-current="page"] {
|
|
1796
|
+
background: var(--lc-sticker);
|
|
1797
|
+
color: var(--sticker-ink);
|
|
1798
|
+
box-shadow: 0 2px 0 var(--sticker-edge);
|
|
1799
|
+
}
|
|
1800
|
+
|
|
1801
|
+
/* Cards: white page cut-outs with a flat paper shadow. */
|
|
1802
|
+
.card,
|
|
1803
|
+
.stat-box,
|
|
1804
|
+
.task-item,
|
|
1805
|
+
.vocab-item,
|
|
1806
|
+
.scenario-card,
|
|
1807
|
+
.ach-item,
|
|
1808
|
+
.goal-option,
|
|
1809
|
+
.px-stat,
|
|
1810
|
+
.px-end-stat {
|
|
1811
|
+
border-color: var(--lc-border);
|
|
1812
|
+
box-shadow: var(--lc-shadow);
|
|
1813
|
+
}
|
|
1814
|
+
.card {
|
|
1815
|
+
border-radius: var(--lc-radius);
|
|
1816
|
+
}
|
|
1817
|
+
.task-item,
|
|
1818
|
+
.vocab-item,
|
|
1819
|
+
.stat-box,
|
|
1820
|
+
.ach-item {
|
|
1821
|
+
border-radius: 10px;
|
|
1822
|
+
}
|
|
1823
|
+
.vocab-item {
|
|
1824
|
+
background: var(--surface);
|
|
1825
|
+
}
|
|
1826
|
+
|
|
1827
|
+
h1,
|
|
1828
|
+
.level-name {
|
|
1829
|
+
font: italic 400 24px/1.2 var(--lc-serif);
|
|
1830
|
+
letter-spacing: 0.01em;
|
|
1831
|
+
}
|
|
1832
|
+
.level-name {
|
|
1833
|
+
font-size: 22px;
|
|
1834
|
+
}
|
|
1835
|
+
|
|
1836
|
+
button.primary,
|
|
1837
|
+
.chip.active,
|
|
1838
|
+
.play-big {
|
|
1839
|
+
color: var(--on-primary);
|
|
1840
|
+
}
|
|
1841
|
+
.chip.active .chip-count {
|
|
1842
|
+
background: color-mix(in srgb, var(--on-primary) 22%, transparent);
|
|
1843
|
+
color: var(--on-primary);
|
|
1844
|
+
}
|
|
1845
|
+
button:focus-visible,
|
|
1846
|
+
select:focus-visible,
|
|
1847
|
+
input:focus-visible,
|
|
1848
|
+
a:focus-visible,
|
|
1849
|
+
.tab-btn:focus-visible,
|
|
1850
|
+
.goal-option:focus-within {
|
|
1851
|
+
outline: 3px solid var(--focus);
|
|
1852
|
+
outline-offset: 2px;
|
|
1853
|
+
}
|
|
1854
|
+
/* Inset so the scrolling tab bar does not clip the ring. */
|
|
1855
|
+
.tab-btn:focus-visible {
|
|
1856
|
+
outline-offset: -3px;
|
|
1857
|
+
}
|
|
1858
|
+
|
|
1859
|
+
/* XP bars: orange to sun-yellow. */
|
|
1860
|
+
.progress-fill {
|
|
1861
|
+
background: linear-gradient(90deg, var(--lc-xp-from), var(--lc-xp-to));
|
|
1862
|
+
border-radius: 999px;
|
|
1863
|
+
}
|
|
1864
|
+
.progress-bar {
|
|
1865
|
+
background: color-mix(in srgb, var(--lc-border) 70%, var(--surface));
|
|
1866
|
+
}
|
|
1867
|
+
|
|
1868
|
+
/* Today: hero page with the streak as an orange tile. */
|
|
1869
|
+
.today-hero {
|
|
1870
|
+
background: var(--surface);
|
|
1871
|
+
align-items: stretch;
|
|
1872
|
+
}
|
|
1873
|
+
.streak-box {
|
|
1874
|
+
margin-top: 10px;
|
|
1875
|
+
padding: 10px 12px;
|
|
1876
|
+
border-radius: var(--lc-radius);
|
|
1877
|
+
background: var(--lc-streak-tile);
|
|
1878
|
+
border: 1px solid var(--lc-border);
|
|
1879
|
+
box-shadow: var(--lc-shadow);
|
|
1880
|
+
}
|
|
1881
|
+
.streak-count {
|
|
1882
|
+
font-size: 26px;
|
|
1883
|
+
font-weight: 800;
|
|
1884
|
+
color: var(--text);
|
|
1885
|
+
}
|
|
1886
|
+
.freeze-pill {
|
|
1887
|
+
background: var(--surface);
|
|
1888
|
+
border: 1px solid var(--lc-border);
|
|
1889
|
+
}
|
|
1890
|
+
.today-goal {
|
|
1891
|
+
align-content: center;
|
|
1892
|
+
}
|
|
1893
|
+
.task-item.done {
|
|
1894
|
+
background: var(--lc-learned-bg);
|
|
1895
|
+
border-color: color-mix(in srgb, var(--lc-learned-fg) 30%, var(--lc-learned-bg));
|
|
1896
|
+
color: var(--lc-learned-fg);
|
|
1897
|
+
}
|
|
1898
|
+
.task-item.done .muted {
|
|
1899
|
+
color: var(--lc-learned-fg);
|
|
1900
|
+
}
|
|
1901
|
+
.practice-cta {
|
|
1902
|
+
background: linear-gradient(135deg, var(--lc-xp-from), var(--lc-xp-to));
|
|
1903
|
+
color: #292524;
|
|
1904
|
+
border-radius: var(--lc-radius);
|
|
1905
|
+
box-shadow: 0 3px 0 #d97706;
|
|
1906
|
+
}
|
|
1907
|
+
.practice-cta:active {
|
|
1908
|
+
box-shadow: 0 1px 0 #d97706;
|
|
1909
|
+
}
|
|
1910
|
+
.practice-cta.fresh {
|
|
1911
|
+
background: var(--lc-words-tile);
|
|
1912
|
+
color: var(--text);
|
|
1913
|
+
border: 1px solid var(--lc-border);
|
|
1914
|
+
box-shadow: var(--lc-shadow);
|
|
1915
|
+
}
|
|
1916
|
+
.scenario-card {
|
|
1917
|
+
border-inline-start-color: var(--lc-xp-from);
|
|
1918
|
+
}
|
|
1919
|
+
.scenario-card.tone-1 { border-inline-start-color: var(--lc-learned-fg); }
|
|
1920
|
+
.scenario-card.tone-2 { border-inline-start-color: var(--lc-xp-to); }
|
|
1921
|
+
.rollover-banner {
|
|
1922
|
+
background: var(--lc-streak-tile);
|
|
1923
|
+
border-color: var(--lc-border);
|
|
1924
|
+
box-shadow: var(--lc-shadow);
|
|
1925
|
+
}
|
|
1926
|
+
|
|
1927
|
+
/* Words: learned / weak chips. */
|
|
1928
|
+
.status-tag.learned,
|
|
1929
|
+
.chip-mastered:not(.active),
|
|
1930
|
+
.btn-know.done,
|
|
1931
|
+
.btn-know:disabled {
|
|
1932
|
+
background: var(--lc-learned-bg);
|
|
1933
|
+
color: var(--lc-learned-fg);
|
|
1934
|
+
border-color: color-mix(in srgb, var(--lc-learned-fg) 25%, var(--lc-learned-bg));
|
|
1935
|
+
}
|
|
1936
|
+
.chip-weak:not(.active) {
|
|
1937
|
+
background: var(--lc-weak-bg);
|
|
1938
|
+
color: var(--lc-weak-fg);
|
|
1939
|
+
border-color: color-mix(in srgb, var(--lc-weak-fg) 25%, var(--lc-weak-bg));
|
|
1940
|
+
}
|
|
1941
|
+
.btn-know {
|
|
1942
|
+
background: var(--surface);
|
|
1943
|
+
color: var(--lc-learned-fg);
|
|
1944
|
+
border: 1px solid var(--lc-border);
|
|
1945
|
+
}
|
|
1946
|
+
.status-tag.new {
|
|
1947
|
+
background: var(--lc-streak-tile);
|
|
1948
|
+
color: var(--primary);
|
|
1949
|
+
border-color: var(--lc-border);
|
|
1950
|
+
}
|
|
1951
|
+
.show-more {
|
|
1952
|
+
background: var(--surface);
|
|
1953
|
+
border: 1px dashed var(--lc-border);
|
|
1954
|
+
}
|
|
1955
|
+
.words-search {
|
|
1956
|
+
border-color: var(--lc-border);
|
|
1957
|
+
}
|
|
1958
|
+
.strength.s3 .strength-bar.on { background: var(--lc-xp-from); }
|
|
1959
|
+
|
|
1960
|
+
/* Progress: the level is a tilted sticker. */
|
|
1961
|
+
.level-badge {
|
|
1962
|
+
background: var(--lc-sticker);
|
|
1963
|
+
color: var(--sticker-ink);
|
|
1964
|
+
border-radius: 10px;
|
|
1965
|
+
transform: rotate(var(--lc-sticker-rotate));
|
|
1966
|
+
box-shadow: 0 3px 0 var(--sticker-edge);
|
|
1967
|
+
}
|
|
1968
|
+
.stat-box:nth-child(1) { background: var(--lc-streak-tile); }
|
|
1969
|
+
.stat-box:nth-child(4) { background: var(--lc-words-tile); }
|
|
1970
|
+
.week-bar {
|
|
1971
|
+
background: var(--lc-streak-tile);
|
|
1972
|
+
border-color: var(--lc-xp-from);
|
|
1973
|
+
}
|
|
1974
|
+
.week-col.met .week-bar {
|
|
1975
|
+
background: linear-gradient(0deg, var(--lc-xp-from), var(--lc-xp-to));
|
|
1976
|
+
border-color: var(--lc-xp-from);
|
|
1977
|
+
}
|
|
1978
|
+
.week-goal-line {
|
|
1979
|
+
border-top-color: var(--text-muted);
|
|
1980
|
+
}
|
|
1981
|
+
.ach-item.unlocked {
|
|
1982
|
+
background: var(--lc-words-tile);
|
|
1983
|
+
border-color: var(--lc-border);
|
|
1984
|
+
}
|
|
1985
|
+
.goal-option.active {
|
|
1986
|
+
border-color: var(--primary);
|
|
1987
|
+
background: var(--lc-streak-tile);
|
|
1988
|
+
}
|
package/extension/background.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { C as toLocalDate, S as isoWeek, T as emptyState, _ as parseState, b as NotFoundError, c as version, g as isDue, l as DEFAULT_SETTINGS, m as progressOf, p as Coach, r as translationPair, s as suggestThemes, u as isMessage, v as CorruptDataError, y as InvalidDataError } from "./chunks/translate-DvfZknmj.js";
|
|
2
2
|
//#region src/shared/dev-hosts.ts
|
|
3
3
|
/**
|
|
4
4
|
* Local hosts the e2e build (and unit tests) accept in place of labs.google. Production builds define
|
|
@@ -874,7 +874,7 @@ function createRouter(deps) {
|
|
|
874
874
|
//#endregion
|
|
875
875
|
//#region src/background/bridge.ts
|
|
876
876
|
/**
|
|
877
|
-
* The local langcoach MCP server (loopback only;
|
|
877
|
+
* The local langcoach MCP server (loopback only, token-paired; protocol in packages/mcp/src/bridge.ts).
|
|
878
878
|
* The e2e build talks to a stub on another port, so tests never reach a real agent's data on this machine.
|
|
879
879
|
*/
|
|
880
880
|
var BRIDGE_ORIGIN = "http://127.0.0.1:47821";
|
|
@@ -7499,7 +7499,7 @@ function progressOf(state, now) {
|
|
|
7499
7499
|
}
|
|
7500
7500
|
//#endregion
|
|
7501
7501
|
//#region ../../packages/core/src/coach.ts
|
|
7502
|
-
var LEVELS = [
|
|
7502
|
+
var LEVELS$1 = [
|
|
7503
7503
|
"A1",
|
|
7504
7504
|
"A2",
|
|
7505
7505
|
"B1",
|
|
@@ -7602,7 +7602,7 @@ var Coach = class {
|
|
|
7602
7602
|
if (input.dailyGoalXp !== void 0 && !DAILY_GOALS.includes(input.dailyGoalXp)) throw new InvalidDataError(`dailyGoalXp must be one of ${DAILY_GOALS.join(", ")}`);
|
|
7603
7603
|
if (blank(input.nativeLanguage)) throw new InvalidDataError("nativeLanguage must be a non-empty string");
|
|
7604
7604
|
if (blank(input.targetLanguage)) throw new InvalidDataError("targetLanguage must be a non-empty string");
|
|
7605
|
-
if (!LEVELS.includes(input.level)) throw new InvalidDataError(`level must be one of ${LEVELS.join(", ")}`);
|
|
7605
|
+
if (!LEVELS$1.includes(input.level)) throw new InvalidDataError(`level must be one of ${LEVELS$1.join(", ")}`);
|
|
7606
7606
|
if (typeof input.goal !== "string") throw new InvalidDataError("goal must be a string");
|
|
7607
7607
|
if (!intIn(input.minutesPerDay, 5, 180)) throw new InvalidDataError("minutesPerDay must be an integer from 5 to 180");
|
|
7608
7608
|
if (!intIn(input.daysPerWeek, 1, 7)) throw new InvalidDataError("daysPerWeek must be an integer from 1 to 7");
|
|
@@ -8143,6 +8143,61 @@ var Coach = class {
|
|
|
8143
8143
|
};
|
|
8144
8144
|
}
|
|
8145
8145
|
};
|
|
8146
|
+
var LEVELS = [
|
|
8147
|
+
0,
|
|
8148
|
+
50,
|
|
8149
|
+
150,
|
|
8150
|
+
300,
|
|
8151
|
+
500,
|
|
8152
|
+
800,
|
|
8153
|
+
1200,
|
|
8154
|
+
1700,
|
|
8155
|
+
2400,
|
|
8156
|
+
3300
|
|
8157
|
+
].map((minXp, i) => ({
|
|
8158
|
+
level: i + 1,
|
|
8159
|
+
minXp,
|
|
8160
|
+
key: `level${i + 1}`
|
|
8161
|
+
}));
|
|
8162
|
+
function levelFor(totalXp) {
|
|
8163
|
+
const xp = Number.isFinite(totalXp) ? Math.max(0, totalXp) : 0;
|
|
8164
|
+
let i = 0;
|
|
8165
|
+
while (i + 1 < LEVELS.length && xp >= LEVELS[i + 1].minXp) i++;
|
|
8166
|
+
const cur = LEVELS[i];
|
|
8167
|
+
const next = LEVELS[i + 1];
|
|
8168
|
+
if (!next) return {
|
|
8169
|
+
...cur,
|
|
8170
|
+
nextMinXp: null,
|
|
8171
|
+
progress: 1,
|
|
8172
|
+
toNext: 0
|
|
8173
|
+
};
|
|
8174
|
+
const span = next.minXp - cur.minXp;
|
|
8175
|
+
return {
|
|
8176
|
+
...cur,
|
|
8177
|
+
nextMinXp: next.minXp,
|
|
8178
|
+
progress: Math.min(1, (xp - cur.minXp) / span),
|
|
8179
|
+
toNext: next.minXp - xp
|
|
8180
|
+
};
|
|
8181
|
+
}
|
|
8182
|
+
Object.freeze({
|
|
8183
|
+
paper: "#faf7f2",
|
|
8184
|
+
rule: "#efe7da",
|
|
8185
|
+
border: "#eadfcd",
|
|
8186
|
+
shadow: "0 3px 0 #eadfcd",
|
|
8187
|
+
streakTile: "#fff1e0",
|
|
8188
|
+
wordsTile: "#eef6e8",
|
|
8189
|
+
sticker: "#fde68a",
|
|
8190
|
+
stickerRotate: "-6deg",
|
|
8191
|
+
xpFrom: "#fb923c",
|
|
8192
|
+
xpTo: "#facc15",
|
|
8193
|
+
learnedBg: "#dcfce7",
|
|
8194
|
+
learnedFg: "#166534",
|
|
8195
|
+
weakBg: "#ffe4e6",
|
|
8196
|
+
weakFg: "#9f1239",
|
|
8197
|
+
radius: "14px",
|
|
8198
|
+
serif: "Georgia, \"Times New Roman\", serif",
|
|
8199
|
+
sans: "ui-sans-serif, system-ui, -apple-system, \"Segoe UI\", Roboto, sans-serif"
|
|
8200
|
+
});
|
|
8146
8201
|
//#endregion
|
|
8147
8202
|
//#region src/shared/messages.ts
|
|
8148
8203
|
var DEFAULT_SETTINGS = {
|
|
@@ -8189,7 +8244,7 @@ async function send(msg) {
|
|
|
8189
8244
|
}
|
|
8190
8245
|
//#endregion
|
|
8191
8246
|
//#region public/manifest.json
|
|
8192
|
-
var version = "0.
|
|
8247
|
+
var version = "0.4.1";
|
|
8193
8248
|
//#endregion
|
|
8194
8249
|
//#region src/lib/themes.ts
|
|
8195
8250
|
var GOALS = [
|
|
@@ -9109,4 +9164,4 @@ function createTranslatorCache(api, getGlobal = globalApi) {
|
|
|
9109
9164
|
};
|
|
9110
9165
|
}
|
|
9111
9166
|
//#endregion
|
|
9112
|
-
export {
|
|
9167
|
+
export { toLocalDate as C, isoWeek as S, emptyState as T, parseState as _, resolveLanguage as a, NotFoundError as b, version as c, send as d, levelFor as f, isDue as g, computeStreak as h, LLL_LANGUAGES as i, DEFAULT_SETTINGS as l, progressOf as m, nativeLanguageCode as n, GOALS as o, Coach as p, translationPair as r, suggestThemes as s, createTranslatorCache as t, isMessage as u, CorruptDataError as v, DAILY_GOALS as w, addDays as x, InvalidDataError as y };
|
package/extension/manifest.json
CHANGED
package/extension/sidepanel.html
CHANGED
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
6
6
|
<title>LangCoach</title>
|
|
7
7
|
<script type="module" crossorigin src="/sidepanel.js"></script>
|
|
8
|
-
<link rel="modulepreload" crossorigin href="/chunks/translate-
|
|
9
|
-
<link rel="stylesheet" crossorigin href="/assets/sidepanel-
|
|
8
|
+
<link rel="modulepreload" crossorigin href="/chunks/translate-DvfZknmj.js">
|
|
9
|
+
<link rel="stylesheet" crossorigin href="/assets/sidepanel-BBkB7IY_.css">
|
|
10
10
|
</head>
|
|
11
11
|
<body>
|
|
12
12
|
<div id="app"></div>
|