Merge pull request 'Switch session permission mode beside the composer' (#41) from feature/permission-hooks into main
This commit was merged in pull request #41.
This commit is contained in:
@@ -27,6 +27,7 @@ export const api = {
|
||||
// 跨项目任务列表(方案 A 侧栏):默认仅未归档;includeArchived 含归档。
|
||||
listTasks: (includeArchived?: boolean) => auth.fetch(`${BASE}/api/conversations${includeArchived ? '?archived=1' : ''}`).then(r => json<Conversation[]>(r)),
|
||||
setConversationFlags: (id: string, flags: { pinned?: boolean; archived?: boolean; allowCrossRead?: boolean; crossPrivate?: boolean }) => auth.fetch(`${BASE}/api/conversations/${id}`, { method: 'PATCH', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(flags) }).then(r => { if (!r.ok) throw new Error(`${r.status}: ${r.statusText}`) }),
|
||||
setPermissionMode: (id: string, permissionMode: string) => auth.fetch(`${BASE}/api/conversations/${id}`, { method: 'PATCH', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ permissionMode }) }).then(r => { if (!r.ok) throw new Error(`${r.status}: ${r.statusText}`) }),
|
||||
me: () => auth.fetch(`${BASE}/api/me`).then(r => json<{ owner: string; name: string; auth: boolean; isAdmin?: boolean; mustChangePassword?: boolean }>(r)),
|
||||
// ---- 认证与会话(管理员发放制,R2)----
|
||||
login: (owner: string, password: string) => auth.fetch(`${BASE}/api/auth/login`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ owner, password }) }).then(async r => {
|
||||
|
||||
@@ -315,8 +315,9 @@ function HITLCardView({ card }: { card: HITLCard }) {
|
||||
setSubmitting(false)
|
||||
}
|
||||
return (
|
||||
<div className="hitl-card">
|
||||
<div className={`hitl-card${card.kind === 'confirm' ? ' wait' : ''}${card.kind === 'choice' || card.kind === 'input' ? ' ask' : ''}`}>
|
||||
<div className="hitl-q"><Icon name="check" size={15} />{card.question}</div>
|
||||
{card.detail && <pre className="hitl-detail">{card.detail}</pre>}
|
||||
{card.choices && (
|
||||
<div className="hitl-choices">
|
||||
{card.choices.map(c => (
|
||||
@@ -579,6 +580,19 @@ export function Conversation() {
|
||||
)}
|
||||
{text.startsWith('/') && <CommandMenu text={text} onPick={c => setText(c)} />}
|
||||
<div className="input-row">
|
||||
<label className="mode-select" title="本会话的权限模式">
|
||||
<span>权限</span>
|
||||
<select value={conversation?.permissionMode || 'default'} onChange={e => {
|
||||
const mode = e.target.value
|
||||
if (mode === 'default' || mode === 'plan' || mode === 'auto_edit') {
|
||||
if (currentConvId) void store.setPermissionMode(currentConvId, mode)
|
||||
}
|
||||
}}>
|
||||
<option value="default">默认</option>
|
||||
<option value="plan">计划</option>
|
||||
<option value="auto_edit">自动改文件</option>
|
||||
</select>
|
||||
</label>
|
||||
<label className="icon-btn" title="上传附件(文档解析后注入上下文)">
|
||||
<Icon name="paperclip" size={16} /><input type="file" multiple style={{ display: 'none' }} onChange={async e => {
|
||||
for (const f of Array.from(e.target.files ?? [])) {
|
||||
|
||||
@@ -16,6 +16,7 @@ export interface Conversation {
|
||||
id: ID; projectId: ID; owner?: ID; title: string; profileId?: ID;
|
||||
pinned?: boolean; archived?: boolean;
|
||||
allowCrossRead?: boolean; crossPrivate?: boolean;
|
||||
permissionMode?: 'default' | 'plan' | 'auto_edit' | '';
|
||||
createdAt: string; updatedAt: string;
|
||||
}
|
||||
|
||||
@@ -151,6 +152,7 @@ export interface HITLCard {
|
||||
choices?: HITLChoice[]; allowFreeInput: boolean; freeInputHint?: string;
|
||||
timeoutSec?: number | null; form?: UINode[]; createdAt: string; resolved: boolean;
|
||||
anchorMessageId?: ID; blocked?: boolean; blockReason?: string;
|
||||
detail?: string;
|
||||
}
|
||||
|
||||
export interface HITLResponse {
|
||||
|
||||
@@ -298,6 +298,12 @@ export const store = {
|
||||
},
|
||||
|
||||
// 跨会话标记(提案 2026-09-30-cross-session-reading):出站 allowCrossRead / 入站 crossPrivate。
|
||||
async setPermissionMode(convId: string, permissionMode: 'default' | 'plan' | 'auto_edit') {
|
||||
const tasks = state.tasks.map(t => t.id === convId ? { ...t, permissionMode } : t)
|
||||
set({ tasks })
|
||||
try { await api.setPermissionMode(convId, permissionMode) } catch (e) { store.toast(`权限模式切换失败:${e}`); store.refreshTasks() }
|
||||
},
|
||||
|
||||
async setCrossFlags(convId: string, flags: { allowCrossRead?: boolean; crossPrivate?: boolean }) {
|
||||
const tasks = state.tasks.map(t => t.id === convId ? { ...t, ...flags } : t)
|
||||
set({ tasks })
|
||||
|
||||
@@ -226,6 +226,11 @@ button { font-family: inherit; font-size: inherit; color: inherit; }
|
||||
|
||||
/* HITL 卡片(确认类交互统一绿色语言) */
|
||||
.hitl-card { border: 1px solid var(--ok-border); background: var(--ok-soft); border-radius: var(--radius-lg); padding: 14px; margin: 10px 0; }
|
||||
.hitl-card.wait { border-color: var(--wait); background: var(--wait-soft); }
|
||||
.hitl-card.ask { border-color: var(--border-strong); background: var(--bg-raised); }
|
||||
.hitl-detail { white-space: pre-wrap; margin: 8px 0 0; font-size: var(--fs-ui-sm); color: var(--text-faint); font-family: inherit; }
|
||||
.mode-select { display: inline-flex; align-items: center; gap: 6px; font-size: var(--fs-ui-sm); color: var(--text-faint); }
|
||||
.mode-select select { font-size: var(--fs-ui-sm); color: var(--text); background: var(--bg-panel); border: 1px solid var(--border); border-radius: var(--radius-sm); padding: 4px 8px; }
|
||||
.hitl-q { font-weight: 600; margin-bottom: 10px; display: flex; gap: 8px; align-items: flex-start; }
|
||||
.hitl-q svg { color: var(--ok); flex: none; margin-top: 1px; }
|
||||
.hitl-choices { display: flex; flex-direction: column; gap: 8px; }
|
||||
|
||||
Reference in New Issue
Block a user