@marckrenn/pi-sub-bar 1.0.0
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/CHANGELOG.md +12 -0
- package/README.md +194 -0
- package/index.ts +655 -0
- package/package.json +36 -0
- package/src/core-settings.ts +25 -0
- package/src/dividers.ts +48 -0
- package/src/errors.ts +71 -0
- package/src/formatting.ts +824 -0
- package/src/paths.ts +14 -0
- package/src/providers/extras.ts +21 -0
- package/src/providers/metadata.ts +137 -0
- package/src/providers/settings.ts +328 -0
- package/src/providers/windows.ts +18 -0
- package/src/settings/display.ts +695 -0
- package/src/settings/menu.ts +163 -0
- package/src/settings/themes.ts +318 -0
- package/src/settings/ui.ts +1064 -0
- package/src/settings-types.ts +542 -0
- package/src/settings-ui.ts +5 -0
- package/src/settings.ts +159 -0
- package/src/share.ts +56 -0
- package/src/status.ts +84 -0
- package/src/storage.ts +61 -0
- package/src/types.ts +19 -0
- package/src/ui/settings-list.ts +304 -0
- package/src/usage/types.ts +5 -0
- package/src/utils.ts +42 -0
- package/test/all.test.ts +4 -0
- package/test/dividers.test.ts +34 -0
- package/test/formatting.test.ts +329 -0
- package/test/providers.test.ts +42 -0
- package/test/settings.test.ts +211 -0
- package/tsconfig.json +5 -0
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Settings menu item builders.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type { SelectItem } from "@mariozechner/pi-tui";
|
|
6
|
+
import type { CoreProviderSettingsMap } from "@marckrenn/pi-sub-shared";
|
|
7
|
+
import type { Settings } from "../settings-types.js";
|
|
8
|
+
import type { ProviderName } from "../types.js";
|
|
9
|
+
import { PROVIDERS, PROVIDER_DISPLAY_NAMES } from "../providers/metadata.js";
|
|
10
|
+
|
|
11
|
+
export type TooltipSelectItem = SelectItem & { tooltip?: string };
|
|
12
|
+
|
|
13
|
+
export function buildMainMenuItems(settings: Settings, pinnedProvider?: ProviderName | null): TooltipSelectItem[] {
|
|
14
|
+
const pinnedLabel = pinnedProvider ? PROVIDER_DISPLAY_NAMES[pinnedProvider] : "auto (current provider)";
|
|
15
|
+
return [
|
|
16
|
+
{
|
|
17
|
+
value: "display",
|
|
18
|
+
label: "Display Settings",
|
|
19
|
+
description: "layout, bars, colors",
|
|
20
|
+
tooltip: "Adjust layout, colors, bar styling, status indicators, and dividers.",
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
value: "providers",
|
|
24
|
+
label: "Provider Settings",
|
|
25
|
+
description: `${Object.keys(settings.providers).length} providers`,
|
|
26
|
+
tooltip: "Configure provider display toggles and window visibility.",
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
value: "pin-provider",
|
|
30
|
+
label: "Provider Shown",
|
|
31
|
+
description: pinnedLabel,
|
|
32
|
+
tooltip: "Select which provider is shown in the widget.",
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
value: "open-core-settings",
|
|
36
|
+
label: "Additional settings",
|
|
37
|
+
description: "in /sub-core:settings",
|
|
38
|
+
tooltip: "Open /sub-core:settings for refresh behavior and provider enablement.",
|
|
39
|
+
},
|
|
40
|
+
];
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function buildProviderListItems(settings: Settings, coreProviders?: CoreProviderSettingsMap): TooltipSelectItem[] {
|
|
44
|
+
const orderedProviders = settings.providerOrder.length > 0 ? settings.providerOrder : PROVIDERS;
|
|
45
|
+
const items: TooltipSelectItem[] = orderedProviders.map((provider) => {
|
|
46
|
+
const ps = settings.providers[provider];
|
|
47
|
+
const core = coreProviders?.[provider];
|
|
48
|
+
const enabledValue = core
|
|
49
|
+
? core.enabled === "auto"
|
|
50
|
+
? "auto"
|
|
51
|
+
: core.enabled === true || core.enabled === "on"
|
|
52
|
+
? "on"
|
|
53
|
+
: "off"
|
|
54
|
+
: "auto";
|
|
55
|
+
const status = ps.showStatus ? "status on" : "status off";
|
|
56
|
+
return {
|
|
57
|
+
value: `provider-${provider}`,
|
|
58
|
+
label: PROVIDER_DISPLAY_NAMES[provider],
|
|
59
|
+
description: `enabled ${enabledValue}, ${status}`,
|
|
60
|
+
tooltip: `Configure ${PROVIDER_DISPLAY_NAMES[provider]} display settings.`,
|
|
61
|
+
};
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
items.push({
|
|
65
|
+
value: "reset-providers",
|
|
66
|
+
label: "Reset Provider Defaults",
|
|
67
|
+
description: "restore provider settings",
|
|
68
|
+
tooltip: "Restore provider display settings to their defaults.",
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
return items;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export function buildDisplayMenuItems(): TooltipSelectItem[] {
|
|
75
|
+
return [
|
|
76
|
+
{
|
|
77
|
+
value: "display-theme",
|
|
78
|
+
label: "Theme",
|
|
79
|
+
description: "save, manage, share",
|
|
80
|
+
tooltip: "Save or manage display themes.",
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
value: "display-layout",
|
|
84
|
+
label: "Layout & Structure",
|
|
85
|
+
description: "alignment, wrapping, padding",
|
|
86
|
+
tooltip: "Control alignment, wrapping, and padding.",
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
value: "display-bar",
|
|
90
|
+
label: "Bars",
|
|
91
|
+
description: "style, width, character",
|
|
92
|
+
tooltip: "Customize bar type, width, and bar styling.",
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
value: "display-provider",
|
|
96
|
+
label: "Labels & Text",
|
|
97
|
+
description: "labels, titles, usage text",
|
|
98
|
+
tooltip: "Adjust provider label visibility and text styling.",
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
value: "display-reset",
|
|
102
|
+
label: "Reset Timer",
|
|
103
|
+
description: "position, format, wrapping",
|
|
104
|
+
tooltip: "Control reset timer placement and formatting.",
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
value: "display-status",
|
|
108
|
+
label: "Status",
|
|
109
|
+
description: "mode, icons, text",
|
|
110
|
+
tooltip: "Configure status mode and icon packs.",
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
value: "display-divider",
|
|
114
|
+
label: "Dividers",
|
|
115
|
+
description: "character, blanks, status divider, lines",
|
|
116
|
+
tooltip: "Change divider character, spacing, status separator, and divider lines.",
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
value: "display-color",
|
|
120
|
+
label: "Colors",
|
|
121
|
+
description: "base, scheme, thresholds",
|
|
122
|
+
tooltip: "Tune base colors, color scheme, and thresholds.",
|
|
123
|
+
},
|
|
124
|
+
];
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export function buildDisplayThemeMenuItems(): TooltipSelectItem[] {
|
|
128
|
+
return [
|
|
129
|
+
{
|
|
130
|
+
value: "display-theme-save",
|
|
131
|
+
label: "Save current theme",
|
|
132
|
+
description: "store current theme",
|
|
133
|
+
tooltip: "Save the current display theme with a custom name.",
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
value: "display-theme-load",
|
|
137
|
+
label: "Load theme",
|
|
138
|
+
description: "restore or apply",
|
|
139
|
+
tooltip: "Load a saved or default theme.",
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
value: "display-theme-import",
|
|
143
|
+
label: "Import theme",
|
|
144
|
+
description: "from share string",
|
|
145
|
+
tooltip: "Import a shared theme string.",
|
|
146
|
+
},
|
|
147
|
+
{
|
|
148
|
+
value: "display-theme-random",
|
|
149
|
+
label: "Random theme",
|
|
150
|
+
description: "generate a new theme",
|
|
151
|
+
tooltip: "Generate a new random display theme.",
|
|
152
|
+
},
|
|
153
|
+
];
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
export function buildProviderSettingsItems(settings: Settings): TooltipSelectItem[] {
|
|
157
|
+
return buildProviderListItems(settings);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
export function getProviderFromCategory(category: string): ProviderName | null {
|
|
161
|
+
const match = category.match(/^provider-(\w+)$/);
|
|
162
|
+
return match ? (match[1] as ProviderName) : null;
|
|
163
|
+
}
|
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
import type { Settings } from "../settings-types.js";
|
|
2
|
+
import type { TooltipSelectItem } from "./menu.js";
|
|
3
|
+
|
|
4
|
+
type DisplaySettings = Settings["display"];
|
|
5
|
+
type BarType = DisplaySettings["barType"];
|
|
6
|
+
type BarStyle = DisplaySettings["barStyle"];
|
|
7
|
+
type BarCharacter = DisplaySettings["barCharacter"];
|
|
8
|
+
type BarWidth = DisplaySettings["barWidth"];
|
|
9
|
+
type DividerCharacter = DisplaySettings["dividerCharacter"];
|
|
10
|
+
type DividerBlanks = DisplaySettings["dividerBlanks"];
|
|
11
|
+
type DisplayAlignment = DisplaySettings["alignment"];
|
|
12
|
+
type WidgetWrapping = DisplaySettings["widgetWrapping"];
|
|
13
|
+
type BaseTextColor = DisplaySettings["baseTextColor"];
|
|
14
|
+
type DividerColor = DisplaySettings["dividerColor"];
|
|
15
|
+
type ResetTimeFormat = DisplaySettings["resetTimeFormat"];
|
|
16
|
+
type ResetTimerContainment = DisplaySettings["resetTimeContainment"];
|
|
17
|
+
type StatusIndicatorMode = DisplaySettings["statusIndicatorMode"];
|
|
18
|
+
type StatusIconPack = DisplaySettings["statusIconPack"];
|
|
19
|
+
type ProviderLabel = DisplaySettings["providerLabel"];
|
|
20
|
+
|
|
21
|
+
const RANDOM_BAR_TYPES: BarType[] = ["horizontal-bar", "horizontal-single", "vertical", "braille", "shade"];
|
|
22
|
+
const RANDOM_BAR_STYLES: BarStyle[] = ["bar", "percentage", "both"];
|
|
23
|
+
const RANDOM_BAR_WIDTHS: BarWidth[] = [1, 4, 6, 8, 10, 12, "fill"];
|
|
24
|
+
const RANDOM_BAR_CHARACTERS: BarCharacter[] = [
|
|
25
|
+
"light",
|
|
26
|
+
"heavy",
|
|
27
|
+
"double",
|
|
28
|
+
"block",
|
|
29
|
+
"▮▯",
|
|
30
|
+
"■□",
|
|
31
|
+
"●○",
|
|
32
|
+
"▲△",
|
|
33
|
+
"◆◇",
|
|
34
|
+
"🚀_",
|
|
35
|
+
];
|
|
36
|
+
const RANDOM_ALIGNMENTS: DisplayAlignment[] = ["left", "center", "right", "split"];
|
|
37
|
+
const RANDOM_WRAPPINGS: WidgetWrapping[] = ["truncate", "wrap"];
|
|
38
|
+
const RANDOM_RESET_POSITIONS: DisplaySettings["resetTimePosition"][] = ["off", "front", "back", "integrated"];
|
|
39
|
+
const RANDOM_RESET_FORMATS: ResetTimeFormat[] = ["relative", "datetime"];
|
|
40
|
+
const RANDOM_RESET_CONTAINMENTS: ResetTimerContainment[] = ["none", "blank", "()", "[]", "<>"];
|
|
41
|
+
const RANDOM_STATUS_MODES: StatusIndicatorMode[] = ["icon", "text", "icon+text"];
|
|
42
|
+
const RANDOM_STATUS_PACKS: StatusIconPack[] = ["minimal", "emoji"];
|
|
43
|
+
const RANDOM_PROVIDER_LABELS: ProviderLabel[] = ["plan", "subscription", "sub", "none"];
|
|
44
|
+
const RANDOM_DIVIDER_CHARACTERS: DividerCharacter[] = ["none", "blank", "|", "│", "┃", "┆", "┇", "║", "•", "●", "○", "◇"];
|
|
45
|
+
const RANDOM_DIVIDER_BLANKS: DividerBlanks[] = [0, 1, 2, 3];
|
|
46
|
+
const RANDOM_COLOR_SCHEMES: DisplaySettings["colorScheme"][] = [
|
|
47
|
+
"base-warning-error",
|
|
48
|
+
"success-base-warning-error",
|
|
49
|
+
"monochrome",
|
|
50
|
+
];
|
|
51
|
+
const RANDOM_BASE_TEXT_COLORS: BaseTextColor[] = ["dim", "muted", "text", "primary", "success", "warning", "error", "border", "borderMuted"];
|
|
52
|
+
const RANDOM_BACKGROUND_COLORS: BaseTextColor[] = [
|
|
53
|
+
"text",
|
|
54
|
+
"selectedBg",
|
|
55
|
+
"userMessageBg",
|
|
56
|
+
"customMessageBg",
|
|
57
|
+
"toolPendingBg",
|
|
58
|
+
"toolSuccessBg",
|
|
59
|
+
"toolErrorBg",
|
|
60
|
+
];
|
|
61
|
+
const RANDOM_DIVIDER_COLORS: DividerColor[] = [
|
|
62
|
+
"primary",
|
|
63
|
+
"text",
|
|
64
|
+
"muted",
|
|
65
|
+
"dim",
|
|
66
|
+
"success",
|
|
67
|
+
"warning",
|
|
68
|
+
"error",
|
|
69
|
+
"border",
|
|
70
|
+
"borderMuted",
|
|
71
|
+
"borderAccent",
|
|
72
|
+
];
|
|
73
|
+
const RANDOM_PADDING: number[] = [0, 1, 2, 3, 4];
|
|
74
|
+
|
|
75
|
+
function pickRandom<T>(items: readonly T[]): T {
|
|
76
|
+
return items[Math.floor(Math.random() * items.length)] ?? items[0]!;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function randomBool(probability = 0.5): boolean {
|
|
80
|
+
return Math.random() < probability;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const THEME_ID_LENGTH = 24;
|
|
84
|
+
const THEME_ID_FALLBACK = "theme";
|
|
85
|
+
|
|
86
|
+
function buildThemeId(name: string): string {
|
|
87
|
+
return name.toLowerCase().replace(/[^a-z0-9_-]+/g, "-").slice(0, THEME_ID_LENGTH) || THEME_ID_FALLBACK;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export interface DisplayThemeTarget {
|
|
91
|
+
id?: string;
|
|
92
|
+
name: string;
|
|
93
|
+
display: Settings["display"];
|
|
94
|
+
deletable: boolean;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export function buildDisplayThemeItems(
|
|
98
|
+
settings: Settings,
|
|
99
|
+
): TooltipSelectItem[] {
|
|
100
|
+
const items: TooltipSelectItem[] = [];
|
|
101
|
+
items.push({
|
|
102
|
+
value: "user",
|
|
103
|
+
label: "Restore backup",
|
|
104
|
+
description: "restore your last theme",
|
|
105
|
+
tooltip: "Restore your previous display theme.",
|
|
106
|
+
});
|
|
107
|
+
items.push({
|
|
108
|
+
value: "default",
|
|
109
|
+
label: "Default",
|
|
110
|
+
description: "restore default settings",
|
|
111
|
+
tooltip: "Reset display settings to defaults.",
|
|
112
|
+
});
|
|
113
|
+
items.push({
|
|
114
|
+
value: "minimal",
|
|
115
|
+
label: "Default Minimal",
|
|
116
|
+
description: "compact display",
|
|
117
|
+
tooltip: "Apply the default minimal theme.",
|
|
118
|
+
});
|
|
119
|
+
for (const theme of settings.displayThemes) {
|
|
120
|
+
const description = theme.source === "imported" ? "manually imported theme" : "manually saved theme";
|
|
121
|
+
items.push({
|
|
122
|
+
value: `theme:${theme.id}`,
|
|
123
|
+
label: theme.name,
|
|
124
|
+
description,
|
|
125
|
+
tooltip: `Manage ${theme.name}.`,
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
return items;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export function resolveDisplayThemeTarget(
|
|
132
|
+
value: string,
|
|
133
|
+
settings: Settings,
|
|
134
|
+
defaults: Settings,
|
|
135
|
+
fallbackUser: Settings["display"] | null,
|
|
136
|
+
): DisplayThemeTarget | null {
|
|
137
|
+
if (value === "user") {
|
|
138
|
+
const display = settings.displayUserTheme ?? fallbackUser ?? settings.display;
|
|
139
|
+
return { name: "Restore backup", display, deletable: false };
|
|
140
|
+
}
|
|
141
|
+
if (value === "default") {
|
|
142
|
+
return { name: "Default", display: { ...defaults.display }, deletable: false };
|
|
143
|
+
}
|
|
144
|
+
if (value === "minimal") {
|
|
145
|
+
return {
|
|
146
|
+
name: "Default Minimal",
|
|
147
|
+
display: {
|
|
148
|
+
...defaults.display,
|
|
149
|
+
alignment: "split",
|
|
150
|
+
barStyle: "percentage",
|
|
151
|
+
barType: "horizontal-bar",
|
|
152
|
+
barWidth: 1,
|
|
153
|
+
barCharacter: "heavy",
|
|
154
|
+
containBar: true,
|
|
155
|
+
brailleFillEmpty: false,
|
|
156
|
+
brailleFullBlocks: false,
|
|
157
|
+
colorScheme: "base-warning-error",
|
|
158
|
+
usageColorTargets: {
|
|
159
|
+
title: true,
|
|
160
|
+
timer: true,
|
|
161
|
+
bar: true,
|
|
162
|
+
usageLabel: true,
|
|
163
|
+
status: true,
|
|
164
|
+
},
|
|
165
|
+
resetTimePosition: "off",
|
|
166
|
+
resetTimeFormat: "relative",
|
|
167
|
+
resetTimeContainment: "blank",
|
|
168
|
+
statusIndicatorMode: "icon",
|
|
169
|
+
statusIconPack: "minimal",
|
|
170
|
+
statusProviderDivider: false,
|
|
171
|
+
statusDismissOk: true,
|
|
172
|
+
showProviderName: false,
|
|
173
|
+
providerLabel: "none",
|
|
174
|
+
providerLabelColon: false,
|
|
175
|
+
providerLabelBold: true,
|
|
176
|
+
baseTextColor: "muted",
|
|
177
|
+
backgroundColor: "text",
|
|
178
|
+
showWindowTitle: false,
|
|
179
|
+
boldWindowTitle: true,
|
|
180
|
+
showUsageLabels: false,
|
|
181
|
+
dividerCharacter: "none",
|
|
182
|
+
dividerColor: "dim",
|
|
183
|
+
dividerBlanks: 1,
|
|
184
|
+
showProviderDivider: true,
|
|
185
|
+
dividerFooterJoin: true,
|
|
186
|
+
showTopDivider: false,
|
|
187
|
+
showBottomDivider: false,
|
|
188
|
+
paddingX: 1,
|
|
189
|
+
widgetPlacement: "belowEditor",
|
|
190
|
+
errorThreshold: 25,
|
|
191
|
+
warningThreshold: 50,
|
|
192
|
+
widgetWrapping: "truncate",
|
|
193
|
+
successThreshold: 75,
|
|
194
|
+
},
|
|
195
|
+
deletable: false,
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
if (value.startsWith("theme:")) {
|
|
199
|
+
const id = value.replace("theme:", "");
|
|
200
|
+
const theme = settings.displayThemes.find((entry) => entry.id === id);
|
|
201
|
+
if (!theme) return null;
|
|
202
|
+
return { id: theme.id, name: theme.name, display: theme.display, deletable: true };
|
|
203
|
+
}
|
|
204
|
+
return null;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
export function buildRandomDisplay(base: DisplaySettings): DisplaySettings {
|
|
208
|
+
const display: DisplaySettings = { ...base };
|
|
209
|
+
|
|
210
|
+
display.alignment = pickRandom(RANDOM_ALIGNMENTS);
|
|
211
|
+
display.widgetWrapping = pickRandom(RANDOM_WRAPPINGS);
|
|
212
|
+
display.paddingX = pickRandom(RANDOM_PADDING);
|
|
213
|
+
display.barStyle = pickRandom(RANDOM_BAR_STYLES);
|
|
214
|
+
display.barType = pickRandom(RANDOM_BAR_TYPES);
|
|
215
|
+
display.barWidth = pickRandom(RANDOM_BAR_WIDTHS);
|
|
216
|
+
display.barCharacter = pickRandom(RANDOM_BAR_CHARACTERS);
|
|
217
|
+
display.containBar = randomBool();
|
|
218
|
+
display.brailleFillEmpty = randomBool();
|
|
219
|
+
display.brailleFullBlocks = randomBool();
|
|
220
|
+
display.colorScheme = pickRandom(RANDOM_COLOR_SCHEMES);
|
|
221
|
+
|
|
222
|
+
const usageColorTargets = {
|
|
223
|
+
title: randomBool(),
|
|
224
|
+
timer: randomBool(),
|
|
225
|
+
bar: randomBool(),
|
|
226
|
+
usageLabel: randomBool(),
|
|
227
|
+
status: randomBool(),
|
|
228
|
+
};
|
|
229
|
+
if (!usageColorTargets.title && !usageColorTargets.timer && !usageColorTargets.bar && !usageColorTargets.usageLabel && !usageColorTargets.status) {
|
|
230
|
+
usageColorTargets.bar = true;
|
|
231
|
+
}
|
|
232
|
+
display.usageColorTargets = usageColorTargets;
|
|
233
|
+
display.resetTimePosition = pickRandom(RANDOM_RESET_POSITIONS);
|
|
234
|
+
display.resetTimeFormat = pickRandom(RANDOM_RESET_FORMATS);
|
|
235
|
+
display.resetTimeContainment = pickRandom(RANDOM_RESET_CONTAINMENTS);
|
|
236
|
+
display.statusIndicatorMode = pickRandom(RANDOM_STATUS_MODES);
|
|
237
|
+
display.statusIconPack = pickRandom(RANDOM_STATUS_PACKS);
|
|
238
|
+
display.statusProviderDivider = randomBool();
|
|
239
|
+
display.statusDismissOk = randomBool();
|
|
240
|
+
display.showProviderName = randomBool();
|
|
241
|
+
display.providerLabel = pickRandom(RANDOM_PROVIDER_LABELS);
|
|
242
|
+
display.providerLabelColon = display.providerLabel !== "none" && randomBool();
|
|
243
|
+
display.providerLabelBold = randomBool();
|
|
244
|
+
display.baseTextColor = pickRandom(RANDOM_BASE_TEXT_COLORS);
|
|
245
|
+
display.backgroundColor = pickRandom(RANDOM_BACKGROUND_COLORS);
|
|
246
|
+
display.boldWindowTitle = randomBool();
|
|
247
|
+
display.showUsageLabels = randomBool();
|
|
248
|
+
display.dividerCharacter = pickRandom(RANDOM_DIVIDER_CHARACTERS);
|
|
249
|
+
display.dividerColor = pickRandom(RANDOM_DIVIDER_COLORS);
|
|
250
|
+
display.dividerBlanks = pickRandom(RANDOM_DIVIDER_BLANKS);
|
|
251
|
+
display.showProviderDivider = randomBool();
|
|
252
|
+
display.dividerFooterJoin = randomBool();
|
|
253
|
+
display.showTopDivider = randomBool();
|
|
254
|
+
display.showBottomDivider = randomBool();
|
|
255
|
+
|
|
256
|
+
if (display.dividerCharacter === "none") {
|
|
257
|
+
display.showProviderDivider = false;
|
|
258
|
+
display.dividerFooterJoin = false;
|
|
259
|
+
display.showTopDivider = false;
|
|
260
|
+
display.showBottomDivider = false;
|
|
261
|
+
}
|
|
262
|
+
if (display.providerLabel === "none") {
|
|
263
|
+
display.providerLabelColon = false;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
return display;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
export function buildThemeActionItems(target: DisplayThemeTarget): TooltipSelectItem[] {
|
|
270
|
+
const items: TooltipSelectItem[] = [
|
|
271
|
+
{
|
|
272
|
+
value: "load",
|
|
273
|
+
label: "Load",
|
|
274
|
+
description: "apply this theme",
|
|
275
|
+
tooltip: "Apply the selected theme.",
|
|
276
|
+
},
|
|
277
|
+
{
|
|
278
|
+
value: "share",
|
|
279
|
+
label: "Share",
|
|
280
|
+
description: "post share string",
|
|
281
|
+
tooltip: "Post a shareable theme string to chat.",
|
|
282
|
+
},
|
|
283
|
+
];
|
|
284
|
+
if (target.deletable) {
|
|
285
|
+
items.push({
|
|
286
|
+
value: "delete",
|
|
287
|
+
label: "Delete",
|
|
288
|
+
description: "remove saved theme",
|
|
289
|
+
tooltip: "Remove this theme from saved themes.",
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
return items;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
export function upsertDisplayTheme(
|
|
296
|
+
settings: Settings,
|
|
297
|
+
name: string,
|
|
298
|
+
display: Settings["display"],
|
|
299
|
+
source?: "saved" | "imported",
|
|
300
|
+
): Settings {
|
|
301
|
+
const trimmed = name.trim() || "Theme";
|
|
302
|
+
const id = buildThemeId(trimmed);
|
|
303
|
+
const snapshot = { ...display };
|
|
304
|
+
const existing = settings.displayThemes.find((theme) => theme.id === id);
|
|
305
|
+
const resolvedSource = source ?? existing?.source ?? "saved";
|
|
306
|
+
if (existing) {
|
|
307
|
+
existing.name = trimmed;
|
|
308
|
+
existing.display = snapshot;
|
|
309
|
+
existing.source = resolvedSource;
|
|
310
|
+
} else {
|
|
311
|
+
settings.displayThemes.push({ id, name: trimmed, display: snapshot, source: resolvedSource });
|
|
312
|
+
}
|
|
313
|
+
return settings;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
export function saveDisplayTheme(settings: Settings, name: string): Settings {
|
|
317
|
+
return upsertDisplayTheme(settings, name, settings.display, "saved");
|
|
318
|
+
}
|