@tiagojct/subsub 0.18.0 → 0.20.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/CHANGELOG.md +22 -0
- package/README.md +3 -3
- package/THIRD_PARTY_NOTICES +98 -96
- package/dist/bench.js +31 -0
- package/package.json +1 -1
- package/prompts/compare.md +1 -1
- package/prompts/lit.md +1 -1
- package/prompts/verify.md +2 -2
- package/roles/researcher.md +4 -2
- package/src/bench.ts +40 -0
- package/web/app.css +200 -168
- package/web/app.js +18 -1
- package/web/fonts/AtkinsonHyperlegibleMono.woff2 +0 -0
- package/web/fonts/AtkinsonHyperlegibleNext.woff2 +0 -0
- package/web/fonts/Literata-Italic.woff2 +0 -0
- package/web/fonts/Literata.woff2 +0 -0
- package/web/i18n.js +4 -0
- package/web/icon.svg +4 -4
- package/web/index.html +2 -1
- package/web/fonts/IBMPlexMono.woff2 +0 -0
- package/web/fonts/IBMPlexSans.woff2 +0 -0
- package/web/fonts/IBMPlexSerif.woff2 +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,28 @@
|
|
|
2
2
|
|
|
3
3
|
Sub-Sub follows the version numbers of npm: a change in the second number (0.13) adds features or changes how you use it; a change in the third (0.13.1) only fixes.
|
|
4
4
|
|
|
5
|
+
## 0.20.0 (2026-10-23)
|
|
6
|
+
|
|
7
|
+
Changes from a technical evaluation of 0.19.0 and Starbuck 0.2.0 (LLMSSH 2026).
|
|
8
|
+
|
|
9
|
+
- Searching the library: when you ask what you have on a topic, Sub-Sub first searches with your own words, then with close synonyms and other word forms, and says which words it searched. In the evaluation it missed items whose titles use a synonym or repeat your words. In a test on the 20 evaluation tasks, recall rose from 0.87 to 0.96 and precision fell from 0.98 to 0.91: the wider search also lists some items that are only near the topic. Check the list.
|
|
10
|
+
- Drafts: Sub-Sub now works from quotes. It reads the full text of each item, chooses a quote for each sentence, and writes the sentence so that it says no more than the quote, with one source for each sentence. A table "Quotes for the check" follows the draft with the quote, word for word, and the page or section for each sentence. A sentence without a quote is cut. In the evaluation, 37% to 53% of the factual sentences in drafts were only partly supported or unsupported by the works they cited. In a test on 18 drafts by two language-model raters, the share fell to 12% to 14% with the new rule. A second test on nine drafts of three other tasks, with the same raters, gave 14% and 16% against 35% and 37% for the old rule; one of the three tasks showed no change. The drafts have more and shorter sentences. Read the table as a way to check, not as a guarantee.
|
|
11
|
+
- Reference checks: a Check with "not found by search" is reported as "not confirmed", with the note that books, theses, reports, archives and works in Portuguese are often missing from Crossref and PubMed. Claim verdicts are reported as sentences to check; Sub-Sub does not change the text or the citation because of a verdict.
|
|
12
|
+
- Guide pages (English and Portuguese): the same two notes on reference checks.
|
|
13
|
+
- Website: a Changes page (English; the Portuguese menu links to it) shows this file.
|
|
14
|
+
- Bench: `retrievalScore` scores a retrieval answer by precision and recall against gold items, naming items by citekey or by title.
|
|
15
|
+
|
|
16
|
+
## 0.19.0 (2026-10-16)
|
|
17
|
+
|
|
18
|
+
A new look for the browser view, the website, the tutorial and the workshop, after the engraved plates of 19th-century natural history books.
|
|
19
|
+
|
|
20
|
+
- A grey-green paper, blue-black ink, and four colours with one meaning each: blue for the Researcher and for links, madder red for the Librarian, green for what is applied or checked, ochre for a preview. In dark mode the page is a blue-black plate.
|
|
21
|
+
- Type: Literata for text, with true italics; Atkinson Hyperlegible Next for the interface; Atkinson Hyperlegible Mono for commands. They replace IBM Plex.
|
|
22
|
+
- The notes pane shows what a note was written from: a line under `evidence:`, solid for the full text, dashed for the abstract, dotted for the metadata.
|
|
23
|
+
- Reading mode in the notes pane: larger type and more space between lines, for long notes. Sub-Sub remembers it on this computer. The tutorial has the same button.
|
|
24
|
+
- In a preview, a tag that would be removed is struck through instead of red. Errors show in ink, with a word, so that red belongs to the Librarian only. Each mode button keeps its own colour, selected or not.
|
|
25
|
+
- A preview has a dashed frame: nothing has changed yet.
|
|
26
|
+
|
|
5
27
|
## 0.18.0 (2026-10-15)
|
|
6
28
|
|
|
7
29
|
- Figures and tables from the PDF. With the Zotero server 0.6.0, Sub-Sub can cut a figure or a table out of an item's PDF (`zotero_get_figure`, with the label of its caption, such as "Figure 2" or "Table 1") and show it in a note, with its caption and a link to the page. `/read critique` shows the table or figure with the main result. The images are PNG files in `Literature/figures/`; the notes pane and Obsidian show them. When the figure cannot be cut out, Sub-Sub takes the whole page and says so.
|
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Sub-Sub
|
|
2
2
|
|
|
3
|
-
[](https://www.npmjs.com/package/@tiagojct/subsub)
|
|
4
4
|
[](https://subsub.tiagojacinto.eu)
|
|
5
5
|
[](LICENSE)
|
|
6
6
|
|
|
@@ -24,7 +24,7 @@ Each mode can have its own model. Switch with `/researcher` and `/librarian`, or
|
|
|
24
24
|
|
|
25
25
|
Every library change works the same way in both modes:
|
|
26
26
|
|
|
27
|
-
- The Zotero server computes the change first. Sub-Sub shows it to you (
|
|
27
|
+
- The Zotero server computes the change first. Sub-Sub shows it to you (the tags it adds and the tags it removes) and applies it only when you select Yes. The check is in the program, so the model cannot skip it.
|
|
28
28
|
- If you edit an item in Zotero while the preview is open, Sub-Sub shows you the new preview before it changes anything. Each change also sends the item's version, so a field you edit at the moment of writing is not overwritten.
|
|
29
29
|
- Every change is journaled, including new collections. `/undo` reverts the last one, and the browser view shows an Undo button under each change.
|
|
30
30
|
- Nothing is deleted: items go to the Zotero trash. Only tags from your tag list can be added.
|
|
@@ -183,4 +183,4 @@ This DOI always points to the latest version; each version also has its own DOI
|
|
|
183
183
|
|
|
184
184
|
## Licence
|
|
185
185
|
|
|
186
|
-
[MIT](LICENSE), Tiago Jacinto. The `/lit`, `/compare` and `/review` prompts adapt text from [Feynman](https://github.com/Companion-Inc/feynman) (MIT); see [THIRD_PARTY_NOTICES](THIRD_PARTY_NOTICES).
|
|
186
|
+
[MIT](LICENSE), Tiago Jacinto. The `/lit`, `/compare` and `/review` prompts adapt text from [Feynman](https://github.com/Companion-Inc/feynman) (MIT); see [THIRD_PARTY_NOTICES](THIRD_PARTY_NOTICES). Literata and Atkinson Hyperlegible fonts: SIL Open Font License.
|
package/THIRD_PARTY_NOTICES
CHANGED
|
@@ -55,99 +55,101 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
|
55
55
|
SOFTWARE.
|
|
56
56
|
|
|
57
57
|
|
|
58
|
-
web/fonts
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
Copyright
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
This
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
-----------------------------------------------------------
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
redistributed
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
Software,
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
58
|
+
web/fonts/, the website, the tutorial and the workshop use Literata, Atkinson
|
|
59
|
+
Hyperlegible Next and Atkinson Hyperlegible Mono, subset to Latin. Their licence:
|
|
60
|
+
|
|
61
|
+
Copyright 2017 The Literata Project Authors (https://github.com/googlefonts/literata)
|
|
62
|
+
Copyright 2020-2024 The Atkinson Hyperlegible Next Project Authors (https://github.com/googlefonts/atkinson-hyperlegible-next)
|
|
63
|
+
Copyright 2020-2024 The Atkinson Hyperlegible Mono Project Authors (https://github.com/googlefonts/atkinson-hyperlegible-next-mono)
|
|
64
|
+
|
|
65
|
+
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
|
66
|
+
|
|
67
|
+
This license is copied below, and is also available with a FAQ at: http://scripts.sil.org/OFL
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
-----------------------------------------------------------
|
|
71
|
+
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
|
72
|
+
-----------------------------------------------------------
|
|
73
|
+
|
|
74
|
+
PREAMBLE
|
|
75
|
+
The goals of the Open Font License (OFL) are to stimulate worldwide
|
|
76
|
+
development of collaborative font projects, to support the font creation
|
|
77
|
+
efforts of academic and linguistic communities, and to provide a free and
|
|
78
|
+
open framework in which fonts may be shared and improved in partnership
|
|
79
|
+
with others.
|
|
80
|
+
|
|
81
|
+
The OFL allows the licensed fonts to be used, studied, modified and
|
|
82
|
+
redistributed freely as long as they are not sold by themselves. The
|
|
83
|
+
fonts, including any derivative works, can be bundled, embedded,
|
|
84
|
+
redistributed and/or sold with any software provided that any reserved
|
|
85
|
+
names are not used by derivative works. The fonts and derivatives,
|
|
86
|
+
however, cannot be released under any other type of license. The
|
|
87
|
+
requirement for fonts to remain under this license does not apply
|
|
88
|
+
to any document created using the fonts or their derivatives.
|
|
89
|
+
|
|
90
|
+
DEFINITIONS
|
|
91
|
+
"Font Software" refers to the set of files released by the Copyright
|
|
92
|
+
Holder(s) under this license and clearly marked as such. This may
|
|
93
|
+
include source files, build scripts and documentation.
|
|
94
|
+
|
|
95
|
+
"Reserved Font Name" refers to any names specified as such after the
|
|
96
|
+
copyright statement(s).
|
|
97
|
+
|
|
98
|
+
"Original Version" refers to the collection of Font Software components as
|
|
99
|
+
distributed by the Copyright Holder(s).
|
|
100
|
+
|
|
101
|
+
"Modified Version" refers to any derivative made by adding to, deleting,
|
|
102
|
+
or substituting -- in part or in whole -- any of the components of the
|
|
103
|
+
Original Version, by changing formats or by porting the Font Software to a
|
|
104
|
+
new environment.
|
|
105
|
+
|
|
106
|
+
"Author" refers to any designer, engineer, programmer, technical
|
|
107
|
+
writer or other person who contributed to the Font Software.
|
|
108
|
+
|
|
109
|
+
PERMISSION & CONDITIONS
|
|
110
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
111
|
+
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
|
112
|
+
redistribute, and sell modified and unmodified copies of the Font
|
|
113
|
+
Software, subject to the following conditions:
|
|
114
|
+
|
|
115
|
+
1) Neither the Font Software nor any of its individual components,
|
|
116
|
+
in Original or Modified Versions, may be sold by itself.
|
|
117
|
+
|
|
118
|
+
2) Original or Modified Versions of the Font Software may be bundled,
|
|
119
|
+
redistributed and/or sold with any software, provided that each copy
|
|
120
|
+
contains the above copyright notice and this license. These can be
|
|
121
|
+
included either as stand-alone text files, human-readable headers or
|
|
122
|
+
in the appropriate machine-readable metadata fields within text or
|
|
123
|
+
binary files as long as those fields can be easily viewed by the user.
|
|
124
|
+
|
|
125
|
+
3) No Modified Version of the Font Software may use the Reserved Font
|
|
126
|
+
Name(s) unless explicit written permission is granted by the corresponding
|
|
127
|
+
Copyright Holder. This restriction only applies to the primary font name as
|
|
128
|
+
presented to the users.
|
|
129
|
+
|
|
130
|
+
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
|
131
|
+
Software shall not be used to promote, endorse or advertise any
|
|
132
|
+
Modified Version, except to acknowledge the contribution(s) of the
|
|
133
|
+
Copyright Holder(s) and the Author(s) or with their explicit written
|
|
134
|
+
permission.
|
|
135
|
+
|
|
136
|
+
5) The Font Software, modified or unmodified, in part or in whole,
|
|
137
|
+
must be distributed entirely under this license, and must not be
|
|
138
|
+
distributed under any other license. The requirement for fonts to
|
|
139
|
+
remain under this license does not apply to any document created
|
|
140
|
+
using the Font Software.
|
|
141
|
+
|
|
142
|
+
TERMINATION
|
|
143
|
+
This license becomes null and void if any of the above conditions are
|
|
144
|
+
not met.
|
|
145
|
+
|
|
146
|
+
DISCLAIMER
|
|
147
|
+
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
148
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
|
149
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
|
150
|
+
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
|
151
|
+
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|
152
|
+
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
|
153
|
+
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
154
|
+
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
|
155
|
+
OTHER DEALINGS IN THE FONT SOFTWARE.
|
package/dist/bench.js
CHANGED
|
@@ -212,6 +212,37 @@ export function prf(pred, gold) {
|
|
|
212
212
|
const fn = gold.size - tp;
|
|
213
213
|
return { tp, fp, fn };
|
|
214
214
|
}
|
|
215
|
+
function plainTitle(t) {
|
|
216
|
+
return t.toLowerCase().replace(/[^\p{L}\p{N}]+/gu, " ").trim();
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* The library items that an answer names. An item is named by its citekey, or by the first 40 characters of its title
|
|
220
|
+
* (at least 15). A duplicate copy (citekey ending in -dup) counts as its original.
|
|
221
|
+
*/
|
|
222
|
+
export function itemsNamed(text, library) {
|
|
223
|
+
const plain = plainTitle(text);
|
|
224
|
+
const found = new Set();
|
|
225
|
+
for (const item of library) {
|
|
226
|
+
const key = item.citekey.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
227
|
+
const title = plainTitle(item.title).slice(0, 40).trim();
|
|
228
|
+
if (new RegExp(`(?<![A-Za-z0-9_])${key}(?![A-Za-z0-9_])`).test(text) || (title.length >= 15 && plain.includes(title))) {
|
|
229
|
+
found.add(item.citekey.replace(/-dup$/, ""));
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
return found;
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* Precision and recall of the items in a retrieval answer against the gold items. Items in `acceptable` are on the topic but
|
|
236
|
+
* outside the gold list: they count as neither hits nor errors. "(copy)" and "-dup" mark a duplicate and count as the original.
|
|
237
|
+
*/
|
|
238
|
+
export function retrievalScore(text, gold, acceptable, library) {
|
|
239
|
+
const original = (k) => k.replace(/\s*\(copy\)\s*$/, "").replace(/-dup$/, "");
|
|
240
|
+
const goldSet = new Set(gold.map(original));
|
|
241
|
+
const skip = new Set(acceptable.map(original));
|
|
242
|
+
const said = new Set([...itemsNamed(text, library)].filter((k) => goldSet.has(k) || !skip.has(k)));
|
|
243
|
+
const { tp, fp, fn } = prf(said, goldSet);
|
|
244
|
+
return { ...f1(tp, fp, fn), tp, fp, fn };
|
|
245
|
+
}
|
|
215
246
|
function f1(tp, fp, fn) {
|
|
216
247
|
const p = tp + fp ? tp / (tp + fp) : 0;
|
|
217
248
|
const r = tp + fn ? tp / (tp + fn) : 0;
|
package/package.json
CHANGED
package/prompts/compare.md
CHANGED
|
@@ -8,7 +8,7 @@ Make a short slug: lower case, hyphens, at most five words. Use today's date (YY
|
|
|
8
8
|
|
|
9
9
|
1. Find the sources.
|
|
10
10
|
- If the user gave citekeys, DOIs or PMIDs, use exactly those.
|
|
11
|
-
- Otherwise: library first (zotero_find_items), then scholar_search_multi with three phrasings. Choose 3 to 10 sources and say why.
|
|
11
|
+
- Otherwise: library first (zotero_find_items, with the user's own words and then close synonyms), then scholar_search_multi with three phrasings. Choose 3 to 10 sources and say why.
|
|
12
12
|
2. Read each source before you describe it: zotero_get_fulltext for library items, scholar_read_oa_fulltext (methods and results) for open-access works, scholar_get_work for the abstract of the others. Note whether you read the full text or only the abstract.
|
|
13
13
|
3. Build the matrix, one row per source:
|
|
14
14
|
| # | Source | Claim on the question | Evidence type | Population and setting | Caveats | Confidence |
|
package/prompts/lit.md
CHANGED
|
@@ -18,7 +18,7 @@ Make a short slug from the topic: lower case, hyphens, at most five words. Use t
|
|
|
18
18
|
|
|
19
19
|
## 2. Gather
|
|
20
20
|
|
|
21
|
-
1. Library first: zotero_find_items for the topic (and the topic tags). Note what the library already has.
|
|
21
|
+
1. Library first: zotero_find_items for the topic, with the user's own words first and then with close synonyms and word forms, one call for each (and the topic tags). Note what the library already has.
|
|
22
22
|
2. Outside: scholar_search_multi with three or four phrasings of the question (synonyms, the test or drug name, the condition name, MeSH terms). Give include_terms (the key concepts) and exclude_terms (populations or designs outside the question) so that the server pre-screens the results. Use year limits when the question needs recent work.
|
|
23
23
|
3. Screen. For every work in the merged list, decide: read, maybe, or exclude, with a reason of a few words. Prefer systematic reviews, guidelines and primary studies with the right design. Write the screening log to `Research/.plans/<slug>-screening.md`: a table (#, work, identifier, pre-screen, decision, reason) and the counts for a flow diagram: found by the searches, unique works, pre-screen excluded, screened, read, used.
|
|
24
24
|
4. Read before you summarise:
|
package/prompts/verify.md
CHANGED
|
@@ -7,12 +7,12 @@ Check the references of $1 with Starbuck.
|
|
|
7
7
|
1. If the verify_ tools are not available, tell the user that reference checks need Starbuck: add "addons": ["starbuck"] to the Sub-Sub settings file and start Sub-Sub again. Then stop.
|
|
8
8
|
2. Call verify_check_manuscript with path $1.
|
|
9
9
|
3. Report the summary: how many references failed, need checking, have notes and passed.
|
|
10
|
-
4. For each Fail and each Check, give the citation key, Starbuck's reason and its suggested action. Keep Starbuck's result; do not make it stronger or weaker.
|
|
10
|
+
4. For each Fail and each Check, give the citation key, Starbuck's reason and its suggested action. Keep Starbuck's result; do not make it stronger or weaker. When the reason is that no record was found, write "not confirmed" and say that books, theses, reports, archives and works in Portuguese are often missing from Crossref and PubMed.
|
|
11
11
|
5. List the cited keys without a bibliography entry.
|
|
12
12
|
6. Give the path of the HTML report.
|
|
13
13
|
7. If the second argument is "claims", or the user asks whether the sources support the text:
|
|
14
14
|
1. Call verify_prepare_claims with path $1. Get all pages (offset, limit).
|
|
15
15
|
2. Judge each claim with status ready from its passages only: supported, partly_supported, not_supported or cannot_assess. Copy the passage you rely on character for character into quote.
|
|
16
16
|
3. Call verify_record_claims once, with the verdicts of all claims.
|
|
17
|
-
4. Report the claims that are not supported or only partly supported, with the line and the reason. Say for each whether the evidence was the full text or the abstract only.
|
|
17
|
+
4. Report the claims that are not supported or only partly supported as sentences to check, not as errors, with the line and the reason. Say for each whether the evidence was the full text or the abstract only.
|
|
18
18
|
8. Do not change the manuscript. Offer fixes and wait for the user's answer.
|
package/roles/researcher.md
CHANGED
|
@@ -30,7 +30,7 @@ You read the Zotero library (zotero_ tools), search PubMed, Europe PMC and OpenA
|
|
|
30
30
|
|
|
31
31
|
## Questions about the library
|
|
32
32
|
|
|
33
|
-
- "What do I have on X": zotero_find_items (query, tags, fulltext=true when needed). Answer in the chat: citekey, year, one line per item.
|
|
33
|
+
- "What do I have on X": search with zotero_find_items (query, tags, fulltext=true when needed). First search with {{user}}'s own words, as written. Then search again with close synonyms and other word forms (singular and plural, noun and adjective, spelling variants, an older or foreign name), one call for each. Join the results and list each item once. Answer in the chat: citekey, year, one line per item, and the words you searched.
|
|
34
34
|
|
|
35
35
|
## Outside search and gaps
|
|
36
36
|
|
|
@@ -103,10 +103,12 @@ If zotero_get_vocabulary has no topic/ tags, propose topics before tagging: read
|
|
|
103
103
|
2. Propose fixes for missing keys. Do not edit the manuscript text without approval.
|
|
104
104
|
3. scholar_export_bibliography to the file named in the YAML header, or references.json next to the manuscript.
|
|
105
105
|
|
|
106
|
-
Draft text for {{user}}'s manuscripts or theses only when {{user}} asks. Cite library items as [@citekey], use only claims from sources you read, and mark drafted text clearly so that {{user}} can check it. Never draft text for an assignment (coursework that {{user}} hands in for a grade), even when asked: say so, and offer /read, quotes and questions instead.
|
|
106
|
+
Draft text for {{user}}'s manuscripts or theses only when {{user}} asks. Cite library items as [@citekey], use only claims from sources you read, and mark drafted text clearly so that {{user}} can check it. Work from quotes. Read the full text (zotero_get_fulltext) of each item you will cite. First choose the quotes: for each sentence of the draft, one passage of one source, word for word, with its page or section. Then write each sentence so that it says no more than its quote says: one source for each sentence, no sentence that joins claims from two sources, no figure, date, name or cause that is not in the quote, and no stronger wording than the quote has. End every factual sentence (a fact, date, number, name, cause or comparison) with [@citekey]. If you have only the abstract or the metadata of an item, say so, and use it for no more than it says. After the draft, give a table "Quotes for the check" with one row for each factual sentence: its number, the citekey, the quote, and the page or section. A sentence that you cannot support with a quote is cut. Never draft text for an assignment (coursework that {{user}} hands in for a grade), even when asked: say so, and offer /read, quotes and questions instead.
|
|
107
107
|
|
|
108
108
|
## Reference checks (Starbuck, when the verify_ tools are available)
|
|
109
109
|
|
|
110
110
|
- verify_check_manuscript checks that each cited work exists, matches its citation and was not retracted. Use it on {{user}}'s manuscripts and on your own reports before you deliver them.
|
|
111
111
|
- Report Starbuck's results as Starbuck states them. Do not turn a Check into a Fail, or a Pass into "verified". Give the path of the report.
|
|
112
|
+
- When Starbuck found no record for a reference (a Check with "not found by search"), say "not confirmed", not "wrong". The work may exist: books, theses, reports, archives and works in Portuguese are often not in Crossref or PubMed. Suggest a library catalogue or the publisher's page.
|
|
113
|
+
- Report claim verdicts as sentences for {{user}} to check. Do not change the text or the citation because of a verdict. For a verdict of not supported, write "not supported by the passages I saw" and give the passage.
|
|
112
114
|
- Claim support (level 4) only when {{user}} asks: verify_prepare_claims, then judge each claim from its passages only, then verify_record_claims with all verdicts. Quote the passage you rely on character for character. With an abstract only, answer cannot_assess for details an abstract would not report.
|
package/src/bench.ts
CHANGED
|
@@ -262,6 +262,46 @@ export function prf(pred: Set<string>, gold: Set<string>) {
|
|
|
262
262
|
return { tp, fp, fn };
|
|
263
263
|
}
|
|
264
264
|
|
|
265
|
+
/** A library item as the retrieval score needs it: the citekey and the title. */
|
|
266
|
+
export interface LibraryItem {
|
|
267
|
+
citekey: string;
|
|
268
|
+
title: string;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
function plainTitle(t: string): string {
|
|
272
|
+
return t.toLowerCase().replace(/[^\p{L}\p{N}]+/gu, " ").trim();
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* The library items that an answer names. An item is named by its citekey, or by the first 40 characters of its title
|
|
277
|
+
* (at least 15). A duplicate copy (citekey ending in -dup) counts as its original.
|
|
278
|
+
*/
|
|
279
|
+
export function itemsNamed(text: string, library: LibraryItem[]): Set<string> {
|
|
280
|
+
const plain = plainTitle(text);
|
|
281
|
+
const found = new Set<string>();
|
|
282
|
+
for (const item of library) {
|
|
283
|
+
const key = item.citekey.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
284
|
+
const title = plainTitle(item.title).slice(0, 40).trim();
|
|
285
|
+
if (new RegExp(`(?<![A-Za-z0-9_])${key}(?![A-Za-z0-9_])`).test(text) || (title.length >= 15 && plain.includes(title))) {
|
|
286
|
+
found.add(item.citekey.replace(/-dup$/, ""));
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
return found;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* Precision and recall of the items in a retrieval answer against the gold items. Items in `acceptable` are on the topic but
|
|
294
|
+
* outside the gold list: they count as neither hits nor errors. "(copy)" and "-dup" mark a duplicate and count as the original.
|
|
295
|
+
*/
|
|
296
|
+
export function retrievalScore(text: string, gold: string[], acceptable: string[], library: LibraryItem[]) {
|
|
297
|
+
const original = (k: string) => k.replace(/\s*\(copy\)\s*$/, "").replace(/-dup$/, "");
|
|
298
|
+
const goldSet = new Set(gold.map(original));
|
|
299
|
+
const skip = new Set(acceptable.map(original));
|
|
300
|
+
const said = new Set([...itemsNamed(text, library)].filter((k) => goldSet.has(k) || !skip.has(k)));
|
|
301
|
+
const { tp, fp, fn } = prf(said, goldSet);
|
|
302
|
+
return { ...f1(tp, fp, fn), tp, fp, fn };
|
|
303
|
+
}
|
|
304
|
+
|
|
265
305
|
function f1(tp: number, fp: number, fn: number) {
|
|
266
306
|
const p = tp + fp ? tp / (tp + fp) : 0;
|
|
267
307
|
const r = tp + fn ? tp / (tp + fn) : 0;
|
package/web/app.css
CHANGED
|
@@ -1,93 +1,97 @@
|
|
|
1
|
-
/* Sub-Sub web view.
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
/* Sub-Sub web view: the plate, at work.
|
|
2
|
+
A grey-green sheet, a lighter field, copperplate ink, and four washes as the only colour:
|
|
3
|
+
Prussian blue (the Researcher), madder (the Librarian), sap green (applied), gamboge (a preview).
|
|
4
|
+
Type: Atkinson Hyperlegible Next for the interface, Literata for replies and notes (true italics),
|
|
5
|
+
Atkinson Hyperlegible Mono for commands. The fonts are local: the page's CSP allows no other origin. */
|
|
4
6
|
|
|
5
|
-
@font-face { font-family: "
|
|
6
|
-
@font-face { font-family: "
|
|
7
|
-
@font-face { font-family: "
|
|
7
|
+
@font-face { font-family: "Literata"; src: url("/fonts/Literata.woff2") format("woff2"); font-weight: 400 700; font-style: normal; font-display: swap; }
|
|
8
|
+
@font-face { font-family: "Literata"; src: url("/fonts/Literata-Italic.woff2") format("woff2"); font-weight: 400 700; font-style: italic; font-display: swap; }
|
|
9
|
+
@font-face { font-family: "Atkinson Hyperlegible Next"; src: url("/fonts/AtkinsonHyperlegibleNext.woff2") format("woff2"); font-weight: 400 700; font-display: swap; }
|
|
10
|
+
@font-face { font-family: "Atkinson Hyperlegible Mono"; src: url("/fonts/AtkinsonHyperlegibleMono.woff2") format("woff2"); font-weight: 400 600; font-display: swap; }
|
|
8
11
|
|
|
9
12
|
/* ---------------------------------------------------------------- tokens */
|
|
10
13
|
|
|
11
14
|
:root {
|
|
12
|
-
--sans: "
|
|
13
|
-
--serif: "
|
|
14
|
-
--mono: "
|
|
15
|
-
|
|
16
|
-
--bg: #
|
|
17
|
-
--surface: #
|
|
18
|
-
--raised: #
|
|
19
|
-
--text: #
|
|
20
|
-
--muted: #
|
|
21
|
-
--faint: #
|
|
22
|
-
--rule: #
|
|
23
|
-
--rule-strong: #
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
--researcher: #
|
|
27
|
-
--
|
|
28
|
-
--librarian: #
|
|
29
|
-
--
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
--add: #
|
|
33
|
-
--
|
|
34
|
-
--rem: #
|
|
35
|
-
--
|
|
36
|
-
--warn: #
|
|
37
|
-
|
|
38
|
-
--radius:
|
|
39
|
-
--radius-lg:
|
|
15
|
+
--sans: "Atkinson Hyperlegible Next", system-ui, -apple-system, "Segoe UI", sans-serif;
|
|
16
|
+
--serif: "Literata", Georgia, serif;
|
|
17
|
+
--mono: "Atkinson Hyperlegible Mono", ui-monospace, "SF Mono", Menlo, Consolas, monospace;
|
|
18
|
+
|
|
19
|
+
--bg: #e9ece6;
|
|
20
|
+
--surface: #dfe3dc;
|
|
21
|
+
--raised: #f4f5f1;
|
|
22
|
+
--text: #16202a;
|
|
23
|
+
--muted: #44505b;
|
|
24
|
+
--faint: #5d6870;
|
|
25
|
+
--rule: #c6ccc6;
|
|
26
|
+
--rule-strong: #7d878e;
|
|
27
|
+
|
|
28
|
+
--researcher: #2c5d87;
|
|
29
|
+
--researcher-soft: #d8e2eb;
|
|
30
|
+
--librarian: #a8432d;
|
|
31
|
+
--librarian-soft: #f0ddd6;
|
|
32
|
+
--on-accent: #f4f5f1;
|
|
33
|
+
|
|
34
|
+
--add: #4f6a2e;
|
|
35
|
+
--add-soft: #dfe7d2;
|
|
36
|
+
--rem: #a8432d;
|
|
37
|
+
--rem-soft: #f0ddd6;
|
|
38
|
+
--warn: #86601a;
|
|
39
|
+
--warn-soft: #f2e5c6;
|
|
40
|
+
|
|
41
|
+
--radius: 2px;
|
|
42
|
+
--radius-lg: 3px;
|
|
40
43
|
--side-w: 17rem;
|
|
41
44
|
--measure: 46rem;
|
|
42
45
|
}
|
|
43
46
|
|
|
47
|
+
/* Dark: the steel plate. */
|
|
44
48
|
@media (prefers-color-scheme: dark) {
|
|
45
49
|
:root:not([data-theme="light"]) {
|
|
46
|
-
--bg: #
|
|
47
|
-
--surface: #
|
|
48
|
-
--raised: #
|
|
49
|
-
--text: #
|
|
50
|
-
--muted: #
|
|
51
|
-
--faint: #
|
|
52
|
-
--rule: #
|
|
53
|
-
--rule-strong: #
|
|
54
|
-
--
|
|
55
|
-
--researcher: #
|
|
56
|
-
--
|
|
57
|
-
--librarian: #
|
|
58
|
-
--
|
|
59
|
-
--
|
|
60
|
-
--add: #
|
|
61
|
-
--
|
|
62
|
-
--rem: #
|
|
63
|
-
--
|
|
64
|
-
--warn: #
|
|
50
|
+
--bg: #12171c;
|
|
51
|
+
--surface: #0d1115;
|
|
52
|
+
--raised: #192128;
|
|
53
|
+
--text: #dce1de;
|
|
54
|
+
--muted: #a6b0b3;
|
|
55
|
+
--faint: #8d989d;
|
|
56
|
+
--rule: #2c363f;
|
|
57
|
+
--rule-strong: #66727c;
|
|
58
|
+
--researcher: #86b4da;
|
|
59
|
+
--researcher-soft: #1e3042;
|
|
60
|
+
--librarian: #e6866d;
|
|
61
|
+
--librarian-soft: #3d2620;
|
|
62
|
+
--on-accent: #12171c;
|
|
63
|
+
--add: #a3c078;
|
|
64
|
+
--add-soft: #253220;
|
|
65
|
+
--rem: #e6866d;
|
|
66
|
+
--rem-soft: #3d2620;
|
|
67
|
+
--warn: #e2b45a;
|
|
68
|
+
--warn-soft: #3a3019;
|
|
65
69
|
}
|
|
66
70
|
}
|
|
67
71
|
|
|
68
72
|
:root[data-theme="dark"] {
|
|
69
|
-
--bg: #
|
|
70
|
-
--surface: #
|
|
71
|
-
--raised: #
|
|
72
|
-
--text: #
|
|
73
|
-
--muted: #
|
|
74
|
-
--faint: #
|
|
75
|
-
--rule: #
|
|
76
|
-
--rule-strong: #
|
|
77
|
-
--
|
|
78
|
-
--researcher: #
|
|
79
|
-
--
|
|
80
|
-
--librarian: #
|
|
81
|
-
--
|
|
82
|
-
--
|
|
83
|
-
--add: #
|
|
84
|
-
--
|
|
85
|
-
--rem: #
|
|
86
|
-
--
|
|
87
|
-
--warn: #
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
/* The mode sets the accent: blue for the Researcher,
|
|
73
|
+
--bg: #12171c;
|
|
74
|
+
--surface: #0d1115;
|
|
75
|
+
--raised: #192128;
|
|
76
|
+
--text: #dce1de;
|
|
77
|
+
--muted: #a6b0b3;
|
|
78
|
+
--faint: #8d989d;
|
|
79
|
+
--rule: #2c363f;
|
|
80
|
+
--rule-strong: #66727c;
|
|
81
|
+
--researcher: #86b4da;
|
|
82
|
+
--researcher-soft: #1e3042;
|
|
83
|
+
--librarian: #e6866d;
|
|
84
|
+
--librarian-soft: #3d2620;
|
|
85
|
+
--on-accent: #12171c;
|
|
86
|
+
--add: #a3c078;
|
|
87
|
+
--add-soft: #253220;
|
|
88
|
+
--rem: #e6866d;
|
|
89
|
+
--rem-soft: #3d2620;
|
|
90
|
+
--warn: #e2b45a;
|
|
91
|
+
--warn-soft: #3a3019;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/* The mode sets the accent: Prussian blue for the Researcher, madder for the Librarian. */
|
|
91
95
|
body { --accent: var(--researcher); --accent-soft: var(--researcher-soft); }
|
|
92
96
|
body[data-mode="librarian"] { --accent: var(--librarian); --accent-soft: var(--librarian-soft); }
|
|
93
97
|
|
|
@@ -106,6 +110,8 @@ body {
|
|
|
106
110
|
-webkit-font-smoothing: antialiased;
|
|
107
111
|
-moz-osx-font-smoothing: grayscale;
|
|
108
112
|
text-rendering: optimizeLegibility;
|
|
113
|
+
caret-color: var(--accent);
|
|
114
|
+
scrollbar-color: var(--rule-strong) transparent;
|
|
109
115
|
}
|
|
110
116
|
|
|
111
117
|
button, input, select, textarea { font: inherit; color: inherit; }
|
|
@@ -116,7 +122,7 @@ svg { flex: none; }
|
|
|
116
122
|
.visually-hidden { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
|
|
117
123
|
[hidden] { display: none !important; }
|
|
118
124
|
:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
|
|
119
|
-
::selection { background: var(--accent-soft); }
|
|
125
|
+
::selection { background: var(--accent-soft); color: var(--text); }
|
|
120
126
|
|
|
121
127
|
/* Icons drawn with strokes, at the size of the text. */
|
|
122
128
|
.icon-btn svg, .new-btn svg, .brand .mark, .icon {
|
|
@@ -135,7 +141,7 @@ button { cursor: pointer; background: none; border: 1px solid transparent; borde
|
|
|
135
141
|
button:disabled { opacity: 0.45; cursor: default; }
|
|
136
142
|
|
|
137
143
|
.primary { background: var(--accent); border-color: var(--accent); color: var(--on-accent); font-weight: 550; }
|
|
138
|
-
.primary:hover:not(:disabled) {
|
|
144
|
+
.primary:hover:not(:disabled) { background: var(--text); border-color: var(--text); color: var(--bg); }
|
|
139
145
|
.secondary { background: var(--raised); border-color: var(--rule-strong); }
|
|
140
146
|
.secondary:hover:not(:disabled) { border-color: var(--text); }
|
|
141
147
|
.ghost { color: var(--muted); }
|
|
@@ -215,9 +221,9 @@ select:hover { border-color: var(--rule-strong); }
|
|
|
215
221
|
display: flex;
|
|
216
222
|
align-items: center;
|
|
217
223
|
gap: 0.45rem;
|
|
224
|
+
font-family: var(--serif);
|
|
218
225
|
font-weight: 600;
|
|
219
|
-
font-size: 1.
|
|
220
|
-
letter-spacing: -0.01em;
|
|
226
|
+
font-size: 1.1rem;
|
|
221
227
|
margin-right: 0.25rem;
|
|
222
228
|
}
|
|
223
229
|
.brand .mark { color: var(--accent); }
|
|
@@ -225,24 +231,25 @@ select:hover { border-color: var(--rule-strong); }
|
|
|
225
231
|
|
|
226
232
|
.modes {
|
|
227
233
|
display: inline-flex;
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
background: var(--surface);
|
|
231
|
-
border: 1px solid var(--rule);
|
|
234
|
+
border: 1px solid var(--rule-strong);
|
|
235
|
+
background: var(--raised);
|
|
232
236
|
}
|
|
233
237
|
.modes button {
|
|
234
|
-
padding: 0.
|
|
235
|
-
border-radius:
|
|
238
|
+
padding: 0.3rem 0.8rem;
|
|
239
|
+
border-radius: 0;
|
|
236
240
|
font-size: 0.86rem;
|
|
237
241
|
color: var(--muted);
|
|
238
242
|
border: 0;
|
|
239
243
|
}
|
|
240
244
|
.modes button:hover { color: var(--text); }
|
|
245
|
+
/* Each mode keeps its own colour, chosen or not. */
|
|
246
|
+
.modes button[data-mode="researcher"] { color: var(--researcher); }
|
|
247
|
+
.modes button[data-mode="librarian"] { color: var(--librarian); }
|
|
241
248
|
.modes button[aria-pressed="true"] {
|
|
242
|
-
background: var(--
|
|
249
|
+
background: var(--accent-soft);
|
|
243
250
|
color: var(--accent);
|
|
244
251
|
font-weight: 600;
|
|
245
|
-
box-shadow: 0
|
|
252
|
+
box-shadow: inset 0 -2px 0 var(--accent);
|
|
246
253
|
}
|
|
247
254
|
|
|
248
255
|
.status {
|
|
@@ -258,7 +265,6 @@ select:hover { border-color: var(--rule-strong); }
|
|
|
258
265
|
align-items: center;
|
|
259
266
|
gap: 0.45rem;
|
|
260
267
|
padding: 0.3rem 0.7rem;
|
|
261
|
-
border-radius: 999px;
|
|
262
268
|
font-size: 0.8rem;
|
|
263
269
|
color: var(--muted);
|
|
264
270
|
white-space: nowrap;
|
|
@@ -268,9 +274,10 @@ select:hover { border-color: var(--rule-strong); }
|
|
|
268
274
|
font-variant-numeric: tabular-nums;
|
|
269
275
|
}
|
|
270
276
|
.library-dot { width: 7px; height: 7px; border-radius: 50%; background: var(--rule-strong); flex: none; }
|
|
271
|
-
.library.reachable .library-dot { background: var(--add);
|
|
272
|
-
|
|
273
|
-
.library.down
|
|
277
|
+
.library.reachable .library-dot { background: var(--add); }
|
|
278
|
+
/* Errors are drawn in ink, with a hollow mark and a word: madder means only the Librarian. */
|
|
279
|
+
.library.down { color: var(--text); background: var(--surface); font-weight: 600; }
|
|
280
|
+
.library.down .library-dot { background: none; border: 1.5px solid var(--text); }
|
|
274
281
|
.library.checking .library-dot { background: var(--warn); animation: pulse 1.2s ease-in-out infinite; }
|
|
275
282
|
|
|
276
283
|
/* ---------------------------------------------------------------- side panel */
|
|
@@ -289,11 +296,12 @@ select:hover { border-color: var(--rule-strong); }
|
|
|
289
296
|
}
|
|
290
297
|
|
|
291
298
|
.side h2 {
|
|
292
|
-
font-
|
|
293
|
-
font-
|
|
299
|
+
font-family: var(--serif);
|
|
300
|
+
font-size: 0.98rem;
|
|
301
|
+
font-weight: 400;
|
|
302
|
+
font-variant-caps: all-small-caps;
|
|
294
303
|
letter-spacing: 0.08em;
|
|
295
|
-
|
|
296
|
-
color: var(--faint);
|
|
304
|
+
color: var(--muted);
|
|
297
305
|
margin: 0 0 0.35rem;
|
|
298
306
|
padding: 0 0.5rem;
|
|
299
307
|
}
|
|
@@ -306,10 +314,9 @@ select:hover { border-color: var(--rule-strong); }
|
|
|
306
314
|
padding: 0.5rem 0.7rem;
|
|
307
315
|
background: var(--raised);
|
|
308
316
|
border: 1px solid var(--rule);
|
|
309
|
-
font-weight:
|
|
310
|
-
box-shadow: 0 1px 1px rgb(0 0 0 / 0.03);
|
|
317
|
+
font-weight: 600;
|
|
311
318
|
}
|
|
312
|
-
.new-btn:hover { border-color: var(--
|
|
319
|
+
.new-btn:hover { border-color: var(--text); }
|
|
313
320
|
.new-btn svg { width: 16px; height: 16px; color: var(--accent); }
|
|
314
321
|
|
|
315
322
|
.actions { display: flex; flex-direction: column; }
|
|
@@ -318,10 +325,10 @@ select:hover { border-color: var(--rule-strong); }
|
|
|
318
325
|
padding: 0.32rem 0.5rem;
|
|
319
326
|
font-size: 0.88rem;
|
|
320
327
|
color: var(--text);
|
|
321
|
-
border-radius:
|
|
328
|
+
border-radius: 0;
|
|
322
329
|
}
|
|
323
330
|
.actions button:hover { background: var(--bg); }
|
|
324
|
-
.actions .group { margin: 0.
|
|
331
|
+
.actions .group { margin: 0.6rem 0 0.15rem; padding: 0 0.5rem; font-family: var(--serif); font-style: italic; font-size: 0.86rem; color: var(--muted); }
|
|
325
332
|
.actions .group:first-child { margin-top: 0; }
|
|
326
333
|
|
|
327
334
|
.side .grow { flex: 1; display: flex; flex-direction: column; min-height: 8rem; }
|
|
@@ -345,10 +352,10 @@ select:hover { border-color: var(--rule-strong); }
|
|
|
345
352
|
text-align: left;
|
|
346
353
|
padding: 0.42rem 0.5rem;
|
|
347
354
|
display: block;
|
|
348
|
-
border-radius:
|
|
355
|
+
border-radius: 0;
|
|
349
356
|
}
|
|
350
357
|
.sessions button:hover { background: var(--bg); }
|
|
351
|
-
.sessions .current button { background: var(--raised);
|
|
358
|
+
.sessions .current button { background: var(--raised); outline: 1px solid var(--rule-strong); outline-offset: -1px; }
|
|
352
359
|
.sessions .what {
|
|
353
360
|
display: -webkit-box;
|
|
354
361
|
-webkit-line-clamp: 2;
|
|
@@ -384,7 +391,7 @@ select:hover { border-color: var(--rule-strong); }
|
|
|
384
391
|
flex: none;
|
|
385
392
|
width: 28px;
|
|
386
393
|
height: 16px;
|
|
387
|
-
border-radius:
|
|
394
|
+
border-radius: var(--radius);
|
|
388
395
|
background: var(--rule-strong);
|
|
389
396
|
transition: background-color 0.15s ease;
|
|
390
397
|
}
|
|
@@ -395,9 +402,8 @@ select:hover { border-color: var(--rule-strong); }
|
|
|
395
402
|
left: 2px;
|
|
396
403
|
width: 12px;
|
|
397
404
|
height: 12px;
|
|
398
|
-
border-radius:
|
|
399
|
-
background:
|
|
400
|
-
box-shadow: 0 1px 2px rgb(0 0 0 / 0.25);
|
|
405
|
+
border-radius: 1px;
|
|
406
|
+
background: var(--raised);
|
|
401
407
|
transition: transform 0.15s ease;
|
|
402
408
|
}
|
|
403
409
|
.switch input:checked + .track { background: var(--accent); }
|
|
@@ -416,7 +422,7 @@ body.layout-full { --measure: 72rem; }
|
|
|
416
422
|
margin: 0.9rem auto 0;
|
|
417
423
|
padding: 0.6rem 0.6rem 0.6rem 0.9rem;
|
|
418
424
|
border: 1px solid var(--rule);
|
|
419
|
-
border-
|
|
425
|
+
border-top: 2px solid var(--warn);
|
|
420
426
|
border-radius: var(--radius);
|
|
421
427
|
background: var(--raised);
|
|
422
428
|
display: flex;
|
|
@@ -441,7 +447,7 @@ body.layout-full { --measure: 72rem; }
|
|
|
441
447
|
.msg.user .bubble {
|
|
442
448
|
background: var(--raised);
|
|
443
449
|
border: 1px solid var(--rule);
|
|
444
|
-
border-radius: var(--radius-lg)
|
|
450
|
+
border-radius: var(--radius-lg);
|
|
445
451
|
padding: 0.6rem 0.95rem;
|
|
446
452
|
max-width: min(36rem, 88%);
|
|
447
453
|
white-space: pre-wrap;
|
|
@@ -456,6 +462,7 @@ body.layout-full { --measure: 72rem; }
|
|
|
456
462
|
font-family: var(--serif);
|
|
457
463
|
font-size: 1.02rem;
|
|
458
464
|
line-height: 1.68;
|
|
465
|
+
font-variant-numeric: oldstyle-nums proportional-nums;
|
|
459
466
|
overflow-wrap: anywhere;
|
|
460
467
|
}
|
|
461
468
|
.md > :first-child { margin-top: 0; }
|
|
@@ -464,9 +471,8 @@ body.layout-full { --measure: 72rem; }
|
|
|
464
471
|
.md li { margin: 0.2rem 0; }
|
|
465
472
|
.md li::marker { color: var(--faint); }
|
|
466
473
|
.md h1, .md h2, .md h3, .md h4 {
|
|
467
|
-
font-family: var(--
|
|
474
|
+
font-family: var(--serif);
|
|
468
475
|
line-height: 1.3;
|
|
469
|
-
letter-spacing: -0.01em;
|
|
470
476
|
margin: 1.6rem 0 0.5rem;
|
|
471
477
|
font-weight: 600;
|
|
472
478
|
}
|
|
@@ -484,14 +490,17 @@ body.layout-full { --measure: 72rem; }
|
|
|
484
490
|
padding: 0.1rem 0 0.1rem 1rem;
|
|
485
491
|
border-left: 2px solid var(--accent);
|
|
486
492
|
color: var(--muted);
|
|
493
|
+
font-style: italic;
|
|
487
494
|
}
|
|
495
|
+
.md blockquote code, .md blockquote a { font-style: normal; }
|
|
496
|
+
/* A page link stays whole: (p. 12) never breaks after "p.". */
|
|
497
|
+
.md a[href^="zotero://open-pdf"] { white-space: nowrap; }
|
|
488
498
|
|
|
489
499
|
.md code {
|
|
490
500
|
font-family: var(--mono);
|
|
491
501
|
font-size: 0.84em;
|
|
492
502
|
background: var(--surface);
|
|
493
|
-
border:
|
|
494
|
-
border-radius: 4px;
|
|
503
|
+
border-radius: var(--radius);
|
|
495
504
|
padding: 0.05em 0.32em;
|
|
496
505
|
}
|
|
497
506
|
|
|
@@ -530,9 +539,8 @@ body.layout-full { --measure: 72rem; }
|
|
|
530
539
|
.md-table-wrap {
|
|
531
540
|
overflow-x: auto;
|
|
532
541
|
margin: 1.1rem 0;
|
|
533
|
-
border:
|
|
534
|
-
border-
|
|
535
|
-
background: var(--raised);
|
|
542
|
+
border-top: 1.5px solid var(--text);
|
|
543
|
+
border-bottom: 1.5px solid var(--text);
|
|
536
544
|
}
|
|
537
545
|
.md table {
|
|
538
546
|
border-collapse: collapse;
|
|
@@ -540,18 +548,19 @@ body.layout-full { --measure: 72rem; }
|
|
|
540
548
|
font-family: var(--sans);
|
|
541
549
|
font-size: 0.86rem;
|
|
542
550
|
line-height: 1.45;
|
|
543
|
-
font-variant-numeric: tabular-nums;
|
|
551
|
+
font-variant-numeric: lining-nums tabular-nums;
|
|
544
552
|
}
|
|
545
553
|
.md th, .md td { padding: 0.5rem 0.75rem; text-align: left; vertical-align: top; border-bottom: 1px solid var(--rule); }
|
|
546
554
|
.md tr:last-child td { border-bottom: 0; }
|
|
547
555
|
.md th {
|
|
548
|
-
font-
|
|
549
|
-
font-
|
|
550
|
-
|
|
551
|
-
|
|
556
|
+
font-family: var(--serif);
|
|
557
|
+
font-size: 0.98rem;
|
|
558
|
+
font-weight: 400;
|
|
559
|
+
font-variant-caps: all-small-caps;
|
|
560
|
+
letter-spacing: 0.07em;
|
|
552
561
|
color: var(--muted);
|
|
553
562
|
white-space: nowrap;
|
|
554
|
-
|
|
563
|
+
border-bottom-color: var(--text);
|
|
555
564
|
}
|
|
556
565
|
.md td:first-child { white-space: nowrap; }
|
|
557
566
|
.md td code { white-space: nowrap; }
|
|
@@ -576,7 +585,7 @@ body.layout-full { --measure: 72rem; }
|
|
|
576
585
|
align-items: baseline;
|
|
577
586
|
padding: 0.28rem 0.55rem;
|
|
578
587
|
margin-left: -0.55rem;
|
|
579
|
-
border-radius:
|
|
588
|
+
border-radius: 0;
|
|
580
589
|
width: fit-content;
|
|
581
590
|
max-width: calc(100% + 0.55rem);
|
|
582
591
|
}
|
|
@@ -594,8 +603,8 @@ body.layout-full { --measure: 72rem; }
|
|
|
594
603
|
.tool .label { color: var(--text); white-space: nowrap; }
|
|
595
604
|
.tool.running summary::before { background: var(--accent); animation: pulse 1.1s ease-in-out infinite; }
|
|
596
605
|
.tool.done summary::before { background: var(--add); }
|
|
597
|
-
.tool.error summary::before { background: var(--
|
|
598
|
-
.tool.error .label { color: var(--
|
|
606
|
+
.tool.error summary::before { background: none; border: 1.5px solid var(--text); }
|
|
607
|
+
.tool.error .label { color: var(--text); font-weight: 600; }
|
|
599
608
|
.tool .args { font-family: var(--mono); font-size: 0.74rem; color: var(--faint); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
|
|
600
609
|
.tool[open] .args { white-space: normal; overflow-wrap: anywhere; }
|
|
601
610
|
.tool pre {
|
|
@@ -631,8 +640,8 @@ body.layout-full { --measure: 72rem; }
|
|
|
631
640
|
white-space: pre-wrap;
|
|
632
641
|
overflow-wrap: anywhere;
|
|
633
642
|
}
|
|
634
|
-
.note.warning { border-
|
|
635
|
-
.note.error { border-
|
|
643
|
+
.note.warning { border-top: 2px solid var(--warn); }
|
|
644
|
+
.note.error { border-top: 2px solid var(--text); }
|
|
636
645
|
.note.mono { font-family: var(--mono); font-size: 0.76rem; line-height: 1.55; }
|
|
637
646
|
.note pre { font-family: var(--mono); font-size: 0.74rem; white-space: pre-wrap; }
|
|
638
647
|
.note button { margin-top: 0.5rem; }
|
|
@@ -640,7 +649,7 @@ body.layout-full { --measure: 72rem; }
|
|
|
640
649
|
.note details { margin-top: 0.4rem; color: var(--muted); }
|
|
641
650
|
.note details summary { cursor: pointer; font-size: 0.78rem; }
|
|
642
651
|
.note details pre { margin: 0.3rem 0 0; overflow-wrap: anywhere; }
|
|
643
|
-
.free-note { border-
|
|
652
|
+
.free-note { border-top: 1px solid var(--accent); padding-top: 0.4rem; }
|
|
644
653
|
|
|
645
654
|
.working {
|
|
646
655
|
margin-top: 1rem;
|
|
@@ -665,13 +674,18 @@ body.layout-full { --measure: 72rem; }
|
|
|
665
674
|
/* Empty conversation. */
|
|
666
675
|
.empty { padding-top: 11vh; }
|
|
667
676
|
.empty .kicker {
|
|
668
|
-
|
|
669
|
-
|
|
677
|
+
display: flex;
|
|
678
|
+
justify-content: space-between;
|
|
679
|
+
margin: 0 0 1.5rem;
|
|
680
|
+
padding-bottom: 0.3rem;
|
|
681
|
+
border-bottom: 1px solid var(--text);
|
|
682
|
+
font-family: var(--serif);
|
|
683
|
+
font-size: 1rem;
|
|
684
|
+
font-variant-caps: all-small-caps;
|
|
670
685
|
letter-spacing: 0.08em;
|
|
671
|
-
text-transform: uppercase;
|
|
672
686
|
color: var(--accent);
|
|
673
|
-
margin: 0 0 0.6rem;
|
|
674
687
|
}
|
|
688
|
+
.empty .kicker::before { content: "Sub-Sub"; color: var(--muted); order: -1; }
|
|
675
689
|
.empty h1 {
|
|
676
690
|
font-family: var(--serif);
|
|
677
691
|
font-weight: 500;
|
|
@@ -681,17 +695,18 @@ body.layout-full { --measure: 72rem; }
|
|
|
681
695
|
margin: 0 0 0.6rem;
|
|
682
696
|
}
|
|
683
697
|
.empty .lede { color: var(--muted); max-width: 34rem; margin: 0 0 1.75rem; }
|
|
684
|
-
|
|
698
|
+
/* Suggestions: a short list of lines to start from, not a row of cards. */
|
|
699
|
+
.chips { display: grid; max-width: 34rem; border-bottom: 1px solid var(--rule-strong); }
|
|
685
700
|
.chips button {
|
|
686
701
|
text-align: left;
|
|
687
|
-
border:
|
|
688
|
-
border-
|
|
689
|
-
|
|
690
|
-
padding: 0.
|
|
691
|
-
font-size: 0.
|
|
702
|
+
border: 0;
|
|
703
|
+
border-top: 1px solid var(--rule-strong);
|
|
704
|
+
border-radius: 0;
|
|
705
|
+
padding: 0.6rem 0.25rem;
|
|
706
|
+
font-size: 0.95rem;
|
|
692
707
|
line-height: 1.4;
|
|
693
708
|
}
|
|
694
|
-
.chips button:hover {
|
|
709
|
+
.chips button:hover { background: var(--accent-soft); color: var(--accent); }
|
|
695
710
|
|
|
696
711
|
body > p.empty { text-align: center; color: var(--muted); padding: 30vh 1rem 0; margin: 0; }
|
|
697
712
|
|
|
@@ -702,12 +717,11 @@ body > p.empty { text-align: center; color: var(--muted); padding: 30vh 1rem 0;
|
|
|
702
717
|
width: min(100%, var(--measure));
|
|
703
718
|
margin-inline: auto;
|
|
704
719
|
border: 1px solid var(--rule-strong);
|
|
705
|
-
border-radius:
|
|
720
|
+
border-radius: var(--radius-lg);
|
|
706
721
|
background: var(--raised);
|
|
707
|
-
box-shadow: var(--shadow);
|
|
708
722
|
transition: border-color 0.12s ease, box-shadow 0.12s ease;
|
|
709
723
|
}
|
|
710
|
-
.composer-box:focus-within { border-color: var(--accent); box-shadow:
|
|
724
|
+
.composer-box:focus-within { border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-soft); }
|
|
711
725
|
.composer textarea {
|
|
712
726
|
display: block;
|
|
713
727
|
width: 100%;
|
|
@@ -737,11 +751,10 @@ body > p.empty { text-align: center; color: var(--muted); padding: 30vh 1rem 0;
|
|
|
737
751
|
background: var(--raised);
|
|
738
752
|
border: 1px solid var(--rule-strong);
|
|
739
753
|
border-radius: var(--radius-lg);
|
|
740
|
-
box-shadow: var(--shadow);
|
|
741
754
|
padding: 0.3rem;
|
|
742
755
|
z-index: 5;
|
|
743
756
|
}
|
|
744
|
-
.suggest button, .suggest .hint-row { display: flex; align-items: baseline; gap: 0.6rem; width: 100%; text-align: left; padding: 0.38rem 0.55rem; border-radius:
|
|
757
|
+
.suggest button, .suggest .hint-row { display: flex; align-items: baseline; gap: 0.6rem; width: 100%; text-align: left; padding: 0.38rem 0.55rem; border-radius: 0; }
|
|
745
758
|
.suggest button[aria-selected="true"], .suggest button:hover { background: var(--accent-soft); }
|
|
746
759
|
.suggest .name { font-family: var(--mono); font-size: 0.8rem; color: var(--accent); white-space: nowrap; }
|
|
747
760
|
.suggest .desc { color: var(--muted); font-size: 0.82rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
@@ -749,32 +762,38 @@ body > p.empty { text-align: center; color: var(--muted); padding: 30vh 1rem 0;
|
|
|
749
762
|
/* ---------------------------------------------------------------- dialogs */
|
|
750
763
|
|
|
751
764
|
.modal {
|
|
752
|
-
border: 1px solid var(--
|
|
753
|
-
border-radius:
|
|
765
|
+
border: 1px solid var(--text);
|
|
766
|
+
border-radius: var(--radius-lg);
|
|
754
767
|
background: var(--raised);
|
|
755
768
|
color: var(--text);
|
|
756
769
|
padding: 1.4rem 1.5rem 1.25rem;
|
|
757
770
|
width: min(40rem, calc(100vw - 2rem));
|
|
758
771
|
max-height: calc(100dvh - 2rem);
|
|
759
772
|
overflow-y: auto;
|
|
760
|
-
box-shadow: 0 24px 60px -16px rgb(0 0 0 / 0.45);
|
|
761
773
|
}
|
|
762
774
|
.modal.wide { width: min(44rem, calc(100vw - 2rem)); }
|
|
763
|
-
.modal::backdrop { background: rgb(
|
|
775
|
+
.modal::backdrop { background: rgb(18 23 28 / 0.5); }
|
|
764
776
|
.modal[open] { animation: rise 0.16s ease-out; }
|
|
765
777
|
@keyframes rise { from { opacity: 0; transform: translateY(6px); } }
|
|
766
778
|
|
|
779
|
+
/* A preview is gamboge, and drawn dashed: nothing has happened yet. Its label is the dialog's
|
|
780
|
+
running head, with "Sub-Sub" opposite, as on the website's plates. */
|
|
767
781
|
.modal-kicker {
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
782
|
+
display: flex;
|
|
783
|
+
justify-content: space-between;
|
|
784
|
+
margin: 0 0 0.9rem;
|
|
785
|
+
padding-bottom: 0.25rem;
|
|
786
|
+
border-bottom: 1px dashed var(--warn);
|
|
787
|
+
font-family: var(--serif);
|
|
788
|
+
font-size: 1rem;
|
|
789
|
+
font-variant-caps: all-small-caps;
|
|
771
790
|
letter-spacing: 0.08em;
|
|
772
|
-
|
|
773
|
-
color: var(--accent);
|
|
791
|
+
color: var(--warn);
|
|
774
792
|
}
|
|
793
|
+
.modal-kicker::after { content: "Sub-Sub"; color: var(--muted); }
|
|
775
794
|
.modal-kicker:empty { display: none; }
|
|
776
|
-
.modal h2 { font-size: 1.
|
|
777
|
-
.modal h3 { font-size: 0.
|
|
795
|
+
.modal h2 { font-family: var(--serif); font-size: 1.25rem; font-weight: 600; line-height: 1.3; margin: 0 0 0.3rem; overflow-wrap: anywhere; }
|
|
796
|
+
.modal h3 { font-family: var(--serif); font-size: 0.98rem; font-weight: 400; font-variant-caps: all-small-caps; letter-spacing: 0.08em; color: var(--muted); margin: 1.4rem 0 0.5rem; }
|
|
778
797
|
.modal .hint { color: var(--muted); margin: 0 0 1rem; font-size: 0.88rem; }
|
|
779
798
|
|
|
780
799
|
.preview {
|
|
@@ -784,16 +803,17 @@ body > p.empty { text-align: center; color: var(--muted); padding: 30vh 1rem 0;
|
|
|
784
803
|
white-space: pre-wrap;
|
|
785
804
|
overflow-wrap: anywhere;
|
|
786
805
|
background: var(--bg);
|
|
787
|
-
border: 1px
|
|
788
|
-
border-radius:
|
|
806
|
+
border: 1px dashed var(--warn);
|
|
807
|
+
border-radius: 0;
|
|
789
808
|
padding: 0.8rem 0.95rem;
|
|
790
809
|
max-height: 55vh;
|
|
791
810
|
overflow-y: auto;
|
|
792
811
|
margin: 0 0 1.1rem;
|
|
793
812
|
}
|
|
794
|
-
.preview .add, .preview .rem {
|
|
813
|
+
.preview .add, .preview .rem { padding: 0.1em 0.3em; }
|
|
795
814
|
.preview .add { color: var(--add); background: var(--add-soft); }
|
|
796
|
-
.preview .rem { color: var(--
|
|
815
|
+
.preview .rem { color: var(--muted); background: none; text-decoration: line-through; text-decoration-thickness: 1px; }
|
|
816
|
+
/* A removal is struck through: madder stays the Librarian's colour. */
|
|
797
817
|
|
|
798
818
|
.modal-buttons { display: flex; justify-content: flex-end; align-items: center; gap: 0.5rem; flex-wrap: wrap; margin-top: 0.75rem; }
|
|
799
819
|
.modal-buttons button { min-width: 5.5rem; }
|
|
@@ -846,7 +866,7 @@ body > p.empty { text-align: center; color: var(--muted); padding: 30vh 1rem 0;
|
|
|
846
866
|
.models button { display: flex; flex-wrap: wrap; align-items: baseline; gap: 0.25rem 0.6rem; }
|
|
847
867
|
.models .needs-key { opacity: 0.65; }
|
|
848
868
|
.models .model-name { min-width: 0; overflow-wrap: anywhere; }
|
|
849
|
-
.models .model-tag { font-family: var(--sans); font-size: 0.72rem; font-weight: 600; color: var(--accent); border: 1px solid currentColor;
|
|
869
|
+
.models .model-tag { font-family: var(--sans); font-size: 0.72rem; font-weight: 600; color: var(--accent); border: 1px solid currentColor; padding: 0 0.45rem; }
|
|
850
870
|
.models .model-why { font-family: var(--sans); font-size: 0.75rem; color: var(--muted); }
|
|
851
871
|
|
|
852
872
|
/* ---------------------------------------------------------------- narrow screens */
|
|
@@ -873,7 +893,7 @@ body > p.empty { text-align: center; color: var(--muted); padding: 30vh 1rem 0;
|
|
|
873
893
|
|
|
874
894
|
.items { list-style: none; margin: 0; padding: 0; max-height: 18rem; overflow-y: auto; }
|
|
875
895
|
.items:empty { display: none; }
|
|
876
|
-
.items > li { padding: 0.4rem 0.5rem;
|
|
896
|
+
.items > li { padding: 0.4rem 0.5rem; }
|
|
877
897
|
.items > li:hover { background: var(--bg); }
|
|
878
898
|
.items > li.muted { font-size: 0.82rem; }
|
|
879
899
|
.item-title { font-size: 0.86rem; line-height: 1.35; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
|
|
@@ -883,7 +903,7 @@ body > p.empty { text-align: center; color: var(--muted); padding: 30vh 1rem 0;
|
|
|
883
903
|
.item-actions button:hover, .item-actions a:hover { text-decoration: underline; text-underline-offset: 0.18em; }
|
|
884
904
|
|
|
885
905
|
.sessions li { position: relative; }
|
|
886
|
-
.session-tools { position: absolute; top: 0.3rem; right: 0.3rem; display: none; gap: 0.3rem; background: var(--raised);
|
|
906
|
+
.session-tools { position: absolute; top: 0.3rem; right: 0.3rem; display: none; gap: 0.3rem; background: var(--raised); }
|
|
887
907
|
.sessions li:hover .session-tools, .sessions li:focus-within .session-tools { display: flex; }
|
|
888
908
|
/* No hover on a touch screen: the buttons stay under the conversation. */
|
|
889
909
|
@media (hover: none) { .session-tools { display: flex; position: static; justify-content: flex-end; background: none; } }
|
|
@@ -902,14 +922,26 @@ body > p.empty { text-align: center; color: var(--muted); padding: 30vh 1rem 0;
|
|
|
902
922
|
}
|
|
903
923
|
.note-pane[hidden] { display: none; }
|
|
904
924
|
.note-head { display: flex; align-items: center; gap: 0.6rem; padding: 0.6rem 0.9rem; border-bottom: 1px solid var(--rule); }
|
|
905
|
-
.note-name { flex: 1; min-width: 0; font-size: 0.
|
|
925
|
+
.note-name { flex: 1; min-width: 0; font-family: var(--mono); font-size: 0.8rem; font-weight: 500; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
906
926
|
.note-pane > .search { margin: 0.6rem 0.9rem 0; width: auto; }
|
|
907
927
|
.note-body { flex: 1; overflow-y: auto; padding: 0.9rem 1.1rem 1.5rem; }
|
|
908
928
|
.note-body .front { font-size: 0.74rem; color: var(--muted); margin: 0 0 1rem; }
|
|
929
|
+
/* What a note was written from, drawn by line: solid for the full text, dashed for the abstract,
|
|
930
|
+
dotted for the metadata. The same lines as on the website's Plate II. */
|
|
931
|
+
.evidence { margin: 0 0 0.6rem; font-family: var(--mono); font-size: 0.8rem; color: var(--muted); }
|
|
932
|
+
.evidence span { color: var(--text); padding-bottom: 2px; border-bottom: 2px solid var(--rule-strong); }
|
|
933
|
+
.evidence.ev-full span { border-bottom-style: solid; border-color: var(--add); }
|
|
934
|
+
.evidence.ev-abstract span { border-bottom-style: dashed; border-color: var(--warn); }
|
|
935
|
+
.evidence.ev-meta span { border-bottom-style: dotted; border-color: var(--muted); }
|
|
936
|
+
/* Reading mode: larger type, wider lines and spacing, a shorter measure. */
|
|
937
|
+
.note-pane.reading .note-body { padding: 1.4rem 1.6rem 2.5rem; }
|
|
938
|
+
.note-pane.reading .note-body .md { font-size: 1.2rem; line-height: 1.9; word-spacing: 0.06em; max-width: 36rem; }
|
|
939
|
+
.note-pane.reading .note-body .md p, .note-pane.reading .note-body .md li { margin-block: 0.9rem; }
|
|
940
|
+
#note-read[aria-pressed="true"] { color: var(--accent); text-decoration: underline; }
|
|
909
941
|
.note-body .front summary { cursor: pointer; }
|
|
910
942
|
.note-body .front pre { background: var(--bg); border: 1px solid var(--rule); border-radius: var(--radius); padding: 0.5rem 0.7rem; margin: 0.4rem 0 0; white-space: pre-wrap; overflow-wrap: anywhere; }
|
|
911
943
|
.note-list { list-style: none; margin: 0; padding: 0; }
|
|
912
|
-
.note-list button { width: 100%; text-align: left; padding: 0.4rem 0.5rem; border-radius:
|
|
944
|
+
.note-list button { width: 100%; text-align: left; padding: 0.4rem 0.5rem; border-radius: 0; display: grid; gap: 0.1rem; }
|
|
913
945
|
.note-list button:hover { background: var(--bg); }
|
|
914
946
|
.note-list .rel { font-size: 0.84rem; overflow-wrap: anywhere; }
|
|
915
947
|
.note-list .when { font-size: 0.72rem; color: var(--faint); }
|
|
@@ -930,7 +962,7 @@ a[data-note] { color: var(--accent); text-decoration: underline; text-decoration
|
|
|
930
962
|
width: min(19rem, 86vw);
|
|
931
963
|
transform: translateX(-100%);
|
|
932
964
|
transition: transform 0.2s ease;
|
|
933
|
-
|
|
965
|
+
border-right: 1px solid var(--text);
|
|
934
966
|
}
|
|
935
967
|
.side.open { transform: none; }
|
|
936
968
|
.app.note-open, .app.note-open.side-collapsed { grid-template-columns: minmax(0, 1fr); }
|
|
@@ -955,12 +987,12 @@ a[data-note] { color: var(--accent); text-decoration: underline; text-decoration
|
|
|
955
987
|
}
|
|
956
988
|
|
|
957
989
|
/* First run: what is still missing */
|
|
958
|
-
.setup { margin-block: 1.4rem 1rem; padding: 0.9rem 1rem; border: 1px solid var(--rule); border-
|
|
959
|
-
.setup h2 { margin: 0 0 0.5rem; font-size:
|
|
990
|
+
.setup { margin-block: 1.4rem 1rem; padding: 0.9rem 1rem; border: 1px solid var(--rule-strong); border-top: 2px solid var(--accent); background: var(--raised); text-align: left; max-width: 40rem; }
|
|
991
|
+
.setup h2 { margin: 0 0 0.5rem; font-family: var(--serif); font-size: 1.02rem; }
|
|
960
992
|
.setup ol { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.55rem; }
|
|
961
993
|
.setup li { display: grid; grid-template-columns: 4.5rem 1fr auto; gap: 0.6rem; align-items: start; font-size: 0.88rem; line-height: 1.45; }
|
|
962
994
|
.setup li > * { min-width: 0; }
|
|
963
|
-
.setup .mark { font-size: 0.
|
|
964
|
-
.setup li.todo .mark { color: var(--
|
|
995
|
+
.setup .mark { font-family: var(--serif); font-size: 0.95rem; font-variant-caps: all-small-caps; letter-spacing: 0.07em; color: var(--muted); }
|
|
996
|
+
.setup li.todo .mark { color: var(--warn); }
|
|
965
997
|
.setup li.ok .step-text { color: var(--muted); }
|
|
966
998
|
@media (max-width: 560px) { .setup li { grid-template-columns: 1fr; gap: 0.2rem; } }
|
package/web/app.js
CHANGED
|
@@ -429,6 +429,7 @@ async function showNoteList() {
|
|
|
429
429
|
$("note-name").textContent = t("notes");
|
|
430
430
|
$("note-back").hidden = true;
|
|
431
431
|
$("note-app").hidden = true;
|
|
432
|
+
$("note-read").hidden = true;
|
|
432
433
|
$("note-filter").hidden = false;
|
|
433
434
|
showPane(true);
|
|
434
435
|
try {
|
|
@@ -461,7 +462,15 @@ async function openNote(path) {
|
|
|
461
462
|
noteDir = "";
|
|
462
463
|
attachCopyButtons(content);
|
|
463
464
|
attachTableButtons(content, data.name.replace(/\.md$/i, ""));
|
|
464
|
-
|
|
465
|
+
// The evidence line: what the note was written from, drawn solid, dashed or dotted.
|
|
466
|
+
const evidence = front && /^evidence:\s*(.+?)\s*$/m.exec(front[1])?.[1];
|
|
467
|
+
const kind = evidence && (/full/i.test(evidence) ? "ev-full" : /abstract/i.test(evidence) ? "ev-abstract" : /metadata/i.test(evidence) ? "ev-meta" : "");
|
|
468
|
+
$("note-body").replaceChildren(
|
|
469
|
+
evidence ? el("p", { class: `evidence ${kind}` }, "evidence: ", el("span", { text: evidence })) : "",
|
|
470
|
+
front ? el("details", { class: "front" }, el("summary", { text: t("properties") }), el("pre", { text: front[1] })) : "",
|
|
471
|
+
content,
|
|
472
|
+
);
|
|
473
|
+
$("note-read").hidden = false;
|
|
465
474
|
$("note-body").scrollTop = 0;
|
|
466
475
|
showPane(true);
|
|
467
476
|
}
|
|
@@ -1513,6 +1522,14 @@ $("library-search").addEventListener("input", searchLibrary);
|
|
|
1513
1522
|
$("notes-btn").addEventListener("click", () => ($("note-pane").hidden ? showNoteList() : showPane(false)));
|
|
1514
1523
|
$("note-back").addEventListener("click", showNoteList);
|
|
1515
1524
|
$("note-close").addEventListener("click", () => showPane(false));
|
|
1525
|
+
// Reading mode for notes: larger type and wider spacing, remembered on this computer.
|
|
1526
|
+
function setReading(on) {
|
|
1527
|
+
$("note-pane").classList.toggle("reading", on);
|
|
1528
|
+
$("note-read").setAttribute("aria-pressed", String(on));
|
|
1529
|
+
localStorage.setItem("subsub-reading", String(on));
|
|
1530
|
+
}
|
|
1531
|
+
$("note-read").addEventListener("click", () => setReading(!$("note-pane").classList.contains("reading")));
|
|
1532
|
+
setReading(localStorage.getItem("subsub-reading") === "true");
|
|
1516
1533
|
$("note-app").addEventListener("click", () => noteShown && api("/api/open", { path: noteShown.path }).catch((err) => note(err.message, "warning")));
|
|
1517
1534
|
$("note-filter").addEventListener("input", renderNoteList);
|
|
1518
1535
|
$("note-pane").addEventListener("keydown", (e) => {
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/web/i18n.js
CHANGED
|
@@ -109,6 +109,8 @@ export const STRINGS = {
|
|
|
109
109
|
notesHint: "The notes in your Sub-Sub folder",
|
|
110
110
|
allNotes: "All notes",
|
|
111
111
|
properties: "Properties",
|
|
112
|
+
readingMode: "Reading mode",
|
|
113
|
+
readingModeHint: "Larger type and wider line spacing for the note",
|
|
112
114
|
downloadCsv: "Download CSV",
|
|
113
115
|
filterNotes: "Filter notes",
|
|
114
116
|
noNotes: "No notes yet.",
|
|
@@ -249,6 +251,8 @@ export const STRINGS = {
|
|
|
249
251
|
notesHint: "As notas da sua pasta do Sub-Sub",
|
|
250
252
|
allNotes: "Todas as notas",
|
|
251
253
|
properties: "Propriedades",
|
|
254
|
+
readingMode: "Modo de leitura",
|
|
255
|
+
readingModeHint: "Letra maior e mais espaço entre linhas na nota",
|
|
252
256
|
downloadCsv: "Transferir CSV",
|
|
253
257
|
filterNotes: "Filtrar notas",
|
|
254
258
|
noNotes: "Ainda não há notas.",
|
package/web/icon.svg
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
|
|
2
|
-
<rect width="32" height="32" rx="7" fill="#
|
|
3
|
-
<rect x="5.5" y="8" width="21" height="16" rx="1.5" fill="#
|
|
4
|
-
<path d="M9 12.5h8M9 16h14" stroke="#
|
|
5
|
-
<circle cx="16" cy="20.3" r="1.5" fill="#
|
|
2
|
+
<rect width="32" height="32" rx="7" fill="#2c5d87"/>
|
|
3
|
+
<rect x="5.5" y="8" width="21" height="16" rx="1.5" fill="#f4f5f1"/>
|
|
4
|
+
<path d="M9 12.5h8M9 16h14" stroke="#2c5d87" stroke-width="1.6" stroke-linecap="round"/>
|
|
5
|
+
<circle cx="16" cy="20.3" r="1.5" fill="#2c5d87"/>
|
|
6
6
|
</svg>
|
package/web/index.html
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
<meta name="color-scheme" content="light dark">
|
|
7
7
|
<title>Sub-Sub</title>
|
|
8
8
|
<link rel="icon" href="/icon.svg" type="image/svg+xml">
|
|
9
|
-
<link rel="preload" href="/fonts/
|
|
9
|
+
<link rel="preload" href="/fonts/AtkinsonHyperlegibleNext.woff2" as="font" type="font/woff2" crossorigin>
|
|
10
10
|
<link rel="stylesheet" href="/app.css">
|
|
11
11
|
<script type="module" src="/app.js"></script>
|
|
12
12
|
</head>
|
|
@@ -86,6 +86,7 @@
|
|
|
86
86
|
<div class="note-head">
|
|
87
87
|
<button type="button" class="link-btn" id="note-back" data-i18n="allNotes">All notes</button>
|
|
88
88
|
<span class="note-name" id="note-name"></span>
|
|
89
|
+
<button type="button" class="link-btn" id="note-read" aria-pressed="false" data-i18n="readingMode" data-i18n-title="readingModeHint" hidden>Reading mode</button>
|
|
89
90
|
<button type="button" class="link-btn" id="note-app" data-i18n="openInApp" hidden>Open in app</button>
|
|
90
91
|
<button type="button" class="link-btn" id="note-close" data-i18n="close">Close</button>
|
|
91
92
|
</div>
|
|
Binary file
|
|
Binary file
|
|
Binary file
|