diff --git a/web/css/site.css b/web/css/site.css index 341ced123..6e65304af 100644 --- a/web/css/site.css +++ b/web/css/site.css @@ -423,12 +423,14 @@ a:hover { color: var(--accent-2); } /* ── Buttons ───────────────────────────────────────────────── */ .btn { - display: inline-flex; align-items: center; gap: 8px; + display: inline-flex; align-items: center; justify-content: center; gap: 8px; + min-height: 36px; /* shared baseline so same-role buttons match exactly */ padding: 8px 14px; background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-sm); - font-family: var(--mono); font-size: 12px; color: var(--text); + font-family: var(--mono); font-size: 12px; line-height: 1.2; color: var(--text); + white-space: nowrap; /* labels never wrap → height/width stay stable (FIX 2c) */ cursor: pointer; transition: all var(--t-fast); } @@ -482,7 +484,10 @@ a:hover { color: var(--accent-2); } box-shadow: 0 0 0 3px var(--accent-tint); } .field textarea { min-height: 90px; resize: vertical; } -.modal .actions { display: flex; justify-content: flex-end; gap: 8px; margin-top: 20px; } +.modal .actions { display: flex; align-items: center; justify-content: flex-end; gap: 8px; margin-top: 20px; flex-wrap: wrap; } +/* All modal action buttons share .btn sizing; equal min-height + nowrap labels + keep same-role buttons identical and aligned regardless of label length. */ +.modal .actions .btn { min-height: 38px; } /* ── Footer ────────────────────────────────────────────────── */ .foot { @@ -519,6 +524,57 @@ a:hover { color: var(--accent-2); } .panel-head { flex-direction: column; align-items: start; } .pipeline-row { grid-template-columns: 1fr; gap: 16px; } .pipeline-row::before { display: none; } + + /* ── Responsive topbar (FIX 1 / #262) ────────────────────────── + The single 64px no-wrap row crammed brand + Sign in + Submit a + paper + Submit Idea together; long labels wrapped to 2–3 lines + each, so every button was a different height and the row + overflowed below ~375px. Let the row wrap to two lines: brand + stays on the first line, the actions wrap onto their own line as + equal-height, equal-width compact buttons that never overflow. */ + .topbar .row { + height: auto; min-height: 56px; + flex-wrap: wrap; gap: 8px 10px; + padding-top: 10px; padding-bottom: 10px; + } + .top-actions { + width: 100%; + gap: 8px; + flex-wrap: wrap; + justify-content: flex-start; + } + /* Buttons size to their (non-wrapping) label so no label is ever clipped, + and wrap to a new line when the row runs out of room. Equal min-height + + shared .btn styling keep them the same height and baseline-aligned + regardless of label length (FIX 2). */ + .top-actions > * { flex: 0 1 auto; } + .top-actions .btn, + .top-actions .auth-chip { + justify-content: center; + padding-left: 12px; padding-right: 12px; + } + + /* The sort/search .bar was a non-wrapping flex row with a 220px-min search + box, so it overflowed the viewport on phones (horizontal page scroll). + Let it wrap and let the search box take the full width of its own line. */ + .bar { flex-wrap: wrap; } + .bar .grow { flex-basis: 100%; height: 0; } /* force the search onto its own row */ + .bar .search { min-width: 0; width: 100%; } +} +/* ── Tabs scroll affordance (FIX 1 / FR-002) ──────────────────── + .tabs-row scrolls horizontally with a hidden scrollbar, so on narrow + screens there was no hint that more tabs exist off-screen. A right-edge + fade signals "scroll for more"; it sits above the row and ignores + pointer events so taps still reach the tabs. */ +.tabs .shell { position: relative; } +.tabs .shell::after { + content: ""; position: absolute; top: 0; right: 0; bottom: 0; + width: 28px; pointer-events: none; + background: linear-gradient(to right, transparent, var(--bg) 85%); +} +@media (min-width: 1100px) { + /* Wide enough that every tab fits — drop the fade to keep desktop clean. */ + .tabs .shell::after { display: none; } } /* ── llmXive 002 additions: auth chip + artifact dialog + expanded kanban ───── */ @@ -597,12 +653,10 @@ a:hover { color: var(--accent-2); } background: var(--accent-tint); color: var(--accent); margin-top: 6px; } -.artifact-dialog .ad-close { - background: transparent; border: 0; cursor: pointer; - font-size: 18px; color: var(--muted); - padding: 4px 10px; border-radius: var(--r-sm); -} -.artifact-dialog .ad-close:hover { color: var(--text); background: var(--bg-2); } +/* .ad-close now rides the shared .btn.ghost class (FIX 2). No bespoke sizing — + it inherits the same min-height / padding / font as every other .btn so the + head-action buttons are equal height and aligned. Only the icon size differs. */ +.artifact-dialog .ad-close i { font-size: 14px; } .artifact-dialog .ad-body { display: grid; @@ -777,6 +831,14 @@ a:hover { color: var(--accent-2); } .artifact-dialog .ad-body { grid-template-columns: 1fr; } .artifact-dialog .ad-pdf { min-height: 240px; } } +@media (max-width: 560px) { + /* Stack the dialog title above its action buttons; the buttons then form + one equal-width row so Add review / Send feedback / Close are the same + height and aligned even on a phone (FIX 2). */ + .artifact-dialog .ad-head { flex-direction: column; align-items: stretch; } + .artifact-dialog .ad-head-actions { justify-content: stretch; } + .artifact-dialog .ad-head-actions .btn { flex: 1 1 0; min-width: 0; } +} /* about page extensions */ .pipeline.two-lane .lane { @@ -795,9 +857,6 @@ a:hover { color: var(--accent-2); } left: 5%; right: 5%; } -/* hidden machine-readable threshold spans */ -.thresholds { display: none; } - /* card stage badge */ .card .stage-pill { font-family: var(--mono); font-size: 10px; @@ -1114,13 +1173,19 @@ a:hover { color: var(--accent-2); } .modal-msg.err { color: oklch(0.5 0.18 30); } .modal-msg a { color: var(--accent); text-decoration: underline; } -.artifact-dialog .ad-head-actions { display: flex; align-items: center; gap: 8px; } -.artifact-dialog .ad-feedback-btn { - background: transparent; border: 1px solid var(--line-soft); border-radius: var(--r-sm); - color: var(--text-2); font-size: 12px; padding: 6px 10px; cursor: pointer; - display: inline-flex; align-items: center; gap: 6px; +/* Head-action buttons (Add review / Send feedback / Close) all ride the shared + .btn class now (FIX 2): equal min-height, identical padding/font/radius, + nowrap labels, baseline-aligned. The row keeps all three on one line on + normal/desktop dialogs (flex-shrink:0); the ≤560px rule below lets them + wrap into an equal-width row on phones without changing any button height. */ +.artifact-dialog .ad-head-actions { + display: flex; align-items: center; gap: 8px; + justify-content: flex-end; + flex-shrink: 0; } -.artifact-dialog .ad-feedback-btn:hover { border-color: var(--accent); color: var(--accent); } +/* Let the title block absorb the remaining width (and wrap its own text) so the + action row never gets squeezed into wrapping on wide dialogs. */ +.artifact-dialog .ad-head > div:first-child { min-width: 0; flex: 1 1 auto; } .artifact-dialog .ad-feedback { border-bottom: 1px solid var(--line-soft); background: var(--bg-2); padding: 14px 20px; diff --git a/web/data/projects.json b/web/data/projects.json index fd099715a..2d5e2d098 100644 --- a/web/data/projects.json +++ b/web/data/projects.json @@ -10072,7 +10072,7 @@ "research_reviewer_data_quality_research", "research_reviewer_filesystem_hygiene" ], - "description": "Seven specialist reviewers (idea quality, creativity, implementation correctness, completeness, code quality, data quality, filesystem hygiene) each vote. Acceptance requires both the points threshold and an accept verdict from every specialist. Human reviews count double.", + "description": "Eight specialist reviewers (a generic reviewer plus idea quality, creativity, implementation correctness, completeness, code quality, data quality, and filesystem hygiene) run an identify → revise → re-review convergence loop. Advancement requires unanimous panel acceptance within a 3-round cap; otherwise the project is kicked back to a prior stage. Human and simulated-personality reviews are advisory inputs routed through stage-aware triage, never a direct gate.", "example_artifacts": [ { "github_url": "https://github.com/ContextLab/llmXive/tree/main/projects/PROJ-651-grepseek-training-search-agents-for-dire", @@ -10398,7 +10398,7 @@ "paper_reviewer_figure_critic", "paper_reviewer_jargon_police" ], - "description": "Twelve specialist reviewers (writing, logic, claims, over-reach, safety/ethics, evidence, statistics, code, data, formatting, figures, jargon) each vote. Acceptance requires both the points threshold and an accept verdict from every specialist.", + "description": "Twelve specialist reviewers (writing, logic, claims, over-reach, safety/ethics, evidence, statistics, code, data, formatting, figures, jargon) run an identify → revise → re-review convergence loop. Advancement requires unanimous acceptance from every reviewer within a 3-round cap; otherwise the paper is kicked back carrying full provenance.", "example_artifacts": [ { "github_url": "https://github.com/ContextLab/llmXive/tree/main/projects/PROJ-651-grepseek-training-search-agents-for-dire", diff --git a/web/index.html b/web/index.html index 4f0fcae68..9bf2c4071 100644 --- a/web/index.html +++ b/web/index.html @@ -275,20 +275,10 @@

About llmXive

- -

What is llmXive?

-

llmXive automates scientific discovery end-to-end. A registry of 28 specialist agents — brainstormer, flesh-out, specifier, clarifier, planner, tasker, implementer, reviewer, paper-writer, figure-generator, statistician, proofreader, LaTeX builder, citation validator, and others — drives each project through two complete Spec Kit pipelines: one for the research itself and one for the paper that reports it.

+

llmXive automates scientific discovery end-to-end. A registry of specialist agents — brainstormer, flesh-out, specifier, clarifier, planner, tasker, implementer, paper-writer, figure-generator, statistician, proofreader, LaTeX builder, citation validator, and others, plus two panels of focused review lenses — drives each project through two complete Spec Kit pipelines: one for the research itself and one for the paper that reports it.

Spec Kit per project

@@ -298,6 +288,10 @@

Spec Kit per project

Two review gates — every specialist must accept

Research review and paper review run an identify → revise → re-review convergence loop driven by the lane's panel — 8 research reviewers (idea quality, creativity, implementation correctness, completeness, code quality, data quality, filesystem hygiene, plus a generic reviewer) and 12 paper specialists (writing, logic, claims, over-reach, safety, evidence, statistics, code, data, formatting, figures, jargon). The gate is unanimous panel acceptance within a 3-round cap; otherwise the project is kicked back to a prior stage carrying full provenance. Human and simulated-personality reviews are advisory inputs via stage-aware triage; self-review is rejected by the schema. (Spec 015 supersedes the prior point-based gate.)

+
+

Claim verification — no fact ships unsourced

+

Beyond the review gates, every factual claim in a generated artifact is detected, registered, and resolved against a real source; an unverifiable claim is marked [UNRESOLVED-CLAIM: …] and hard-blocks advancement (spec 016) — execution receipts are harness-signed so an agent can't forge a pass. When a claim can't be verified as written, an authoritative-fill step searches real sources (OEIS, Wikipedia, Wikidata, papers) and substitutes a value only if it is actually present in a fetched source, never model memory (spec 017). Verification picks a per-claim mode — exact count, approximate constant, safe symbolic computation, or source-fact (spec 018). For prose sources, a value is accepted only when the source semantically asserts that this subject has this value, not a coincidental digit match (spec 019).

+

Model selection — right-sized to each task

Each pipeline step is associated with an appropriate open model. Long, complex tasks (planning, paper writing, deep review) are routed to Qwen3.5 122B; faster, less complex tasks (clarifying questions, atomization, quick judgments) are routed to Gemma 3 27B. All inference runs on Dartmouth’s Discovery Cluster, with a fallback to open-weight Hugging Face models run locally via transformers as needed.

diff --git a/web/js/app.js b/web/js/app.js index d3f655798..e74a5b04b 100644 --- a/web/js/app.js +++ b/web/js/app.js @@ -917,6 +917,23 @@ activate(tab.dataset.tab, tab); } if (document.fonts && document.fonts.ready) document.fonts.ready.then(init); else init(); + + // #262: in-page anchors (the brand `` and the About + // page's "Browse projects" / "Find something to review" buttons, which + // point at `#inProgress`) only rewrote the hash — there was no + // `hashchange` listener, so the active tab never switched. Route every + // hash that names a real tab through the same `activate()` path used on + // load, then scroll it into view. Unknown/empty hashes are a no-op so + // unrelated in-page anchors (e.g. `#how-to-contribute`) keep working. + window.addEventListener("hashchange", () => { + const name = (location.hash || "").slice(1); + if (!name) return; + const tab = tabs.find(t => t.dataset.tab === name); + if (!tab) return; // not a tab anchor — leave it alone + if (!tab.classList.contains("active")) activate(name, tab); + const panel = document.querySelector('.panel[data-panel="' + name + '"]'); + (panel || tab).scrollIntoView({ behavior: "smooth", block: "start" }); + }); // Recompute on font load (widths change once the web font swaps in). if (document.fonts && document.fonts.ready) document.fonts.ready.then(positionUnderline); diff --git a/web/js/auth.js b/web/js/auth.js index 16dd315dd..72c1a7897 100644 --- a/web/js/auth.js +++ b/web/js/auth.js @@ -70,14 +70,13 @@ } const state = _randomState(); sessionStorage.setItem(KEY_STATE, state); - const authorize = _authorizeUrl(); - // FR-011 (account-switching): route through github.com/logout first so the - // user is presented with GitHub's account/consent screen and a *different* - // account can be chosen. This works regardless of whether the OAuth proxy's - // /revoke route is deployed (it's the no-Worker-change fallback path); when - // /revoke IS deployed, signOut() also revokes the grant for good measure. - const logoutUrl = "https://github.com/logout?return_to=" + encodeURIComponent(authorize); - location.href = logoutUrl; + // Navigate STRAIGHT to GitHub's OAuth authorize URL so the user reaches the + // consent screen directly. We must NOT pre-route through github.com/logout: + // GitHub renders a sign-out confirmation page there and does not reliably + // honor return_to to a cross-site authorize URL, so the user would land on a + // sign-out page and never reach consent (issue #217). Account-switching lives + // behind the explicit signOut() action, which revokes the grant via /revoke. + location.href = _authorizeUrl(); } // Non-blocking, dismissible notice (used when grant-revocation fails). diff --git a/web/js/dialog.js b/web/js/dialog.js index deaf12551..da7055313 100644 --- a/web/js/dialog.js +++ b/web/js/dialog.js @@ -68,15 +68,16 @@ + '
' + '

' + '
' - + '' - + '' + + '' + + '' + + '' + '
' + '
' + '