Release: dev → main (first public release) #258

Merged
bvandeusen merged 94 commits from dev into main 2026-09-25 10:02:40 -04:00
2 changed files with 56 additions and 15 deletions
Showing only changes of commit b3e4491b0a - Show all commits
@@ -6,9 +6,10 @@
>
<div class="text-caption fc-muted mb-3">
Some creators post a cropped fragment on Patreon to say the real thing has
landed in their Discord. FC proposes those pairs; nothing is linked until
you accept one. A wrong link would tell you two different pieces are the
same, so this stays a suggestion.
landed in their Discord. When the creator's own file name settles it —
the same working name on both posts and nowhere else in their library —
FC links the pair for you. Anything less certain waits here, because a
wrong link would tell you two different pieces are the same.
</div>
<v-switch
@@ -17,6 +18,17 @@
@update:model-value="store.setEnabled"
/>
<v-switch
v-model="auto" color="accent" hide-details density="compact"
:disabled="!enabled"
label="Link a pair outright when the file name settles it"
@update:model-value="store.setAuto"
/>
<div class="text-caption fc-muted mb-2">
Off, every pair waits for you here — including the ones there is nothing
left to decide about.
</div>
<v-row class="mt-2">
<v-col cols="12" sm="6">
<SettingNumberField
@@ -26,10 +38,13 @@
:disabled="!enabled" @change="store.setThreshold(Number(threshold))"
/>
<div class="text-caption fc-muted mt-1">
A pair always needs <strong>two</strong> reasons — being close in time
and the post itself mentioning Discord. Neither is enough alone at any
setting at or above 0.55, which is what stops a busy posting day from
producing false pairs.
A pair needs <strong>two</strong> reasons — close in time, the post
mentioning Discord or the server, or a marker the creator uses on
both and almost nowhere else. None is enough alone at any setting at
or above 0.45, which is what stops a busy posting day from producing
false pairs. A shared working name is weighed separately: it says the
two are the same piece rather than that they happened near each
other.
</div>
</v-col>
<v-col cols="12" sm="6">
@@ -57,8 +72,9 @@
</div>
<div v-if="!store.proposals.length" class="text-caption fc-muted">
Nothing proposed. That is the expected state most of the time — pairs only
appear when a post both lands near a drop and says it is about Discord.
Nothing waiting. That is the expected state most of the time — a pair the
file names settle is linked without asking, and everything else needs a
post to both land near a drop and point at Discord.
</div>
<div
@@ -80,6 +96,19 @@
{{ Math.round(p.score * 100) }}% ·
timing {{ Math.round((p.signals?.proximity ?? 0) * 100) }}% ·
says so {{ Math.round((p.signals?.declared ?? 0) * 100) }}%
<template v-if="p.signals?.marker">
· shared marker {{ Math.round(p.signals.marker * 100) }}%
</template>
</div>
<!-- The name, when there is one. A queue that cannot say WHY is one
you learn to click through without reading — and this is the only
signal that claims the two are the SAME piece rather than that
they happened near each other. -->
<div v-if="p.signals?.identity_token" class="text-caption fc-muted">
both call it
<code>{{ p.signals.identity_token }}</code>
({{ Math.round((p.signals.identity ?? 0) * 100) }}% — shared with
another post of theirs, so not conclusive on its own)
</div>
</div>
<v-btn size="small" variant="tonal" @click="store.accept(p.id)">Link</v-btn>
@@ -98,10 +127,12 @@ import SettingNumberField from '../common/SettingNumberField.vue'
const store = usePostAssociationsStore()
const enabled = ref(true)
const auto = ref(true)
const threshold = ref(0.6)
const windowHours = ref(24)
watch(() => store.enabled, (v) => { enabled.value = v }, { immediate: true })
watch(() => store.auto, (v) => { auto.value = v }, { immediate: true })
watch(() => store.threshold, (v) => { threshold.value = v }, { immediate: true })
watch(() => store.windowHours, (v) => { windowHours.value = v }, { immediate: true })
+16 -6
View File
@@ -6,14 +6,18 @@ import { useAsyncAction } from '../composables/useAsyncAction.js'
import { toast } from '../utils/toast.js'
// Backs the announcement review queue (#388 E5): "this Patreon post announced
// that Discord drop". Confirm-only, deliberately — a wrongly-asserted link
// tells the operator two different pieces are one, which is worse than no link
// at all, so accept is the ONLY thing that makes a link real. Mirrors
// seriesSuggestions (FC-6.3), which is the same shape for the same reason.
// that Discord drop". A wrongly-asserted link tells the operator two different
// pieces are one, which is worse than no link at all — so what reaches this
// queue is everything FC is NOT sure enough about to act on by itself.
//
// A pair the creator's own working name identifies, where that name is on
// these two posts and nowhere else in their library, is linked without asking
// (`auto`). Mirrors seriesSuggestions (FC-6.3) in shape.
export const usePostAssociationsStore = defineStore('postAssociations', () => {
const api = useApi()
const proposals = ref([])
const enabled = ref(true)
const auto = ref(true)
const threshold = ref(0.6)
const windowHours = ref(24)
const { loading, error, run } = useAsyncAction({ errorAs: 'message' })
@@ -28,6 +32,7 @@ export const usePostAssociationsStore = defineStore('postAssociations', () => {
async function loadSettings () {
const s = await api.get('/api/settings/import')
enabled.value = s.discord_link_enabled
auto.value = s.discord_link_auto
threshold.value = s.discord_link_threshold
windowHours.value = s.discord_link_window_hours
}
@@ -41,6 +46,11 @@ export const usePostAssociationsStore = defineStore('postAssociations', () => {
await saveSettings({ discord_link_enabled: v })
}
async function setAuto (v) {
auto.value = v
await saveSettings({ discord_link_auto: v })
}
async function setThreshold (v) {
threshold.value = v
await saveSettings({ discord_link_threshold: v })
@@ -76,8 +86,8 @@ export const usePostAssociationsStore = defineStore('postAssociations', () => {
}
return {
proposals, enabled, threshold, windowHours, loading, error,
load, loadSettings, setEnabled, setThreshold, setWindowHours,
proposals, enabled, auto, threshold, windowHours, loading, error,
load, loadSettings, setEnabled, setAuto, setThreshold, setWindowHours,
accept, dismiss, rescan
}
})