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.
- package/README.md +10 -9
- package/README.zh-CN.md +6 -6
- package/package.json +1 -1
- package/src/api-client/data.ts +7 -0
- package/src/api-client/index.test.ts +47 -0
- package/src/api-client/index.ts +61 -0
- package/src/api-client/paths.ts +16 -0
- package/src/api-client/types.ts +35 -0
- package/src/app/pane-runtime/index.tsx +4 -1
- package/src/app/pane-runtime/plugin-bindings.test.ts +117 -0
- package/src/app/pane-runtime/plugin-bindings.ts +25 -4
- package/src/app/pane-runtime/ticker-inspector-runtime.ts +25 -14
- package/src/app/runtime/desktop-deeplink.test.ts +16 -0
- package/src/app/runtime/desktop-deeplink.ts +66 -2
- package/src/app/runtime/desktop-menu.ts +3 -0
- package/src/app/runtime/plugin-bindings.ts +30 -28
- package/src/app.tsx +9 -2
- package/src/cli/commands/automation.test.ts +9 -0
- package/src/cli/commands/automation.ts +22 -73
- package/src/cli/index.ts +0 -2
- package/src/components/chart/composite/composite-chart.test.tsx +45 -2
- package/src/components/chart/composite/composite-chart.tsx +12 -9
- package/src/components/chart/composite/scene.ts +0 -21
- package/src/components/chart/composite/tools.ts +49 -0
- package/src/components/command-bar/commands/direct/index.ts +4 -0
- package/src/components/command-bar/commands/registry.ts +8 -1
- package/src/components/command-bar/layout-items/current-layout.ts +1 -1
- package/src/components/command-bar/pane-templates/workflow.ts +4 -0
- package/src/components/command-bar/routes/root/shortcut-feedback.ts +3 -0
- package/src/components/command-bar/routes/root/shortcuts.test.ts +19 -0
- package/src/components/command-bar/routes/root/shortcuts.ts +3 -1
- package/src/components/command-bar/surface/index.test.tsx +6 -6
- package/src/components/command-bar/surface/pane.test.tsx +66 -2
- package/src/components/command-bar/view-model.test.ts +2 -1
- package/src/components/command-bar/workflow/ops.test.ts +54 -0
- package/src/components/command-bar/workflow/ops.ts +1 -1
- package/src/components/layout/detached-pane-shell.tsx +74 -10
- package/src/components/layout/pane/title.ts +31 -3
- package/src/components/layout/shell/active-drag.ts +4 -11
- package/src/components/layout/shell/drag/index.ts +4 -20
- package/src/components/layout/shell/drag/runtime.ts +3 -4
- package/src/components/layout/shell/index.test.tsx +17 -3
- package/src/components/layout/shell/index.tsx +86 -30
- package/src/components/layout/shell/menu.test.ts +37 -1
- package/src/components/layout/shell/menu.ts +14 -0
- package/src/components/layout/shell/native/pointer-runtime.ts +22 -8
- package/src/components/layout/shell/pane/actions.ts +0 -5
- package/src/components/layout/shell/pane/management-shortcuts.ts +9 -4
- package/src/components/layout/shell/shortcuts.ts +6 -3
- package/src/components/layout/shell/ticker-link-menu.test.ts +82 -0
- package/src/components/layout/shell/ticker-link-menu.ts +78 -0
- package/src/components/layout/status-bar.test.tsx +62 -61
- package/src/components/layout/status-bar.tsx +53 -75
- package/src/core/state/app/layout-reducer.ts +36 -0
- package/src/core/state/app/layout.ts +10 -5
- package/src/core/state/app/state.ts +0 -13
- package/src/core/state/app/types.ts +6 -4
- package/src/data/config/chart-settings.ts +1 -1
- package/src/data/config/layout.ts +2 -0
- package/src/data/config/store/index.test.ts +27 -6
- package/src/data/config/store/normalize.ts +3 -5
- package/src/data/plugin-state-store.ts +11 -0
- package/src/data/sqlite/cache.test.ts +10 -0
- package/src/i18n/es.ts +3 -4
- package/src/i18n/ja.ts +3 -4
- package/src/i18n/ko.ts +3 -4
- package/src/i18n/zh-cn.ts +3 -4
- package/src/i18n/zh-tw.ts +3 -4
- package/src/layout-marketplace/api.test.ts +54 -0
- package/src/layout-marketplace/api.ts +65 -0
- package/src/layout-marketplace/gallery-desktop.test.tsx +262 -0
- package/src/layout-marketplace/gallery-desktop.tsx +375 -0
- package/src/layout-marketplace/gallery-terminal.tsx +376 -0
- package/src/layout-marketplace/gallery.test.tsx +84 -0
- package/src/layout-marketplace/gallery.tsx +323 -0
- package/src/layout-marketplace/geometry.test.ts +47 -0
- package/src/layout-marketplace/geometry.ts +80 -0
- package/src/layout-marketplace/mini-workspace.test.tsx +122 -0
- package/src/layout-marketplace/mini-workspace.tsx +428 -0
- package/src/layout-marketplace/model.test.ts +118 -0
- package/src/layout-marketplace/model.ts +171 -0
- package/src/layout-marketplace/name-dialog.tsx +60 -0
- package/src/layout-marketplace/pane-imagery.ts +85 -0
- package/src/layout-marketplace/pane.tsx +20 -0
- package/src/layout-marketplace/payload.test.ts +246 -0
- package/src/layout-marketplace/payload.ts +624 -0
- package/src/layout-marketplace/use-marketplace.ts +63 -0
- package/src/market-data/hooks.tsx +0 -41
- package/src/plugins/broker-sync/broker-sync.test.ts +29 -0
- package/src/plugins/broker-sync/normalize.ts +9 -2
- package/src/plugins/builtin/ai/index.tsx +18 -2
- package/src/plugins/builtin/ai/screener/pane.test.tsx +47 -4
- package/src/plugins/builtin/ai/screener/pane.tsx +35 -12
- package/src/plugins/builtin/alerts/alert-engine.test.ts +16 -1
- package/src/plugins/builtin/alerts/alert-engine.ts +4 -0
- package/src/plugins/builtin/alerts/index.tsx +4 -0
- package/src/plugins/builtin/alerts/pane.tsx +2 -8
- package/src/plugins/builtin/analytics/index.tsx +3 -0
- package/src/plugins/builtin/broker-manager/index.tsx +3 -0
- package/src/plugins/builtin/cds/client.test.ts +83 -0
- package/src/plugins/builtin/cds/client.ts +67 -0
- package/src/plugins/builtin/cds/index.tsx +41 -0
- package/src/plugins/builtin/cds/model.test.ts +232 -0
- package/src/plugins/builtin/cds/model.ts +401 -0
- package/src/plugins/builtin/cds/pane.test.tsx +132 -0
- package/src/plugins/builtin/cds/pane.tsx +358 -0
- package/src/plugins/builtin/chart-composer/chart-spec.ts +31 -0
- package/src/plugins/builtin/chart-composer/index.tsx +88 -2
- package/src/plugins/builtin/chart-composer/pane-share.test.ts +71 -0
- package/src/plugins/builtin/chart-composer/pane.tsx +81 -14
- package/src/plugins/builtin/chat/content/index.tsx +4 -1
- package/src/plugins/builtin/chat/controller/index.ts +8 -1
- package/src/plugins/builtin/chat/controller/notifications.ts +13 -3
- package/src/plugins/builtin/chat/controller/session-runtime.ts +14 -2
- package/src/plugins/builtin/chat/controller/utils.ts +4 -6
- package/src/plugins/builtin/chat/controller.test.ts +64 -14
- package/src/plugins/builtin/chat/message/desktop.test.tsx +88 -0
- package/src/plugins/builtin/chat/message/desktop.tsx +3 -1
- package/src/plugins/builtin/chat/message/profile-popover.test.tsx +1 -29
- package/src/plugins/builtin/chat/profile-popover.ts +53 -22
- package/src/plugins/builtin/chat/sidebar.test.tsx +81 -0
- package/src/plugins/builtin/chat/sidebar.tsx +63 -0
- package/src/plugins/builtin/chat/test-harness.tsx +29 -1
- package/src/plugins/builtin/cloud/auth-commands.ts +0 -4
- package/src/plugins/builtin/cloud/auth-model.ts +4 -1
- package/src/plugins/builtin/cloud/plugin.tsx +11 -1
- package/src/plugins/builtin/cloud-tweets/pane-share.test.ts +56 -0
- package/src/plugins/builtin/cloud-tweets/pane.tsx +20 -5
- package/src/plugins/builtin/cloud-tweets/registration.ts +44 -5
- package/src/plugins/builtin/composite-plugins.ts +3 -1
- package/src/plugins/builtin/connections/index.ts +3 -0
- package/src/plugins/builtin/econ/calendar-model.ts +0 -5
- package/src/plugins/builtin/fear-greed/charts.tsx +12 -8
- package/src/plugins/builtin/help/index.tsx +2 -2
- package/src/plugins/builtin/kelly-sizer/index.tsx +4 -1
- package/src/plugins/builtin/layout-manager/index.tsx +17 -4
- package/src/plugins/builtin/news/index.tsx +1 -0
- package/src/plugins/builtin/news/wire/feed-config.test.ts +15 -1
- package/src/plugins/builtin/news/wire/feed-config.ts +25 -5
- package/src/plugins/builtin/news/wire/index.tsx +2 -2
- package/src/plugins/builtin/options/index.tsx +1 -0
- package/src/plugins/builtin/options/view.test.tsx +25 -3
- package/src/plugins/builtin/options/view.tsx +1 -0
- package/src/plugins/builtin/portfolio-list/account-metrics.test.ts +2 -0
- package/src/plugins/builtin/portfolio-list/account-metrics.ts +1 -1
- package/src/plugins/builtin/portfolio-list/cli/render.ts +5 -3
- package/src/plugins/builtin/portfolio-list/column-values.ts +15 -4
- package/src/plugins/builtin/portfolio-list/header.tsx +13 -3
- package/src/plugins/builtin/portfolio-list/index.tsx +8 -0
- package/src/plugins/builtin/portfolio-list/metrics.test.ts +31 -1
- package/src/plugins/builtin/portfolio-list/pane/index.tsx +10 -7
- package/src/plugins/builtin/portfolio-list/position-metrics.ts +25 -6
- package/src/plugins/builtin/portfolio-list/summary/index.tsx +21 -16
- package/src/plugins/builtin/portfolio-list/summary/totals.ts +20 -6
- package/src/plugins/builtin/research/equity-diagnostic-pane.test.tsx +33 -7
- package/src/plugins/builtin/research/equity-diagnostic-pane.tsx +109 -12
- package/src/plugins/builtin/research/index.tsx +4 -0
- package/src/plugins/builtin/shared/public-pane.test.ts +12 -0
- package/src/plugins/builtin/shared/public-pane.ts +9 -0
- package/src/plugins/builtin/shared/ticker-source.ts +32 -0
- package/src/plugins/builtin/shared/ticker-surface.test.ts +27 -0
- package/src/plugins/builtin/shared/ticker-surface.ts +17 -0
- package/src/plugins/builtin/ticker-detail/index.tsx +10 -0
- package/src/plugins/builtin/treasury-auctions/index.tsx +2 -0
- package/src/plugins/builtin/world-indices/index.tsx +2 -0
- package/src/plugins/catalog-browser.test.ts +10 -0
- package/src/plugins/catalog-browser.ts +3 -1
- package/src/plugins/gridlock-notification.ts +2 -1
- package/src/plugins/pane-manager/gridlock.ts +87 -3
- package/src/plugins/pane-manager.test.ts +30 -0
- package/src/plugins/pane-manager.ts +6 -1
- package/src/plugins/prediction-markets/chart.tsx +2 -2
- package/src/plugins/prediction-markets/controller/index.ts +5 -1
- package/src/plugins/prediction-markets/controller/keyboard.ts +10 -14
- package/src/plugins/prediction-markets/detail/overview.tsx +21 -0
- package/src/plugins/prediction-markets/detail/pane.tsx +43 -34
- package/src/plugins/prediction-markets/detail/trades.tsx +34 -9
- package/src/plugins/prediction-markets/detail.test.tsx +29 -2
- package/src/plugins/prediction-markets/index.tsx +8 -4
- package/src/plugins/prediction-markets/navigation.ts +0 -1
- package/src/plugins/prediction-markets/pane.test.tsx +77 -0
- package/src/plugins/prediction-markets/pane.tsx +4 -0
- package/src/plugins/prediction-markets/plugin.test.tsx +36 -0
- package/src/plugins/prediction-markets/test-helpers.tsx +9 -0
- package/src/plugins/registry/index.ts +2 -0
- package/src/plugins/ticker-navigation.test.ts +24 -0
- package/src/plugins/ticker-navigation.ts +40 -0
- package/src/remote/schema.ts +1 -1
- package/src/renderers/browser/cloud-transport.test.ts +36 -0
- package/src/renderers/browser/cloud-transport.ts +19 -0
- package/src/renderers/browser/config-host.ts +0 -4
- package/src/renderers/browser/deeplink-bridge.test.ts +41 -0
- package/src/renderers/browser/deeplink-bridge.ts +22 -0
- package/src/renderers/browser/main.tsx +3 -0
- package/src/renderers/cloudflare/worker.test.ts +8 -5
- package/src/renderers/cloudflare/worker.ts +6 -5
- package/src/renderers/electrobun/bun/application-menu/index.ts +3 -2
- package/src/renderers/electrobun/bun/desktop/initialization.ts +1 -0
- package/src/renderers/electrobun/bun/desktop/plugin-state.ts +7 -1
- package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +1 -1
- package/src/renderers/electrobun/view/host/native.ts +0 -5
- package/src/renderers/electrobun/view/remote/persistence.ts +5 -0
- package/src/renderers/electrobun/view/styles.css +107 -7
- package/src/renderers/electrobun/view/toast-host.test.tsx +78 -0
- package/src/renderers/electrobun/view/toast-host.tsx +69 -43
- package/src/renderers/electrobun/view/ui-host.tsx +4 -1
- package/src/renderers/share/layout-view.test.tsx +58 -0
- package/src/renderers/share/layout-view.tsx +133 -0
- package/src/renderers/share/main.tsx +54 -6
- package/src/renderers/share/styles.css +35 -0
- package/src/renderers/share/view.test.tsx +78 -0
- package/src/renderers/share/view.tsx +71 -0
- package/src/shares/api.test.ts +52 -3
- package/src/shares/api.ts +39 -15
- package/src/shares/location.test.ts +19 -0
- package/src/shares/location.ts +11 -0
- package/src/shares/pane.test.ts +110 -0
- package/src/shares/pane.ts +54 -0
- package/src/shares/payload.ts +74 -1
- package/src/state/app/bootstrap.ts +6 -7
- package/src/state/app/context/index.test.ts +41 -1
- package/src/state/app/context/index.test.tsx +34 -16
- package/src/state/selectors-ui.ts +0 -8
- package/src/sync/controller.test.ts +57 -1
- package/src/sync/controller.ts +22 -11
- package/src/sync/react.ts +13 -0
- package/src/theme/font-scale.ts +0 -11
- package/src/types/config.ts +35 -5
- package/src/types/desktop-menu.ts +1 -0
- package/src/types/plugin.ts +36 -0
- package/src/ui/context-menu.tsx +1 -1
- package/src/ui/toast.tsx +2 -0
- 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,
|
|
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
|
-
|
|
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 {
|
|
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 {
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
139
|
-
|
|
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("
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
{
|
|
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
|
-
|
|
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
|
-
{
|
|
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
|
|
426
|
-
|
|
427
|
-
handleGridlockTip,
|
|
420
|
+
function NativeTidyWindows({
|
|
421
|
+
handleTidyWindows,
|
|
428
422
|
hoveredControl,
|
|
429
423
|
setHoveredControl,
|
|
430
|
-
}: Pick<StatusBarViewProps, "
|
|
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"
|
|
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={
|
|
430
|
+
fg={hovered ? colors.textBright : colors.borderFocused}
|
|
437
431
|
attributes={TextAttributes.BOLD}
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
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("
|
|
437
|
+
{t("Tidy Windows")}
|
|
451
438
|
</Text>
|
|
452
439
|
</Box>
|
|
453
440
|
);
|
|
454
441
|
}
|
|
455
442
|
|
|
456
|
-
function
|
|
457
|
-
|
|
458
|
-
handleGridlockTip,
|
|
443
|
+
function TerminalTidyWindows({
|
|
444
|
+
handleTidyWindows,
|
|
459
445
|
hoveredControl,
|
|
460
446
|
setHoveredControl,
|
|
461
|
-
}: Pick<StatusBarViewProps, "
|
|
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={
|
|
469
|
-
onMouseOver={() => setHoveredControl((current) => (current === "
|
|
470
|
-
onMouseDown={
|
|
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("
|
|
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
|
-
|
|
127
|
-
|
|
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
|
|
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("
|
|
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")).
|
|
136
|
-
expect(getDockedPaneIds(layout)).
|
|
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
|
|
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
|
|
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
|
|
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
|
-
"
|
|
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
|
-
"
|
|
916
|
-
"Gridlock All": "すべてをグリッド配置",
|
|
915
|
+
"Tidy Windows": "ウィンドウを整理",
|
|
917
916
|
"Dismiss": "閉じる",
|
|
918
917
|
"Minimize": "最小化",
|
|
919
918
|
"Maximize": "最大化",
|