feat(settings): 新建对话按钮行为可选,移除根目录创建文件与诊断日志入口
This commit is contained in:
parent
57352b28fb
commit
87a6a03943
@ -64,18 +64,6 @@ class CrudMixin:
|
||||
relative_path = self._relative_path(full_path)
|
||||
|
||||
try:
|
||||
# 检查是否允许在根目录创建文件
|
||||
personalization_config = self._load_personalization_config()
|
||||
allow_root_creation = bool(
|
||||
personalization_config.get("allow_root_file_creation", False)
|
||||
) if isinstance(personalization_config, dict) else False
|
||||
|
||||
if full_path.parent == self.project_path and not allow_root_creation:
|
||||
return {
|
||||
"success": False,
|
||||
"error": "禁止在项目根目录直接创建文件,请先创建或选择合适的子目录。",
|
||||
"suggestion": "然后必须**重新**再次创建文件。"
|
||||
}
|
||||
if self._use_container():
|
||||
result = self._container_call("create_file", {
|
||||
"path": relative_path,
|
||||
|
||||
@ -103,7 +103,7 @@ DEFAULT_PERSONALIZATION_CONFIG: Dict[str, Any] = {
|
||||
"versioning_backup_mode": "shallow", # 文件备份方式:shallow-浅备份(只备份AI编辑的文件)/ full-完全备份(整个工作区)
|
||||
"versioning_restore_mode": "overwrite", # 版本回溯模式固定为 overwrite
|
||||
"agents_md_auto_inject": False, # AGENTS.md 自动注入开关
|
||||
"allow_root_file_creation": False, # 允许在根目录创建文件开关
|
||||
"new_chat_button_behavior": "route", # 新建对话按钮行为:route-跳转空白新对话页 / blank-立即创建空对话
|
||||
"default_hide_workspace": False, # 默认隐藏工作区
|
||||
"hide_quick_dock": False, # 隐藏快捷窗口(对话区右侧的待办/子智能体/后台指令/文件窗口列)
|
||||
"group_sidebar_by_workspace": False, # 侧边栏按工作区/项目分组显示对话
|
||||
@ -517,11 +517,12 @@ def sanitize_personalization_payload(
|
||||
else:
|
||||
base["agents_md_auto_inject"] = bool(base.get("agents_md_auto_inject", False))
|
||||
|
||||
# 允许根目录创建文件开关
|
||||
if "allow_root_file_creation" in data:
|
||||
base["allow_root_file_creation"] = bool(data.get("allow_root_file_creation"))
|
||||
# 新建对话按钮行为:route-跳转空白新对话页 / blank-立即创建空对话
|
||||
new_chat_behavior = data.get("new_chat_button_behavior", base.get("new_chat_button_behavior"))
|
||||
if isinstance(new_chat_behavior, str) and new_chat_behavior.strip().lower() in ("route", "blank"):
|
||||
base["new_chat_button_behavior"] = new_chat_behavior.strip().lower()
|
||||
else:
|
||||
base["allow_root_file_creation"] = bool(base.get("allow_root_file_creation", False))
|
||||
base["new_chat_button_behavior"] = "route"
|
||||
|
||||
# 默认隐藏工作区
|
||||
if "default_hide_workspace" in data:
|
||||
|
||||
@ -1134,21 +1134,6 @@ def resource_busy_page():
|
||||
return app.send_static_file('resource_busy.html'), 503
|
||||
|
||||
|
||||
@app.route('/api/voice_debug', methods=['POST'])
|
||||
def voice_debug():
|
||||
"""接收手机端语音调试日志"""
|
||||
import os, datetime
|
||||
data = request.get_data(as_text=True)
|
||||
log_dir = os.environ.get('LOGS_DIR', os.path.join(os.path.dirname(__file__), '..', 'logs'))
|
||||
voice_log_dir = os.path.join(log_dir, 'voice_debug')
|
||||
os.makedirs(voice_log_dir, exist_ok=True)
|
||||
ts = datetime.datetime.now().strftime('%Y%m%d_%H%M%S')
|
||||
filename = os.path.join(voice_log_dir, f'voice_{ts}.log')
|
||||
with open(filename, 'w') as f:
|
||||
f.write(data)
|
||||
return jsonify({'ok': True, 'file': filename})
|
||||
|
||||
|
||||
@app.route('/api/client_debug_log', methods=['POST'])
|
||||
def client_debug_log():
|
||||
"""接收前端目标模式等调试日志"""
|
||||
|
||||
@ -62,6 +62,23 @@ export const actionMethods = {
|
||||
keepalive: true
|
||||
}).catch(() => {});
|
||||
|
||||
// 按个性化设置分流:route-跳转空白新对话页(发送首条消息时才真正创建);
|
||||
// blank-保持现有行为,立即创建空对话
|
||||
let newChatBehavior = 'route';
|
||||
try {
|
||||
newChatBehavior = usePersonalizationStore()?.form?.new_chat_button_behavior || 'route';
|
||||
} catch (error) {
|
||||
console.warn('读取新建对话按钮行为设置失败,按默认跳转处理:', error);
|
||||
}
|
||||
if (newChatBehavior !== 'blank') {
|
||||
const target = this.multiAgentMode ? '/multiagent/new' : '/new';
|
||||
const normalize = (p) => String(p || '/').replace(/^\/+|\/+$/g, '');
|
||||
if (normalize(window.location.pathname) !== normalize(target)) {
|
||||
window.location.href = target;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// 应用个性化设置中的默认模型和思考模式
|
||||
try {
|
||||
const personalizationStore = usePersonalizationStore();
|
||||
|
||||
@ -431,21 +431,6 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="settings-action-row">
|
||||
<span class="settings-row-copy">
|
||||
<span class="settings-row-title">上传诊断日志</span>
|
||||
<span class="settings-row-desc"
|
||||
>复现问题后点击,把当前环境信息上传给开发者排查</span
|
||||
>
|
||||
</span>
|
||||
<button
|
||||
type="button"
|
||||
class="settings-secondary-button"
|
||||
@click="uploadDiagnosticLog"
|
||||
>
|
||||
上传日志
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section
|
||||
@ -640,6 +625,28 @@
|
||||
class="fancy-path"
|
||||
></path></svg></span
|
||||
></label>
|
||||
<label class="settings-toggle-row"
|
||||
><span class="settings-row-copy"
|
||||
><span class="settings-row-title">新建对话跳转空白页</span
|
||||
><span class="settings-row-desc"
|
||||
>开启后点击「新建对话」跳转新对话页,发送首条消息时才创建;关闭则立即创建空对话</span
|
||||
></span
|
||||
><input
|
||||
type="checkbox"
|
||||
:checked="form.new_chat_button_behavior === 'route'"
|
||||
@change="
|
||||
personalization.updateField({
|
||||
key: 'new_chat_button_behavior',
|
||||
value: $event.target.checked ? 'route' : 'blank'
|
||||
})
|
||||
" /><span class="fancy-check" aria-hidden="true"
|
||||
><svg viewBox="0 0 64 64">
|
||||
<path
|
||||
d="M 0 16 V 56 A 8 8 90 0 0 8 64 H 56 A 8 8 90 0 0 64 56 V 8 A 8 8 90 0 0 56 0 H 8 A 8 8 90 0 0 0 8 V 16 L 32 48 L 64 16 V 8 A 8 8 90 0 0 56 0 H 8 A 8 8 90 0 0 0 8 V 56 A 8 8 90 0 0 8 64 H 56 A 8 8 90 0 0 64 56 V 16"
|
||||
pathLength="575.0541381835938"
|
||||
class="fancy-path"
|
||||
></path></svg></span
|
||||
></label>
|
||||
<label class="settings-toggle-row"
|
||||
><span class="settings-row-copy"
|
||||
><span class="settings-row-title">使用自定义称呼</span
|
||||
@ -915,28 +922,6 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<label class="settings-toggle-row"
|
||||
><span class="settings-row-copy"
|
||||
><span class="settings-row-title">允许根目录创建文件</span
|
||||
><span class="settings-row-desc"
|
||||
>关闭则禁止在工作区根目录创建文件</span
|
||||
></span
|
||||
><input
|
||||
type="checkbox"
|
||||
:checked="form.allow_root_file_creation"
|
||||
@change="
|
||||
personalization.updateField({
|
||||
key: 'allow_root_file_creation',
|
||||
value: $event.target.checked
|
||||
})
|
||||
" /><span class="fancy-check" aria-hidden="true"
|
||||
><svg viewBox="0 0 64 64">
|
||||
<path
|
||||
d="M 0 16 V 56 A 8 8 90 0 0 8 64 H 56 A 8 8 90 0 0 64 56 V 8 A 8 8 90 0 0 56 0 H 8 A 8 8 90 0 0 0 8 V 16 L 32 48 L 64 16 V 8 A 8 8 90 0 0 56 0 H 8 A 8 8 90 0 0 0 8 V 56 A 8 8 90 0 0 8 64 H 56 A 8 8 90 0 0 64 56 V 16"
|
||||
pathLength="575.0541381835938"
|
||||
class="fancy-path"
|
||||
></path></svg></span
|
||||
></label>
|
||||
<label class="settings-toggle-row"
|
||||
><span class="settings-row-copy"
|
||||
><span class="settings-row-title">AGENTS.md 自动注入</span
|
||||
@ -1711,20 +1696,6 @@
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="settings-action-row" style="margin-top: 8px">
|
||||
<span class="settings-row-copy">
|
||||
<span class="settings-row-desc" style="font-size: 12px"
|
||||
>点击麦克风闪退?先复现一次,再点「上传日志」</span
|
||||
>
|
||||
</span>
|
||||
<button
|
||||
type="button"
|
||||
class="settings-secondary-button"
|
||||
@click="saveDebugLog"
|
||||
>
|
||||
上传日志
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
v-if="voiceDownloading"
|
||||
class="voice-download-bar"
|
||||
@ -2259,29 +2230,6 @@ const deleteVoiceModel = () => {
|
||||
voiceDownloadMsg.value = '';
|
||||
};
|
||||
|
||||
const saveDebugLog = async () => {
|
||||
const bridge = (window as any)?.AndroidVoiceBridge;
|
||||
if (!bridge) return;
|
||||
let log = '';
|
||||
if (typeof bridge.collectDebugLog === 'function') {
|
||||
log = bridge.collectDebugLog();
|
||||
}
|
||||
if (!log) {
|
||||
alert('无法收集日志');
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const res = await fetch('/api/voice_debug', { method: 'POST', body: log });
|
||||
if (res.ok) {
|
||||
alert('日志已上传到服务器');
|
||||
} else {
|
||||
alert('上传失败: ' + res.status);
|
||||
}
|
||||
} catch (e: any) {
|
||||
alert('上传失败: ' + (e.message || e));
|
||||
}
|
||||
};
|
||||
|
||||
// 打开个人空间时检查模型状态
|
||||
checkVoiceModel();
|
||||
|
||||
@ -2721,86 +2669,6 @@ const downloadLatestApp = () => {
|
||||
window.location.href = url;
|
||||
};
|
||||
|
||||
const uploadDiagnosticLog = async () => {
|
||||
try {
|
||||
const w = window as any;
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
const screenInfo =
|
||||
typeof window !== 'undefined'
|
||||
? {
|
||||
width: window.innerWidth,
|
||||
height: window.innerHeight,
|
||||
dpr: window.devicePixelRatio
|
||||
}
|
||||
: null;
|
||||
const bridgeProbe = (name: string) => {
|
||||
const b = w?.[name];
|
||||
if (!b) return { exists: false };
|
||||
const methods: Record<string, any> = {};
|
||||
[
|
||||
'getAppVersionCode',
|
||||
'getAppVersionName',
|
||||
'onThemeChanged',
|
||||
'previewPdf',
|
||||
'isPdfPreviewSupported',
|
||||
'downloadFile',
|
||||
'isSupported',
|
||||
'isModelReady',
|
||||
'isModelPartial',
|
||||
'startRecording',
|
||||
'stopRecording'
|
||||
].forEach((m) => {
|
||||
try {
|
||||
methods[m] = typeof b[m] === 'function';
|
||||
} catch {
|
||||
methods[m] = 'error';
|
||||
}
|
||||
});
|
||||
let versionCode = '';
|
||||
let versionName = '';
|
||||
try {
|
||||
versionCode =
|
||||
typeof b.getAppVersionCode === 'function' ? String(b.getAppVersionCode() || '') : '';
|
||||
versionName =
|
||||
typeof b.getAppVersionName === 'function' ? String(b.getAppVersionName() || '') : '';
|
||||
} catch {}
|
||||
return { exists: true, methods, versionCode, versionName };
|
||||
};
|
||||
const payload = {
|
||||
source: 'diagnostic_log',
|
||||
url: window.location.href,
|
||||
userAgent: navigator.userAgent,
|
||||
screen: screenInfo,
|
||||
urlParams: {
|
||||
app_vc: params.get('app_vc') || '',
|
||||
app_vn: params.get('app_vn') || '',
|
||||
app_shell: params.get('app_shell') || ''
|
||||
},
|
||||
bridges: {
|
||||
AndroidThemeBridge: bridgeProbe('AndroidThemeBridge'),
|
||||
AndroidPdfBridge: bridgeProbe('AndroidPdfBridge'),
|
||||
AndroidDownloadBridge: bridgeProbe('AndroidDownloadBridge'),
|
||||
AndroidVoiceBridge: bridgeProbe('AndroidVoiceBridge')
|
||||
},
|
||||
appCurrentVersionCode: appCurrentVersionCode.value,
|
||||
appCurrentVersionName: appCurrentVersionName.value,
|
||||
appUpdateInfo: appUpdateInfo.value,
|
||||
appUpdateError: appUpdateError.value,
|
||||
appUpdateCheckedAt: appUpdateCheckedAt.value,
|
||||
timestamp: new Date().toISOString()
|
||||
};
|
||||
const resp = await fetch('/api/client_debug_log', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(payload)
|
||||
});
|
||||
if (!resp.ok) throw new Error(`status ${resp.status}`);
|
||||
alert('诊断日志已上传');
|
||||
} catch (e: any) {
|
||||
alert('上传诊断日志失败:' + (e?.message || String(e)));
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(async () => {
|
||||
hydrateAppVersionFromBridge();
|
||||
try {
|
||||
|
||||
@ -60,7 +60,7 @@ interface PersonalForm {
|
||||
deep_compress_trigger_tokens: number | null;
|
||||
deep_compress_form: 'file' | 'inject';
|
||||
agents_md_auto_inject: boolean;
|
||||
allow_root_file_creation: boolean;
|
||||
new_chat_button_behavior: 'route' | 'blank';
|
||||
group_sidebar_by_workspace: boolean;
|
||||
sidebar_pinned_workspaces: string[];
|
||||
sidebar_workspace_order: string[];
|
||||
@ -212,7 +212,7 @@ const defaultForm = (): PersonalForm => ({
|
||||
default_model: null,
|
||||
image_compression: 'original',
|
||||
auto_shallow_compress_enabled: false,
|
||||
auto_deep_compress_enabled: false,
|
||||
auto_deep_compress_enabled: true,
|
||||
shallow_compress_trigger_tokens: null,
|
||||
shallow_compress_keep_recent_tools: null,
|
||||
shallow_compress_keep_user_turn_tools: null,
|
||||
@ -221,7 +221,7 @@ const defaultForm = (): PersonalForm => ({
|
||||
deep_compress_trigger_tokens: null,
|
||||
deep_compress_form: 'file',
|
||||
agents_md_auto_inject: false,
|
||||
allow_root_file_creation: false,
|
||||
new_chat_button_behavior: 'route',
|
||||
group_sidebar_by_workspace: false,
|
||||
sidebar_pinned_workspaces: [],
|
||||
sidebar_workspace_order: [],
|
||||
@ -460,7 +460,7 @@ export const usePersonalizationStore = defineStore('personalization', {
|
||||
),
|
||||
deep_compress_form: data.deep_compress_form === 'inject' ? 'inject' : 'file',
|
||||
agents_md_auto_inject: !!data.agents_md_auto_inject,
|
||||
allow_root_file_creation: !!data.allow_root_file_creation,
|
||||
new_chat_button_behavior: data.new_chat_button_behavior === 'blank' ? 'blank' : 'route',
|
||||
group_sidebar_by_workspace: !!data.group_sidebar_by_workspace,
|
||||
sidebar_pinned_workspaces: Array.isArray(data.sidebar_pinned_workspaces)
|
||||
? data.sidebar_pinned_workspaces.filter((item: unknown) => typeof item === 'string')
|
||||
|
||||
Loading…
Reference in New Issue
Block a user