Restore project workspace state
This commit is contained in:
@@ -96,6 +96,9 @@ value ShellVisibility {
|
||||
sidebar_visible: Boolean
|
||||
panel_visible: Boolean
|
||||
assistant_sidebar_visible: Boolean
|
||||
-- Existing shell fields are stored per project with the tab session;
|
||||
-- see tabs.allium ProjectUiSession. RuDS currently has no assistant sidebar,
|
||||
-- so only the sidebar and bottom-panel fields participate there.
|
||||
}
|
||||
|
||||
surface ShellVisibilitySurface {
|
||||
|
||||
@@ -49,7 +49,7 @@ entity Project {
|
||||
-- the SQLite database, the per-project embeddings index
|
||||
-- (projects/{id}/embeddings.usearch + .meta.json sidecar), the
|
||||
-- downloaded embedding-model cache, the project registry, and
|
||||
-- window/UI state.
|
||||
-- window/UI state (project:{id}:ui_state in the SQLite settings table).
|
||||
-- OS-specific base (resolved via :filename.basedir, app name "bds"):
|
||||
-- macOS: ~/Library/Application Support/bds (:user_config)
|
||||
-- Linux: $XDG_CONFIG_HOME/bds (default ~/.config/bds)
|
||||
|
||||
@@ -206,7 +206,9 @@ entity PostMediaLink {
|
||||
|
||||
entity Setting {
|
||||
key: String -- Primary key
|
||||
-- Per-project UI sessions use project:{id}:ui_state.
|
||||
value: String -- Serialized value
|
||||
-- UI session values are version-tolerant JSON.
|
||||
updated_at: Timestamp
|
||||
}
|
||||
|
||||
|
||||
@@ -25,6 +25,9 @@ surface TabRuntimeSurface {
|
||||
provides:
|
||||
TabOpening(tab_type, intent)
|
||||
ActiveTabChanged(active_tab)
|
||||
ProjectUiStateChanged(project_id)
|
||||
ProjectActivated(project_id)
|
||||
ApplicationClosing(project_id)
|
||||
}
|
||||
|
||||
-- ─── Tab types ────────────────────────────────────────────────
|
||||
@@ -247,5 +250,74 @@ invariant DirtyIndicator {
|
||||
|
||||
-- ─── Tab state persistence ───────────────────────────────────
|
||||
|
||||
-- Tab state (list + activeTabId) can be serialized/restored
|
||||
-- for session continuity across project switches.
|
||||
value ProjectUiSession {
|
||||
project_id: String
|
||||
active_view: String
|
||||
sidebar_visible: Boolean
|
||||
sidebar_width: Integer
|
||||
panel_visible: Boolean
|
||||
panel_tab: String
|
||||
tabs: List<Tab>
|
||||
active_tab_id: String?
|
||||
}
|
||||
|
||||
surface ProjectUiSessionSurface {
|
||||
context session: ProjectUiSession
|
||||
|
||||
exposes:
|
||||
session.project_id
|
||||
session.active_view
|
||||
session.sidebar_visible
|
||||
session.sidebar_width
|
||||
session.panel_visible
|
||||
session.panel_tab
|
||||
session.tabs
|
||||
session.active_tab_id when session.active_tab_id != null
|
||||
}
|
||||
|
||||
rule PersistProjectUiSession {
|
||||
when: ProjectUiStateChanged(project_id)
|
||||
let session = current_project_ui_session(project_id)
|
||||
ensures: DatabaseSettingWritten(
|
||||
key: format("project:{project_id}:ui_state", project_id: project_id),
|
||||
value: serialize_json(session)
|
||||
)
|
||||
-- Persist after every shell/tab mutation, so the database already
|
||||
-- represents the last visible state if the process is closed.
|
||||
}
|
||||
|
||||
rule PersistProjectUiSessionOnClose {
|
||||
when: ApplicationClosing(project_id)
|
||||
let session = current_project_ui_session(project_id)
|
||||
ensures: DatabaseProjectUiSession(project_id) = session
|
||||
}
|
||||
|
||||
rule RestoreProjectUiSession {
|
||||
when: ProjectActivated(project_id)
|
||||
let session = DatabaseProjectUiSession(project_id)
|
||||
if session != null:
|
||||
ensures: active_view = valid_sidebar_view_or_default(session.active_view, posts)
|
||||
ensures: sidebar_visible = session.sidebar_visible
|
||||
ensures: sidebar_width = clamp(session.sidebar_width, 200, 500)
|
||||
ensures: panel_visible = session.panel_visible
|
||||
ensures: panel_tab = valid_panel_tab_or_default(session.panel_tab, tasks)
|
||||
ensures: tabs = existing_project_tabs(session.tabs, project_id)
|
||||
-- Stale, unknown, and cross-project entity references are discarded.
|
||||
ensures: active_tab = valid_restored_active_tab_or_null(session.active_tab_id, tabs)
|
||||
ensures: EditorsHydratedFromCurrentRecords(tabs, project_id)
|
||||
-- Titles and editor content are resolved afresh; unsaved editor bodies
|
||||
-- are never serialized as UI session state.
|
||||
else:
|
||||
ensures: active_view = posts
|
||||
ensures: sidebar_visible = true
|
||||
ensures: sidebar_width = 280
|
||||
ensures: panel_visible = false
|
||||
ensures: panel_tab = tasks
|
||||
ensures: tabs = empty
|
||||
ensures: active_tab = null
|
||||
}
|
||||
|
||||
invariant ProjectUiSessionIsolation {
|
||||
-- Each project has an independent database session. Activating one project
|
||||
-- never retains tabs, editor state, or shell state from another project.
|
||||
}
|
||||
|
||||
@@ -18,10 +18,10 @@ entity TuiState {
|
||||
config {
|
||||
-- Same preference sections as the settings backends; matches the
|
||||
-- GUI settings nav minus the GUI-only Style tab.
|
||||
tui_settings_sections: List<String> = [
|
||||
tui_settings_sections: Set<String> = {
|
||||
"project", "editor", "content", "ai",
|
||||
"technology", "publishing", "data", "mcp"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
surface TuiSurface {
|
||||
|
||||
Reference in New Issue
Block a user