@tiagojct/subsub 0.6.1 → 0.8.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 +200 -62
- package/THIRD_PARTY_NOTICES +153 -0
- package/assets/icon.icns +0 -0
- package/assets/icon.ico +0 -0
- package/assets/icon.png +0 -0
- package/dist/bench.js +60 -5
- package/dist/config.js +28 -1
- package/dist/doctor.js +23 -2
- package/dist/init.js +17 -3
- package/dist/notes.js +42 -0
- package/dist/profiles.js +15 -0
- package/dist/prompt.js +3 -0
- package/dist/roles.js +7 -1
- package/dist/subsub.js +94 -9
- package/dist/web.js +17 -3
- package/package.json +7 -3
- package/prompts/compare.md +23 -0
- package/prompts/digest.md +23 -0
- package/prompts/lit.md +87 -0
- package/prompts/request-copy.md +35 -0
- package/prompts/review.md +31 -0
- package/prompts/verify.md +18 -0
- package/roles/researcher.md +18 -5
- package/src/bench.ts +63 -4
- package/src/config.ts +39 -1
- package/src/doctor.ts +32 -2
- package/src/init.ts +33 -4
- package/src/notes.ts +46 -0
- package/src/profiles.ts +17 -0
- package/src/prompt.ts +6 -0
- package/src/roles.ts +8 -1
- package/src/subsub.ts +100 -9
- package/src/web.ts +15 -3
- package/templates/Zotero agent.md +2 -0
- package/themes/subsub-glauca-dark.json +34 -34
- package/themes/subsub-glauca-light.json +32 -32
- package/themes/subsub-try-works-dark.json +34 -34
- package/themes/subsub-try-works-light.json +34 -34
- package/web/app.css +820 -206
- package/web/app.js +109 -17
- package/web/i18n.js +48 -4
- package/web/icon.svg +6 -1
- package/web/index.html +27 -19
- package/web/glauca.css +0 -106
package/web/app.js
CHANGED
|
@@ -67,6 +67,24 @@ marked.use({
|
|
|
67
67
|
image(token) {
|
|
68
68
|
return escapeHtml(token.text || token.href || "");
|
|
69
69
|
},
|
|
70
|
+
table(token) {
|
|
71
|
+
let headerHtml = "";
|
|
72
|
+
for (let r = 0; r < token.header.length; r++) {
|
|
73
|
+
headerHtml += this.tablecell(token.header[r]);
|
|
74
|
+
}
|
|
75
|
+
let t = this.tablerow({ text: headerHtml });
|
|
76
|
+
let s = "";
|
|
77
|
+
for (let r = 0; r < token.rows.length; r++) {
|
|
78
|
+
let row = token.rows[r];
|
|
79
|
+
let n = "";
|
|
80
|
+
for (let o = 0; o < row.length; o++) {
|
|
81
|
+
n += this.tablecell(row[o]);
|
|
82
|
+
}
|
|
83
|
+
s += this.tablerow({ text: n });
|
|
84
|
+
}
|
|
85
|
+
if (s) s = `<tbody>${s}</tbody>`;
|
|
86
|
+
return `<div class="md-table-wrap"><table><thead>${t}</thead>${s}</table></div>`;
|
|
87
|
+
},
|
|
70
88
|
},
|
|
71
89
|
});
|
|
72
90
|
function md(text) {
|
|
@@ -77,9 +95,28 @@ function md(text) {
|
|
|
77
95
|
}
|
|
78
96
|
}
|
|
79
97
|
|
|
80
|
-
// ---------------------------------------------------------------- theme & language
|
|
98
|
+
// ---------------------------------------------------------------- theme, layout & language
|
|
81
99
|
|
|
82
100
|
let currentTheme = localStorage.getItem("subsub-theme") || "auto";
|
|
101
|
+
let currentLayout = localStorage.getItem("subsub-layout") || "standard";
|
|
102
|
+
|
|
103
|
+
function updateLayoutUI() {
|
|
104
|
+
const isWide = currentLayout === "wide";
|
|
105
|
+
document.body.classList.toggle("layout-full", isWide);
|
|
106
|
+
const textEl = $("width-btn-text");
|
|
107
|
+
if (textEl) textEl.textContent = t("layoutWide");
|
|
108
|
+
const btn = $("width-btn");
|
|
109
|
+
if (btn) {
|
|
110
|
+
btn.title = t("toggleWidth");
|
|
111
|
+
btn.setAttribute("aria-pressed", String(isWide));
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function toggleLayout() {
|
|
116
|
+
currentLayout = currentLayout === "wide" ? "standard" : "wide";
|
|
117
|
+
localStorage.setItem("subsub-layout", currentLayout);
|
|
118
|
+
updateLayoutUI();
|
|
119
|
+
}
|
|
83
120
|
|
|
84
121
|
function themeLabel(theme) {
|
|
85
122
|
if (theme === "light") return t("themeLight");
|
|
@@ -87,16 +124,23 @@ function themeLabel(theme) {
|
|
|
87
124
|
return t("themeAuto");
|
|
88
125
|
}
|
|
89
126
|
|
|
127
|
+
/** Stroke icons for the theme button: fixed markup, no user text. */
|
|
128
|
+
const THEME_ICONS = {
|
|
129
|
+
light: '<circle cx="12" cy="12" r="4"/><path d="M12 2.5v2M12 19.5v2M2.5 12h2M19.5 12h2M5.3 5.3l1.4 1.4M17.3 17.3l1.4 1.4M5.3 18.7l1.4-1.4M17.3 6.7l1.4-1.4"/>',
|
|
130
|
+
dark: '<path d="M20 14.5A8 8 0 0 1 9.5 4a8 8 0 1 0 10.5 10.5Z"/>',
|
|
131
|
+
auto: '<rect x="3" y="4.5" width="18" height="12" rx="1.5"/><path d="M8.5 20h7M12 16.5V20"/>',
|
|
132
|
+
};
|
|
133
|
+
|
|
90
134
|
function updateThemeUI() {
|
|
91
|
-
const
|
|
92
|
-
const label = `${icon} ${themeLabel(currentTheme)}`;
|
|
135
|
+
const label = `${t("theme")}: ${themeLabel(currentTheme)}`;
|
|
93
136
|
const btn = $("theme-btn");
|
|
94
137
|
if (btn) {
|
|
95
|
-
btn.
|
|
96
|
-
btn.
|
|
138
|
+
btn.innerHTML = `<svg viewBox="0 0 24 24" aria-hidden="true">${THEME_ICONS[currentTheme] ?? THEME_ICONS.auto}</svg>`;
|
|
139
|
+
btn.append(el("span", { class: "visually-hidden", text: label }));
|
|
140
|
+
btn.title = `${label} (${t("themeToggle")})`;
|
|
97
141
|
}
|
|
98
142
|
const sideBtn = $("theme-btn-side");
|
|
99
|
-
if (sideBtn) sideBtn.textContent =
|
|
143
|
+
if (sideBtn) sideBtn.textContent = themeLabel(currentTheme);
|
|
100
144
|
}
|
|
101
145
|
|
|
102
146
|
function setTheme(theme) {
|
|
@@ -119,7 +163,10 @@ function toggleTheme() {
|
|
|
119
163
|
function applyLang() {
|
|
120
164
|
document.documentElement.lang = lang === "pt" ? "pt-PT" : "en";
|
|
121
165
|
for (const e of document.querySelectorAll("[data-i18n]")) e.textContent = t(e.dataset.i18n);
|
|
166
|
+
for (const e of document.querySelectorAll("[data-i18n-placeholder]")) e.placeholder = t(e.dataset.i18nPlaceholder);
|
|
167
|
+
for (const e of document.querySelectorAll("[data-i18n-title]")) e.title = t(e.dataset.i18nTitle);
|
|
122
168
|
updateThemeUI();
|
|
169
|
+
updateLayoutUI();
|
|
123
170
|
}
|
|
124
171
|
|
|
125
172
|
function mode() {
|
|
@@ -130,6 +177,8 @@ function renderHeader() {
|
|
|
130
177
|
document.body.dataset.mode = mode();
|
|
131
178
|
for (const b of document.querySelectorAll(".modes button")) b.setAttribute("aria-pressed", String(b.dataset.mode === mode()));
|
|
132
179
|
if (state?.profile) $("profile").value = $("profile-side").value = state.profile;
|
|
180
|
+
$("starbuck-toggle").checked = Boolean(state?.starbuck);
|
|
181
|
+
$("starbuck-label").title = t("starbuckHint");
|
|
133
182
|
const m = state?.model ?? session?.model;
|
|
134
183
|
$("model-btn").textContent = $("model-btn-side").textContent = m ? m.id : t("noModel");
|
|
135
184
|
const lib = $("library");
|
|
@@ -143,16 +192,22 @@ function renderHeader() {
|
|
|
143
192
|
txt.textContent = t("zoteroDown");
|
|
144
193
|
lib.classList.add("down");
|
|
145
194
|
} else {
|
|
146
|
-
const
|
|
147
|
-
|
|
195
|
+
const num = (n) => (typeof n === "number" ? n.toLocaleString(lang === "pt" ? "pt-PT" : "en-GB") : (n ?? "?"));
|
|
196
|
+
const parts = [t("items", { n: num(L.items) })];
|
|
197
|
+
if (L.toReview) parts.push(t("toReview", { n: num(L.toReview) }));
|
|
148
198
|
txt.textContent = `Zotero: ${parts.join(", ")}`;
|
|
149
199
|
lib.classList.add("reachable");
|
|
150
200
|
}
|
|
151
201
|
if (state?.down?.length) {
|
|
152
|
-
txt.textContent += `
|
|
202
|
+
txt.textContent += ` · ${t("serversDown", { x: state.down.join(", ") })}`;
|
|
153
203
|
lib.classList.add("down");
|
|
154
204
|
}
|
|
205
|
+
lib.title = txt.textContent;
|
|
155
206
|
$("input").placeholder = mode() === "librarian" ? t("placeholderLibrarian") : t("placeholderResearcher");
|
|
207
|
+
if (log.querySelector(".empty")) {
|
|
208
|
+
clearEmpty();
|
|
209
|
+
renderEmpty();
|
|
210
|
+
}
|
|
156
211
|
renderActions();
|
|
157
212
|
renderBanner();
|
|
158
213
|
updateThemeUI();
|
|
@@ -166,7 +221,9 @@ function hasCommand(name) {
|
|
|
166
221
|
function renderActions() {
|
|
167
222
|
const box = $("actions");
|
|
168
223
|
box.replaceChildren();
|
|
169
|
-
const list = [...ACTIONS[mode()]].filter(
|
|
224
|
+
const list = [...ACTIONS[mode()]].filter(
|
|
225
|
+
(a) => hasCommand(a.cmd) && (!a.needs || state?.[a.needs]) && (!a.profiles || a.profiles.includes(state?.profile ?? "scholar")),
|
|
226
|
+
);
|
|
170
227
|
for (const a of list) box.append(actionButton(a));
|
|
171
228
|
box.append(el("hr"));
|
|
172
229
|
for (const a of ACTIONS.both.filter((x) => hasCommand(x.cmd))) box.append(actionButton(a));
|
|
@@ -194,7 +251,7 @@ function renderBanner() {
|
|
|
194
251
|
b.replaceChildren();
|
|
195
252
|
const m = state?.model ?? session?.model;
|
|
196
253
|
if (!m && session) {
|
|
197
|
-
b.append(el("span", { text: t("connectModel") }), el("button", { type: "button", class: "primary small", text: t("connect"), onclick:
|
|
254
|
+
b.append(el("span", { text: t("connectModel") }), el("button", { type: "button", class: "primary small", text: t("connect"), onclick: showModels }));
|
|
198
255
|
b.hidden = false;
|
|
199
256
|
} else b.hidden = true;
|
|
200
257
|
}
|
|
@@ -317,14 +374,16 @@ function renderEmpty() {
|
|
|
317
374
|
el(
|
|
318
375
|
"div",
|
|
319
376
|
{ class: "empty" },
|
|
377
|
+
el("p", { class: "kicker", text: t(mode()) }),
|
|
320
378
|
el("h1", { text: t("emptyTitle") }),
|
|
379
|
+
el("p", { class: "lede", text: t(mode() === "librarian" ? "emptyLibrarian" : "emptyResearcher") }),
|
|
321
380
|
el(
|
|
322
381
|
"div",
|
|
323
382
|
{ class: "chips" },
|
|
324
383
|
s.map((x) =>
|
|
325
384
|
el("button", {
|
|
326
385
|
type: "button",
|
|
327
|
-
text: x.trim(),
|
|
386
|
+
text: x.endsWith(" ") ? `${x.trim()}…` : x,
|
|
328
387
|
onclick: () => {
|
|
329
388
|
const input = $("input");
|
|
330
389
|
input.value = x;
|
|
@@ -733,6 +792,7 @@ function nextDialog() {
|
|
|
733
792
|
const d = dialogs[0];
|
|
734
793
|
shownDialog = d;
|
|
735
794
|
const dlg = $("dialog");
|
|
795
|
+
$("dialog-kicker").textContent = d.method === "confirm" ? t("previewKicker") : "";
|
|
736
796
|
$("dialog-title").textContent = dialogTitle(d);
|
|
737
797
|
const hint = $("dialog-hint");
|
|
738
798
|
hint.textContent = d.method === "confirm" ? t("approveHint") : "";
|
|
@@ -790,6 +850,11 @@ function nextDialog() {
|
|
|
790
850
|
|
|
791
851
|
// ---------------------------------------------------------------- models and keys
|
|
792
852
|
|
|
853
|
+
/** Open the model dialog; a failure becomes a note, not an unhandled rejection. */
|
|
854
|
+
function showModels() {
|
|
855
|
+
openModels().catch((err) => note(err.message, "warning"));
|
|
856
|
+
}
|
|
857
|
+
|
|
793
858
|
async function openModels() {
|
|
794
859
|
const dlg = $("model-dialog");
|
|
795
860
|
const ul = $("models");
|
|
@@ -973,11 +1038,24 @@ for (const b of document.querySelectorAll(".modes button")) {
|
|
|
973
1038
|
});
|
|
974
1039
|
}
|
|
975
1040
|
$("profile").addEventListener("change", (e) => sendMessage(`/profile ${e.target.value}`));
|
|
1041
|
+
$("starbuck-toggle").addEventListener("change", async (e) => {
|
|
1042
|
+
const on = e.target.checked;
|
|
1043
|
+
e.target.disabled = true;
|
|
1044
|
+
try {
|
|
1045
|
+
await api("/api/addons", { starbuck: on });
|
|
1046
|
+
note(t(on ? "starbuckOn" : "starbuckOff"), "info");
|
|
1047
|
+
} catch (err) {
|
|
1048
|
+
e.target.checked = !on;
|
|
1049
|
+
note(err.message, "warning");
|
|
1050
|
+
} finally {
|
|
1051
|
+
e.target.disabled = false;
|
|
1052
|
+
}
|
|
1053
|
+
});
|
|
976
1054
|
$("profile-side").addEventListener("change", (e) => sendMessage(`/profile ${e.target.value}`));
|
|
977
|
-
$("model-btn").addEventListener("click",
|
|
1055
|
+
$("model-btn").addEventListener("click", showModels);
|
|
978
1056
|
$("model-btn-side").addEventListener("click", () => {
|
|
979
1057
|
closeSide();
|
|
980
|
-
|
|
1058
|
+
showModels();
|
|
981
1059
|
});
|
|
982
1060
|
$("model-close").addEventListener("click", () => $("model-dialog").close());
|
|
983
1061
|
$("main").addEventListener("click", () => {
|
|
@@ -985,10 +1063,19 @@ $("main").addEventListener("click", () => {
|
|
|
985
1063
|
});
|
|
986
1064
|
$("menu-btn").addEventListener("click", (e) => {
|
|
987
1065
|
e.stopPropagation();
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
1066
|
+
if (window.innerWidth > 832) {
|
|
1067
|
+
const app = document.querySelector(".app");
|
|
1068
|
+
app.classList.toggle("side-collapsed");
|
|
1069
|
+
const collapsed = app.classList.contains("side-collapsed");
|
|
1070
|
+
$("menu-btn").setAttribute("aria-expanded", String(!collapsed));
|
|
1071
|
+
localStorage.setItem("subsub-side-collapsed", String(collapsed));
|
|
1072
|
+
} else {
|
|
1073
|
+
const side = $("side");
|
|
1074
|
+
side.classList.toggle("open");
|
|
1075
|
+
$("menu-btn").setAttribute("aria-expanded", String(side.classList.contains("open")));
|
|
1076
|
+
}
|
|
991
1077
|
});
|
|
1078
|
+
$("width-btn")?.addEventListener("click", toggleLayout);
|
|
992
1079
|
$("theme-btn")?.addEventListener("click", toggleTheme);
|
|
993
1080
|
$("theme-btn-side")?.addEventListener("click", () => {
|
|
994
1081
|
closeSide();
|
|
@@ -1029,5 +1116,10 @@ function connect() {
|
|
|
1029
1116
|
}
|
|
1030
1117
|
|
|
1031
1118
|
setTheme(currentTheme);
|
|
1119
|
+
updateLayoutUI();
|
|
1120
|
+
if (localStorage.getItem("subsub-side-collapsed") === "true" && window.innerWidth > 832) {
|
|
1121
|
+
document.querySelector(".app")?.classList.add("side-collapsed");
|
|
1122
|
+
$("menu-btn")?.setAttribute("aria-expanded", "false");
|
|
1123
|
+
}
|
|
1032
1124
|
applyLang();
|
|
1033
1125
|
connect();
|
package/web/i18n.js
CHANGED
|
@@ -68,13 +68,24 @@ export const STRINGS = {
|
|
|
68
68
|
themeLight: "Light",
|
|
69
69
|
themeDark: "Dark",
|
|
70
70
|
themeToggle: "Toggle theme",
|
|
71
|
+
layoutWide: "Wide",
|
|
72
|
+
layoutFocus: "Focus",
|
|
73
|
+
toggleWidth: "Toggle wide layout",
|
|
71
74
|
copy: "Copy",
|
|
72
|
-
copied: "Copied
|
|
75
|
+
copied: "Copied",
|
|
73
76
|
copyNote: "Copy text",
|
|
74
|
-
searchConversations: "Search conversations
|
|
77
|
+
searchConversations: "Search conversations",
|
|
75
78
|
export: "Export",
|
|
76
79
|
exportTitle: "Export conversation",
|
|
77
80
|
confirmShortcutHint: "Y to approve, N to decline",
|
|
81
|
+
starbuck: "Reference checks (Starbuck)",
|
|
82
|
+
starbuckHint: "Checks that cited works exist, match their citation and were not retracted. Sub-Sub restarts when you change this.",
|
|
83
|
+
starbuckOn: "Reference checks are on. Use /verify, or ask the researcher to check a manuscript's references.",
|
|
84
|
+
starbuckOff: "Reference checks are off.",
|
|
85
|
+
composerHint: "Enter sends. Shift+Enter starts a new line. / shows the commands.",
|
|
86
|
+
emptyResearcher: "The researcher reads your library and searches PubMed, Europe PMC and OpenAlex. It cannot change your library.",
|
|
87
|
+
emptyLibrarian: "The librarian changes your library. You see a preview of each change, and nothing changes until you approve it.",
|
|
88
|
+
previewKicker: "Preview",
|
|
78
89
|
},
|
|
79
90
|
pt: {
|
|
80
91
|
researcher: "Investigador",
|
|
@@ -142,13 +153,24 @@ export const STRINGS = {
|
|
|
142
153
|
themeLight: "Claro",
|
|
143
154
|
themeDark: "Escuro",
|
|
144
155
|
themeToggle: "Mudar tema",
|
|
156
|
+
layoutWide: "Largo",
|
|
157
|
+
layoutFocus: "Foco",
|
|
158
|
+
toggleWidth: "Mudar largura do ecrã",
|
|
145
159
|
copy: "Copiar",
|
|
146
|
-
copied: "Copiado
|
|
160
|
+
copied: "Copiado",
|
|
147
161
|
copyNote: "Copiar texto",
|
|
148
|
-
searchConversations: "Procurar conversas
|
|
162
|
+
searchConversations: "Procurar conversas",
|
|
149
163
|
export: "Exportar",
|
|
150
164
|
exportTitle: "Exportar conversa",
|
|
151
165
|
confirmShortcutHint: "Y para aprovar, N para recusar",
|
|
166
|
+
starbuck: "Verificação de referências (Starbuck)",
|
|
167
|
+
starbuckHint: "Verifica se as obras citadas existem, correspondem à citação e não foram retratadas. O Sub-Sub reinicia quando muda esta opção.",
|
|
168
|
+
starbuckOn: "A verificação de referências está ligada. Use /verify, ou peça ao investigador que verifique as referências de um manuscrito.",
|
|
169
|
+
starbuckOff: "A verificação de referências está desligada.",
|
|
170
|
+
composerHint: "Enter envia. Shift+Enter muda de linha. / mostra os comandos.",
|
|
171
|
+
emptyResearcher: "O investigador lê a sua biblioteca e pesquisa na PubMed, Europe PMC e OpenAlex. Não pode alterar a biblioteca.",
|
|
172
|
+
emptyLibrarian: "O bibliotecário altera a sua biblioteca. Vê uma pré-visualização de cada alteração, e nada muda até a aprovar.",
|
|
173
|
+
previewKicker: "Pré-visualização",
|
|
152
174
|
},
|
|
153
175
|
};
|
|
154
176
|
|
|
@@ -156,11 +178,17 @@ export const STRINGS = {
|
|
|
156
178
|
export const ACTIONS = {
|
|
157
179
|
researcher: [
|
|
158
180
|
{ cmd: "subsub", send: true, en: "Library overview", pt: "Resumo da biblioteca" },
|
|
181
|
+
{ cmd: "lit", fill: true, profiles: ["scholar", "author", "editor"], en: "Literature review", pt: "Revisão da literatura" },
|
|
182
|
+
{ cmd: "compare", fill: true, profiles: ["scholar", "author", "editor"], en: "Compare sources", pt: "Comparar fontes" },
|
|
159
183
|
{ cmd: "lit-note", fill: true, en: "Literature note", pt: "Nota de leitura" },
|
|
160
184
|
{ cmd: "synthesis", fill: true, en: "Synthesis", pt: "Síntese" },
|
|
161
185
|
{ cmd: "gaps", fill: true, en: "What is missing", pt: "O que falta" },
|
|
162
186
|
{ cmd: "manuscript", fill: true, en: "Check a manuscript", pt: "Verificar um manuscrito" },
|
|
187
|
+
{ cmd: "verify", fill: true, needs: "starbuck", en: "Check references", pt: "Verificar referências" },
|
|
188
|
+
{ cmd: "review", fill: true, profiles: ["author", "editor"], en: "Review a manuscript", pt: "Rever um manuscrito" },
|
|
163
189
|
{ cmd: "alert", send: true, en: "New publications", pt: "Novas publicações" },
|
|
190
|
+
{ cmd: "request-copy", fill: true, en: "Ask an author for a copy", pt: "Pedir uma cópia ao autor" },
|
|
191
|
+
{ cmd: "digest", send: true, profiles: ["scholar", "author", "editor"], en: "Digest of the week", pt: "Resumo da semana" },
|
|
164
192
|
],
|
|
165
193
|
librarian: [
|
|
166
194
|
{ cmd: "subsub", send: true, en: "Library overview", pt: "Resumo da biblioteca" },
|
|
@@ -228,6 +256,14 @@ export const TOOLS = {
|
|
|
228
256
|
queue_imports: "queue imports",
|
|
229
257
|
attach_note: "add a linked note",
|
|
230
258
|
check_manuscript: "check a manuscript",
|
|
259
|
+
verify_check_manuscript: "check the references",
|
|
260
|
+
search_europepmc: "search Europe PMC",
|
|
261
|
+
search_multi: "search PubMed, Europe PMC and OpenAlex",
|
|
262
|
+
read_oa_fulltext: "read the open-access full text",
|
|
263
|
+
find_contact: "find the author's address",
|
|
264
|
+
verify_check_references: "check references",
|
|
265
|
+
verify_prepare_claims: "prepare the claim check",
|
|
266
|
+
verify_record_claims: "record the claim check",
|
|
231
267
|
export_bibliography: "export a bibliography",
|
|
232
268
|
read: "read a file",
|
|
233
269
|
write: "write a file",
|
|
@@ -276,6 +312,14 @@ export const TOOLS = {
|
|
|
276
312
|
queue_imports: "pôr importações na fila",
|
|
277
313
|
attach_note: "acrescentar uma nota ligada",
|
|
278
314
|
check_manuscript: "verificar um manuscrito",
|
|
315
|
+
verify_check_manuscript: "verificar as referências",
|
|
316
|
+
search_europepmc: "pesquisar na Europe PMC",
|
|
317
|
+
search_multi: "pesquisar na PubMed, Europe PMC e OpenAlex",
|
|
318
|
+
read_oa_fulltext: "ler o texto integral em acesso aberto",
|
|
319
|
+
find_contact: "procurar o endereço do autor",
|
|
320
|
+
verify_check_references: "verificar referências",
|
|
321
|
+
verify_prepare_claims: "preparar a verificação das afirmações",
|
|
322
|
+
verify_record_claims: "registar a verificação das afirmações",
|
|
279
323
|
export_bibliography: "exportar uma bibliografia",
|
|
280
324
|
read: "ler um ficheiro",
|
|
281
325
|
write: "escrever um ficheiro",
|
package/web/icon.svg
CHANGED
|
@@ -1 +1,6 @@
|
|
|
1
|
-
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32"
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
|
|
2
|
+
<rect width="32" height="32" rx="7" fill="#1d4f7c"/>
|
|
3
|
+
<rect x="5.5" y="8" width="21" height="16" rx="1.5" fill="#f7f6f2"/>
|
|
4
|
+
<path d="M9 12.5h8M9 16h14" stroke="#1d4f7c" stroke-width="1.6" stroke-linecap="round"/>
|
|
5
|
+
<circle cx="16" cy="20.3" r="1.5" fill="#1d4f7c"/>
|
|
6
|
+
</svg>
|
package/web/index.html
CHANGED
|
@@ -6,22 +6,22 @@
|
|
|
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="
|
|
9
|
+
<link rel="preload" href="/fonts/IBMPlexSans.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>
|
|
13
13
|
<body data-mode="researcher">
|
|
14
14
|
<div class="app">
|
|
15
15
|
<header class="top">
|
|
16
|
-
<button type="button" class="
|
|
17
|
-
<div class="brand"><
|
|
16
|
+
<button type="button" class="icon-btn" id="menu-btn" aria-controls="side" aria-expanded="true" data-i18n-title="menu" title="Menu"><svg viewBox="0 0 24 24" aria-hidden="true"><rect x="3.5" y="4.5" width="17" height="15" rx="2"/><path d="M9.5 4.5v15"/></svg><span class="visually-hidden" data-i18n="menu">Menu</span></button>
|
|
17
|
+
<div class="brand"><svg class="mark" viewBox="0 0 24 24" aria-hidden="true"><rect x="3" y="5" width="18" height="14" rx="1.5"/><path d="M6.5 9h7M6.5 12h11"/><circle cx="12" cy="16" r="1.1"/></svg><span>Sub<span class="hyphen">-</span>Sub</span></div>
|
|
18
18
|
<div class="modes" role="group" aria-label="Mode">
|
|
19
19
|
<button type="button" data-mode="researcher" aria-pressed="true" data-i18n="researcher">Researcher</button>
|
|
20
20
|
<button type="button" data-mode="librarian" aria-pressed="false" data-i18n="librarian">Librarian</button>
|
|
21
21
|
</div>
|
|
22
22
|
<div class="status">
|
|
23
23
|
<span class="library" id="library"><span class="library-dot" aria-hidden="true"></span><span class="library-text"></span></span>
|
|
24
|
-
<label class="profile"><span class="visually-hidden" data-i18n="profile">Profile</span>
|
|
24
|
+
<label class="profile wide-only"><span class="visually-hidden" data-i18n="profile">Profile</span>
|
|
25
25
|
<select id="profile">
|
|
26
26
|
<option value="reader">Reader</option>
|
|
27
27
|
<option value="scholar">Scholar</option>
|
|
@@ -29,13 +29,14 @@
|
|
|
29
29
|
<option value="editor">Editor</option>
|
|
30
30
|
</select>
|
|
31
31
|
</label>
|
|
32
|
-
<button type="button" class="
|
|
33
|
-
<button type="button" class="
|
|
32
|
+
<button type="button" class="chip-btn model-btn wide-only" id="model-btn"></button>
|
|
33
|
+
<button type="button" class="icon-btn wide-only" id="width-btn" aria-pressed="false"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M4 12h16M4 12l3.5-3.5M4 12l3.5 3.5M20 12l-3.5-3.5M20 12l-3.5 3.5"/></svg><span class="visually-hidden" id="width-btn-text">Wide</span></button>
|
|
34
|
+
<button type="button" class="icon-btn" id="theme-btn"></button>
|
|
34
35
|
</div>
|
|
35
36
|
</header>
|
|
36
37
|
|
|
37
38
|
<aside class="side" id="side">
|
|
38
|
-
<button type="button" class="
|
|
39
|
+
<button type="button" class="new-btn" id="new-btn"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M12 5v14M5 12h14"/></svg><span data-i18n="newConversation">New conversation</span></button>
|
|
39
40
|
<section class="phone-only settings">
|
|
40
41
|
<label><span data-i18n="profile">Profile</span>
|
|
41
42
|
<select id="profile-side">
|
|
@@ -49,7 +50,7 @@
|
|
|
49
50
|
<button type="button" class="secondary model-btn" id="model-btn-side"></button>
|
|
50
51
|
</label>
|
|
51
52
|
<label><span data-i18n="theme">Theme</span>
|
|
52
|
-
<button type="button" class="secondary
|
|
53
|
+
<button type="button" class="secondary" id="theme-btn-side"></button>
|
|
53
54
|
</label>
|
|
54
55
|
</section>
|
|
55
56
|
<section>
|
|
@@ -59,16 +60,17 @@
|
|
|
59
60
|
<section class="grow">
|
|
60
61
|
<div class="side-subhead">
|
|
61
62
|
<h2 data-i18n="conversations">Conversations</h2>
|
|
62
|
-
<button type="button" class="
|
|
63
|
-
</div>
|
|
64
|
-
<div class="search-wrap">
|
|
65
|
-
<input type="search" id="session-search" placeholder="Search conversations..." autocomplete="off">
|
|
63
|
+
<button type="button" class="link-btn" id="export-btn" data-i18n="export" data-i18n-title="exportTitle">Export</button>
|
|
66
64
|
</div>
|
|
65
|
+
<input type="search" id="session-search" class="search" data-i18n-placeholder="searchConversations" placeholder="Search conversations" autocomplete="off">
|
|
67
66
|
<ul class="sessions" id="sessions"></ul>
|
|
68
67
|
</section>
|
|
69
68
|
<footer class="side-foot">
|
|
70
|
-
<
|
|
71
|
-
<
|
|
69
|
+
<label class="switch" id="starbuck-label"><input type="checkbox" role="switch" id="starbuck-toggle"><span class="track" aria-hidden="true"></span><span data-i18n="starbuck">Reference checks (Starbuck)</span></label>
|
|
70
|
+
<div class="side-foot-row">
|
|
71
|
+
<button type="button" class="link-btn" id="quit-btn" data-i18n="quit">Close Sub-Sub</button>
|
|
72
|
+
<span class="version" id="version"></span>
|
|
73
|
+
</div>
|
|
72
74
|
</footer>
|
|
73
75
|
</aside>
|
|
74
76
|
|
|
@@ -77,10 +79,15 @@
|
|
|
77
79
|
<div class="log" id="log" aria-live="polite"></div>
|
|
78
80
|
<form class="composer" id="composer">
|
|
79
81
|
<div class="suggest" id="suggest" role="listbox" hidden></div>
|
|
80
|
-
<
|
|
81
|
-
|
|
82
|
-
<
|
|
83
|
-
|
|
82
|
+
<div class="composer-box">
|
|
83
|
+
<textarea id="input" rows="1" autocomplete="off" spellcheck="true"></textarea>
|
|
84
|
+
<div class="composer-row">
|
|
85
|
+
<span class="composer-hint" data-i18n="composerHint">Enter to send, Shift+Enter for a new line, / for commands</span>
|
|
86
|
+
<div class="composer-buttons">
|
|
87
|
+
<button type="button" class="secondary" id="stop" hidden data-i18n="stop">Stop</button>
|
|
88
|
+
<button type="submit" class="primary" id="send" data-i18n="send">Send</button>
|
|
89
|
+
</div>
|
|
90
|
+
</div>
|
|
84
91
|
</div>
|
|
85
92
|
</form>
|
|
86
93
|
</main>
|
|
@@ -88,6 +95,7 @@
|
|
|
88
95
|
|
|
89
96
|
<dialog id="dialog" class="modal">
|
|
90
97
|
<form method="dialog" id="dialog-form">
|
|
98
|
+
<p class="modal-kicker" id="dialog-kicker"></p>
|
|
91
99
|
<h2 id="dialog-title"></h2>
|
|
92
100
|
<p class="hint" id="dialog-hint"></p>
|
|
93
101
|
<pre class="preview" id="dialog-message"></pre>
|
|
@@ -99,7 +107,7 @@
|
|
|
99
107
|
<dialog id="model-dialog" class="modal wide">
|
|
100
108
|
<div class="modal-head">
|
|
101
109
|
<h2 data-i18n="modelTitle">Model</h2>
|
|
102
|
-
<button type="button" class="
|
|
110
|
+
<button type="button" class="link-btn" id="model-close" data-i18n="close">Close</button>
|
|
103
111
|
</div>
|
|
104
112
|
<h3 data-i18n="chooseModel">Choose a model</h3>
|
|
105
113
|
<label class="check"><input type="checkbox" id="model-both" checked> <span data-i18n="forBoth">Use it in both modes</span></label>
|
package/web/glauca.css
DELETED
|
@@ -1,106 +0,0 @@
|
|
|
1
|
-
/* Generated from glauca.json. Edit the json, then `make generate`. */
|
|
2
|
-
:root {
|
|
3
|
-
--gl-font-serif: "IBM Plex Serif", "IBM Plex Serif fallback", Georgia, serif;
|
|
4
|
-
--gl-font-sans: "IBM Plex Sans", "IBM Plex Sans fallback", system-ui, sans-serif;
|
|
5
|
-
--gl-font-mono: "IBM Plex Mono", "IBM Plex Mono fallback", ui-monospace, monospace;
|
|
6
|
-
--gl-font-reading: "IBM Plex Sans", "IBM Plex Sans fallback", system-ui, sans-serif;
|
|
7
|
-
--gl-text-xs: 0.64rem;
|
|
8
|
-
--gl-text-sm: 0.8rem;
|
|
9
|
-
--gl-text-base: 1rem;
|
|
10
|
-
--gl-text-lg: 1.25rem;
|
|
11
|
-
--gl-text-xl: 1.563rem;
|
|
12
|
-
--gl-text-2xl: 1.953rem;
|
|
13
|
-
--gl-text-3xl: 2.441rem;
|
|
14
|
-
--gl-text-4xl: 3.052rem;
|
|
15
|
-
--gl-text-display: clamp(2.5rem, 6vw, 4rem);
|
|
16
|
-
--gl-text-poster: clamp(3rem, 10vw, 6.5rem);
|
|
17
|
-
--gl-leading-tight: 1.08;
|
|
18
|
-
--gl-leading-snug: 1.3;
|
|
19
|
-
--gl-leading-normal: 1.55;
|
|
20
|
-
--gl-leading-relaxed: 1.7;
|
|
21
|
-
--gl-weight-regular: 400;
|
|
22
|
-
--gl-weight-medium: 500;
|
|
23
|
-
--gl-weight-semibold: 600;
|
|
24
|
-
--gl-weight-bold: 700;
|
|
25
|
-
--gl-space-0: 0;
|
|
26
|
-
--gl-space-1: 0.25rem;
|
|
27
|
-
--gl-space-2: 0.5rem;
|
|
28
|
-
--gl-space-3: 0.75rem;
|
|
29
|
-
--gl-space-4: 1rem;
|
|
30
|
-
--gl-space-5: 1.25rem;
|
|
31
|
-
--gl-space-6: 1.5rem;
|
|
32
|
-
--gl-space-8: 2rem;
|
|
33
|
-
--gl-space-10: 2.5rem;
|
|
34
|
-
--gl-space-12: 3rem;
|
|
35
|
-
--gl-space-16: 4rem;
|
|
36
|
-
--gl-space-20: 5rem;
|
|
37
|
-
--gl-space-24: 6rem;
|
|
38
|
-
--gl-radius-none: 0;
|
|
39
|
-
--gl-radius-sm: 2px;
|
|
40
|
-
--gl-radius-md: 4px;
|
|
41
|
-
--gl-radius-lg: 6px;
|
|
42
|
-
--gl-radius-pill: 999px;
|
|
43
|
-
--gl-border-width: 1px;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/* Pruina is the light default; Profundum is dark. */
|
|
47
|
-
:root,
|
|
48
|
-
[data-mode="light"],
|
|
49
|
-
[data-theme="light"],
|
|
50
|
-
html[data-theme="light"] {
|
|
51
|
-
--gl-bg: #f0f4f6;
|
|
52
|
-
--gl-surface: #f7fafb;
|
|
53
|
-
--gl-surface-raised: #ffffff;
|
|
54
|
-
--gl-text: #16222a;
|
|
55
|
-
--gl-text-muted: #55646d;
|
|
56
|
-
--gl-border: #cdd7dc;
|
|
57
|
-
--gl-accent: #0b62cf;
|
|
58
|
-
--gl-accent-bright: #007aff;
|
|
59
|
-
--gl-accent-deep: #084b96;
|
|
60
|
-
--gl-on-accent: #f2f7fb;
|
|
61
|
-
--gl-tint-deep: #16303f;
|
|
62
|
-
--gl-tint: #35576b;
|
|
63
|
-
--gl-tint-bright: #3e6d84;
|
|
64
|
-
--gl-tint-pale: #b7cdd6;
|
|
65
|
-
--gl-on-tint: #f2f7fb;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
[data-mode="dark"],
|
|
69
|
-
[data-theme="dark"],
|
|
70
|
-
html[data-theme="dark"] {
|
|
71
|
-
--gl-bg: #10161c;
|
|
72
|
-
--gl-surface: #171f26;
|
|
73
|
-
--gl-surface-raised: #1f2932;
|
|
74
|
-
--gl-text: #e8eef2;
|
|
75
|
-
--gl-text-muted: #8c8c8c;
|
|
76
|
-
--gl-border: #2a3540;
|
|
77
|
-
--gl-accent: #3d97ff;
|
|
78
|
-
--gl-accent-bright: #6cb2ff;
|
|
79
|
-
--gl-accent-deep: #007aff;
|
|
80
|
-
--gl-on-accent: #0b1218;
|
|
81
|
-
--gl-tint-deep: #142430;
|
|
82
|
-
--gl-tint: #2b4356;
|
|
83
|
-
--gl-tint-bright: #4d7391;
|
|
84
|
-
--gl-tint-pale: #93b7c9;
|
|
85
|
-
--gl-on-tint: #0d1720;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
@media (prefers-color-scheme: dark) {
|
|
89
|
-
:root:not([data-theme="light"]) {
|
|
90
|
-
--gl-bg: #10161c;
|
|
91
|
-
--gl-surface: #171f26;
|
|
92
|
-
--gl-surface-raised: #1f2932;
|
|
93
|
-
--gl-text: #e8eef2;
|
|
94
|
-
--gl-text-muted: #8c8c8c;
|
|
95
|
-
--gl-border: #2a3540;
|
|
96
|
-
--gl-accent: #3d97ff;
|
|
97
|
-
--gl-accent-bright: #6cb2ff;
|
|
98
|
-
--gl-accent-deep: #007aff;
|
|
99
|
-
--gl-on-accent: #0b1218;
|
|
100
|
-
--gl-tint-deep: #142430;
|
|
101
|
-
--gl-tint: #2b4356;
|
|
102
|
-
--gl-tint-bright: #4d7391;
|
|
103
|
-
--gl-tint-pale: #93b7c9;
|
|
104
|
-
--gl-on-tint: #0d1720;
|
|
105
|
-
}
|
|
106
|
-
}
|