@blastlauncher/runtime 1.0.5 → 3.0.0-alpha.1
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 +4 -10
- package/dist/api/action-panel.d.ts +134 -0
- package/dist/api/action-panel.js +36 -0
- package/dist/api/action.d.ts +386 -0
- package/dist/api/action.js +260 -0
- package/dist/api/ai-browser.d.ts +423 -0
- package/dist/api/ai-browser.js +113 -0
- package/dist/api/ai-decide.d.ts +41 -0
- package/dist/api/ai-decide.js +62 -0
- package/dist/api/applications.d.ts +13 -0
- package/dist/api/applications.js +48 -0
- package/dist/api/cache.d.ts +27 -0
- package/dist/api/cache.js +126 -0
- package/dist/api/capability.d.ts +14 -0
- package/dist/api/capability.js +30 -0
- package/dist/api/clipboard.d.ts +57 -0
- package/dist/api/clipboard.js +83 -0
- package/dist/api/detail.d.ts +33 -0
- package/dist/api/detail.js +11 -0
- package/dist/api/diagnostics.d.ts +16 -0
- package/dist/api/diagnostics.js +26 -0
- package/dist/api/dropdown.d.ts +17 -0
- package/dist/api/dropdown.js +48 -0
- package/dist/api/enums.generated.d.ts +750 -0
- package/dist/api/enums.generated.js +719 -0
- package/dist/api/environment.d.ts +44 -0
- package/dist/api/environment.js +61 -0
- package/dist/api/feedback.d.ts +94 -0
- package/dist/api/feedback.js +228 -0
- package/dist/api/form.d.ts +162 -0
- package/dist/api/form.js +345 -0
- package/dist/api/grid.d.ts +293 -0
- package/dist/api/grid.js +36 -0
- package/dist/api/implemented.d.ts +53 -0
- package/dist/api/implemented.js +43 -0
- package/dist/api/index.d.ts +612 -0
- package/dist/api/index.js +82 -0
- package/dist/api/keyboard.d.ts +182 -0
- package/dist/api/keyboard.js +107 -0
- package/dist/api/launch.d.ts +40 -0
- package/dist/api/launch.js +27 -0
- package/dist/api/legacy.d.ts +9 -0
- package/dist/api/legacy.js +25 -0
- package/dist/api/list.d.ts +291 -0
- package/dist/api/list.js +28 -0
- package/dist/api/menu-bar.d.ts +54 -0
- package/dist/api/menu-bar.js +30 -0
- package/dist/api/metadata.d.ts +62 -0
- package/dist/api/metadata.js +33 -0
- package/dist/api/navigation.d.ts +36 -0
- package/dist/api/navigation.js +99 -0
- package/dist/api/oauth.d.ts +84 -0
- package/dist/api/oauth.js +125 -0
- package/dist/api/open.d.ts +18 -0
- package/dist/api/open.js +36 -0
- package/dist/api/preferences.d.ts +43 -0
- package/dist/api/preferences.js +41 -0
- package/dist/api/selection.d.ts +10 -0
- package/dist/api/selection.js +31 -0
- package/dist/api/slot.d.ts +7 -0
- package/dist/api/slot.js +11 -0
- package/dist/api/storage.d.ts +24 -0
- package/dist/api/storage.js +45 -0
- package/dist/api/types.d.ts +46 -0
- package/dist/api/types.js +5 -0
- package/dist/api/unsupported.d.ts +18 -0
- package/dist/api/unsupported.generated.d.ts +1255 -0
- package/dist/api/unsupported.generated.js +56 -0
- package/dist/api/unsupported.js +65 -0
- package/dist/api/window-management.d.ts +97 -0
- package/dist/api/window-management.js +164 -0
- package/dist/api/window.d.ts +16 -0
- package/dist/api/window.js +32 -0
- package/dist/main.d.ts +1 -0
- package/dist/main.js +339 -0
- package/dist/provider.d.ts +8 -0
- package/dist/provider.js +107 -0
- package/dist/reconciler/index.d.ts +9 -0
- package/dist/reconciler/index.js +186 -0
- package/dist/reconciler/serialize.d.ts +3 -0
- package/dist/reconciler/serialize.js +53 -0
- package/dist/reconciler/types.d.ts +8 -0
- package/dist/reconciler/types.js +1 -0
- package/package.json +38 -23
- package/.eslintrc.js +0 -6
- package/CHANGELOG.md +0 -19
- package/dist/App.d.ts +0 -5
- package/dist/App.js +0 -10
- package/dist/components/CommandList/index.d.ts +0 -1
- package/dist/components/CommandList/index.js +0 -20
- package/dist/components/CommandList/utils.d.ts +0 -1
- package/dist/components/CommandList/utils.js +0 -24
- package/dist/index.d.ts +0 -1
- package/dist/index.js +0 -11
- package/dist/utils/connectDevtools.d.ts +0 -1
- package/dist/utils/connectDevtools.js +0 -11
- package/dist/utils/run.d.ts +0 -4
- package/dist/utils/run.js +0 -12
- package/dist/utils/window.d.ts +0 -0
- package/dist/utils/window.js +0 -2
- package/src/@types/raycast.d.ts +0 -4
- package/src/@types/react-devtools-core.d.ts +0 -19
- package/src/App.tsx +0 -18
- package/src/components/CommandList/index.tsx +0 -27
- package/src/components/CommandList/utils.ts +0 -26
- package/src/index.ts +0 -13
- package/src/utils/connectDevtools.ts +0 -8
- package/src/utils/run.ts +0 -7
- package/src/utils/window.js +0 -1
- package/tsconfig.json +0 -10
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { CommandMode, InitEnvironment } from "@blastlauncher/protocol";
|
|
2
|
+
import { type LaunchContext, type LaunchTypeValue } from "./launch.js";
|
|
3
|
+
/** Raycast's `raycastVersion`; Blast is not Raycast, so this is our own value. */
|
|
4
|
+
export declare const RAYCAST_VERSION = "0.0.0-blast";
|
|
5
|
+
/** The theme the host reports; a headless host has no UI and defaults to `"dark"`. */
|
|
6
|
+
export type Appearance = "light" | "dark";
|
|
7
|
+
export type TextSize = "medium" | "large";
|
|
8
|
+
/** Raycast's `EntryPointTypes`: Blast launches commands only. */
|
|
9
|
+
export type EntryPointType = "command" | "tool";
|
|
10
|
+
export type EntryPointMode = CommandMode;
|
|
11
|
+
export type Environment = {
|
|
12
|
+
raycastVersion: string;
|
|
13
|
+
ownerOrAuthorName: string;
|
|
14
|
+
extensionName: string;
|
|
15
|
+
entryPointType: EntryPointType;
|
|
16
|
+
entryPointName: string;
|
|
17
|
+
entryPointMode: EntryPointMode;
|
|
18
|
+
assetsPath: string;
|
|
19
|
+
supportPath: string;
|
|
20
|
+
isDevelopment: boolean;
|
|
21
|
+
appearance: Appearance;
|
|
22
|
+
textSize: TextSize;
|
|
23
|
+
launchType: LaunchTypeValue;
|
|
24
|
+
/** Deprecated alias of {@link Environment.entryPointName}; extensions still read it. */
|
|
25
|
+
commandName: string;
|
|
26
|
+
/** @deprecated Use `appearance` instead. */
|
|
27
|
+
theme: Appearance;
|
|
28
|
+
/** @deprecated Use the top-level `launchContext` prop instead. */
|
|
29
|
+
launchContext?: LaunchContext;
|
|
30
|
+
/** @deprecated Use `entryPointMode` instead. */
|
|
31
|
+
commandMode: EntryPointMode;
|
|
32
|
+
canAccess(api: unknown): boolean;
|
|
33
|
+
};
|
|
34
|
+
/** Mutable in place: extension modules bind this object when they import it. */
|
|
35
|
+
export declare const environment: Environment;
|
|
36
|
+
/**
|
|
37
|
+
* Installs the values from `init`; the runtime calls it before importing the
|
|
38
|
+
* command. The entry point mirrors the launched command, as Raycast defines it.
|
|
39
|
+
*/
|
|
40
|
+
export declare function setEnvironment(values: InitEnvironment, command: {
|
|
41
|
+
name: string;
|
|
42
|
+
mode: CommandMode;
|
|
43
|
+
launchContext?: LaunchContext;
|
|
44
|
+
}): void;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { LaunchType } from "./launch.js";
|
|
2
|
+
import { OPTIONAL_API } from "./ai-browser.js";
|
|
3
|
+
import { unsupportedPath } from "./unsupported.js";
|
|
4
|
+
/** Raycast's `raycastVersion`; Blast is not Raycast, so this is our own value. */
|
|
5
|
+
export const RAYCAST_VERSION = "0.0.0-blast";
|
|
6
|
+
let availableAPIs = [];
|
|
7
|
+
function canAccess(api) {
|
|
8
|
+
const optional = (typeof api === "object" && api !== null) || typeof api === "function"
|
|
9
|
+
? Reflect.get(api, OPTIONAL_API)
|
|
10
|
+
: undefined;
|
|
11
|
+
return optional === undefined
|
|
12
|
+
? unsupportedPath(api) === undefined
|
|
13
|
+
: availableAPIs.includes(optional);
|
|
14
|
+
}
|
|
15
|
+
/** Mutable in place: extension modules bind this object when they import it. */
|
|
16
|
+
export const environment = {
|
|
17
|
+
raycastVersion: RAYCAST_VERSION,
|
|
18
|
+
ownerOrAuthorName: "",
|
|
19
|
+
extensionName: "",
|
|
20
|
+
// Blast launches commands, never tools.
|
|
21
|
+
entryPointType: "command",
|
|
22
|
+
entryPointName: "",
|
|
23
|
+
entryPointMode: "view",
|
|
24
|
+
assetsPath: "",
|
|
25
|
+
supportPath: "",
|
|
26
|
+
isDevelopment: false,
|
|
27
|
+
appearance: "dark",
|
|
28
|
+
textSize: "medium",
|
|
29
|
+
// Blast only launches commands the user initiated; a background launch is
|
|
30
|
+
// out of scope (task 0028).
|
|
31
|
+
launchType: LaunchType.UserInitiated,
|
|
32
|
+
commandName: "",
|
|
33
|
+
// The deprecated aliases mirror their replacements from the start.
|
|
34
|
+
theme: "dark",
|
|
35
|
+
commandMode: "view",
|
|
36
|
+
canAccess,
|
|
37
|
+
};
|
|
38
|
+
/**
|
|
39
|
+
* Installs the values from `init`; the runtime calls it before importing the
|
|
40
|
+
* command. The entry point mirrors the launched command, as Raycast defines it.
|
|
41
|
+
*/
|
|
42
|
+
export function setEnvironment(values, command) {
|
|
43
|
+
availableAPIs = values.availableAPIs ?? [];
|
|
44
|
+
environment.extensionName = values.extensionName;
|
|
45
|
+
environment.ownerOrAuthorName = values.ownerOrAuthorName;
|
|
46
|
+
environment.entryPointType = "command";
|
|
47
|
+
environment.entryPointName = command.name;
|
|
48
|
+
environment.entryPointMode = command.mode;
|
|
49
|
+
environment.assetsPath = values.assetsPath;
|
|
50
|
+
environment.supportPath = values.supportPath;
|
|
51
|
+
environment.isDevelopment = values.isDevelopment;
|
|
52
|
+
environment.appearance = values.appearance;
|
|
53
|
+
environment.textSize = values.textSize;
|
|
54
|
+
environment.commandName = values.commandName;
|
|
55
|
+
environment.theme = values.appearance;
|
|
56
|
+
environment.commandMode = command.mode;
|
|
57
|
+
if (command.launchContext === undefined)
|
|
58
|
+
delete environment.launchContext;
|
|
59
|
+
else
|
|
60
|
+
environment.launchContext = command.launchContext;
|
|
61
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import type { ImageLike, Shortcut } from "./types.js";
|
|
2
|
+
import type { PopToRootTypeValue } from "./window.js";
|
|
3
|
+
export interface ToastActionOptions {
|
|
4
|
+
title: string;
|
|
5
|
+
shortcut?: Shortcut;
|
|
6
|
+
onAction(toast: Toast): void;
|
|
7
|
+
}
|
|
8
|
+
export interface ToastOptions {
|
|
9
|
+
title: string;
|
|
10
|
+
message?: string | undefined;
|
|
11
|
+
style?: string;
|
|
12
|
+
primaryAction?: ToastActionOptions | undefined;
|
|
13
|
+
secondaryAction?: ToastActionOptions | undefined;
|
|
14
|
+
}
|
|
15
|
+
export interface AlertActionOptions {
|
|
16
|
+
title: string;
|
|
17
|
+
style?: string;
|
|
18
|
+
onAction?(): void;
|
|
19
|
+
}
|
|
20
|
+
export interface AlertOptions {
|
|
21
|
+
title: string;
|
|
22
|
+
message?: string | undefined;
|
|
23
|
+
icon?: ImageLike;
|
|
24
|
+
primaryAction?: AlertActionOptions;
|
|
25
|
+
dismissAction?: AlertActionOptions;
|
|
26
|
+
rememberUserChoice?: boolean;
|
|
27
|
+
}
|
|
28
|
+
export interface HUDOptions {
|
|
29
|
+
clearRootSearch?: boolean;
|
|
30
|
+
popToRootType?: PopToRootTypeValue;
|
|
31
|
+
}
|
|
32
|
+
export declare const Alert: {
|
|
33
|
+
readonly ActionStyle: {
|
|
34
|
+
readonly Default: "default";
|
|
35
|
+
readonly Cancel: "cancel";
|
|
36
|
+
readonly Destructive: "destructive";
|
|
37
|
+
};
|
|
38
|
+
};
|
|
39
|
+
export declare const AlertActionStyle: {
|
|
40
|
+
readonly Default: "default";
|
|
41
|
+
readonly Cancel: "cancel";
|
|
42
|
+
readonly Destructive: "destructive";
|
|
43
|
+
};
|
|
44
|
+
export declare function setFeedbackMount(callback: () => void): void;
|
|
45
|
+
/**
|
|
46
|
+
* Mounts that lazily created scene, if the command has one. Feedback state and
|
|
47
|
+
* the window's `closeMainWindow` request both publish through it.
|
|
48
|
+
*/
|
|
49
|
+
export declare function mountScene(): void;
|
|
50
|
+
export declare class Toast {
|
|
51
|
+
static Style: {
|
|
52
|
+
readonly Success: "SUCCESS";
|
|
53
|
+
readonly Failure: "FAILURE";
|
|
54
|
+
readonly Animated: "ANIMATED";
|
|
55
|
+
};
|
|
56
|
+
private options;
|
|
57
|
+
constructor(props: ToastOptions);
|
|
58
|
+
get title(): string;
|
|
59
|
+
set title(value: string);
|
|
60
|
+
get message(): string | undefined;
|
|
61
|
+
set message(value: string | undefined);
|
|
62
|
+
get style(): string;
|
|
63
|
+
set style(value: string);
|
|
64
|
+
get primaryAction(): ToastActionOptions | undefined;
|
|
65
|
+
set primaryAction(value: ToastActionOptions | undefined);
|
|
66
|
+
get secondaryAction(): ToastActionOptions | undefined;
|
|
67
|
+
set secondaryAction(value: ToastActionOptions | undefined);
|
|
68
|
+
show(): Promise<void>;
|
|
69
|
+
hide(): Promise<void>;
|
|
70
|
+
private update;
|
|
71
|
+
}
|
|
72
|
+
export declare const ToastStyle: {
|
|
73
|
+
readonly Success: "SUCCESS";
|
|
74
|
+
readonly Failure: "FAILURE";
|
|
75
|
+
readonly Animated: "ANIMATED";
|
|
76
|
+
};
|
|
77
|
+
export declare function showToast(options: ToastOptions): Promise<Toast>;
|
|
78
|
+
export declare function showToast(style: string, title: string, message?: string): Promise<Toast>;
|
|
79
|
+
export declare function showHUD(title: string, options?: HUDOptions): Promise<void>;
|
|
80
|
+
export declare function confirmAlert(options: AlertOptions): Promise<boolean>;
|
|
81
|
+
export declare function FeedbackScene(): (import("react").DOMElement<import("react").DOMAttributes<Element>, Element> | import("react").ReactElement<{
|
|
82
|
+
key: string;
|
|
83
|
+
title: string;
|
|
84
|
+
message: string | undefined;
|
|
85
|
+
style: string;
|
|
86
|
+
}, string | import("react").JSXElementConstructor<any>> | import("react").ReactElement<{
|
|
87
|
+
key: number;
|
|
88
|
+
title: string;
|
|
89
|
+
message: string | undefined;
|
|
90
|
+
icon: ImageLike | undefined;
|
|
91
|
+
rememberUserChoice: boolean | undefined;
|
|
92
|
+
rememberChoice?: boolean;
|
|
93
|
+
onRememberUserChoiceChange?: (checked: boolean) => void;
|
|
94
|
+
}, string | import("react").JSXElementConstructor<any>> | undefined)[];
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
import { createElement as h, useLayoutEffect, useSyncExternalStore } from "react";
|
|
2
|
+
// `AlertActionStyle_2` and `ToastStyle_2` are the declarations' names behind the
|
|
3
|
+
// public `Alert.ActionStyle` and `Toast.Style` paths (api-extractor dedupes
|
|
4
|
+
// alias collisions with a numeric suffix).
|
|
5
|
+
import { AlertActionStyle_2, ToastStyle_2 } from "./enums.generated.js";
|
|
6
|
+
import { requestCapability } from "./capability.js";
|
|
7
|
+
import { environment } from "./environment.js";
|
|
8
|
+
export const Alert = { ActionStyle: AlertActionStyle_2 };
|
|
9
|
+
export const AlertActionStyle = Alert.ActionStyle;
|
|
10
|
+
const activeToast = { current: undefined };
|
|
11
|
+
let hud;
|
|
12
|
+
let revision = 0;
|
|
13
|
+
const listeners = new Set();
|
|
14
|
+
const committed = new Map();
|
|
15
|
+
let nextAlert = 0;
|
|
16
|
+
const alerts = [];
|
|
17
|
+
let mount;
|
|
18
|
+
// No-view commands mount a scene lazily, retaining their scene-free fast path.
|
|
19
|
+
export function setFeedbackMount(callback) {
|
|
20
|
+
mount = callback;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Mounts that lazily created scene, if the command has one. Feedback state and
|
|
24
|
+
* the window's `closeMainWindow` request both publish through it.
|
|
25
|
+
*/
|
|
26
|
+
export function mountScene() {
|
|
27
|
+
mount?.();
|
|
28
|
+
}
|
|
29
|
+
function changed(wait = false) {
|
|
30
|
+
const pendingRevision = ++revision;
|
|
31
|
+
const result = wait
|
|
32
|
+
? new Promise((resolve) => committed.set(pendingRevision, resolve))
|
|
33
|
+
: Promise.resolve();
|
|
34
|
+
for (const listener of listeners)
|
|
35
|
+
listener();
|
|
36
|
+
mountScene();
|
|
37
|
+
// Without a subscriber no React commit can acknowledge this revision.
|
|
38
|
+
queueMicrotask(() => {
|
|
39
|
+
if (listeners.size === 0) {
|
|
40
|
+
committed.get(pendingRevision)?.();
|
|
41
|
+
committed.delete(pendingRevision);
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
return result;
|
|
45
|
+
}
|
|
46
|
+
const subscribe = (listener) => {
|
|
47
|
+
listeners.add(listener);
|
|
48
|
+
return () => {
|
|
49
|
+
listeners.delete(listener);
|
|
50
|
+
if (listeners.size === 0) {
|
|
51
|
+
activeToast.current = undefined;
|
|
52
|
+
hud = undefined;
|
|
53
|
+
for (const alert of alerts.splice(0)) {
|
|
54
|
+
alert.remember = false;
|
|
55
|
+
alert.resolve(false);
|
|
56
|
+
}
|
|
57
|
+
for (const resolve of committed.values())
|
|
58
|
+
resolve();
|
|
59
|
+
committed.clear();
|
|
60
|
+
revision++;
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
};
|
|
64
|
+
const snapshot = () => revision;
|
|
65
|
+
export class Toast {
|
|
66
|
+
static Style = ToastStyle_2;
|
|
67
|
+
options;
|
|
68
|
+
constructor(props) {
|
|
69
|
+
this.options = { ...props, style: props.style ?? Toast.Style.Success };
|
|
70
|
+
}
|
|
71
|
+
get title() {
|
|
72
|
+
return this.options.title;
|
|
73
|
+
}
|
|
74
|
+
set title(value) {
|
|
75
|
+
this.options.title = value;
|
|
76
|
+
this.update();
|
|
77
|
+
}
|
|
78
|
+
get message() {
|
|
79
|
+
return this.options.message;
|
|
80
|
+
}
|
|
81
|
+
set message(value) {
|
|
82
|
+
this.options.message = value;
|
|
83
|
+
this.update();
|
|
84
|
+
}
|
|
85
|
+
get style() {
|
|
86
|
+
return this.options.style;
|
|
87
|
+
}
|
|
88
|
+
set style(value) {
|
|
89
|
+
this.options.style = value;
|
|
90
|
+
this.update();
|
|
91
|
+
}
|
|
92
|
+
get primaryAction() {
|
|
93
|
+
return this.options.primaryAction;
|
|
94
|
+
}
|
|
95
|
+
set primaryAction(value) {
|
|
96
|
+
this.options.primaryAction = value;
|
|
97
|
+
this.update();
|
|
98
|
+
}
|
|
99
|
+
get secondaryAction() {
|
|
100
|
+
return this.options.secondaryAction;
|
|
101
|
+
}
|
|
102
|
+
set secondaryAction(value) {
|
|
103
|
+
this.options.secondaryAction = value;
|
|
104
|
+
this.update();
|
|
105
|
+
}
|
|
106
|
+
show() {
|
|
107
|
+
activeToast.current = this;
|
|
108
|
+
return changed(true);
|
|
109
|
+
}
|
|
110
|
+
hide() {
|
|
111
|
+
if (activeToast.current !== this)
|
|
112
|
+
return Promise.resolve();
|
|
113
|
+
activeToast.current = undefined;
|
|
114
|
+
return changed(true);
|
|
115
|
+
}
|
|
116
|
+
update() {
|
|
117
|
+
if (activeToast.current === this)
|
|
118
|
+
void changed();
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
export const ToastStyle = Toast.Style;
|
|
122
|
+
export async function showToast(options, title, message) {
|
|
123
|
+
const next = new Toast(typeof options === "string" ? { style: options, title: title, message } : options);
|
|
124
|
+
await next.show();
|
|
125
|
+
return next;
|
|
126
|
+
}
|
|
127
|
+
export function showHUD(title, options = {}) {
|
|
128
|
+
hud = { title, ...options };
|
|
129
|
+
return changed(true);
|
|
130
|
+
}
|
|
131
|
+
export async function confirmAlert(options) {
|
|
132
|
+
// Host storage supplies extension isolation; the key separates commands and
|
|
133
|
+
// alert text/actions without depending on function identity or queue order.
|
|
134
|
+
const key = `blast:alert:${JSON.stringify([
|
|
135
|
+
environment.commandName,
|
|
136
|
+
options.title,
|
|
137
|
+
options.message ?? null,
|
|
138
|
+
options.primaryAction?.title ?? "OK",
|
|
139
|
+
options.primaryAction?.style ?? Alert.ActionStyle.Default,
|
|
140
|
+
options.dismissAction?.title ?? "Cancel",
|
|
141
|
+
options.dismissAction?.style ?? Alert.ActionStyle.Cancel,
|
|
142
|
+
])}`;
|
|
143
|
+
if (options.rememberUserChoice) {
|
|
144
|
+
const answer = await requestCapability("storage.get", { scope: "blast:internal", key });
|
|
145
|
+
if (typeof answer === "boolean") {
|
|
146
|
+
(answer ? options.primaryAction : options.dismissAction)?.onAction?.();
|
|
147
|
+
return answer;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
const alert = { id: ++nextAlert, options, remember: false, resolve: (_value) => { } };
|
|
151
|
+
const answer = await new Promise((resolve) => {
|
|
152
|
+
alert.resolve = resolve;
|
|
153
|
+
alerts.push(alert);
|
|
154
|
+
void changed();
|
|
155
|
+
});
|
|
156
|
+
if (options.rememberUserChoice && alert.remember)
|
|
157
|
+
await requestCapability("storage.set", { scope: "blast:internal", key, value: answer });
|
|
158
|
+
return answer;
|
|
159
|
+
}
|
|
160
|
+
export function FeedbackScene() {
|
|
161
|
+
const version = useSyncExternalStore(subscribe, snapshot, snapshot);
|
|
162
|
+
useLayoutEffect(() => {
|
|
163
|
+
for (const [pending, resolve] of committed) {
|
|
164
|
+
if (pending <= version) {
|
|
165
|
+
committed.delete(pending);
|
|
166
|
+
resolve();
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}, [version]);
|
|
170
|
+
const currentToast = activeToast.current;
|
|
171
|
+
const alert = alerts[0];
|
|
172
|
+
const respond = (answer) => {
|
|
173
|
+
// Stale or repeated events must not answer the next queued alert.
|
|
174
|
+
if (alerts[0] !== alert || !alert)
|
|
175
|
+
return;
|
|
176
|
+
alerts.shift();
|
|
177
|
+
void changed();
|
|
178
|
+
alert.resolve(answer);
|
|
179
|
+
return (answer ? alert.options.primaryAction : alert.options.dismissAction)?.onAction?.();
|
|
180
|
+
};
|
|
181
|
+
const toastAction = (action, name) => action &&
|
|
182
|
+
h("Toast.Action", {
|
|
183
|
+
key: name,
|
|
184
|
+
name,
|
|
185
|
+
title: action.title,
|
|
186
|
+
shortcut: action.shortcut,
|
|
187
|
+
onAction: () => action.onAction(currentToast),
|
|
188
|
+
});
|
|
189
|
+
return [
|
|
190
|
+
currentToast &&
|
|
191
|
+
h("Toast", {
|
|
192
|
+
key: "toast",
|
|
193
|
+
title: currentToast.title,
|
|
194
|
+
message: currentToast.message,
|
|
195
|
+
style: currentToast.style,
|
|
196
|
+
}, toastAction(currentToast.primaryAction, "primaryAction"), toastAction(currentToast.secondaryAction, "secondaryAction")),
|
|
197
|
+
hud && h("HUD", { key: "hud", ...hud }),
|
|
198
|
+
alert &&
|
|
199
|
+
h("Alert", {
|
|
200
|
+
key: alert.id,
|
|
201
|
+
title: alert.options.title,
|
|
202
|
+
message: alert.options.message,
|
|
203
|
+
icon: alert.options.icon,
|
|
204
|
+
rememberUserChoice: alert.options.rememberUserChoice,
|
|
205
|
+
...(alert.options.rememberUserChoice
|
|
206
|
+
? {
|
|
207
|
+
rememberChoice: alert.remember,
|
|
208
|
+
onRememberUserChoiceChange: (checked) => {
|
|
209
|
+
if (alerts[0] !== alert)
|
|
210
|
+
return;
|
|
211
|
+
alert.remember = checked === true;
|
|
212
|
+
void changed();
|
|
213
|
+
},
|
|
214
|
+
}
|
|
215
|
+
: {}),
|
|
216
|
+
}, h("Alert.Action", {
|
|
217
|
+
name: "primaryAction",
|
|
218
|
+
title: alert.options.primaryAction?.title ?? "OK",
|
|
219
|
+
style: alert.options.primaryAction?.style ?? Alert.ActionStyle.Default,
|
|
220
|
+
onAction: () => respond(true),
|
|
221
|
+
}), h("Alert.Action", {
|
|
222
|
+
name: "dismissAction",
|
|
223
|
+
title: alert.options.dismissAction?.title ?? "Cancel",
|
|
224
|
+
style: alert.options.dismissAction?.style ?? Alert.ActionStyle.Cancel,
|
|
225
|
+
onAction: () => respond(false),
|
|
226
|
+
})),
|
|
227
|
+
];
|
|
228
|
+
}
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import { type ReactNode } from "react";
|
|
2
|
+
import { DatePickerType } from "./enums.generated.js";
|
|
3
|
+
import type { ImageLike, Shortcut } from "./types.js";
|
|
4
|
+
export type FormValue = string | number | boolean | string[] | number[] | Date | null;
|
|
5
|
+
export type FormValues = Record<string, FormValue>;
|
|
6
|
+
export interface FormEvent<T extends FormValue> {
|
|
7
|
+
target: {
|
|
8
|
+
id: string;
|
|
9
|
+
value?: T;
|
|
10
|
+
};
|
|
11
|
+
type: "focus" | "blur";
|
|
12
|
+
}
|
|
13
|
+
/** `Form.ItemReference`: the shared imperative handle every field ref exposes. */
|
|
14
|
+
export interface FormItemRef {
|
|
15
|
+
focus: () => void;
|
|
16
|
+
reset: () => void;
|
|
17
|
+
}
|
|
18
|
+
export interface FormItemProps<T extends FormValue> {
|
|
19
|
+
id: string;
|
|
20
|
+
title?: string;
|
|
21
|
+
info?: string;
|
|
22
|
+
error?: string;
|
|
23
|
+
storeValue?: boolean;
|
|
24
|
+
autoFocus?: boolean;
|
|
25
|
+
value?: T;
|
|
26
|
+
defaultValue?: T;
|
|
27
|
+
onChange?: (value: T) => void;
|
|
28
|
+
onBlur?: (event: FormEvent<T>) => void;
|
|
29
|
+
onFocus?: (event: FormEvent<T>) => void;
|
|
30
|
+
}
|
|
31
|
+
export interface FormProps {
|
|
32
|
+
children?: ReactNode;
|
|
33
|
+
actions?: ReactNode;
|
|
34
|
+
searchBarAccessory?: ReactNode;
|
|
35
|
+
navigationTitle?: string;
|
|
36
|
+
isLoading?: boolean;
|
|
37
|
+
enableDrafts?: boolean;
|
|
38
|
+
}
|
|
39
|
+
export interface FormTextFieldProps extends FormItemProps<string> {
|
|
40
|
+
placeholder?: string;
|
|
41
|
+
}
|
|
42
|
+
export interface FormTextAreaProps extends FormTextFieldProps {
|
|
43
|
+
enableMarkdown?: boolean;
|
|
44
|
+
}
|
|
45
|
+
export interface FormCheckboxProps extends FormItemProps<boolean> {
|
|
46
|
+
label: string;
|
|
47
|
+
}
|
|
48
|
+
/** `Form.DatePicker.Type`: what a date picker lets the user pick. */
|
|
49
|
+
export { DatePickerType };
|
|
50
|
+
export interface FormFilePickerProps extends FormItemProps<string[]> {
|
|
51
|
+
allowMultipleSelection?: boolean;
|
|
52
|
+
canChooseDirectories?: boolean;
|
|
53
|
+
canChooseFiles?: boolean;
|
|
54
|
+
showHiddenFiles?: boolean;
|
|
55
|
+
}
|
|
56
|
+
export interface FormPasswordFieldProps extends FormItemProps<string> {
|
|
57
|
+
placeholder?: string;
|
|
58
|
+
}
|
|
59
|
+
export interface FormTagPickerProps extends FormItemProps<string[]> {
|
|
60
|
+
children?: ReactNode;
|
|
61
|
+
placeholder?: string;
|
|
62
|
+
}
|
|
63
|
+
export interface FormTagPickerItemProps {
|
|
64
|
+
value: string;
|
|
65
|
+
title: string;
|
|
66
|
+
icon?: ImageLike;
|
|
67
|
+
}
|
|
68
|
+
export interface FormLinkAccessoryProps {
|
|
69
|
+
target: string;
|
|
70
|
+
text: string;
|
|
71
|
+
}
|
|
72
|
+
export interface FormDatePickerProps extends FormItemProps<Date | null> {
|
|
73
|
+
type?: (typeof DatePickerType)[keyof typeof DatePickerType];
|
|
74
|
+
min?: Date;
|
|
75
|
+
max?: Date;
|
|
76
|
+
}
|
|
77
|
+
export interface FormDropdownProps extends FormItemProps<string> {
|
|
78
|
+
children?: ReactNode;
|
|
79
|
+
placeholder?: string;
|
|
80
|
+
filtering?: boolean | {
|
|
81
|
+
keepSectionOrder: boolean;
|
|
82
|
+
};
|
|
83
|
+
isLoading?: boolean;
|
|
84
|
+
throttle?: boolean;
|
|
85
|
+
onSearchTextChange?: (text: string) => void;
|
|
86
|
+
}
|
|
87
|
+
export interface FormDropdownItemProps {
|
|
88
|
+
value: string;
|
|
89
|
+
title: string;
|
|
90
|
+
icon?: ImageLike;
|
|
91
|
+
keywords?: string[];
|
|
92
|
+
}
|
|
93
|
+
export interface FormDropdownSectionProps {
|
|
94
|
+
children?: ReactNode;
|
|
95
|
+
title?: string;
|
|
96
|
+
}
|
|
97
|
+
export interface FormDescriptionProps {
|
|
98
|
+
title?: string;
|
|
99
|
+
text: string;
|
|
100
|
+
}
|
|
101
|
+
type Fields = Map<string, {
|
|
102
|
+
read: () => FormValue;
|
|
103
|
+
secret: boolean;
|
|
104
|
+
event: (type: "focus" | "blur") => void;
|
|
105
|
+
}>;
|
|
106
|
+
/** Installed before importing the command, alongside its launch props. */
|
|
107
|
+
export declare function setFormDraftValues(values: FormValues | undefined): void;
|
|
108
|
+
export interface FormContextValue {
|
|
109
|
+
fields: Fields;
|
|
110
|
+
enableDrafts: boolean;
|
|
111
|
+
focusedId: string | null;
|
|
112
|
+
focus: (id: string | null) => void;
|
|
113
|
+
blur: (id: string) => void;
|
|
114
|
+
draft: FormValues | undefined;
|
|
115
|
+
}
|
|
116
|
+
/** Decodes a stored or wire-carried values object back into typed `FormValue`s. */
|
|
117
|
+
export declare function decodeFormValues(values: Record<string, unknown>): FormValues;
|
|
118
|
+
/** Graceful shutdown must receive the host's write acknowledgements before exiting. */
|
|
119
|
+
export declare function flushFormPersistence(): Promise<void>;
|
|
120
|
+
export declare const Form: (({ children, actions, searchBarAccessory, ...props }: FormProps) => import("react").FunctionComponentElement<import("react").ProviderProps<FormContextValue | null>>) & {
|
|
121
|
+
FilePicker: import("react").ForwardRefExoticComponent<FormFilePickerProps & import("react").RefAttributes<FormItemRef>>;
|
|
122
|
+
TagPicker: import("react").ForwardRefExoticComponent<FormTagPickerProps & import("react").RefAttributes<FormItemRef>> & {
|
|
123
|
+
Item: (props: FormTagPickerItemProps) => import("react").ReactElement<FormTagPickerItemProps, string | import("react").JSXElementConstructor<any>>;
|
|
124
|
+
};
|
|
125
|
+
PasswordField: import("react").ForwardRefExoticComponent<FormPasswordFieldProps & import("react").RefAttributes<FormItemRef>>;
|
|
126
|
+
TextField: import("react").ForwardRefExoticComponent<FormTextFieldProps & import("react").RefAttributes<FormItemRef>>;
|
|
127
|
+
TextArea: import("react").ForwardRefExoticComponent<FormTextAreaProps & import("react").RefAttributes<FormItemRef>>;
|
|
128
|
+
Checkbox: import("react").ForwardRefExoticComponent<FormCheckboxProps & import("react").RefAttributes<FormItemRef>>;
|
|
129
|
+
DatePicker: import("react").ForwardRefExoticComponent<FormDatePickerProps & import("react").RefAttributes<FormItemRef>> & {
|
|
130
|
+
Type: {
|
|
131
|
+
readonly Date: "date";
|
|
132
|
+
readonly DateTime: "date_time";
|
|
133
|
+
};
|
|
134
|
+
/** Whether the date is a full day: non-null with local time exactly midnight. */
|
|
135
|
+
isFullDay(date?: Date | null): boolean;
|
|
136
|
+
};
|
|
137
|
+
Dropdown: import("react").ForwardRefExoticComponent<FormDropdownProps & import("react").RefAttributes<FormItemRef>> & {
|
|
138
|
+
Item: (props: FormDropdownItemProps) => import("react").ReactElement<FormDropdownItemProps, string | import("react").JSXElementConstructor<any>>;
|
|
139
|
+
Section: (props: FormDropdownSectionProps) => import("react").DOMElement<FormDropdownSectionProps, Element>;
|
|
140
|
+
};
|
|
141
|
+
LinkAccessory: (props: FormLinkAccessoryProps) => import("react").ReactElement<FormLinkAccessoryProps, string | import("react").JSXElementConstructor<any>>;
|
|
142
|
+
Description: (props: FormDescriptionProps) => import("react").ReactElement<FormDescriptionProps, string | import("react").JSXElementConstructor<any>>;
|
|
143
|
+
Separator: (props: Record<string, never>) => import("react").DOMElement<Record<string, never>, Element>;
|
|
144
|
+
};
|
|
145
|
+
export interface SubmitFormProps<T extends FormValues = FormValues> {
|
|
146
|
+
title?: string;
|
|
147
|
+
icon?: ImageLike;
|
|
148
|
+
shortcut?: Shortcut;
|
|
149
|
+
style?: "regular" | "destructive";
|
|
150
|
+
onSubmit?: (input: T) => void | boolean | Promise<void | boolean>;
|
|
151
|
+
}
|
|
152
|
+
export declare function SubmitForm<T extends FormValues>({ onSubmit, title, ...props }: SubmitFormProps<T>): import("react").ReactElement<{
|
|
153
|
+
icon?: ImageLike;
|
|
154
|
+
shortcut?: Shortcut;
|
|
155
|
+
style?: "regular" | "destructive";
|
|
156
|
+
submissionFailureCount: number;
|
|
157
|
+
title: string;
|
|
158
|
+
onAction: () => void | boolean | Promise<void | boolean>;
|
|
159
|
+
}, string | import("react").JSXElementConstructor<any>>;
|
|
160
|
+
export declare namespace SubmitForm {
|
|
161
|
+
var displayName: string;
|
|
162
|
+
}
|