Skip to repository content146 lines · 4.8 KB · rust
tenant.openagents/omega
No repository description is available.
OpenAgents Git authority 2026-07-28T02:34:48.021Z Public web read
NIP-34 coordinate
30617:7649603503856e5148d571eac2766b288a8ff1e9e35d380337a1d2b0015b4f92:omegaMaintainersHidden in public view
References2 branches · 1 tag
Read-only clone
git clone https://openagents.com/git/tenant.openagents/omega.gitBrowse files
persistence.rs
1use anyhow::{Context, anyhow};
2use db::kvp::KeyValueStore;
3use gpui::App;
4use ui::Window;
5
6use crate::preview;
7use crate::shape::{self, Centered, RelativeHeight, RelativeWidth, Shape, ViewportFraction};
8
9const PICKERS_NAMESPACE: &str = "pickers_v2";
10
11pub(crate) fn store_shape_for_this_layout(
12 picker_delegate: &'static str,
13 preview_layout: Option<preview::Layout>,
14 shape: shape::Centered,
15 window: &ui::Window,
16 cx: &App,
17) {
18 let shape = PickerConfig::from_centered(shape, window);
19
20 let kvp = KeyValueStore::global(cx);
21 db::write_and_log(cx, async move || {
22 kvp.scoped(PICKERS_NAMESPACE)
23 .write(
24 shape_key(picker_delegate, preview_layout),
25 serde_json::to_string(&shape).context("Could not serialize size of picker")?,
26 )
27 .await?;
28 // Must be written after the shape or loading can break
29 // if this future is ever cancelled.
30 kvp.scoped(PICKERS_NAMESPACE)
31 .write(
32 last_layout_key(picker_delegate),
33 layout_as_str(preview_layout).to_string(),
34 )
35 .await
36 });
37}
38
39/// Persist the last-used preview layout without touching the stored shape.
40///
41/// Changing the preview position (e.g. right/below/hidden) without resizing
42/// must still be remembered, so this is called whenever the layout changes.
43pub(crate) fn store_last_layout(
44 picker_delegate: &'static str,
45 preview_layout: Option<preview::Layout>,
46 cx: &App,
47) {
48 let kvp = KeyValueStore::global(cx);
49 db::write_and_log(cx, async move || {
50 kvp.scoped(PICKERS_NAMESPACE)
51 .write(
52 last_layout_key(picker_delegate),
53 layout_as_str(preview_layout).to_string(),
54 )
55 .await
56 });
57}
58
59pub(crate) fn try_load_shape(
60 picker_delegate: &'static str,
61 preview_layout: impl Into<Option<preview::Layout>>,
62 cx: &App,
63) -> anyhow::Result<Option<Shape>> {
64 let Some(shape) = KeyValueStore::global(cx)
65 .scoped(PICKERS_NAMESPACE)
66 .read(&shape_key(picker_delegate, preview_layout.into()))
67 .context("Could not read picker shape from KeyValueStore")?
68 else {
69 return Ok(None);
70 };
71
72 let shape = serde_json::from_str::<PickerConfig>(&shape)
73 .context("Could not deserialize loaded picker shape from persistence")?
74 .into_centered();
75 Ok(Some(Shape::HorizontallyCentered(shape)))
76}
77
78pub(crate) fn load_last_preview_layout(
79 picker_delegate: &'static str,
80 cx: &App,
81) -> anyhow::Result<Option<preview::Layout>> {
82 let Some(last_layout) = KeyValueStore::global(cx)
83 .scoped(PICKERS_NAMESPACE)
84 .read(&last_layout_key(picker_delegate))
85 .context("Could not read last picker layout from KeyValueStore")?
86 else {
87 return Ok(None);
88 };
89
90 parse_layout(&last_layout)
91}
92
93fn shape_key(picker_delegate: &'static str, preview_layout: Option<preview::Layout>) -> String {
94 format!("{picker_delegate}/{}", layout_as_str(preview_layout))
95}
96
97fn last_layout_key(picker_delegate: &'static str) -> String {
98 format!("{picker_delegate}/LAST_PREVIEW_LAYOUT")
99}
100
101fn layout_as_str(layout: Option<preview::Layout>) -> &'static str {
102 match layout {
103 Some(preview::Layout::Hidden) => "hidden",
104 Some(preview::Layout::Below) => "below",
105 Some(preview::Layout::Right) => "right",
106 None => "none",
107 }
108}
109
110fn parse_layout(s: &str) -> anyhow::Result<Option<preview::Layout>> {
111 Ok(Some(match s {
112 "hidden" => preview::Layout::Hidden,
113 "below" => preview::Layout::Below,
114 "right" => preview::Layout::Right,
115 "none" => return Ok(None),
116 _ => return Err(anyhow!("Unknown layout: `{}`", s)),
117 }))
118}
119
120/// A resized picker for persisting its size. All values are stored as fractions
121/// of the viewport so they remain meaningful across window sizes.
122#[derive(Clone, Copy, serde::Serialize, serde::Deserialize)]
123pub(crate) struct PickerConfig {
124 width: f32, // relative fraction of viewport
125 height: f32, // relative fraction of viewport
126 preview_size: f32, // relative fraction of viewport
127}
128
129impl PickerConfig {
130 pub(crate) fn from_centered(shape: Centered, window: &Window) -> Self {
131 PickerConfig {
132 width: shape.width.as_viewport_fraction(window).raw(),
133 height: shape.height.as_viewport_fraction(window).raw(),
134 preview_size: shape.preview_size.raw(),
135 }
136 }
137
138 pub(crate) fn into_centered(self) -> Centered {
139 Centered {
140 width: RelativeWidth::viewport(self.width.clamp(0.0, 1.0)),
141 height: RelativeHeight::viewport(self.height.clamp(0.0, 1.0)),
142 preview_size: ViewportFraction::fraction(self.preview_size.clamp(0.0, 1.0)),
143 }
144 }
145}
146