Files
tutti/services/tuttid/data/workspace/sqlite_preferences_write.go
agent-radar 2ed3415fb7
Publish Claude Code Binaries / Stage and publish binaries (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-arm64 (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build linux-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build windows-amd64 (push) Canceled after 0s
Sync Tutti Agent Skills / sync (push) Canceled after 0s
Windows Agent Adapters / Test Windows agent adapters (push) Canceled after 0s
Windows Daemon Adapters / Test Windows daemon adapters (push) Canceled after 0s
Publish Tutti App Runtime / Publish runtime catalog (push) Canceled after 0s
源码快照 @ 2026-09-01 (source-only, 剔除非源码素材)
2026-09-01 20:17:27 +08:00

223 lines
8.8 KiB
Go

package workspace
import (
"context"
"database/sql"
"errors"
"fmt"
"time"
preferencesbiz "github.com/tutti-os/tutti/services/tuttid/biz/preferences"
)
const desktopPreferencesInsertSQL = `
INSERT INTO desktop_preferences (
id,
agent_cli_update_check_enabled,
default_agent_provider,
agent_conversation_detail_mode,
agent_dock_layout,
dock_icon_style,
dock_placement,
deleted_agent_conversation_retention_days,
locale,
theme_source,
sleep_prevention_mode,
update_channel,
update_policy,
agent_composer_defaults_by_provider_json,
agent_composer_defaults_by_agent_target_json,
agent_gui_conversation_rail_collapsed_by_provider_json,
agent_session_launch_modes_by_workspace_json,
file_default_openers_by_extension_json,
app_catalog_channel,
browser_use_connection_mode,
minimize_animation,
show_app_developer_sources,
workbench_window_snapping_enabled,
workbench_window_snapping_shortcut_preset,
feature_flags_json,
workbench_shortcuts_json,
updated_at_unix_ms
)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
`
const desktopPreferencesReplaceOnConflictSQL = `
ON CONFLICT(id) DO UPDATE SET
agent_cli_update_check_enabled = excluded.agent_cli_update_check_enabled,
default_agent_provider = excluded.default_agent_provider,
agent_conversation_detail_mode = excluded.agent_conversation_detail_mode,
agent_dock_layout = excluded.agent_dock_layout,
dock_icon_style = excluded.dock_icon_style,
dock_placement = excluded.dock_placement,
deleted_agent_conversation_retention_days = excluded.deleted_agent_conversation_retention_days,
locale = excluded.locale,
theme_source = excluded.theme_source,
sleep_prevention_mode = excluded.sleep_prevention_mode,
update_channel = excluded.update_channel,
update_policy = excluded.update_policy,
agent_composer_defaults_by_provider_json = excluded.agent_composer_defaults_by_provider_json,
agent_gui_conversation_rail_collapsed_by_provider_json = excluded.agent_gui_conversation_rail_collapsed_by_provider_json,
file_default_openers_by_extension_json = excluded.file_default_openers_by_extension_json,
app_catalog_channel = excluded.app_catalog_channel,
browser_use_connection_mode = excluded.browser_use_connection_mode,
minimize_animation = excluded.minimize_animation,
show_app_developer_sources = excluded.show_app_developer_sources,
workbench_window_snapping_enabled = excluded.workbench_window_snapping_enabled,
workbench_window_snapping_shortcut_preset = excluded.workbench_window_snapping_shortcut_preset,
feature_flags_json = excluded.feature_flags_json,
workbench_shortcuts_json = excluded.workbench_shortcuts_json,
updated_at_unix_ms = excluded.updated_at_unix_ms
`
type desktopPreferencesExecer interface {
ExecContext(context.Context, string, ...any) (sql.Result, error)
}
type encodedDesktopPreferences struct {
agentComposerDefaultsJSON string
agentComposerDefaultsByAgentTargetJSON string
agentGUIConversationRailCollapsedJSON string
agentSessionLaunchModesJSON string
featureFlagsJSON string
fileDefaultOpenersJSON string
workbenchShortcutsJSON string
}
func (s *SQLiteStore) PutDesktopPreferences(ctx context.Context, preferences preferencesbiz.DesktopPreferences) (preferencesbiz.DesktopPreferences, error) {
if s == nil || s.writeDB == nil {
return preferencesbiz.DesktopPreferences{}, errors.New("workspace database is not initialized")
}
if _, err := writeDesktopPreferences(ctx, s.writeDB, preferences, desktopPreferencesReplaceOnConflictSQL); err != nil {
return preferencesbiz.DesktopPreferences{}, fmt.Errorf("put desktop preferences: %w", err)
}
// Re-read after the write because the dedicated target-defaults or launch-mode
// patch may have committed between a full preferences caller's read and this
// update. The conflict clause deliberately preserves those columns, so
// returning the input object here would publish a stale snapshot.
return s.GetDesktopPreferences(ctx)
}
func (s *SQLiteStore) InitializeDesktopPreferences(
ctx context.Context,
preferences preferencesbiz.DesktopPreferences,
) (preferencesbiz.DesktopPreferences, bool, error) {
if s == nil || s.writeDB == nil {
return preferencesbiz.DesktopPreferences{}, false, errors.New("workspace database is not initialized")
}
tx, err := s.writeDB.BeginTx(ctx, nil)
if err != nil {
return preferencesbiz.DesktopPreferences{}, false, fmt.Errorf("begin desktop preferences initialization: %w", err)
}
defer func() { _ = tx.Rollback() }()
created, err := insertDesktopPreferencesIfAbsent(ctx, tx, preferences)
if err != nil {
return preferencesbiz.DesktopPreferences{}, false, fmt.Errorf("initialize desktop preferences: %w", err)
}
if err := tx.Commit(); err != nil {
return preferencesbiz.DesktopPreferences{}, false, fmt.Errorf("commit desktop preferences initialization: %w", err)
}
stored, err := s.GetDesktopPreferences(ctx)
if err != nil {
return preferencesbiz.DesktopPreferences{}, false, fmt.Errorf("read initialized desktop preferences: %w", err)
}
return stored, created, nil
}
func insertDesktopPreferencesIfAbsent(
ctx context.Context,
execer desktopPreferencesExecer,
preferences preferencesbiz.DesktopPreferences,
) (bool, error) {
result, err := writeDesktopPreferences(ctx, execer, preferences, "ON CONFLICT(id) DO NOTHING")
if err != nil {
return false, err
}
rows, err := result.RowsAffected()
if err != nil {
return false, fmt.Errorf("count initialized desktop preferences rows: %w", err)
}
return rows == 1, nil
}
func writeDesktopPreferences(
ctx context.Context,
execer desktopPreferencesExecer,
preferences preferencesbiz.DesktopPreferences,
conflictSQL string,
) (sql.Result, error) {
encoded, err := encodeDesktopPreferences(preferences)
if err != nil {
return nil, err
}
return execer.ExecContext(
ctx,
desktopPreferencesInsertSQL+conflictSQL,
desktopPreferencesRowID,
preferences.AgentCLIUpdateCheckEnabled,
preferences.DefaultAgentProvider,
preferencesbiz.NormalizeDesktopAgentConversationDetailMode(preferences.AgentConversationDetailMode),
preferencesbiz.NormalizeDesktopAgentDockLayout(preferences.AgentDockLayout),
preferences.DockIconStyle,
preferences.DockPlacement,
preferencesbiz.NormalizeDeletedAgentConversationRetentionDays(preferences.DeletedAgentConversationRetentionDays),
preferences.Locale,
preferences.ThemeSource,
preferences.SleepPreventionMode,
preferences.UpdateChannel,
preferences.UpdatePolicy,
encoded.agentComposerDefaultsJSON,
encoded.agentComposerDefaultsByAgentTargetJSON,
encoded.agentGUIConversationRailCollapsedJSON,
encoded.agentSessionLaunchModesJSON,
encoded.fileDefaultOpenersJSON,
preferences.AppCatalogChannel,
preferences.BrowserUseConnectionMode,
preferences.MinimizeAnimation,
preferences.ShowAppDeveloperSources,
preferences.WindowSnappingEnabled,
preferences.WindowSnappingShortcutPreset,
encoded.featureFlagsJSON,
encoded.workbenchShortcutsJSON,
unixMs(time.Now().UTC()),
)
}
func encodeDesktopPreferences(preferences preferencesbiz.DesktopPreferences) (encodedDesktopPreferences, error) {
var encoded encodedDesktopPreferences
var err error
encoded.agentComposerDefaultsJSON, err = encodeAgentComposerDefaultsByProvider(preferences.AgentComposerDefaultsByProvider)
if err != nil {
return encoded, fmt.Errorf("encode desktop preferences agent composer defaults: %w", err)
}
encoded.agentComposerDefaultsByAgentTargetJSON, err = encodeAgentComposerDefaultsByProvider(preferences.AgentComposerDefaultsByAgentTarget)
if err != nil {
return encoded, fmt.Errorf("encode desktop preferences agent composer defaults by agent target: %w", err)
}
encoded.agentGUIConversationRailCollapsedJSON, err = encodeAgentGUIConversationRailCollapsedByProvider(preferences.AgentGUIConversationRailCollapsedByProvider)
if err != nil {
return encoded, fmt.Errorf("encode desktop preferences agent gui conversation rail: %w", err)
}
encoded.agentSessionLaunchModesJSON, err = encodeAgentSessionLaunchModesByWorkspace(preferences.AgentSessionLaunchModesByWorkspace)
if err != nil {
return encoded, fmt.Errorf("encode desktop preferences Agent Session launch modes: %w", err)
}
encoded.fileDefaultOpenersJSON, err = encodeFileDefaultOpenersByExtension(preferences.FileDefaultOpenersByExtension)
if err != nil {
return encoded, fmt.Errorf("encode desktop preferences file default openers: %w", err)
}
encoded.featureFlagsJSON, err = encodeFeatureFlags(preferences.FeatureFlags)
if err != nil {
return encoded, fmt.Errorf("encode desktop preferences feature flags: %w", err)
}
encoded.workbenchShortcutsJSON, err = encodeWorkbenchShortcuts(preferences.WorkbenchShortcuts)
if err != nil {
return encoded, fmt.Errorf("encode desktop preferences workbench shortcuts: %w", err)
}
return encoded, nil
}