gloomberb 0.18.2 → 0.18.3
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/package.json +1 -1
- package/patches/electrobun@1.18.1.patch +106 -5
- package/src/api-client/data.ts +15 -0
- package/src/api-client/index.ts +2 -0
- package/src/api-client/request.ts +5 -3
- package/src/api-client/telemetry.ts +17 -0
- package/src/app/global-shortcuts.ts +1 -16
- package/src/app/runtime/update.ts +28 -8
- package/src/architecture/pane-tabs-allowlist.json +65 -0
- package/src/cli/commands/market.ts +73 -33
- package/src/cli/commands/ticker.ts +82 -25
- package/src/cli/help.ts +1 -1
- package/src/cli/index.ts +8 -0
- package/src/cli/issuer-membership.ts +103 -0
- package/src/cli/listing-arg.ts +15 -2
- package/src/cli/name-entries.ts +51 -12
- package/src/cli/pane-functions/capabilities.ts +2 -0
- package/src/cli/pane-functions/catalog.ts +16 -0
- package/src/cli/pane-functions/dom.ts +2 -1
- package/src/cli/pane-functions/headless.ts +25 -6
- package/src/cli/pane-functions/index.ts +64 -5
- package/src/cli/pane-functions/resolver.ts +19 -5
- package/src/cli/report-tables.ts +136 -18
- package/src/cli/result.ts +12 -4
- package/src/cli/symbol-list.ts +47 -11
- package/src/components/chart/composite/composite-chart.tsx +14 -5
- package/src/components/chart/composite/drawing-store.ts +26 -6
- package/src/components/chart/composite/interactions.ts +2 -1
- package/src/components/chart/composite/panel-surface.tsx +11 -0
- package/src/components/chart/composite/types.ts +6 -0
- package/src/components/chart/composite/use-composite-chart-keyboard.ts +1 -1
- package/src/components/command-bar/routes/root/command-items.ts +32 -6
- package/src/components/data-table/view.tsx +23 -3
- package/src/components/index.ts +1 -0
- package/src/components/layout/detached-pane-shell.tsx +1 -0
- package/src/components/layout/floating-pane.tsx +1 -0
- package/src/components/layout/header.tsx +228 -55
- package/src/components/layout/pane/header.tsx +10 -3
- package/src/components/layout/pane/index.tsx +1 -0
- package/src/components/layout/pane/pane-tabs.tsx +9 -1
- package/src/components/ui/stat-grid.tsx +28 -3
- package/src/components/ui/status.tsx +3 -1
- package/src/core/app-services.ts +4 -0
- package/src/i18n/es.ts +22 -0
- package/src/i18n/ja.ts +22 -0
- package/src/i18n/ko.ts +22 -0
- package/src/i18n/zh-cn.ts +22 -0
- package/src/i18n/zh-tw.ts +22 -0
- package/src/plugins/builtin/alerts/alert-engine.ts +3 -1
- package/src/plugins/builtin/alerts/events.ts +1 -0
- package/src/plugins/builtin/alerts/research-rules.ts +1 -0
- package/src/plugins/builtin/analytics/risk-pane.tsx +10 -7
- package/src/plugins/builtin/attention/pane.tsx +16 -14
- package/src/plugins/builtin/awards/pane.tsx +6 -6
- package/src/plugins/builtin/backtest/export.ts +1 -1
- package/src/plugins/builtin/broker-trading/dock.tsx +4 -2
- package/src/plugins/builtin/broker-trading/pane.tsx +6 -4
- package/src/plugins/builtin/broker-trading/ticket.tsx +10 -5
- package/src/plugins/builtin/buildout/pane/index.tsx +9 -6
- package/src/plugins/builtin/catalysts/client.ts +2 -2
- package/src/plugins/builtin/catalysts/pane.tsx +3 -3
- package/src/plugins/builtin/chart-composer/pane.tsx +1 -0
- package/src/plugins/builtin/commodity-balances/pane.tsx +3 -4
- package/src/plugins/builtin/company-attention/app-rank.tsx +4 -5
- package/src/plugins/builtin/company-attention/pane.tsx +13 -11
- package/src/plugins/builtin/company-kpis/pane.tsx +4 -3
- package/src/plugins/builtin/correlation/headless.ts +2 -1
- package/src/plugins/builtin/correlation/relationship/model.ts +1 -0
- package/src/plugins/builtin/correlation/settings.ts +1 -0
- package/src/plugins/builtin/cot/pane.tsx +4 -3
- package/src/plugins/builtin/credit-documents/pane.tsx +8 -7
- package/src/plugins/builtin/debt-maturities/pane.tsx +4 -3
- package/src/plugins/builtin/doe/pane.tsx +1 -1
- package/src/plugins/builtin/equity-screener/headless.ts +10 -0
- package/src/plugins/builtin/equity-screener/model.ts +28 -0
- package/src/plugins/builtin/equity-screener/pane.tsx +105 -98
- package/src/plugins/builtin/exposure/pane.tsx +10 -9
- package/src/plugins/builtin/gpu/board.tsx +4 -1
- package/src/plugins/builtin/gpu/changes.tsx +10 -10
- package/src/plugins/builtin/gpu/equities.tsx +4 -1
- package/src/plugins/builtin/gpu/history.tsx +4 -2
- package/src/plugins/builtin/gpu/pane.tsx +5 -3
- package/src/plugins/builtin/holders/client.ts +2 -1
- package/src/plugins/builtin/holders/headless.ts +2 -1
- package/src/plugins/builtin/holders/thirteenf-match.ts +3 -1
- package/src/plugins/builtin/insider/base-values.ts +11 -7
- package/src/plugins/builtin/insider/client.ts +3 -0
- package/src/plugins/builtin/insider/headless.ts +19 -6
- package/src/plugins/builtin/insider/index.tsx +17 -8
- package/src/plugins/builtin/insider/issuer.ts +34 -4
- package/src/plugins/builtin/insider/signals-client.ts +2 -2
- package/src/plugins/builtin/insider/table-model.ts +35 -13
- package/src/plugins/builtin/insider/table.tsx +5 -2
- package/src/plugins/builtin/iv-history/events.ts +145 -0
- package/src/plugins/builtin/iv-history/format.ts +3 -0
- package/src/plugins/builtin/iv-history/headless.ts +91 -7
- package/src/plugins/builtin/jodi/pane.tsx +4 -4
- package/src/plugins/builtin/market-movers/client.ts +4 -3
- package/src/plugins/builtin/market-movers/screener.ts +4 -3
- package/src/plugins/builtin/notes/cli.ts +1 -1
- package/src/plugins/builtin/notes/export.ts +17 -6
- package/src/plugins/builtin/notes/files.ts +16 -5
- package/src/plugins/builtin/options/enrichment-model.ts +10 -9
- package/src/plugins/builtin/options/model-spot.ts +70 -4
- package/src/plugins/builtin/options/view.tsx +41 -13
- package/src/plugins/builtin/options-calculator/pane.tsx +3 -2
- package/src/plugins/builtin/options-positioning/headless.ts +11 -3
- package/src/plugins/builtin/options-positioning/model.ts +24 -0
- package/src/plugins/builtin/options-positioning/pane.tsx +10 -12
- package/src/plugins/builtin/options-scenario/client.ts +19 -1
- package/src/plugins/builtin/options-scenario/headless.ts +13 -6
- package/src/plugins/builtin/options-scenario/pane.tsx +13 -5
- package/src/plugins/builtin/plugin-marketplace/pane.tsx +5 -4
- package/src/plugins/builtin/portfolio-list/cli/bulk-symbols.ts +8 -5
- package/src/plugins/builtin/power/client.ts +2 -2
- package/src/plugins/builtin/power/pane.tsx +36 -31
- package/src/plugins/builtin/reverse-dcf/client.ts +2 -1
- package/src/plugins/builtin/reverse-dcf/model.ts +4 -3
- package/src/plugins/builtin/seasonality/client.ts +2 -1
- package/src/plugins/builtin/sectors/client.ts +4 -3
- package/src/plugins/builtin/shared/cloud-resource.ts +15 -2
- package/src/plugins/builtin/shared/research-trading.tsx +2 -0
- package/src/plugins/builtin/shared/series-list-detail.tsx +3 -1
- package/src/plugins/builtin/shared/volatility/options.ts +2 -1
- package/src/plugins/builtin/social-mentions/client.ts +2 -1
- package/src/plugins/builtin/vol-surface/client.ts +23 -3
- package/src/plugins/builtin/vol-surface/headless.ts +33 -14
- package/src/plugins/builtin/vol-surface/model.ts +30 -10
- package/src/plugins/builtin/vol-surface/pane.tsx +33 -13
- package/src/plugins/builtin/vol-surface/stored.ts +34 -3
- package/src/plugins/builtin/vol-surface/tables.ts +14 -0
- package/src/renderers/dom/desktop/controls.tsx +1 -1
- package/src/renderers/dom/desktop/query-bar.tsx +2 -0
- package/src/renderers/dom/host/tabs.tsx +16 -5
- package/src/renderers/dom/host/top-surface.ts +127 -0
- package/src/renderers/dom/styles.css +39 -0
- package/src/renderers/electrobun/bun/core-capabilities.ts +9 -1
- package/src/renderers/electrobun/bun/desktop/backend.ts +10 -0
- package/src/renderers/electrobun/bun/desktop/update.ts +210 -30
- package/src/renderers/electrobun/view/news-source.ts +4 -2
- package/src/renderers/electrobun/view/remote/asset-data-client.ts +8 -1
- package/src/renderers/opentui/start.tsx +7 -0
- package/src/sources/gloomberb-cloud/index.ts +7 -0
- package/src/sources/provider-router/index.ts +16 -0
- package/src/telemetry/crash-reports.ts +4 -0
- package/src/telemetry/update-failures-node.ts +59 -0
- package/src/telemetry/update-failures.ts +307 -0
- package/src/tickers/company-name-listing.ts +9 -7
- package/src/tickers/company-name.ts +43 -0
- package/src/tickers/listing-fate.ts +30 -0
- package/src/types/data-provider.ts +6 -0
- package/src/types/headless.ts +7 -0
- package/src/types/issuer-listings.ts +28 -0
- package/src/updater/failure.ts +163 -0
- package/src/updater/index.ts +87 -23
- package/src/utils/currency-pair.ts +26 -0
- package/src/utils/sec.ts +68 -0
|
@@ -12,6 +12,7 @@ import { WebIcon, WebIconButton } from "../desktop/icons";
|
|
|
12
12
|
import { WebMenu } from "../desktop/menu";
|
|
13
13
|
import { WebPopover } from "../desktop/popover";
|
|
14
14
|
import { useHorizontalOverflow } from "./overflow-fade";
|
|
15
|
+
import { useTopSurfaceColor } from "./top-surface";
|
|
15
16
|
import { hideDragShield, showDragShield } from "./drag-shield";
|
|
16
17
|
|
|
17
18
|
type CssVars = CSSProperties & Record<`--${string}`, string>;
|
|
@@ -44,9 +45,10 @@ export function WebTabs({
|
|
|
44
45
|
// A tab bar occupies exactly the one row every caller reserves for it. Any
|
|
45
46
|
// extra pixels here are pixels the pane's children are told they own and the
|
|
46
47
|
// pane then clips, which is how a chart's time axis ends up behind a footer.
|
|
47
|
-
// Title-bar tabs fill the header to its bottom edge,
|
|
48
|
-
//
|
|
49
|
-
// look in styles.css: text,
|
|
48
|
+
// Title-bar tabs fill the header to its bottom edge, where the active tab's
|
|
49
|
+
// box covers the header's bottom rule and opens into the pane. Every variant
|
|
50
|
+
// draws the one tab look in styles.css: text, the active tab on an accent
|
|
51
|
+
// underline, and title-bar tabs as boxes instead of underlines.
|
|
50
52
|
const listHeight = header ? "100%" : variant === "underline" || compact ? WEB_CELL_HEIGHT : "100%";
|
|
51
53
|
|
|
52
54
|
useEffect(() => () => dragCleanupRef.current?.(), []);
|
|
@@ -67,6 +69,9 @@ export function WebTabs({
|
|
|
67
69
|
useEffect(() => {
|
|
68
70
|
if (!overflowing) setOverflowMenuOpen(false);
|
|
69
71
|
}, [overflowing]);
|
|
72
|
+
// The active title-bar tab opens into the pane, so it takes the colour of
|
|
73
|
+
// whatever is directly under it.
|
|
74
|
+
useTopSurfaceColor(tabListRef, header, [activeValue, tabs]);
|
|
70
75
|
|
|
71
76
|
const selectFromOverflowMenu = (value: string) => {
|
|
72
77
|
setOverflowMenuOpen(false);
|
|
@@ -173,12 +178,14 @@ export function WebTabs({
|
|
|
173
178
|
<div
|
|
174
179
|
ref={tabListRef}
|
|
175
180
|
data-gloom-role="tab-list"
|
|
181
|
+
data-gloom-variant={header ? "header" : undefined}
|
|
176
182
|
role="tablist"
|
|
177
183
|
onWheel={overflow.onWheel}
|
|
178
184
|
style={{
|
|
179
185
|
display: "flex",
|
|
180
186
|
flexDirection: "row",
|
|
181
|
-
|
|
187
|
+
// Title-bar tabs stand on the header's bottom edge, clear of its top.
|
|
188
|
+
alignItems: header ? "flex-end" : "stretch",
|
|
182
189
|
gap: 0,
|
|
183
190
|
width: "100%",
|
|
184
191
|
height: listHeight,
|
|
@@ -217,7 +224,10 @@ export function WebTabs({
|
|
|
217
224
|
transform: `translateX(${dragTranslateX}px)${sourceDragging ? " scale(1.03)" : ""}`,
|
|
218
225
|
zIndex: dragSourceValue === tab.value ? 2 : undefined,
|
|
219
226
|
willChange: dragSourceValue ? "transform" : undefined,
|
|
220
|
-
|
|
227
|
+
// An inactive title-bar tab fades its hover fill in. The active one's
|
|
228
|
+
// box changes with the bar's rule and the body under it, which do not
|
|
229
|
+
// fade, so it does not either.
|
|
230
|
+
transition: `color 110ms ease${header && !active ? ", background-color 110ms ease" : ""}, transform ${dragSourceValue && dragSourceValue !== tab.value ? "var(--tab-reorder-duration, 160ms)" : "0ms"} cubic-bezier(0.77, 0, 0.175, 1)`,
|
|
221
231
|
cursor: disabled ? "default" : dragSourceValue ? "grabbing" : reorderable ? "grab" : "pointer",
|
|
222
232
|
...(dragTarget ? { color: "var(--gloom-text)" } : {}),
|
|
223
233
|
} satisfies CssVars;
|
|
@@ -230,6 +240,7 @@ export function WebTabs({
|
|
|
230
240
|
data-active={active ? "true" : undefined}
|
|
231
241
|
data-dense={dense || compact ? "true" : undefined}
|
|
232
242
|
data-reorderable={reorderable ? "true" : undefined}
|
|
243
|
+
data-drag-target={dragTarget ? "true" : undefined}
|
|
233
244
|
data-tab-value={tab.value}
|
|
234
245
|
type="button"
|
|
235
246
|
role="tab"
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { useLayoutEffect, type RefObject } from "react";
|
|
2
|
+
|
|
3
|
+
type Rgba = [number, number, number, number];
|
|
4
|
+
|
|
5
|
+
const THROTTLE_MS = 120;
|
|
6
|
+
/** Rows and cells follow the cursor and selection; a tab must not take their colour. */
|
|
7
|
+
const TRANSIENT_ROLES = /^data-table-(row|cell|header-cell)$/;
|
|
8
|
+
const ACTIVE_BG = "--pane-tab-active-bg";
|
|
9
|
+
|
|
10
|
+
function parseColor(value: string): Rgba | null {
|
|
11
|
+
const trimmed = value.trim();
|
|
12
|
+
if (!trimmed || trimmed === "transparent") return [0, 0, 0, 0];
|
|
13
|
+
const rgb = /^rgba?\(([^)]+)\)$/.exec(trimmed);
|
|
14
|
+
if (rgb) {
|
|
15
|
+
const parts = rgb[1]!.split(/[\s,/]+/).filter(Boolean).map(Number);
|
|
16
|
+
if (parts.length < 3 || parts.some(Number.isNaN)) return null;
|
|
17
|
+
return [parts[0]!, parts[1]!, parts[2]!, parts[3] ?? 1];
|
|
18
|
+
}
|
|
19
|
+
const srgb = /^color\(srgb\s+([^)]+)\)$/.exec(trimmed);
|
|
20
|
+
if (srgb) {
|
|
21
|
+
const parts = srgb[1]!.split(/[\s/]+/).filter(Boolean).map(Number);
|
|
22
|
+
if (parts.length < 3 || parts.some(Number.isNaN)) return null;
|
|
23
|
+
return [parts[0]! * 255, parts[1]! * 255, parts[2]! * 255, parts[3] ?? 1];
|
|
24
|
+
}
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function over(top: Rgba, base: Rgba): Rgba {
|
|
29
|
+
const alpha = top[3];
|
|
30
|
+
return [
|
|
31
|
+
top[0] * alpha + base[0] * (1 - alpha),
|
|
32
|
+
top[1] * alpha + base[1] * (1 - alpha),
|
|
33
|
+
top[2] * alpha + base[2] * (1 - alpha),
|
|
34
|
+
1,
|
|
35
|
+
];
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* The colour the active title-bar tab takes so it reads as the top of the pane
|
|
40
|
+
* body: whatever surface sits directly under it (the query bar's band, a detail
|
|
41
|
+
* header, a table header, the body itself), translucent layers composited.
|
|
42
|
+
*/
|
|
43
|
+
function measureTopSurface(body: HTMLElement, x: number): string | null {
|
|
44
|
+
const bodyRect = body.getBoundingClientRect();
|
|
45
|
+
if (bodyRect.width <= 0 || bodyRect.height <= 0) return null;
|
|
46
|
+
const y = bodyRect.top + 1;
|
|
47
|
+
let top: Element = document.elementsFromPoint(x, y).find((element) => body.contains(element)) ?? body;
|
|
48
|
+
// A marked surface (query bar, detail header) is taken whole, so hovering a
|
|
49
|
+
// control inside it never tints the tab.
|
|
50
|
+
const surface = top.closest("[data-gloom-top-surface]");
|
|
51
|
+
if (surface && body.contains(surface)) top = surface;
|
|
52
|
+
|
|
53
|
+
const layers: Rgba[] = [];
|
|
54
|
+
for (let element: Element | null = top; element; element = element.parentElement) {
|
|
55
|
+
const role = element.getAttribute("data-gloom-role") ?? "";
|
|
56
|
+
const rect = element.getBoundingClientRect();
|
|
57
|
+
const startsAtTop = Math.abs(rect.top - bodyRect.top) <= 1.5;
|
|
58
|
+
const spansBody = rect.width >= bodyRect.width * 0.6;
|
|
59
|
+
if (element === body || element === surface || (startsAtTop && spansBody && !TRANSIENT_ROLES.test(role))) {
|
|
60
|
+
const color = parseColor(getComputedStyle(element).backgroundColor);
|
|
61
|
+
if (color && color[3] > 0) layers.push(color);
|
|
62
|
+
}
|
|
63
|
+
if (element === body) break;
|
|
64
|
+
}
|
|
65
|
+
// From the body outwards to the topmost surface.
|
|
66
|
+
let color: Rgba | null = null;
|
|
67
|
+
for (let index = layers.length - 1; index >= 0; index -= 1) {
|
|
68
|
+
const layer = layers[index]!;
|
|
69
|
+
color = color ? over(layer, color) : layer[3] >= 1 ? layer : null;
|
|
70
|
+
}
|
|
71
|
+
if (!color) return null;
|
|
72
|
+
return `rgb(${Math.round(color[0])}, ${Math.round(color[1])}, ${Math.round(color[2])})`;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Keeps `--pane-tab-active-bg` on a title-bar tab list equal to the surface
|
|
77
|
+
* under its active tab, so the tab opens into the pane. Without a measure the
|
|
78
|
+
* header's own value (the body colour) applies. A change to the body itself
|
|
79
|
+
* (focus and theme recolour it) is measured at once, before it paints; changes
|
|
80
|
+
* inside it (a detail opens, a query bar appears) at most every 120 ms; a
|
|
81
|
+
* resize once it stops, so dragging a pane's edge costs no measures.
|
|
82
|
+
*/
|
|
83
|
+
export function useTopSurfaceColor(listRef: RefObject<HTMLElement | null>, enabled: boolean, deps: readonly unknown[]) {
|
|
84
|
+
useLayoutEffect(() => {
|
|
85
|
+
const list = listRef.current;
|
|
86
|
+
if (!enabled || !list) return;
|
|
87
|
+
const body = list.closest("[data-gloom-role=pane-window], [data-gloom-role=detached-pane-window]")
|
|
88
|
+
?.querySelector<HTMLElement>(":scope > [data-gloom-role=pane-body]");
|
|
89
|
+
if (!body) return;
|
|
90
|
+
|
|
91
|
+
let timer: ReturnType<typeof setTimeout> | null = null;
|
|
92
|
+
const measure = () => {
|
|
93
|
+
if (timer != null) clearTimeout(timer);
|
|
94
|
+
timer = null;
|
|
95
|
+
const active = list.querySelector<HTMLElement>("[data-active=true]");
|
|
96
|
+
if (!active) return;
|
|
97
|
+
const rect = active.getBoundingClientRect();
|
|
98
|
+
const color = measureTopSurface(body, rect.left + rect.width / 2);
|
|
99
|
+
if (color === (list.style.getPropertyValue(ACTIVE_BG) || null)) return;
|
|
100
|
+
if (color) list.style.setProperty(ACTIVE_BG, color);
|
|
101
|
+
else list.style.removeProperty(ACTIVE_BG);
|
|
102
|
+
};
|
|
103
|
+
const schedule = () => {
|
|
104
|
+
if (timer == null) timer = setTimeout(measure, THROTTLE_MS);
|
|
105
|
+
};
|
|
106
|
+
const settle = () => {
|
|
107
|
+
if (timer != null) clearTimeout(timer);
|
|
108
|
+
timer = setTimeout(measure, THROTTLE_MS);
|
|
109
|
+
};
|
|
110
|
+
measure();
|
|
111
|
+
const resize = new ResizeObserver(settle);
|
|
112
|
+
resize.observe(body);
|
|
113
|
+
// Live data rewrites text constantly; only structure and inline styles
|
|
114
|
+
// (visibility, focus colours, theme) can change what sits under the tab.
|
|
115
|
+
const mutations = new MutationObserver((records) => {
|
|
116
|
+
if (records.some((record) => record.target === body)) measure();
|
|
117
|
+
else schedule();
|
|
118
|
+
});
|
|
119
|
+
mutations.observe(body, { subtree: true, childList: true, attributes: true, attributeFilter: ["style", "data-gloom-top-surface"] });
|
|
120
|
+
return () => {
|
|
121
|
+
if (timer != null) clearTimeout(timer);
|
|
122
|
+
resize.disconnect();
|
|
123
|
+
mutations.disconnect();
|
|
124
|
+
};
|
|
125
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
126
|
+
}, [enabled, ...deps]);
|
|
127
|
+
}
|
|
@@ -507,6 +507,45 @@ button, [data-gloom-interactive="true"] { cursor: pointer; }
|
|
|
507
507
|
[data-gloom-role="tab-button"][data-active="true"]::after { opacity: 1; }
|
|
508
508
|
[data-gloom-role="tab-button"]:disabled { color: var(--gloom-text-muted); cursor: default; }
|
|
509
509
|
[data-gloom-role="tab-button"]:focus-visible { outline: none; }
|
|
510
|
+
/* Title-bar tabs: the same text, sizes and colours, drawn as raised boxes
|
|
511
|
+
instead of underlines. The active tab is a box with rounded top corners,
|
|
512
|
+
open at the bottom: its top and sides in the header rule's colour (the
|
|
513
|
+
accent while the pane is focused), its fill the surface under it, laid over
|
|
514
|
+
the rule so it opens into the pane. An inactive tab takes the hover fill in
|
|
515
|
+
the same shape. The pane header sets the colours (pane/header.tsx). */
|
|
516
|
+
[data-gloom-role="tab-list"][data-gloom-variant="header"] {
|
|
517
|
+
/* Header colour left above a box, and the box's line: the header rule's width. */
|
|
518
|
+
--pane-tab-top-gap: 2px;
|
|
519
|
+
--pane-tab-rule: 1px;
|
|
520
|
+
}
|
|
521
|
+
[data-gloom-role="tab-list"][data-gloom-variant="header"] > [data-gloom-role="tab-button"] {
|
|
522
|
+
height: calc(100% - var(--pane-tab-top-gap));
|
|
523
|
+
/* The sides take their line from the padding, so a label sits where it
|
|
524
|
+
would without the box. The gap and the top line push the label down; the
|
|
525
|
+
bottom padding lifts it back onto the header's centre line, halfway
|
|
526
|
+
between its top rule (a divider or a floating border, when there is one)
|
|
527
|
+
and its bottom rule, where the title sits. */
|
|
528
|
+
padding: 0 calc(var(--gloom-space-sm) - var(--pane-tab-rule)) calc(var(--pane-tab-top-gap) + 2 * var(--pane-tab-rule) - var(--pane-tab-top-inset, 0px));
|
|
529
|
+
border: var(--pane-tab-rule) solid transparent;
|
|
530
|
+
border-bottom: 0;
|
|
531
|
+
border-radius: var(--gloom-radius-md) var(--gloom-radius-md) 0 0;
|
|
532
|
+
/* The line lies on the bar, as the rule does, so a translucent hairline is
|
|
533
|
+
the same colour on both. */
|
|
534
|
+
background-clip: padding-box;
|
|
535
|
+
}
|
|
536
|
+
[data-gloom-role="tab-list"][data-gloom-variant="header"] > [data-gloom-role="tab-button"]::after { content: none; }
|
|
537
|
+
[data-gloom-role="tab-list"][data-gloom-variant="header"] > [data-gloom-role="tab-button"][data-active="true"] {
|
|
538
|
+
background-color: var(--pane-tab-active-bg, var(--gloom-bg));
|
|
539
|
+
border-color: var(--pane-tab-border, var(--gloom-hairline));
|
|
540
|
+
}
|
|
541
|
+
[data-gloom-role="tab-list"][data-gloom-variant="header"] > [data-gloom-role="tab-button"]:not([data-active="true"]):not(:disabled):is(:hover, :focus-visible, [data-drag-target="true"]) {
|
|
542
|
+
background-color: var(--gloom-hover-fill);
|
|
543
|
+
}
|
|
544
|
+
/* A tab's close mark stays inside the box: in a 20px title bar the box is
|
|
545
|
+
shorter than the icon button, whose fill would cover the box's top line. */
|
|
546
|
+
[data-gloom-role="tab-list"][data-gloom-variant="header"] [data-gloom-role="tab-close"] {
|
|
547
|
+
max-height: 100%;
|
|
548
|
+
}
|
|
510
549
|
/* A tab's close mark and a strip's plus are the icon button: 20 by 18, the control radius, the hover fill. */
|
|
511
550
|
[data-gloom-role="tab-add"] {
|
|
512
551
|
border: 0;
|
|
@@ -8,6 +8,7 @@ import type { AppServices } from "../../../core/app-services";
|
|
|
8
8
|
import { diskNotesFilesIO, isNotesFile } from "../../../plugins/builtin/notes/files";
|
|
9
9
|
import type { BrokerAdapter } from "../../../types/broker";
|
|
10
10
|
import type { AppConfig, BrokerInstanceConfig } from "../../../types/config";
|
|
11
|
+
import { errorMessage } from "../../../utils/errors";
|
|
11
12
|
|
|
12
13
|
const DESKTOP_CORE_PLUGIN_ID = "desktop-core";
|
|
13
14
|
|
|
@@ -221,7 +222,14 @@ function createNotesFilesCapability(): PluginCapability {
|
|
|
221
222
|
await io(input).delete(file(input));
|
|
222
223
|
return null;
|
|
223
224
|
}, "action"),
|
|
224
|
-
listKeys: op((input: any) =>
|
|
225
|
+
listKeys: op(async (input: any) => {
|
|
226
|
+
const skipped: Array<{ key: string; reason: string }> = [];
|
|
227
|
+
const keys = await io(input).listKeys({
|
|
228
|
+
strict: input.strict === true,
|
|
229
|
+
...(input.reportSkipped === true ? { skipped: (key: string, error: unknown) => skipped.push({ key, reason: errorMessage(error) }) } : {}),
|
|
230
|
+
});
|
|
231
|
+
return input.reportSkipped === true ? { keys, skipped } : keys;
|
|
232
|
+
}),
|
|
225
233
|
},
|
|
226
234
|
};
|
|
227
235
|
}
|
|
@@ -15,6 +15,8 @@ import { startRemoteControlServer, type RemoteControlServer } from "../../../../
|
|
|
15
15
|
import type { RemoteControlRequest, RemoteControlResponse } from "../../../../remote/types";
|
|
16
16
|
import { installCrashReporter } from "../../../../telemetry/crash-reports";
|
|
17
17
|
import { createNodeCrashReporterHost } from "../../../../telemetry/crash-reports-node";
|
|
18
|
+
import { installUpdateFailureReporter, reviewLastUpdateAttempt } from "../../../../telemetry/update-failures";
|
|
19
|
+
import { createNodeUpdateFailureHost } from "../../../../telemetry/update-failures-node";
|
|
18
20
|
import type { AppConfig } from "../../../../types/config";
|
|
19
21
|
import type { DesktopSharedStateSnapshot } from "../../../../types/desktop-window";
|
|
20
22
|
import type {
|
|
@@ -37,6 +39,7 @@ import {
|
|
|
37
39
|
} from "../external-plugins";
|
|
38
40
|
import { MAIN_WINDOW_RPC_KEY } from "../window/focus";
|
|
39
41
|
import { handleDesktopBackendRequest } from "./backend-requests";
|
|
42
|
+
import { detectDesktopInstallKind, removeUpdateAsides } from "./update";
|
|
40
43
|
import { DesktopCapabilityBridge } from "./capability-bridge";
|
|
41
44
|
import { DesktopDetachedWindowManager } from "./detached-windows";
|
|
42
45
|
import { handleDesktopHostRequest } from "./host-requests";
|
|
@@ -138,6 +141,13 @@ export class DesktopBackend {
|
|
|
138
141
|
if (!this.crashReporterInstalled) {
|
|
139
142
|
this.crashReporterInstalled = true;
|
|
140
143
|
installCrashReporter(createNodeCrashReporterHost({ surface: "desktop", getConfig: () => this.config }));
|
|
144
|
+
// This process runs the desktop update, so it reports how the last one ended and any that fail.
|
|
145
|
+
installUpdateFailureReporter(createNodeUpdateFailureHost({
|
|
146
|
+
installKind: detectDesktopInstallKind(),
|
|
147
|
+
getDataDir: () => this.config?.dataDir ?? null,
|
|
148
|
+
}));
|
|
149
|
+
reviewLastUpdateAttempt();
|
|
150
|
+
void removeUpdateAsides();
|
|
141
151
|
}
|
|
142
152
|
// The native menu shows the same accelerators the keys use, so a rebind
|
|
143
153
|
// rebuilds it.
|