peregrine/web/src/stores/settings/privacy.ts
pyr0ball 8bd1be7b16 feat(web): merge Vue SPA from feature/vue-spa; add ClassicUIButton + useFeatureFlag
- Import web/ directory (Vue 3 + Vite + UnoCSS SPA) from feature/vue-spa branch
- Add web/src/components/ClassicUIButton.vue: switch-back to Streamlit via
  cookie (prgn_ui=streamlit) + ?prgn_switch=streamlit query param bridge
- Add web/src/composables/useFeatureFlag.ts: reads prgn_demo_tier cookie for
  demo toolbar visual consistency (not an authoritative gate, see issue #8)
- Update .gitignore: add .superpowers/, pytest-output.txt, docs/superpowers/
2026-03-22 18:46:11 -07:00

64 lines
2.3 KiB
TypeScript

import { ref, computed } from 'vue'
import { defineStore } from 'pinia'
import { useApiFetch } from '../../composables/useApi'
export const usePrivacyStore = defineStore('settings/privacy', () => {
// Session-scoped BYOK panel state
const activeCloudBackends = ref<string[]>([])
const byokInfoDismissed = ref(false)
const dismissedForBackends = ref<string[]>([])
// Self-hosted privacy prefs
const telemetryOptIn = ref(false)
// Cloud privacy prefs
const masterOff = ref(false)
const usageEvents = ref(true)
const contentSharing = ref(false)
const loading = ref(false)
const saving = ref(false)
// Panel shows if there are active cloud backends not yet covered by dismissal snapshot,
// or if byokInfoDismissed was set directly (e.g. loaded from server) and new backends haven't appeared
const showByokPanel = computed(() => {
if (activeCloudBackends.value.length === 0) return false
if (byokInfoDismissed.value && activeCloudBackends.value.every(b => dismissedForBackends.value.includes(b))) return false
if (byokInfoDismissed.value && dismissedForBackends.value.length === 0) return false
return !activeCloudBackends.value.every(b => dismissedForBackends.value.includes(b))
})
function dismissByokInfo() {
dismissedForBackends.value = [...activeCloudBackends.value]
byokInfoDismissed.value = true
}
async function loadPrivacy() {
loading.value = true
const { data } = await useApiFetch<Record<string, unknown>>('/api/settings/privacy')
loading.value = false
if (!data) return
telemetryOptIn.value = Boolean(data.telemetry_opt_in)
byokInfoDismissed.value = Boolean(data.byok_info_dismissed)
masterOff.value = Boolean(data.master_off)
usageEvents.value = data.usage_events !== false
contentSharing.value = Boolean(data.content_sharing)
}
async function savePrivacy(prefs: Record<string, unknown>) {
saving.value = true
await useApiFetch('/api/settings/privacy', {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(prefs),
})
saving.value = false
}
return {
activeCloudBackends, byokInfoDismissed, dismissedForBackends,
telemetryOptIn, masterOff, usageEvents, contentSharing,
loading, saving, showByokPanel,
dismissByokInfo, loadPrivacy, savePrivacy,
}
})