merge: resolve v1.23.0 upstream conflicts, preserve custom git provider config
Resolved conflicts in 8 files by taking upstream refactored code: - desktop.ts: re-export DesktopSettings from registry - openchamberConfig.ts: simplified project setup client - persistence.ts: registry-derived settings, add git provider hydration - search.ts: upstream search entries + git provider entries - useConfigStore.ts: loadDesktopSettings() path - settings-helpers.js: add gitProviderId/gitModelId/gitProviders sanitization - DOCUMENTATION.md: upstream walkthrough docs - vite.config.ts: upstream SW glob patterns Custom fork additions preserved: - gitProviderId, gitModelId, gitProviders fields in settings registry - Git provider domain store hydration in persistence.ts - Git provider search entries in search.ts - Git provider sanitization in settings-helpers.js
This commit is contained in:
@@ -1,5 +1,33 @@
|
||||
import { sanitizeGitProviders } from '../git-providers/config.js';
|
||||
import { createRequire } from 'node:module';
|
||||
|
||||
import { isAgentMemoryFeatureAvailable } from '../agent-memory/feature-flag.js';
|
||||
import { sanitizeGitProviders } from '../git-providers/config.js';
|
||||
|
||||
// Generated from packages/ui/src/lib/settings/registry.ts by
|
||||
// `bun run settings-registry:generate`; `registry.test.ts` fails when stale.
|
||||
// The server is plain ESM without a bundler, so the snapshot is read with
|
||||
// `createRequire` (import attributes differ across the Node versions we run on).
|
||||
const settingsRegistry = createRequire(import.meta.url)('./settings-registry.json');
|
||||
|
||||
/**
|
||||
* Whether a client may persist this key through PUT /api/config/settings:
|
||||
* it must be a registry key, not a server-computed flag, not a device field
|
||||
* that only lives in the browser, and not one the desktop shell writes itself.
|
||||
*/
|
||||
const isPersistableSettingsKey = (key) => {
|
||||
const field = settingsRegistry.fields[key];
|
||||
if (!field) return false;
|
||||
if (field.computed || field.local) return false;
|
||||
if (field.owner === 'desktop-shell') return false;
|
||||
return true;
|
||||
};
|
||||
|
||||
/** Keys accepted on write but never returned by a read. */
|
||||
const SECRET_SETTINGS_KEYS = Object.freeze(
|
||||
Object.entries(settingsRegistry.fields)
|
||||
.filter(([, field]) => field.secret === true)
|
||||
.map(([key]) => key),
|
||||
);
|
||||
import {
|
||||
DEFAULT_INPUT_HISTORY_LIMIT,
|
||||
DEFAULT_INPUT_HISTORY_SCOPE,
|
||||
@@ -19,7 +47,6 @@ export const createSettingsHelpers = (dependencies) => {
|
||||
normalizeManagedRemoteTunnelHostname,
|
||||
normalizeManagedRemoteTunnelPresets,
|
||||
normalizeManagedRemoteTunnelPresetTokens,
|
||||
sanitizeTypographySizesPartial,
|
||||
normalizeStringArray,
|
||||
sanitizeModelRefs,
|
||||
sanitizeSkillCatalogs,
|
||||
@@ -204,6 +231,9 @@ export const createSettingsHelpers = (dependencies) => {
|
||||
...new Set(candidate.workStatusHiddenSections.filter((entry) => typeof entry === 'string' && entry.length > 0)),
|
||||
];
|
||||
}
|
||||
if (typeof candidate.workStatusHiddenSectionsExplicit === 'boolean') {
|
||||
result.workStatusHiddenSectionsExplicit = candidate.workStatusHiddenSectionsExplicit;
|
||||
}
|
||||
if (typeof candidate.desktopLanAccessEnabled === 'boolean') {
|
||||
result.desktopLanAccessEnabled = candidate.desktopLanAccessEnabled;
|
||||
}
|
||||
@@ -314,9 +344,6 @@ export const createSettingsHelpers = (dependencies) => {
|
||||
if (typeof candidate.monoFont === 'string' && candidate.monoFont.length > 0) {
|
||||
result.monoFont = candidate.monoFont;
|
||||
}
|
||||
if (typeof candidate.markdownDisplayMode === 'string' && candidate.markdownDisplayMode.length > 0) {
|
||||
result.markdownDisplayMode = candidate.markdownDisplayMode;
|
||||
}
|
||||
if (typeof candidate.githubClientId === 'string') {
|
||||
const trimmed = candidate.githubClientId.trim();
|
||||
if (trimmed.length > 0) {
|
||||
@@ -332,6 +359,45 @@ export const createSettingsHelpers = (dependencies) => {
|
||||
if (typeof candidate.showReasoningTraces === 'boolean') {
|
||||
result.showReasoningTraces = candidate.showReasoningTraces;
|
||||
}
|
||||
if (typeof candidate.streamingAutoFollowEnabled === 'boolean') {
|
||||
result.streamingAutoFollowEnabled = candidate.streamingAutoFollowEnabled;
|
||||
}
|
||||
if (typeof candidate.codeBlockLineWrap === 'boolean') {
|
||||
result.codeBlockLineWrap = candidate.codeBlockLineWrap;
|
||||
}
|
||||
if (typeof candidate.autoSaveEnabled === 'boolean') {
|
||||
result.autoSaveEnabled = candidate.autoSaveEnabled;
|
||||
}
|
||||
if (typeof candidate.diffWrapLines === 'boolean') {
|
||||
result.diffWrapLines = candidate.diffWrapLines;
|
||||
}
|
||||
if (typeof candidate.persistChatDraft === 'boolean') {
|
||||
result.persistChatDraft = candidate.persistChatDraft;
|
||||
}
|
||||
if (typeof candidate.allowPromptingSubagentSessions === 'boolean') {
|
||||
result.allowPromptingSubagentSessions = candidate.allowPromptingSubagentSessions;
|
||||
}
|
||||
if (typeof candidate.showOpenCodeRestartConfirm === 'boolean') {
|
||||
result.showOpenCodeRestartConfirm = candidate.showOpenCodeRestartConfirm;
|
||||
}
|
||||
if (typeof candidate.sessionTabsEnabled === 'boolean') {
|
||||
result.sessionTabsEnabled = candidate.sessionTabsEnabled;
|
||||
}
|
||||
if (typeof candidate.largeTextPasteBehavior === 'string') {
|
||||
const mode = candidate.largeTextPasteBehavior.trim();
|
||||
if (mode === 'ask' || mode === 'attach' || mode === 'inline') {
|
||||
result.largeTextPasteBehavior = mode;
|
||||
}
|
||||
}
|
||||
if (typeof candidate.fileEditorKeymap === 'string') {
|
||||
const mode = candidate.fileEditorKeymap.trim();
|
||||
if (mode === 'default' || mode === 'vim') {
|
||||
result.fileEditorKeymap = mode;
|
||||
}
|
||||
}
|
||||
if (Array.isArray(candidate.providerOrder)) {
|
||||
result.providerOrder = normalizeStringArray(candidate.providerOrder);
|
||||
}
|
||||
if (typeof candidate.sessionRecapEnabled === 'boolean') {
|
||||
result.sessionRecapEnabled = candidate.sessionRecapEnabled;
|
||||
}
|
||||
@@ -453,11 +519,6 @@ export const createSettingsHelpers = (dependencies) => {
|
||||
result.managedRemoteTunnelSelectedPresetId = id || undefined;
|
||||
}
|
||||
|
||||
const typography = sanitizeTypographySizesPartial(candidate.typographySizes);
|
||||
if (typography) {
|
||||
result.typographySizes = typography;
|
||||
}
|
||||
|
||||
if (typeof candidate.defaultModel === 'string') {
|
||||
const trimmed = candidate.defaultModel.trim();
|
||||
result.defaultModel = trimmed.length > 0 ? trimmed : undefined;
|
||||
@@ -527,12 +588,6 @@ export const createSettingsHelpers = (dependencies) => {
|
||||
result.mobileKeyboardMode = mode;
|
||||
}
|
||||
}
|
||||
if (typeof candidate.toolCallExpansion === 'string') {
|
||||
const mode = candidate.toolCallExpansion.trim();
|
||||
if (mode === 'collapsed' || mode === 'activity' || mode === 'detailed' || mode === 'changes') {
|
||||
result.toolCallExpansion = mode;
|
||||
}
|
||||
}
|
||||
if (typeof candidate.inputSpellcheckEnabled === 'boolean') {
|
||||
result.inputSpellcheckEnabled = candidate.inputSpellcheckEnabled;
|
||||
}
|
||||
@@ -624,9 +679,6 @@ export const createSettingsHelpers = (dependencies) => {
|
||||
if (typeof candidate.promptNavigatorEnabled === 'boolean') {
|
||||
result.promptNavigatorEnabled = candidate.promptNavigatorEnabled;
|
||||
}
|
||||
if (typeof candidate.expandedEditorToolbar === 'boolean') {
|
||||
result.expandedEditorToolbar = candidate.expandedEditorToolbar;
|
||||
}
|
||||
if (typeof candidate.wideChatLayoutEnabled === 'boolean') {
|
||||
result.wideChatLayoutEnabled = candidate.wideChatLayoutEnabled;
|
||||
}
|
||||
@@ -729,11 +781,6 @@ export const createSettingsHelpers = (dependencies) => {
|
||||
}
|
||||
}
|
||||
|
||||
// Message limit — single setting for fetch / trim / Load More chunk
|
||||
if (typeof candidate.messageLimit === 'number' && Number.isFinite(candidate.messageLimit)) {
|
||||
result.messageLimit = Math.max(10, Math.min(500, Math.round(candidate.messageLimit)));
|
||||
}
|
||||
|
||||
const skillCatalogs = sanitizeSkillCatalogs(candidate.skillCatalogs);
|
||||
if (skillCatalogs) {
|
||||
result.skillCatalogs = skillCatalogs;
|
||||
@@ -917,6 +964,16 @@ export const createSettingsHelpers = (dependencies) => {
|
||||
}
|
||||
}
|
||||
|
||||
// The registry is the last word on what a client may persist: a key the
|
||||
// code above still names but the registry no longer lists is dropped here,
|
||||
// so the two cannot drift apart silently (settings-helpers.test.js checks
|
||||
// the other direction).
|
||||
for (const key of Object.keys(result)) {
|
||||
if (!isPersistableSettingsKey(key)) {
|
||||
delete result[key];
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
@@ -927,13 +984,6 @@ export const createSettingsHelpers = (dependencies) => {
|
||||
? current.securityScopedBookmarks
|
||||
: [];
|
||||
|
||||
const nextTypographySizes = changes.typographySizes
|
||||
? {
|
||||
...(current.typographySizes || {}),
|
||||
...changes.typographySizes
|
||||
}
|
||||
: current.typographySizes;
|
||||
|
||||
const next = {
|
||||
...current,
|
||||
...changes,
|
||||
@@ -942,7 +992,6 @@ export const createSettingsHelpers = (dependencies) => {
|
||||
baseBookmarks.filter((entry) => typeof entry === 'string' && entry.length > 0)
|
||||
)
|
||||
),
|
||||
typographySizes: nextTypographySizes
|
||||
};
|
||||
|
||||
return next;
|
||||
@@ -950,9 +999,12 @@ export const createSettingsHelpers = (dependencies) => {
|
||||
|
||||
const formatSettingsResponse = (settings) => {
|
||||
const sanitized = sanitizeSettingsUpdate(settings);
|
||||
delete sanitized.managedRemoteTunnelToken;
|
||||
for (const key of SECRET_SETTINGS_KEYS) {
|
||||
delete sanitized[key];
|
||||
}
|
||||
const bookmarks = normalizeStringArray(settings.securityScopedBookmarks);
|
||||
const hasManagedRemoteTunnelToken = typeof settings?.managedRemoteTunnelToken === 'string' && settings.managedRemoteTunnelToken.trim().length > 0;
|
||||
const hasDesktopUiPassword = typeof settings?.desktopUiPassword === 'string' && settings.desktopUiPassword.trim().length > 0;
|
||||
const pwaAppName = normalizePwaAppName(settings?.pwaAppName, '');
|
||||
const pwaOrientation = normalizePwaOrientation(settings?.pwaOrientation, 'system');
|
||||
const mobileKeyboardMode = normalizeMobileKeyboardMode(settings?.mobileKeyboardMode, 'native');
|
||||
@@ -962,6 +1014,7 @@ export const createSettingsHelpers = (dependencies) => {
|
||||
return {
|
||||
...sanitized,
|
||||
hasManagedRemoteTunnelToken,
|
||||
hasDesktopUiPassword,
|
||||
// Tells the client whether agent memory exists in this build at all, so
|
||||
// its settings row and panel tab can be absent rather than merely off.
|
||||
agentMemoryFeatureAvailable: isAgentMemoryFeatureAvailable(),
|
||||
@@ -972,7 +1025,6 @@ export const createSettingsHelpers = (dependencies) => {
|
||||
inputHistoryLimit,
|
||||
securityScopedBookmarks: bookmarks,
|
||||
pinnedDirectories: normalizeStringArray(settings.pinnedDirectories),
|
||||
typographySizes: sanitizeTypographySizesPartial(settings.typographySizes),
|
||||
...(process.env.OPENCHAMBER_RUNTIME === 'desktop'
|
||||
? {
|
||||
desktopLanAccessActive: process.env.OPENCHAMBER_DESKTOP_LAN_ACCESS_ACTIVE === 'true',
|
||||
|
||||
Reference in New Issue
Block a user