Show replication status, retractions, and open-access text for academic papers while browsing
翻譯準備中
AI 摘要 · README
外掛TypeScript · HTMLGitHub @forrtproject自行安裝營運者未確認
試用過了嗎? 點一下就好,不需要登入。
尚未偵測到更新。GitHub 發布版本後會顯示在這裡。
個人 GitHub 帳號下公開儲存庫的擁有者可以登入並驗證所有權,自行管理介紹,無需管理員核准。
登入並產生驗證檔案,在 30 分鐘內將其提交至此儲存庫的預設分支。其他分支或分叉儲存庫中的檔案無效。然後在下方檢查檔案,即可無需管理員核准直接管理專案。請保留檔案以維持管理權限。
如需登入,將前往登入頁面。登入後請回到此處,再次點擊所選的驗證按鈕。
安裝型專案無需部署網域也可驗證。個人儲存庫可透過 GitHub 登入驗證,組織儲存庫可使用上方的儲存庫驗證檔案。
正在載入完整 README。 以下為已儲存的摘錄。
FORRT ORE (Open Research Extension) is a browser extension for Chrome and Edge that detects academic papers on the web and surfaces what is known about them: replication and reproduction evidence from the FORRT Replication Database, retractions and expressions of concern, PubPeer discussion, and open-access full text.檢視完整 README ↗FORRT ORE (Open Research Extension) is a browser extension for Chrome and Edge that detects academic papers on the web and surfaces what is known about them: replication and reproduction evidence from the FORRT Replication Database, retractions and expressions of concern, PubPeer discussion, and open-access full text.
When you visit an article page (PubMed, journal websites, preprint servers, etc.) or search on Google Scholar, OpenAlex, Semantic Scholar, PubMed, Europe PMC, Scopus or EBSCOhost, ORE scans for DOIs, looks them up, and shows what it found in an inline pill next to each paper and each reference.
📖 User documentation — what ORE shows, where it works, how to install it, and the privacy policy. Source for that site lives in docs/.
src/content-search/sites/ supplies the selectors and a stylesheet that places the panel in that site's layout. Adding a site means adding one adapter file, one CSS file and the manifest URL patterns.Install FORRT ORE from the Chrome Web Store — works in Chrome and Edge.
To run the latest build before it reaches the store, load it manually:
chrome://extensions (or edge://extensions)If you want to develop or modify the extension:
Install Node.js (v24, matching CI)
Clone and build:
git clone https://github.com/forrtproject/chromium-extension.git
cd chromium-extension
npm install
npm run build
Load the project root folder as an unpacked extension (as above).
After making changes, run npm run build and click the reload icon on the extension's card in chrome://extensions.
See CONTRIBUTING.md for setup, checks, bug reports, and pull-request guidance.
chromium-extension/
manifest.json # Chrome MV3 manifest
esbuild.config.ts # Build configuration
src/
shared/ # Shared modules used by all entry points
types.ts # Zod schemas, branded types, state types
doi-normalise.ts # DOI string normalisation
doi-extractor.ts # Extract DOIs from page HTML (meta, JSON-LD, visible text, tables)
doi-augment.ts # Resolve titles to DOIs via Crossref + OpenAlex
flora-api.ts # FORRT replication API client
messages.ts # Message types for content ↔ background
cache.ts # Persistent lookup cache (chrome.storage.local)
site-adapters.ts # Per-site pill placement registry (see below)
debounce.ts # Debounce utility
debug.ts # Debug logger + capture (gated on the flora_debug flag)
debug-log.ts # Persistent debug log; the service worker is its only writer
debug-report.ts # Builds the diagnostic report attached to issue reports
background/
service-worker.ts # MV3 service worker — handles FLORA_LOOKUP messages
content-general/
index.ts # Content script for article pages (SPA-aware)
injector.ts # Banner, inline badge, and DOI popover rendering (Shadow DOM)
styles.css # Banner/badge styles
content-github/
index.ts # Fills the debug report into ORE's own GitHub issue form
content-search/
index.ts # Content script for search-results sites (picks the adapter by hostname)
pipeline.ts # Shared row → DOI → lookup → panel pipeline
observer.ts # Re-runs the pipeline when the site adds result rows
sites/ # One adapter (.ts) + placement stylesheet (.css) per site; index.ts registers them
options/
index.html # Options page
tests/
setup.ts # Chrome API mocks
helpers.ts # Test utilities (doi(), mockResult())
unit/ # Unit tests for all modules
fixtures/ # HTML fixtures for extractor tests
dist/ # Built output (generated by npm run build)
By default ORE infers where to put a pill from the shape of the page — the
longest text container in a reference entry, the last link in the entry, and so
on. That works across the long tail of publishers, but on sites we know it can
land badly. On Atypon platforms (science.org, Sage) the only DOI in a reference
is the href of the "Crossref" link, so the generic rule wedges the pill into the
middle of the Crossref | Web of Science | Google Scholar row.
src/shared/site-adapters.ts holds a registry that names the element a pill
belongs in, per hostname. Every site spells out its own selectors, even
where two sites currently agree — sites are tuned and broken independently, and
a shared rule set means you can't fix one publisher without retesting the other.
A test enforces this: no two adapters may share a rule object by reference.
To add a site, copy an existing block, change the selectors, and append it to
SITE_ADAPTERS:
const NATURE: SiteAdapter = {
id: "nature",
hostnames: ["nature.com"], // subdomains and www. match too
referencePill: [
{ selector: ".c-article-references__text", position: "append" },
],
titlePill: [
{ selector: "h1.c-article-title", position: "append" },
],
referenceScope: "#references", // optional
referencePillStyle: { top: "2px" }, // optional
titlePillStyle: { top: "0" }, // optional
};
Record the DOM path you verified the selectors against in a comment above the block, as the existing entries do. That's what makes the next person's fix a two-minute job rather than an archaeology exercise.
referencePill / titlePill are ordered candidate lists. The first selector
matching a live element wins, so you can list a preferred target then a
fallback for older page templates.position is append (default), prepend, before, or after. The
selector ":self" targets the search root itself.referencePillStyle / titlePillStyle override the pill wrapper's CSS, per
slot — most often top, the vertical nudge, whose default suits body text and
usually needs lowering inside a large h1. Anything you don't name keeps its
default, and a value may end in !important for publishers with aggressive
CSS.referenceScope confines pills to one part of the page. Sage marks author
endnotes up much like citations, and without a scope they get pilled — and
worse, sent to Crossref/OpenAlex for augmentation, which can return a
confident-looking wrong DOI.Sites not in the registry use the generic placement, and so does any registered site whose selectors stop matching. That fallback is deliberate: publisher markup changes without notice, and a stale selector should quietly degrade to generic placement rather than drop the pill from the page.
Placement is covered by tests/unit/site-adapters.test.ts (rules in isolation)
and tests/unit/reference-placement.test.ts (the real render path, hostname
stubbed). Both run against fixtures trimmed from live article pages, so add a
fixture when you add a site.
Toolbar popup → Debug mode turns on logging. Every debugLog/debugWarn/
debugError call, plus uncaught errors thrown by extension code, is then
captured alongside the console output. Content scripts and extension pages batch
their entries and message them to the service worker, which is the only writer of
the flora_debug_log storage key — so there is no read-modify-write race between
contexts. The log is a ring buffer capped at 800 entries.
With debug mode on, the progress toast (bottom right while a page is worked)
also offers Copy log in its expanded panel, and — with the popup's
Offer "Copy log" after each pass switch on — stays up as a one-line
Done in 2.3 s · Copy log after the pass. Every stage transition is logged with
its duration (Work: augment done in 2310 ms), so a slow pass shows which stage
took the time. The same panel lists the stages of the current pass (on Google
Scholar also the DOIs and titles being resolved) and a Cancel button; the clock
icon snoozes ORE on the site for an hour or until tomorrow (resume from the popup)
or disables it on the domain.
To report a bug: turn debug mode on, reload the page, reproduce, then hit Report an issue in the popup. The diagnostic report — build, user agent, behaviour-changing settings and the log — is written into the GitHub issue body for you; nothing is submitted until you press GitHub's own button.
Two routes get it there, and the first doesn't touch GitHub's DOM at all:
?body= prefill. GitHub's documented prefill is the reliable path, but a
URL only holds so much — issueUrl() binary-searches the entry count to fit
as much of the log's tail as stays under 6 KB, and labels the result "most
recent N of M entries" so a trimmed log is never mistaken for a whole one.
Trimming whole entries rather than slicing the string keeps the markdown and
its code fence intact.content-github.ts, declared only for
github.com/forrtproject/chromium-extension/issues/new*, waits for the body
field and swaps it in between the <!-- ORE-DEBUG-REPORT:START/END -->
fences. Writes go through the HTMLTextAreaElement.prototype value setter
plus a bubbling input event, or React discards the text on its next render.The second step is best-effort by design: if it fails, step 1 has already put a usable log in the issue. The report is claimed only once a field exists to hold it, so a detour through GitHub sign-in leaves it parked rather than consumed (15 minute TTL). Failure is never silent — the content script verifies the text survived 250 ms of re-rendering and raises an on-page toast if it didn't, and the full report is on the clipboard either way.
Settings → Troubleshooting shows the same report on screen so it can be reviewed, downloaded or cleared before it is shared; the user's contact email is never included.
main or testing creates or updates that channel's draft release from latest sources. Pushes that only change docs/, src/retractions.json or src/registered-reports.json are skipped.| Stable | Testing | |
|---|---|---|
| Branch | main | testing |
| Tag | v0.1.4.0 | testing-v0.1.4.0 |
| GitHub release | Marked latest | Marked pre-release |
| Zip for the Web Store | flora-extension.zip | flora-extension-testing.zip |
| Debug log | Warnings and errors, unless debug mode is on | Every line |
| Visible label | None | Banner in the popup and settings, "Testing" in the toolbar tooltip and extension name |
To release, set version in manifest.json on the channel's branch, then tag that commit:
git tag testing-v0.1.4.0 origin/testing && git push origin testing-v0.1.4.0
The workflow rejects a tag unless its commit is on the channel's branch and its version matches manifest.json. The build type decides whether the testing label shows, and the workflow stamps only the zip's copy of manifest.json, so testing merges into main without carrying the label over.
| Command | Description |
|---|---|
npm run build | Build the extension (one-time); testing build that shows the testing banner and keeps a detailed debug log |
npm run build:release | Build for release: only warnings and errors are logged unless the user turns on debug mode |
npm run watch | Build and watch for changes |
npm test | Run all unit tests |
npm run test:watch | Run tests in watch mode |
npm run typecheck | TypeScript type checking |
npm run typecheckchrome.runtime.sendMessage..gs_ggs column, created if absent; OpenAlex: a right-hand column beside the result text; Semantic Scholar: beside the TLDR/abstract; PubMed: beside the authors/journal/PMID block; Europe PMC: a right-hand column below the title; EBSCOhost: beside the By/In metadata line, above the abstract; Scopus: the right end of the result's columns row in list view and in the title cell in table view).Confirmed FORRT no-matches are cached locally for five minutes to avoid repeated requests during scans. Newly added records may take up to five minutes to appear on a subsequent lookup; provider failures are not cached. These entries share the configured provider cache budget.
The page summary's "details" link opens the Replication Atlas for every matched DOI on the page. A short list travels in the link itself (?doi=a,b,c). Once that URL would pass 2,500 characters (needsAtlasSet in src/shared/flora-atlas.ts), the extension instead stores the list, encrypted, through the FORRT API and links to it as ?set=<id>#k=<key>:
sealDoiSet in src/shared/set-crypto.ts): a fresh 256-bit key and 12-byte IV per set, AES-256-GCM over {dois, created} with flora-doi-set:v2 as additional authenticated data. If the user clicks before the link is ready, a reserved tab is redirected once it is.POSTs only {data, iv} to /v1/sets. The API stores the ciphertext with a 30-day TTL and returns an 8-hex id. The key never leaves the browser.atlasSetUrl puts the id in the query and the key (43 characters of unpadded base64url) in the #k= fragment. Browsers never send the fragment to a server, so neither the API nor forrt.org sees the key.k from the fragment, fetches the ciphertext with GET /v1/sets/{id}, and decrypts it in the browser. A wrong or damaged key fails GCM's tag check and the Atlas reports the link as incomplete; an unknown or expired id returns 404 set_expired.The full link is a bearer secret: anyone holding it can read the list until it expires. Inside the extension the set travels as <id>.<key> until it becomes a link; debug logs record only the id, and redactDebugText strips the key from either form, so issue reports never carry it. See section 11 of the privacy policy.
This project is part of the FORRT initiative.