@domicile-desktop/sdk 0.0.0-alpha-e77cfff77793 → 0.0.0-alpha-5166c4cfb4be
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 +1 -0
- package/config.schema.json +6 -0
- package/dist/config.d.ts +6 -0
- package/dist/domicile-host.d.ts +8 -0
- package/dist/fake-host.js +2 -0
- package/dist/options.d.ts +118 -0
- package/dist/options.js +224 -0
- package/package.json +5 -1
package/README.md
CHANGED
|
@@ -63,6 +63,7 @@ See [docs/ELEMENTS.md](docs/ELEMENTS.md) for `<app>` input and focus, and the
|
|
|
63
63
|
| `./system` | `system(domicile)`: files, watches, processes and D-Bus. Calls resolve a `Result`; the compositor refuses most while the desktop is locked. See [SHELL-SYSTEM-ACCESS.md](/docs/SHELL-SYSTEM-ACCESS.md). |
|
|
64
64
|
| `./portal` | `watchPortalRequests` and `answerPortalRequest`: application dialogs from `xdg-desktop-portal`, parsed by kind. `watchCapturing` and `stopCapturing`: running sessions that record the screen or control or capture input. `<PortalDialogs />` in component-library draws them. See [PORTALS.md](/docs/PORTALS.md). |
|
|
65
65
|
| `./global-shortcuts` | `fireGlobalShortcuts`: grab the chords applications hold through the GlobalShortcuts portal and report each press and release. `<PortalDialogs />` calls it. |
|
|
66
|
+
| `./options` | `defineOptions` and `option`: the options a shell declares as its `options` export, which the Settings app edits. `watchShellOptions` reads the config's values of them, live. See [WRITING-A-SHELL.md](/docs/WRITING-A-SHELL.md#options). |
|
|
66
67
|
| `./appearance` | `watchAppearance`: the config's accent color, contrast and reduced motion, which the Settings portal also serves applications. See [SHELL-CONFIG.md](/docs/SHELL-CONFIG.md#theme). |
|
|
67
68
|
| `./icon-theme` | `readIconTheme(system)`: the config's `theme.icon_theme`, read from the compositor's Settings portal, or `None` for `hicolor` only. `watchIconTheme` reads it and follows `SettingChanged`. `@domicile-desktop/system-apps/app-icons` takes it. |
|
|
68
69
|
| `./extension`, `./tray`, `./notification`, `./theme`, `./display-transform` | Data types and Zod schemas for what the desktop holds. |
|
package/config.schema.json
CHANGED
|
@@ -503,6 +503,12 @@
|
|
|
503
503
|
"description": "The shell `domicile` runs when given none: a path or a package, as\n`domicile load-shell` takes. The compositor ignores it.",
|
|
504
504
|
"type": ["string", "null"]
|
|
505
505
|
},
|
|
506
|
+
"shell_options": {
|
|
507
|
+
"additionalProperties": true,
|
|
508
|
+
"default": {},
|
|
509
|
+
"description": "The values of the options the shell declares, by key. The compositor\nsends them to the shell without reading them. See\n`docs/WRITING-A-SHELL.md#options`.",
|
|
510
|
+
"type": "object"
|
|
511
|
+
},
|
|
506
512
|
"startup": {
|
|
507
513
|
"$ref": "#/$defs/StartupConfig"
|
|
508
514
|
},
|
package/dist/config.d.ts
CHANGED
|
@@ -14,6 +14,12 @@ export type Config = {
|
|
|
14
14
|
* `domicile load-shell` takes. The compositor ignores it.
|
|
15
15
|
*/
|
|
16
16
|
shell?: string | null;
|
|
17
|
+
/**
|
|
18
|
+
* The values of the options the shell declares, by key. The compositor
|
|
19
|
+
* sends them to the shell without reading them. See
|
|
20
|
+
* `docs/WRITING-A-SHELL.md#options`.
|
|
21
|
+
*/
|
|
22
|
+
shell_options?: Record<string, unknown>;
|
|
17
23
|
startup?: StartupConfig;
|
|
18
24
|
theme?: ThemeConfig;
|
|
19
25
|
};
|
package/dist/domicile-host.d.ts
CHANGED
|
@@ -317,6 +317,7 @@ export type DomicileHostEventMap = {
|
|
|
317
317
|
windowsthemechanged: Event;
|
|
318
318
|
modifierschanged: Event;
|
|
319
319
|
appearancechanged: Event;
|
|
320
|
+
shelloptionschanged: Event;
|
|
320
321
|
/**
|
|
321
322
|
* Fires when a window opens, closes, navigates or changes title. Read
|
|
322
323
|
* `browserWindows` for the current list.
|
|
@@ -638,6 +639,13 @@ export type DomicileHost = {
|
|
|
638
639
|
readonly accentColor: string | null;
|
|
639
640
|
readonly highContrast: boolean | null;
|
|
640
641
|
readonly reducedMotion: boolean | null;
|
|
642
|
+
/**
|
|
643
|
+
* The config's `shell_options` as JSON text: the values of the options the
|
|
644
|
+
* shell declares. Null until the compositor says. Text rather than an
|
|
645
|
+
* object, so the page parses it against its own declaration; see the SDK's
|
|
646
|
+
* `watchShellOptions`.
|
|
647
|
+
*/
|
|
648
|
+
readonly shellOptions: string | null;
|
|
641
649
|
/**
|
|
642
650
|
* The desk's browser windows, oldest first. Null until the browser lists
|
|
643
651
|
* them, which it does as soon as this page listens. The browser holds each
|
package/dist/fake-host.js
CHANGED
|
@@ -24,6 +24,7 @@ const CHANGED = {
|
|
|
24
24
|
metaKey: "modifierschanged",
|
|
25
25
|
notifications: "notificationschanged",
|
|
26
26
|
reducedMotion: "appearancechanged",
|
|
27
|
+
shellOptions: "shelloptionschanged",
|
|
27
28
|
shiftKey: "modifierschanged",
|
|
28
29
|
theme: "themechanged",
|
|
29
30
|
tray: "traychanged",
|
|
@@ -46,6 +47,7 @@ const UNDESCRIBED = {
|
|
|
46
47
|
metaKey: null,
|
|
47
48
|
notifications: null,
|
|
48
49
|
reducedMotion: null,
|
|
50
|
+
shellOptions: null,
|
|
49
51
|
shiftKey: null,
|
|
50
52
|
theme: null,
|
|
51
53
|
tray: null,
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import type { DomicileHost } from "./domicile-host";
|
|
3
|
+
declare const toggleSchema: z.ZodObject<{
|
|
4
|
+
default: z.ZodBoolean;
|
|
5
|
+
kind: z.ZodLiteral<"toggle">;
|
|
6
|
+
label: z.ZodString;
|
|
7
|
+
}, z.core.$strict>;
|
|
8
|
+
declare const choiceSchema: z.ZodObject<{
|
|
9
|
+
choices: z.ZodArray<z.ZodString>;
|
|
10
|
+
default: z.ZodString;
|
|
11
|
+
kind: z.ZodLiteral<"choice">;
|
|
12
|
+
label: z.ZodString;
|
|
13
|
+
}, z.core.$strict>;
|
|
14
|
+
declare const numberSchema: z.ZodObject<{
|
|
15
|
+
default: z.ZodNumber;
|
|
16
|
+
kind: z.ZodLiteral<"number">;
|
|
17
|
+
label: z.ZodString;
|
|
18
|
+
max: z.ZodOptional<z.ZodNumber>;
|
|
19
|
+
min: z.ZodOptional<z.ZodNumber>;
|
|
20
|
+
}, z.core.$strict>;
|
|
21
|
+
declare const textSchema: z.ZodObject<{
|
|
22
|
+
default: z.ZodString;
|
|
23
|
+
kind: z.ZodLiteral<"text">;
|
|
24
|
+
label: z.ZodString;
|
|
25
|
+
}, z.core.$strict>;
|
|
26
|
+
declare const listSchema: z.ZodObject<{
|
|
27
|
+
default: z.ZodArray<z.ZodString>;
|
|
28
|
+
kind: z.ZodLiteral<"list">;
|
|
29
|
+
label: z.ZodString;
|
|
30
|
+
}, z.core.$strict>;
|
|
31
|
+
/** A switch. Its value is `true` or `false`. */
|
|
32
|
+
export type ToggleOption = z.infer<typeof toggleSchema>;
|
|
33
|
+
/** One of a list of words. */
|
|
34
|
+
export type ChoiceOption<C extends string = string> = Omit<z.infer<typeof choiceSchema>, "choices" | "default"> & {
|
|
35
|
+
readonly choices: readonly C[];
|
|
36
|
+
readonly default: C;
|
|
37
|
+
};
|
|
38
|
+
/** A number, within `min` and `max` when they are given. */
|
|
39
|
+
export type NumberOption = z.infer<typeof numberSchema>;
|
|
40
|
+
/** A line of text. */
|
|
41
|
+
export type TextOption = z.infer<typeof textSchema>;
|
|
42
|
+
/** A list of lines of text. */
|
|
43
|
+
export type ListOption = Omit<z.infer<typeof listSchema>, "default"> & {
|
|
44
|
+
readonly default: readonly string[];
|
|
45
|
+
};
|
|
46
|
+
/** One option a shell reads. */
|
|
47
|
+
export type Option = ToggleOption | ChoiceOption | NumberOption | TextOption | ListOption;
|
|
48
|
+
/** Options under one key, as `clock: { format, seconds }`. */
|
|
49
|
+
export type GroupOption<T extends Options = Options> = {
|
|
50
|
+
readonly kind: "group";
|
|
51
|
+
readonly options: T;
|
|
52
|
+
};
|
|
53
|
+
/** A shell's declared options, by key: what `options.json` holds. */
|
|
54
|
+
export type Options = {
|
|
55
|
+
readonly [key: string]: Option | GroupOption;
|
|
56
|
+
};
|
|
57
|
+
/** What `defineOptions` takes: options, and plain objects of them as groups. */
|
|
58
|
+
export type OptionsInput = {
|
|
59
|
+
readonly [key: string]: Option | OptionsInput;
|
|
60
|
+
};
|
|
61
|
+
/** The declaration `defineOptions` makes of `T`. */
|
|
62
|
+
export type Declared<T extends OptionsInput> = {
|
|
63
|
+
readonly [K in keyof T]: T[K] extends Option ? T[K] : T[K] extends OptionsInput ? GroupOption<Declared<T[K]>> : never;
|
|
64
|
+
};
|
|
65
|
+
/** The value each option in `T` has. */
|
|
66
|
+
export type OptionValues<T extends Options> = {
|
|
67
|
+
readonly [K in keyof T]: OptionValue<T[K]>;
|
|
68
|
+
};
|
|
69
|
+
/** The value an option, or a group of them, has. */
|
|
70
|
+
export type OptionValue<E> = E extends GroupOption<infer G> ? OptionValues<G> : E extends ToggleOption ? boolean : E extends ChoiceOption<infer C> ? C : E extends NumberOption ? number : E extends TextOption ? string : E extends ListOption ? readonly string[] : never;
|
|
71
|
+
/** The values a config gives, and why any it gave were dropped. */
|
|
72
|
+
export type ReadOptions<T extends Options> = {
|
|
73
|
+
readonly values: OptionValues<T>;
|
|
74
|
+
/** One line per value dropped: where it is, and what is wrong with it. */
|
|
75
|
+
readonly dropped: readonly string[];
|
|
76
|
+
};
|
|
77
|
+
/** `options.json`: a shell's declared options, by key. */
|
|
78
|
+
export declare const optionsSchema: z.ZodType<Options>;
|
|
79
|
+
/** The option kinds, each with a label and a default. */
|
|
80
|
+
export declare const option: {
|
|
81
|
+
choice: <const C extends string>(label: string, choices: readonly C[], defaultChoice: C) => ChoiceOption<C>;
|
|
82
|
+
list: (label: string, defaultList: readonly string[]) => ListOption;
|
|
83
|
+
number: (label: string, defaultNumber: number, limits?: {
|
|
84
|
+
readonly min?: number;
|
|
85
|
+
readonly max?: number;
|
|
86
|
+
}) => NumberOption;
|
|
87
|
+
text: (label: string, defaultText: string) => TextOption;
|
|
88
|
+
toggle: (label: string, defaultValue: boolean) => ToggleOption;
|
|
89
|
+
};
|
|
90
|
+
/**
|
|
91
|
+
* A shell's options, for its `options` export. A plain object of options is a
|
|
92
|
+
* group. Throws on a default its own choices or limits refuse.
|
|
93
|
+
*/
|
|
94
|
+
export declare const defineOptions: <const T extends OptionsInput>(input: T) => Declared<T>;
|
|
95
|
+
/**
|
|
96
|
+
* The value of every option in `declared`: the config's where it gives a good
|
|
97
|
+
* one, the default elsewhere.
|
|
98
|
+
*/
|
|
99
|
+
export declare const readOptions: <T extends Options>(declared: T, given: Readonly<Record<string, unknown>> | undefined) => ReadOptions<T>;
|
|
100
|
+
/**
|
|
101
|
+
* The value `given` sets for `option`, or its default where it sets none or
|
|
102
|
+
* one `option` refuses.
|
|
103
|
+
*/
|
|
104
|
+
export declare const readOption: <O extends Option>(option: O, given: unknown) => OptionValue<O>;
|
|
105
|
+
/** What is wrong with `value` for `option`, or `undefined` if nothing is. */
|
|
106
|
+
export declare const optionProblem: (option: Option, value: unknown) => string | undefined;
|
|
107
|
+
/** What `watchShellOptions` needs of the desktop `Shell` is handed. */
|
|
108
|
+
export type ShellOptionsHost = Pick<DomicileHost, "addEventListener" | "removeEventListener" | "shellOptions">;
|
|
109
|
+
/**
|
|
110
|
+
* Call `listener` with the values of `declared` now and each time the config
|
|
111
|
+
* changes them. Returns a function that stops listening.
|
|
112
|
+
*
|
|
113
|
+
* A value the config gives that `declared` refuses is dropped, takes its
|
|
114
|
+
* default and is passed to `report`, so the shell starts whatever the config
|
|
115
|
+
* says.
|
|
116
|
+
*/
|
|
117
|
+
export declare const watchShellOptions: <T extends Options>(host: ShellOptionsHost, declared: T, listener: (values: OptionValues<T>) => void, report?: (dropped: string) => void) => (() => void);
|
|
118
|
+
export {};
|
package/dist/options.js
ADDED
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
// The options a shell declares, their JSON form and the values a config gives
|
|
2
|
+
// them. The shell exports the declaration as `options`; the builder writes it
|
|
3
|
+
// to `options.json`, the Settings app draws a form from it, and the config's
|
|
4
|
+
// `shell_options` holds the values. See `docs/architecture/SHELL-OPTIONS.md`.
|
|
5
|
+
import { z } from "zod";
|
|
6
|
+
const label = z.string().min(1);
|
|
7
|
+
const toggleSchema = z.strictObject({
|
|
8
|
+
default: z.boolean(),
|
|
9
|
+
kind: z.literal("toggle"),
|
|
10
|
+
label,
|
|
11
|
+
});
|
|
12
|
+
const choiceSchema = z
|
|
13
|
+
.strictObject({
|
|
14
|
+
choices: z.array(z.string()).min(1),
|
|
15
|
+
default: z.string(),
|
|
16
|
+
kind: z.literal("choice"),
|
|
17
|
+
label,
|
|
18
|
+
})
|
|
19
|
+
.refine((choice) => choice.choices.includes(choice.default), {
|
|
20
|
+
message: "the default is not one of the choices",
|
|
21
|
+
});
|
|
22
|
+
const numberSchema = z
|
|
23
|
+
.strictObject({
|
|
24
|
+
default: z.number(),
|
|
25
|
+
kind: z.literal("number"),
|
|
26
|
+
label,
|
|
27
|
+
max: z.number().optional(),
|
|
28
|
+
min: z.number().optional(),
|
|
29
|
+
})
|
|
30
|
+
.refine((number) => numberProblem(number, number.default) === undefined, {
|
|
31
|
+
message: "the default is outside the limits",
|
|
32
|
+
});
|
|
33
|
+
const textSchema = z.strictObject({
|
|
34
|
+
default: z.string(),
|
|
35
|
+
kind: z.literal("text"),
|
|
36
|
+
label,
|
|
37
|
+
});
|
|
38
|
+
const listSchema = z.strictObject({
|
|
39
|
+
default: z.array(z.string()),
|
|
40
|
+
kind: z.literal("list"),
|
|
41
|
+
label,
|
|
42
|
+
});
|
|
43
|
+
const optionSchema = z.union([
|
|
44
|
+
toggleSchema,
|
|
45
|
+
choiceSchema,
|
|
46
|
+
numberSchema,
|
|
47
|
+
textSchema,
|
|
48
|
+
listSchema,
|
|
49
|
+
]);
|
|
50
|
+
const groupSchema = z.strictObject({
|
|
51
|
+
kind: z.literal("group"),
|
|
52
|
+
options: z.lazy(() => optionsSchema),
|
|
53
|
+
});
|
|
54
|
+
/** `options.json`: a shell's declared options, by key. */
|
|
55
|
+
export const optionsSchema = z.record(z.string(), z.union([optionSchema, groupSchema]));
|
|
56
|
+
/** The option kinds, each with a label and a default. */
|
|
57
|
+
export const option = {
|
|
58
|
+
choice: (label, choices, defaultChoice) => ({
|
|
59
|
+
choices,
|
|
60
|
+
default: defaultChoice,
|
|
61
|
+
kind: "choice",
|
|
62
|
+
label,
|
|
63
|
+
}),
|
|
64
|
+
list: (label, defaultList) => ({
|
|
65
|
+
default: defaultList,
|
|
66
|
+
kind: "list",
|
|
67
|
+
label,
|
|
68
|
+
}),
|
|
69
|
+
number: (label, defaultNumber, limits = {}) => ({
|
|
70
|
+
default: defaultNumber,
|
|
71
|
+
kind: "number",
|
|
72
|
+
label,
|
|
73
|
+
...limits,
|
|
74
|
+
}),
|
|
75
|
+
text: (label, defaultText) => ({
|
|
76
|
+
default: defaultText,
|
|
77
|
+
kind: "text",
|
|
78
|
+
label,
|
|
79
|
+
}),
|
|
80
|
+
toggle: (label, defaultValue) => ({
|
|
81
|
+
default: defaultValue,
|
|
82
|
+
kind: "toggle",
|
|
83
|
+
label,
|
|
84
|
+
}),
|
|
85
|
+
};
|
|
86
|
+
/**
|
|
87
|
+
* A shell's options, for its `options` export. A plain object of options is a
|
|
88
|
+
* group. Throws on a default its own choices or limits refuse.
|
|
89
|
+
*/
|
|
90
|
+
export const defineOptions = (input) => {
|
|
91
|
+
const declared = declare(input);
|
|
92
|
+
optionsSchema.parse(declared);
|
|
93
|
+
return declared;
|
|
94
|
+
};
|
|
95
|
+
/**
|
|
96
|
+
* The value of every option in `declared`: the config's where it gives a good
|
|
97
|
+
* one, the default elsewhere.
|
|
98
|
+
*/
|
|
99
|
+
export const readOptions = (declared, given) => {
|
|
100
|
+
const { dropped, values } = readGroup(declared, given ?? {}, "");
|
|
101
|
+
return {
|
|
102
|
+
dropped,
|
|
103
|
+
// The values follow `declared` key by key, which the type cannot see.
|
|
104
|
+
values: values,
|
|
105
|
+
};
|
|
106
|
+
};
|
|
107
|
+
/**
|
|
108
|
+
* The value `given` sets for `option`, or its default where it sets none or
|
|
109
|
+
* one `option` refuses.
|
|
110
|
+
*/
|
|
111
|
+
export const readOption = (option, given) =>
|
|
112
|
+
// Checked against `option` just here, which the type cannot see.
|
|
113
|
+
(given !== undefined && optionProblem(option, given) === undefined
|
|
114
|
+
? given
|
|
115
|
+
: option.default);
|
|
116
|
+
/** What is wrong with `value` for `option`, or `undefined` if nothing is. */
|
|
117
|
+
export const optionProblem = (option, value) => {
|
|
118
|
+
switch (option.kind) {
|
|
119
|
+
case "toggle":
|
|
120
|
+
return typeof value === "boolean" ? undefined : "is not true or false";
|
|
121
|
+
case "choice":
|
|
122
|
+
return typeof value === "string" && option.choices.includes(value)
|
|
123
|
+
? undefined
|
|
124
|
+
: `is not one of ${option.choices.join(", ")}`;
|
|
125
|
+
case "number":
|
|
126
|
+
return typeof value === "number" && Number.isFinite(value)
|
|
127
|
+
? numberProblem(option, value)
|
|
128
|
+
: "is not a number";
|
|
129
|
+
case "text":
|
|
130
|
+
return typeof value === "string" ? undefined : "is not text";
|
|
131
|
+
case "list":
|
|
132
|
+
return Array.isArray(value) &&
|
|
133
|
+
value.every((item) => typeof item === "string")
|
|
134
|
+
? undefined
|
|
135
|
+
: "is not a list of text";
|
|
136
|
+
}
|
|
137
|
+
};
|
|
138
|
+
/**
|
|
139
|
+
* Call `listener` with the values of `declared` now and each time the config
|
|
140
|
+
* changes them. Returns a function that stops listening.
|
|
141
|
+
*
|
|
142
|
+
* A value the config gives that `declared` refuses is dropped, takes its
|
|
143
|
+
* default and is passed to `report`, so the shell starts whatever the config
|
|
144
|
+
* says.
|
|
145
|
+
*/
|
|
146
|
+
export const watchShellOptions = (host, declared, listener, report = reportToConsole) => {
|
|
147
|
+
const heard = () => {
|
|
148
|
+
const { dropped, values } = readOptions(declared, host.shellOptions === null
|
|
149
|
+
? undefined
|
|
150
|
+
: recordSchema.parse(JSON.parse(host.shellOptions)));
|
|
151
|
+
for (const line of dropped) {
|
|
152
|
+
report(line);
|
|
153
|
+
}
|
|
154
|
+
listener(values);
|
|
155
|
+
};
|
|
156
|
+
host.addEventListener("shelloptionschanged", heard);
|
|
157
|
+
heard();
|
|
158
|
+
return () => {
|
|
159
|
+
host.removeEventListener("shelloptionschanged", heard);
|
|
160
|
+
};
|
|
161
|
+
};
|
|
162
|
+
/** `input` with each plain object made a group. */
|
|
163
|
+
const declare = (input) => Object.fromEntries(Object.entries(input).map(([key, entry]) => [
|
|
164
|
+
key,
|
|
165
|
+
isOption(entry) ? entry : { kind: "group", options: declare(entry) },
|
|
166
|
+
]));
|
|
167
|
+
const isOption = (entry) => typeof entry.kind === "string";
|
|
168
|
+
const readGroup = (declared, given, at) => {
|
|
169
|
+
const unknown = Object.keys(given)
|
|
170
|
+
.filter((key) => !Object.hasOwn(declared, key))
|
|
171
|
+
.map((key) => `${at}${key}: no such option`);
|
|
172
|
+
const read = Object.entries(declared).map(([key, entry]) => ({
|
|
173
|
+
key,
|
|
174
|
+
...readEntry(entry, given[key], `${at}${key}`),
|
|
175
|
+
}));
|
|
176
|
+
return {
|
|
177
|
+
dropped: [...read.flatMap(({ dropped }) => dropped), ...unknown].toSorted(),
|
|
178
|
+
values: Object.fromEntries(read.map(({ key, value }) => [key, value])),
|
|
179
|
+
};
|
|
180
|
+
};
|
|
181
|
+
const readEntry = (entry, given, at) => {
|
|
182
|
+
if (entry.kind === "group") {
|
|
183
|
+
const group = recordSchema.safeParse(given === undefined ? {} : given);
|
|
184
|
+
if (group.success) {
|
|
185
|
+
const { dropped, values } = readGroup(entry.options, group.data, `${at}.`);
|
|
186
|
+
return { dropped, value: values };
|
|
187
|
+
}
|
|
188
|
+
else {
|
|
189
|
+
return {
|
|
190
|
+
dropped: [`${at}: ${JSON.stringify(given)} is not a group of options`],
|
|
191
|
+
value: readGroup(entry.options, {}, "").values,
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
else if (given === undefined) {
|
|
196
|
+
return { dropped: [], value: entry.default };
|
|
197
|
+
}
|
|
198
|
+
else {
|
|
199
|
+
const problem = optionProblem(entry, given);
|
|
200
|
+
return problem === undefined
|
|
201
|
+
? { dropped: [], value: given }
|
|
202
|
+
: {
|
|
203
|
+
dropped: [`${at}: ${JSON.stringify(given)} ${problem}`],
|
|
204
|
+
value: entry.default,
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
};
|
|
208
|
+
const recordSchema = z.record(z.string(), z.unknown());
|
|
209
|
+
const reportToConsole = (dropped) => {
|
|
210
|
+
// biome-ignore lint/suspicious/noConsole: the option takes its default, and the config's author should learn why
|
|
211
|
+
console.error(`shell_options.${dropped}`);
|
|
212
|
+
};
|
|
213
|
+
/** Which of `limits` `value` is outside, or `undefined` if neither. */
|
|
214
|
+
const numberProblem = (limits, value) => {
|
|
215
|
+
if (limits.min !== undefined && value < limits.min) {
|
|
216
|
+
return `is below ${String(limits.min)}`;
|
|
217
|
+
}
|
|
218
|
+
else if (limits.max !== undefined && value > limits.max) {
|
|
219
|
+
return `is above ${String(limits.max)}`;
|
|
220
|
+
}
|
|
221
|
+
else {
|
|
222
|
+
return undefined;
|
|
223
|
+
}
|
|
224
|
+
};
|
package/package.json
CHANGED
|
@@ -78,6 +78,10 @@
|
|
|
78
78
|
"types": "./dist/notification.d.ts",
|
|
79
79
|
"default": "./dist/notification.js"
|
|
80
80
|
},
|
|
81
|
+
"./options": {
|
|
82
|
+
"types": "./dist/options.d.ts",
|
|
83
|
+
"default": "./dist/options.js"
|
|
84
|
+
},
|
|
81
85
|
"./own-keybindings": {
|
|
82
86
|
"types": "./dist/own-keybindings.d.ts",
|
|
83
87
|
"default": "./dist/own-keybindings.js"
|
|
@@ -128,7 +132,7 @@
|
|
|
128
132
|
"test:unit": "bun test"
|
|
129
133
|
},
|
|
130
134
|
"type": "module",
|
|
131
|
-
"version": "0.0.0-alpha-
|
|
135
|
+
"version": "0.0.0-alpha-5166c4cfb4be",
|
|
132
136
|
"repository": {
|
|
133
137
|
"type": "git",
|
|
134
138
|
"url": "git+https://github.com/cprussin/domicile.git",
|