@domicile-desktop/sdk 0.0.0-alpha-bf4214ef2f5b → 0.0.0-alpha-dfd80a336b89
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/dist/portal.d.ts +113 -3
- package/dist/portal.js +121 -7
- package/package.json +1 -1
package/dist/portal.d.ts
CHANGED
|
@@ -2,8 +2,12 @@ import type { DomicileHost } from "./domicile-host";
|
|
|
2
2
|
export declare enum PortalKind {
|
|
3
3
|
/** A yes/no question, such as whether an application may use the camera. */
|
|
4
4
|
Access = 0,
|
|
5
|
+
/** Pick an application to open a file or URI with. */
|
|
6
|
+
AppChooser = 1,
|
|
7
|
+
/** Files to open, or where to save. */
|
|
8
|
+
FileChooser = 2,
|
|
5
9
|
/** A kind this SDK cannot parse. Answer it with {@link PortalAnswer.Refused}. */
|
|
6
|
-
Unknown =
|
|
10
|
+
Unknown = 3
|
|
7
11
|
}
|
|
8
12
|
/** What every request carries. */
|
|
9
13
|
export type PortalRequestBase = {
|
|
@@ -22,6 +26,79 @@ export type AccessBody = {
|
|
|
22
26
|
grantLabel: string | undefined;
|
|
23
27
|
denyLabel: string | undefined;
|
|
24
28
|
};
|
|
29
|
+
/** What an app chooser opens, and the applications it offers. */
|
|
30
|
+
export type AppChooserBody = {
|
|
31
|
+
/** Desktop file IDs without `.desktop`. May change while the dialog is up. */
|
|
32
|
+
choices: readonly string[];
|
|
33
|
+
/** The application chosen last time for this content type. */
|
|
34
|
+
lastChoice: string | undefined;
|
|
35
|
+
/** The MIME type being opened. */
|
|
36
|
+
contentType: string | undefined;
|
|
37
|
+
uri: string | undefined;
|
|
38
|
+
/** The file's name, without its directory. */
|
|
39
|
+
filename: string | undefined;
|
|
40
|
+
};
|
|
41
|
+
/** Which `FileChooser` method asked. */
|
|
42
|
+
export declare enum FileChooserMode {
|
|
43
|
+
/** Existing files, or a folder when `directory` is set. */
|
|
44
|
+
Open = 0,
|
|
45
|
+
/** One new path. */
|
|
46
|
+
Save = 1,
|
|
47
|
+
/** A folder to save `files` into. */
|
|
48
|
+
SaveFiles = 2
|
|
49
|
+
}
|
|
50
|
+
/** A named group of accepted extensions, such as "Images". */
|
|
51
|
+
export type FileChooserFilter = {
|
|
52
|
+
/** Lowercase and without the dot; empty accepts any file. */
|
|
53
|
+
extensions: readonly string[];
|
|
54
|
+
name: string;
|
|
55
|
+
};
|
|
56
|
+
/**
|
|
57
|
+
* A question asked beside the files. No options means a checkbox, answered
|
|
58
|
+
* `"true"` or `"false"`.
|
|
59
|
+
*/
|
|
60
|
+
export type FileChoice = {
|
|
61
|
+
id: string;
|
|
62
|
+
/** The option id, or `"true"`/`"false"`, selected at first. */
|
|
63
|
+
initial: string;
|
|
64
|
+
label: string;
|
|
65
|
+
options: readonly {
|
|
66
|
+
id: string;
|
|
67
|
+
label: string;
|
|
68
|
+
}[];
|
|
69
|
+
};
|
|
70
|
+
/** A file chooser's request. Paths are absolute. */
|
|
71
|
+
export type FileChooserBody = {
|
|
72
|
+
/** The confirm button's label; the shell's own when absent. */
|
|
73
|
+
acceptLabel: string | undefined;
|
|
74
|
+
choices: readonly FileChoice[];
|
|
75
|
+
/** The index in `filters` to start with. */
|
|
76
|
+
currentFilter: number | undefined;
|
|
77
|
+
/** The folder to start in; the home when absent. */
|
|
78
|
+
currentFolder: string | undefined;
|
|
79
|
+
/** The suggested name for {@link FileChooserMode.Save}. */
|
|
80
|
+
currentName: string | undefined;
|
|
81
|
+
/** Open a folder rather than a file. */
|
|
82
|
+
directory: boolean;
|
|
83
|
+
/** The names {@link FileChooserMode.SaveFiles} saves into the folder. */
|
|
84
|
+
files: readonly string[];
|
|
85
|
+
/** Empty accepts any file. */
|
|
86
|
+
filters: readonly FileChooserFilter[];
|
|
87
|
+
/** The user's home directory, where the picker's places are. */
|
|
88
|
+
home: string;
|
|
89
|
+
mode: FileChooserMode;
|
|
90
|
+
multiple: boolean;
|
|
91
|
+
title: string;
|
|
92
|
+
};
|
|
93
|
+
/** What the user chose in a file chooser. */
|
|
94
|
+
export type FileChosen = {
|
|
95
|
+
/** Each {@link FileChoice}'s answer, by id. */
|
|
96
|
+
choices: ReadonlyMap<string, string>;
|
|
97
|
+
/** The index in the request's `filters` in use. */
|
|
98
|
+
currentFilter: number | undefined;
|
|
99
|
+
/** Absolute paths: the files, or the folder for `SaveFiles`. */
|
|
100
|
+
paths: readonly string[];
|
|
101
|
+
};
|
|
25
102
|
export declare const PortalRequest: {
|
|
26
103
|
Access: (base: PortalRequestBase, body: AccessBody) => {
|
|
27
104
|
/** The id {@link answerPortalRequest} takes. */
|
|
@@ -33,6 +110,26 @@ export declare const PortalRequest: {
|
|
|
33
110
|
body: AccessBody;
|
|
34
111
|
kind: PortalKind.Access;
|
|
35
112
|
};
|
|
113
|
+
AppChooser: (base: PortalRequestBase, body: AppChooserBody) => {
|
|
114
|
+
/** The id {@link answerPortalRequest} takes. */
|
|
115
|
+
id: number;
|
|
116
|
+
/** The asking application's desktop file id. Empty when unknown. */
|
|
117
|
+
appId: string;
|
|
118
|
+
/** The `<app>` to show the dialog over, or the focused screen when absent. */
|
|
119
|
+
parentAppId: string | undefined;
|
|
120
|
+
body: AppChooserBody;
|
|
121
|
+
kind: PortalKind.AppChooser;
|
|
122
|
+
};
|
|
123
|
+
FileChooser: (base: PortalRequestBase, body: FileChooserBody) => {
|
|
124
|
+
/** The id {@link answerPortalRequest} takes. */
|
|
125
|
+
id: number;
|
|
126
|
+
/** The asking application's desktop file id. Empty when unknown. */
|
|
127
|
+
appId: string;
|
|
128
|
+
/** The `<app>` to show the dialog over, or the focused screen when absent. */
|
|
129
|
+
parentAppId: string | undefined;
|
|
130
|
+
body: FileChooserBody;
|
|
131
|
+
kind: PortalKind.FileChooser;
|
|
132
|
+
};
|
|
36
133
|
Unknown: (base: PortalRequestBase, wireKind: string) => {
|
|
37
134
|
/** The id {@link answerPortalRequest} takes. */
|
|
38
135
|
id: number;
|
|
@@ -48,18 +145,31 @@ export type PortalRequest = ReturnType<(typeof PortalRequest)[keyof typeof Porta
|
|
|
48
145
|
export declare enum PortalAnswerKind {
|
|
49
146
|
/** The user allowed an {@link PortalKind.Access} request. */
|
|
50
147
|
Access = 0,
|
|
148
|
+
/** The application the user picked in an {@link PortalKind.AppChooser}. */
|
|
149
|
+
AppChooser = 1,
|
|
150
|
+
/** The user chose in a {@link PortalKind.FileChooser}. */
|
|
151
|
+
FileChooser = 2,
|
|
51
152
|
/** The user dismissed or denied the dialog. */
|
|
52
|
-
Canceled =
|
|
153
|
+
Canceled = 3,
|
|
53
154
|
/** The shell has no dialog for this kind. */
|
|
54
|
-
Refused =
|
|
155
|
+
Refused = 4
|
|
55
156
|
}
|
|
56
157
|
export declare const PortalAnswer: {
|
|
57
158
|
Access: () => {
|
|
58
159
|
kind: PortalAnswerKind.Access;
|
|
59
160
|
};
|
|
161
|
+
/** `choice` must be one of the request's `choices`. */
|
|
162
|
+
AppChooser: (choice: string) => {
|
|
163
|
+
choice: string;
|
|
164
|
+
kind: PortalAnswerKind.AppChooser;
|
|
165
|
+
};
|
|
60
166
|
Canceled: () => {
|
|
61
167
|
kind: PortalAnswerKind.Canceled;
|
|
62
168
|
};
|
|
169
|
+
FileChooser: (chosen: FileChosen) => {
|
|
170
|
+
chosen: FileChosen;
|
|
171
|
+
kind: PortalAnswerKind.FileChooser;
|
|
172
|
+
};
|
|
63
173
|
Refused: () => {
|
|
64
174
|
kind: PortalAnswerKind.Refused;
|
|
65
175
|
};
|
package/dist/portal.js
CHANGED
|
@@ -9,15 +9,39 @@ export var PortalKind;
|
|
|
9
9
|
(function (PortalKind) {
|
|
10
10
|
/** A yes/no question, such as whether an application may use the camera. */
|
|
11
11
|
PortalKind[PortalKind["Access"] = 0] = "Access";
|
|
12
|
+
/** Pick an application to open a file or URI with. */
|
|
13
|
+
PortalKind[PortalKind["AppChooser"] = 1] = "AppChooser";
|
|
14
|
+
/** Files to open, or where to save. */
|
|
15
|
+
PortalKind[PortalKind["FileChooser"] = 2] = "FileChooser";
|
|
12
16
|
/** A kind this SDK cannot parse. Answer it with {@link PortalAnswer.Refused}. */
|
|
13
|
-
PortalKind[PortalKind["Unknown"] =
|
|
17
|
+
PortalKind[PortalKind["Unknown"] = 3] = "Unknown";
|
|
14
18
|
})(PortalKind || (PortalKind = {}));
|
|
19
|
+
/** Which `FileChooser` method asked. */
|
|
20
|
+
export var FileChooserMode;
|
|
21
|
+
(function (FileChooserMode) {
|
|
22
|
+
/** Existing files, or a folder when `directory` is set. */
|
|
23
|
+
FileChooserMode[FileChooserMode["Open"] = 0] = "Open";
|
|
24
|
+
/** One new path. */
|
|
25
|
+
FileChooserMode[FileChooserMode["Save"] = 1] = "Save";
|
|
26
|
+
/** A folder to save `files` into. */
|
|
27
|
+
FileChooserMode[FileChooserMode["SaveFiles"] = 2] = "SaveFiles";
|
|
28
|
+
})(FileChooserMode || (FileChooserMode = {}));
|
|
15
29
|
export const PortalRequest = {
|
|
16
30
|
Access: (base, body) => ({
|
|
17
31
|
...base,
|
|
18
32
|
body,
|
|
19
33
|
kind: PortalKind.Access,
|
|
20
34
|
}),
|
|
35
|
+
AppChooser: (base, body) => ({
|
|
36
|
+
...base,
|
|
37
|
+
body,
|
|
38
|
+
kind: PortalKind.AppChooser,
|
|
39
|
+
}),
|
|
40
|
+
FileChooser: (base, body) => ({
|
|
41
|
+
...base,
|
|
42
|
+
body,
|
|
43
|
+
kind: PortalKind.FileChooser,
|
|
44
|
+
}),
|
|
21
45
|
Unknown: (base, wireKind) => ({
|
|
22
46
|
...base,
|
|
23
47
|
kind: PortalKind.Unknown,
|
|
@@ -28,14 +52,27 @@ export var PortalAnswerKind;
|
|
|
28
52
|
(function (PortalAnswerKind) {
|
|
29
53
|
/** The user allowed an {@link PortalKind.Access} request. */
|
|
30
54
|
PortalAnswerKind[PortalAnswerKind["Access"] = 0] = "Access";
|
|
55
|
+
/** The application the user picked in an {@link PortalKind.AppChooser}. */
|
|
56
|
+
PortalAnswerKind[PortalAnswerKind["AppChooser"] = 1] = "AppChooser";
|
|
57
|
+
/** The user chose in a {@link PortalKind.FileChooser}. */
|
|
58
|
+
PortalAnswerKind[PortalAnswerKind["FileChooser"] = 2] = "FileChooser";
|
|
31
59
|
/** The user dismissed or denied the dialog. */
|
|
32
|
-
PortalAnswerKind[PortalAnswerKind["Canceled"] =
|
|
60
|
+
PortalAnswerKind[PortalAnswerKind["Canceled"] = 3] = "Canceled";
|
|
33
61
|
/** The shell has no dialog for this kind. */
|
|
34
|
-
PortalAnswerKind[PortalAnswerKind["Refused"] =
|
|
62
|
+
PortalAnswerKind[PortalAnswerKind["Refused"] = 4] = "Refused";
|
|
35
63
|
})(PortalAnswerKind || (PortalAnswerKind = {}));
|
|
36
64
|
export const PortalAnswer = {
|
|
37
65
|
Access: () => ({ kind: PortalAnswerKind.Access }),
|
|
66
|
+
/** `choice` must be one of the request's `choices`. */
|
|
67
|
+
AppChooser: (choice) => ({
|
|
68
|
+
choice,
|
|
69
|
+
kind: PortalAnswerKind.AppChooser,
|
|
70
|
+
}),
|
|
38
71
|
Canceled: () => ({ kind: PortalAnswerKind.Canceled }),
|
|
72
|
+
FileChooser: (chosen) => ({
|
|
73
|
+
chosen,
|
|
74
|
+
kind: PortalAnswerKind.FileChooser,
|
|
75
|
+
}),
|
|
39
76
|
Refused: () => ({ kind: PortalAnswerKind.Refused }),
|
|
40
77
|
};
|
|
41
78
|
/**
|
|
@@ -56,7 +93,7 @@ export const watchPortalRequests = (host, listener) => {
|
|
|
56
93
|
};
|
|
57
94
|
/** Answer request `id`. The first answer to reach the compositor wins. */
|
|
58
95
|
export const answerPortalRequest = (host, id, answer) => {
|
|
59
|
-
host.answerPortalRequest(id, JSON.stringify(
|
|
96
|
+
host.answerPortalRequest(id, JSON.stringify(wireAnswer(answer)));
|
|
60
97
|
};
|
|
61
98
|
const accessSchema = z
|
|
62
99
|
.object({
|
|
@@ -73,12 +110,69 @@ const accessSchema = z
|
|
|
73
110
|
subtitle: body.subtitle,
|
|
74
111
|
title: body.title,
|
|
75
112
|
}));
|
|
113
|
+
const appChooserSchema = z
|
|
114
|
+
.object({
|
|
115
|
+
choices: z.array(z.string()),
|
|
116
|
+
content_type: z.string().optional(),
|
|
117
|
+
filename: z.string().optional(),
|
|
118
|
+
last_choice: z.string().optional(),
|
|
119
|
+
uri: z.string().optional(),
|
|
120
|
+
})
|
|
121
|
+
.transform((body) => ({
|
|
122
|
+
choices: body.choices,
|
|
123
|
+
contentType: body.content_type,
|
|
124
|
+
filename: body.filename,
|
|
125
|
+
lastChoice: body.last_choice,
|
|
126
|
+
uri: body.uri,
|
|
127
|
+
}));
|
|
128
|
+
const fileChooserSchema = z
|
|
129
|
+
.object({
|
|
130
|
+
accept_label: z.string().optional(),
|
|
131
|
+
choices: z.array(z.object({
|
|
132
|
+
id: z.string(),
|
|
133
|
+
initial: z.string(),
|
|
134
|
+
label: z.string(),
|
|
135
|
+
options: z.array(z.object({ id: z.string(), label: z.string() })),
|
|
136
|
+
})),
|
|
137
|
+
current_filter: z.number().optional(),
|
|
138
|
+
current_folder: z.string().optional(),
|
|
139
|
+
current_name: z.string().optional(),
|
|
140
|
+
directory: z.boolean(),
|
|
141
|
+
files: z.array(z.string()),
|
|
142
|
+
filters: z.array(z.object({ extensions: z.array(z.string()), name: z.string() })),
|
|
143
|
+
home: z.string(),
|
|
144
|
+
mode: z.enum(["open", "save", "save_files"]),
|
|
145
|
+
multiple: z.boolean(),
|
|
146
|
+
title: z.string(),
|
|
147
|
+
})
|
|
148
|
+
.transform((body) => ({
|
|
149
|
+
acceptLabel: body.accept_label,
|
|
150
|
+
choices: body.choices,
|
|
151
|
+
currentFilter: body.current_filter,
|
|
152
|
+
currentFolder: body.current_folder,
|
|
153
|
+
currentName: body.current_name,
|
|
154
|
+
directory: body.directory,
|
|
155
|
+
files: body.files,
|
|
156
|
+
filters: body.filters,
|
|
157
|
+
home: body.home,
|
|
158
|
+
mode: chooserMode(body.mode),
|
|
159
|
+
multiple: body.multiple,
|
|
160
|
+
title: body.title,
|
|
161
|
+
}));
|
|
76
162
|
/** Each kind's wire name, and how to read its body into a request. */
|
|
77
163
|
const KINDS = new Map([
|
|
78
164
|
[
|
|
79
165
|
"access",
|
|
80
166
|
(base, body) => PortalRequest.Access(base, accessSchema.parse(body)),
|
|
81
167
|
],
|
|
168
|
+
[
|
|
169
|
+
"app_chooser",
|
|
170
|
+
(base, body) => PortalRequest.AppChooser(base, appChooserSchema.parse(body)),
|
|
171
|
+
],
|
|
172
|
+
[
|
|
173
|
+
"file_chooser",
|
|
174
|
+
(base, body) => PortalRequest.FileChooser(base, fileChooserSchema.parse(body)),
|
|
175
|
+
],
|
|
82
176
|
]);
|
|
83
177
|
const itemSchema = z.object({
|
|
84
178
|
app_id: z.string(),
|
|
@@ -102,13 +196,33 @@ const parseRequest = (item) => {
|
|
|
102
196
|
? PortalRequest.Unknown(base, item.kind)
|
|
103
197
|
: parse(base, item.body);
|
|
104
198
|
};
|
|
199
|
+
const chooserMode = (mode) => {
|
|
200
|
+
switch (mode) {
|
|
201
|
+
case "open":
|
|
202
|
+
return FileChooserMode.Open;
|
|
203
|
+
case "save":
|
|
204
|
+
return FileChooserMode.Save;
|
|
205
|
+
case "save_files":
|
|
206
|
+
return FileChooserMode.SaveFiles;
|
|
207
|
+
}
|
|
208
|
+
};
|
|
209
|
+
/** `answer` as the compositor reads it. */
|
|
105
210
|
const wireAnswer = (answer) => {
|
|
106
211
|
switch (answer.kind) {
|
|
107
212
|
case PortalAnswerKind.Access:
|
|
108
|
-
return "access";
|
|
213
|
+
return { kind: "access" };
|
|
214
|
+
case PortalAnswerKind.AppChooser:
|
|
215
|
+
return { choice: answer.choice, kind: "app_chooser" };
|
|
109
216
|
case PortalAnswerKind.Canceled:
|
|
110
|
-
return "canceled";
|
|
217
|
+
return { kind: "canceled" };
|
|
218
|
+
case PortalAnswerKind.FileChooser:
|
|
219
|
+
return {
|
|
220
|
+
choices: Object.fromEntries(answer.chosen.choices),
|
|
221
|
+
current_filter: answer.chosen.currentFilter,
|
|
222
|
+
kind: "file_chooser",
|
|
223
|
+
paths: answer.chosen.paths,
|
|
224
|
+
};
|
|
111
225
|
case PortalAnswerKind.Refused:
|
|
112
|
-
return "refused";
|
|
226
|
+
return { kind: "refused" };
|
|
113
227
|
}
|
|
114
228
|
};
|
package/package.json
CHANGED