// CoCreate.jsx — 墨吞 · 牵线(共创) // 找人帮助分两条路,是墨吞常用的问题结构: // ① 现成的(过去):有人已经写过 → 引用并在文末嵌入来源(带 ID / 出处) // ② 实时的(现在):信息还没有、或需要现场(如到某地拍照)→ 约人; // 墨吞依据你写的内容做 faiss 检索,并判断是否该请专业人士介入。 // 墨吞在这里只做"建立联系"。(演示流程,确定性匹配,未接真实 faiss) const { useState: useStateCC } = React; function CoPersonCard({ row, mode, joined, onInvite }) { const { p, score, whenPlace, pro } = row; const live = (mode === 'live'); return (
{p.name[0]}
{p.name} {p.prof} {live && pro && 专业}
{p.id}
{score}% 语境相关
现在 {p.now} {p.been.length > 0 && ( 去过 {p.been.join(' · ')} )} {live && whenPlace && {whenPlace}}

{p.snippet}

{live ? {p.offer} : 来源《{p.note}》· 已发表}
{joined ? ( ) : live ? ( ) : ( )}
); } function CoCreate({ note, embedded, lockMode, onCite }) { const [mode, setMode] = useStateCC(lockMode || 'past'); // past 现成 / live 实时 const [query, setQuery] = useStateCC(''); const [activeDim, setActiveDim] = useStateCC(null); const [busy, setBusy] = useStateCC(false); const [results, setResults] = useStateCC(null); const [cited, setCited] = useStateCC([]); // ① 已引用(带出处) const [invited, setInvited] = useStateCC([]); // ② 已约人(实时) const [compose, setCompose] = useStateCC(null); // 正在写的邀约卡(live) const [inviteText, setInviteText] = useStateCC(''); const [available, setAvailable] = useStateCC(true); // 可被邀约(用户可关闭) const judge = motunCoJudge(note); const dims = coDimensions(note, mode); const pageTitle = (note && note.title) || '这一页'; const place0 = (note && note.locs && note.locs[0]) || ''; function switchMode(m) { if (m === mode) return; setMode(m); setResults(null); setActiveDim(null); setQuery(''); } function search(q, dimId) { if (busy) return; setActiveDim(dimId || null); setBusy(true); setResults(null); setTimeout(() => { setResults(faissMatch(note, q, mode === 'live')); setBusy(false); }, 950); } function runDim(d) { setQuery(d.id === 'prof' || d.id === 'pro' ? '' : d.label.replace(/[「」]/g, '')); search(d.q || d.label, d.id); } // —— ① 引用现成(past)—— function cite(row) { if (cited.some(j => j.p.id === row.p.id)) return; const n = onCite ? onCite(row.p, 0) : (cited.length + 1); setCited(j => [...j, { ...row, n }]); } // —— ② 约人(live):先写邀约卡,再发出 —— function openCompose(row) { if (invited.some(j => j.p.id === row.p.id)) return; const pl = place0 || row.p.now; const canShoot = /拍|现拍/.test(row.p.offer || ''); setInviteText(canShoot ? `想请你在${pl},为《${pageTitle}》拍一张照片 —— 如果你愿意。` : `想请你聊聊「${(note && note.notes && note.notes[0]) || row.p.prof}」,为《${pageTitle}》补一块。`); setCompose(row); } function sendInvite() { if (!compose) return; const row = compose; const canShoot = /拍|现拍/.test(inviteText) || /现拍/.test(row.p.offer || ''); setInvited(j => [...j, { p: row.p, pro: row.pro, msg: inviteText, status: 'waiting', material: null, n: 0 }]); setCompose(null); setTimeout(() => { setInvited(list => list.map(it => { if (it.p.id !== row.p.id || it.status !== 'waiting') return it; const material = canShoot ? { kind: 'photo', label: '应约现拍 · ' + (place0 || row.p.now) } : { kind: 'note', label: row.p.snippet }; let n = 0; if (onCite) { const src = canShoot ? '应约现拍《' + pageTitle + '》· ' + row.p.id + ' · ' + (typeof NOW !== 'undefined' ? NOW.date : '') : '应约共创 · ' + row.p.id + ' · “' + row.p.snippet + '”'; n = onCite(row.p, 0, { source: src }); } return Object.assign({}, it, { status: 'accepted', material, n }); })); }, 2200); } function invite(row) { mode === 'live' ? openCompose(row) : cite(row); } function removeCite(id) { setCited(j => j.filter(x => x.p.id !== id).map((x, i) => ({ ...x, n: i + 1 }))); } function removeInvite(id) { setInvited(j => j.filter(x => x.p.id !== id)); } const isJoined = id => (mode === 'live' ? invited : cited).some(j => j.p.id === id); const corpus = (1240 + (note ? note.id.charCodeAt(note.id.length - 1) * 7 : 0)); return (
{/* 墨吞 · 判断这一页缺什么,并结构成两条路 */} {!embedded && (
墨吞 · 牵线 — 这一页,要不要请人补一块?
我读了这一页,判断你可能缺:{judge.missing}。这通常有两条路 —
向量检索
)} {/* 两条路 · 模式切换(嵌入书写视图时由页签锁定,隐藏切换)*/} {!lockMode && (
)} {/* 当前路的墨吞提示 */}
{mode === 'past' ? {judge.pastLine} : {judge.liveLine} {judge.proRole && 建议请「{judge.proRole}」介入。} }
{/* 可被邀约 · 用户可随时关闭 */} {mode === 'live' && ( )} {/* 邀约卡 · 真正发出的一张卡片 */} {compose && (
{compose.p.name[0]}
致 {compose.p.name} {compose.p.prof}
一张来自《{pageTitle}》的邀约