Install with Codex or Claude Copy this prompt, paste it into Codex, Claude, or another assistant, and let it review the skill page and install it for you.
A direct command skips the review prompt. Inspect the source before running it.
Deep layout system implementation knowledge: ILayoutStorage contracts, IndexedDB schema, sync operation computation, mutex-locked LayoutManager, conflict resolution, WriteThroughLayoutCache, NamespacedLayoutStorage, and CurrentLayoutProvider reducers.
Layouts Internals Skill
ILayoutStorage Interface
Defined in packages/suite-base/src/services/ILayoutStorage.ts. Every CRUD method takes a
namespace argument — there are no sync-time methods like getLastSyncTime.
// Optional one-time migration of pre-namespace local layouts
namespace
string
Promise
void
// Convert local layouts to personal layouts on login
importLayouts
params
fromNamespace
string
toNamespace
string
Promise
void
The Layout type tracks baseline (last explicit save), working (unsaved edits, or undefined),
and syncInfo (remote status). LayoutSyncStatus is
"new" | "updated" | "tracked" | "locally-deleted" | "remotely-deleted".
Every read passes the record through migrateLayout() before returning
The stored value wraps the layout: { namespace, layout } — the primary key reaches into
layout.id via the "layout.id" keyPath segment
NamespacedLayoutStorage
packages/suite-base/src/services/LayoutManager/NamespacedLayoutStorage.ts — wraps an
ILayoutStorage and binds a namespace so callers omit it. It is not itself an ILayoutStorage
(its methods drop the namespace argument). The constructor kicks off an async migration/import:
packages/suite-base/src/services/LayoutManager/WriteThroughLayoutCache.ts — an ILayoutStorage
that calls the underlying list() once per namespace (via LazilyInitialized) and serves
subsequent reads from an in-memory Map, writing through to the inner storage on put/delete.
Assumes nothing else mutates the underlying storage.
All local storage access is wrapped in a MutexLocked (from @lichtblick/den/async) so multi-step
operations are atomic. A single in-flight sync is tracked by currentSync?: Promise<void>.
⚠️ LayoutManager does not implement exponential backoff / jitter / #baseInterval /
#maxInterval. Sync scheduling (and any retry/online-trigger behavior) lives in the provider
layer, not in LayoutManager. Do not assume a built-in backoff timer here.
computeLayoutSyncOperations() (Detail)
packages/suite-base/src/services/LayoutManager/utils/computeLayoutSyncOperations.ts. The real
SyncOperation is a tagged union carrying a local boolean and the operation type:
⚠️ There is no "upload" | "download" | "conflict" union, no layoutId field, and no
syncStatus/baseline.savedAt comparison as shown previously. Operations are keyed by
localLayout / remoteLayout, and the local flag indicates whether the op mutates local
(cache) or remote storage.
The function iterates local layouts (matching against a remoteLayoutsById map) and then any
remaining remote-only layouts, pushing the appropriate operation for each.
CurrentLayoutProvider Reducers
packages/suite-base/src/providers/CurrentLayoutProvider/reducers.ts. The actual action type
values handled are:
⚠️ There are no REMOVE_PANEL, MOVE_PANEL, or UPDATE_PANEL_CONFIG actions. Panel removal is
CLOSE_PANEL; config writes go through SAVE_PANEL_CONFIGS / SAVE_FULL_PANEL_CONFIG.
Panel Tree Operations (examples)
ADD_PANEL — find insertion point → add new leaf to the mosaic tree.
CLOSE_PANEL — remove leaf → if parent collapses to a single child, hoist it.
case"SAVE_PANEL_CONFIGS":
// merges each { id, config } entry into state's configById,// optionally via a per-panel override function
DesktopLayoutLoader
packages/suite-desktop/src/renderer/services/DesktopLayoutLoader.ts — namespace = "local". Reads
layouts from the desktop file system through the preload storageBridge (list / get / put /
delete), not a desktopBridge.fetchLayouts() call.
Common Issues
Sync conflicts: User edits while offline → both sides changed → manual resolution needed
Mutex deadlock: If sync operation throws without releasing → next sync hangs (mitigated by timeout)
IndexedDB quota: Large layouts with many panels → check quota before save
Baseline drift: If baseline gets corrupted → all syncs show as conflicts (reset baseline)