@flashlearnai/cli 0.1.0
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/LICENSE +21 -0
- package/README.md +122 -0
- package/THIRD_PARTY_NOTICES.txt +74 -0
- package/client/dist/assets/index-CPLGfjHO.css +1 -0
- package/client/dist/assets/index-CnfGuuA3.js +40 -0
- package/client/dist/flashlearn-icon.png +0 -0
- package/client/dist/index.html +13 -0
- package/dist/index.js +1840 -0
- package/package.json +39 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 FlashLearn contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
# FlashLearn
|
|
2
|
+
|
|
3
|
+
Agentic AI for compounding learning velocity. Turn unfamiliar repositories into
|
|
4
|
+
source-attributed study cards and review them in a local Teams-style interface.
|
|
5
|
+
|
|
6
|
+
## Install
|
|
7
|
+
|
|
8
|
+
Requires Node.js 22.14+ and Git on PATH for Git attribution.
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
npm install --global @flashlearnai/cli
|
|
12
|
+
flashlearn --help
|
|
13
|
+
flashlearn --version
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Or run without a global installation: `npx @flashlearnai/cli --help`.
|
|
17
|
+
The npm package is `@flashlearnai/cli`; the executable is `flashlearn`.
|
|
18
|
+
The package includes compiled JavaScript and the live UI. No workspace build,
|
|
19
|
+
TypeScript, or tsx installation is required.
|
|
20
|
+
|
|
21
|
+
## Quick start
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
flashlearn generate --project /path/to/repository
|
|
25
|
+
flashlearn start --project /path/to/repository
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Open `http://localhost:4173`. `generate` creates missing storage automatically;
|
|
29
|
+
`init` is optional storage-only setup and preserves existing data.
|
|
30
|
+
|
|
31
|
+
Every command defaults to the current working directory. Use `-p, --project`
|
|
32
|
+
before or after the command to target another directory for that invocation.
|
|
33
|
+
Relative paths resolve against the working directory.
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
flashlearn project show --project /path/to/repository
|
|
37
|
+
flashlearn project status -p /path/to/repository -o json
|
|
38
|
+
flashlearn question list -p /path/to/repository -o yaml
|
|
39
|
+
flashlearn question get CARD_ID -p /path/to/repository
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Queries accept `-o, --output text|json|yaml` (default text). Project diagnostics
|
|
43
|
+
and generation progress go to stderr, keeping query stdout parseable.
|
|
44
|
+
Run `flashlearn <command> --help` for details.
|
|
45
|
+
|
|
46
|
+
**Migration:** `FLASHLEARN_PROJECT` and old saved user configuration are ignored
|
|
47
|
+
and left untouched. `project set` has been removed and exits 2 with migration
|
|
48
|
+
guidance. Positional directories still work for `init [directory]`,
|
|
49
|
+
`generate [directory]`, and `start [directory]`, but cannot be combined with
|
|
50
|
+
`--project`. Repeated project flags are invalid.
|
|
51
|
+
|
|
52
|
+
## Empty decks and scoped generation
|
|
53
|
+
|
|
54
|
+
If `start` finds an empty deck, an interactive terminal asks whether to generate
|
|
55
|
+
cards first (default no), noting that a configured AI endpoint may be used.
|
|
56
|
+
Non-interactive runs exit 1 with guidance unless `start --yes` (or `-y`) approves
|
|
57
|
+
generation. Existing cards are not regenerated. Failed generation or a still-empty
|
|
58
|
+
deck prevents server startup.
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
flashlearn start --project /path/to/repository --yes
|
|
62
|
+
flashlearn generate --project /path/to/repository --subpath src --max-files 20
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
`--subpath` is a repository-relative directory, not an individual file; absolute
|
|
66
|
+
paths and `..` segments are invalid. `--max-files` is a positive safe integer
|
|
67
|
+
limiting supported files scanned, not cards. Both flags belong to `generate`
|
|
68
|
+
only; run it before `start` to scope first-run generation. By default generation
|
|
69
|
+
scans the whole project with no file limit. Attribution stays relative to the
|
|
70
|
+
project root.
|
|
71
|
+
|
|
72
|
+
Generation reports new or updated cards and the total available for study. It
|
|
73
|
+
upserts without pruning existing cards outside the scan. Producing zero cards
|
|
74
|
+
can succeed if a previous deck remains; no available study cards means exit 1.
|
|
75
|
+
Exit codes are 0 for success, 1 for operation failure, and 2 for invalid arguments.
|
|
76
|
+
|
|
77
|
+
## Live study
|
|
78
|
+
|
|
79
|
+
The local UI uses `GET /api/cards/next` to select each due card on the server;
|
|
80
|
+
`GET /api/cards` supplies deck counts. There is no live topic filter. Recall the
|
|
81
|
+
answer, use **Reveal answer** (`GET /api/cards/:id`), then rate it incorrect,
|
|
82
|
+
hard, correct, or easy. Sessions stop after 12 acknowledged reviews; incorrect
|
|
83
|
+
cards may be due again immediately and repeats count toward the cap.
|
|
84
|
+
|
|
85
|
+
Ratings are saved through `POST /api/review`. The UI shows **Saving review…**
|
|
86
|
+
until confirmation, displays the server's due date, and waits for **Next due card**.
|
|
87
|
+
Failed or malformed acknowledgements block advancement and new sessions and let
|
|
88
|
+
you retry the same rating. A lost response may mean the save already succeeded;
|
|
89
|
+
without an idempotency key, retrying can record the rating twice.
|
|
90
|
+
|
|
91
|
+
Saved schedules survive reloads, while the session transcript resets. The server
|
|
92
|
+
selects due cards again rather than replaying future cards. No cards due (a `404`
|
|
93
|
+
from the next-card endpoint) is distinct from an empty project.
|
|
94
|
+
|
|
95
|
+
## Local data and endpoints
|
|
96
|
+
|
|
97
|
+
Cards, review state, and settings are stored in the project's `.flashlearn/`.
|
|
98
|
+
Keep that directory out of version control. The server binds to localhost by
|
|
99
|
+
default on port 4173; use `start --host <host> --port 4180` to override the bind
|
|
100
|
+
address and port. Wildcard hosts `0.0.0.0` and `::` are rejected.
|
|
101
|
+
|
|
102
|
+
Without endpoint configuration, generation uses deterministic source/documentation
|
|
103
|
+
extractors. Setting both `FLASHLEARN_ENDPOINT_URL` and `FLASHLEARN_ENDPOINT_MODEL`
|
|
104
|
+
enables a chat-completions endpoint. Code is sent to that configured endpoint;
|
|
105
|
+
its access controls and retention policy are separate from local repository
|
|
106
|
+
permissions. Keep endpoint credentials out of Git and public demo builds.
|
|
107
|
+
|
|
108
|
+
The GitHub Pages showcase uses only public hand-authored samples in multiple-choice
|
|
109
|
+
sessions of up to 12 cards. Selected topics share the slots, with the starting topic
|
|
110
|
+
rotating when topics outnumber slots and each topic advancing by cards actually
|
|
111
|
+
dealt. Ratings and topic/card cursors are session-only and reset on reload. It does
|
|
112
|
+
not read your project, contact an API, or save a learning-engine schedule.
|
|
113
|
+
|
|
114
|
+
## Upgrade and support
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
npm install --global @flashlearnai/cli
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
Report reproducible issues at https://github.com/flashlearn-ai/flashlearn/issues
|
|
121
|
+
with the CLI version and Node version. Do not attach private source, generated
|
|
122
|
+
decks, or credentials. MIT licensed; see LICENSE and THIRD_PARTY_NOTICES.txt.
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
react
|
|
2
|
+
MIT License
|
|
3
|
+
|
|
4
|
+
Copyright (c) Facebook, Inc. and its affiliates.
|
|
5
|
+
|
|
6
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
7
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
8
|
+
in the Software without restriction, including without limitation the rights
|
|
9
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
10
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
11
|
+
furnished to do so, subject to the following conditions:
|
|
12
|
+
|
|
13
|
+
The above copyright notice and this permission notice shall be included in all
|
|
14
|
+
copies or substantial portions of the Software.
|
|
15
|
+
|
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
17
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
18
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
19
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
20
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
21
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
22
|
+
SOFTWARE.
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
react-dom
|
|
28
|
+
MIT License
|
|
29
|
+
|
|
30
|
+
Copyright (c) Facebook, Inc. and its affiliates.
|
|
31
|
+
|
|
32
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
33
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
34
|
+
in the Software without restriction, including without limitation the rights
|
|
35
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
36
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
37
|
+
furnished to do so, subject to the following conditions:
|
|
38
|
+
|
|
39
|
+
The above copyright notice and this permission notice shall be included in all
|
|
40
|
+
copies or substantial portions of the Software.
|
|
41
|
+
|
|
42
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
43
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
44
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
45
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
46
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
47
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
48
|
+
SOFTWARE.
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
scheduler
|
|
54
|
+
MIT License
|
|
55
|
+
|
|
56
|
+
Copyright (c) Facebook, Inc. and its affiliates.
|
|
57
|
+
|
|
58
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
59
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
60
|
+
in the Software without restriction, including without limitation the rights
|
|
61
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
62
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
63
|
+
furnished to do so, subject to the following conditions:
|
|
64
|
+
|
|
65
|
+
The above copyright notice and this permission notice shall be included in all
|
|
66
|
+
copies or substantial portions of the Software.
|
|
67
|
+
|
|
68
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
69
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
70
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
71
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
72
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
73
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
74
|
+
SOFTWARE.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
:root{--n-0:#ffffff;--n-2:#faf9f8;--n-4:#f4f3f1;--line:#e4e4e8;--line-2:#eeeef2;--sep:#c9ccd4;--ink:#1b1a19;--ink-2:#3b3a39;--muted:#5f5e5c;--faint:#8a8886;--teams:#5b5fc7;--teams-soft:#edecfa;--forest:#0e2417;--emerald:#12965a;--emerald-2:#0c7a48;--mint:#8ff0bd;--azure:#3f63d6;--azure-2:#2b46a0;--grad-dark:linear-gradient(140deg,var(--forest),#132a55);--emerald-soft:#e7f5ec;--emerald-line:#c9e8d6;--ok:#12965a;--ok-soft:#e7f5ec;--bad:#c4314b;--bad-soft:#fdeef1;--avail:#3aa657;--busy:#c50f1f;--away:#f2b73d;--sans:"Segoe UI",system-ui,-apple-system,Roboto,sans-serif;--serif:"Segoe UI",system-ui,-apple-system,Roboto,sans-serif;--mono:ui-monospace,"Cascadia Code",SFMono-Regular,Menlo,monospace;--rk:9px 3px 9px 3px;--ease:cubic-bezier(.22,.8,.28,1);--spring:cubic-bezier(.34,1.42,.5,1);--sh-1:0 1px 2px rgba(0,0,0,.05);--sh-2:0 10px 26px -14px rgba(14,36,23,.32)}*{box-sizing:border-box}html,body,#root{height:100%}body{margin:0;font-family:var(--sans);color:var(--ink);background:radial-gradient(140% 120% at 50% -8%,#dfe1ec,#bfc2d3 55%,#adb0c3);-webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility}button{font-family:inherit;cursor:pointer;border:none;background:none;color:inherit}img{-webkit-user-drag:none;-webkit-user-select:none;user-select:none}::selection{background:var(--emerald-soft)}.stage{height:100vh;display:grid;place-items:center;padding:26px}.window{width:min(1320px,100%);height:min(816px,calc(100vh - 52px));display:flex;background:var(--n-0);border-radius:16px;overflow:hidden;box-shadow:0 0 0 1px #1820360f,0 2px 6px #18203614,0 36px 70px -30px #0c162899}.rail{width:68px;background:var(--n-2);border-right:1px solid var(--sep);display:flex;flex-direction:column;align-items:stretch;padding-top:8px;gap:2px}.rail-item{display:flex;flex-direction:column;align-items:center;gap:3px;padding:8px 0;color:var(--muted);font-size:9.5px;position:relative;transition:color .15s}.rail-item:hover{color:var(--ink)}.rail-item.on{color:var(--teams)}.rail-item.on:before{content:"";position:absolute;left:0;top:8px;bottom:8px;width:3px;border-radius:0 3px 3px 0;background:var(--teams)}.rail-me{margin-top:auto;margin-bottom:12px;display:grid;place-items:center}.list{width:296px;background:var(--n-0);border-right:1px solid var(--sep);display:flex;flex-direction:column;flex-shrink:0}.list-top{padding:14px 16px 6px;display:flex;align-items:center;justify-content:space-between}.list-top h1{font-size:19px;font-weight:700;margin:0;letter-spacing:-.3px}.list-top .tools{display:flex;gap:12px;color:var(--muted)}.search{margin:2px 12px 8px;background:var(--n-4);border-radius:8px;padding:8px 11px;display:flex;align-items:center;gap:8px;color:var(--faint);font-size:12.5px}.tabs{display:flex;gap:16px;padding:2px 16px 8px;font-size:12.5px;color:var(--muted)}.tabs .on{color:var(--ink);font-weight:600;border-bottom:2px solid var(--teams);padding-bottom:6px}.chat{display:flex;gap:11px;align-items:center;padding:9px 12px;margin:0 6px;border-radius:8px;transition:background .15s}.chat:hover{background:var(--n-4)}.chat.on{background:var(--teams-soft)}.chat .meta{flex:1;min-width:0}.chat .row1{display:flex;justify-content:space-between;align-items:baseline;gap:8px}.chat .name{font-weight:600;font-size:13.5px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.chat.on .name{color:var(--teams)}.chat .time{font-size:11px;color:var(--faint);flex-shrink:0}.chat .prev{font-size:12px;color:var(--muted);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.chat .prev .you{color:var(--faint)}.chat.unread .prev{color:var(--ink);font-weight:600}.chat.unread .pip{width:7px;height:7px;border-radius:50%;background:var(--teams);flex-shrink:0}.avatar{border-radius:50%;display:flex;align-items:center;justify-content:center;color:#fff;font-weight:700;flex-shrink:0;position:relative;overflow:visible}.avatar .dot{position:absolute;right:-1px;bottom:-1px;width:11px;height:11px;border-radius:50%;border:2px solid #fff}.avatar.bot{background:var(--grad-dark)}.avatar.bot img{border-radius:50%}.conv{flex:1;display:flex;flex-direction:column;min-width:0;background:var(--n-0)}.conv-head{height:58px;border-bottom:1px solid var(--sep);padding:0 18px;display:flex;align-items:center;gap:11px}.conv-head .who{display:flex;flex-direction:column;gap:1px}.conv-head .title-row{display:flex;align-items:center;gap:7px}.conv-head .title{font-weight:700;font-size:15px}.conv-head .sub{font-size:11.5px;color:var(--muted);display:flex;align-items:center;gap:6px}.conv-head .sub .p{width:8px;height:8px;border-radius:50%;background:var(--avail);box-shadow:0 0 0 2px #3aa6572e}.badge{font-size:9px;font-weight:700;color:var(--emerald-2);background:var(--emerald-soft);border:1px solid var(--emerald-line);padding:1px 5px;border-radius:4px;letter-spacing:.4px}.conv-head .actions{margin-left:auto;display:flex;gap:18px;color:var(--muted)}.conv-head .actions svg{transition:color .15s}.conv-head .actions svg:hover{color:var(--ink)}.conv-tabs{height:42px;border-bottom:1px solid var(--sep);padding:0 18px;display:flex;gap:22px;align-items:center;font-size:13px;color:var(--muted)}.conv-tabs .on{color:var(--ink);font-weight:600;height:100%;display:flex;align-items:center;border-bottom:2px solid var(--teams)}.messages{flex:1;overflow-y:auto;padding:22px max(24px,calc((100% - 880px)/2)) 8px;display:flex;flex-direction:column;gap:16px;background:#f4f6f9;scroll-behavior:smooth}.divider{align-self:stretch;display:flex;align-items:center;gap:14px;color:var(--muted);font-size:12px;font-weight:500;margin:2px}.divider:before,.divider:after{content:"";height:1px;background:var(--sep);flex:1}.composer{margin:10px auto 16px;width:min(880px,calc(100% - 36px));border:1px solid var(--line);border-radius:12px;background:#fff;box-shadow:var(--sh-1)}.composer .field{padding:11px 14px;color:var(--faint);font-size:13px}.composer .bar{border-top:1px solid var(--line-2);padding:6px 12px;display:flex;align-items:center;gap:15px;color:var(--muted)}.composer .bar .send{margin-left:auto;color:var(--emerald)}.composer .bar .aa{font-weight:700;font-size:13px}.composer .bar .gif{font-size:11px;font-weight:800;letter-spacing:.5px}.msg{display:flex;gap:11px;animation:rise .5s var(--ease) both}.msg .who{width:32px;height:32px;flex-shrink:0}.msg .body{min-width:0;flex:1}.msg .head{font-size:11.5px;margin-bottom:5px;color:var(--muted)}.msg .head b{color:var(--ink)}.bubble{background:#fff;border:1px solid var(--line-2);border-radius:4px 14px 14px;padding:10px 13px;font-size:13.5px;line-height:1.45;display:inline-block;max-width:80%;box-shadow:var(--sh-1)}.user-msg{align-self:flex-end;max-width:70%;display:flex;flex-direction:column;align-items:flex-end;animation:riseR .45s var(--ease) both}.user-bubble{background:var(--emerald-soft);border:1px solid var(--emerald-line);color:#0c3d25;border-radius:14px 14px 2px;padding:9px 13px;font-size:13px;font-weight:600}.typing{display:inline-flex;gap:4px;padding:12px 14px;background:#fff;border:1px solid var(--line-2);border-radius:4px 14px 14px;box-shadow:var(--sh-1)}.typing i{width:7px;height:7px;border-radius:50%;background:#c7ccc9;animation:blink 1.2s infinite}.typing i:nth-child(2){animation-delay:.2s}.typing i:nth-child(3){animation-delay:.4s}.welcome{max-width:432px;border-radius:28px 10px;overflow:hidden;border:1px solid #143122;background:radial-gradient(125% 112% at 6% 0%,#173d29,#0c2419 55%,#0e2340);color:#fff;box-shadow:var(--sh-2);position:relative;animation:cardIn .55s var(--spring) both}.welcome:after{content:"";position:absolute;top:0;right:0;bottom:0;left:0;background-image:radial-gradient(circle at 1px 1px,rgba(143,240,189,.14) 1px,transparent 0);background-size:18px 18px;opacity:.5;pointer-events:none}.welcome .inner{position:relative;padding:26px 24px 24px}.welcome .mk{width:58px;height:58px;border-radius:18px 6px;overflow:hidden;background:var(--grad-dark);display:grid;place-items:center;box-shadow:0 8px 22px -8px #0009;border:1px solid #204634}.welcome h2{font-family:var(--serif);font-weight:600;font-size:27px;margin:16px 0 6px;letter-spacing:-.4px}.welcome p{color:#bfe9cf;font-size:13.5px;line-height:1.5;margin:0 0 20px;max-width:34ch}.welcome .cta{display:inline-flex;align-items:center;gap:9px;background:var(--mint);color:#0b1c12;font-weight:700;font-size:14px;padding:12px 20px;border-radius:14px 6px;transition:transform .18s var(--spring),box-shadow .18s}.welcome .cta:hover{transform:translateY(-2px);box-shadow:0 12px 26px -12px #8ff0bd99}.welcome .meta{display:flex;gap:18px;margin-top:18px;color:#8db8a1;font-size:11.5px}.welcome .meta b{color:#eafff3;font-family:var(--serif);font-size:15px}.pick{max-width:400px;border-radius:26px 8px;overflow:hidden;border:1px solid var(--line);box-shadow:var(--sh-2);background:#fff;animation:cardIn .5s var(--spring) both}.pick-head{padding:16px 18px 6px;display:flex;align-items:flex-start;justify-content:space-between}.pick-head .psub{display:block;font-size:11.5px;color:var(--faint);margin-top:3px}.pick-head .t{font-family:var(--serif);font-size:19px;font-weight:600;color:var(--forest);letter-spacing:-.3px}.selall{font-size:11px;color:var(--emerald-2);background:var(--emerald-soft);border:1px solid var(--emerald-line);border-radius:10px 4px;padding:5px 11px;font-weight:700;transition:.15s}.selall:hover{background:#d7f0e0}.pick-body{padding:12px 18px 18px}.topics{display:flex;flex-direction:column;gap:9px}.topic{display:flex;align-items:center;gap:12px;border:1.5px solid var(--line);padding:12px 13px;text-align:left;width:100%;background:#fff;transition:transform .16s var(--spring),border-color .15s,background .15s,box-shadow .16s}.topic:nth-child(odd){border-radius:16px 6px}.topic:nth-child(2n){border-radius:6px 16px}.topic:hover{border-color:var(--a);transform:translateY(-1px);box-shadow:0 12px 24px -16px color-mix(in srgb,var(--a) 75%,transparent)}.topic.sel{border-color:var(--a);background:color-mix(in srgb,var(--a) 6%,#fff)}.tglyph{width:36px;height:36px;border-radius:11px 4px;display:grid;place-items:center;color:var(--a);background:color-mix(in srgb,var(--a) 13%,#fff);flex-shrink:0;transition:.16s}.topic.sel .tglyph{background:var(--a);color:#fff}.tmain{flex:1;min-width:0;display:flex;flex-direction:column;gap:4px}.tlabel{font-weight:600;font-size:14px;color:var(--ink)}.tmastery{display:flex;align-items:center;gap:8px}.tmastery em{font-style:normal;font-size:10.5px;color:var(--faint)}.tcheck{width:24px;height:24px;border-radius:50%;border:1.6px solid #cdd3cd;display:grid;place-items:center;color:#fff;flex-shrink:0;transition:.16s}.topic.sel .tcheck{background:var(--a);border-color:var(--a);animation:pop .3s var(--spring)}.lone{margin:0 0 4px;font-size:15px;line-height:1.5;color:var(--ink);background:var(--emerald-soft);border:1px solid var(--emerald-line);border-radius:12px 4px;padding:12px 14px}.welcome .cmd{margin:14px 0 0;font-family:var(--mono);font-size:12.5px;color:var(--mint);background:#00000047;border:1px solid rgba(143,240,189,.22);border-radius:8px 3px;padding:9px 12px;display:inline-block}.capped{margin:9px 2px 0;font-size:11px;line-height:1.5;color:var(--faint);text-align:center}.start{width:100%;margin-top:15px;background:var(--grad-dark);color:#fff;border:1.5px solid transparent;border-radius:16px 6px;padding:13px;font-weight:600;font-size:14px;display:flex;align-items:center;justify-content:center;gap:8px;transition:transform .18s var(--spring),box-shadow .18s,background .2s,color .2s,border-color .2s}.start:not(:disabled):hover{transform:translateY(-2px);box-shadow:0 14px 30px -14px #1f4fb08c}.start:disabled{background:transparent;color:var(--faint);border-color:var(--line);cursor:default}.qcard{position:relative;max-width:432px;padding:20px 20px 18px;background:linear-gradient(180deg,#fff,#fcfdfb);border:1px solid var(--line);border-radius:28px 8px;box-shadow:var(--sh-2),inset 0 1px #fff;overflow:hidden;animation:cardIn .5s var(--spring) both}.qtop{position:relative;display:flex;align-items:center;justify-content:space-between}.qbrand{display:flex;align-items:center;gap:8px;font-weight:600;font-size:12px;color:var(--forest)}.qbrand .m{width:22px;height:22px;border-radius:8px 3px;overflow:hidden;background:var(--grad-dark);display:grid;place-items:center}.qmeta{position:relative;display:flex;align-items:center;gap:11px;margin:15px 0 3px}.qmeta .frac{font-family:var(--mono);font-size:11px;color:var(--faint);letter-spacing:.5px;flex-shrink:0}.qseg{flex:1;display:flex;gap:3px}.qseg>i{flex:1;height:5px;border-radius:2px;background:#e6ebe6;transition:background .4s}.qseg>i.done{background:var(--emerald)}.qseg>i.cur{background:var(--azure)}.qtext{position:relative;font-family:var(--serif);font-weight:700;font-size:22px;line-height:1.24;color:var(--forest);letter-spacing:-.4px;margin:12px 0 16px}.qchoices{position:relative;display:flex;flex-direction:column;gap:9px}.qchoice{display:flex;align-items:center;gap:13px;padding:13px 15px;border:1.5px solid var(--line);background:#fff;text-align:left;width:100%;font-size:14.5px;color:var(--ink);transition:transform .18s var(--spring),border-color .16s,box-shadow .16s,background .16s;animation:choiceIn .42s var(--spring) both}.qc-text{flex:1}.qc-mark{width:22px;height:22px;border-radius:50%;display:grid;place-items:center;color:#fff;flex-shrink:0;animation:pop .3s var(--spring)}.qc-mark.ok{background:var(--ok)}.qc-mark.no{background:var(--bad)}.qchoice:nth-child(odd){border-radius:16px 6px}.qchoice:nth-child(2n){border-radius:6px 16px}.qchoice:nth-child(1){animation-delay:.06s}.qchoice:nth-child(2){animation-delay:.13s}.qchoice:nth-child(3){animation-delay:.2s}.qchoice:nth-child(4){animation-delay:.27s}.qchoice:not(:disabled):hover{border-color:var(--emerald);transform:translateY(-2px);box-shadow:0 12px 24px -14px #12965a99}.qchoice:disabled{cursor:default}.qchoice .k{width:27px;height:27px;border-radius:var(--rk);background:var(--n-4);color:var(--emerald-2);font-weight:800;font-size:12px;display:grid;place-items:center;flex-shrink:0;border:1px solid var(--line);transition:.16s}.qchoice:not(:disabled):hover .k{background:var(--forest);color:var(--mint)}.qchoice.correct{border-color:var(--ok);background:var(--ok-soft);font-weight:600}.qchoice.correct .k{background:var(--ok);color:#fff;animation:pop .34s var(--spring)}.qchoice.wrong{border-color:var(--bad);background:var(--bad-soft);font-weight:600}.qchoice.wrong .k{background:var(--bad);color:#fff}.qchoice.dim{opacity:.4}.confused{margin:14px 0 0;font-size:12.5px;line-height:1.55;color:var(--muted)}.confused b{color:var(--ink)}.qsrc{position:relative;display:flex;align-items:center;gap:7px;width:100%;font-size:10.5px;color:var(--faint);margin:16px 0 0;padding:12px 0 0;border-top:1px dashed var(--line);font-family:var(--mono);background:none;text-align:left;transition:color .15s}.qsrc:hover{color:var(--muted)}.qsrc .chev{margin-left:auto;transition:transform .22s var(--ease)}.qsrc.open .chev{transform:rotate(180deg)}.qcode{margin-top:11px;border-radius:12px 4px;overflow:hidden;border:1px solid #17301f;box-shadow:0 12px 26px -18px #0e2417a6;animation:codeIn .3s var(--ease) both}.qcode-h{background:#0b1c12;color:#9cc7b0;padding:8px 12px;font-family:var(--mono);font-size:10px;display:flex;align-items:center;gap:7px;border-bottom:1px solid #17301f;letter-spacing:.2px}.qcode-h .g{color:var(--mint)}.qcode pre{margin:0;padding:12px 14px;background:#0e2417;color:#cfe9db;font-family:var(--mono);font-size:11.5px;line-height:1.6;white-space:pre-wrap;word-break:break-word}@keyframes codeIn{0%{opacity:0;transform:translateY(-6px)}to{opacity:1;transform:none}}.qsrc .sha{color:var(--emerald-2);background:var(--emerald-soft);border-radius:5px;padding:1px 6px}.grade{margin-top:15px}.grade-q{font-size:11.5px;color:var(--muted);font-weight:600;letter-spacing:.2px;margin-bottom:9px}.grade-btns{display:grid;grid-template-columns:1fr 1fr;gap:7px}.g-primary{width:100%;margin-top:8px;display:flex;align-items:center;justify-content:center;gap:8px;padding:12px;border:none;border-radius:14px 6px;background:var(--grad-dark);color:#fff;font-weight:600;font-size:13.5px;transition:transform .15s var(--spring),box-shadow .18s}.g-primary:hover{transform:translateY(-1px);box-shadow:0 12px 26px -14px #122a5280}.g{display:flex;flex-direction:column;align-items:center;gap:3px;padding:10px 4px;border:1.5px solid var(--line);border-radius:12px 4px;font-size:12.5px;font-weight:700;background:#fff;color:var(--ink);transition:transform .15s var(--spring),border-color .15s,box-shadow .15s,background .15s}.g em{font-style:normal;font-size:9.5px;color:var(--faint);font-weight:600}.g:hover{transform:translateY(-2px);box-shadow:var(--sh-1)}.g-hard:hover{border-color:#c78a1f;background:#fdf3e1;color:#a3701a}.g-easy:hover{border-color:var(--azure);background:#eef2fd;color:var(--azure-2)}.graded{margin-top:13px;font-size:12px;color:var(--muted)}.graded.unrecorded{color:var(--bad)}.qtext,.lone,.qsrc{overflow-wrap:anywhere}.results{position:relative;max-width:420px;border-radius:28px 8px;overflow:hidden;border:1px solid var(--line);box-shadow:var(--sh-2);background:linear-gradient(180deg,#fff,#fbfdfb);padding:22px;text-align:left;animation:cardIn .5s var(--spring) both}.results-top{display:flex;align-items:center;gap:16px}.results .ring .val{font-family:var(--serif);font-size:26px;font-weight:700;color:var(--forest)}.results-head h3{font-family:var(--serif);font-size:19px;color:var(--forest);margin:0 0 4px}.results-head .sub{color:var(--muted);font-size:12.5px;line-height:1.5}.missed{display:flex;flex-direction:column;gap:8px}.miss{display:flex;align-items:center;gap:10px;font-size:12.5px;color:var(--ink-2)}.miss .mx{width:20px;height:20px;flex-shrink:0;border-radius:50%;background:var(--bad-soft);color:var(--bad);display:grid;place-items:center}.miss .mq{flex:1;min-width:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.results .again{width:100%;margin-top:18px}.confetti{position:absolute;top:0;left:0;right:0;height:0;pointer-events:none}.confetti i{position:absolute;top:0;width:8px;height:12px;border-radius:2px;opacity:0;animation:fall 1.4s var(--ease) forwards}.details{width:300px;border-left:1px solid var(--sep);background:var(--n-2);display:flex;flex-direction:column;flex-shrink:0;overflow-y:auto}.details .hero{padding:24px 20px 20px;text-align:center;border-bottom:1px solid var(--line)}.details .hero .mk{width:66px;height:66px;border-radius:20px 7px;overflow:hidden;margin:0 auto 13px;background:var(--grad-dark);display:grid;place-items:center;box-shadow:var(--sh-2)}.details .hero h3{font-family:var(--serif);font-size:20px;color:var(--forest);margin:0 0 2px}.details .hero .by{font-size:12px;color:var(--muted)}.details .hero .pill{display:inline-flex;align-items:center;gap:6px;margin-top:12px;font-size:11.5px;color:var(--emerald-2);background:var(--emerald-soft);border:1px solid var(--emerald-line);border-radius:99px;padding:4px 11px;font-weight:600}.details .hero .pill .p{width:7px;height:7px;border-radius:50%;background:var(--avail)}.dsec{padding:16px 20px;border-bottom:1px solid var(--line)}.dsec h4{font-size:10.5px;letter-spacing:1.2px;text-transform:uppercase;color:var(--faint);margin:0 0 10px;font-weight:700}.dsec p{margin:0;font-size:12.5px;line-height:1.55;color:var(--ink-2)}.steps{display:flex;flex-direction:column;gap:11px}.step{display:flex;gap:11px;align-items:flex-start;font-size:12.5px;color:var(--ink-2)}.step .n{width:22px;height:22px;flex-shrink:0;border-radius:7px 3px;background:var(--grad-dark);color:var(--mint);font-weight:700;font-size:11px;display:grid;place-items:center}.dstats{display:flex;gap:10px}.dstat{flex:1;border:1px solid var(--line);border-radius:12px 5px;padding:11px 8px;text-align:center;background:#fff}.dstat b{display:block;font-family:var(--serif);font-size:21px;color:var(--forest);line-height:1}.dstat span{font-size:10px;color:var(--faint);letter-spacing:.3px;text-transform:uppercase}.prog{display:flex;align-items:center;gap:14px;margin-bottom:14px}.ring{position:relative;flex-shrink:0;display:grid;place-items:center}.ring .rval{font-family:var(--serif);font-size:17px;color:var(--forest);letter-spacing:-.3px}.pline{margin:0;font-size:12.5px;line-height:1.5;color:var(--muted)}.pline b{color:var(--ink);font-size:14px}.runs{display:flex;flex-direction:column;gap:8px}.run{display:flex;align-items:center;gap:10px;font-size:12.5px;color:var(--ink-2)}.run .lab{flex:1;min-width:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.run.now .lab{font-weight:700;color:var(--ink)}.run.next{opacity:.45}.run .sc{font-family:var(--mono);font-size:10.5px;color:var(--faint)}.run .bars{display:flex;gap:2px}.run .bars i{width:12px;height:5px;border-radius:2px;background:#e0e5e0;transition:background .3s}.run .bars i.ok{background:var(--emerald)}.run .bars i.no{background:var(--bad)}.insights{display:flex;flex-direction:column;gap:12px}.insight{display:flex;flex-direction:column;gap:5px}.insight-head{display:flex;align-items:baseline;justify-content:space-between;gap:10px;font-size:12.5px;color:var(--ink-2)}.insight-head span{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.insight-head b{font-family:var(--mono);font-size:11px;color:var(--forest)}.insight-track{display:block;height:6px;border-radius:3px;background:#e1e6e2;overflow:hidden}.insight-track i{display:block;height:100%;border-radius:inherit;background:var(--emerald)}.insight small{font-size:10.5px;color:var(--faint)}@keyframes rise{0%{opacity:0;transform:translateY(12px)}to{opacity:1;transform:none}}@keyframes riseR{0%{opacity:0;transform:translateY(10px)}to{opacity:1;transform:none}}@keyframes cardIn{0%{opacity:0;transform:translateY(16px) scale(.985)}to{opacity:1;transform:none}}@keyframes choiceIn{0%{opacity:0;transform:translate(-10px)}to{opacity:1;transform:none}}@keyframes pop{0%{transform:scale(.5)}60%{transform:scale(1.18)}to{transform:scale(1)}}@keyframes blink{0%,60%,to{opacity:.3;transform:translateY(0)}30%{opacity:1;transform:translateY(-3px)}}@keyframes fall{0%{opacity:1;transform:translateY(0) rotate(0)}to{opacity:0;transform:translateY(320px) rotate(320deg)}}@media(prefers-reduced-motion:reduce){*{animation:none!important;transition:none!important}}.live-insights{display:none}@media(max-width:1120px){.details{display:none}.live-insights{display:block;background:var(--n-2);border:1px solid var(--sep);border-radius:8px;margin-bottom:16px}.live-insights summary{cursor:pointer;padding:12px 20px;color:var(--forest)}}@media(max-width:840px){.rail{display:none}.list{width:86px}.list .name,.list .prev,.list-top h1,.search,.tabs,.chat .time{display:none}.chat{justify-content:center;padding:9px 0;margin:0}.window{height:calc(100vh - 24px)}.stage{padding:12px}.messages{padding:18px 14px 8px}}
|