mirror of
https://github.com/thegeneralist01/archivr
synced 2026-10-09 12:55:00 +02:00
feat: add summary image consent control
This commit is contained in:
parent
ed3bc56e14
commit
7051d459db
3 changed files with 62 additions and 2 deletions
|
|
@ -42,13 +42,13 @@ export async function fetchEntrySummary(archiveId, entryUid) {
|
||||||
// caller polls fetchEntrySummary either way.
|
// caller polls fetchEntrySummary either way.
|
||||||
// The server returns 400 with the exact missing env var name when a provider is
|
// The server returns 400 with the exact missing env var name when a provider is
|
||||||
// unconfigured, so its body is surfaced verbatim rather than replaced.
|
// unconfigured, so its body is surfaced verbatim rather than replaced.
|
||||||
export async function requestEntrySummary(archiveId, entryUid, { provider, force = false } = {}) {
|
export async function requestEntrySummary(archiveId, entryUid, { provider, force = false, includeImages = false } = {}) {
|
||||||
const resp = await fetch(
|
const resp = await fetch(
|
||||||
`/api/archives/${archiveId}/entries/${entryUid}/summary`,
|
`/api/archives/${archiveId}/entries/${entryUid}/summary`,
|
||||||
{
|
{
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: { "Content-Type": "application/json" },
|
headers: { "Content-Type": "application/json" },
|
||||||
body: JSON.stringify({ provider, force }),
|
body: JSON.stringify({ provider, force, include_images: includeImages }),
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
if (!resp.ok) {
|
if (!resp.ok) {
|
||||||
|
|
|
||||||
|
|
@ -69,6 +69,7 @@ export default function ContextRail({ archiveId, selectedEntry, selectedUids, se
|
||||||
return sessionStorage.getItem(SUMMARY_PROVIDER_KEY) || SUMMARY_PROVIDERS[0].value
|
return sessionStorage.getItem(SUMMARY_PROVIDER_KEY) || SUMMARY_PROVIDERS[0].value
|
||||||
} catch { return SUMMARY_PROVIDERS[0].value }
|
} catch { return SUMMARY_PROVIDERS[0].value }
|
||||||
})
|
})
|
||||||
|
const [includeSummaryImages, setIncludeSummaryImages] = useState(false)
|
||||||
const summaryPollRef = useRef(null)
|
const summaryPollRef = useRef(null)
|
||||||
|
|
||||||
// ── Bulk-panel state ────────────────────────────────────────────────────
|
// ── Bulk-panel state ────────────────────────────────────────────────────
|
||||||
|
|
@ -129,6 +130,7 @@ export default function ContextRail({ archiveId, selectedEntry, selectedUids, se
|
||||||
setSummary(detail?.latest_summary ?? null)
|
setSummary(detail?.latest_summary ?? null)
|
||||||
setSummaryError('')
|
setSummaryError('')
|
||||||
setSummaryBusy(false)
|
setSummaryBusy(false)
|
||||||
|
setIncludeSummaryImages(false)
|
||||||
}, [detail?.summary?.entry_uid])
|
}, [detail?.summary?.entry_uid])
|
||||||
|
|
||||||
// Poll only while the latest summary is non-terminal. Anchoring the effect on
|
// Poll only while the latest summary is non-terminal. Anchoring the effect on
|
||||||
|
|
@ -173,6 +175,7 @@ export default function ContextRail({ archiveId, selectedEntry, selectedUids, se
|
||||||
const res = await requestEntrySummary(archiveId, detail.summary.entry_uid, {
|
const res = await requestEntrySummary(archiveId, detail.summary.entry_uid, {
|
||||||
provider: summaryProvider,
|
provider: summaryProvider,
|
||||||
force,
|
force,
|
||||||
|
includeImages: includeSummaryImages,
|
||||||
})
|
})
|
||||||
if (res.status === 'completed') {
|
if (res.status === 'completed') {
|
||||||
// 200 cache hit: the response *is* the row, no polling needed.
|
// 200 cache hit: the response *is* the row, no polling needed.
|
||||||
|
|
@ -192,6 +195,7 @@ export default function ContextRail({ archiveId, selectedEntry, selectedUids, se
|
||||||
|
|
||||||
function handleProviderChange(value) {
|
function handleProviderChange(value) {
|
||||||
setSummaryProvider(value)
|
setSummaryProvider(value)
|
||||||
|
if (value === 'claude_cli') setIncludeSummaryImages(false)
|
||||||
try { sessionStorage.setItem(SUMMARY_PROVIDER_KEY, value) } catch { /* private mode */ }
|
try { sessionStorage.setItem(SUMMARY_PROVIDER_KEY, value) } catch { /* private mode */ }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -615,6 +619,22 @@ export default function ContextRail({ archiveId, selectedEntry, selectedUids, se
|
||||||
<option key={p.value} value={p.value}>{p.label}</option>
|
<option key={p.value} value={p.value}>{p.label}</option>
|
||||||
))}
|
))}
|
||||||
</select>
|
</select>
|
||||||
|
<div className={`rail-summary-image-option${summaryProvider === 'claude_cli' ? ' rail-summary-image-option--disabled' : ''}`}>
|
||||||
|
<label className="rail-summary-image-option__label">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
checked={includeSummaryImages}
|
||||||
|
disabled={summaryProvider === 'claude_cli'}
|
||||||
|
onChange={e => setIncludeSummaryImages(e.target.checked)}
|
||||||
|
/>
|
||||||
|
Include attached images
|
||||||
|
</label>
|
||||||
|
<p className="rail-summary-image-option__note">
|
||||||
|
{summaryProvider === 'claude_cli'
|
||||||
|
? 'Claude CLI cannot attach local images. Choose an HTTP provider or Codex CLI.'
|
||||||
|
: 'Selected archived images are sent to the chosen provider. Up to 4 supported images (5 MiB each, 12 MiB total) can be attached; unsupported or oversized artifacts are skipped.'}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
<button
|
<button
|
||||||
className="rail-rearchive-btn"
|
className="rail-rearchive-btn"
|
||||||
onClick={() => handleGenerateSummary(!!parsed)}
|
onClick={() => handleGenerateSummary(!!parsed)}
|
||||||
|
|
|
||||||
|
|
@ -3285,6 +3285,46 @@ body.has-audio-bar { padding-bottom: 56px; }
|
||||||
border: 1px solid var(--line);
|
border: 1px solid var(--line);
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
}
|
}
|
||||||
|
.rail-summary-image-option {
|
||||||
|
padding: 7px 8px;
|
||||||
|
color: var(--muted);
|
||||||
|
background: var(--paper);
|
||||||
|
border: 1px solid var(--line);
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
.rail-summary-image-option__label {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
color: var(--ink);
|
||||||
|
font-size: 12.5px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.rail-summary-image-option__label input[type="checkbox"] {
|
||||||
|
margin: 0;
|
||||||
|
accent-color: var(--accent);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.rail-summary-image-option__label input[type="checkbox"]:focus-visible {
|
||||||
|
outline: 2px solid var(--accent);
|
||||||
|
outline-offset: 2px;
|
||||||
|
}
|
||||||
|
.rail-summary-image-option__note {
|
||||||
|
margin: 5px 0 0;
|
||||||
|
font-size: 11px;
|
||||||
|
line-height: 1.4;
|
||||||
|
}
|
||||||
|
.rail-summary-image-option--disabled {
|
||||||
|
color: var(--muted-2);
|
||||||
|
background: var(--paper);
|
||||||
|
}
|
||||||
|
.rail-summary-image-option--disabled .rail-summary-image-option__label {
|
||||||
|
color: var(--muted-2);
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
.rail-summary-image-option--disabled .rail-summary-image-option__label input[type="checkbox"] {
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
/* ── Text / Markdown preview ────────────────────────────────────────────── */
|
/* ── Text / Markdown preview ────────────────────────────────────────────── */
|
||||||
.text-preview {
|
.text-preview {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue