feat(vscode): 多人协作二期:状态分层/写校验/签名/团队视图

- 本机状态(会话id/worktree/profile/prDiffFile)迁 workspaceState,
  共享 state JSON 只留团队字段;读侧本地记录叠加,旧数据兜底
- mergeStateJsonComment 写后读回校验,并发覆盖时在最新状态上重放一次
- webhook 支持 Gitea secret,校验 X-Gitea-Signature(HMAC-SHA256),
  不匹配 401;设置面板新增 Webhook Secret 字段
- 看板新增 我的/全部 范围切换(团队视图),按工作区持久化
This commit is contained in:
2026-08-19 00:32:06 +08:00
parent c370214d12
commit faf227412e
18 changed files with 665 additions and 202 deletions
@@ -8,9 +8,9 @@
*/
import type { ReactElement } from 'react'
import { useEffect, useState } from 'react'
import { X } from 'lucide-react'
import type { ClaudeProfile } from '../hooks/useIssues'
import { X } from 'lucide-react'
import { useEffect, useState } from 'react'
type GroupKey = 'auth' | 'network' | 'youtrack' | 'prompts' | 'hooks'
@@ -18,6 +18,7 @@ interface SubmitValues {
host: string
token: string
webhookPort: number
webhookSecret: string
brainstormPrompt: string
brainstormContinuePrompt: string
implementPlanPrompt: string
@@ -63,6 +64,7 @@ export interface SettingsModalProps {
canCancel?: boolean
initialTokenSaved: boolean
initialWebhookPort: number
initialWebhookSecret: string
initialBrainstormPrompt: string
initialBrainstormContinuePrompt: string
initialImplementPlanPrompt: string
@@ -127,8 +129,10 @@ const GROUPS: Array<{ key: GroupKey, label: string }> = [
type ErrorKey = 'host' | 'token' | 'webhookPort'
/** Map a field name to the group it lives in, so we can auto-switch to the
* first invalid field's group on save. */
/**
* Map a field name to the group it lives in, so we can auto-switch to the
* first invalid field's group on save.
*/
const FIELD_TO_GROUP: Record<ErrorKey, GroupKey> = {
host: 'auth',
token: 'auth',
@@ -142,6 +146,7 @@ export function SettingsModal({
canCancel,
initialTokenSaved,
initialWebhookPort,
initialWebhookSecret,
initialBrainstormPrompt,
initialBrainstormContinuePrompt,
initialImplementPlanPrompt,
@@ -175,6 +180,7 @@ export function SettingsModal({
const [host, setHost] = useState(initialHost)
const [token, setToken] = useState('')
const [webhookPort, setWebhookPort] = useState<string>(String(initialWebhookPort))
const [webhookSecret, setWebhookSecret] = useState(initialWebhookSecret)
const [brainstormPrompt, setBrainstormPrompt] = useState(initialBrainstormPrompt)
const [brainstormContinuePrompt, setBrainstormContinuePrompt] = useState(initialBrainstormContinuePrompt)
const [implementPlanPrompt, setImplementPlanPrompt] = useState(initialImplementPlanPrompt)
@@ -210,6 +216,7 @@ export function SettingsModal({
setHost(initialHost)
setToken('')
setWebhookPort(String(initialWebhookPort))
setWebhookSecret(initialWebhookSecret)
setBrainstormPrompt(initialBrainstormPrompt)
setBrainstormContinuePrompt(initialBrainstormContinuePrompt)
setImplementPlanPrompt(initialImplementPlanPrompt)
@@ -294,6 +301,7 @@ export function SettingsModal({
// extension side detects empty + existing-saved as "preserve".
token: trimmedToken,
webhookPort: portNum,
webhookSecret: webhookSecret.trim(),
brainstormPrompt,
brainstormContinuePrompt,
implementPlanPrompt,
@@ -578,7 +586,13 @@ export function SettingsModal({
HTTP server gitea Webhooks URL
{' '}
<code>{'http://<你的本机/公网域名>:<此端口>/webhook'}</code>
<b></b>issue opened/edited<b></b>PR opened/synchronize/closed<b></b>codex
<b></b>
issue opened/edited
<b></b>
PR opened/synchronize/closed
<b></b>
codex
</>
)}
>
@@ -592,6 +606,30 @@ export function SettingsModal({
/>
</Field>
<Field
label="Webhook Secret"
hint={(
<>
gitea Webhooks
{' '}
<b></b>
{' '}
{' '}
<code>X-Gitea-Signature</code>
webhook =
</>
)}
>
<input
type="text"
value={webhookSecret}
onChange={e => setWebhookSecret(e.target.value)}
placeholder="留空表示不校验签名"
className={inputClass}
/>
</Field>
<Field
label="开启自动审查"
hint="PR 创建后自动用 codex exec review 跑审查并把结果灌到实施会话终端"
@@ -791,7 +829,12 @@ export function SettingsModal({
<Field
label="审查提示词"
hint={<><code>{'{prNumber}'}</code></>}
hint={(
<>
<code>{'{prNumber}'}</code>
</>
)}
>
<textarea
rows={4}