@domicile-desktop/component-library 0.0.0-alpha-da1cf1c5441c → 0.0.0-alpha-e1457b57e6b2
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
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
"dependencies": {
|
|
3
3
|
"@base-ui/react": "^1.8.0",
|
|
4
4
|
"@cprussin/option-result": "^2.0.1",
|
|
5
|
-
"@domicile-desktop/sdk": "0.0.0-alpha-
|
|
6
|
-
"@domicile-desktop/system-apps": "0.0.0-alpha-
|
|
5
|
+
"@domicile-desktop/sdk": "0.0.0-alpha-e1457b57e6b2",
|
|
6
|
+
"@domicile-desktop/system-apps": "0.0.0-alpha-e1457b57e6b2",
|
|
7
7
|
"@phosphor-icons/react": "^2.1.10"
|
|
8
8
|
},
|
|
9
9
|
"description": "React components and the Panda CSS design system that Domicile's shells are built from.",
|
|
@@ -103,5 +103,5 @@
|
|
|
103
103
|
"test:unit": "bun test"
|
|
104
104
|
},
|
|
105
105
|
"type": "module",
|
|
106
|
-
"version": "0.0.0-alpha-
|
|
106
|
+
"version": "0.0.0-alpha-e1457b57e6b2"
|
|
107
107
|
}
|
|
@@ -1,57 +1,86 @@
|
|
|
1
1
|
import type { AccountBody, PortalAnswer } from "@domicile-desktop/sdk/portal";
|
|
2
2
|
import { PortalAnswer as Answer } from "@domicile-desktop/sdk/portal";
|
|
3
|
+
import type { System } from "@domicile-desktop/sdk/system";
|
|
3
4
|
import { css } from "../../styled-system/css";
|
|
5
|
+
import { hstack } from "../../styled-system/patterns";
|
|
4
6
|
import { Button } from "../Button/Button";
|
|
5
7
|
import { ModalDialog } from "../ModalDialog/ModalDialog";
|
|
8
|
+
import { usePictureUrl } from "../usePictureUrl/usePictureUrl";
|
|
6
9
|
|
|
7
10
|
type Props = {
|
|
8
11
|
answer: (answer: PortalAnswer) => void;
|
|
9
12
|
/** Who asks, as the dialog names them. */
|
|
10
13
|
asker: string;
|
|
11
14
|
body: AccountBody;
|
|
15
|
+
/** Reads the user's picture. */
|
|
16
|
+
files: Pick<System, "readFile">;
|
|
12
17
|
screen: string | undefined;
|
|
13
18
|
};
|
|
14
19
|
|
|
15
20
|
/**
|
|
16
|
-
* Whether an application may have the user's name and picture
|
|
17
|
-
* keeps them.
|
|
21
|
+
* Whether an application may have the user's name and picture, showing both.
|
|
22
|
+
* Dismissing it keeps them.
|
|
18
23
|
*/
|
|
19
|
-
export const AccountDialog = ({
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
24
|
+
export const AccountDialog = ({
|
|
25
|
+
answer,
|
|
26
|
+
asker,
|
|
27
|
+
body,
|
|
28
|
+
files,
|
|
29
|
+
screen,
|
|
30
|
+
}: Props) => {
|
|
31
|
+
const picture = usePictureUrl(files, body.image);
|
|
32
|
+
return (
|
|
33
|
+
<ModalDialog
|
|
34
|
+
closeButton={false}
|
|
35
|
+
footer={
|
|
36
|
+
<>
|
|
37
|
+
<Button
|
|
38
|
+
onClick={() => {
|
|
39
|
+
answer(Answer.Canceled());
|
|
40
|
+
}}
|
|
41
|
+
variant="outline"
|
|
42
|
+
>
|
|
43
|
+
Don't share
|
|
44
|
+
</Button>
|
|
45
|
+
<Button
|
|
46
|
+
onClick={() => {
|
|
47
|
+
answer(Answer.Access());
|
|
48
|
+
}}
|
|
49
|
+
>
|
|
50
|
+
Share
|
|
51
|
+
</Button>
|
|
52
|
+
</>
|
|
44
53
|
}
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
+
onOpenChange={(open) => {
|
|
55
|
+
if (!open) {
|
|
56
|
+
answer(Answer.Canceled());
|
|
57
|
+
}
|
|
58
|
+
}}
|
|
59
|
+
open
|
|
60
|
+
screen={screen}
|
|
61
|
+
title="Share your name and picture?"
|
|
62
|
+
>
|
|
63
|
+
<p className={askerStyles}>{`${asker} asks`}</p>
|
|
64
|
+
{body.reason !== undefined && <p className={textStyles}>{body.reason}</p>}
|
|
65
|
+
<div className={userStyles}>
|
|
66
|
+
{picture !== undefined && (
|
|
67
|
+
<img alt={body.name} className={pictureStyles} src={picture} />
|
|
68
|
+
)}
|
|
69
|
+
<span className={textStyles}>{body.name}</span>
|
|
70
|
+
</div>
|
|
71
|
+
</ModalDialog>
|
|
72
|
+
);
|
|
73
|
+
};
|
|
54
74
|
|
|
55
75
|
const askerStyles = css({ color: "muted", fontSize: "sm", margin: 0 });
|
|
56
76
|
|
|
57
77
|
const textStyles = css({ color: "foreground", margin: 0 });
|
|
78
|
+
|
|
79
|
+
const userStyles = hstack({ gap: 3, marginBlockStart: 3 });
|
|
80
|
+
|
|
81
|
+
const pictureStyles = css({
|
|
82
|
+
blockSize: 10,
|
|
83
|
+
borderRadius: "full",
|
|
84
|
+
inlineSize: 10,
|
|
85
|
+
objectFit: "cover",
|
|
86
|
+
});
|
|
@@ -4,6 +4,7 @@ import type { Capturing, PortalRequest } from "@domicile-desktop/sdk/portal";
|
|
|
4
4
|
import {
|
|
5
5
|
answerPortalRequest,
|
|
6
6
|
CapturingKind,
|
|
7
|
+
CastSourceKind,
|
|
7
8
|
PortalAnswer,
|
|
8
9
|
PortalKind,
|
|
9
10
|
stopCapturing,
|
|
@@ -14,6 +15,7 @@ import type { System, SystemHost } from "@domicile-desktop/sdk/system";
|
|
|
14
15
|
import { system } from "@domicile-desktop/sdk/system";
|
|
15
16
|
import { useCallback, useEffect, useMemo, useState } from "react";
|
|
16
17
|
import { listDirectory } from "../FilePicker/list-directory";
|
|
18
|
+
import type { App } from "../useApps/useApps";
|
|
17
19
|
import { useApps } from "../useApps/useApps";
|
|
18
20
|
import { AccessDialog } from "./AccessDialog";
|
|
19
21
|
import { AccountDialog } from "./AccountDialog";
|
|
@@ -82,7 +84,7 @@ export const PortalDialogs = ({
|
|
|
82
84
|
);
|
|
83
85
|
const [sessions, setSessions] = useState<readonly Capturing[]>([]);
|
|
84
86
|
const apps = useApps(files, [
|
|
85
|
-
...requests.
|
|
87
|
+
...requests.flatMap(appIds),
|
|
86
88
|
...sessions.map((session) => session.appId),
|
|
87
89
|
]);
|
|
88
90
|
|
|
@@ -120,6 +122,7 @@ export const PortalDialogs = ({
|
|
|
120
122
|
answer={(answer) => {
|
|
121
123
|
answerPortalRequest(host, shown.id, answer);
|
|
122
124
|
}}
|
|
125
|
+
apps={apps}
|
|
123
126
|
asker={apps(shown.appId).name}
|
|
124
127
|
key={shown.id}
|
|
125
128
|
list={list}
|
|
@@ -136,6 +139,7 @@ export const PortalDialogs = ({
|
|
|
136
139
|
/** The dialog for `request`'s kind. */
|
|
137
140
|
const Dialog = ({
|
|
138
141
|
answer,
|
|
142
|
+
apps,
|
|
139
143
|
asker,
|
|
140
144
|
list,
|
|
141
145
|
request,
|
|
@@ -144,6 +148,7 @@ const Dialog = ({
|
|
|
144
148
|
system: files,
|
|
145
149
|
}: {
|
|
146
150
|
answer: (answer: PortalAnswer) => void;
|
|
151
|
+
apps: (appId: string) => App;
|
|
147
152
|
asker: string;
|
|
148
153
|
list: (path: string) => Promise<readonly string[]>;
|
|
149
154
|
request: PortalRequest;
|
|
@@ -204,6 +209,7 @@ const Dialog = ({
|
|
|
204
209
|
answer={answer}
|
|
205
210
|
asker={asker}
|
|
206
211
|
body={request.body}
|
|
212
|
+
files={files}
|
|
207
213
|
screen={screen}
|
|
208
214
|
/>
|
|
209
215
|
);
|
|
@@ -249,6 +255,7 @@ const Dialog = ({
|
|
|
249
255
|
return (
|
|
250
256
|
<ScreenCastDialog
|
|
251
257
|
answer={answer}
|
|
258
|
+
apps={apps}
|
|
252
259
|
asker={asker}
|
|
253
260
|
body={request.body}
|
|
254
261
|
screen={screen}
|
|
@@ -300,6 +307,17 @@ const screenFor = (
|
|
|
300
307
|
return parents ?? screen;
|
|
301
308
|
};
|
|
302
309
|
|
|
310
|
+
/** The applications `request` names: who asks, and windows it may record. */
|
|
311
|
+
const appIds = (request: PortalRequest): string[] =>
|
|
312
|
+
request.kind === PortalKind.ScreenCast
|
|
313
|
+
? [
|
|
314
|
+
request.appId,
|
|
315
|
+
...request.body.sources.flatMap((source) =>
|
|
316
|
+
source.kind === CastSourceKind.Window ? [source.appId] : [],
|
|
317
|
+
),
|
|
318
|
+
]
|
|
319
|
+
: [request.appId];
|
|
320
|
+
|
|
303
321
|
/** Whether `request` is a question this draws a dialog for. */
|
|
304
322
|
const isAsked = (request: PortalRequest): boolean => {
|
|
305
323
|
switch (request.kind) {
|
|
@@ -17,10 +17,13 @@ import { css } from "../../styled-system/css";
|
|
|
17
17
|
import { Button } from "../Button/Button";
|
|
18
18
|
import { ModalDialog } from "../ModalDialog/ModalDialog";
|
|
19
19
|
import { Tabs } from "../Tabs/Tabs";
|
|
20
|
+
import type { App } from "../useApps/useApps";
|
|
20
21
|
import { RegionPicker } from "./RegionPicker";
|
|
21
22
|
|
|
22
23
|
type Props = {
|
|
23
24
|
answer: (answer: PortalAnswer) => void;
|
|
25
|
+
/** Names a window's application by its Wayland app id. */
|
|
26
|
+
apps: (appId: string) => App;
|
|
24
27
|
/** Who asks, as the dialog names them. */
|
|
25
28
|
asker: string;
|
|
26
29
|
body: ScreenCastBody;
|
|
@@ -33,7 +36,13 @@ type Props = {
|
|
|
33
36
|
* asks for one. Picks one, or several when the application asks for more.
|
|
34
37
|
* Dismissing it cancels.
|
|
35
38
|
*/
|
|
36
|
-
export const ScreenCastDialog = ({
|
|
39
|
+
export const ScreenCastDialog = ({
|
|
40
|
+
answer,
|
|
41
|
+
apps,
|
|
42
|
+
asker,
|
|
43
|
+
body,
|
|
44
|
+
screen,
|
|
45
|
+
}: Props) => {
|
|
37
46
|
const [picked, setPicked] = useState<readonly string[]>([]);
|
|
38
47
|
const [drawing, setDrawing] = useState(false);
|
|
39
48
|
const windows = body.sources.filter(
|
|
@@ -72,8 +81,8 @@ export const ScreenCastDialog = ({ answer, asker, body, screen }: Props) => {
|
|
|
72
81
|
key={sourceKey(source)}
|
|
73
82
|
value={sourceKey(source)}
|
|
74
83
|
>
|
|
75
|
-
<SourceIcon source={source} />
|
|
76
|
-
<span>{sourceName(source)}</span>
|
|
84
|
+
<SourceIcon app={appOf(apps, source)} source={source} />
|
|
85
|
+
<span>{sourceName(source, appOf(apps, source))}</span>
|
|
77
86
|
</Toggle>
|
|
78
87
|
))}
|
|
79
88
|
</ToggleGroup>
|
|
@@ -152,13 +161,19 @@ export const ScreenCastDialog = ({ answer, asker, body, screen }: Props) => {
|
|
|
152
161
|
};
|
|
153
162
|
|
|
154
163
|
/** A window's application icon, a window outline without one, or a screen. */
|
|
155
|
-
const SourceIcon = ({
|
|
164
|
+
const SourceIcon = ({
|
|
165
|
+
app,
|
|
166
|
+
source,
|
|
167
|
+
}: {
|
|
168
|
+
app: App | undefined;
|
|
169
|
+
source: CastSource;
|
|
170
|
+
}) => {
|
|
156
171
|
switch (source.kind) {
|
|
157
172
|
case CastSourceKind.Window:
|
|
158
|
-
return
|
|
173
|
+
return app?.icon === undefined ? (
|
|
159
174
|
<AppWindowIcon aria-hidden className={iconStyles} />
|
|
160
175
|
) : (
|
|
161
|
-
<img alt="" className={iconStyles} src={
|
|
176
|
+
<img alt="" className={iconStyles} src={app.icon} />
|
|
162
177
|
);
|
|
163
178
|
case CastSourceKind.Monitor:
|
|
164
179
|
case CastSourceKind.Region:
|
|
@@ -226,14 +241,21 @@ const sourceKey = (source: CastSource): string => {
|
|
|
226
241
|
}
|
|
227
242
|
};
|
|
228
243
|
|
|
229
|
-
/**
|
|
230
|
-
const
|
|
244
|
+
/** A window's application, or `undefined` for a screen or a window with no app id. */
|
|
245
|
+
const appOf = (
|
|
246
|
+
apps: (appId: string) => App,
|
|
247
|
+
source: CastSource,
|
|
248
|
+
): App | undefined =>
|
|
249
|
+
source.kind === CastSourceKind.Window && source.appId !== ""
|
|
250
|
+
? apps(source.appId)
|
|
251
|
+
: undefined;
|
|
252
|
+
|
|
253
|
+
/** How the picker names a source, a window by its `app` too. */
|
|
254
|
+
const sourceName = (source: CastSource, app: App | undefined): string => {
|
|
231
255
|
switch (source.kind) {
|
|
232
256
|
case CastSourceKind.Window: {
|
|
233
257
|
const title = source.title === "" ? "Untitled window" : source.title;
|
|
234
|
-
return
|
|
235
|
-
? title
|
|
236
|
-
: `${source.appName}: ${title}`;
|
|
258
|
+
return app === undefined ? title : `${app.name}: ${title}`;
|
|
237
259
|
}
|
|
238
260
|
case CastSourceKind.Monitor:
|
|
239
261
|
return source.description === ""
|