gloomberb 0.11.1 → 0.11.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (233) hide show
  1. package/README.md +10 -9
  2. package/README.zh-CN.md +6 -6
  3. package/package.json +1 -1
  4. package/src/api-client/data.ts +7 -0
  5. package/src/api-client/index.test.ts +47 -0
  6. package/src/api-client/index.ts +61 -0
  7. package/src/api-client/paths.ts +16 -0
  8. package/src/api-client/types.ts +35 -0
  9. package/src/app/pane-runtime/index.tsx +4 -1
  10. package/src/app/pane-runtime/plugin-bindings.test.ts +117 -0
  11. package/src/app/pane-runtime/plugin-bindings.ts +25 -4
  12. package/src/app/pane-runtime/ticker-inspector-runtime.ts +25 -14
  13. package/src/app/runtime/desktop-deeplink.test.ts +16 -0
  14. package/src/app/runtime/desktop-deeplink.ts +66 -2
  15. package/src/app/runtime/desktop-menu.ts +3 -0
  16. package/src/app/runtime/plugin-bindings.ts +30 -28
  17. package/src/app.tsx +9 -2
  18. package/src/cli/commands/automation.test.ts +9 -0
  19. package/src/cli/commands/automation.ts +22 -73
  20. package/src/cli/index.ts +0 -2
  21. package/src/components/chart/composite/composite-chart.test.tsx +45 -2
  22. package/src/components/chart/composite/composite-chart.tsx +12 -9
  23. package/src/components/chart/composite/scene.ts +0 -21
  24. package/src/components/chart/composite/tools.ts +49 -0
  25. package/src/components/command-bar/commands/direct/index.ts +4 -0
  26. package/src/components/command-bar/commands/registry.ts +8 -1
  27. package/src/components/command-bar/layout-items/current-layout.ts +1 -1
  28. package/src/components/command-bar/pane-templates/workflow.ts +4 -0
  29. package/src/components/command-bar/routes/root/shortcut-feedback.ts +3 -0
  30. package/src/components/command-bar/routes/root/shortcuts.test.ts +19 -0
  31. package/src/components/command-bar/routes/root/shortcuts.ts +3 -1
  32. package/src/components/command-bar/surface/index.test.tsx +6 -6
  33. package/src/components/command-bar/surface/pane.test.tsx +66 -2
  34. package/src/components/command-bar/view-model.test.ts +2 -1
  35. package/src/components/command-bar/workflow/ops.test.ts +54 -0
  36. package/src/components/command-bar/workflow/ops.ts +1 -1
  37. package/src/components/layout/detached-pane-shell.tsx +74 -10
  38. package/src/components/layout/pane/title.ts +31 -3
  39. package/src/components/layout/shell/active-drag.ts +4 -11
  40. package/src/components/layout/shell/drag/index.ts +4 -20
  41. package/src/components/layout/shell/drag/runtime.ts +3 -4
  42. package/src/components/layout/shell/index.test.tsx +17 -3
  43. package/src/components/layout/shell/index.tsx +86 -30
  44. package/src/components/layout/shell/menu.test.ts +37 -1
  45. package/src/components/layout/shell/menu.ts +14 -0
  46. package/src/components/layout/shell/native/pointer-runtime.ts +22 -8
  47. package/src/components/layout/shell/pane/actions.ts +0 -5
  48. package/src/components/layout/shell/pane/management-shortcuts.ts +9 -4
  49. package/src/components/layout/shell/shortcuts.ts +6 -3
  50. package/src/components/layout/shell/ticker-link-menu.test.ts +82 -0
  51. package/src/components/layout/shell/ticker-link-menu.ts +78 -0
  52. package/src/components/layout/status-bar.test.tsx +62 -61
  53. package/src/components/layout/status-bar.tsx +53 -75
  54. package/src/core/state/app/layout-reducer.ts +36 -0
  55. package/src/core/state/app/layout.ts +10 -5
  56. package/src/core/state/app/state.ts +0 -13
  57. package/src/core/state/app/types.ts +6 -4
  58. package/src/data/config/chart-settings.ts +1 -1
  59. package/src/data/config/layout.ts +2 -0
  60. package/src/data/config/store/index.test.ts +27 -6
  61. package/src/data/config/store/normalize.ts +3 -5
  62. package/src/data/plugin-state-store.ts +11 -0
  63. package/src/data/sqlite/cache.test.ts +10 -0
  64. package/src/i18n/es.ts +3 -4
  65. package/src/i18n/ja.ts +3 -4
  66. package/src/i18n/ko.ts +3 -4
  67. package/src/i18n/zh-cn.ts +3 -4
  68. package/src/i18n/zh-tw.ts +3 -4
  69. package/src/layout-marketplace/api.test.ts +54 -0
  70. package/src/layout-marketplace/api.ts +65 -0
  71. package/src/layout-marketplace/gallery-desktop.test.tsx +262 -0
  72. package/src/layout-marketplace/gallery-desktop.tsx +375 -0
  73. package/src/layout-marketplace/gallery-terminal.tsx +376 -0
  74. package/src/layout-marketplace/gallery.test.tsx +84 -0
  75. package/src/layout-marketplace/gallery.tsx +323 -0
  76. package/src/layout-marketplace/geometry.test.ts +47 -0
  77. package/src/layout-marketplace/geometry.ts +80 -0
  78. package/src/layout-marketplace/mini-workspace.test.tsx +122 -0
  79. package/src/layout-marketplace/mini-workspace.tsx +428 -0
  80. package/src/layout-marketplace/model.test.ts +118 -0
  81. package/src/layout-marketplace/model.ts +171 -0
  82. package/src/layout-marketplace/name-dialog.tsx +60 -0
  83. package/src/layout-marketplace/pane-imagery.ts +85 -0
  84. package/src/layout-marketplace/pane.tsx +20 -0
  85. package/src/layout-marketplace/payload.test.ts +246 -0
  86. package/src/layout-marketplace/payload.ts +624 -0
  87. package/src/layout-marketplace/use-marketplace.ts +63 -0
  88. package/src/market-data/hooks.tsx +0 -41
  89. package/src/plugins/broker-sync/broker-sync.test.ts +29 -0
  90. package/src/plugins/broker-sync/normalize.ts +9 -2
  91. package/src/plugins/builtin/ai/index.tsx +18 -2
  92. package/src/plugins/builtin/ai/screener/pane.test.tsx +47 -4
  93. package/src/plugins/builtin/ai/screener/pane.tsx +35 -12
  94. package/src/plugins/builtin/alerts/alert-engine.test.ts +16 -1
  95. package/src/plugins/builtin/alerts/alert-engine.ts +4 -0
  96. package/src/plugins/builtin/alerts/index.tsx +4 -0
  97. package/src/plugins/builtin/alerts/pane.tsx +2 -8
  98. package/src/plugins/builtin/analytics/index.tsx +3 -0
  99. package/src/plugins/builtin/broker-manager/index.tsx +3 -0
  100. package/src/plugins/builtin/cds/client.test.ts +83 -0
  101. package/src/plugins/builtin/cds/client.ts +67 -0
  102. package/src/plugins/builtin/cds/index.tsx +41 -0
  103. package/src/plugins/builtin/cds/model.test.ts +232 -0
  104. package/src/plugins/builtin/cds/model.ts +401 -0
  105. package/src/plugins/builtin/cds/pane.test.tsx +132 -0
  106. package/src/plugins/builtin/cds/pane.tsx +358 -0
  107. package/src/plugins/builtin/chart-composer/chart-spec.ts +31 -0
  108. package/src/plugins/builtin/chart-composer/index.tsx +88 -2
  109. package/src/plugins/builtin/chart-composer/pane-share.test.ts +71 -0
  110. package/src/plugins/builtin/chart-composer/pane.tsx +81 -14
  111. package/src/plugins/builtin/chat/content/index.tsx +4 -1
  112. package/src/plugins/builtin/chat/controller/index.ts +8 -1
  113. package/src/plugins/builtin/chat/controller/notifications.ts +13 -3
  114. package/src/plugins/builtin/chat/controller/session-runtime.ts +14 -2
  115. package/src/plugins/builtin/chat/controller/utils.ts +4 -6
  116. package/src/plugins/builtin/chat/controller.test.ts +64 -14
  117. package/src/plugins/builtin/chat/message/desktop.test.tsx +88 -0
  118. package/src/plugins/builtin/chat/message/desktop.tsx +3 -1
  119. package/src/plugins/builtin/chat/message/profile-popover.test.tsx +1 -29
  120. package/src/plugins/builtin/chat/profile-popover.ts +53 -22
  121. package/src/plugins/builtin/chat/sidebar.test.tsx +81 -0
  122. package/src/plugins/builtin/chat/sidebar.tsx +63 -0
  123. package/src/plugins/builtin/chat/test-harness.tsx +29 -1
  124. package/src/plugins/builtin/cloud/auth-commands.ts +0 -4
  125. package/src/plugins/builtin/cloud/auth-model.ts +4 -1
  126. package/src/plugins/builtin/cloud/plugin.tsx +11 -1
  127. package/src/plugins/builtin/cloud-tweets/pane-share.test.ts +56 -0
  128. package/src/plugins/builtin/cloud-tweets/pane.tsx +20 -5
  129. package/src/plugins/builtin/cloud-tweets/registration.ts +44 -5
  130. package/src/plugins/builtin/composite-plugins.ts +3 -1
  131. package/src/plugins/builtin/connections/index.ts +3 -0
  132. package/src/plugins/builtin/econ/calendar-model.ts +0 -5
  133. package/src/plugins/builtin/fear-greed/charts.tsx +12 -8
  134. package/src/plugins/builtin/help/index.tsx +2 -2
  135. package/src/plugins/builtin/kelly-sizer/index.tsx +4 -1
  136. package/src/plugins/builtin/layout-manager/index.tsx +17 -4
  137. package/src/plugins/builtin/news/index.tsx +1 -0
  138. package/src/plugins/builtin/news/wire/feed-config.test.ts +15 -1
  139. package/src/plugins/builtin/news/wire/feed-config.ts +25 -5
  140. package/src/plugins/builtin/news/wire/index.tsx +2 -2
  141. package/src/plugins/builtin/options/index.tsx +1 -0
  142. package/src/plugins/builtin/options/view.test.tsx +25 -3
  143. package/src/plugins/builtin/options/view.tsx +1 -0
  144. package/src/plugins/builtin/portfolio-list/account-metrics.test.ts +2 -0
  145. package/src/plugins/builtin/portfolio-list/account-metrics.ts +1 -1
  146. package/src/plugins/builtin/portfolio-list/cli/render.ts +5 -3
  147. package/src/plugins/builtin/portfolio-list/column-values.ts +15 -4
  148. package/src/plugins/builtin/portfolio-list/header.tsx +13 -3
  149. package/src/plugins/builtin/portfolio-list/index.tsx +8 -0
  150. package/src/plugins/builtin/portfolio-list/metrics.test.ts +31 -1
  151. package/src/plugins/builtin/portfolio-list/pane/index.tsx +10 -7
  152. package/src/plugins/builtin/portfolio-list/position-metrics.ts +25 -6
  153. package/src/plugins/builtin/portfolio-list/summary/index.tsx +21 -16
  154. package/src/plugins/builtin/portfolio-list/summary/totals.ts +20 -6
  155. package/src/plugins/builtin/research/equity-diagnostic-pane.test.tsx +33 -7
  156. package/src/plugins/builtin/research/equity-diagnostic-pane.tsx +109 -12
  157. package/src/plugins/builtin/research/index.tsx +4 -0
  158. package/src/plugins/builtin/shared/public-pane.test.ts +12 -0
  159. package/src/plugins/builtin/shared/public-pane.ts +9 -0
  160. package/src/plugins/builtin/shared/ticker-source.ts +32 -0
  161. package/src/plugins/builtin/shared/ticker-surface.test.ts +27 -0
  162. package/src/plugins/builtin/shared/ticker-surface.ts +17 -0
  163. package/src/plugins/builtin/ticker-detail/index.tsx +10 -0
  164. package/src/plugins/builtin/treasury-auctions/index.tsx +2 -0
  165. package/src/plugins/builtin/world-indices/index.tsx +2 -0
  166. package/src/plugins/catalog-browser.test.ts +10 -0
  167. package/src/plugins/catalog-browser.ts +3 -1
  168. package/src/plugins/gridlock-notification.ts +2 -1
  169. package/src/plugins/pane-manager/gridlock.ts +87 -3
  170. package/src/plugins/pane-manager.test.ts +30 -0
  171. package/src/plugins/pane-manager.ts +6 -1
  172. package/src/plugins/prediction-markets/chart.tsx +2 -2
  173. package/src/plugins/prediction-markets/controller/index.ts +5 -1
  174. package/src/plugins/prediction-markets/controller/keyboard.ts +10 -14
  175. package/src/plugins/prediction-markets/detail/overview.tsx +21 -0
  176. package/src/plugins/prediction-markets/detail/pane.tsx +43 -34
  177. package/src/plugins/prediction-markets/detail/trades.tsx +34 -9
  178. package/src/plugins/prediction-markets/detail.test.tsx +29 -2
  179. package/src/plugins/prediction-markets/index.tsx +8 -4
  180. package/src/plugins/prediction-markets/navigation.ts +0 -1
  181. package/src/plugins/prediction-markets/pane.test.tsx +77 -0
  182. package/src/plugins/prediction-markets/pane.tsx +4 -0
  183. package/src/plugins/prediction-markets/plugin.test.tsx +36 -0
  184. package/src/plugins/prediction-markets/test-helpers.tsx +9 -0
  185. package/src/plugins/registry/index.ts +2 -0
  186. package/src/plugins/ticker-navigation.test.ts +24 -0
  187. package/src/plugins/ticker-navigation.ts +40 -0
  188. package/src/remote/schema.ts +1 -1
  189. package/src/renderers/browser/cloud-transport.test.ts +36 -0
  190. package/src/renderers/browser/cloud-transport.ts +19 -0
  191. package/src/renderers/browser/config-host.ts +0 -4
  192. package/src/renderers/browser/deeplink-bridge.test.ts +41 -0
  193. package/src/renderers/browser/deeplink-bridge.ts +22 -0
  194. package/src/renderers/browser/main.tsx +3 -0
  195. package/src/renderers/cloudflare/worker.test.ts +8 -5
  196. package/src/renderers/cloudflare/worker.ts +6 -5
  197. package/src/renderers/electrobun/bun/application-menu/index.ts +3 -2
  198. package/src/renderers/electrobun/bun/desktop/initialization.ts +1 -0
  199. package/src/renderers/electrobun/bun/desktop/plugin-state.ts +7 -1
  200. package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +1 -1
  201. package/src/renderers/electrobun/view/host/native.ts +0 -5
  202. package/src/renderers/electrobun/view/remote/persistence.ts +5 -0
  203. package/src/renderers/electrobun/view/styles.css +107 -7
  204. package/src/renderers/electrobun/view/toast-host.test.tsx +78 -0
  205. package/src/renderers/electrobun/view/toast-host.tsx +69 -43
  206. package/src/renderers/electrobun/view/ui-host.tsx +4 -1
  207. package/src/renderers/share/layout-view.test.tsx +58 -0
  208. package/src/renderers/share/layout-view.tsx +133 -0
  209. package/src/renderers/share/main.tsx +54 -6
  210. package/src/renderers/share/styles.css +35 -0
  211. package/src/renderers/share/view.test.tsx +78 -0
  212. package/src/renderers/share/view.tsx +71 -0
  213. package/src/shares/api.test.ts +52 -3
  214. package/src/shares/api.ts +39 -15
  215. package/src/shares/location.test.ts +19 -0
  216. package/src/shares/location.ts +11 -0
  217. package/src/shares/pane.test.ts +110 -0
  218. package/src/shares/pane.ts +54 -0
  219. package/src/shares/payload.ts +74 -1
  220. package/src/state/app/bootstrap.ts +6 -7
  221. package/src/state/app/context/index.test.ts +41 -1
  222. package/src/state/app/context/index.test.tsx +34 -16
  223. package/src/state/selectors-ui.ts +0 -8
  224. package/src/sync/controller.test.ts +57 -1
  225. package/src/sync/controller.ts +22 -11
  226. package/src/sync/react.ts +13 -0
  227. package/src/theme/font-scale.ts +0 -11
  228. package/src/types/config.ts +35 -5
  229. package/src/types/desktop-menu.ts +1 -0
  230. package/src/types/plugin.ts +36 -0
  231. package/src/ui/context-menu.tsx +1 -1
  232. package/src/ui/toast.tsx +2 -0
  233. package/src/utils/back-navigation.ts +0 -4
@@ -1,28 +1,29 @@
1
1
  import { Box, Span, Text, TextAttributes, contextMenuDivider, useContextMenu, useUiCapabilities } from "../../ui";
2
2
  import { useDialog, type PromptContext } from "../../ui/dialog";
3
- import { useCallback, useEffect, useState } from "react";
3
+ import { useCallback, useMemo, useState } from "react";
4
4
  import { blendHex, hoverBg } from "../../theme/colors";
5
5
  import { t, tf } from "../../i18n";
6
6
  import { useThemeColors } from "../../theme/theme-context";
7
7
  import { useAppDispatch, useAppSelector } from "../../state/app/context";
8
8
  import {
9
9
  selectActiveLayoutIndex,
10
- selectGridlockTipSequence,
11
- selectGridlockTipVisible,
10
+ selectLayout,
12
11
  selectSavedLayouts,
13
12
  selectStatusBarVisible,
14
13
  } from "../../state/selectors-ui";
15
14
  import { getSharedRegistry } from "../../plugins/registry";
16
- import { gridlockAllPanes } from "../../plugins/pane-manager";
15
+ import {
16
+ gridlockAllPanes,
17
+ shouldShowTidyWindows,
18
+ } from "../../plugins/pane-manager";
17
19
  import { notifyGridlockComplete } from "../../plugins/gridlock-notification";
18
20
  import { PluginSlot } from "../../react/plugins/plugin-slot";
19
21
  import type { ContextMenuItem } from "../../types/context-menu";
20
- import { Tabs } from "../ui/tabs";
22
+ import type { LayoutConfig } from "../../types/config";
21
23
  import { ConfirmDialog } from "../ui/confirm-dialog";
24
+ import { Tabs } from "../ui/tabs";
22
25
  import { useTransientLayout } from "./transient-layout";
23
26
 
24
- const GRIDLOCK_TIP_DURATION_MS = 60_000;
25
-
26
27
  type StatusBarEvent = { stopPropagation?: () => void; preventDefault?: () => void };
27
28
  type HoveredControl = string | null;
28
29
  type SetHoveredControl = (updater: (current: HoveredControl) => HoveredControl) => void;
@@ -37,10 +38,9 @@ type LayoutTabItem = {
37
38
  type StatusBarViewProps = {
38
39
  activeLayoutIdx: number;
39
40
  activeLayoutValue: string;
40
- dismissGridlockTip: (event?: StatusBarEvent) => void;
41
- handleGridlockTip: (event?: StatusBarEvent) => void;
42
41
  handleLayoutReorder: (fromValue: string, toValue: string) => void;
43
42
  handleLayoutSelect: (value: string) => void;
43
+ handleTidyWindows: (event?: StatusBarEvent) => void;
44
44
  hasMultipleLayouts: boolean;
45
45
  hoveredControl: HoveredControl;
46
46
  layoutTabItems: LayoutTabItem[];
@@ -48,7 +48,7 @@ type StatusBarViewProps = {
48
48
  openCommandBar: (event?: StatusBarEvent) => void;
49
49
  openLayoutContextMenu: (index: number, event: any) => void | Promise<unknown>;
50
50
  setHoveredControl: SetHoveredControl;
51
- showGridlockTip: boolean;
51
+ showTidyWindows: boolean;
52
52
  };
53
53
 
54
54
  function truncate(text: string, width: number): string {
@@ -67,13 +67,14 @@ export function StatusBar() {
67
67
  const layouts = useAppSelector(selectSavedLayouts);
68
68
  const activeLayoutIdx = useAppSelector(selectActiveLayoutIndex);
69
69
  const statusBarVisible = useAppSelector(selectStatusBarVisible);
70
- const gridlockTipVisible = useAppSelector(selectGridlockTipVisible);
71
- const gridlockTipSequence = useAppSelector(selectGridlockTipSequence);
70
+ const layout = useAppSelector(selectLayout);
72
71
  const { transientLayout } = useTransientLayout();
73
72
  const [hoveredControl, setHoveredControl] = useState<string | null>(null);
74
73
 
75
74
  const hasMultipleLayouts = layouts.length > 1 || !!transientLayout;
76
- const showGridlockTip = gridlockTipVisible && !!registry;
75
+ const showTidyWindows = useMemo(() => shouldShowTidyWindows(layout), [layout])
76
+ && !transientLayout?.active
77
+ && !!registry;
77
78
  const savedLayoutTabs = layouts.map((layout, index) => ({
78
79
  label: `^${index + 1} ${truncate(layout.name, 14)}`,
79
80
  value: String(index),
@@ -122,34 +123,24 @@ export function StatusBar() {
122
123
  dispatch({ type: "REORDER_LAYOUT", fromIndex, toIndex });
123
124
  };
124
125
 
125
- useEffect(() => {
126
- if (!gridlockTipVisible) return;
127
- const timer = setTimeout(() => {
128
- dispatch({ type: "DISMISS_GRIDLOCK_TIP" });
129
- }, GRIDLOCK_TIP_DURATION_MS);
130
- return () => clearTimeout(timer);
131
- }, [dispatch, gridlockTipSequence, gridlockTipVisible]);
132
-
133
- const handleGridlockTip = (event?: StatusBarEvent) => {
126
+ const handleTidyWindows = (event?: StatusBarEvent) => {
134
127
  event?.preventDefault?.();
135
128
  event?.stopPropagation?.();
136
129
  if (!registry) return;
130
+
131
+ const currentLayout = registry.getLayoutFn();
137
132
  const { width, height } = registry.getTermSizeFn();
138
- registry.updateLayoutFn(gridlockAllPanes(
139
- registry.getLayoutFn(),
133
+ const nextLayout = gridlockAllPanes(
134
+ currentLayout,
140
135
  { x: 0, y: 0, width, height },
141
136
  registry.panes,
142
- ));
137
+ );
138
+ if (nextLayout === currentLayout) return;
139
+
140
+ registry.updateLayoutFn(nextLayout);
143
141
  notifyGridlockComplete(registry.notify.bind(registry), () => {
144
142
  dispatch({ type: "UNDO_LAYOUT" });
145
143
  });
146
- dispatch({ type: "DISMISS_GRIDLOCK_TIP" });
147
- };
148
-
149
- const dismissGridlockTip = (event?: StatusBarEvent) => {
150
- event?.preventDefault?.();
151
- event?.stopPropagation?.();
152
- dispatch({ type: "DISMISS_GRIDLOCK_TIP" });
153
144
  };
154
145
 
155
146
  const openCommandBar = (event?: StatusBarEvent) => {
@@ -226,10 +217,15 @@ export function StatusBar() {
226
217
  onSelect: () => requestDeleteLayout(index),
227
218
  },
228
219
  contextMenuDivider("layout:actions-divider"),
220
+ {
221
+ id: "layout:gallery",
222
+ label: "Browse Layouts...",
223
+ onSelect: () => registry?.showPane("layout-marketplace"),
224
+ },
229
225
  {
230
226
  id: "layout:actions",
231
227
  label: "Layout Actions...",
232
- onSelect: () => registry?.openCommandBar("LAY "),
228
+ onSelect: () => registry?.openCommandBar("LMA "),
233
229
  },
234
230
  );
235
231
 
@@ -270,10 +266,9 @@ export function StatusBar() {
270
266
  const viewProps: StatusBarViewProps = {
271
267
  activeLayoutIdx,
272
268
  activeLayoutValue,
273
- dismissGridlockTip,
274
- handleGridlockTip,
275
269
  handleLayoutReorder,
276
270
  handleLayoutSelect,
271
+ handleTidyWindows,
277
272
  hasMultipleLayouts,
278
273
  hoveredControl,
279
274
  layoutTabItems,
@@ -281,7 +276,7 @@ export function StatusBar() {
281
276
  openCommandBar,
282
277
  openLayoutContextMenu,
283
278
  setHoveredControl,
284
- showGridlockTip,
279
+ showTidyWindows,
285
280
  };
286
281
 
287
282
  if (nativePaneChrome) {
@@ -294,7 +289,7 @@ export function StatusBar() {
294
289
  function NativeStatusBar({
295
290
  activeLayoutIdx,
296
291
  openLayoutContextMenu,
297
- showGridlockTip,
292
+ showTidyWindows,
298
293
  ...props
299
294
  }: StatusBarViewProps) {
300
295
  const colors = useThemeColors();
@@ -315,7 +310,7 @@ function NativeStatusBar({
315
310
  }}
316
311
  >
317
312
  <StatusBarLayoutControl nativePaneChrome {...props} />
318
- {showGridlockTip && <NativeGridlockTip {...props} />}
313
+ {showTidyWindows && <NativeTidyWindows {...props} />}
319
314
  <StatusBarWidgets />
320
315
  </Box>
321
316
  );
@@ -324,7 +319,7 @@ function NativeStatusBar({
324
319
  function TerminalStatusBar({
325
320
  activeLayoutIdx,
326
321
  openLayoutContextMenu,
327
- showGridlockTip,
322
+ showTidyWindows,
328
323
  ...props
329
324
  }: StatusBarViewProps) {
330
325
  const colors = useThemeColors();
@@ -340,7 +335,7 @@ function TerminalStatusBar({
340
335
  }}
341
336
  >
342
337
  <StatusBarLayoutControl nativePaneChrome={false} {...props} />
343
- {showGridlockTip && <TerminalGridlockTip {...props} />}
338
+ {showTidyWindows && <TerminalTidyWindows {...props} />}
344
339
  <StatusBarWidgets />
345
340
  </Box>
346
341
  );
@@ -422,62 +417,45 @@ function CommandBarHint({
422
417
  );
423
418
  }
424
419
 
425
- function NativeGridlockTip({
426
- dismissGridlockTip,
427
- handleGridlockTip,
420
+ function NativeTidyWindows({
421
+ handleTidyWindows,
428
422
  hoveredControl,
429
423
  setHoveredControl,
430
- }: Pick<StatusBarViewProps, "dismissGridlockTip" | "handleGridlockTip" | "hoveredControl" | "setHoveredControl">) {
424
+ }: Pick<StatusBarViewProps, "handleTidyWindows" | "hoveredControl" | "setHoveredControl">) {
431
425
  const colors = useThemeColors();
426
+ const hovered = hoveredControl === "tidy-windows";
432
427
  return (
433
- <Box paddingLeft={2} flexShrink={0} flexDirection="row" alignItems="center" gap={1}>
434
- <Text fg={colors.textDim}>{t("Snapped a window?")}</Text>
428
+ <Box paddingLeft={2} flexShrink={0} flexDirection="row" alignItems="center">
435
429
  <Text
436
- fg={hoveredControl === "gridlock-tip" ? colors.textBright : colors.borderFocused}
430
+ fg={hovered ? colors.textBright : colors.borderFocused}
437
431
  attributes={TextAttributes.BOLD}
438
- onMouseOver={() => setHoveredControl((current) => (current === "gridlock-tip" ? current : "gridlock-tip"))}
439
- onMouseDown={handleGridlockTip}
440
- data-gloom-interactive="true"
441
- >
442
- {t("Gridlock All")}
443
- </Text>
444
- <Text
445
- fg={hoveredControl === "gridlock-tip-dismiss" ? colors.text : colors.textDim}
446
- onMouseOver={() => setHoveredControl((current) => (current === "gridlock-tip-dismiss" ? current : "gridlock-tip-dismiss"))}
447
- onMouseDown={dismissGridlockTip}
432
+ title={t("Tidy Windows")}
433
+ onMouseOver={() => setHoveredControl((current) => (current === "tidy-windows" ? current : "tidy-windows"))}
434
+ onMouseDown={handleTidyWindows}
448
435
  data-gloom-interactive="true"
449
436
  >
450
- {t("Dismiss")}
437
+ {t("Tidy Windows")}
451
438
  </Text>
452
439
  </Box>
453
440
  );
454
441
  }
455
442
 
456
- function TerminalGridlockTip({
457
- dismissGridlockTip,
458
- handleGridlockTip,
443
+ function TerminalTidyWindows({
444
+ handleTidyWindows,
459
445
  hoveredControl,
460
446
  setHoveredControl,
461
- }: Pick<StatusBarViewProps, "dismissGridlockTip" | "handleGridlockTip" | "hoveredControl" | "setHoveredControl">) {
447
+ }: Pick<StatusBarViewProps, "handleTidyWindows" | "hoveredControl" | "setHoveredControl">) {
462
448
  const colors = useThemeColors();
449
+ const hovered = hoveredControl === "tidy-windows";
463
450
  return (
464
451
  <Box paddingLeft={1} flexShrink={0} flexDirection="row">
465
- <Text fg={colors.textDim}>{t("Snapped a window?")}</Text>
466
- <Box width={1} />
467
452
  <Box
468
- backgroundColor={hoveredControl === "gridlock-tip" ? hoverBg(colors) : colors.header}
469
- onMouseOver={() => setHoveredControl((current) => (current === "gridlock-tip" ? current : "gridlock-tip"))}
470
- onMouseDown={handleGridlockTip}
453
+ backgroundColor={hovered ? hoverBg(colors) : colors.header}
454
+ onMouseOver={() => setHoveredControl((current) => (current === "tidy-windows" ? current : "tidy-windows"))}
455
+ onMouseDown={handleTidyWindows}
471
456
  >
472
- <Text fg={colors.headerText}> {t("Gridlock All")} </Text>
457
+ <Text fg={colors.headerText}> {t("Tidy Windows")} </Text>
473
458
  </Box>
474
- <Text
475
- fg={hoveredControl === "gridlock-tip-dismiss" ? colors.text : colors.textDim}
476
- onMouseOver={() => setHoveredControl((current) => (current === "gridlock-tip-dismiss" ? current : "gridlock-tip-dismiss"))}
477
- onMouseDown={dismissGridlockTip}
478
- >
479
- {" x"}
480
- </Text>
481
459
  </Box>
482
460
  );
483
461
  }
@@ -14,6 +14,15 @@ import type { AppAction, AppState } from "./types";
14
14
 
15
15
  const MAX_LAYOUT_HISTORY = 50;
16
16
 
17
+ function availableLayoutName(name: string, layouts: SavedLayout[]): string {
18
+ const base = name.trim() || "Community Layout";
19
+ const existing = new Set(layouts.map((layout) => layout.name.toLowerCase()));
20
+ if (!existing.has(base.toLowerCase())) return base;
21
+ let suffix = 2;
22
+ while (existing.has(`${base} (${suffix})`.toLowerCase())) suffix += 1;
23
+ return `${base} (${suffix})`;
24
+ }
25
+
17
26
  export function reduceLayoutAction(state: AppState, action: AppAction): AppState | undefined {
18
27
  switch (action.type) {
19
28
  case "PUSH_LAYOUT_HISTORY": {
@@ -153,6 +162,33 @@ export function reduceLayoutAction(state: AppState, action: AppAction): AppState
153
162
  });
154
163
  }
155
164
 
165
+ case "INSTALL_LAYOUT_COPY": {
166
+ const currentConfig = syncConfigActiveLayoutState(
167
+ state.config,
168
+ state.paneState,
169
+ state.focusedPaneId,
170
+ state.activePanel,
171
+ );
172
+ const installed: SavedLayout = {
173
+ name: availableLayoutName(action.name, currentConfig.layouts),
174
+ layout: cloneLayout(action.layout),
175
+ paneState: clonePaneStateMap(action.paneState),
176
+ };
177
+ const layouts = [...currentConfig.layouts, installed];
178
+ return withFocusedPane({
179
+ ...state,
180
+ layoutHistory: setHistoryForIndex(state.layoutHistory, layouts.length - 1, { past: [], future: [] }),
181
+ }, {
182
+ ...currentConfig,
183
+ layout: cloneLayout(installed.layout),
184
+ layouts,
185
+ activeLayoutIndex: layouts.length - 1,
186
+ }, {
187
+ paneState: clonePaneStateMap(installed.paneState ?? {}),
188
+ focusedPaneId: null,
189
+ });
190
+ }
191
+
156
192
  case "DELETE_LAYOUT": {
157
193
  if (state.config.layouts.length <= 1) return state;
158
194
  const currentConfig = syncConfigActiveLayoutState(
@@ -118,15 +118,17 @@ function getPaneState(state: Pick<AppState, "paneState">, paneId: string): PaneR
118
118
  return state.paneState[paneId] ?? {};
119
119
  }
120
120
 
121
+ /**
122
+ * Any pane that publishes `cursorSymbol` in its pane state is a ticker source; every other pane
123
+ * resolves through its binding, so follow chains keep working across source types.
124
+ */
121
125
  export function resolveTickerForPane(state: AppState, paneId: string, seen = new Set<string>()): string | null {
122
126
  if (seen.has(paneId)) return null;
123
127
  seen.add(paneId);
124
128
  const instance = findPaneInstance(state.config.layout, paneId);
125
129
  if (!instance) return null;
126
- if (instance.paneId === "portfolio-list") {
127
- const paneState = getPaneState(state, paneId);
128
- return typeof paneState.cursorSymbol === "string" ? paneState.cursorSymbol : null;
129
- }
130
+ const cursorSymbol = getPaneState(state, paneId).cursorSymbol;
131
+ if (typeof cursorSymbol === "string" && cursorSymbol.trim()) return cursorSymbol;
130
132
  return resolveTickerFromBinding(state, instance.binding, seen);
131
133
  }
132
134
 
@@ -388,7 +390,10 @@ export function withFocusedPane(
388
390
  activePanel?: "left" | "right";
389
391
  } = {},
390
392
  ): AppState {
391
- const normalizedLayout = normalizePaneLayout(config.layout);
393
+ const normalizedLayout = normalizePaneLayout(config.layout, {
394
+ // Keep a follower alive on its last symbol when its source pane is gone.
395
+ resolveOrphanSymbol: (instanceId) => resolveTickerForPane(state, instanceId),
396
+ });
392
397
  const nextConfig = normalizedLayout === config.layout
393
398
  ? config
394
399
  : {
@@ -173,17 +173,6 @@ export function appReducer(state: AppState, action: AppAction): AppState {
173
173
  case "TOGGLE_STATUS_BAR":
174
174
  return { ...state, statusBarVisible: !state.statusBarVisible };
175
175
 
176
- case "SHOW_GRIDLOCK_TIP":
177
- return {
178
- ...state,
179
- gridlockTipVisible: true,
180
- gridlockTipSequence: state.gridlockTipSequence + 1,
181
- };
182
-
183
- case "DISMISS_GRIDLOCK_TIP":
184
- if (!state.gridlockTipVisible) return state;
185
- return { ...state, gridlockTipVisible: false };
186
-
187
176
  case "SET_THEME":
188
177
  if (state.config.theme === action.theme && state.themePreview == null) return state;
189
178
  return { ...state, themePreview: null, config: { ...state.config, theme: action.theme } };
@@ -360,8 +349,6 @@ export function createInitialState(config: AppConfig, sessionSnapshot: AppSessio
360
349
  refreshing: new Set(),
361
350
  initialized: false,
362
351
  statusBarVisible: sessionSnapshot?.statusBarVisible !== false,
363
- gridlockTipVisible: false,
364
- gridlockTipSequence: 0,
365
352
  inputCaptured: false,
366
353
  updateAvailable: null,
367
354
  updateProgress: null,
@@ -58,8 +58,6 @@ export interface AppState {
58
58
  refreshing: Set<string>;
59
59
  initialized: boolean;
60
60
  statusBarVisible: boolean;
61
- gridlockTipVisible: boolean;
62
- gridlockTipSequence: number;
63
61
  inputCaptured: boolean;
64
62
  updateAvailable: ReleaseInfo | null;
65
63
  updateProgress: UpdateProgress | null;
@@ -95,8 +93,6 @@ export type AppAction =
95
93
  | { type: "SET_BROKER_ACCOUNTS"; instanceId: string; accounts: BrokerAccount[] }
96
94
  | { type: "SET_INITIALIZED" }
97
95
  | { type: "TOGGLE_STATUS_BAR" }
98
- | { type: "SHOW_GRIDLOCK_TIP" }
99
- | { type: "DISMISS_GRIDLOCK_TIP" }
100
96
  | { type: "SET_THEME"; theme: string }
101
97
  | { type: "PREVIEW_THEME"; theme: string | null }
102
98
  | { type: "SET_UPDATE_AVAILABLE"; release: ReleaseInfo | null }
@@ -114,6 +110,12 @@ export type AppAction =
114
110
  | { type: "SWITCH_LAYOUT"; index: number }
115
111
  | { type: "REORDER_LAYOUT"; fromIndex: number; toIndex: number }
116
112
  | { type: "NEW_LAYOUT"; name: string }
113
+ | {
114
+ type: "INSTALL_LAYOUT_COPY";
115
+ name: string;
116
+ layout: LayoutConfig;
117
+ paneState: Record<string, PaneRuntimeState>;
118
+ }
117
119
  | { type: "DELETE_LAYOUT"; index: number }
118
120
  | { type: "RENAME_LAYOUT"; index: number; name: string }
119
121
  | { type: "DUPLICATE_LAYOUT"; index: number }
@@ -372,7 +372,7 @@ export function migrateChartPaneSettings(
372
372
  settings: Record<string, unknown> | undefined,
373
373
  context: LegacyChartMigrationContext = {},
374
374
  ): Record<string, unknown> | undefined {
375
- if (context.migrateLegacy === false) return settings;
375
+ if (!context.migrateLegacy) return settings;
376
376
  if (paneId === "chart-composer") {
377
377
  const retained = stripKeys(settings, [
378
378
  "chartAxisMode",
@@ -220,6 +220,7 @@ export function sanitizeLayout(
220
220
  const layout = cloneLayout(fallback);
221
221
  return normalizePaneLayout(layout, {
222
222
  defaultFollowSourceInstanceId: getDefaultFollowSourceInstanceId(layout.instances),
223
+ resolveOrphanSymbol: () => null,
223
224
  });
224
225
  }
225
226
 
@@ -242,5 +243,6 @@ export function sanitizeLayout(
242
243
 
243
244
  return normalizePaneLayout(layout, {
244
245
  defaultFollowSourceInstanceId: getDefaultFollowSourceInstanceId(layout.instances),
246
+ resolveOrphanSymbol: () => null,
245
247
  });
246
248
  }
@@ -118,7 +118,7 @@ describe("sanitizeLayout", () => {
118
118
  expect(getDockedPaneIds(layout)).toEqual(["portfolio-list:main", "ticker-detail:main"]);
119
119
  });
120
120
 
121
- test("removes follow panes whose source pane is missing", () => {
121
+ test("keeps a ticker research pane safely unlinked when its source is missing", () => {
122
122
  const layout = sanitizeLayout({
123
123
  dockRoot: { kind: "pane", instanceId: "ticker-detail:main" },
124
124
  instances: [
@@ -132,9 +132,8 @@ describe("sanitizeLayout", () => {
132
132
  detached: [],
133
133
  }, DEFAULT_LAYOUT);
134
134
 
135
- expect(findPaneInstance(layout, "ticker-detail:main")).toBeUndefined();
136
- expect(getDockedPaneIds(layout)).toHaveLength(0);
137
- expect(layout.dockRoot).toBeNull();
135
+ expect(findPaneInstance(layout, "ticker-detail:main")?.binding).toEqual({ kind: "none" });
136
+ expect(getDockedPaneIds(layout)).toEqual(["ticker-detail:main"]);
138
137
  });
139
138
 
140
139
  test("clamps floating placement memory and drops invalid docked placement hints", () => {
@@ -211,7 +210,7 @@ describe("sanitizeLayout", () => {
211
210
  }],
212
211
  floating: [],
213
212
  detached: [],
214
- }, DEFAULT_LAYOUT);
213
+ }, DEFAULT_LAYOUT, { migrateLegacy: true });
215
214
 
216
215
  const pane = findPaneInstance(layout, "comparison-chart:main");
217
216
  expect(pane?.paneId).toBe("chart-composer");
@@ -252,7 +251,7 @@ describe("sanitizeLayout", () => {
252
251
  }],
253
252
  floating: [],
254
253
  detached: [],
255
- }, DEFAULT_LAYOUT);
254
+ }, DEFAULT_LAYOUT, { migrateLegacy: true });
256
255
 
257
256
  const settings = findPaneInstance(layout, "ticker-detail:aapl")?.settings;
258
257
  expect(settings).toEqual({
@@ -277,6 +276,28 @@ describe("sanitizeLayout", () => {
277
276
  expect(settings).not.toHaveProperty("chartResolution");
278
277
  });
279
278
 
279
+ test("does not convert legacy chart settings during ordinary sanitization", () => {
280
+ const layout = sanitizeLayout({
281
+ dockRoot: { kind: "pane", instanceId: "ticker-detail:aapl" },
282
+ instances: [{
283
+ instanceId: "ticker-detail:aapl",
284
+ paneId: "ticker-research",
285
+ binding: { kind: "fixed", symbol: "AAPL" },
286
+ settings: {
287
+ chartRangePreset: "1Y",
288
+ chartResolution: "1wk",
289
+ },
290
+ }],
291
+ floating: [],
292
+ detached: [],
293
+ }, DEFAULT_LAYOUT);
294
+
295
+ expect(findPaneInstance(layout, "ticker-detail:aapl")?.settings).toEqual({
296
+ chartRangePreset: "1Y",
297
+ chartResolution: "1wk",
298
+ });
299
+ });
300
+
280
301
  test("falls back to the default layout when given an obsolete column layout", () => {
281
302
  const layout = sanitizeLayout({
282
303
  columns: [{ width: "100%" }],
@@ -17,13 +17,11 @@ import { clampFontSize } from "../../../theme/font-scale";
17
17
  import { isLayoutConfig, sanitizeLayout } from "../layout";
18
18
  import { migrateSavedConfig } from "./migrations";
19
19
 
20
- const CURRENT_CHART_SANITIZATION = { migrateLegacy: false } as const;
21
-
22
20
  export function normalizeLoadedConfig(saved: Record<string, unknown>, dataDir: string): { config: AppConfig; needsSave: boolean } {
23
21
  const defaults = createDefaultConfig(dataDir);
24
22
  const migration = migrateSavedConfig(saved, dataDir);
25
23
  const candidate = migration.config;
26
- const directLayout = sanitizeLayout(candidate.layout, defaults.layout, CURRENT_CHART_SANITIZATION);
24
+ const directLayout = sanitizeLayout(candidate.layout, defaults.layout);
27
25
  const layouts = sanitizeSavedLayouts(candidate.layouts, directLayout);
28
26
  const activeLayoutIndex = sanitizeActiveLayoutIndex(candidate.activeLayoutIndex, layouts.length);
29
27
  const layout = cloneLayout(layouts[activeLayoutIndex]?.layout ?? directLayout);
@@ -88,7 +86,7 @@ export function normalizeLoadedConfig(saved: Record<string, unknown>, dataDir: s
88
86
 
89
87
  export function normalizeConfigForSave(config: AppConfig): AppConfig {
90
88
  const defaults = createDefaultConfig(config.dataDir);
91
- const directLayout = sanitizeLayout(config.layout, defaults.layout, CURRENT_CHART_SANITIZATION);
89
+ const directLayout = sanitizeLayout(config.layout, defaults.layout);
92
90
  const sanitizedLayouts = sanitizeSavedLayouts(config.layouts, directLayout);
93
91
  const activeLayoutIndex = sanitizeActiveLayoutIndex(config.activeLayoutIndex, sanitizedLayouts.length || 1);
94
92
  const layout = cloneLayout(sanitizedLayouts[activeLayoutIndex]?.layout ?? directLayout);
@@ -306,7 +304,7 @@ function sanitizeSavedLayouts(
306
304
  && typeof (entry as SavedLayout).name === "string",
307
305
  )
308
306
  .map((entry) => {
309
- const layout = sanitizeLayout(entry.layout, fallbackLayout, CURRENT_CHART_SANITIZATION);
307
+ const layout = sanitizeLayout(entry.layout, fallbackLayout);
310
308
  const paneState = sanitizeSavedPaneState((entry as { paneState?: unknown }).paneState, layout);
311
309
  return {
312
310
  id: typeof entry.id === "string" ? entry.id : undefined,
@@ -126,6 +126,17 @@ export class PluginStateStore {
126
126
  ));
127
127
  }
128
128
 
129
+ pluginIds(): string[] {
130
+ return withSqliteBusyRetry("list plugin state ids", () => (
131
+ this.db
132
+ .query<{ plugin_id: string }, []>(
133
+ "SELECT DISTINCT plugin_id FROM plugin_state ORDER BY plugin_id",
134
+ )
135
+ .all()
136
+ .map((row) => row.plugin_id)
137
+ ));
138
+ }
139
+
129
140
  clear(pluginId: string): void {
130
141
  withSqliteBusyRetry("clear plugin state", () => {
131
142
  this.db.query("DELETE FROM plugin_state WHERE plugin_id = ?").run(pluginId);
@@ -221,6 +221,16 @@ describe("AppPersistence", () => {
221
221
  persistence.close();
222
222
  });
223
223
 
224
+ test("lists plugin ids that have stored state", () => {
225
+ const dbPath = createTempDbPath("plugin-state-ids");
226
+ const persistence = new AppPersistence(dbPath);
227
+ persistence.pluginState.set("gloomberb-cloud", "session", { sessionToken: "token" }, 1);
228
+ persistence.pluginState.set("ai", "provider", "openai", 1);
229
+
230
+ expect(persistence.pluginState.pluginIds()).toEqual(["ai", "gloomberb-cloud"]);
231
+ persistence.close();
232
+ });
233
+
224
234
  test("stores multiple plugin state records in one batch", () => {
225
235
  const dbPath = createTempDbPath("plugin-state-batch");
226
236
  const persistence = new AppPersistence(dbPath);
package/src/i18n/es.ts CHANGED
@@ -13,6 +13,7 @@ export const es: Record<string, string> = {
13
13
  "Cancel": "Cancelar",
14
14
  "Close": "Cerrar",
15
15
  "Open": "Abrir",
16
+ "Linked to {source}": "Vinculado a {source}",
16
17
  "Working…": "Procesando…",
17
18
  "Searching…": "Buscando…",
18
19
  "Loading...": "Cargando...",
@@ -220,12 +221,12 @@ export const es: Record<string, string> = {
220
221
  "Dock Pane": "Anclar panel",
221
222
  "Settings": "Ajustes",
222
223
  "Copy Screenshot": "Copiar captura",
224
+ "Share Pane": "Compartir panel",
223
225
  "Pop Out": "Abrir en ventana",
224
226
  "Move Window...": "Mover ventana...",
225
227
  "Resize Window...": "Redimensionar ventana...",
226
228
  "Move Window": "Mover ventana",
227
229
  "Resize Window": "Redimensionar ventana",
228
- "Gridlock All Windows": "Cuadricular todas las ventanas",
229
230
  "Change the current saved layout name": "Cambiar el nombre del diseño guardado actual",
230
231
  "Create a copy of the current layout": "Crear una copia del diseño actual",
231
232
  "Create a fresh saved layout": "Crear un nuevo diseño guardado",
@@ -517,7 +518,6 @@ export const es: Record<string, string> = {
517
518
  "Pop the focused pane out to a desktop window.": "Extraer el panel activo a una ventana de escritorio.",
518
519
  "Copy a screenshot of the focused pane.": "Copiar una captura del panel activo.",
519
520
  "Open layout actions.": "Abrir acciones de diseño.",
520
- "Gridlock all windows.": "Cuadricular todas las ventanas.",
521
521
  "Enter window move mode.": "Entrar en modo mover ventana.",
522
522
  "Enter window resize mode.": "Entrar en modo redimensionar ventana.",
523
523
  "Cancel an active pane drag.": "Cancelar el arrastre de panel activo.",
@@ -897,8 +897,7 @@ export const es: Record<string, string> = {
897
897
  "v{version} available — run {command}": "v{version} disponible — ejecuta {command}",
898
898
  "v{version} available — starting download...": "v{version} disponible — iniciando descarga...",
899
899
  "command bar": "barra de comandos",
900
- "Snapped a window?": "¿Ajustaste una ventana?",
901
- "Gridlock All": "Cuadricular todo",
900
+ "Tidy Windows": "Ordenar ventanas",
902
901
  "Dismiss": "Descartar",
903
902
  "Minimize": "Minimizar",
904
903
  "Maximize": "Maximizar",
package/src/i18n/ja.ts CHANGED
@@ -13,6 +13,7 @@ export const ja: Record<string, string> = {
13
13
  "Cancel": "キャンセル",
14
14
  "Close": "閉じる",
15
15
  "Open": "開く",
16
+ "Linked to {source}": "{source} にリンク中",
16
17
  "Working…": "処理中…",
17
18
  "Searching…": "検索中…",
18
19
  "Loading...": "読み込み中...",
@@ -221,12 +222,12 @@ export const ja: Record<string, string> = {
221
222
  "Dock Pane": "ペインをドッキング",
222
223
  "Settings": "設定",
223
224
  "Copy Screenshot": "スクリーンショットをコピー",
225
+ "Share Pane": "ペインを共有",
224
226
  "Pop Out": "別ウィンドウで表示",
225
227
  "Move Window...": "ウィンドウを移動...",
226
228
  "Resize Window...": "ウィンドウのサイズを変更...",
227
229
  "Move Window": "ウィンドウを移動",
228
230
  "Resize Window": "ウィンドウのサイズを変更",
229
- "Gridlock All Windows": "すべてのウィンドウをグリッド配置",
230
231
  "Change the current saved layout name": "現在保存されているレイアウト名を変更します",
231
232
  "Create a copy of the current layout": "現在のレイアウトのコピーを作成します",
232
233
  "Create a fresh saved layout": "新しく保存されたレイアウトを作成する",
@@ -518,7 +519,6 @@ export const ja: Record<string, string> = {
518
519
  "Pop the focused pane out to a desktop window.": "フォーカスされたペインをデスクトップ ウィンドウにポップアウトします。",
519
520
  "Copy a screenshot of the focused pane.": "フォーカスされたペインのスクリーンショットをコピーします。",
520
521
  "Open layout actions.": "レイアウト アクションを開きます。",
521
- "Gridlock all windows.": "すべてのウィンドウをグリッドロックします。",
522
522
  "Enter window move mode.": "ウィンドウ移動モードに入ります。",
523
523
  "Enter window resize mode.": "ウィンドウサイズ変更モードに入ります。",
524
524
  "Cancel an active pane drag.": "アクティブなペインのドラッグをキャンセルします。",
@@ -912,8 +912,7 @@ export const ja: Record<string, string> = {
912
912
  "v{version} available — run {command}": "v{version} が利用可能 — {command} を実行",
913
913
  "v{version} available — starting download...": "v{version} が利用可能 — ダウンロードを開始しています...",
914
914
  "command bar": "コマンド バー",
915
- "Snapped a window?": "ウィンドウをスナップしましたか?",
916
- "Gridlock All": "すべてをグリッド配置",
915
+ "Tidy Windows": "ウィンドウを整理",
917
916
  "Dismiss": "閉じる",
918
917
  "Minimize": "最小化",
919
918
  "Maximize": "最大化",