@dxos/plugin-script 0.7.5-main.9d26e3a → 0.7.5-main.e9bb01b
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/lib/browser/{AutomationPanel-RFQAQ7NF.mjs → AutomationPanel-KZVZMZE6.mjs} +3 -3
- package/dist/lib/browser/ScriptContainer-AERJBGYK.mjs +685 -0
- package/dist/lib/browser/ScriptContainer-AERJBGYK.mjs.map +7 -0
- package/dist/lib/browser/chunk-ABNZIPST.mjs +15 -0
- package/dist/lib/browser/chunk-ABNZIPST.mjs.map +7 -0
- package/dist/lib/browser/chunk-ESPJMGXJ.mjs +21 -0
- package/dist/lib/browser/chunk-ESPJMGXJ.mjs.map +7 -0
- package/dist/lib/browser/chunk-J7CBIZI2.mjs +224 -0
- package/dist/lib/browser/chunk-J7CBIZI2.mjs.map +7 -0
- package/dist/lib/browser/chunk-NFPF4OIU.mjs +40 -0
- package/dist/lib/browser/chunk-NFPF4OIU.mjs.map +7 -0
- package/dist/lib/browser/chunk-REL2R7SB.mjs +52 -0
- package/dist/lib/browser/chunk-REL2R7SB.mjs.map +7 -0
- package/dist/lib/browser/{chunk-4UTRJLQ3.mjs → chunk-SDHKTS6H.mjs} +2 -51
- package/dist/lib/browser/{chunk-4UTRJLQ3.mjs.map → chunk-SDHKTS6H.mjs.map} +4 -4
- package/dist/lib/browser/{chunk-K7KY6YMG.mjs → chunk-Y4CVWFVS.mjs} +2 -2
- package/dist/lib/browser/compiler-4QD7VFZX.mjs +84 -0
- package/dist/lib/browser/compiler-4QD7VFZX.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +112 -288
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/intent-resolver-YABC4JRJ.mjs +45 -0
- package/dist/lib/browser/intent-resolver-YABC4JRJ.mjs.map +7 -0
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/react-surface-FRQIIYKG.mjs +65 -0
- package/dist/lib/browser/react-surface-FRQIIYKG.mjs.map +7 -0
- package/dist/lib/browser/settings-S4MGGM4D.mjs +24 -0
- package/dist/lib/browser/settings-S4MGGM4D.mjs.map +7 -0
- package/dist/lib/browser/types.mjs +6 -4
- package/dist/lib/node/{AutomationPanel-5CJEF4MI.cjs → AutomationPanel-6QSV7ZYK.cjs} +8 -8
- package/dist/lib/node/ScriptContainer-DOAQ6KOE.cjs +689 -0
- package/dist/lib/node/ScriptContainer-DOAQ6KOE.cjs.map +7 -0
- package/dist/lib/node/chunk-63C2KSD6.cjs +252 -0
- package/dist/lib/node/chunk-63C2KSD6.cjs.map +7 -0
- package/dist/lib/node/{chunk-PK3OQP5L.cjs → chunk-6M2VRFBC.cjs} +26 -11
- package/dist/lib/node/chunk-6M2VRFBC.cjs.map +7 -0
- package/dist/lib/node/{chunk-4INUJLXL.cjs → chunk-CZVVFUVL.cjs} +6 -6
- package/dist/lib/node/{chunk-6LYX73ZT.cjs → chunk-HU2JXUXI.cjs} +11 -8
- package/dist/lib/node/chunk-HU2JXUXI.cjs.map +7 -0
- package/dist/lib/node/{meta.cjs → chunk-RZHY5LAR.cjs} +12 -8
- package/dist/lib/node/chunk-RZHY5LAR.cjs.map +7 -0
- package/dist/lib/node/{chunk-WPP65EYS.cjs → chunk-T4TXCRN5.cjs} +6 -44
- package/dist/lib/node/chunk-T4TXCRN5.cjs.map +7 -0
- package/dist/lib/node/chunk-TJ4BTPOS.cjs +64 -0
- package/dist/lib/node/chunk-TJ4BTPOS.cjs.map +7 -0
- package/dist/lib/node/compiler-Q3EXWKV5.cjs +104 -0
- package/dist/lib/node/compiler-Q3EXWKV5.cjs.map +7 -0
- package/dist/lib/node/index.cjs +105 -276
- package/dist/lib/node/index.cjs.map +4 -4
- package/dist/lib/node/intent-resolver-7HI2CDGB.cjs +59 -0
- package/dist/lib/node/intent-resolver-7HI2CDGB.cjs.map +7 -0
- package/dist/lib/node/meta.json +1 -1
- package/dist/lib/node/react-surface-IKONODLG.cjs +84 -0
- package/dist/lib/node/react-surface-IKONODLG.cjs.map +7 -0
- package/dist/lib/node/settings-Q5XIA4LZ.cjs +38 -0
- package/dist/lib/node/settings-Q5XIA4LZ.cjs.map +7 -0
- package/dist/lib/node/types.cjs +6 -4
- package/dist/lib/node/types.cjs.map +2 -2
- package/dist/lib/node-esm/{AutomationPanel-7Y6REBNJ.mjs → AutomationPanel-EZQSGJLS.mjs} +3 -3
- package/dist/lib/node-esm/ScriptContainer-HE5QPPFZ.mjs +686 -0
- package/dist/lib/node-esm/ScriptContainer-HE5QPPFZ.mjs.map +7 -0
- package/dist/lib/node-esm/chunk-3YOO5E2D.mjs +16 -0
- package/dist/lib/node-esm/chunk-3YOO5E2D.mjs.map +7 -0
- package/dist/lib/node-esm/chunk-5D2AXTAW.mjs +41 -0
- package/dist/lib/node-esm/chunk-5D2AXTAW.mjs.map +7 -0
- package/dist/lib/node-esm/{chunk-OFYGBUNI.mjs → chunk-77LRZF2W.mjs} +2 -2
- package/dist/lib/node-esm/{chunk-SLTZMY34.mjs → chunk-EOBGHKNT.mjs} +7 -4
- package/dist/lib/node-esm/chunk-EOBGHKNT.mjs.map +7 -0
- package/dist/lib/node-esm/{chunk-4SRPREUK.mjs → chunk-G76M6O46.mjs} +2 -51
- package/dist/lib/node-esm/{chunk-4SRPREUK.mjs.map → chunk-G76M6O46.mjs.map} +4 -4
- package/dist/lib/node-esm/chunk-JJZYWSD7.mjs +54 -0
- package/dist/lib/node-esm/chunk-JJZYWSD7.mjs.map +7 -0
- package/dist/lib/node-esm/chunk-UWHMEJYW.mjs +225 -0
- package/dist/lib/node-esm/chunk-UWHMEJYW.mjs.map +7 -0
- package/dist/lib/node-esm/compiler-KJEIPDZ6.mjs +85 -0
- package/dist/lib/node-esm/compiler-KJEIPDZ6.mjs.map +7 -0
- package/dist/lib/node-esm/index.mjs +112 -288
- package/dist/lib/node-esm/index.mjs.map +4 -4
- package/dist/lib/node-esm/intent-resolver-KG7I6JJA.mjs +46 -0
- package/dist/lib/node-esm/intent-resolver-KG7I6JJA.mjs.map +7 -0
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/lib/node-esm/react-surface-YA7WIRAM.mjs +66 -0
- package/dist/lib/node-esm/react-surface-YA7WIRAM.mjs.map +7 -0
- package/dist/lib/node-esm/settings-NZXHNIQ3.mjs +25 -0
- package/dist/lib/node-esm/settings-NZXHNIQ3.mjs.map +7 -0
- package/dist/lib/node-esm/types.mjs +6 -4
- package/dist/types/src/ScriptPlugin.d.ts +1 -3
- package/dist/types/src/ScriptPlugin.d.ts.map +1 -1
- package/dist/types/src/capabilities/capabilities.d.ts +5 -0
- package/dist/types/src/capabilities/capabilities.d.ts.map +1 -0
- package/dist/types/src/capabilities/compiler.d.ts +4 -0
- package/dist/types/src/capabilities/compiler.d.ts.map +1 -0
- package/dist/types/src/capabilities/index.d.ts +6 -0
- package/dist/types/src/capabilities/index.d.ts.map +1 -0
- package/dist/types/src/capabilities/intent-resolver.d.ts +4 -0
- package/dist/types/src/capabilities/intent-resolver.d.ts.map +1 -0
- package/dist/types/src/capabilities/react-surface.d.ts +4 -0
- package/dist/types/src/capabilities/react-surface.d.ts.map +1 -0
- package/dist/types/src/capabilities/settings.d.ts +4 -0
- package/dist/types/src/capabilities/settings.d.ts.map +1 -0
- package/dist/types/src/components/ScriptContainer.d.ts +10 -2
- package/dist/types/src/components/ScriptContainer.d.ts.map +1 -1
- package/dist/types/src/components/ScriptSettings/ScriptSettings.d.ts.map +1 -1
- package/dist/types/src/components/ScriptSettingsPanel.d.ts.map +1 -1
- package/dist/types/src/components/ScriptToolbar/ScriptToolbar.d.ts +11 -0
- package/dist/types/src/components/ScriptToolbar/ScriptToolbar.d.ts.map +1 -0
- package/dist/types/src/components/{Toolbar/Toolbar.stories.d.ts → ScriptToolbar/ScriptToolbar.stories.d.ts} +1 -1
- package/dist/types/src/components/ScriptToolbar/ScriptToolbar.stories.d.ts.map +1 -0
- package/dist/types/src/components/ScriptToolbar/deploy.d.ts +48 -0
- package/dist/types/src/components/ScriptToolbar/deploy.d.ts.map +1 -0
- package/dist/types/src/components/ScriptToolbar/format.d.ts +25 -0
- package/dist/types/src/components/ScriptToolbar/format.d.ts.map +1 -0
- package/dist/types/src/components/ScriptToolbar/index.d.ts +2 -0
- package/dist/types/src/components/ScriptToolbar/index.d.ts.map +1 -0
- package/dist/types/src/components/ScriptToolbar/templateSelect.d.ts +31 -0
- package/dist/types/src/components/ScriptToolbar/templateSelect.d.ts.map +1 -0
- package/dist/types/src/components/ScriptToolbar/useToolbarAction.d.ts +14 -0
- package/dist/types/src/components/ScriptToolbar/useToolbarAction.d.ts.map +1 -0
- package/dist/types/src/components/ScriptToolbar/useToolbarState.d.ts +10 -0
- package/dist/types/src/components/ScriptToolbar/useToolbarState.d.ts.map +1 -0
- package/dist/types/src/components/ScriptToolbar/view.d.ts +33 -0
- package/dist/types/src/components/ScriptToolbar/view.d.ts.map +1 -0
- package/dist/types/src/components/TypescriptEditor/TypescriptEditor.d.ts +4 -2
- package/dist/types/src/components/TypescriptEditor/TypescriptEditor.d.ts.map +1 -1
- package/dist/types/src/components/TypescriptEditor/TypescriptEditor.stories.d.ts.map +1 -1
- package/dist/types/src/components/index.d.ts +1 -1
- package/dist/types/src/components/index.d.ts.map +1 -1
- package/dist/types/src/index.d.ts +2 -4
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/meta.d.ts +2 -2
- package/dist/types/src/meta.d.ts.map +1 -1
- package/dist/types/src/templates/email.d.ts +2 -1
- package/dist/types/src/templates/email.d.ts.map +1 -1
- package/dist/types/src/{templates.d.ts → templates/index.d.ts} +1 -1
- package/dist/types/src/templates/index.d.ts.map +1 -0
- package/dist/types/src/translations.d.ts +91 -0
- package/dist/types/src/translations.d.ts.map +1 -1
- package/dist/types/src/types.d.ts +14 -6
- package/dist/types/src/types.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +45 -46
- package/src/ScriptPlugin.tsx +55 -91
- package/src/bundler/bundler.test.ts +4 -3
- package/src/capabilities/capabilities.ts +12 -0
- package/src/capabilities/compiler.ts +33 -0
- package/src/capabilities/index.ts +12 -0
- package/src/capabilities/intent-resolver.ts +46 -0
- package/src/capabilities/react-surface.tsx +53 -0
- package/src/capabilities/settings.ts +14 -0
- package/src/components/ScriptContainer.tsx +64 -9
- package/src/components/ScriptSettings/ScriptSettings.tsx +28 -4
- package/src/components/ScriptSettingsPanel.tsx +124 -50
- package/src/components/{Toolbar/Toolbar.stories.tsx → ScriptToolbar/ScriptToolbar.stories.tsx} +2 -2
- package/src/components/ScriptToolbar/ScriptToolbar.tsx +51 -0
- package/src/components/ScriptToolbar/deploy.ts +157 -0
- package/src/components/ScriptToolbar/format.ts +47 -0
- package/src/components/{Toolbar → ScriptToolbar}/index.ts +1 -1
- package/src/components/ScriptToolbar/templateSelect.ts +64 -0
- package/src/components/ScriptToolbar/useToolbarAction.ts +54 -0
- package/src/components/ScriptToolbar/useToolbarState.ts +16 -0
- package/src/components/ScriptToolbar/view.ts +52 -0
- package/src/components/TypescriptEditor/TypescriptEditor.stories.tsx +2 -0
- package/src/components/TypescriptEditor/TypescriptEditor.tsx +73 -8
- package/src/index.ts +2 -6
- package/src/meta.ts +3 -2
- package/src/templates/email.ts +26 -23
- package/src/{templates.ts → templates/index.ts} +5 -5
- package/src/translations.ts +21 -0
- package/src/types.ts +19 -18
- package/dist/lib/browser/ScriptContainer-GDVBWFI7.mjs +0 -415
- package/dist/lib/browser/ScriptContainer-GDVBWFI7.mjs.map +0 -7
- package/dist/lib/browser/chunk-C7VBULMK.mjs +0 -18
- package/dist/lib/browser/chunk-C7VBULMK.mjs.map +0 -7
- package/dist/lib/browser/chunk-RQA3EB5K.mjs +0 -26
- package/dist/lib/browser/chunk-RQA3EB5K.mjs.map +0 -7
- package/dist/lib/browser/meta.mjs +0 -9
- package/dist/lib/browser/meta.mjs.map +0 -7
- package/dist/lib/node/ScriptContainer-YWXXNLG6.cjs +0 -426
- package/dist/lib/node/ScriptContainer-YWXXNLG6.cjs.map +0 -7
- package/dist/lib/node/chunk-6LYX73ZT.cjs.map +0 -7
- package/dist/lib/node/chunk-PK3OQP5L.cjs.map +0 -7
- package/dist/lib/node/chunk-WPP65EYS.cjs.map +0 -7
- package/dist/lib/node/meta.cjs.map +0 -7
- package/dist/lib/node-esm/ScriptContainer-BG2PHKEH.mjs +0 -416
- package/dist/lib/node-esm/ScriptContainer-BG2PHKEH.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-H6BBLQ5Q.mjs +0 -27
- package/dist/lib/node-esm/chunk-H6BBLQ5Q.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-SLTZMY34.mjs.map +0 -7
- package/dist/lib/node-esm/meta.mjs +0 -10
- package/dist/lib/node-esm/meta.mjs.map +0 -7
- package/dist/types/src/components/ScriptBlock/ScriptBlock.d.ts +0 -12
- package/dist/types/src/components/ScriptBlock/ScriptBlock.d.ts.map +0 -1
- package/dist/types/src/components/ScriptBlock/ScriptBlock.stories.d.ts +0 -6
- package/dist/types/src/components/ScriptBlock/ScriptBlock.stories.d.ts.map +0 -1
- package/dist/types/src/components/ScriptBlock/index.d.ts +0 -2
- package/dist/types/src/components/ScriptBlock/index.d.ts.map +0 -1
- package/dist/types/src/components/ScriptEditor.d.ts +0 -9
- package/dist/types/src/components/ScriptEditor.d.ts.map +0 -1
- package/dist/types/src/components/Toolbar/Toolbar.d.ts +0 -23
- package/dist/types/src/components/Toolbar/Toolbar.d.ts.map +0 -1
- package/dist/types/src/components/Toolbar/Toolbar.stories.d.ts.map +0 -1
- package/dist/types/src/components/Toolbar/index.d.ts +0 -2
- package/dist/types/src/components/Toolbar/index.d.ts.map +0 -1
- package/dist/types/src/templates.d.ts.map +0 -1
- package/src/components/ScriptBlock/ScriptBlock.stories.tsx +0 -55
- package/src/components/ScriptBlock/ScriptBlock.tsx +0 -102
- package/src/components/ScriptBlock/index.ts +0 -5
- package/src/components/ScriptEditor.tsx +0 -193
- package/src/components/Toolbar/Toolbar.tsx +0 -177
- /package/dist/lib/browser/{AutomationPanel-RFQAQ7NF.mjs.map → AutomationPanel-KZVZMZE6.mjs.map} +0 -0
- /package/dist/lib/browser/{chunk-K7KY6YMG.mjs.map → chunk-Y4CVWFVS.mjs.map} +0 -0
- /package/dist/lib/node/{AutomationPanel-5CJEF4MI.cjs.map → AutomationPanel-6QSV7ZYK.cjs.map} +0 -0
- /package/dist/lib/node/{chunk-4INUJLXL.cjs.map → chunk-CZVVFUVL.cjs.map} +0 -0
- /package/dist/lib/node-esm/{AutomationPanel-7Y6REBNJ.mjs.map → AutomationPanel-EZQSGJLS.mjs.map} +0 -0
- /package/dist/lib/node-esm/{chunk-OFYGBUNI.mjs.map → chunk-77LRZF2W.mjs.map} +0 -0
|
@@ -2,31 +2,60 @@
|
|
|
2
2
|
// Copyright 2024 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import
|
|
5
|
+
import { Octokit } from '@octokit/core';
|
|
6
|
+
import React, { type ChangeEvent, useCallback, useEffect, useState } from 'react';
|
|
6
7
|
|
|
8
|
+
import { createIntent, SettingsAction, useIntentDispatcher } from '@dxos/app-framework';
|
|
7
9
|
import { FunctionType, type ScriptType, getInvocationUrl, getUserFunctionUrlInMetadata } from '@dxos/functions';
|
|
10
|
+
import { log } from '@dxos/log';
|
|
8
11
|
import { useClient } from '@dxos/react-client';
|
|
9
12
|
import { Filter, getMeta, getSpace, useQuery } from '@dxos/react-client/echo';
|
|
10
|
-
import { Button,
|
|
13
|
+
import { Button, Clipboard, Input, Separator, useControlledValue, useTranslation } from '@dxos/react-ui';
|
|
14
|
+
import { AccessTokenType } from '@dxos/schema';
|
|
11
15
|
|
|
12
16
|
import { SCRIPT_PLUGIN } from '../meta';
|
|
13
17
|
|
|
18
|
+
// TODO(wittjosiah): Factor out.
|
|
19
|
+
// From https://stackoverflow.com/a/67243723/2804332
|
|
20
|
+
const kebabize = (str: string) => str.replace(/[A-Z]+(?![a-z])|[A-Z]/g, ($, ofs) => (ofs ? '-' : '') + $.toLowerCase());
|
|
21
|
+
|
|
14
22
|
export type ScriptSettingsPanelProps = {
|
|
15
23
|
script: ScriptType;
|
|
16
24
|
};
|
|
17
25
|
|
|
18
26
|
export const ScriptSettingsPanel = ({ script }: ScriptSettingsPanelProps) => {
|
|
19
27
|
const { t } = useTranslation(SCRIPT_PLUGIN);
|
|
28
|
+
const { dispatchPromise: dispatch } = useIntentDispatcher();
|
|
20
29
|
const client = useClient();
|
|
21
30
|
const space = getSpace(script);
|
|
22
31
|
const [fn] = useQuery(space, Filter.schema(FunctionType, { source: script }));
|
|
32
|
+
const [githubToken] = useQuery(space, Filter.schema(AccessTokenType, { source: 'github.com' }));
|
|
33
|
+
const gistKey = getMeta(script).keys.find(({ source }) => source === 'github.com');
|
|
34
|
+
const [gistUrl, setGistUrl] = useState<string | undefined>();
|
|
23
35
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
36
|
+
useEffect(() => {
|
|
37
|
+
const token = githubToken?.token;
|
|
38
|
+
const gistId = gistKey?.id;
|
|
39
|
+
if (!token || !gistId) {
|
|
40
|
+
setGistUrl(undefined);
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const timeout = setTimeout(async () => {
|
|
45
|
+
try {
|
|
46
|
+
const octokit = new Octokit({ auth: githubToken.token });
|
|
47
|
+
const response = await octokit.request('GET /gists/{gist_id}', {
|
|
48
|
+
gist_id: gistId,
|
|
49
|
+
});
|
|
50
|
+
setGistUrl(response.data.html_url);
|
|
51
|
+
} catch (err) {
|
|
52
|
+
log.catch(err);
|
|
53
|
+
setGistUrl(undefined);
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
return () => clearTimeout(timeout);
|
|
58
|
+
}, [githubToken, gistKey]);
|
|
30
59
|
|
|
31
60
|
const [binding, setBinding] = useControlledValue(fn?.binding ?? '');
|
|
32
61
|
const handleBindingChange = useCallback(
|
|
@@ -46,55 +75,100 @@ export const ScriptSettingsPanel = ({ script }: ScriptSettingsPanelProps) => {
|
|
|
46
75
|
spaceId: space?.id,
|
|
47
76
|
});
|
|
48
77
|
|
|
49
|
-
|
|
50
|
-
|
|
78
|
+
const handleOpenTokenManager = useCallback(
|
|
79
|
+
() => dispatch(createIntent(SettingsAction.Open, { plugin: 'dxos.org/plugin/token-manager' })),
|
|
80
|
+
[],
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
const [publishing, setPublishing] = useState(false);
|
|
84
|
+
const handlePublish = useCallback(async () => {
|
|
85
|
+
setPublishing(true);
|
|
86
|
+
const octokit = new Octokit({ auth: githubToken.token });
|
|
87
|
+
const source = await script.source.load();
|
|
88
|
+
const filename = script.name ? kebabize(script.name) : 'composer-script';
|
|
89
|
+
const files = { [`${filename}.ts`]: { content: source.content } };
|
|
90
|
+
|
|
91
|
+
try {
|
|
92
|
+
// TODO(wittjosiah): Factor out to intent.
|
|
93
|
+
const meta = getMeta(script);
|
|
94
|
+
const githubKey = meta.keys.find(({ source }) => source === 'github.com');
|
|
95
|
+
const gistId = githubKey?.id;
|
|
96
|
+
if (gistId) {
|
|
97
|
+
await octokit.request('PATCH /gists/{gist_id}', {
|
|
98
|
+
gist_id: gistId,
|
|
99
|
+
files,
|
|
100
|
+
});
|
|
101
|
+
} else {
|
|
102
|
+
const response = await octokit.request('POST /gists', {
|
|
103
|
+
public: true,
|
|
104
|
+
files,
|
|
105
|
+
});
|
|
106
|
+
if (response.data.id) {
|
|
107
|
+
meta.keys.push({ source: 'github.com', id: response.data.id });
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
} finally {
|
|
111
|
+
setPublishing(false);
|
|
112
|
+
}
|
|
113
|
+
}, [script, githubToken]);
|
|
51
114
|
|
|
52
115
|
return (
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
<div role='none' className='flex flex-col gap-1'>
|
|
56
|
-
<Input.Label>{t('name label')}</Input.Label>
|
|
57
|
-
<Input.TextInput
|
|
58
|
-
placeholder={t('object title placeholder')}
|
|
59
|
-
value={script.name ?? ''}
|
|
60
|
-
onChange={handleNameChange}
|
|
61
|
-
/>
|
|
62
|
-
</div>
|
|
63
|
-
</Input.Root>
|
|
64
|
-
{fn && (
|
|
116
|
+
<>
|
|
117
|
+
{fn && functionUrl && (
|
|
65
118
|
<>
|
|
66
|
-
<
|
|
67
|
-
<
|
|
68
|
-
|
|
69
|
-
<
|
|
70
|
-
<
|
|
71
|
-
|
|
119
|
+
<Separator />
|
|
120
|
+
<div role='form' className='flex flex-col w-full p-2 gap-4'>
|
|
121
|
+
<h2>{t('remote function settings heading')}</h2>
|
|
122
|
+
<Input.Root>
|
|
123
|
+
<div role='none' className='flex flex-col gap-1'>
|
|
124
|
+
<Input.Label>{t('function url label')}</Input.Label>
|
|
125
|
+
<div role='none' className='flex gap-1'>
|
|
126
|
+
<Input.TextInput
|
|
127
|
+
disabled
|
|
128
|
+
value={functionUrl}
|
|
129
|
+
onChange={(event) => {
|
|
130
|
+
fn.name = event.target.value;
|
|
131
|
+
}}
|
|
132
|
+
/>
|
|
133
|
+
<Clipboard.IconButton value={functionUrl} />
|
|
134
|
+
</div>
|
|
135
|
+
</div>
|
|
136
|
+
</Input.Root>
|
|
137
|
+
<Input.Root>
|
|
138
|
+
<div role='none' className='flex flex-col gap-1'>
|
|
139
|
+
<Input.Label>{t('function binding label')}</Input.Label>
|
|
72
140
|
<Input.TextInput
|
|
73
|
-
|
|
74
|
-
value={
|
|
75
|
-
onChange={
|
|
76
|
-
|
|
77
|
-
}}
|
|
141
|
+
placeholder={t('function binding placeholder')}
|
|
142
|
+
value={binding}
|
|
143
|
+
onChange={handleBindingChange}
|
|
144
|
+
onBlur={handleBindingBlur}
|
|
78
145
|
/>
|
|
79
|
-
<Button onClick={handleCopy}>
|
|
80
|
-
<Icon icon='ph--copy--regular' size={4} />
|
|
81
|
-
</Button>
|
|
82
146
|
</div>
|
|
83
|
-
</
|
|
84
|
-
</
|
|
85
|
-
<Input.Root>
|
|
86
|
-
<div role='none' className='flex flex-col gap-1'>
|
|
87
|
-
<Input.Label>{t('function binding label')}</Input.Label>
|
|
88
|
-
<Input.TextInput
|
|
89
|
-
placeholder={t('function binding placeholder')}
|
|
90
|
-
value={binding}
|
|
91
|
-
onChange={handleBindingChange}
|
|
92
|
-
onBlur={handleBindingBlur}
|
|
93
|
-
/>
|
|
94
|
-
</div>
|
|
95
|
-
</Input.Root>
|
|
147
|
+
</Input.Root>
|
|
148
|
+
</div>
|
|
96
149
|
</>
|
|
97
150
|
)}
|
|
98
|
-
|
|
151
|
+
<Separator />
|
|
152
|
+
<div className='flex flex-col w-full p-2 gap-4'>
|
|
153
|
+
<div>
|
|
154
|
+
<h2>{t('script publish settings label')}</h2>
|
|
155
|
+
<p className='text-description text-sm'>{t('script publish settings description')}</p>
|
|
156
|
+
</div>
|
|
157
|
+
{!githubToken && (
|
|
158
|
+
<div className='flex flex-col items-center justify-center gap-2'>
|
|
159
|
+
<span>{t('no github token label')}</span>
|
|
160
|
+
<Button onClick={handleOpenTokenManager}>{t('open token manager label')}</Button>
|
|
161
|
+
</div>
|
|
162
|
+
)}
|
|
163
|
+
{githubToken && (
|
|
164
|
+
<div className='flex justify-end gap-2'>
|
|
165
|
+
{gistUrl && <Clipboard.IconButton value={gistUrl} />}
|
|
166
|
+
<Button disabled={publishing} onClick={handlePublish}>
|
|
167
|
+
{t(publishing ? 'publishing label' : 'publish label')}
|
|
168
|
+
</Button>
|
|
169
|
+
</div>
|
|
170
|
+
)}
|
|
171
|
+
</div>
|
|
172
|
+
</>
|
|
99
173
|
);
|
|
100
174
|
};
|
package/src/components/{Toolbar/Toolbar.stories.tsx → ScriptToolbar/ScriptToolbar.stories.tsx}
RENAMED
|
@@ -8,7 +8,7 @@ import { type Meta } from '@storybook/react';
|
|
|
8
8
|
|
|
9
9
|
import { withLayout, withTheme } from '@dxos/storybook-utils';
|
|
10
10
|
|
|
11
|
-
import {
|
|
11
|
+
import { ScriptToolbar } from './ScriptToolbar';
|
|
12
12
|
import { templates } from '../../templates';
|
|
13
13
|
|
|
14
14
|
export const Default = {
|
|
@@ -23,7 +23,7 @@ export const Default = {
|
|
|
23
23
|
|
|
24
24
|
const meta: Meta = {
|
|
25
25
|
title: 'plugins/plugin-script/Toolbar',
|
|
26
|
-
component:
|
|
26
|
+
component: ScriptToolbar,
|
|
27
27
|
decorators: [withTheme, withLayout({ tooltips: true })],
|
|
28
28
|
};
|
|
29
29
|
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2024 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import React, { useCallback } from 'react';
|
|
6
|
+
|
|
7
|
+
import { type ScriptType } from '@dxos/functions';
|
|
8
|
+
import { fullyQualifiedId } from '@dxos/react-client/echo';
|
|
9
|
+
import { ElevationProvider, type ThemedClassName } from '@dxos/react-ui';
|
|
10
|
+
import { stackItemContentToolbarClassNames } from '@dxos/react-ui-editor';
|
|
11
|
+
import { createGapSeparator, MenuProvider, ToolbarMenu, useMenuActions } from '@dxos/react-ui-menu';
|
|
12
|
+
|
|
13
|
+
import { createDeploy } from './deploy';
|
|
14
|
+
import { createFormat } from './format';
|
|
15
|
+
import { createTemplateSelect } from './templateSelect';
|
|
16
|
+
import { useToolbarAction } from './useToolbarAction';
|
|
17
|
+
import { type ScriptToolbarState } from './useToolbarState';
|
|
18
|
+
import { createView } from './view';
|
|
19
|
+
|
|
20
|
+
export type ScriptToolbarProps = ThemedClassName<{
|
|
21
|
+
role?: string;
|
|
22
|
+
script: ScriptType;
|
|
23
|
+
state: ScriptToolbarState;
|
|
24
|
+
}>;
|
|
25
|
+
|
|
26
|
+
const createToolbar = (state: ScriptToolbarState) => {
|
|
27
|
+
const templateSelect = createTemplateSelect();
|
|
28
|
+
const format = createFormat();
|
|
29
|
+
const gap = createGapSeparator();
|
|
30
|
+
const deploy = createDeploy(state);
|
|
31
|
+
const view = createView(state);
|
|
32
|
+
return {
|
|
33
|
+
nodes: [...templateSelect.nodes, ...format.nodes, ...gap.nodes, ...deploy.nodes, ...view.nodes],
|
|
34
|
+
edges: [...templateSelect.edges, ...format.edges, ...gap.edges, ...deploy.edges, ...view.edges],
|
|
35
|
+
};
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
export const ScriptToolbar = ({ script, role, state, classNames }: ScriptToolbarProps) => {
|
|
39
|
+
const handleAction = useToolbarAction({ state, script });
|
|
40
|
+
const toolbarCreator = useCallback(() => createToolbar(state), [state]);
|
|
41
|
+
const menu = useMenuActions(toolbarCreator);
|
|
42
|
+
return (
|
|
43
|
+
<div role='none' className={stackItemContentToolbarClassNames(role)}>
|
|
44
|
+
<ElevationProvider elevation={role === 'section' ? 'positioned' : 'base'}>
|
|
45
|
+
<MenuProvider {...menu} onAction={handleAction} attendableId={fullyQualifiedId(script)}>
|
|
46
|
+
<ToolbarMenu classNames={classNames} />
|
|
47
|
+
</MenuProvider>
|
|
48
|
+
</ElevationProvider>
|
|
49
|
+
</div>
|
|
50
|
+
);
|
|
51
|
+
};
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { useCallback, useEffect, useMemo } from 'react';
|
|
6
|
+
|
|
7
|
+
import {
|
|
8
|
+
FunctionType,
|
|
9
|
+
getInvocationUrl,
|
|
10
|
+
getUserFunctionUrlInMetadata,
|
|
11
|
+
incrementSemverPatch,
|
|
12
|
+
type ScriptType,
|
|
13
|
+
setUserFunctionUrlInMetadata,
|
|
14
|
+
uploadWorkerFunction,
|
|
15
|
+
} from '@dxos/functions';
|
|
16
|
+
import { log } from '@dxos/log';
|
|
17
|
+
import { useClient } from '@dxos/react-client';
|
|
18
|
+
import { Filter, create, getMeta, getSpace, makeRef, useQuery } from '@dxos/react-client/echo';
|
|
19
|
+
import { useTranslation } from '@dxos/react-ui';
|
|
20
|
+
import { createMenuAction } from '@dxos/react-ui-menu';
|
|
21
|
+
|
|
22
|
+
import { type ViewState } from './view';
|
|
23
|
+
import { Bundler } from '../../bundler';
|
|
24
|
+
import { SCRIPT_PLUGIN } from '../../meta';
|
|
25
|
+
|
|
26
|
+
export type DeployActionProperties = { type: 'deploy' } | { type: 'copy' };
|
|
27
|
+
|
|
28
|
+
export type DeployState = ViewState & {
|
|
29
|
+
deploying: boolean;
|
|
30
|
+
deployed: boolean;
|
|
31
|
+
functionUrl: string;
|
|
32
|
+
error: string;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export const createDeploy = (state: Partial<DeployState>) => {
|
|
36
|
+
const errorItem = createMenuAction('error', {
|
|
37
|
+
label: state.error ?? ['no error label', { ns: SCRIPT_PLUGIN }],
|
|
38
|
+
icon: 'ph--warning-circle--regular',
|
|
39
|
+
hidden: !state.error,
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
const deployAction = createMenuAction<DeployActionProperties>('deploy', {
|
|
43
|
+
type: 'deploy',
|
|
44
|
+
label: [state.deploying ? 'pending label' : 'deploy label', { ns: SCRIPT_PLUGIN }],
|
|
45
|
+
icon: state.deploying ? 'ph--spinner-gap--regular' : 'ph--cloud-arrow-up--regular',
|
|
46
|
+
disabled: state.deploying,
|
|
47
|
+
classNames: state.deploying ? '[&_svg]:animate-spin' : '',
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
const copyAction = createMenuAction<DeployActionProperties>('copy', {
|
|
51
|
+
type: 'copy',
|
|
52
|
+
label: ['copy link label', { ns: SCRIPT_PLUGIN }],
|
|
53
|
+
icon: 'ph--link--regular',
|
|
54
|
+
disabled: !state.functionUrl,
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
return {
|
|
58
|
+
nodes: [errorItem, deployAction, copyAction],
|
|
59
|
+
edges: [
|
|
60
|
+
{ source: 'root', target: 'error' },
|
|
61
|
+
{ source: 'root', target: 'deploy' },
|
|
62
|
+
{ source: 'root', target: 'copy' },
|
|
63
|
+
],
|
|
64
|
+
};
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
const useDeployDeps = ({ state, script }: { state: Partial<DeployState>; script: ScriptType }) => {
|
|
68
|
+
const space = getSpace(script);
|
|
69
|
+
const [fn] = useQuery(space, Filter.schema(FunctionType, { source: script }));
|
|
70
|
+
const client = useClient();
|
|
71
|
+
const existingFunctionUrl = useMemo(() => fn && getUserFunctionUrlInMetadata(getMeta(fn)), [fn]);
|
|
72
|
+
return { space, fn, client, existingFunctionUrl };
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
export const useDeployState = ({ state, script }: { state: Partial<DeployState>; script: ScriptType }) => {
|
|
76
|
+
const { space, client, existingFunctionUrl } = useDeployDeps({ state, script });
|
|
77
|
+
useEffect(() => {
|
|
78
|
+
if (!existingFunctionUrl) {
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
state.functionUrl = getInvocationUrl(existingFunctionUrl, client.config.values.runtime?.services?.edge?.url ?? '', {
|
|
83
|
+
spaceId: space?.id,
|
|
84
|
+
});
|
|
85
|
+
}, [existingFunctionUrl, space]);
|
|
86
|
+
|
|
87
|
+
useEffect(() => {
|
|
88
|
+
state.deployed = Boolean(existingFunctionUrl) && !script.changed;
|
|
89
|
+
}, [script.changed, existingFunctionUrl]);
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
export const useDeployHandler = ({ state, script }: { state: Partial<DeployState>; script: ScriptType }) => {
|
|
93
|
+
const { space, fn, client, existingFunctionUrl } = useDeployDeps({ state, script });
|
|
94
|
+
const { t } = useTranslation(SCRIPT_PLUGIN);
|
|
95
|
+
|
|
96
|
+
return useCallback(async () => {
|
|
97
|
+
if (!script.source || !space) {
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
state.error = undefined;
|
|
102
|
+
state.deploying = true;
|
|
103
|
+
|
|
104
|
+
try {
|
|
105
|
+
const existingFunctionId = existingFunctionUrl?.split('/').at(-1);
|
|
106
|
+
|
|
107
|
+
const bundler = new Bundler({ platform: 'browser', sandboxedModules: [], remoteModules: {} });
|
|
108
|
+
const buildResult = await bundler.bundle(script.source.target!.content);
|
|
109
|
+
if (buildResult.error || !buildResult.bundle) {
|
|
110
|
+
throw buildResult.error;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const { functionId, version, meta } = await uploadWorkerFunction({
|
|
114
|
+
client,
|
|
115
|
+
spaceId: space.id,
|
|
116
|
+
version: fn ? incrementSemverPatch(fn.version) : '0.0.1',
|
|
117
|
+
functionId: existingFunctionId,
|
|
118
|
+
source: buildResult.bundle,
|
|
119
|
+
});
|
|
120
|
+
if (functionId === undefined || version === undefined) {
|
|
121
|
+
throw new Error(`Upload didn't return expected data: functionId=${functionId}, version=${version}`);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
log.info('function uploaded', { functionId, version });
|
|
125
|
+
if (fn) {
|
|
126
|
+
fn.version = version;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const deployedFunction =
|
|
130
|
+
fn ?? space.db.add(create(FunctionType, { name: functionId, version, source: makeRef(script) }));
|
|
131
|
+
|
|
132
|
+
script.changed = false;
|
|
133
|
+
|
|
134
|
+
if (meta.inputSchema) {
|
|
135
|
+
deployedFunction.inputSchema = meta.inputSchema;
|
|
136
|
+
} else {
|
|
137
|
+
log.verbose('no input schema in function metadata', { functionId });
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
setUserFunctionUrlInMetadata(getMeta(deployedFunction), `/${space.id}/${functionId}`);
|
|
141
|
+
|
|
142
|
+
state.view = 'split';
|
|
143
|
+
} catch (err: any) {
|
|
144
|
+
log.catch(err);
|
|
145
|
+
state.error = t('upload failed label');
|
|
146
|
+
}
|
|
147
|
+
state.deploying = false;
|
|
148
|
+
}, [fn, client, t, existingFunctionUrl, script, script.name, script.source, space?.id ?? null]);
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
export const useCopyHandler = ({ state }: { state: Partial<DeployState> }) =>
|
|
152
|
+
useCallback(async () => {
|
|
153
|
+
if (!state.functionUrl) {
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
await navigator.clipboard.writeText(state.functionUrl);
|
|
157
|
+
}, [state.functionUrl]);
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { format } from 'prettier';
|
|
6
|
+
import prettierPluginEstree from 'prettier/plugins/estree';
|
|
7
|
+
import prettierPluginTypescript from 'prettier/plugins/typescript';
|
|
8
|
+
import { useCallback } from 'react';
|
|
9
|
+
|
|
10
|
+
import { type ScriptType } from '@dxos/functions';
|
|
11
|
+
import { log } from '@dxos/log';
|
|
12
|
+
import { createMenuAction } from '@dxos/react-ui-menu';
|
|
13
|
+
|
|
14
|
+
import { SCRIPT_PLUGIN } from '../../meta';
|
|
15
|
+
|
|
16
|
+
export type FormatActionProperties = { type: 'format' };
|
|
17
|
+
|
|
18
|
+
export const createFormat = () => {
|
|
19
|
+
const formatAction = createMenuAction('format', {
|
|
20
|
+
label: ['format label', { ns: SCRIPT_PLUGIN }],
|
|
21
|
+
icon: 'ph--magic-wand--regular',
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
return {
|
|
25
|
+
nodes: [formatAction],
|
|
26
|
+
edges: [{ source: 'root', target: 'format' }],
|
|
27
|
+
};
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export const useFormatHandler = ({ script }: { script: ScriptType }) =>
|
|
31
|
+
useCallback(async () => {
|
|
32
|
+
if (!script.source) {
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
try {
|
|
37
|
+
script.source.target!.content = await format(script.source.target!.content, {
|
|
38
|
+
parser: 'typescript',
|
|
39
|
+
plugins: [prettierPluginEstree, prettierPluginTypescript],
|
|
40
|
+
semi: true,
|
|
41
|
+
singleQuote: true,
|
|
42
|
+
});
|
|
43
|
+
} catch (err: any) {
|
|
44
|
+
// TODO(wittjosiah): Show error in UI.
|
|
45
|
+
log.catch(err);
|
|
46
|
+
}
|
|
47
|
+
}, [script.source]);
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { useCallback } from 'react';
|
|
6
|
+
|
|
7
|
+
import { FUNCTIONS_PRESET_META_KEY, type ScriptType } from '@dxos/functions';
|
|
8
|
+
import { getMeta } from '@dxos/live-object';
|
|
9
|
+
import { createMenuItemGroup, createMenuAction } from '@dxos/react-ui-menu';
|
|
10
|
+
|
|
11
|
+
import { SCRIPT_PLUGIN } from '../../meta';
|
|
12
|
+
import { templates } from '../../templates';
|
|
13
|
+
|
|
14
|
+
export type TemplateActionProperties = { type: 'template'; value: string };
|
|
15
|
+
|
|
16
|
+
const createTemplateSelectGroup = () => {
|
|
17
|
+
return createMenuItemGroup('template-select', {
|
|
18
|
+
label: ['template select group label', { ns: SCRIPT_PLUGIN }],
|
|
19
|
+
variant: 'dropdownMenu',
|
|
20
|
+
});
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const createTemplateSelectActions = () => {
|
|
24
|
+
return templates.map((template) => {
|
|
25
|
+
return createMenuAction<TemplateActionProperties>(`template--${template.id}`, {
|
|
26
|
+
label: template.name,
|
|
27
|
+
value: template.id,
|
|
28
|
+
type: 'template',
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export const createTemplateSelect = () => {
|
|
34
|
+
const templateSelectGroup = createTemplateSelectGroup();
|
|
35
|
+
const templateSelectActions = createTemplateSelectActions();
|
|
36
|
+
return {
|
|
37
|
+
nodes: [templateSelectGroup, ...templateSelectActions],
|
|
38
|
+
edges: [
|
|
39
|
+
{ source: 'root', target: 'template-select' },
|
|
40
|
+
...templateSelectActions.map((action) => ({ source: 'template-select', target: action.id })),
|
|
41
|
+
],
|
|
42
|
+
};
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
export const useTemplateSelectHandler = ({ script }: { script: ScriptType }) => {
|
|
46
|
+
return useCallback(
|
|
47
|
+
(templateId: string) => {
|
|
48
|
+
const template = templates.find((template) => template.id === templateId);
|
|
49
|
+
if (template) {
|
|
50
|
+
script.name = template.name;
|
|
51
|
+
script.source!.target!.content = template.source;
|
|
52
|
+
const metaKeys = getMeta(script).keys;
|
|
53
|
+
const oldPresetIndex = metaKeys.findIndex((key) => key.source === FUNCTIONS_PRESET_META_KEY);
|
|
54
|
+
if (oldPresetIndex >= 0) {
|
|
55
|
+
metaKeys.splice(oldPresetIndex, 1);
|
|
56
|
+
}
|
|
57
|
+
if (template.presetId) {
|
|
58
|
+
metaKeys.push({ source: FUNCTIONS_PRESET_META_KEY, id: template.presetId });
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
},
|
|
62
|
+
[script],
|
|
63
|
+
);
|
|
64
|
+
};
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { useCallback } from 'react';
|
|
6
|
+
|
|
7
|
+
import { type ScriptType } from '@dxos/functions';
|
|
8
|
+
import { log } from '@dxos/log';
|
|
9
|
+
import { type MenuAction, type MenuActionHandler } from '@dxos/react-ui-menu';
|
|
10
|
+
|
|
11
|
+
import { type DeployActionProperties, useDeployHandler, useCopyHandler } from './deploy';
|
|
12
|
+
import { type FormatActionProperties, useFormatHandler } from './format';
|
|
13
|
+
import { type TemplateActionProperties, useTemplateSelectHandler } from './templateSelect';
|
|
14
|
+
import { type ScriptToolbarState } from './useToolbarState';
|
|
15
|
+
import { type ViewActionProperties } from './view';
|
|
16
|
+
|
|
17
|
+
export type ScriptToolbarActionProperties =
|
|
18
|
+
| TemplateActionProperties
|
|
19
|
+
| ViewActionProperties
|
|
20
|
+
| FormatActionProperties
|
|
21
|
+
| DeployActionProperties;
|
|
22
|
+
|
|
23
|
+
export type ScriptToolbarAction = MenuAction<ScriptToolbarActionProperties>;
|
|
24
|
+
|
|
25
|
+
export const useToolbarAction = (props: { state: ScriptToolbarState; script: ScriptType }) => {
|
|
26
|
+
const handleTemplateSelect = useTemplateSelectHandler(props);
|
|
27
|
+
const handleFormat = useFormatHandler(props);
|
|
28
|
+
const handleDeploy = useDeployHandler(props);
|
|
29
|
+
const handleCopy = useCopyHandler(props);
|
|
30
|
+
return useCallback(
|
|
31
|
+
((action: ScriptToolbarAction) => {
|
|
32
|
+
switch (action.properties.type) {
|
|
33
|
+
case 'template':
|
|
34
|
+
handleTemplateSelect(action.properties.value);
|
|
35
|
+
break;
|
|
36
|
+
case 'view':
|
|
37
|
+
props.state.view = action.properties.value;
|
|
38
|
+
break;
|
|
39
|
+
case 'format':
|
|
40
|
+
void handleFormat();
|
|
41
|
+
break;
|
|
42
|
+
case 'deploy':
|
|
43
|
+
void handleDeploy();
|
|
44
|
+
break;
|
|
45
|
+
case 'copy':
|
|
46
|
+
void handleCopy();
|
|
47
|
+
break;
|
|
48
|
+
default:
|
|
49
|
+
log.error('Unknown action type', action);
|
|
50
|
+
}
|
|
51
|
+
}) as MenuActionHandler,
|
|
52
|
+
[handleTemplateSelect, handleFormat, handleDeploy, handleCopy],
|
|
53
|
+
);
|
|
54
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { useMemo } from 'react';
|
|
6
|
+
|
|
7
|
+
import { create } from '@dxos/live-object';
|
|
8
|
+
|
|
9
|
+
import { type DeployState } from './deploy';
|
|
10
|
+
import { type ViewState } from './view';
|
|
11
|
+
|
|
12
|
+
export type ScriptToolbarState = Partial<DeployState & ViewState>;
|
|
13
|
+
|
|
14
|
+
export const useToolbarState = (initialState: ScriptToolbarState = {}) => {
|
|
15
|
+
return useMemo(() => create<ScriptToolbarState>(initialState), []);
|
|
16
|
+
};
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { createMenuAction, createMenuItemGroup } from '@dxos/react-ui-menu';
|
|
6
|
+
|
|
7
|
+
import { SCRIPT_PLUGIN } from '../../meta';
|
|
8
|
+
|
|
9
|
+
export type ViewType = 'editor' | 'split' | 'debug';
|
|
10
|
+
|
|
11
|
+
export type ViewState = { view: ViewType };
|
|
12
|
+
|
|
13
|
+
export type ViewActionProperties = { type: 'view'; value: ViewType };
|
|
14
|
+
|
|
15
|
+
const views: Record<ViewType, string> = {
|
|
16
|
+
editor: 'ph--code--regular',
|
|
17
|
+
split: 'ph--square-split-vertical--regular',
|
|
18
|
+
debug: 'ph--bug--regular',
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
const createViewGroupItem = (state: Partial<ViewState>) => {
|
|
22
|
+
return createMenuItemGroup('view', {
|
|
23
|
+
label: ['view group label', { ns: SCRIPT_PLUGIN }],
|
|
24
|
+
variant: 'dropdownMenu',
|
|
25
|
+
applyActive: true,
|
|
26
|
+
value: state.view,
|
|
27
|
+
});
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const createViewActions = (state: Partial<ViewState>) => {
|
|
31
|
+
return Object.entries(views).map(([viewType, icon]) => {
|
|
32
|
+
return createMenuAction<ViewActionProperties>(`view--${viewType}`, {
|
|
33
|
+
type: 'view',
|
|
34
|
+
value: viewType as ViewType,
|
|
35
|
+
label: [`view ${viewType} label`, { ns: SCRIPT_PLUGIN }],
|
|
36
|
+
icon,
|
|
37
|
+
checked: state.view === viewType,
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
export const createView = (state: Partial<ViewState>) => {
|
|
43
|
+
const viewGroupItem = createViewGroupItem(state);
|
|
44
|
+
const viewActions = createViewActions(state);
|
|
45
|
+
return {
|
|
46
|
+
nodes: [viewGroupItem, ...viewActions],
|
|
47
|
+
edges: [
|
|
48
|
+
{ source: 'root', target: 'view' },
|
|
49
|
+
...viewActions.map((action) => ({ source: 'view', target: action.id })),
|
|
50
|
+
],
|
|
51
|
+
};
|
|
52
|
+
};
|