---
phase: 34-jump-to-deep-reading-button
plan: 01
type: execute
wave: 1
depends_on: []
files_modified:
  - paperforge/plugin/main.js
autonomous: true
requirements:
  - NAV-01
must_haves:
  truths:
    - "Per-paper dashboard card shows jump-to-deep-reading button when deep_reading_path exists AND deep_reading_status is 'done'"
    - "Button is fully hidden (not disabled) when conditions are not met — Copy Context shows instead"
    - "Clicking the button opens deep-reading.md in the active leaf via openLinkText()"
    - "When deep-reading.md is missing from disk despite the index claiming it, a Notice appears instead of a crash"
  artifacts:
    - path: "paperforge/plugin/main.js"
      provides: "LANG i18n keys + modified _renderNextStepCard() ready state"
      contains: "jump_to_deep_reading"
  key_links:
    - from: "_renderNextStepCard() ready state"
      to: "entry.deep_reading_path + entry.deep_reading_status"
      via: "conditional check before rendering button"
      pattern: "deep_reading_status.*done"
    - from: "Jump button click handler"
      to: "this.app.workspace.openLinkText()"
      via: "getAbstractFileByPath() verification first"
      pattern: "openLinkText"
---

<objective>
Add a "Jump to Deep Reading" button to the per-paper dashboard's next-step recommendation card.

Purpose: When a paper is fully processed (next_step === 'ready') and has deep_reading_status === 'done', the "All Set" card shows a button that opens the deep-reading.md file in Obsidian — replacing the generic "Copy Context" button. This provides a one-click path from per-paper dashboard into the deep-reading dashboard (Phase 32 → Phase 33 pipeline).

Output:
- paperforge/plugin/main.js modified with:
  - i18n keys in both LANG.zh and LANG.en for button label
  - Conditional logic in _renderNextStepCard() ready state to show jump button vs Copy Context
</objective>

<execution_context>
@C:/Users/Lin/.opencode/get-shit-done/workflows/execute-plan.md
@C:/Users/Lin/.opencode/get-shit-done/templates/summary.md
</execution_context>

<context>

@.planning/PROJECT.md
@.planning/STATE.md
@.planning/REQUIREMENTS.md

@.planning/phases/34-jump-to-deep-reading-button/34-CONTEXT.md

@paperforge/plugin/main.js

<interfaces>

From paperforge/plugin/main.js:

**LANG object** (lines 17-20):
```js
const LANG = {
    en: { /* ~145 keys as single-line object */ },
    zh: { /* ~145 keys as single-line object */ },
};
```
Pattern: each language has a flat key-value object. Keys are snake_case. Values are strings.

**t() function** (line 145):
```js
function t(key) { return (T && T[key]) || (LANG.en[key]) || key; }
```
T is set to LANG.zh by default (line 22), then overridden by langFromApp() at runtime. Adding a key to both LANG.en and LANG.zh means t('jump_to_deep_reading') returns the correct locale string.

**Object.assign(LANG.en) block** (lines 24-81) and **Object.assign(LANG.zh) block** (lines 83-127):
Additional keys appended via Object.assign after the main LANG definition. New keys should follow the same pattern.

**_renderNextStepCard() ready state** (lines 932-940):
```js
} else if (nextStep === 'ready') {
    // Show "Copy Context" shortcut for ready state (D-09)
    const trigger = card.createEl('button', { cls: 'paperforge-next-step-trigger' });
    trigger.createEl('span', { text: '\u2139  Copy Context' });
    trigger.addEventListener('click', () => {
        const action = ACTIONS.find(a => a.id === 'paperforge-copy-context');
        if (action) this._runAction(action, trigger);
    });
}
```

**_openFulltext() pattern** (lines 944-957) — reference for jump button click handler:
```js
_openFulltext(fulltextPath) {
    if (!fulltextPath) {
        new Notice('[!!] No fulltext path available for this paper', 6000);
        return;
    }
    const file = this.app.vault.getAbstractFileByPath(fulltextPath);
    if (file) {
        this.app.workspace.openLinkText(file.path, '');
    } else {
        new Notice('[!!] Fulltext file not found: ' + fulltextPath, 6000);
    }
}
```

**entry data shape** (consumed from canonical index, established by Phase 32):
```
entry.deep_reading_path   — string, relative vault path to deep-reading.md (e.g., "Literature/Ortho/ABCDEFG - Title/deep-reading.md")
entry.deep_reading_status — string, 'done' | 'pending' | 'processing' | 'failed'
```

</interfaces>

</context>

<tasks>

<task type="auto">
  <name>Task 1: Add i18n keys for jump-to-deep-reading button</name>
  <files>paperforge/plugin/main.js</files>
  <read_first>paperforge/plugin/main.js (lines 17-155 — full LANG object and t() function)</read_first>
  <action>
    Add a `jump_to_deep_reading` i18n key to both language packs in main.js:

    1. In the LANG.en initial object (line 18), add the key `jump_to_deep_reading: 'Open Deep Reading',` — insert it before the final closing brace of the en object.
       The en object is defined as a single-line object starting at line 18. Insert the new key as the last entry before the closing `}`.

    2. In the LANG.zh initial object (line 19), add the key `jump_to_deep_reading: '跳转到精读',` — insert it before the final closing brace of the zh object.
       The zh object is also a single-line object. Insert as the last entry before the closing `}`.

    Per D-06 and D-07: The label is accessed via `t('jump_to_deep_reading')` at runtime.
    No Object.assign blocks need updating — the keys go directly in the LANG.en and LANG.zh literal objects.
  </action>
  <verify>
    <automated>rg -n "jump_to_deep_reading" paperforge/plugin/main.js</automated>
  </verify>
  <acceptance_criteria>
    - `rg "jump_to_deep_reading" paperforge/plugin/main.js` shows exactly 2 matches: 1 in LANG.en ("Open Deep Reading"), 1 in LANG.zh ("跳转到精读")
    - The t() function at line 145 remains untouched
    - Node.js syntax check: `node -e "require('fs').readFileSync('paperforge/plugin/main.js','utf8').match(/const LANG = \{[\s\S]*?\};/)"` — no parse errors
  </acceptance_criteria>
  <done>i18n keys added: 'jump_to_deep_reading' resolves to "Open Deep Reading" (en) and "跳转到精读" (zh)</done>
</task>

<task type="auto">
  <name>Task 2: Modify _renderNextStepCard() ready state to show jump button</name>
  <files>paperforge/plugin/main.js</files>
  <read_first>paperforge/plugin/main.js (lines 889-957 — _renderNextStepCard function, _openFulltext pattern)</read_first>
  <action>
    Modify the `ready` case in `_renderNextStepCard()` (currently lines 932-940) to conditionally render either the jump-to-deep-reading button or the existing Copy Context button.

    The current code block (lines 932-940):
    ```js
    } else if (nextStep === 'ready') {
        // Show "Copy Context" shortcut for ready state (D-09)
        const trigger = card.createEl('button', { cls: 'paperforge-next-step-trigger' });
        trigger.createEl('span', { text: '\u2139  Copy Context' });
        trigger.addEventListener('click', () => {
            const action = ACTIONS.find(a => a.id === 'paperforge-copy-context');
            if (action) this._runAction(action, trigger);
        });
    }
    ```

    Replace with the following conditional logic (per D-01, D-03, D-04, D-05, D-08):

    ```js
    } else if (nextStep === 'ready') {
        if (entry.deep_reading_path && entry.deep_reading_status === 'done') {
            // D-01, D-03: Show jump-to-deep-reading button when paper has deep reading content
            const trigger = card.createEl('button', { cls: 'paperforge-next-step-trigger' });
            trigger.createEl('span', { text: '\uD83D\uDD0D  ' + t('jump_to_deep_reading') });
            trigger.addEventListener('click', () => {
                // D-04: Follow _openFulltext() pattern — verify file, open, error Notice
                const drFile = this.app.vault.getAbstractFileByPath(entry.deep_reading_path);
                if (drFile) {
                    this.app.workspace.openLinkText(drFile.path, '');
                } else {
                    // D-05: File missing from disk despite index claiming it exists
                    new Notice('[!!] ' + t('deep_reading_not_found'), 6000);
                }
            });
        } else {
            // D-02, D-03: Fall back to existing Copy Context button
            const trigger = card.createEl('button', { cls: 'paperforge-next-step-trigger' });
            trigger.createEl('span', { text: '\u2139  Copy Context' });
            trigger.addEventListener('click', () => {
                const action = ACTIONS.find(a => a.id === 'paperforge-copy-context');
                if (action) this._runAction(action, trigger);
            });
        }
    }
    ```

    Also add a second i18n key for the error notice message:
    - In LANG.en, add: `deep_reading_not_found: 'Deep reading file not found',` (before the closing brace)
    - In LANG.zh, add: `deep_reading_not_found: '精读文件未找到',` (before the closing brace)

    Note: The magnifying glass icon \uD83D\uDD0D is the same unicode used by the `/pf-deep` state at line 899 (per D-08). The `entry` and `t` variables are already in scope — no additional imports needed.
  </action>
  <verify>
    <automated>node -e "const src=require('fs').readFileSync('paperforge/plugin/main.js','utf8'); const hasJumpButton=src.includes('deep_reading_status'); const hasNotFoundKey=src.includes('deep_reading_not_found'); const hasOpenLinkText=src.includes('openLinkText'); console.log('jump_condition:',hasJumpButton,'not_found_key:',hasNotFoundKey,'openLinkText:',hasOpenLinkText)"</automated>
  </verify>
  <acceptance_criteria>
    - `rg "deep_reading_status.*done" paperforge/plugin/main.js` matches in _renderNextStepCard()
    - `rg "deep_reading_not_found" paperforge/plugin/main.js` matches exactly 2 times (en + zh)
    - `rg "openLinkText" paperforge/plugin/main.js` matches at least 2 times (existing _openFulltext + new jump button handler)
    - Node.js test: `node -e "require('fs').readFileSync('paperforge/plugin/main.js','utf8')"` — no syntax errors (JS is valid)
    - The old unconditional "Copy Context" render (without the deep_reading_path / deep_reading_status guard) is removed — the else branch keeps Copy Context but only as fallback
  </acceptance_criteria>
  <done>_renderNextStepCard() ready state conditionally shows jump-to-deep-reading button when deep_reading_path exists and deep_reading_status is 'done', falling back to Copy Context otherwise</done>
</task>

</tasks>

<verification>
All checks are automated:

1. i18n keys present: `rg -c "jump_to_deep_reading" paperforge/plugin/main.js` = 2
2. i18n keys present: `rg -c "deep_reading_not_found" paperforge/plugin/main.js` = 2
3. Conditional jump logic: `rg "deep_reading_status" paperforge/plugin/main.js` matches in _renderNextStepCard
4. openLinkText usage: `rg "openLinkText" paperforge/plugin/main.js` >= 2 matches
5. No syntax errors: `node -e "require('fs').readFileSync('paperforge/plugin/main.js','utf8')"` runs without error
6. Verify old unconditional Copy Context is gone: `rg "Copy Context" paperforge/plugin/main.js` — the el branch keeps it, but the unconditional version (without the deep_reading checks) is replaced
</verification>

<success_criteria>
- [ ] i18n keys added for `jump_to_deep_reading` and `deep_reading_not_found` in both LANG.zh and LANG.en
- [ ] _renderNextStepCard() ready state conditionally checks `entry.deep_reading_path && entry.deep_reading_status === 'done'`
- [ ] Jump button click handler follows _openFulltext() pattern: getAbstractFileByPath → openLinkText → Notice on missing
- [ ] JS syntax valid — no breakage of existing dashboard rendering
- [ ] All 5 locked decisions (D-01, D-03, D-04, D-05, D-08) implemented
- [ ] All 4 roadmap success criteria satisfied
</success_criteria>

<output>
After completion, create `.planning/phases/34-jump-to-deep-reading-button/34-01-SUMMARY.md`
</output>
