From 7f595bbfd182a4d5b4053d5e58a7e3c247d29233 Mon Sep 17 00:00:00 2001 From: Dan Knauss Date: Wed, 17 Jun 2026 01:17:52 -0600 Subject: [PATCH] Revert "docs: add Phase-2 frontend cite/export planning docs (#34)" This reverts commit e6f19a1b46dfbe709b6673ebfd7f504bdbc5a73a. --- .../02-01-PLAN.md | 123 ---- .../02-02-PLAN.md | 200 ------- .../02-03-PLAN.md | 282 --------- .../02-RESEARCH.md | 560 ------------------ .../02-VALIDATION.md | 82 --- ...tize-biblatex-and-pmid-interoperability.md | 19 - ...te-in-input-resolution-via-open-library.md | 26 - 7 files changed, 1292 deletions(-) delete mode 100644 .planning/phases/02-frontend-cite-and-export-affordances/02-01-PLAN.md delete mode 100644 .planning/phases/02-frontend-cite-and-export-affordances/02-02-PLAN.md delete mode 100644 .planning/phases/02-frontend-cite-and-export-affordances/02-03-PLAN.md delete mode 100644 .planning/phases/02-frontend-cite-and-export-affordances/02-RESEARCH.md delete mode 100644 .planning/phases/02-frontend-cite-and-export-affordances/02-VALIDATION.md delete mode 100644 .planning/todos/done/2026-05-04-prioritize-biblatex-and-pmid-interoperability.md delete mode 100644 .planning/todos/pending/2026-06-04-add-isbn-paste-in-input-resolution-via-open-library.md diff --git a/.planning/phases/02-frontend-cite-and-export-affordances/02-01-PLAN.md b/.planning/phases/02-frontend-cite-and-export-affordances/02-01-PLAN.md deleted file mode 100644 index c9d2ee7..0000000 --- a/.planning/phases/02-frontend-cite-and-export-affordances/02-01-PLAN.md +++ /dev/null @@ -1,123 +0,0 @@ ---- -phase: 02-frontend-cite-and-export-affordances -plan: "02-01" -type: tdd -wave: 1 -depends_on: [] -files_modified: - - src/lib/export-single.js - - src/lib/export-single.test.js -autonomous: true -requirements: - - REQ-FE-02 - - REQ-FE-06 - -must_haves: - truths: - - "buildSingleRisContent(cslItem) returns a valid RIS string for one entry" - - "buildSingleBibtexContent(cslItem) returns a valid BibTeX string for one entry" - - "buildSingleCslJsonContent(cslItem) returns a valid JSON string for one entry" - - "Functions accept a non-DOI entry (thesis) without errors" - - "A reader can receive a single-entry export in RIS, BibTeX, or CSL-JSON format by clicking a per-entry button" - artifacts: - - path: "src/lib/export-single.js" - provides: "Per-entry export helpers (RIS, BibTeX, CSL-JSON)" - exports: - - buildSingleRisContent - - buildSingleBibtexContent - - buildSingleCslJsonContent - - path: "src/lib/export-single.test.js" - provides: "Unit tests for all three per-entry helpers" - min_lines: 60 - key_links: - - from: "src/lib/export-single.js" - to: "src/lib/export.js" - via: "buildRisExportContent, buildBibtexExportContent imports" - pattern: "from './export'" ---- - - -Create the per-entry export helpers that the frontend view script will call to generate BibTeX, RIS, and CSL-JSON content for a single citation. - -Purpose: The existing export.js functions operate on arrays with sorting. The frontend needs thin, single-entry wrappers that accept one CSL-JSON object and return the formatted export string without sort side-effects. -Output: src/lib/export-single.js with three tested functions; src/lib/export-single.test.js with full unit coverage. - - - -@/Users/danknauss/.claude/get-shit-done/workflows/execute-plan.md -@/Users/danknauss/.claude/get-shit-done/templates/summary.md - - - -@.planning/PROJECT.md -@.planning/ROADMAP.md -@.planning/STATE.md -@.planning/phases/02-frontend-cite-and-export-affordances/02-RESEARCH.md - - - - -```javascript -// src/lib/export.js — relevant exports - -export function buildRisExportContent(citations, citationStyle) -// citations: Array of { id: string, csl: Object } — pass [{id: cslItem.id || 'citation', csl: cslItem}] -// citationStyle: string — for single entry pass any valid style; sort has no effect on one item -// returns: string (RIS formatted, synchronous) - -export async function buildBibtexExportContent(citations, citationStyle, { CiteCtor } = {}) -// Same citation record shape; async (loads @citation-js/core dynamically) -// CiteCtor: optional test injection to replace Cite class -// returns: Promise - -export function buildCslJsonExportContent(citations, citationStyle) -// returns: string (JSON array with trailing newline) - -export const RIS_EXPORT_MIME_TYPE -export const BIBTEX_EXPORT_MIME_TYPE -export const CSL_JSON_EXPORT_MIME_TYPE -``` - - - - - Per-entry export helpers - src/lib/export-single.js, src/lib/export-single.test.js - - - buildSingleRisContent({ type: 'thesis', title: 'My Thesis', id: 'thesis-1' }) returns string starting with 'TY - THES' - - buildSingleRisContent({ type: 'article-journal', title: 'Paper', DOI: '10.1/x', id: 'j1' }) contains 'DO - 10.1/x' - - buildSingleRisContent for non-DOI entry returns valid RIS (no crash, no empty string) - - buildSingleBibtexContent({ type: 'book', title: 'Book Title', id: 'b1' }) resolves to string containing '@book' - - buildSingleBibtexContent accepts CiteCtor injection for testing (no real @citation-js load) - - buildSingleCslJsonContent({ type: 'article-journal', title: 'T', id: 'c1' }) returns JSON string parseable as array of length 1 - - buildSingleCslJsonContent preserves all fields from the input cslItem - - All three functions accept a cslItem with no id property (fall back to 'citation' as id) - - - TDD red-green-refactor. Tests first, then minimal implementation. - - src/lib/export-single.js: - - Import buildRisExportContent, buildBibtexExportContent, buildCslJsonExportContent from './export' - - buildSingleRisContent(cslItem): wraps cslItem as [{ id: cslItem.id || 'citation', csl: cslItem }], calls buildRisExportContent with 'chicago-notes-bibliography' style, returns result - - buildSingleBibtexContent(cslItem, { CiteCtor } = {}): same wrapping, calls buildBibtexExportContent async, passes CiteCtor through for test injection - - buildSingleCslJsonContent(cslItem): returns JSON.stringify([cslItem], null, 2) + '\n' directly (no sort needed, simpler than going through buildCslJsonExportContent which wraps into citation records) - - Note on buildSingleCslJsonContent: The base buildCslJsonExportContent maps citation.csl — for single-entry frontend use it is simpler and more predictable to serialize the cslItem array directly. This avoids the citation-record wrapping round-trip and is consistent with what the frontend embed already stores. - - - - -npm test -- --testPathPattern="export-single" --passWithNoTests=false - - - -- src/lib/export-single.js exports buildSingleRisContent, buildSingleBibtexContent, buildSingleCslJsonContent -- All unit tests pass (red commit then green commit per TDD cycle) -- buildSingleRisContent is synchronous; buildSingleBibtexContent is async -- Non-DOI thesis entry produces valid RIS output without errors -- CiteCtor injection works in buildSingleBibtexContent (no real @citation-js load in tests) - - - -After completion, create `.planning/phases/02-frontend-cite-and-export-affordances/02-01-SUMMARY.md` - diff --git a/.planning/phases/02-frontend-cite-and-export-affordances/02-02-PLAN.md b/.planning/phases/02-frontend-cite-and-export-affordances/02-02-PLAN.md deleted file mode 100644 index a8f4680..0000000 --- a/.planning/phases/02-frontend-cite-and-export-affordances/02-02-PLAN.md +++ /dev/null @@ -1,200 +0,0 @@ ---- -phase: 02-frontend-cite-and-export-affordances -plan: "02-02" -type: tdd -wave: 1 -depends_on: [] -files_modified: - - src/save-markup.js - - block.json - - webpack.config.js -autonomous: true -requirements: - - REQ-FE-01 - - REQ-FE-03 - - REQ-FE-04 - - REQ-FE-05 - -must_haves: - truths: - - "Each
  • in rendered save markup has a data-csl attribute containing escaped JSON" - - "data-csl JSON contains the full csl object for that entry" - - "Angle-bracket characters in title fields are escaped as \\u003c in data-csl" - - "block.json declares viewScript pointing to build/view.js" - - "webpack.config.js includes a 'view' entry for src/view.js" - - "Save markup without JS is fully readable (bibliography text unaffected)" - artifacts: - - path: "src/save-markup.js" - provides: "data-csl attribute on each
  • " - contains: "data-csl" - - path: "block.json" - provides: "viewScript registration" - contains: "viewScript" - - path: "webpack.config.js" - provides: "view entry point" - contains: "view:" - key_links: - - from: "block.json viewScript" - to: "build/view.js" - via: "file:./build/view.js reference" - pattern: "viewScript.*file:.*build/view" - - from: "src/save-markup.js
  • " - to: "data-csl attribute" - via: "JSON.stringify(citation.csl).replace" - pattern: "data-csl" ---- - - -Embed per-entry CSL-JSON into save markup and wire the viewScript entry point so WordPress will auto-enqueue the frontend script when the block is present. - -Purpose: The view.js (Plan 03) needs two things from this plan: (1) CSL-JSON data co-located with each bibliography entry in saved HTML, and (2) WordPress to auto-enqueue view.js only on pages containing the block. -Output: Modified save-markup.js with data-csl on each li; block.json with viewScript; webpack.config.js with view entry. No render_callback added. - - - -@/Users/danknauss/.claude/get-shit-done/workflows/execute-plan.md -@/Users/danknauss/.claude/get-shit-done/templates/summary.md - - - -@.planning/PROJECT.md -@.planning/ROADMAP.md -@.planning/phases/02-frontend-cite-and-export-affordances/02-RESEARCH.md -@src/save-markup.js -@src/save.test.js - - - - -```jsx -
  • -``` - - - - - -"editorScript": "file:./build/index.js", -"editorStyle": "file:./build/index.css", -"style": "file:./build/style-index.css" - - - -entry: async () => { - return { - ...base, - validation: path.resolve(__dirname, 'src/validation.js'), - }; -} - - - - - - - - Task 1: Add data-csl attribute to save-markup li elements - src/save-markup.js, src/save.test.js - - - save output for a single citation includes data-csl attribute on the li element - - data-csl value is a valid JSON object matching the citation's csl property - - a citation with title containing angle brackets (e.g. "A title") has data-csl with < escaped as \\u003c - - the existing bibliography text content is unchanged (no regression) - - citations without csl.language still render (lang attribute remains optional) - - - In src/save-markup.js, inside the renderBibliographySave function, add a data-csl attribute to each li element: - - ```jsx -
  • - ``` - - The .replace(/ - - npm test -- --testPathPattern="src/save.test" --passWithNoTests=false - - - - src/save-markup.js has data-csl on every li - - New tests pass: data-csl present, JSON parses correctly, angle brackets escaped as \\u003c - - All existing save.test.js tests still pass - - - - - Task 2: Wire viewScript in block.json and webpack.config.js - block.json, webpack.config.js - - 1. In block.json, add "viewScript" field after "style": - "viewScript": "file:./build/view.js" - - WordPress 6.1+ auto-enqueues this script on frontend pages where the block is present. The generated handle is "bibliography-builder/bibliography-view-script". No PHP enqueue code is needed. - - 2. In webpack.config.js, add the view entry to the entry async function: - ```js - entry: async () => { - const base = typeof defaultEntry === 'function' ? await defaultEntry() : defaultEntry; - return { - ...base, - validation: path.resolve(__dirname, 'src/validation.js'), - view: path.resolve(__dirname, 'src/view.js'), - }; - }, - ``` - - Note: src/view.js does not exist yet (Plan 03 creates it). webpack will fail to build until view.js is created. This is expected — Plan 03 runs in Wave 2 after this plan. The build does not need to pass until Plan 03 completes. - - The viewScript field in block.json tells WordPress to load build/view.js only when the block is rendered on a page. This is zero-overhead for pages without bibliography blocks and requires no PHP changes — consistent with the no-render_callback requirement. - - - node -e "const b=require('./block.json'); if(!b.viewScript) process.exit(1); console.log('viewScript:', b.viewScript)" && node -e "require('./webpack.config.js')" && echo 'webpack config parses' - - - - block.json has "viewScript": "file:./build/view.js" - - webpack.config.js entry async function includes view: path.resolve(__dirname, 'src/view.js') - - webpack.config.js is syntactically valid (node -e require parses without error) - - No PHP files modified - - - - - - -Run after both tasks: -- `npm test -- --testPathPattern="src/save.test"` — all save tests pass including new data-csl cases -- `node -e "const b=require('./block.json'); console.log('viewScript:', b.viewScript)"` — prints the viewScript value -- `node -e "require('./webpack.config.js')" && echo 'webpack config parses'` — webpack config is syntactically valid -- `grep -n "view:" webpack.config.js` — shows the view entry - - - -- Every li in save markup output has data-csl with valid, \\u003c-escaped JSON -- block.json viewScript field is present and points to file:./build/view.js -- webpack.config.js entry includes view entry and is syntactically valid -- All existing save tests still pass, new data-csl tests pass -- No PHP files changed, no render_callback added - - - -After completion, create `.planning/phases/02-frontend-cite-and-export-affordances/02-02-SUMMARY.md` - diff --git a/.planning/phases/02-frontend-cite-and-export-affordances/02-03-PLAN.md b/.planning/phases/02-frontend-cite-and-export-affordances/02-03-PLAN.md deleted file mode 100644 index 2356b06..0000000 --- a/.planning/phases/02-frontend-cite-and-export-affordances/02-03-PLAN.md +++ /dev/null @@ -1,282 +0,0 @@ ---- -phase: 02-frontend-cite-and-export-affordances -plan: "02-03" -type: tdd -wave: 2 -depends_on: - - "02-01" - - "02-02" -files_modified: - - src/view.js - - src/view.test.js - - src/style.scss -autonomous: true -requirements: - - REQ-FE-01 - - REQ-FE-02 - - REQ-FE-04 - - REQ-FE-05 - - REQ-FE-06 - -must_haves: - truths: - - "view.js reads data-csl from each li and appends a
    cite panel" - - "The cite panel has Copy RIS, Download RIS, Download BibTeX, and Download CSL-JSON buttons" - - "bibliography-builder-js-ready class is added to the block wrapper after panels are injected" - - "li elements without data-csl are silently skipped (old saved blocks work)" - - "Malformed data-csl JSON does not throw — entry is skipped" - - "CSS hides .bibliography-builder-cite-controls until .bibliography-builder-js-ready is present" - artifacts: - - path: "src/view.js" - provides: "Frontend DOMContentLoaded hydration script" - exports: [] - - path: "src/view.test.js" - provides: "JSDOM unit tests for cite panel injection and progressive enhancement" - min_lines: 80 - - path: "src/style.scss" - provides: "Progressive enhancement CSS for cite controls" - contains: "bibliography-builder-cite-controls" - key_links: - - from: "src/view.js" - to: "src/lib/export-single.js" - via: "buildSingleRisContent, buildSingleBibtexContent, buildSingleCslJsonContent imports" - pattern: "from './lib/export-single'" - - from: "src/view.js" - to: "src/lib/clipboard.js" - via: "copyTextToClipboard import" - pattern: "from './lib/clipboard'" - - from: "src/view.js" - to: "src/lib/export.js" - via: "downloadTextExport, MIME type constants imports" - pattern: "from './lib/export'" - - from: "CSS .bibliography-builder-js-ready" - to: ".bibliography-builder-cite-controls visibility" - via: "parent class selector" - pattern: "bibliography-builder-js-ready.*cite-controls" ---- - - -Create the frontend viewScript (view.js) that hydrates bibliography entries with Cite/Export controls, and add the progressive-enhancement CSS to style.scss. - -Purpose: This is the user-visible deliverable of Phase 2. When a reader visits a page with a bibliography block, JS adds per-entry "Cite / Export" disclosure panels with copy and download buttons for RIS, BibTeX, and CSL-JSON formats. Without JS, the bibliography renders identically to before. -Output: src/view.js (DOMContentLoaded hydration), src/view.test.js (JSDOM unit tests), src/style.scss additions. - - - -@/Users/danknauss/.claude/get-shit-done/workflows/execute-plan.md -@/Users/danknauss/.claude/get-shit-done/templates/summary.md - - - -@.planning/PROJECT.md -@.planning/ROADMAP.md -@.planning/phases/02-frontend-cite-and-export-affordances/02-RESEARCH.md -@.planning/phases/02-frontend-cite-and-export-affordances/02-01-SUMMARY.md -@.planning/phases/02-frontend-cite-and-export-affordances/02-02-SUMMARY.md - - - - -```javascript -export function buildSingleRisContent(cslItem) -// synchronous; returns RIS string for one CSL item - -export async function buildSingleBibtexContent(cslItem, { CiteCtor } = {}) -// async (dynamic import of @citation-js); returns BibTeX string - -export function buildSingleCslJsonContent(cslItem) -// synchronous; returns JSON array string with trailing newline -``` - - - -```javascript -export async function copyTextToClipboard(text, { navigatorRef, documentRef } = {}) -// returns true on success; throws if clipboard unavailable -``` - - - -```javascript -export function downloadTextExport({ content, filename, mimeType }, { documentRef, urlRef, BlobCtor } = {}) -export const RIS_EXPORT_MIME_TYPE // 'application/x-research-info-systems;charset=utf-8' -export const BIBTEX_EXPORT_MIME_TYPE // 'text/x-bibtex;charset=utf-8' -export const CSL_JSON_EXPORT_MIME_TYPE // 'application/vnd.citationstyles.csl+json;charset=utf-8' -``` - - - - - - - - - - Frontend cite panel hydration (view.js) - src/view.js, src/view.test.js - - DOM injection (unit-testable via JSDOM): - - buildCitePanel(cslItem) returns a
    element with class 'bibliography-builder-cite-controls' - - The
    contains a with text 'Cite / Export' - - The
    contains a
    with buttons - - Buttons present: 'Copy RIS', 'Copy BibTeX', 'Download .ris', 'Download .bib', 'Download CSL-JSON' - - Each button has type="button" - - Hydration (unit-testable via JSDOM with mocked document): - - hydrateBibliography(container) finds all li[data-csl] within the container and appends cite panels - - After hydrateBibliography, the container has class 'bibliography-builder-js-ready' - - li elements without data-csl are skipped (no panel appended) - - li with invalid JSON in data-csl is skipped (no panel, no throw) - - li with null or non-object JSON in data-csl is skipped - - Button behavior (unit tests use stubs for downloadTextExport and copyTextToClipboard): - - 'Copy RIS' click calls copyTextToClipboard with buildSingleRisContent(cslItem) result - - 'Download .ris' click calls downloadTextExport with RIS content and RIS MIME type - - 'Download .bib' click awaits buildSingleBibtexContent then calls downloadTextExport - - 'Download CSL-JSON' click calls downloadTextExport with CSL-JSON content and CSL-JSON MIME type - - - TDD red-green-refactor. - - IMPORTANT: Export buildCitePanel and hydrateBibliography as named exports from view.js so they can be tested in isolation. The DOMContentLoaded listener at module scope is the only non-exported code. - - src/view.js structure: - 1. Imports: copyTextToClipboard from ./lib/clipboard; downloadTextExport, RIS/BIBTEX/CSL_JSON_EXPORT_MIME_TYPE from ./lib/export; buildSingleRisContent, buildSingleBibtexContent, buildSingleCslJsonContent from ./lib/export-single - 2. export function buildCitePanel(cslItem, { copyFn, downloadFn } = {}) — creates and returns the
    element. Accept copyFn and downloadFn as injectable dependencies for testing (default to copyTextToClipboard and downloadTextExport). - 3. export function hydrateBibliography(container, { copyFn, downloadFn } = {}) — finds li[data-csl], parses JSON, appends panel, marks container js-ready. - 4. DOMContentLoaded listener calls hydrateBibliography on each .wp-block-bibliography-builder-bibliography element. - - src/view.test.js: Use JSDOM (Jest's default jsdom environment). Mock imports: - - jest.mock('./lib/export-single', ...) with sync stubs that return predictable strings - - jest.mock('./lib/clipboard', ...) with a stub returning Promise.resolve(true) - - jest.mock('./lib/export', ...) returning a stub downloadTextExport and the real MIME type constants - - i18n note: Button labels are hardcoded English in this phase. The open question about data-i18n attributes is deferred — hardcoded labels are acceptable for Phase 2. Track as follow-on. - - - - - - - Task 1: TDD — view.js cite panel and hydration logic - src/view.js, src/view.test.js - - Per the feature behavior block above — run RED commit then GREEN commit. - - - Follow TDD cycle strictly: - - RED: Write src/view.test.js with: - - Tests for buildCitePanel(cslItem) DOM structure (class, summary text, button count/labels, button types) - - Tests for hydrateBibliography(container): panels added to li[data-csl] entries, js-ready class added, li without data-csl skipped, invalid JSON skipped - - Tests for button click behavior using injected copyFn/downloadFn stubs (no real async imports needed) - - Mock strategy in test file: - ```js - jest.mock('./lib/export-single', () => ({ - buildSingleRisContent: jest.fn(() => 'RIS_CONTENT'), - buildSingleBibtexContent: jest.fn(async () => 'BIBTEX_CONTENT'), - buildSingleCslJsonContent: jest.fn(() => 'CSL_CONTENT'), - })); - jest.mock('./lib/clipboard', () => ({ - copyTextToClipboard: jest.fn(async () => true), - })); - jest.mock('./lib/export', () => ({ - downloadTextExport: jest.fn(), - RIS_EXPORT_MIME_TYPE: 'application/x-research-info-systems;charset=utf-8', - BIBTEX_EXPORT_MIME_TYPE: 'text/x-bibtex;charset=utf-8', - CSL_JSON_EXPORT_MIME_TYPE: 'application/vnd.citationstyles.csl+json;charset=utf-8', - })); - ``` - - Run `npm test -- --testPathPattern="src/view.test"` — tests must FAIL. - Commit: test(02-03): add failing tests for view.js cite panel hydration - - GREEN: Create src/view.js with buildCitePanel and hydrateBibliography exported, plus DOMContentLoaded listener. - Run tests — must PASS. - Commit: feat(02-03): implement view.js cite panel hydration - - The DOMContentLoaded listener is not tested directly — it is glue code calling the tested hydrateBibliography function. - - - npm test -- --testPathPattern="src/view.test" --passWithNoTests=false - - - - src/view.js exports buildCitePanel and hydrateBibliography - - src/view.test.js has tests covering DOM structure, hydration, skip-on-missing-data-csl, skip-on-invalid-JSON, button behavior - - All view.test.js tests pass - - RED commit then GREEN commit both present - - - - - Task 2: Progressive enhancement CSS in style.scss - src/style.scss - - Append to src/style.scss (do not replace existing content): - - ```scss - // Cite / Export panel — progressive enhancement - // Controls are hidden by default; view.js adds .bibliography-builder-js-ready - // to reveal them only when JS has run successfully. - .bibliography-builder-cite-controls { - display: none; - } - - .bibliography-builder-js-ready .bibliography-builder-cite-controls { - display: block; - } - - .bibliography-builder-cite-panel { - display: flex; - flex-wrap: wrap; - gap: 0.5em; - padding: 0.5em 0; - } - - .bibliography-builder-cite-panel button { - font-size: 0.875em; - cursor: pointer; - } - ``` - - This CSS ensures: - - No cite controls are visible when JS is disabled (REQ-FE-04: no-JS bibliography fully readable) - - Controls appear only after view.js adds bibliography-builder-js-ready (REQ-FE-05: deactivation-resilient) - - When plugin is deactivated, bibliography-builder-js-ready is never added, controls remain hidden - - Existing bibliography styles are unaffected (additive-only change) - - - npm run build && echo "Build clean" - - - - src/style.scss includes .bibliography-builder-cite-controls { display: none } - - src/style.scss includes .bibliography-builder-js-ready .bibliography-builder-cite-controls rules - - npm run build completes without errors - - build/view.js is present in the build output - - build/style-index.css includes the cite control rules - - - - - - -After both tasks: -- `npm test` — full suite passes (export-single + view + save tests all green) -- `npm run build` — build/view.js present, no build errors -- `grep -n "bibliography-builder-cite-controls" build/style-index.css` — CSS rule present in output -- `ls build/view.js` — viewScript compiled artifact exists - - - -- src/view.js exists, exports buildCitePanel and hydrateBibliography, has DOMContentLoaded listener -- src/view.test.js passes: DOM structure, hydration, skip logic, button wiring all covered -- src/style.scss hides cite controls by default, reveals under js-ready parent -- npm run build succeeds with build/view.js in output -- Full Jest suite green -- No PHP files modified - - - -After completion, create `.planning/phases/02-frontend-cite-and-export-affordances/02-03-SUMMARY.md` - diff --git a/.planning/phases/02-frontend-cite-and-export-affordances/02-RESEARCH.md b/.planning/phases/02-frontend-cite-and-export-affordances/02-RESEARCH.md deleted file mode 100644 index 03d10d2..0000000 --- a/.planning/phases/02-frontend-cite-and-export-affordances/02-RESEARCH.md +++ /dev/null @@ -1,560 +0,0 @@ -# Phase 2: Frontend Cite and Export Affordances - Research - -**Researched:** 2026-06-04 -**Domain:** WordPress static block, progressive-enhancement frontend interactivity, citation export (BibTeX/RIS/CSL-JSON), Clipboard API, Blob download -**Confidence:** HIGH - ---- - - -## Phase Requirements - -| ID | Description | Research Support | -|----|-------------|-----------------| -| REQ-FE-01 | Visible per-entry Cite/Export UI on the public bibliography frontend | viewScript + data-attribute hydration pattern; Google Scholar-style cite affordance | -| REQ-FE-02 | BibTeX, RIS, and CSL-JSON download/copy per entry | `buildBibtexExportContent`, `buildRisExportContent`, `buildCslJsonExportContent` in `src/lib/export.js` are single-entry capable after trivial per-entry extraction; Blob+anchor download pattern | -| REQ-FE-03 | No render_callback — all output stays in static save() | viewScript hydrates HTML already saved by save.js; no PHP render required for JS-layer behavior | -| REQ-FE-04 | No-JS bibliography remains fully readable | Existing `` + `
  • ` structure is complete text; cite/export controls are additive DOM elements hidden until JS runs or styled as graceful degradation | -| REQ-FE-05 | Preserves plugin-deactivation resilience | Citation data embedded in `data-csl` attributes is inert HTML; controls can be hidden by default (CSS: `display:none`) and shown by JS; deactivation removes JS but leaves readable bibliography markup intact | -| REQ-FE-06 | Mendeley/Zotero manual-import acceptance | Visible per-entry RIS and BibTeX download buttons directly serve the gap confirmed in Mendeley testing (non-DOI entries missed by auto-detection) | - - ---- - -## Summary - -This phase adds optional Scholar-like Cite/Export controls to the already-saved static bibliography frontend. The plugin uses a static `save()` function — no PHP `render_callback` — which constrains the approach: all interactive behavior must come from JavaScript that hydrates the saved HTML at runtime. - -The cleanest solution for this project is a `viewScript` frontend bundle registered in `block.json`. WordPress automatically enqueues `viewScript` only when the block is present on a page (since WP 6.1), making it zero-cost for posts without bibliography blocks. The script reads per-entry CSL-JSON data from attributes embedded during save and renders Cite/Export controls progressively. The WordPress Interactivity API is available but not required; it introduces a `data-wp-interactive` namespace coupling into saved block markup that becomes dead HTML if the plugin is deactivated — contrary to the deactivation-resilience requirement. Vanilla JS with `data-csl` attributes is the preferred approach. - -The existing `src/lib/export.js` already contains `buildBibtexExportContent`, `buildRisExportContent`, `buildCslJsonExportContent`, and `buildCslJsonExportContent`. These functions operate on arrays of citations and sort them by style; they need a thin wrapper to operate on a single CSL item. The existing `copyTextToClipboard` in `src/lib/clipboard.js` already handles the `navigator.clipboard` + `execCommand` fallback and is injectable for testing. - -**Primary recommendation:** Add a `viewScript` file (`src/view.js`) that reads per-entry CSL-JSON from `data-csl` attributes, builds a per-entry modal/details panel with Copy and Download buttons (BibTeX, RIS, CSL-JSON), and uses the existing export utilities. Embed per-entry CSL-JSON into the save markup using a `data-csl` attribute on each `
  • `. Keep controls hidden with CSS until JS initialises them. - ---- - -## Standard Stack - -### Core -| Library | Version | Purpose | Why Standard | -|---------|---------|---------|--------------| -| `@citation-js/core` | 0.7.18 (already in deps) | BibTeX generation | Already bundled; `buildBibtexExportContent` uses it async | -| `@citation-js/plugin-bibtex` | 0.7.18 (already in deps) | BibTeX/BibLaTeX format | Already bundled async chunk `citation-plugin-bibtex.js` | -| WordPress `viewScript` | WP 6.1+ | Frontend-only JS enqueue | Auto-enqueued when block present; zero overhead otherwise | -| Clipboard API (`navigator.clipboard`) | Baseline (2025) | Copy text to clipboard | `copyTextToClipboard` in `clipboard.js` already wraps it with fallback | - -### Supporting -| Library | Version | Purpose | When to Use | -|---------|---------|---------|-------------| -| `Blob` + `URL.createObjectURL` | Baseline | File download in browser | Already used by `downloadTextExport` in `export.js` | -| `
    `/`` HTML | HTML5 native | No-JS-accessible disclosure widget | Use as the cite panel toggle; degrades to always-open without CSS | - -### Alternatives Considered -| Instead of | Could Use | Tradeoff | -|------------|-----------|----------| -| `viewScript` + vanilla JS | WordPress Interactivity API (`viewScriptModule`) | Interactivity API requires `data-wp-interactive` in saved markup, which becomes dead/inert HTML post-deactivation. Also requires WP 6.5+ minimum. Not worth the constraint for this use case. | -| `data-csl` attribute per `
  • ` | Existing `