diff --git a/paperforge/plugin/main.js b/paperforge/plugin/main.js index ef4f11d6..67083316 100644 --- a/paperforge/plugin/main.js +++ b/paperforge/plugin/main.js @@ -1460,11 +1460,20 @@ class PaperForgeStatusView extends ItemView { const truncated = extracted.length > 200 ? extracted.slice(0, 200) + '...' : extracted; excerptEl.setText(truncated); if (extracted.length > 200) { - const expandBtn = body.createEl('button', { cls: 'paperforge-paper-overview-expand', text: '展开' }); + const expandContainer = body.createEl('div', { cls: 'paperforge-expand-container' }); + const expandBtn = expandContainer.createEl('button', { cls: 'paperforge-expand-icon', title: '展开/收起' }); + + // Insert arrow SVG + expandBtn.innerHTML = ''; + let expanded = false; - expandBtn.addEventListener('click', () => { + + // Make the whole container clickable + expandContainer.addEventListener('click', () => { excerptEl.setText(expanded ? truncated : extracted); - expandBtn.setText(expanded ? '展开' : '收起'); + expandBtn.innerHTML = expanded + ? '' + : ''; expanded = !expanded; }); } @@ -1556,14 +1565,23 @@ class PaperForgeStatusView extends ItemView { : (qa.answer || ''); aEl.createEl('span', { cls: 'paperforge-discussion-a-text', text: '解答:' + shortAnswer }); if (qa.answer && qa.answer.length > 150) { - const expandLink = aEl.createEl('button', { cls: 'paperforge-discussion-expand', text: '展开' }); + const expandContainer = aEl.createEl('div', { cls: 'paperforge-expand-container' }); + const expandBtn = expandContainer.createEl('button', { cls: 'paperforge-expand-icon', title: '展开/收起' }); + + // Insert arrow SVG + expandBtn.innerHTML = ''; + let expanded = false; - expandLink.addEventListener('click', () => { + + // Make the whole container clickable + expandContainer.addEventListener('click', () => { const textSpan = aEl.querySelector('.paperforge-discussion-a-text'); if (textSpan) { textSpan.setText(expanded ? ('解答:' + shortAnswer) : ('解答:' + qa.answer)); } - expandLink.setText(expanded ? '展开' : '收起'); + expandBtn.innerHTML = expanded + ? '' + : ''; expanded = !expanded; }); } @@ -1692,9 +1710,19 @@ class PaperForgeStatusView extends ItemView { }); // Show "Run in [agent_platform]" label below the button (DASH-02) - const platform = this.app.plugins.plugins['paperforge']?.settings?.agent_platform || 'opencode'; + const platformKey = this.app.plugins.plugins['paperforge']?.settings?.agent_platform || 'opencode'; + const AGENTS = { + 'opencode': 'OpenCode', + 'claude': 'Claude Code', + 'cursor': 'Cursor', + 'github_copilot': 'GitHub Copilot', + 'windsurf': 'Windsurf', + 'codex': 'Codex', + 'cline': 'Cline' + }; + const platformName = AGENTS[platformKey] || platformKey; const labelEl = card.createEl('div', { cls: 'paperforge-agent-platform-label' }); - labelEl.setText(t('run_in_agent').replace('{0}', platform)); + labelEl.setText(t('run_in_agent').replace('{0}', platformName)); } else if (nextStep === 'ready') { const trigger = card.createEl('button', { cls: 'paperforge-next-step-trigger' }); trigger.createEl('span', { text: '✓ ' + info.label }); @@ -1806,7 +1834,7 @@ class PaperForgeStatusView extends ItemView { { cls: 'pending', value: ocrPending, label: 'Pending' }, { cls: 'active', value: ocrProcessing, label: 'Processing' }, { cls: 'done', value: ocrDone, label: 'Done' }, - { cls: 'failed', value: ocrFailed, label: 'Needs Attention' }, + { cls: 'failed', value: ocrFailed, label: 'Attention' }, ]; for (const l of ocrLabels) { const cnt = ocrCounts.createEl('div', { cls: 'paperforge-ocr-count' }); diff --git a/paperforge/plugin/styles.css b/paperforge/plugin/styles.css index 842b8a35..f861ef3a 100644 --- a/paperforge/plugin/styles.css +++ b/paperforge/plugin/styles.css @@ -8,18 +8,25 @@ font-family: inherit; font-size: 14px; color: var(--text-normal); - padding: 14px; + padding: 14px 14px 56px 14px; height: 100%; overflow-y: auto; + scrollbar-gutter: stable; display: flex; flex-direction: column; gap: 12px; + container-type: inline-size; + container-name: pfpanel; --pf-surface: var(--background-secondary); --pf-surface-alt: var(--background-primary-alt); --pf-border: var(--background-modifier-border); --pf-border-hover: var(--background-modifier-border-hover); --pf-accent: var(--interactive-accent); --pf-radius: var(--radius-m); + --pf-paper-accent: #6f8780; + --pf-collection-accent: #9a6b52; + --pf-global-accent: #5f7180; + --pf-warm-line: #a28a5d; } /* ── Typography tokens ── */ @@ -343,7 +350,11 @@ } .paperforge-progress-seg.pending { - background: var(--text-faint); + background: #e2d9c8; +} + +.theme-dark .paperforge-progress-seg.pending { + background: #4a4135; } .paperforge-progress-seg.active { @@ -352,7 +363,11 @@ } .paperforge-progress-seg.done { - background: var(--color-green); + background: #3e5a47; +} + +.theme-dark .paperforge-progress-seg.done { + background: #4f735b; } .paperforge-progress-seg.failed { @@ -380,8 +395,8 @@ } .paperforge-ocr-count-value { - font-size: var(--font-ui-medium); - font-weight: var(--font-bold); + font-size: 18px; + font-weight: 600; color: var(--text-normal); line-height: 1.2; } @@ -1203,6 +1218,7 @@ font-weight: var(--font-semibold); padding: 2px 8px; border-radius: 10px; + border: 1px solid transparent; text-transform: uppercase; letter-spacing: 0.5px; line-height: 1.4; @@ -1210,18 +1226,21 @@ } .paperforge-mode-badge.global { - background: var(--interactive-accent); - color: var(--text-on-accent); + background: color-mix(in srgb, var(--pf-global-accent) 12%, var(--background-secondary)); + border-color: color-mix(in srgb, var(--pf-global-accent) 28%, var(--pf-border)); + color: var(--pf-global-accent); } .paperforge-mode-badge.paper { - background: var(--color-cyan); - color: var(--text-on-accent); + background: color-mix(in srgb, var(--pf-paper-accent) 12%, var(--background-secondary)); + border-color: color-mix(in srgb, var(--pf-paper-accent) 28%, var(--pf-border)); + color: var(--pf-paper-accent); } .paperforge-mode-badge.collection { - background: var(--color-purple); - color: var(--text-on-accent); + background: color-mix(in srgb, var(--pf-collection-accent) 12%, var(--background-secondary)); + border-color: color-mix(in srgb, var(--pf-collection-accent) 28%, var(--pf-border)); + color: var(--pf-collection-accent); } .paperforge-mode-name { @@ -1294,36 +1313,6 @@ flex-wrap: wrap; } -.paperforge-contextual-btn { - display: inline-flex; - align-items: center; - gap: 6px; - padding: 6px 14px; - font-size: var(--font-ui-small); - font-weight: var(--font-semibold); - color: var(--text-normal); - background: var(--background-secondary-alt); - border: 1px solid var(--background-modifier-border); - border-radius: var(--radius-m); - cursor: pointer; - transition: background 0.15s, color 0.15s; - user-select: none; -} - -.paperforge-contextual-btn:hover { - background: var(--background-modifier-hover); - color: var(--text-accent); -} - -.paperforge-contextual-btn:active { - transform: scale(0.97); -} - -.paperforge-contextual-btn-icon { - font-size: 14px; - line-height: 1; -} - /* ========================================================================== SECTION 16 — Next-Step Recommendation Card ========================================================================== */ @@ -1393,6 +1382,8 @@ display: flex; flex-direction: column; gap: 24px; + max-width: 800px; + margin: 0 auto; } .paperforge-collection-metrics { @@ -1687,65 +1678,170 @@ } /* ========================================================================== - SECTION 39 — Shared Components (Native Light Surface Design) + SECTION 39 — Dashboard Visual System ========================================================================== */ -/* ── Section Label ── */ +/* Shared tokens */ .paperforge-section-label { font-size: 12px; font-weight: 600; color: var(--text-muted); + margin: 0 0 10px 0; + padding-left: 10px; + border-left: 3px solid var(--pf-warm-line); text-transform: uppercase; - letter-spacing: 0.04em; - margin-bottom: 6px; + letter-spacing: 0.06em; + line-height: 1.3; +} + +.paperforge-contextual-btn { + display: inline-flex; + align-items: center; + gap: 5px; + padding: 5px 12px; + background: var(--background-secondary); + border: 1px solid var(--pf-border); + border-radius: var(--radius-s); + color: var(--text-muted); + font-size: 12px; + font-weight: 500; + cursor: pointer; + transition: background 0.15s, border-color 0.15s, color 0.15s; + white-space: nowrap; + user-select: none; +} + +.paperforge-contextual-btn:hover { + background: var(--background-modifier-hover); + border-color: color-mix(in srgb, var(--pf-warm-line) 52%, var(--pf-border-hover)); + color: var(--text-normal); +} + +.paperforge-contextual-btn.running { + opacity: 0.6; + pointer-events: none; +} + +.paperforge-contextual-btn:active { + transform: scale(0.97); +} + +.paperforge-contextual-btn:focus-visible { + outline: 2px solid color-mix(in srgb, var(--interactive-accent) 62%, white 38%); + outline-offset: 2px; +} + +.paperforge-contextual-btn-icon { + font-size: 14px; + line-height: 1; } -/* ── Status Pill ── */ .paperforge-status-pill { display: inline-flex; align-items: center; gap: 4px; - padding: 2px 8px; + padding: 2px 10px; + border: 1px solid var(--pf-border); border-radius: 999px; - background: var(--background-primary-alt); - color: var(--text-muted); font-size: 12px; font-weight: 500; - line-height: 1.4; -} - -.paperforge-status-pill-icon { font-size: 11px; line-height: 1; } - -.paperforge-status-pill.ok { color: var(--text-success); } -.paperforge-status-pill.fail { color: var(--text-error); } -.paperforge-status-pill.pending { color: var(--text-muted); } - -/* ── Cards (only primary content modules) ── */ -.paperforge-paper-overview, -.paperforge-discussion-card { - margin: 8px 0; - padding: 12px; - background: var(--background-secondary); - border: 1px solid var(--background-modifier-border); - border-radius: var(--radius-m); -} - -/* ── Paper Overview ── */ -.paperforge-paper-overview-header { margin-bottom: 6px; } - -.paperforge-paper-overview-title { - font-size: 12px; - font-weight: 600; + line-height: 1.5; + background: var(--background-primary-alt); color: var(--text-muted); +} + +.paperforge-status-pill.pending { + background: color-mix(in srgb, var(--pf-paper-accent) 9%, var(--background-primary)); + border-color: color-mix(in srgb, var(--pf-paper-accent) 20%, var(--pf-border)); + color: var(--pf-paper-accent); +} + +.paperforge-status-pill.ok { + background: rgba(var(--background-modifier-success-rgb, 0, 200, 100), 0.12); + border-color: rgba(var(--background-modifier-success-rgb, 0, 200, 100), 0.2); + color: var(--text-success, var(--color-green)); +} + +.paperforge-status-pill.fail { + background: rgba(var(--background-modifier-error-rgb, 255, 80, 80), 0.12); + border-color: rgba(var(--background-modifier-error-rgb, 255, 80, 80), 0.22); + color: var(--text-error); +} + +.paperforge-status-pill-icon { + font-size: 11px; + line-height: 1; +} + +.paperforge-library-snapshot, +.paperforge-system-status, +.paperforge-global-actions { + padding: 20px; + background: color-mix(in srgb, var(--background-primary) 92%, var(--background-secondary) 8%); + border: 1px solid color-mix(in srgb, var(--pf-warm-line) 20%, var(--pf-border)); + border-radius: calc(var(--pf-radius) + 2px); + box-shadow: 0 1px 2px rgba(20, 18, 14, 0.04), 0 10px 24px rgba(20, 18, 14, 0.04); + transition: border-color 0.2s, box-shadow 0.2s, transform 0.2s; +} + +.paperforge-library-snapshot:hover, +.paperforge-system-status:hover, +.paperforge-global-actions:hover { + border-color: color-mix(in srgb, var(--pf-warm-line) 42%, var(--pf-border)); + box-shadow: 0 2px 10px rgba(20, 18, 14, 0.08), 0 14px 28px rgba(20, 18, 14, 0.06); +} + +/* Paper mode */ +.paperforge-status-strip { + display: flex; + align-items: center; + justify-content: space-between; + flex-wrap: wrap; + gap: 10px; + padding: 10px 0 8px; + margin: 4px 0 6px; +} + +.paperforge-status-strip-left, +.paperforge-status-strip-right { + display: flex; + align-items: center; + gap: 6px; + flex-wrap: wrap; +} + +.paperforge-paper-overview { + margin: 10px 0; + padding: 18px; + background: var(--background-primary); + border: 1px solid color-mix(in srgb, var(--pf-warm-line) 18%, var(--pf-border)); + border-radius: calc(var(--pf-radius) + 2px); + box-shadow: inset 3px 0 0 color-mix(in srgb, var(--pf-paper-accent) 16%, transparent); + transition: border-color 0.2s, box-shadow 0.2s; +} + +.paperforge-paper-overview:hover { + border-color: color-mix(in srgb, var(--pf-warm-line) 34%, var(--pf-border)); + box-shadow: inset 3px 0 0 color-mix(in srgb, var(--pf-paper-accent) 22%, transparent), 0 2px 8px rgba(0, 0, 0, 0.06); +} + +.paperforge-paper-overview-header { + margin-bottom: 8px; +} + +.paperforge-paper-overview-title, +.paperforge-discussion-title { + font-size: 12px; + font-weight: 700; + color: var(--pf-paper-accent); text-transform: uppercase; - letter-spacing: 0.04em; + letter-spacing: 0.06em; } .paperforge-paper-overview-body { font-size: 14px; - font-weight: 400; - color: var(--text-muted); - line-height: 1.55; + color: var(--text-normal); + line-height: 1.72; } .paperforge-paper-overview-excerpt { @@ -1753,38 +1849,74 @@ word-break: break-word; } -.paperforge-paper-overview-expand { - display: inline-block; - margin-top: 4px; - padding: 2px 0; - background: none; - border: none; - color: var(--text-accent); - font-size: 13px; - font-weight: 400; - cursor: pointer; - transition: color 0.15s; +.paperforge-expand-container, +.paperforge-discussion-viewall { + color: var(--pf-paper-accent); } -.paperforge-paper-overview-expand:hover { +.paperforge-expand-container { + display: flex; + justify-content: center; + align-items: center; + width: 100%; + margin-top: 8px; + padding: 4px 0; + cursor: pointer; + transition: background-color 0.2s ease, color 0.2s ease; + border-radius: var(--pf-radius); + background: linear-gradient(to bottom, transparent, color-mix(in srgb, var(--pf-surface-alt) 50%, transparent)); +} + +.paperforge-expand-icon { + background: none; + border: none; + padding: 0; + margin: 0; + color: inherit; + display: flex; + align-items: center; + justify-content: center; + pointer-events: none; /* Let container handle clicks */ + opacity: 0.6; + transition: opacity 0.2s ease; +} + +.paperforge-expand-container:hover, +.paperforge-discussion-viewall:hover { color: var(--interactive-accent-hover); } -/* ── Discussion Card ── */ -.paperforge-discussion-header { margin-bottom: 8px; } +.paperforge-expand-container:hover { + background: color-mix(in srgb, var(--pf-surface-alt) 80%, transparent); +} -.paperforge-discussion-title { - font-size: 12px; - font-weight: 600; - color: var(--text-muted); - text-transform: uppercase; - letter-spacing: 0.04em; +.paperforge-expand-container:hover .paperforge-expand-icon { + opacity: 1; +} + +.paperforge-discussion-header { + margin-bottom: 10px; +} + +.paperforge-discussion-card { + margin: 10px 0; + padding: 18px; + background: var(--background-primary); + border: 1px solid color-mix(in srgb, var(--pf-warm-line) 18%, var(--pf-border)); + border-radius: calc(var(--pf-radius) + 2px); + box-shadow: inset 3px 0 0 color-mix(in srgb, var(--pf-paper-accent) 11%, transparent); + transition: border-color 0.2s, box-shadow 0.2s; +} + +.paperforge-discussion-card:hover { + border-color: color-mix(in srgb, var(--pf-warm-line) 34%, var(--pf-border)); + box-shadow: inset 3px 0 0 color-mix(in srgb, var(--pf-paper-accent) 16%, transparent), 0 2px 8px rgba(0, 0, 0, 0.06); } .paperforge-discussion-item { - margin-bottom: 8px; - padding-bottom: 8px; - border-bottom: 1px solid var(--background-modifier-border); + margin-bottom: 10px; + padding-bottom: 10px; + border-bottom: 1px solid var(--pf-border); } .paperforge-discussion-item:last-child { @@ -1803,82 +1935,92 @@ .paperforge-discussion-a { font-size: 14px; - font-weight: 400; - color: var(--text-muted); - line-height: 1.55; + color: var(--text-normal); + line-height: 1.68; } -.paperforge-discussion-expand { - display: inline; - padding: 0; - margin-left: 4px; - background: none; - border: none; - color: var(--text-accent); - font-size: 13px; - font-weight: 400; - cursor: pointer; - transition: color 0.15s; -} -.paperforge-discussion-expand:hover { color: var(--interactive-accent-hover); } .paperforge-discussion-viewall { display: block; text-align: right; - margin-top: 4px; - font-size: 13px; - font-weight: 400; - color: var(--text-accent); + margin-top: 6px; + font-size: 12px; + font-weight: 500; cursor: pointer; text-decoration: none; transition: color 0.15s; } -.paperforge-discussion-viewall:hover { color: var(--interactive-accent-hover); } - -/* ── Complete state row ── */ -.paperforge-complete-row { - display: flex; - align-items: center; - gap: 6px; - color: var(--text-success); - font-size: 13px; - font-weight: 400; - line-height: 1.4; - padding: 4px 0; +.paperforge-discussion-viewall:hover { + text-decoration: underline; +} + +.paperforge-paper-files { + display: flex; + gap: 8px; + margin: 10px 0; +} + +.paperforge-status-strip-right .paperforge-contextual-btn { + min-height: 34px; + padding: 7px 12px; + background: color-mix(in srgb, var(--background-secondary) 78%, var(--background-primary)); + border-color: color-mix(in srgb, var(--pf-border) 88%, var(--pf-warm-line)); + color: var(--text-muted); +} + +.paperforge-status-strip-right .paperforge-contextual-btn:hover { + background: color-mix(in srgb, var(--background-modifier-hover) 78%, var(--background-primary)); + border-color: color-mix(in srgb, var(--pf-border-hover) 82%, var(--pf-warm-line)); + color: var(--text-normal); } -/* ── Technical Details disclosure ── */ .paperforge-technical-details { - margin: 4px 0; + margin: 8px 0 4px; } .paperforge-technical-details-toggle { - display: inline-block; - padding: 4px 0; + width: 100%; + min-height: 34px; + padding: 8px 0; background: none; border: none; + border-radius: 0; color: var(--text-muted); - font-size: 13px; - font-weight: 400; + font-size: 12px; + font-weight: 600; cursor: pointer; - user-select: none; + text-align: left; + letter-spacing: 0.02em; transition: color 0.15s; } -.paperforge-technical-details-toggle:hover { color: var(--text-normal); } +.paperforge-technical-details-toggle:hover { + background: none; + border-color: transparent; + color: var(--text-normal); +} .paperforge-technical-details-body { - padding: 6px 0 2px 0; + margin-top: 0; + padding: 8px 0 0; + background: none; + border: none; + border-radius: 0; font-size: 12px; - color: var(--text-faint); } .paperforge-technical-row { display: flex; justify-content: space-between; - padding: 2px 0; + gap: 12px; + padding: 6px 0; + border-bottom: 1px solid color-mix(in srgb, var(--pf-border) 72%, transparent); +} + +.paperforge-technical-row:last-child { + border-bottom: none; } .paperforge-technical-label { @@ -1896,87 +2038,27 @@ text-align: right; } -/* ── Contextual buttons ── */ -.paperforge-contextual-btn { - display: inline-flex; - align-items: center; - gap: 4px; - padding: 3px 8px; - background: var(--interactive-normal); - border: 1px solid var(--background-modifier-border); - color: var(--text-normal); - border-radius: var(--button-radius, 4px); - font-size: 12px; - font-weight: 400; - cursor: pointer; - transition: background 0.15s; - white-space: nowrap; -} - -.paperforge-contextual-btn:hover { - background: var(--interactive-hover); - border-color: var(--background-modifier-border-hover); -} - -.paperforge-contextual-btn.running { - opacity: 0.6; - pointer-events: none; -} - -.paperforge-contextual-btn-icon { - font-size: 14px; - line-height: 1; -} - -.paperforge-contextual-btn.primary { - background: var(--interactive-accent); - color: var(--text-on-accent); - border-color: var(--interactive-accent); - font-weight: 500; -} - -.paperforge-contextual-btn.primary:hover { - opacity: 0.85; -} - -/* ========================================================================== - SECTION 40 — Per-Paper View - ========================================================================== */ - -/* ── Status Strip + Files Row (merged) ── */ -.paperforge-status-strip { - display: flex; - align-items: center; - justify-content: space-between; - flex-wrap: wrap; - gap: 8px; - padding: 6px 0; - margin: 4px 0; -} - -.paperforge-status-strip-left { - display: flex; - align-items: center; - gap: 6px; -} - -.paperforge-status-strip-right { - display: flex; - align-items: center; - gap: 6px; -} - -/* ── Workflow Toggles (inline, not a card) ── */ .paperforge-workflow-toggles { display: flex; + flex-wrap: wrap; gap: 14px; - padding: 6px 0; + margin: 0 0 10px; + padding: 0 0 10px; + background: none; + border: none; + border-bottom: 1px solid color-mix(in srgb, var(--pf-border) 72%, transparent); + border-radius: 0; } .paperforge-workflow-toggle { display: flex; align-items: center; gap: 6px; + min-height: 34px; + padding: 6px 10px; + background: color-mix(in srgb, var(--background-primary-alt) 88%, var(--background-primary)); + border: 1px solid color-mix(in srgb, var(--pf-border) 82%, var(--pf-paper-accent)); + border-radius: 999px; cursor: pointer; font-size: 13px; color: var(--text-muted); @@ -1992,28 +2074,25 @@ } .paperforge-workflow-toggle-label { - font-weight: 500; + font-weight: 600; color: var(--text-normal); } .paperforge-workflow-toggle-hint { color: var(--text-faint); - font-size: 12px; + font-size: 11px; } -/* ========================================================================== - SECTION 41 — Collection / Base View - ========================================================================== */ - +/* Collection mode */ .paperforge-collection-header { - margin-bottom: 8px; + margin-bottom: 10px; } .paperforge-collection-title { font-size: 16px; font-weight: 600; color: var(--text-normal); - line-height: 1.35; + line-height: 1.3; } .paperforge-collection-count { @@ -2022,629 +2101,87 @@ margin-top: 2px; } +.paperforge-global-view { + display: flex; + flex-direction: column; + gap: 18px; + max-width: 800px; + margin: 0 auto; +} + +.paperforge-global-view .paperforge-section-label, +.paperforge-workflow-overview .paperforge-section-label { + margin-bottom: 12px; +} + .paperforge-workflow-overview { - margin: 8px 0; - padding: 12px; - background: var(--background-secondary); - border: 1px solid var(--background-modifier-border); - border-radius: var(--radius-m); + margin: 10px 0; + padding: 18px; + background: color-mix(in srgb, var(--background-primary) 94%, var(--background-primary-alt) 6%); + border: 1px solid color-mix(in srgb, var(--pf-warm-line) 20%, var(--pf-border)); + border-radius: calc(var(--pf-radius) + 2px); + box-shadow: inset 3px 0 0 color-mix(in srgb, var(--pf-collection-accent) 12%, transparent); + transition: border-color 0.2s, box-shadow 0.2s; +} + +.paperforge-workflow-overview:hover { + border-color: color-mix(in srgb, var(--pf-warm-line) 42%, var(--pf-border)); + box-shadow: inset 3px 0 0 color-mix(in srgb, var(--pf-collection-accent) 17%, transparent), 0 2px 10px rgba(20, 18, 14, 0.08); } .paperforge-workflow-funnel { - display: flex; + display: grid; + grid-template-columns: 1fr auto 1fr auto 1fr auto 1fr; + gap: 8px 6px; align-items: center; - gap: 3px; - flex-wrap: wrap; - justify-content: center; +} + +@container pfpanel (max-width: 380px) { + .paperforge-workflow-funnel { + grid-template-columns: 1fr auto 1fr; + } + .paperforge-workflow-funnel > div:nth-child(4) { + display: none; + } +} + +@container pfpanel (max-width: 220px) { + .paperforge-workflow-funnel { + grid-template-columns: 1fr; + } + .paperforge-workflow-arrow { + display: none; + } } .paperforge-workflow-stage { display: flex; flex-direction: column; align-items: center; - padding: 6px 12px; - background: var(--background-primary-alt); - border-radius: 6px; - min-width: 52px; -} - -.paperforge-workflow-stage-value { - font-size: 18px; - font-weight: 600; - color: var(--text-normal); - line-height: 1.2; -} - -.paperforge-workflow-stage-label { - font-size: 11px; - color: var(--text-muted); - text-transform: uppercase; - letter-spacing: 0.04em; - margin-top: 2px; - text-align: center; -} - -.paperforge-workflow-arrow { - color: var(--text-faint); - font-size: 12px; - flex-shrink: 0; -} - -.paperforge-collection-ocr-header { - display: flex; - align-items: center; - gap: 10px; - margin-bottom: 14px; -} - -.paperforge-issue-summary { - margin: 8px 0; - padding: 10px 12px; - background: var(--background-secondary); - border: 1px solid var(--background-modifier-border); - border-left: 3px solid var(--color-orange, #e67e22); - border-radius: var(--radius-m); -} - -.paperforge-issue-summary .paperforge-section-label { - color: var(--color-orange, #e67e22); -} - -.paperforge-issue-list { - margin: 4px 0 6px 0; -} - -.paperforge-issue-item { - font-size: 14px; - color: var(--text-muted); - padding: 2px 0; -} - -.paperforge-issue-actions { - display: flex; - gap: 8px; - margin-top: 8px; -} - -.paperforge-collection-actions { - display: flex; - gap: 8px; - margin-top: 8px; - padding-top: 8px; - border-top: 1px solid var(--background-modifier-border); -} - -/* ========================================================================== - SECTION 42 — Global / Home View - ========================================================================== */ - -.paperforge-library-snapshot { - padding: 12px; - background: var(--background-secondary); - border: 1px solid var(--background-modifier-border); - border-radius: var(--radius-m); -} - -.paperforge-snapshot-pills { - display: flex; - gap: 8px; - flex-wrap: wrap; -} - -.paperforge-snapshot-pill { - display: flex; - flex-direction: column; - align-items: center; - padding: 8px 14px; - background: var(--background-primary-alt); - border-radius: 6px; - min-width: 56px; - flex: 1; -} - -.paperforge-snapshot-value { - font-size: 18px; - font-weight: 600; - color: var(--text-normal); - line-height: 1.2; -} - -.paperforge-snapshot-label { - font-size: 11px; - color: var(--text-muted); - text-transform: uppercase; - letter-spacing: 0.04em; - margin-top: 2px; - text-align: center; -} - -.paperforge-system-status { - padding: 12px; - background: var(--background-secondary); - border: 1px solid var(--background-modifier-border); - border-radius: var(--radius-m); -} - -.paperforge-status-grid { - display: flex; - flex-direction: column; - gap: 1px; -} - -.paperforge-status-row { - display: flex; - align-items: center; - gap: 8px; - padding: 3px 0; -} - -.paperforge-status-dot { - width: 7px; - height: 7px; - border-radius: 50%; - flex-shrink: 0; - background: var(--background-modifier-border); - transition: background 0.3s; -} - -.paperforge-status-dot.ok { background: var(--text-success); } -.paperforge-status-dot.fail { background: var(--text-error); } - -.paperforge-status-label { - font-size: 14px; - color: var(--text-muted); - min-width: 85px; -} - -.paperforge-status-detail { - font-size: 13px; - color: var(--text-faint); - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} - -.paperforge-global-actions { - padding: 12px; - background: var(--background-secondary); - border: 1px solid var(--background-modifier-border); - border-radius: var(--radius-m); -} - -.paperforge-global-actions-row { - display: flex; - gap: 8px; -} - -/* ========================================================================== - SECTION 43 — Dark Theme - ========================================================================== */ - -.theme-dark .paperforge-status-pill { - background: var(--background-primary-alt); -} - -.theme-dark .paperforge-status-pill.ok { - color: var(--text-success); -} - -.theme-dark .paperforge-status-pill.fail { - color: var(--text-error); -} - -/* ========================================================================== - SECTION 39 — Dashboard Redesign: Shared Components - ========================================================================== */ - -/* ── Section Label ── */ -.paperforge-section-label { - font-size: 11px; - font-weight: 600; - color: var(--text-muted); - margin: 0 0 10px 0; - padding-left: 10px; - border-left: 3px solid var(--interactive-accent); - text-transform: uppercase; - letter-spacing: 0.06em; - line-height: 1.3; -} - -/* ── Contextual Button ── */ -.paperforge-contextual-btn { - display: inline-flex; - align-items: center; - gap: 5px; - padding: 5px 12px; - background: var(--background-secondary); - border: 1px solid var(--background-modifier-border); - border-radius: var(--radius-s); - color: var(--text-muted); - font-size: 12px; - font-weight: 500; - cursor: pointer; - transition: background 0.15s, border-color 0.15s, color 0.15s; - white-space: nowrap; -} - -.paperforge-contextual-btn:hover { - background: var(--background-modifier-hover); - border-color: var(--interactive-accent); - color: var(--text-normal); -} - -.paperforge-contextual-btn.running { - opacity: 0.6; - pointer-events: none; -} - -.paperforge-contextual-btn-icon { - font-size: 14px; - line-height: 1; -} - -/* ========================================================================== - SECTION 40 — Per-Paper View (Reading Companion) - ========================================================================== */ - -/* ── Status Strip ── */ -.paperforge-status-strip { - display: flex; - gap: 6px; - padding: 8px 0; - margin: 6px 0; -} - -.paperforge-status-pill { - display: inline-flex; - align-items: center; - gap: 4px; - padding: 2px 10px; - border-radius: 4px; - font-size: 12px; - font-weight: 500; - line-height: 1.5; - background: var(--background-primary-alt); - color: var(--text-muted); -} - -.paperforge-status-pill.ok { - background: rgba(var(--background-modifier-success-rgb, 0, 200, 100), 0.12); - color: var(--text-success, var(--color-green)); -} - -.paperforge-status-pill.fail { - background: rgba(var(--background-modifier-error-rgb, 255, 80, 80), 0.12); - color: var(--text-error); -} - -.paperforge-status-pill.pending { - background: var(--background-primary-alt); - color: var(--text-muted); -} - -.paperforge-status-pill-icon { - font-size: 11px; - line-height: 1; -} - -/* Theme dark: softer pill backgrounds */ -.theme-dark .paperforge-status-pill.ok { - background: rgba(100, 200, 120, 0.15); -} -.theme-dark .paperforge-status-pill.fail { - background: rgba(255, 100, 100, 0.15); -} - -/* ── Paper Overview Card ── */ -.paperforge-paper-overview { - margin: 10px 0; - padding: 16px; - background: var(--background-primary); - border: 1px solid var(--background-modifier-border); - border-radius: var(--radius-m); - transition: border-color 0.2s, box-shadow 0.2s; - position: relative; -} - -.paperforge-paper-overview::before { - content: ''; - position: absolute; - inset: -1px; - border-radius: inherit; - opacity: 0; - box-shadow: 0 2px 8px rgba(0,0,0,0.08); - transition: opacity 0.2s; - pointer-events: none; -} - -.paperforge-paper-overview:hover::before { - opacity: 1; -} - -.paperforge-paper-overview-header { - margin-bottom: 8px; -} - -.paperforge-paper-overview-title { - font-size: 11px; - font-weight: 700; - color: var(--text-accent); - text-transform: uppercase; - letter-spacing: 0.06em; -} - -.paperforge-paper-overview-body { - font-size: 13.5px; - color: var(--text-muted); - line-height: 1.65; -} - -.paperforge-paper-overview-excerpt { - white-space: pre-wrap; - word-break: break-word; -} - -.paperforge-paper-overview-expand { - display: inline-block; - margin-top: 6px; - padding: 3px 0; - background: none; - border: none; - border-bottom: 1px dashed var(--text-accent); - color: var(--text-accent); - font-size: 12px; - font-weight: 500; - cursor: pointer; - transition: color 0.15s, border-color 0.15s; -} - -.paperforge-paper-overview-expand:hover { - color: var(--interactive-accent-hover); - border-color: var(--interactive-accent-hover); -} - -/* ── Discussion Card ── */ -.paperforge-discussion-card { - margin: 10px 0; - padding: 16px; - background: var(--background-primary); - border: 1px solid var(--background-modifier-border); - border-radius: var(--radius-m); - transition: border-color 0.2s, box-shadow 0.2s; - position: relative; -} - -.paperforge-discussion-card::before { - content: ''; - position: absolute; - inset: -1px; - border-radius: inherit; - opacity: 0; - box-shadow: 0 2px 8px rgba(0,0,0,0.08); - transition: opacity 0.2s; - pointer-events: none; -} - -.paperforge-discussion-card:hover::before { - opacity: 1; -} - -.paperforge-discussion-header { - margin-bottom: 10px; -} - -.paperforge-discussion-title { - font-size: 11px; - font-weight: 700; - color: var(--text-accent); - text-transform: uppercase; - letter-spacing: 0.06em; -} - -.paperforge-discussion-item { - margin-bottom: 10px; - padding-bottom: 10px; - border-bottom: 1px solid var(--background-modifier-border); -} - -.paperforge-discussion-item:last-child { - border-bottom: none; - margin-bottom: 0; - padding-bottom: 0; -} - -.paperforge-discussion-q { - font-size: 13.5px; - font-weight: 600; - color: var(--text-normal); - margin-bottom: 4px; - line-height: 1.5; -} - -.paperforge-discussion-a { - font-size: 13.5px; - color: var(--text-muted); - line-height: 1.6; -} - -.paperforge-discussion-expand { - display: inline; - padding: 0; - margin-left: 4px; - background: none; - border: none; - border-bottom: 1px dashed var(--text-accent); - color: var(--text-accent); - font-size: 12px; - font-weight: 500; - cursor: pointer; - transition: color 0.15s; -} - -.paperforge-discussion-expand:hover { - color: var(--interactive-accent-hover); -} - -.paperforge-discussion-viewall { - display: block; - text-align: right; - margin-top: 6px; - font-size: 12px; - font-weight: 500; - color: var(--text-accent); - cursor: pointer; - text-decoration: none; - transition: color 0.15s; -} - -.paperforge-discussion-viewall:hover { - color: var(--interactive-accent-hover); - text-decoration: underline; -} - -/* ── Paper Files Row ── */ -.paperforge-paper-files { - display: flex; - gap: 8px; - margin: 10px 0; -} - -/* ── Technical Details ── */ -.paperforge-technical-details { - margin: 6px 0; -} - -.paperforge-technical-details-toggle { + padding: 12px 6px; + background: color-mix(in srgb, var(--background-primary-alt) 88%, var(--background-primary) 12%); + border: 1px solid color-mix(in srgb, var(--pf-collection-accent) 22%, var(--pf-border)); + border-radius: 8px; + min-width: 0; width: 100%; - padding: 6px 12px; - background: var(--background-primary); - border: 1px solid var(--background-modifier-border); - border-radius: var(--radius-s); - color: var(--text-faint); - font-size: 11px; - font-weight: 500; - cursor: pointer; - text-align: left; - transition: background 0.15s, border-color 0.15s, color 0.15s; -} - -.paperforge-technical-details-toggle:hover { - background: var(--background-modifier-hover); - border-color: var(--background-modifier-border-hover); - color: var(--text-muted); -} - -.paperforge-technical-details-body { - margin-top: 4px; - padding: 8px 12px; - background: var(--background-primary-alt); - border: 1px solid var(--background-modifier-border); - border-radius: var(--radius-s); - font-size: 11px; -} - -.paperforge-technical-row { - display: flex; - justify-content: space-between; - padding: 2px 0; -} - -.paperforge-technical-label { - color: var(--text-faint); - flex-shrink: 0; - margin-right: 12px; -} - -.paperforge-technical-value { - color: var(--text-muted); - max-width: 60%; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - text-align: right; -} - -/* ========================================================================== - SECTION 41 — Collection / Base View (Batch Workspace) - ========================================================================== */ - -/* ── Collection Header ── */ -.paperforge-collection-header { - margin-bottom: 10px; -} - -.paperforge-collection-title { - font-size: 16px; - font-weight: 600; - color: var(--text-normal); - line-height: 1.3; -} - -.paperforge-collection-count { - font-size: 12px; - color: var(--text-muted); - margin-top: 2px; -} - -/* ── Workflow Overview ── */ -.paperforge-workflow-overview { - margin: 10px 0; - padding: 16px; - background: var(--background-primary); - border: 1px solid var(--background-modifier-border); - border-radius: var(--radius-m); - transition: border-color 0.2s, box-shadow 0.2s; - position: relative; -} - -.paperforge-workflow-overview::before { - content: ''; - position: absolute; - inset: -1px; - border-radius: inherit; - opacity: 0; - box-shadow: 0 2px 8px rgba(0,0,0,0.08); - transition: opacity 0.2s; - pointer-events: none; -} - -.paperforge-workflow-overview:hover::before { - opacity: 1; -} - -.paperforge-workflow-funnel { - display: flex; - align-items: center; - gap: 3px; - flex-wrap: wrap; - justify-content: center; -} - -.paperforge-workflow-stage { - display: flex; - flex-direction: column; - align-items: center; - padding: 8px 12px; - background: var(--background-primary-alt); - border-radius: 6px; - min-width: 56px; } .paperforge-workflow-stage-value { - font-size: 20px; + font-size: 26px; font-weight: 700; - color: var(--text-accent); + color: var(--pf-collection-accent); line-height: 1.2; - letter-spacing: -0.3px; + letter-spacing: -0.5px; } .paperforge-workflow-stage-label { - font-size: 10px; + font-size: 11px; color: var(--text-muted); text-transform: uppercase; letter-spacing: 0.3px; - margin-top: 2px; + margin-top: 4px; text-align: center; + word-break: break-word; } .paperforge-workflow-arrow { @@ -2653,7 +2190,6 @@ flex-shrink: 0; } -/* ── OCR Pipeline in Base ── */ .paperforge-collection-ocr-header { display: flex; align-items: center; @@ -2661,77 +2197,60 @@ margin-bottom: 14px; } -/* ── Issue Summary (serious blockers only) ── */ .paperforge-issue-summary { margin: 10px 0; padding: 12px 14px; background: var(--background-primary); - border: 1px solid var(--background-modifier-border); - border-left: 3px solid var(--color-orange); - border-radius: var(--radius-m); + border: 1px solid var(--pf-border); + border-left: 3px solid var(--pf-warm-line); + border-radius: var(--pf-radius); } .paperforge-issue-summary .paperforge-section-label { - color: var(--color-orange); + border-left-color: var(--pf-warm-line); + color: var(--text-normal); } .paperforge-issue-list { - margin: 4px 0 6px 0; + margin: 8px 0 12px 0; + display: flex; + flex-direction: column; + gap: 6px; } .paperforge-issue-item { font-size: 13px; - color: var(--text-muted); - padding: 2px 0; -} - -.paperforge-issue-actions { + font-weight: 500; + color: var(--text-normal); + padding: 6px 10px; + background: color-mix(in srgb, var(--background-secondary) 50%, transparent); + border-radius: calc(var(--pf-radius) - 2px); display: flex; - gap: 8px; - margin-top: 8px; + align-items: center; } -/* ── Collection Actions ── */ +.paperforge-issue-actions, .paperforge-collection-actions { display: flex; gap: 8px; - margin-top: 10px; - padding-top: 10px; - border-top: 1px solid var(--background-modifier-border); } -/* ========================================================================== - SECTION 42 — Global / Home View (System Homepage) - ========================================================================== */ - -/* ── Library Snapshot ── */ -.paperforge-library-snapshot { - padding: 16px; - background: var(--background-primary); - border: 1px solid var(--background-modifier-border); - border-radius: var(--radius-m); - transition: border-color 0.2s, box-shadow 0.2s; - position: relative; +.paperforge-issue-actions { + margin-top: 8px; } -.paperforge-library-snapshot::before { - content: ''; - position: absolute; - inset: -1px; - border-radius: inherit; - opacity: 0; - box-shadow: 0 2px 8px rgba(0,0,0,0.08); - transition: opacity 0.2s; - pointer-events: none; -} - -.paperforge-library-snapshot:hover::before { - opacity: 1; +.paperforge-collection-actions { + align-items: center; + flex-wrap: wrap; + margin-top: 8px; + padding-top: 8px; + border-top: 1px solid color-mix(in srgb, var(--pf-border) 82%, var(--pf-warm-line)); } +/* Global mode */ .paperforge-snapshot-pills { display: flex; - gap: 8px; + gap: 10px; flex-wrap: wrap; } @@ -2739,55 +2258,32 @@ display: flex; flex-direction: column; align-items: center; - padding: 10px 14px; - background: var(--background-primary-alt); - border-radius: 6px; - min-width: 60px; + justify-content: center; + padding: 14px 16px; + background: color-mix(in srgb, var(--background-primary-alt) 86%, var(--background-primary) 14%); + border: 1px solid color-mix(in srgb, var(--pf-global-accent) 22%, var(--pf-border)); + border-radius: 8px; + min-width: 72px; flex: 1; } .paperforge-snapshot-value { - font-size: 20px; + font-size: 26px; font-weight: 700; - color: var(--text-accent); + color: var(--pf-global-accent); line-height: 1.2; - letter-spacing: -0.3px; + letter-spacing: -0.5px; } .paperforge-snapshot-label { - font-size: 10px; + font-size: 11px; color: var(--text-muted); text-transform: uppercase; letter-spacing: 0.3px; - margin-top: 2px; + margin-top: 4px; text-align: center; } -/* ── System Status ── */ -.paperforge-system-status { - padding: 16px; - background: var(--background-primary); - border: 1px solid var(--background-modifier-border); - border-radius: var(--radius-m); - transition: border-color 0.2s, box-shadow 0.2s; - position: relative; -} - -.paperforge-system-status::before { - content: ''; - position: absolute; - inset: -1px; - border-radius: inherit; - opacity: 0; - box-shadow: 0 2px 8px rgba(0,0,0,0.08); - transition: opacity 0.2s; - pointer-events: none; -} - -.paperforge-system-status:hover::before { - opacity: 1; -} - .paperforge-status-grid { display: flex; flex-direction: column; @@ -2797,8 +2293,8 @@ .paperforge-status-row { display: flex; align-items: center; - gap: 8px; - padding: 4px 0; + gap: 10px; + padding: 6px 0; } .paperforge-status-dot { @@ -2806,12 +2302,12 @@ height: 7px; border-radius: 50%; flex-shrink: 0; - background: var(--background-modifier-border); + background: var(--pf-border); transition: background 0.3s; } .paperforge-status-dot.ok { - background: var(--color-green); + background: var(--text-success); } .paperforge-status-dot.fail { @@ -2825,123 +2321,89 @@ } .paperforge-status-detail { - font-size: 12px; + font-size: 13px; color: var(--text-faint); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } -/* ── Global Actions ── */ -.paperforge-global-actions { - padding: 16px; - background: var(--background-primary); - border: 1px solid var(--background-modifier-border); - border-radius: var(--radius-m); - transition: border-color 0.2s, box-shadow 0.2s; - position: relative; -} - -.paperforge-global-actions::before { - content: ''; - position: absolute; - inset: -1px; - border-radius: inherit; - opacity: 0; - box-shadow: 0 2px 8px rgba(0,0,0,0.08); - transition: opacity 0.2s; - pointer-events: none; -} - -.paperforge-global-actions:hover::before { - opacity: 1; -} - .paperforge-global-actions-row { display: flex; gap: 8px; + flex-wrap: wrap; } -/* ========================================================================== - SECTION 43 — Workflow Toggle Checkboxes (per-paper) - ========================================================================== */ - -.paperforge-workflow-toggles { - display: flex; - gap: 14px; - padding: 12px 16px; - background: var(--background-primary); - border: 1px solid var(--background-modifier-border); - border-radius: var(--radius-m); - margin: 10px 0; - transition: border-color 0.2s, box-shadow 0.2s; - position: relative; -} - -.paperforge-workflow-toggles::before { - content: ''; - position: absolute; - inset: -1px; - border-radius: inherit; - opacity: 0; - box-shadow: 0 2px 8px rgba(0,0,0,0.08); - transition: opacity 0.2s; - pointer-events: none; -} - -.paperforge-workflow-toggles:hover::before { - opacity: 1; -} - -.paperforge-workflow-toggle { - display: flex; - align-items: center; - gap: 6px; - cursor: pointer; +.paperforge-collection-actions .paperforge-contextual-btn, +.paperforge-global-actions-row .paperforge-contextual-btn { + min-height: 36px; + padding: 8px 14px; font-size: 13px; - color: var(--text-muted); - user-select: none; -} - -.paperforge-workflow-checkbox { - width: 15px; - height: 15px; - margin: 0; - cursor: pointer; - accent-color: var(--interactive-accent); -} - -.paperforge-workflow-toggle-label { font-weight: 600; - color: var(--text-normal); } -.paperforge-workflow-toggle-hint { - color: var(--text-faint); +.paperforge-global-actions-row .paperforge-contextual-btn.primary { + background: color-mix(in srgb, var(--pf-global-accent) 86%, black 14%); + border-color: color-mix(in srgb, var(--pf-global-accent) 90%, black 10%); + color: var(--text-on-accent); +} + +.paperforge-collection-actions .paperforge-contextual-btn.primary { + background: color-mix(in srgb, var(--pf-collection-accent) 84%, black 16%); + border-color: color-mix(in srgb, var(--pf-collection-accent) 88%, black 12%); + color: var(--text-on-accent); +} + +.paperforge-global-actions-row .paperforge-contextual-btn.primary:hover { + background: color-mix(in srgb, var(--pf-global-accent) 78%, black 22%); + border-color: color-mix(in srgb, var(--pf-global-accent) 82%, black 18%); +} + +.paperforge-collection-actions .paperforge-contextual-btn.primary:hover { + background: color-mix(in srgb, var(--pf-collection-accent) 76%, black 24%); + border-color: color-mix(in srgb, var(--pf-collection-accent) 80%, black 20%); +} + +.paperforge-collection-view .paperforge-ocr-section { + padding: 18px 18px 16px; + background: color-mix(in srgb, var(--background-secondary) 90%, var(--background-primary) 10%); + border-color: color-mix(in srgb, var(--pf-warm-line) 20%, var(--background-modifier-border)); + box-shadow: 0 1px 2px rgba(20, 18, 14, 0.04), 0 8px 20px rgba(20, 18, 14, 0.04); +} + +.paperforge-collection-view .paperforge-ocr-count-label { font-size: 11px; } -/* ========================================================================== - SECTION 44 — Dark Theme Specific Overrides - ========================================================================== */ - -.theme-dark .paperforge-paper-overview, -.theme-dark .paperforge-discussion-card, -.theme-dark .paperforge-workflow-overview, +.theme-dark .paperforge-collection-view .paperforge-ocr-section, .theme-dark .paperforge-library-snapshot, .theme-dark .paperforge-system-status, .theme-dark .paperforge-global-actions, -.theme-dark .paperforge-workflow-toggles { - background: var(--background-primary); - border-color: var(--background-modifier-border); +.theme-dark .paperforge-workflow-overview { + box-shadow: 0 2px 12px rgba(0, 0, 0, 0.3); } -.theme-dark .paperforge-paper-overview::before, -.theme-dark .paperforge-discussion-card::before, -.theme-dark .paperforge-workflow-overview::before, -.theme-dark .paperforge-library-snapshot::before, -.theme-dark .paperforge-system-status::before, -.theme-dark .paperforge-global-actions::before, -.theme-dark .paperforge-workflow-toggles::before { - box-shadow: 0 2px 12px rgba(0,0,0,0.3); +/* Dark theme overrides */ +.theme-dark .paperforge-status-pill.ok { + background: rgba(100, 200, 120, 0.15); +} + +.theme-dark .paperforge-status-pill.fail { + background: rgba(255, 100, 100, 0.15); +} + +.theme-dark .paperforge-library-snapshot:hover, +.theme-dark .paperforge-system-status:hover, +.theme-dark .paperforge-global-actions:hover, +.theme-dark .paperforge-workflow-overview:hover, +.theme-dark .paperforge-workflow-toggles:hover { + box-shadow: 0 2px 12px rgba(0, 0, 0, 0.3); +} + +.theme-dark .paperforge-paper-overview:hover { + box-shadow: inset 3px 0 0 color-mix(in srgb, var(--pf-paper-accent) 22%, transparent), 0 2px 12px rgba(0, 0, 0, 0.3); +} + +.theme-dark .paperforge-discussion-card:hover { + box-shadow: inset 3px 0 0 color-mix(in srgb, var(--pf-paper-accent) 16%, transparent), 0 2px 12px rgba(0, 0, 0, 0.3); } diff --git a/paperforge/setup_wizard.py b/paperforge/setup_wizard.py index f6f58a8a..6bd28b01 100644 --- a/paperforge/setup_wizard.py +++ b/paperforge/setup_wizard.py @@ -1012,7 +1012,7 @@ def headless_setup( "OCR dir": (pf_path / "ocr").exists(), "paperforge.json": pf_json.exists(), "Obsidian plugin": (vault / ".obsidian" / "plugins" / "paperforge" / "main.js").exists(), - "AGENTS.md": True if not agents_src.exists() else agents_dst.exists(), + "AGENTS.md": (vault / "AGENTS.md").exists(), } failed = [k for k, v in checks.items() if not v] if failed: diff --git a/paperforge/skills/literature-qa/SKILL.md b/paperforge/skills/literature-qa/SKILL.md new file mode 100644 index 00000000..2ad91877 --- /dev/null +++ b/paperforge/skills/literature-qa/SKILL.md @@ -0,0 +1,80 @@ +--- +name: literature-qa +description: > + 学术文献精读与问答。MUST trigger when user types /pf-deep, /pf-paper, /pf-end, + or says "精读", "深度阅读", "读一下", "查一下这篇论文", "帮我看看这篇文章", + "这篇文章讲了什么", "保存讨论", "结束讨论", "做这篇文献的问答", + or any phrase about reading/analyzing papers in their Zotero library. + 支持 Zotero key, DOI, 标题, 作者/年份, 自然语言描述定位论文. +license: Apache-2.0 +compatibility: opencode +--- + +# Literature QA — 学术文献精读与问答 + +## 路由表 + +Agent 读到本文件后,首先根据用户意图路由到对应的 reference 文件: + +| 用户意图 | 典型输入 | 加载文件 | +|---------|---------|---------| +| 三阶段精读(指定论文) | `/pf-deep `, `pf-deep `, "精读 XXX", "深度阅读 XXX" | [references/deep-reading.md](references/deep-reading.md) | +| 三阶段精读(查看队列) | `/pf-deep`(无参数), "精读队列", "有哪些该读了" | [references/deep-reading.md](references/deep-reading.md) | +| 论文问答 | `/pf-paper `, `pf-paper `, "做这篇的问答", "帮我看看 XXX" | [references/paper-qa.md](references/paper-qa.md) | +| 保存讨论记录 | `/pf-end`, `pf-end`, "保存", "结束讨论", "完成讨论" | [references/save-session.md](references/save-session.md) | + +> **重要:** 加载 reference 文件后,严格按照该文件的流程执行。不要跳过任何步骤。 + +## 论文定位(所有路由共用,先执行) + +详见 [references/paper-resolution.md](references/paper-resolution.md)。 + +**核心原则:二路定位。Python 干确定的活,Agent 干理解的活。路径全在环境变量里,零硬编码。** + +### 第零步:加载环境变量(每条路由启动时跑一次) + +```pwsh +python -m paperforge.worker.paper_resolver env --vault . --shell pwsh | Invoke-Expression +``` + +此后所有路径用环境变量引用,不再拼接: + +| 变量 | 含义 | +| ------------------ | --------------------------- | +| `$env:PF_VAULT` | vault 根目录 | +| `$env:PF_INDEX_PATH` | formal-library.json 路径 | +| `$env:PF_LITERATURE_DIR` | formal notes 目录 | +| `$env:PF_OCR_DIR` | OCR 结果目录 | + +### 第一步:判断输入类型,选择路径 + +| 输入特征 | 执行命令 | +|---------|---------| +| 8位 key | `python -m paperforge.worker.paper_resolver resolve-key --vault "$env:PF_VAULT"` | +| DOI | `python -m paperforge.worker.paper_resolver resolve-doi "" --vault "$env:PF_VAULT"` | +| 标题片段 | `python -m paperforge.worker.paper_resolver search --title "..." --vault "$env:PF_VAULT"` | +| 作者+年份 | `python -m paperforge.worker.paper_resolver search --author "Smith" --year 2024 --vault "$env:PF_VAULT"` | +| 自然语言 | Agent 读 `$env:PF_INDEX_PATH` 指向的 formal-library.json | + +### 第二步:处理结果 + +- **Python 返回匹配:** 直接使用返回的 workspace(`formal_note_path` 等由 config 动态计算) +- **Python 搜不到:** Agent grep fallback:`rg -l "zotero_key:.*ABC" "$env:PF_LITERATURE_DIR/"` +- **自然语言搜不到:** 告知用户 "未找到,请确认或先运行 `paperforge sync`" +- **命中多篇:** 列出候选清单让用户选 + +## 文件结构 + +``` +literature-qa/ +├── SKILL.md ← 本文件(路由入口) +├── references/ +│ ├── deep-reading.md ← 精读工作流 +│ ├── paper-qa.md ← 问答工作流 +│ ├── save-session.md ← 保存记录工作流 +│ ├── paper-resolution.md ← 论文定位详细协议 +│ ├── deep-subagent.md ← 子代理提示词模板 +│ └── chart-reading/ ← 19 个图表类型阅读指南 +└── scripts/ + └── ld_deep.py ← 精读引擎(Python) +``` diff --git a/paperforge/skills/literature-qa/chart-reading/GSEA富集图.md b/paperforge/skills/literature-qa/references/chart-reading/GSEA富集图.md similarity index 100% rename from paperforge/skills/literature-qa/chart-reading/GSEA富集图.md rename to paperforge/skills/literature-qa/references/chart-reading/GSEA富集图.md diff --git a/paperforge/skills/literature-qa/chart-reading/INDEX.md b/paperforge/skills/literature-qa/references/chart-reading/INDEX.md similarity index 100% rename from paperforge/skills/literature-qa/chart-reading/INDEX.md rename to paperforge/skills/literature-qa/references/chart-reading/INDEX.md diff --git a/paperforge/skills/literature-qa/chart-reading/ROC与PR曲线.md b/paperforge/skills/literature-qa/references/chart-reading/ROC与PR曲线.md similarity index 100% rename from paperforge/skills/literature-qa/chart-reading/ROC与PR曲线.md rename to paperforge/skills/literature-qa/references/chart-reading/ROC与PR曲线.md diff --git a/paperforge/skills/literature-qa/chart-reading/Western Blot条带图.md b/paperforge/skills/literature-qa/references/chart-reading/Western Blot条带图.md similarity index 100% rename from paperforge/skills/literature-qa/chart-reading/Western Blot条带图.md rename to paperforge/skills/literature-qa/references/chart-reading/Western Blot条带图.md diff --git a/paperforge/skills/literature-qa/chart-reading/免疫荧光定量图.md b/paperforge/skills/literature-qa/references/chart-reading/免疫荧光定量图.md similarity index 100% rename from paperforge/skills/literature-qa/chart-reading/免疫荧光定量图.md rename to paperforge/skills/literature-qa/references/chart-reading/免疫荧光定量图.md diff --git a/paperforge/skills/literature-qa/chart-reading/折线图与时间序列.md b/paperforge/skills/literature-qa/references/chart-reading/折线图与时间序列.md similarity index 100% rename from paperforge/skills/literature-qa/chart-reading/折线图与时间序列.md rename to paperforge/skills/literature-qa/references/chart-reading/折线图与时间序列.md diff --git a/paperforge/skills/literature-qa/chart-reading/散点图与气泡图.md b/paperforge/skills/literature-qa/references/chart-reading/散点图与气泡图.md similarity index 100% rename from paperforge/skills/literature-qa/chart-reading/散点图与气泡图.md rename to paperforge/skills/literature-qa/references/chart-reading/散点图与气泡图.md diff --git a/paperforge/skills/literature-qa/chart-reading/显微照片与SEM图.md b/paperforge/skills/literature-qa/references/chart-reading/显微照片与SEM图.md similarity index 100% rename from paperforge/skills/literature-qa/chart-reading/显微照片与SEM图.md rename to paperforge/skills/literature-qa/references/chart-reading/显微照片与SEM图.md diff --git a/paperforge/skills/literature-qa/chart-reading/条形图与误差棒.md b/paperforge/skills/literature-qa/references/chart-reading/条形图与误差棒.md similarity index 100% rename from paperforge/skills/literature-qa/chart-reading/条形图与误差棒.md rename to paperforge/skills/literature-qa/references/chart-reading/条形图与误差棒.md diff --git a/paperforge/skills/literature-qa/chart-reading/桑基图与弦图.md b/paperforge/skills/literature-qa/references/chart-reading/桑基图与弦图.md similarity index 100% rename from paperforge/skills/literature-qa/chart-reading/桑基图与弦图.md rename to paperforge/skills/literature-qa/references/chart-reading/桑基图与弦图.md diff --git a/paperforge/skills/literature-qa/chart-reading/森林图与Meta分析.md b/paperforge/skills/literature-qa/references/chart-reading/森林图与Meta分析.md similarity index 100% rename from paperforge/skills/literature-qa/chart-reading/森林图与Meta分析.md rename to paperforge/skills/literature-qa/references/chart-reading/森林图与Meta分析.md diff --git a/paperforge/skills/literature-qa/chart-reading/火山图与曼哈顿图.md b/paperforge/skills/literature-qa/references/chart-reading/火山图与曼哈顿图.md similarity index 100% rename from paperforge/skills/literature-qa/chart-reading/火山图与曼哈顿图.md rename to paperforge/skills/literature-qa/references/chart-reading/火山图与曼哈顿图.md diff --git a/paperforge/skills/literature-qa/chart-reading/热图与聚类图.md b/paperforge/skills/literature-qa/references/chart-reading/热图与聚类图.md similarity index 100% rename from paperforge/skills/literature-qa/chart-reading/热图与聚类图.md rename to paperforge/skills/literature-qa/references/chart-reading/热图与聚类图.md diff --git a/paperforge/skills/literature-qa/chart-reading/生存曲线.md b/paperforge/skills/literature-qa/references/chart-reading/生存曲线.md similarity index 100% rename from paperforge/skills/literature-qa/chart-reading/生存曲线.md rename to paperforge/skills/literature-qa/references/chart-reading/生存曲线.md diff --git a/paperforge/skills/literature-qa/chart-reading/箱式图与小提琴图.md b/paperforge/skills/literature-qa/references/chart-reading/箱式图与小提琴图.md similarity index 100% rename from paperforge/skills/literature-qa/chart-reading/箱式图与小提琴图.md rename to paperforge/skills/literature-qa/references/chart-reading/箱式图与小提琴图.md diff --git a/paperforge/skills/literature-qa/chart-reading/组织学半定量图.md b/paperforge/skills/literature-qa/references/chart-reading/组织学半定量图.md similarity index 100% rename from paperforge/skills/literature-qa/chart-reading/组织学半定量图.md rename to paperforge/skills/literature-qa/references/chart-reading/组织学半定量图.md diff --git a/paperforge/skills/literature-qa/chart-reading/网络图与通路图.md b/paperforge/skills/literature-qa/references/chart-reading/网络图与通路图.md similarity index 100% rename from paperforge/skills/literature-qa/chart-reading/网络图与通路图.md rename to paperforge/skills/literature-qa/references/chart-reading/网络图与通路图.md diff --git a/paperforge/skills/literature-qa/chart-reading/蛋白质结构图.md b/paperforge/skills/literature-qa/references/chart-reading/蛋白质结构图.md similarity index 100% rename from paperforge/skills/literature-qa/chart-reading/蛋白质结构图.md rename to paperforge/skills/literature-qa/references/chart-reading/蛋白质结构图.md diff --git a/paperforge/skills/literature-qa/chart-reading/降维图(PCA-tSNE-UMAP).md b/paperforge/skills/literature-qa/references/chart-reading/降维图(PCA-tSNE-UMAP).md similarity index 100% rename from paperforge/skills/literature-qa/chart-reading/降维图(PCA-tSNE-UMAP).md rename to paperforge/skills/literature-qa/references/chart-reading/降维图(PCA-tSNE-UMAP).md diff --git a/paperforge/skills/literature-qa/chart-reading/雷达图与漏斗图.md b/paperforge/skills/literature-qa/references/chart-reading/雷达图与漏斗图.md similarity index 100% rename from paperforge/skills/literature-qa/chart-reading/雷达图与漏斗图.md rename to paperforge/skills/literature-qa/references/chart-reading/雷达图与漏斗图.md diff --git a/paperforge/skills/literature-qa/references/deep-reading.md b/paperforge/skills/literature-qa/references/deep-reading.md new file mode 100644 index 00000000..3563a5d7 --- /dev/null +++ b/paperforge/skills/literature-qa/references/deep-reading.md @@ -0,0 +1,171 @@ +# 三阶段精读 + +Keshav 三阶段组会式精读。触发后执行以下工作流。 + +> **路径说明:** 本文件中的 `scripts/ld_deep.py` 相对于本 skill 目录。Agent 运行时 skill 目录由平台注入(通常为 `/paperforge/skills/literature-qa/`)。如不确定,AI 应从 SKILL.md 所在目录推断。 + +--- + +## 前置条件检查 + +执行前确认: +- [ ] 已完成论文定位(参考 [paper-resolution.md](paper-resolution.md)),拿到 zotero_key 和 workspace +- [ ] `analyze: true`(在 formal note frontmatter 中,resolver 返回的 workspace 里可查到) +- [ ] `ocr_status: done`(在 resolver 返回的 workspace 里可查到) + +如果前置条件不满足,告知用户并停止。 + +--- + +## 执行流程 + +### Step 1: Prepare(机械操作,跑脚本) + +```bash +python scripts/ld_deep.py prepare --vault . --key +``` + +返回 JSON 解析: +- `status: "ok"` → 继续 +- `status: "error"` → 报告 `message` 给用户,停止 + +Prepare 做的事情(Agent 不需要关心细节): +- 检查 analyze 和 ocr_status +- 生成 figure-map.json 和 chart-type-map.json +- 在 formal note 中插入 `## 🔍 精读` 骨架 + +读 formal note 确认骨架已插入。 + +--- + +### Step 2: Pass 1 — 概览 + +只填 `### Pass 1: 概览` 区域。不碰 Pass 2/3。 + +**填写内容:** + +- **一句话总览**:论文类型 + 核心发现,一句话。 +- **5 Cs 快速评估**: + - **Category**(类型):RCT / 队列研究 / 病例对照 / 综述 / 基础研究 / ... + - **Context**(上下文):该领域当前共识,本文要解决什么问题 + - **Correctness**(合理性初判):初步直觉,逻辑是否有明显漏洞 + - **Contributions**(贡献):1-3 条 + - **Clarity**(清晰度):写作质量,图表可读性 +- **Figure 导读**(基于 fulltext.md 浏览各图 caption): + - 关键主图:列出并一句话概括每个主图要证明什么 + - 证据转折点:哪个 figure 是叙事的关键转折 + - 需要重点展开的 supplementary:如果有 + - 关键表格:列出 + +填完立即保存 formal note。 + +--- + +### Step 3: Pass 2 — 精读还原 + +填 `### Pass 2: 精读还原` 区域。**按 figure 顺序逐个处理。** + +#### 图表类型定位(两步) + +**Step A: Python 给建议(快速初筛)** +```bash +python scripts/ld_deep.py chart-type-scan --vault . --key +``` +输出每个 figure 的关键词命中结果。这只是建议,Agent 不要盲信。 + +**Step B: Agent 读 caption 做最终判断** + +对每个 figure: +1. 读该 figure 的 caption(来自 fulltext.md 或 figure-map.json) +2. 根据 caption 内容,对照 [chart-reading/INDEX.md](chart-reading/INDEX.md) 判断图表类型 +3. 如果 Python 建议和 Agent 判断不一致 → 以 Agent 判断为准 +4. 如果无法确定类型 → 跳过 chart guide,按通用 figure 结构分析 +5. 确定类型后,读对应的 chart-reading 指南(如 `chart-reading/条形图与误差棒.md`),按指南中的检查清单分析 + +#### 每张 Figure 的子标题(固定,不可少) + +按以下格式填入 formal note 中该 figure 的 callout block: + +``` +**图像定位与核心问题**:页码 + 要回答什么问题 +**方法与结果**:实验设计/数据来源/技术手段。核心数据、趋势、对比。 +**图表质量审查**:按 chart-reading 指南检查坐标轴、单位、误差棒、统计标注等。 +**作者解释**:作者在正文中对该图的解读 +**我的理解**:自己的理解(区分于作者解释) +**疑点/局限**:读图时发现的疑问,用 `> [!warning]` 突出 +``` + +#### 每张 Table 的子标题 + +``` +回答什么问题、关键字段/分组、主要结果、我的理解、疑点/局限 +``` + +#### 每张 figure 填完立即保存,再处理下一张。 + +#### 所有 figure/table 处理完后,填: + +**关键方法补课**:简要解释不熟悉的实验技术(1-2 项即可) + +**主要发现与新意**: +- 发现 1:...(来源:Figure X) +- 发现 2:...(来源:Figure Y / Table Z) + +保存。 + +--- + +### Step 4: Postprocess(跑校验脚本,修正问题) + +```bash +python scripts/ld_deep.py postprocess-pass2 --figures --format text +``` + +- 输出 `OK` → 继续 +- 输出错误 → 按错误提示修正(包含行号),修正后重新跑 +- 最多 3 轮修正。3 轮后仍失败 → 报告剩余错误给用户 + +--- + +### Step 5: Pass 3 — 深度理解 + +填 `### Pass 3: 深度理解` 区域。基于 Pass 1/2 已写的内容。 + +**填写内容:** + +- **假设挑战与隐藏缺陷**:隐含假设;如果放宽某个假设结论还成立吗;缺少哪些关键引用;实验/分析技术潜在问题 +- **哪些结论扎实,哪些仍存疑**: + - **较扎实**:... + - **仍存疑**:...(用 `> [!warning]`) +- **Discussion 与 Conclusion 怎么读**:作者真正完成了什么;哪些地方有拔高;哪些是推测 +- **对我的启发**:研究设计上;figure 组织上;方法组合上;未来工作想法 +- **遗留问题**:...(用 `> [!question]`) + +保存。 + +--- + +### Step 6: Final Validation + +```bash +python scripts/ld_deep.py validate-note --fulltext +``` + +- 输出 `OK` → 告知用户精读完成 +- 输出错误 → 修正缺失项,不报告成功直到通过 + +--- + +## Callout 格式规则 + +- `> [!important]`:每个 main finding +- `> [!warning]`:疑问、局限、证据边界、仍存疑条目 +- `> [!question]`:遗留问题 +- **间距:** 相邻 callout block 之间必须有空行,否则 Obsidian 会合并 + - 正确:`> [!important] A\n\n> [!important] B` + - 错误:`> [!important] A\n> [!important] B` + +## Supplementary 规则 + +- 默认不逐张展开 supplementary figure/table +- 仅在以下情况纳入:对主结论形成关键支撑、补足方法可信度、限制主文结论解释范围、作者在正文中明显依赖该补充材料 diff --git a/paperforge/skills/literature-qa/prompt_deep_subagent.md b/paperforge/skills/literature-qa/references/deep-subagent.md similarity index 100% rename from paperforge/skills/literature-qa/prompt_deep_subagent.md rename to paperforge/skills/literature-qa/references/deep-subagent.md diff --git a/paperforge/skills/literature-qa/references/paper-qa.md b/paperforge/skills/literature-qa/references/paper-qa.md new file mode 100644 index 00000000..626f23e0 --- /dev/null +++ b/paperforge/skills/literature-qa/references/paper-qa.md @@ -0,0 +1,61 @@ +# 论文问答 + +交互式论文 Q&A 工作台。不强制要求 OCR,但 OCR 完成后回答更准确。 + +--- + +## 前置条件 + +- [ ] 已完成论文定位(参考 [paper-resolution.md](paper-resolution.md)),拿到 zotero_key 和 workspace +- [ ] OCR 完成(推荐但非强制) + +--- + +## 执行流程 + +### Step 1: 加载论文 + +1. 确认 workspace 路径 +2. 读 `fulltext.md`(如果存在)作为主要回答依据 +3. 读 formal note frontmatter 获取元数据(标题、作者、期刊、年份) +4. 如果 fulltext.md 不存在,告知用户 "OCR 文本不可用,回答将基于元数据和公开信息" + +### Step 2: 显示论文信息 + +``` +已加载论文: [title] ([year], [journal]) +作者: [authors] +Zotero Key: [key] +领域: [domain] +OCR 状态: [done / 不可用] +结束对话时说 "保存" 即可保存讨论。 +请问有什么问题? +``` + +### Step 3: 进入 Q&A 模式 + +- 等待用户提问 +- 每次回答后等待下一个问题 +- 持续到用户说 "保存"、"结束"、"完成" 等关键词 + +--- + +## 回答原则 + +- **严格基于** fulltext.md 中的文本内容回答 +- 引用原文时标注来源页码/章节(如 "第 3 页,Methods 部分") +- 用中文(简体中文)回答 +- 论文中未提及的内容,明确说明 "论文中未提及该内容" +- 需要结合论文以外知识的问题,说明 "该问题需要结合论文以外的知识" + +--- + +## 切换模式 + +用户在当前对话中可以说 "精读这篇文章" 切换到 deep-reading 模式。此时加载 [deep-reading.md](deep-reading.md) 执行精读流程。 + +--- + +## 保存记录 + +用户说 "保存"、"结束"、"完成"、"保存讨论" 时,加载 [save-session.md](save-session.md) 执行保存。不要自动保存。 diff --git a/paperforge/skills/literature-qa/references/paper-resolution.md b/paperforge/skills/literature-qa/references/paper-resolution.md new file mode 100644 index 00000000..763425eb --- /dev/null +++ b/paperforge/skills/literature-qa/references/paper-resolution.md @@ -0,0 +1,173 @@ +# 论文定位协议 + +本文件定义如何将用户输入(key / DOI / 标题 / 作者年份 / 自然语言)解析为论文 workspace。所有子流程(deep-reading, paper-qa, save-session)共用此协议。 + +## 核心原则 + +1. **确定性输入走 Python。** key、DOI、标题片段、作者+年份 —— 这些是机器能精确处理的。 +2. **自然语言走 Agent。** "关于骨再生的那篇"、"去年那篇Nature" —— 需要 AI 理解语义。 +3. **Python 搜不到时 Agent 兜底。** 不是报错,是换个方式再试一次。 +4. **所有路径从环境变量获取。** 每条路由启动时先跑 `python -m paperforge.worker.paper_resolver env --vault . --shell pwsh | Invoke-Expression`。此后 `$env:PF_VAULT`、`$env:PF_LITERATURE_DIR`、`$env:PF_OCR_DIR`、`$env:PF_INDEX_PATH` 全程可用。不写死任何目录名。 + +--- + +## 前置:获取 vault 路径配置(每个路由启动时跑一次) + +```bash +python -m paperforge.worker.paper_resolver paths --vault . +``` + +返回示例(所有路径均由 `paperforge.json` 动态计算): +```json +{ + "ok": true, + "data": { + "vault_root": "/path/to/vault", + "index_path": "/PaperForge/indexes/formal-library.json", + "literature_dir": "/", + "ocr_dir": "/PaperForge/ocr" + } +} +``` + +**后续所有路径操作使用此输出中的值,不要自己拼接。** + +--- + +## 输入类型判断 + +### 类型 1: Zotero Key(8位字符) + +**识别规则:** 8位字母数字组合,如 `XGT9Z257`、`ABC12345` + +**执行命令:** +```bash +python -m paperforge.worker.paper_resolver resolve-key --vault . +``` + +**返回示例(路径由 paperforge.json 配置决定,不固定):** +```json +{ + "ok": true, + "data": { + "match": { + "key": "ABC12345", + "title": "TGF-beta in Bone Regeneration", + "domain": "骨科", + "formal_note_path": "...", + "ocr_path": "...", + "fulltext_path": "...", + "ocr_status": "done" + } + } +} +``` + +**返回 `"match": null` 时:** Agent fallback — 用 `paths` 命令获取的 `literature_dir` 路径 grep frontmatter: +```bash +rg -l "zotero_key:.*ABC12345" / +``` + +--- + +### 类型 2: DOI + +**识别规则:** 以 `10.` 开头的标准 DOI 格式,可能带 URL 前缀 + +**执行命令:** +```bash +python -m paperforge.worker.paper_resolver resolve-doi "10.1016/j.jse.2018.01.001" --vault . +``` + +**返回格式同类型1。** 返回的路径直接使用,不自己拼接。 + +--- + +### 类型 3: 标题片段 + +**识别规则:** 看起来像论文标题的文本(含学术关键词,非自然语言问句) + +**执行命令:** +```bash +python -m paperforge.worker.paper_resolver search --title "Predictive findings on MRI" --vault . +``` + +**返回示例:** +```json +{ + "ok": true, + "data": { + "matches": [{ "key": "...", "title": "...", "formal_note_path": "...", ... }], + "count": 3 + } +} +``` + +--- + +### 类型 4: 作者 + 年份 + +**识别规则:** 包含作者名(英文姓)和年份 + +**执行命令:** +```bash +python -m paperforge.worker.paper_resolver search --author "Smith" --year 2024 --vault . +``` + +--- + +### 类型 5: 自然语言 + +**识别规则:** 中文自然语言描述,如 "关于骨再生的那篇"、"去年Nature上那篇讲TGF的" + +**Agent 操作:** +1. 用 `paths` 命令获取的 `index_path` 读 `formal-library.json` +2. 理解用户意图中的关键信息:主题词、年份、期刊、领域 +3. 在 JSON 的 `title`、`domain`、`journal`、`abstract` 字段中搜索匹配 +4. 如果 formal-library.json 无结果,用 `paths` 命令获取的 `literature_dir` grep formal notes: + ```bash + rg -i -l "骨再生|bone regeneration" / --include "*.md" -g "!*.canvas" + ``` +5. 读匹配的 frontmatter 确认是目标论文 + +--- + +## 多篇匹配处理 + +当搜索返回多个匹配时,Agent 必须列出候选清单,让用户选择: + +``` +找到 3 篇匹配的论文: + +[1] ABC12345 — TGF-beta in Bone Regeneration (2024, 骨科, OCR: done) +[2] DEF67890 — Bone Healing After Fracture (2023, 骨科, OCR: pending) +[3] GHI11111 — Scaffold Design for Bone Repair (2024, 骨科, OCR: done) + +请输入编号选择,或 refine 搜索词。 +``` + +**格式要求:** +- 编号 + key + title + (year, domain, ocr_status) +- 不要只列标题或只列 key + +--- + +## Fallback 顺序 + +``` +输入 + │ + ├── 看起来像 key/DOI/标题/作者年份? + │ └── YES → Python paper_resolver + │ ├── 有结果 → 使用 + │ └── 无结果 → Agent grep fallback + │ ├── 有结果 → 使用 + │ └── 无结果 → 告知用户 + │ + └── 看起来像自然语言? + └── Agent 读 formal-library.json + ├── 有结果 → 列出/使用 + └── 无结果 → Agent grep fallback + ├── 有结果 → 使用 + └── 无结果 → 告知用户 +``` diff --git a/paperforge/skills/literature-qa/references/save-session.md b/paperforge/skills/literature-qa/references/save-session.md new file mode 100644 index 00000000..de023e21 --- /dev/null +++ b/paperforge/skills/literature-qa/references/save-session.md @@ -0,0 +1,55 @@ +# 保存讨论记录 + +将 paper-qa 会话中的 Q&A 记录持久化到论文工作区。 + +--- + +## 触发条件 + +- 用户显式说 "保存"、"保存记录"、"结束"、"完成讨论"、"save" +- 或显式输入 `/pf-end` +- 不要自动触发 + +--- + +## 执行 + +### Step 1: 收集 Q&A 对 + +汇总本次 paper-qa 会话中所有 Q&A,序列化为 JSON 数组: + +```json +[ + { + "question": "用户的问题", + "answer": "Agent 的回答", + "source": "user_question", + "timestamp": "2026-05-10T12:00:00+08:00" + } +] +``` + +`source` 为 `"user_question"`(用户提问)或 `"agent_analysis"`(Agent 主动分析)。 + +### Step 2: 调用 discussion 模块 + +```bash +python -m paperforge.worker.discussion record \ + --vault . \ + --agent pf-paper \ + --model "" \ + --qa-pairs '' +``` + +### Step 3: 确认结果 + +CLI 返回 `{"status": "ok", ...}` → 告知用户记录已保存。 + +返回 `{"status": "error"}` → 记录错误,重试一次。仍失败则告知用户。 + +--- + +## 注意事项 + +- 仅 paper-qa 会话需要记录。deep-reading 的内容直接写入 formal note,不需要通过本文件。 +- 如果无法从 formal-library.json 找到论文 domain/title,记录失败不应影响用户使用。 diff --git a/paperforge/skills/literature-qa/scripts/pf-deep.md b/paperforge/skills/literature-qa/scripts/pf-deep.md deleted file mode 100644 index 3d2af01c..00000000 --- a/paperforge/skills/literature-qa/scripts/pf-deep.md +++ /dev/null @@ -1,237 +0,0 @@ ---- -name: pf-deep -description: Complete three-pass deep reading of an academic paper (Keshav method). Requires OCR fulltext. Searches by Zotero key, title, DOI, or PMID. -allowed-tools: [Read, Bash, Edit] ---- - -# pf-deep - -## Purpose - -基于单篇论文的组会式精读入口。 - -1. 解析 `pf-deep ` 中的查询词 -2. 支持 Zotero key、标题片段、DOI、PMID、关键词 -3. 优先搜索本地 Zotero 并锁定单篇论文 -4. 绑定该论文对应的: - - `/PaperForge/ocr//fulltext.md` - - `/PaperForge/ocr//meta.json` - - `//.../KEY - Title.md` -5. 在正式文献卡片中检查或创建 `## 精读` -6. 以"研究思路 + figure-by-figure"方式一次性完成精读写回 - -## CLI Equivalent - -```bash -# 准备阶段(间接) -python .opencode/skills/pf-deep/scripts/ld_deep.py prepare --vault "" --key -# 返回 JSON:{status, formal_note, fulltext_md, figures, tables} -``` - -> `pf-deep` 是 **Agent 层命令**,通过 Python 代码自动检测论文状态,无需先行 CLI 准备。 - -## Detection(自动检测,无需手动 sync) - -启动时,Agent 执行以下 Python 检测命令,代码会自动判断是否需要精读: - -```bash -python .opencode/skills/pf-deep/scripts/ld_deep.py prepare --vault "" --key -``` - -返回 JSON: -- `status: "ok"` → 就绪,可以开始精读 -- `status: "error"` → 被阻塞(`message` 说明原因:analyze=false / OCR 未完成 / 未找到论文) - -Agent 根据返回的 `status` 决定是否进入精读流程,不自行读取 frontmatter。 - -**队列模式**(无参数时自动检测):Agent 运行: -```bash -python .opencode/skills/pf-deep/scripts/ld_deep.py queue --vault "" -``` -代码自动扫描 canonical index 中 `analyze=true` 且 `deep_reading_status=pending` 的论文,按 OCR 状态分组。 - -## Arguments - -| 参数 | 必需 | 说明 | -|------|------|------| -| `` | 是(queue 模式除外) | Zotero key、标题片段、DOI、PMID 或关键词 | -| `queue` | 否 | 启动批量精读队列模式 | - -### 参数说明 - -1. 如果输入看起来像 8 位 Zotero key,则直接按 key 解析。 -2. 否则先在本地 Zotero 中搜索标题/摘要。 -3. 若命中唯一结果或明显最佳结果,则直接载入。 -4. 若存在多个合理候选,则先列候选清单再让用户选。 -5. 不要强迫用户先知道 Zotero key。 - -## Example - -### 单篇精读(已知 key) - -```bash -pf-deep XGT9Z257 -pf-deep Predictive findings on magnetic resonance imaging -pf-deep 10.1016/j.jse.2018.01.001 -``` - -### 无参数:自动检测队列 - -```bash -pf-deep -``` - -当不提供具体 key/标题时,agent 自动检测精读队列: - -1. 运行 `python .opencode/skills/pf-deep/scripts/ld_deep.py queue --vault ""` 扫描队列 -2. 解析输出的 JSON,按 OCR 状态分组展示 -3. 由用户选择篇目 - -无需先跑 `paperforge sync`。 - -## Output - -Agent 在正式笔记中创建或更新 `## 精读` 区域,包含: - -- **Pass 1: 概览** — 一句话总览、5 Cs 快速评估、Figure 导读 -- **Pass 2: 精读还原** — Figure-by-Figure 解析、Table-by-Table 解析、关键方法补课、主要发现与新意 -- **Pass 3: 深度理解** — 假设挑战与隐藏缺陷、结论扎实性评估、Discussion 解读、个人启发、遗留问题 - -## Error Handling - -### OCR 未完成 -- **表现**:Agent 提示 `ocr_status` 不是 `done` -- **解决**:先运行 `paperforge ocr`,确认 `meta.json` 中 `ocr_status` 变为 `done` - -### 内容已存在(覆盖确认) -- **表现**:正式笔记中已存在 `## 精读` 区域且包含非占位符的实质内容 -- **处理**:Agent **必须**询问用户:追加 / 覆盖 / 跳过 - -### 未找到论文 -- **表现**:Zotero key 无效或搜索无结果 -- **解决**:确认 key 正确,或尝试用标题片段搜索 - -## 精读结构参考 - -### 执行原则 - -- `pf-deep` 对用户来说是一次触发直接完成。 -- 内部逻辑分两步: - 1. 先生成 `## 精读` 骨架和 figure 标题位 - 2. 再补全所有空段 -- 后续再次运行时(用户选择追加): - - 只补空段,不覆盖已有内容 - -### 精读定位 - -这不是综述提取,也不是信息摘录。目标是模拟高水平博士/博士后组会讲解单篇论文的学习型精读。 - -主线必须是: - -1. 文章整体研究思路 -2. 主文 figure 逐张解析 -3. 关键方法补课 -4. 主要发现、新意、疑点与启发 - -### Supplementary 规则 - -- 默认不逐张展开 supplementary figure/table。 -- 仅在以下情况下纳入: - - 对主结论形成关键支撑 - - 补足方法可信度 - - 限制主文结论的解释范围 - - 作者在正文中明显依赖该补充材料 - -### 标准骨架 - -```md -## 精读 - -**证据边界**:区分三层信息:`论文结果`、`作者解释`、`我的理解/推断`。 - -### Pass 1: 概览 - -**一句话总览** -(待补充) - -**5 Cs 快速评估** -- **Category**(类型): -- **Context**(上下文): -- **Correctness**(合理性初判): -- **Contributions**(贡献): -- **Clarity**(清晰度): - -**Figure 导读** -- 关键主图: -- 证据转折点: -- 需要重点展开的 supplementary: -- 关键表格: - -### Pass 2: 精读还原 - -#### Figure-by-Figure 解析 -(每张 figure 下方按以下顺序填写) -- **图像定位与核心问题**:页码 + 要回答什么问题 -- **方法与结果**:方法 + 结果 -- **作者解释**:作者对该图的解读 -- **我的理解**:自己的理解(区分于作者解释) -- **在全文中的作用**:该图在整体故事线中的位置 -- **疑点 / 局限**:读图时发现的疑问 - -#### Table-by-Table 解析 -(如有重要表格,按同样结构展开) - -#### 关键方法补课 -- 方法 1: -- 方法 2: - -#### 主要发现与新意 -**主要发现** -- 发现 1: -- 发现 2: - -### Pass 3: 深度理解 - -#### 假设挑战与隐藏缺陷 -- 隐含假设: -- 如果放宽某个假设,结论还成立吗? -- 缺少哪些关键引用? -- 实验/分析技术的潜在问题: - -#### 哪些结论扎实,哪些仍存疑 -**较扎实** -- - -**仍存疑** -- - -#### Discussion 与 Conclusion 怎么读 -- 作者真正完成了什么: -- 哪些地方有拔高: -- 哪些地方是推测: - -#### 对我的启发 -- 研究设计上: -- figure 组织上: -- 方法组合上: -- 未来工作想法: - -#### 遗留问题 -**遗留问题** -- -``` - -### Figure 节要求 - -每个 figure 小节按以下顺序填写: - -- **图像定位与核心问题**:页码 + 要回答什么问题 -- **方法与结果**:方法 + 结果 -- **作者解释**:作者对该图的解读 -- **我的理解**:自己的理解(区分于作者解释) -- **在全文中的作用**:该图在整体故事线中的位置 -- **疑点 / 局限**:读图时发现的疑问(可酌情用 `> [!warning]` 突出) - -## See Also - -- [pf-paper](pf-paper.md) — 快速摘要与问答 diff --git a/paperforge/skills/literature-qa/scripts/pf-end.md b/paperforge/skills/literature-qa/scripts/pf-end.md deleted file mode 100644 index 0f3a0f04..00000000 --- a/paperforge/skills/literature-qa/scripts/pf-end.md +++ /dev/null @@ -1,67 +0,0 @@ ---- -name: pf-end -description: Save the paper Q&A discussion record. Triggered when user says "保存" "结束" "save" or types $pf-end. Summarizes all Q&A pairs and writes them to the paper's ai/discussion directory via the discussion module. -allowed-tools: [Read, Bash] ---- - -# pf-end - -## Purpose - -结束当前论文对话并保存讨论记录。需要用户**显式要求**时才执行,不自动触发。 - -1. 汇总本次对话中所有 Q&A 对 -2. 通过 `discussion.record_session()` 写入论文工作区的 `ai/` 目录 -3. 告知用户记录已保存 - -## Trigger - -用户说以下任一关键词时执行(全平台通用): -- `保存` / `记录` / `保存记录` -- `结束` / `完成` -- `save discussion` / `save` / `done` - -也可以显式指定 key: -- `{prefix}pf-end `(OpenCode) -- `保存 `(全平台) - -如果未指定 key,则使用当前已加载的论文 key。 - -## Save Format - -将会话期间的所有 Q&A 序列化为 JSON 数组: - -```json -{ - "question": "用户的问题", - "answer": "Agent 的回答", - "source": "user_question", - "timestamp": "2026-05-06T12:00:00+08:00" -} -``` - -`source` 为 `"user_question"`(用户提问)或 `"agent_analysis"`(Agent 主动分析)。 - -## Command - -```bash -python -m paperforge.worker.discussion record \ - --vault "" \ - --agent pf-paper \ - --model "" \ - --qa-pairs '' -``` - -## Verification - -CLI 返回: -```json -{"status": "ok", "json_path": "Literature/{domain}/{key} - {title}/ai/discussion.json", "md_path": "..."} -``` - -如果 `status` 为 `"error"`,记录错误信息并重试,不要跳过。 - -## See Also - -- [pf-paper](pf-paper.md) — 论文 Q&A 工作台 -- [pf-deep](pf-deep.md) — 完整三阶段精读 diff --git a/paperforge/skills/literature-qa/scripts/pf-ocr.md b/paperforge/skills/literature-qa/scripts/pf-ocr.md deleted file mode 100644 index 000611a3..00000000 --- a/paperforge/skills/literature-qa/scripts/pf-ocr.md +++ /dev/null @@ -1,102 +0,0 @@ ---- -name: pf-ocr -description: Process the PDF OCR queue for formal notes marked do_ocr: true. Uploads PDFs to PaddleOCR API and extracts fulltext with figures. -allowed-tools: [Bash] ---- - -# /pf-ocr - -## Purpose - -处理正式笔记 frontmatter 中 `do_ocr: true` 的 PDF OCR 队列。 - -`paperforge ocr` 会自动读取 `paperforge.json` 定位 ocr 目录和 worker 脚本,运行 OCR 并自动诊断结果。 - -## CLI Equivalent - -```bash -paperforge ocr -``` - -## Prerequisites - -- [ ] formal note frontmatter 中 `do_ocr: true` 已设置 -- [ ] PDF 附件存在(`has_pdf: true`) -- [ ] PaddleOCR API Key 已配置(`.env` 中 `PADDLEOCR_API_TOKEN`) -- [ ] 网络连接正常(可访问 PaddleOCR 服务) - -## Arguments - -| 参数 | 必需 | 说明 | -|------|------|------| -| `--diagnose` | 否 | 仅诊断配置,不上传 PDF | -| `--key ` | 否 | 仅处理指定 Zotero key 的文献 | -| `--vault ` | 否 | 指定 Vault 根目录(默认当前目录) | -| `--live` | 否 | 与 `--diagnose` 联用,执行实时 PDF 测试 | - -### 诊断等级 - -| 等级 | 检查项 | 失败含义 | -|------|--------|---------| -| L1 | API token 存在性 | `PADDLEOCR_API_TOKEN` 缺失或为空 | -| L2 | URL 可达性 | 无法连接 PaddleOCR 服务 | -| L3 | API 格式验证 | 服务可达但响应格式异常 | -| L4 | 实时 PDF 往返 | 完整提交和结果获取失败 | - -> Exit code `0` = 所有检查通过。Exit code `1` = 至少一项检查失败。 - -## Example - -```bash -# 处理所有标记 do_ocr: true 的文献 -paperforge ocr - -# 仅处理指定文献 -paperforge ocr --key ABCDEFG - -# 诊断模式(不实际运行) -paperforge ocr --diagnose - -# 完整诊断(含实时测试) -paperforge ocr --diagnose --live - -# 指定 Vault 目录 -paperforge ocr --vault /path/to/vault -``` - -## Output - -OCR 完成后,每个文献生成以下文件: - -``` -/PaperForge/ocr// -├── fulltext.md # 提取的全文(含 分页标记) -├── images/ # 自动切割的图表图片 -├── meta.json # OCR 元数据(含 ocr_status) -└── figure-map.json # 图表索引(自动生成) -``` - -`meta.json` 中的 `ocr_status` 字段: -- `pending` — 等待处理 -- `processing` — 正在处理 -- `done` — 完成 -- `failed` — 失败 - -## Error Handling - -### API Token 缺失(L1 失败) -- `PADDLEOCR_API_TOKEN` 未设置 → 在 `.env` 文件中添加 - -### 服务不可达(L2 失败) -- 无法连接 PaddleOCR → 检查网络连接 - -### PDF 上传失败(L4 失败) -- PDF 提交后未返回结果 → 检查 PDF 文件是否损坏 - -### OCR 状态卡住 -- `ocr_status` 长期显示 `processing` → 重新设置 `do_ocr: true` 后再次运行 - -## See Also - -- [pf-sync](pf-sync.md) — 文献同步(生成正式笔记) -- [pf-deep](pf-deep.md) — 深度精读(依赖 OCR 结果) diff --git a/paperforge/skills/literature-qa/scripts/pf-paper.md b/paperforge/skills/literature-qa/scripts/pf-paper.md deleted file mode 100644 index aa3e032b..00000000 --- a/paperforge/skills/literature-qa/scripts/pf-paper.md +++ /dev/null @@ -1,107 +0,0 @@ ---- -name: pf-paper -description: Quick paper Q&A workbench. Load a paper by Zotero key, title, DOI, or PMID and answer questions. Does not require OCR. -allowed-tools: [Read, Bash] ---- - -# pf-paper - -## Purpose - -基于 Zotero OCR 文本的单篇论文工作台入口。 - -1. 解析 `pf-paper ` 中的查询词 -2. 支持 Zotero key、标题片段、DOI、PMID、关键词 -3. 优先搜索本地 Zotero,解析到单篇目标论文 -4. 根据 Vault 根目录的 `paperforge.json` 加载 `/PaperForge/ocr//fulltext.md` 作为主文本 -5. 读取 `meta.json` 显示论文标题、作者、期刊、年份 -6. 进入 Q&A 模式,用中文回答用户关于该论文的问题 -7. 在当前论文上下文中,用户可再说"精读这篇文章"切换到 deep 层 - -## CLI Equivalent - -```bash -# 准备阶段(间接) -paperforge sync # 生成正式笔记 -``` - -> `pf-paper` 是 **Agent 层命令**,无直接 CLI 等效命令。 - -## Prerequisites - -- [ ] 正式笔记已生成(`paperforge sync` 生成) -- [ ] `fulltext.md` 存在(推荐,用于基于原文回答;如不存在则基于元数据回答) -- [ ] discussion.py 模块可用(`python -m paperforge.worker.discussion --help` 可执行) - -> **注意**:与 `/pf-deep` 不同,`pf-paper` **不强制要求** OCR 完成。没有 OCR 时基于论文元数据和公开信息回答。 - -## Arguments - -| 参数 | 必需 | 说明 | -|------|------|------| -| `` | 是 | Zotero key、标题片段、DOI、PMID 或关键词 | -| ` ...` | 否 | 可同时加载多篇论文 | - -### 解析规则 - -1. 如果输入看起来像 8 位 Zotero key,则直接按 key 解析。 -2. 否则先在本地 Zotero 中搜索标题/摘要。 -3. 若命中唯一结果或明显最佳结果,则直接载入。 -4. 若存在多个合理候选,则先列候选清单再让用户选。 -5. 不要强迫用户先知道 Zotero key。 - -## Example - -```bash -pf-paper XGT9Z257 -pf-paper Predictive findings on magnetic resonance imaging -pf-paper 10.1016/j.jse.2018.01.001 -pf-paper XGT9Z257 PQR8KLM -``` - -## Output - -加载成功后显示: - -``` -已加载论文: [title] ([year], [journal]) -Zotero Key: [key] -结束对话时说"保存记录"即可保存讨论。 -请问有什么问题? -``` - -### 回答原则 - -- **严格基于** `fulltext.md` 中的文本内容回答 -- 引用原文时标注来源页码/章节 -- 用中文(简体中文)回答 -- 论文中未提及的内容,明确说明"论文中未提及该内容" -- 需要结合论文以外知识的问题,说明"该问题需要结合论文以外的知识回答" - -## Error Handling - -### 论文未找到 -- **表现**:Zotero key 无效或搜索无结果 -- **解决**:确认 key 正确,或尝试用标题片段搜索 - -### 多个候选结果 -- **表现**:搜索返回多个匹配的论文 -- **处理**:Agent 列出候选清单,让用户选择目标论文 - -### OCR 文件缺失 -- **表现**:`fulltext.md` 不存在 -- **处理**:Agent 基于元数据和公开信息回答,并告知用户"OCR 文本不可用,回答基于元数据" - -## Saving Discussion Record - -当用户说"保存"、"结束"、"完成"等关键词时(或显式输入 `{prefix}pf-end `),必须执行记录保存。具体步骤见 [pf-end](pf-end.md)。 - -### 注意事项 - -- 仅 `/pf-paper` Q&A 需要记录。`/pf-deep` 不记录(精读内容已写入正式笔记)。 -- 如果无法从 formal-library.json 找到论文 domain/title,记录失败不应影响用户使用。 - -## See Also - -- [pf-end](pf-end.md) — 结束对话并保存记录 -- [pf-deep](pf-deep.md) — 完整三阶段精读 diff --git a/paperforge/skills/literature-qa/scripts/pf-status.md b/paperforge/skills/literature-qa/scripts/pf-status.md deleted file mode 100644 index 67d22f94..00000000 --- a/paperforge/skills/literature-qa/scripts/pf-status.md +++ /dev/null @@ -1,94 +0,0 @@ ---- -name: pf-status -description: Check PaperForge installation and runtime status. Verifies configuration, paths, and data integrity. -allowed-tools: [Bash] ---- - -# /pf-status - -## Purpose - -查看 PaperForge 当前安装与运行状态。 - -`paperforge status` 会检查: - -- 安装完整性(Python 包、依赖) -- 配置文件(`paperforge.json`、`.env`) -- 路径连通性(exports、ocr、literature 目录) -- Zotero 数据目录链接状态 -- Better BibTeX 导出文件状态 - -## CLI Equivalent - -```bash -paperforge status -``` - -## Prerequisites - -无特殊前置条件。此命令用于诊断安装问题,即使在配置不完整时也会尽量输出可用信息。 - -## Arguments - -| 参数 | 必需 | 说明 | -|------|------|------| -| `--vault ` | 否 | 指定 Vault 根目录(默认当前目录) | - -## Example - -```bash -# 检查当前目录的 Vault 状态 -paperforge status - -# 检查指定 Vault 的状态 -paperforge status --vault /path/to/vault -``` - -## Output - -典型输出示例: - -``` -PaperForge Lite v1.2 -==================== - -[安装检查] -✓ Python 包: paperforge v1.2.0 -✓ 依赖: requests, pymupdf, pillow - -[配置检查] -✓ paperforge.json: 存在且有效 -✓ .env: 存在 -✓ PADDLEOCR_API_TOKEN: 已设置 - -[路径检查] -✓ exports: /PaperForge/exports/ -✓ ocr: /PaperForge/ocr/ -✓ literature: // -✓ Zotero: /Zotero/ - -[数据检查] -✓ library.json: 存在,包含 150 条文献 -✓ 正式笔记: 150 篇 - -状态: 一切正常 ✅ -``` - -## Error Handling - -### 配置缺失 -- `✗ paperforge.json: 未找到` → 运行 `paperforge doctor` 或重新执行安装 - -### 路径错误 -- `✗ Zotero: 目录不存在或不是有效链接` → 创建 junction/symlink 到 Zotero 数据目录 - -### 依赖缺失 -- `✗ 依赖: requests 未安装` → `pip install requests pymupdf pillow` - -### API Key 未设置 -- `✗ PADDLEOCR_API_TOKEN: 未设置` → 在 `.env` 文件中添加 API token - -## See Also - -- [pf-sync](pf-sync.md) — 文献同步 -- [pf-ocr](pf-ocr.md) — OCR 提取 diff --git a/paperforge/skills/literature-qa/scripts/pf-sync.md b/paperforge/skills/literature-qa/scripts/pf-sync.md deleted file mode 100644 index 02dedf87..00000000 --- a/paperforge/skills/literature-qa/scripts/pf-sync.md +++ /dev/null @@ -1,85 +0,0 @@ ---- -name: pf-sync -description: Sync Zotero Better BibTeX JSON export and generate/update formal literature notes. -allowed-tools: [Bash] ---- - -# /pf-sync - -## Purpose - -同步 Zotero Better BibTeX JSON 导出并生成/更新正式文献笔记。 - -`paperforge sync` 读取 Zotero JSON 中的新条目,直接生成正式文献笔记。 - -自动读取 `paperforge.json` 定位 exports 目录和 literature 目录。 - -## CLI Equivalent - -```bash -paperforge sync -``` - -## Prerequisites - -- [ ] Zotero 已安装且 Better BibTeX 插件已启用 -- [ ] Better BibTeX 已配置自动导出 JSON -- [ ] JSON 导出文件存在(`/PaperForge/exports/library.json`) -- [ ] `paperforge.json` 配置正确(Vault 根目录下) - -## Arguments - -| 参数 | 必需 | 说明 | -|------|------|------| -| `--dry-run` | 否 | 预览变更,不实际写入文件 | -| `--domain ` | 否 | 仅同步指定领域(如 `骨科`) | -| `--selection` | 否 | (已废弃)仅保留以兼容旧版 | -| `--index` | 否 | (已废弃)仅保留以兼容旧版 | -| `--vault ` | 否 | 指定 Vault 根目录(默认当前目录) | - -### 选项 - -```bash -paperforge sync --dry-run # 预览同步结果 -paperforge sync --domain 骨科 # 按领域过滤同步 -``` - -## Example - -```bash -# 同步 Zotero 并生成正式笔记 -paperforge sync - -# 预览模式 -paperforge sync --dry-run - -# 仅同步特定领域 -paperforge sync --domain 骨科 - -# 指定 Vault 目录 -paperforge sync --vault /path/to/vault -``` - -## Output - -``` -[INFO] Found 5 new items -[INFO] Created 骨科/XXXXXXX.md -[INFO] Generated 5 formal notes -[INFO] Output: //骨科/XXXXXXX - Title.md -``` - -生成文件:`/// - .md` - -## Error Handling - -### JSON 文件不存在 -- `[ERROR] library.json not found` → 检查 Better BibTeX 导出路径 - -### 空 JSON 导出 -- `[INFO] Found 0 new items` → 确认 Zotero 中有带 citation key 的文献,Better BibTeX 已启用"Keep updated" - -## See Also - -- [pf-ocr](pf-ocr.md) — OCR 提取(下一步操作) -- [pf-status](pf-status.md) — 检查系统状态