@dxos/plugin-script 0.6.11 → 0.6.12-main.2d19bf1

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.
Files changed (182) hide show
  1. package/README.md +1 -1
  2. package/dist/lib/browser/{ScriptEditor-5Y3EROG3.mjs → ScriptContainer-M4MDQTBU.mjs} +258 -106
  3. package/dist/lib/browser/ScriptContainer-M4MDQTBU.mjs.map +7 -0
  4. package/dist/lib/browser/{chunk-DL5XQRQM.mjs → chunk-G6FWCTGT.mjs} +7 -5
  5. package/dist/lib/browser/chunk-G6FWCTGT.mjs.map +7 -0
  6. package/dist/lib/browser/{chunk-RHEZ5LUU.mjs → chunk-G6H56WFS.mjs} +51 -2
  7. package/dist/lib/browser/{chunk-RHEZ5LUU.mjs.map → chunk-G6H56WFS.mjs.map} +4 -4
  8. package/dist/lib/browser/{chunk-O4LWBX6A.mjs → chunk-KXVQPS4Y.mjs} +2 -2
  9. package/dist/lib/browser/{chunk-O4LWBX6A.mjs.map → chunk-KXVQPS4Y.mjs.map} +2 -2
  10. package/dist/lib/browser/chunk-SGQHRB4P.mjs +17 -0
  11. package/dist/lib/browser/chunk-SGQHRB4P.mjs.map +7 -0
  12. package/dist/lib/browser/edge/index.mjs +3 -1
  13. package/dist/lib/browser/index.mjs +80 -62
  14. package/dist/lib/browser/index.mjs.map +4 -4
  15. package/dist/lib/browser/meta.json +1 -1
  16. package/dist/lib/browser/meta.mjs +1 -1
  17. package/dist/lib/browser/types/index.mjs +2 -2
  18. package/dist/lib/node/ScriptContainer-KMIPKT3H.cjs +668 -0
  19. package/dist/lib/node/ScriptContainer-KMIPKT3H.cjs.map +7 -0
  20. package/dist/lib/node/{chunk-5RBVXKXT.cjs → chunk-25PQE7FL.cjs} +6 -6
  21. package/dist/lib/node/{chunk-5RBVXKXT.cjs.map → chunk-25PQE7FL.cjs.map} +2 -2
  22. package/dist/lib/node/{chunk-4GTXIELQ.cjs → chunk-ACLCRWDB.cjs} +44 -6
  23. package/dist/lib/node/chunk-ACLCRWDB.cjs.map +7 -0
  24. package/dist/lib/node/{TypescriptEditor-M3WWFIP2.cjs → chunk-EJC55SDD.cjs} +18 -8
  25. package/dist/lib/node/chunk-EJC55SDD.cjs.map +7 -0
  26. package/dist/lib/node/{chunk-PFH3ROEH.cjs → chunk-UZ3ZXEBF.cjs} +11 -8
  27. package/dist/lib/node/chunk-UZ3ZXEBF.cjs.map +7 -0
  28. package/dist/lib/node/edge/index.cjs +10 -8
  29. package/dist/lib/node/edge/index.cjs.map +2 -2
  30. package/dist/lib/node/index.cjs +95 -82
  31. package/dist/lib/node/index.cjs.map +4 -4
  32. package/dist/lib/node/meta.cjs +3 -3
  33. package/dist/lib/node/meta.cjs.map +1 -1
  34. package/dist/lib/node/meta.json +1 -1
  35. package/dist/lib/node/types/index.cjs +5 -5
  36. package/dist/lib/node/types/index.cjs.map +1 -1
  37. package/dist/lib/node-esm/ScriptContainer-EAE6L5JV.mjs +666 -0
  38. package/dist/lib/node-esm/ScriptContainer-EAE6L5JV.mjs.map +7 -0
  39. package/dist/lib/node-esm/chunk-2NX3HWPM.mjs +245 -0
  40. package/dist/lib/node-esm/chunk-2NX3HWPM.mjs.map +7 -0
  41. package/dist/lib/node-esm/chunk-7EDYDVQH.mjs +47 -0
  42. package/dist/lib/node-esm/chunk-7EDYDVQH.mjs.map +7 -0
  43. package/dist/lib/node-esm/chunk-GO7HYFM5.mjs +19 -0
  44. package/dist/lib/node-esm/chunk-GO7HYFM5.mjs.map +7 -0
  45. package/dist/lib/node-esm/chunk-LDLNTVAK.mjs +280 -0
  46. package/dist/lib/node-esm/chunk-LDLNTVAK.mjs.map +7 -0
  47. package/dist/lib/node-esm/edge/index.mjs +22 -0
  48. package/dist/lib/node-esm/index.mjs +310 -0
  49. package/dist/lib/node-esm/index.mjs.map +7 -0
  50. package/dist/lib/node-esm/meta.json +1 -0
  51. package/dist/lib/node-esm/meta.mjs +10 -0
  52. package/dist/lib/node-esm/meta.mjs.map +7 -0
  53. package/dist/lib/node-esm/types/index.mjs +13 -0
  54. package/dist/lib/node-esm/types/index.mjs.map +7 -0
  55. package/dist/types/src/ScriptPlugin.d.ts.map +1 -1
  56. package/dist/types/src/components/DebugPanel/DebugPanel.d.ts.map +1 -0
  57. package/dist/types/src/components/DebugPanel/index.d.ts +2 -0
  58. package/dist/types/src/components/DebugPanel/index.d.ts.map +1 -0
  59. package/dist/types/src/components/{ScriptBlock/FrameContainer → FrameContainer}/FrameContainer.d.ts +1 -1
  60. package/dist/types/src/components/FrameContainer/FrameContainer.d.ts.map +1 -0
  61. package/dist/types/src/components/FrameContainer/frame.d.ts.map +1 -0
  62. package/dist/types/src/components/FrameContainer/index.d.ts.map +1 -0
  63. package/dist/types/src/components/ScriptBlock/ScriptBlock.d.ts +1 -3
  64. package/dist/types/src/components/ScriptBlock/ScriptBlock.d.ts.map +1 -1
  65. package/dist/types/src/components/ScriptBlock/ScriptBlock.stories.d.ts +1 -1
  66. package/dist/types/src/components/ScriptBlock/ScriptBlock.stories.d.ts.map +1 -1
  67. package/dist/types/src/components/ScriptContainer.d.ts +5 -0
  68. package/dist/types/src/components/ScriptContainer.d.ts.map +1 -0
  69. package/dist/types/src/components/ScriptEditor.d.ts +4 -5
  70. package/dist/types/src/components/ScriptEditor.d.ts.map +1 -1
  71. package/dist/types/src/components/ScriptSettings/ScriptSettings.d.ts +6 -0
  72. package/dist/types/src/components/ScriptSettings/ScriptSettings.d.ts.map +1 -0
  73. package/dist/types/src/components/ScriptSettings/index.d.ts +2 -0
  74. package/dist/types/src/components/ScriptSettings/index.d.ts.map +1 -0
  75. package/dist/types/src/components/Toolbar/Toolbar.d.ts +23 -0
  76. package/dist/types/src/components/Toolbar/Toolbar.d.ts.map +1 -0
  77. package/dist/types/src/components/Toolbar/Toolbar.stories.d.ts +17 -0
  78. package/dist/types/src/components/Toolbar/Toolbar.stories.d.ts.map +1 -0
  79. package/dist/types/src/components/Toolbar/index.d.ts +2 -0
  80. package/dist/types/src/components/Toolbar/index.d.ts.map +1 -0
  81. package/dist/types/src/components/{TypescriptEditor.d.ts → TypescriptEditor/TypescriptEditor.d.ts} +1 -2
  82. package/dist/types/src/components/TypescriptEditor/TypescriptEditor.d.ts.map +1 -0
  83. package/dist/types/src/components/TypescriptEditor/TypescriptEditor.stories.d.ts +10 -0
  84. package/dist/types/src/components/TypescriptEditor/TypescriptEditor.stories.d.ts.map +1 -0
  85. package/dist/types/src/components/TypescriptEditor/index.d.ts +2 -0
  86. package/dist/types/src/components/TypescriptEditor/index.d.ts.map +1 -0
  87. package/dist/types/src/components/index.d.ts +2 -2
  88. package/dist/types/src/components/index.d.ts.map +1 -1
  89. package/dist/types/src/edge/functions.d.ts +1 -0
  90. package/dist/types/src/edge/functions.d.ts.map +1 -1
  91. package/dist/types/src/meta.d.ts +4 -9
  92. package/dist/types/src/meta.d.ts.map +1 -1
  93. package/dist/types/src/templates/chat.d.ts +6 -0
  94. package/dist/types/src/templates/chat.d.ts.map +1 -0
  95. package/dist/types/src/templates/chess.d.ts +6 -0
  96. package/dist/types/src/templates/chess.d.ts.map +1 -0
  97. package/dist/types/src/templates/echo.d.ts +6 -0
  98. package/dist/types/src/templates/echo.d.ts.map +1 -0
  99. package/dist/types/src/templates/email.d.ts +6 -0
  100. package/dist/types/src/templates/email.d.ts.map +1 -0
  101. package/dist/types/src/templates/forex.d.ts +6 -0
  102. package/dist/types/src/templates/forex.d.ts.map +1 -0
  103. package/dist/types/src/templates.d.ts +8 -0
  104. package/dist/types/src/templates.d.ts.map +1 -0
  105. package/dist/types/src/translations.d.ts +2 -0
  106. package/dist/types/src/translations.d.ts.map +1 -1
  107. package/dist/types/src/types/script.d.ts +4 -4
  108. package/dist/types/src/types/types.d.ts +4 -3
  109. package/dist/types/src/types/types.d.ts.map +1 -1
  110. package/package.json +61 -55
  111. package/src/ScriptPlugin.tsx +22 -38
  112. package/src/bundler/bundler.test.ts +12 -2
  113. package/src/components/{DebugPanel.tsx → DebugPanel/DebugPanel.tsx} +48 -41
  114. package/src/components/DebugPanel/index.ts +5 -0
  115. package/src/components/{ScriptBlock/FrameContainer → FrameContainer}/FrameContainer.tsx +1 -1
  116. package/src/components/ScriptBlock/ScriptBlock.stories.tsx +4 -3
  117. package/src/components/ScriptBlock/ScriptBlock.tsx +10 -36
  118. package/src/components/ScriptContainer.tsx +22 -0
  119. package/src/components/ScriptEditor.tsx +47 -46
  120. package/src/components/ScriptSettings/ScriptSettings.tsx +47 -0
  121. package/src/components/ScriptSettings/index.ts +5 -0
  122. package/src/components/{Toolbar.stories.tsx → Toolbar/Toolbar.stories.tsx} +10 -1
  123. package/src/components/Toolbar/Toolbar.tsx +180 -0
  124. package/src/components/{ScriptBlock/Splitter → Toolbar}/index.ts +1 -1
  125. package/src/components/{TypescriptEditor.stories.tsx → TypescriptEditor/TypescriptEditor.stories.tsx} +7 -28
  126. package/src/components/{TypescriptEditor.tsx → TypescriptEditor/TypescriptEditor.tsx} +1 -3
  127. package/src/components/TypescriptEditor/index.ts +5 -0
  128. package/src/components/index.ts +3 -2
  129. package/src/edge/functions.ts +1 -0
  130. package/src/meta.ts +15 -0
  131. package/src/templates/chat.ts +102 -0
  132. package/src/templates/chess.ts +41 -0
  133. package/src/templates/echo.ts +15 -0
  134. package/src/templates/email.ts +38 -0
  135. package/src/templates/forex.ts +21 -0
  136. package/src/templates.ts +54 -0
  137. package/src/translations.ts +2 -0
  138. package/src/types/types.ts +9 -5
  139. package/dist/lib/browser/ScriptEditor-5Y3EROG3.mjs.map +0 -7
  140. package/dist/lib/browser/TypescriptEditor-GPE72Y6I.mjs +0 -9
  141. package/dist/lib/browser/chunk-DL5XQRQM.mjs.map +0 -7
  142. package/dist/lib/browser/chunk-JBACGDOJ.mjs +0 -21
  143. package/dist/lib/browser/chunk-JBACGDOJ.mjs.map +0 -7
  144. package/dist/lib/browser/chunk-MRGOT5RF.mjs +0 -78
  145. package/dist/lib/browser/chunk-MRGOT5RF.mjs.map +0 -7
  146. package/dist/lib/node/ScriptEditor-YCUQFWQM.cjs +0 -526
  147. package/dist/lib/node/ScriptEditor-YCUQFWQM.cjs.map +0 -7
  148. package/dist/lib/node/TypescriptEditor-M3WWFIP2.cjs.map +0 -7
  149. package/dist/lib/node/chunk-4GTXIELQ.cjs.map +0 -7
  150. package/dist/lib/node/chunk-4VMNBXWU.cjs +0 -54
  151. package/dist/lib/node/chunk-4VMNBXWU.cjs.map +0 -7
  152. package/dist/lib/node/chunk-IMLDKD5Y.cjs +0 -111
  153. package/dist/lib/node/chunk-IMLDKD5Y.cjs.map +0 -7
  154. package/dist/lib/node/chunk-PFH3ROEH.cjs.map +0 -7
  155. package/dist/types/src/components/DebugPanel.d.ts.map +0 -1
  156. package/dist/types/src/components/ScriptBlock/FrameContainer/FrameContainer.d.ts.map +0 -1
  157. package/dist/types/src/components/ScriptBlock/FrameContainer/frame.d.ts.map +0 -1
  158. package/dist/types/src/components/ScriptBlock/FrameContainer/index.d.ts.map +0 -1
  159. package/dist/types/src/components/ScriptBlock/Splitter/Splitter.d.ts +0 -15
  160. package/dist/types/src/components/ScriptBlock/Splitter/Splitter.d.ts.map +0 -1
  161. package/dist/types/src/components/ScriptBlock/Splitter/Splitter.stories.d.ts +0 -13
  162. package/dist/types/src/components/ScriptBlock/Splitter/Splitter.stories.d.ts.map +0 -1
  163. package/dist/types/src/components/ScriptBlock/Splitter/index.d.ts +0 -2
  164. package/dist/types/src/components/ScriptBlock/Splitter/index.d.ts.map +0 -1
  165. package/dist/types/src/components/Toolbar.d.ts +0 -13
  166. package/dist/types/src/components/Toolbar.d.ts.map +0 -1
  167. package/dist/types/src/components/Toolbar.stories.d.ts +0 -14
  168. package/dist/types/src/components/Toolbar.stories.d.ts.map +0 -1
  169. package/dist/types/src/components/TypescriptEditor.d.ts.map +0 -1
  170. package/dist/types/src/components/TypescriptEditor.stories.d.ts +0 -10
  171. package/dist/types/src/components/TypescriptEditor.stories.d.ts.map +0 -1
  172. package/src/components/ScriptBlock/Splitter/Splitter.stories.tsx +0 -37
  173. package/src/components/ScriptBlock/Splitter/Splitter.tsx +0 -52
  174. package/src/components/Toolbar.tsx +0 -147
  175. package/src/meta.tsx +0 -19
  176. /package/dist/lib/{browser/TypescriptEditor-GPE72Y6I.mjs.map → node-esm/edge/index.mjs.map} +0 -0
  177. /package/dist/types/src/components/{DebugPanel.d.ts → DebugPanel/DebugPanel.d.ts} +0 -0
  178. /package/dist/types/src/components/{ScriptBlock/FrameContainer → FrameContainer}/frame.d.ts +0 -0
  179. /package/dist/types/src/components/{ScriptBlock/FrameContainer → FrameContainer}/index.d.ts +0 -0
  180. /package/src/components/{ScriptBlock/FrameContainer → FrameContainer}/frame.html +0 -0
  181. /package/src/components/{ScriptBlock/FrameContainer → FrameContainer}/frame.tsx +0 -0
  182. /package/src/components/{ScriptBlock/FrameContainer → FrameContainer}/index.ts +0 -0
@@ -2,19 +2,17 @@
2
2
  // Copyright 2023 DXOS.org
3
3
  //
4
4
 
5
- import { Check, Play, Warning } from '@phosphor-icons/react';
6
5
  // @ts-ignore
7
6
  import wasmUrl from 'esbuild-wasm/esbuild.wasm?url';
8
7
  import React, { useCallback, useEffect, useMemo, useState } from 'react';
9
8
 
10
9
  import { createDocAccessor, DocAccessor } from '@dxos/react-client/echo';
11
- import { DensityProvider, Toolbar, Button } from '@dxos/react-ui';
12
- import { mx, getSize } from '@dxos/react-ui-theme';
10
+ import { DensityProvider, Toolbar, Button, Icon } from '@dxos/react-ui';
11
+ import { mx } from '@dxos/react-ui-theme';
13
12
 
14
- import { FrameContainer } from './FrameContainer';
15
- import { Splitter, SplitterSelector, type View } from './Splitter';
16
13
  import { Bundler, type BundlerResult, initializeBundler } from '../../bundler';
17
14
  import { type ScriptType } from '../../types';
15
+ import { FrameContainer } from '../FrameContainer';
18
16
  import { ScriptEditor } from '../ScriptEditor';
19
17
 
20
18
  // Keep in sync with packages/apps/composer-app/script-frame/main.tsx .
@@ -29,7 +27,6 @@ const PROVIDED_MODULES = [
29
27
 
30
28
  export type ScriptBlockProps = {
31
29
  script: ScriptType;
32
- view?: View;
33
30
  hideSelector?: boolean;
34
31
  classes?: {
35
32
  root?: string;
@@ -44,17 +41,8 @@ export type ScriptBlockProps = {
44
41
  * @deprecated
45
42
  */
46
43
  // TODO(burdon): Cache compiled results in context.
47
- export const ScriptBlock = ({
48
- script,
49
- view: controlledView,
50
- hideSelector,
51
- classes,
52
- containerUrl,
53
- }: ScriptBlockProps) => {
44
+ export const ScriptBlock = ({ script, hideSelector, classes, containerUrl }: ScriptBlockProps) => {
54
45
  const source = useMemo(() => script.source && createDocAccessor(script.source, ['content']), [script.source]);
55
- const [view, setView] = useState<View>(controlledView ?? 'editor');
56
- useEffect(() => handleSetView(controlledView ?? 'editor'), [controlledView]);
57
-
58
46
  const [result, setResult] = useState<BundlerResult>();
59
47
  const bundler = useMemo(
60
48
  () => new Bundler({ platform: 'browser', sandboxedModules: PROVIDED_MODULES, remoteModules: {} }),
@@ -78,16 +66,6 @@ export const ScriptBlock = ({
78
66
  return () => clearTimeout(t);
79
67
  }, [source]);
80
68
 
81
- const handleSetView = useCallback(
82
- (view: View) => {
83
- setView(view);
84
- if (!result && view !== 'editor') {
85
- void handleExec(false);
86
- }
87
- },
88
- [result],
89
- );
90
-
91
69
  const handleExec = useCallback(
92
70
  async (auto = true) => {
93
71
  if (!source) {
@@ -95,11 +73,8 @@ export const ScriptBlock = ({
95
73
  }
96
74
  const result = await bundler.bundle(DocAccessor.getValue(source));
97
75
  setResult(result);
98
- if (auto && view === 'editor') {
99
- setView('preview');
100
- }
101
76
  },
102
- [source, view],
77
+ [source],
103
78
  );
104
79
 
105
80
  return (
@@ -107,29 +82,28 @@ export const ScriptBlock = ({
107
82
  {!hideSelector && (
108
83
  <DensityProvider density='fine'>
109
84
  <Toolbar.Root classNames={mx('mb-2', classes?.toolbar)}>
110
- <SplitterSelector view={view} onChange={handleSetView} />
111
85
  <div className='grow' />
112
86
  {result?.bundle && !result?.error && (
113
87
  <div title={String(result.error)}>
114
- <Check className={mx(getSize(5), 'text-green-500')} />
88
+ <Icon icon='ph--check--regular' size={5} classNames='text-green-500' />
115
89
  </div>
116
90
  )}
117
91
  {result?.error && (
118
92
  <div title={String(result.error)}>
119
- <Warning className={mx(getSize(5), 'text-orange-500')} />
93
+ <Icon icon='ph--warning--regular' size={5} classNames='text-orange-500' />
120
94
  </div>
121
95
  )}
122
96
  <Button variant='ghost' onClick={() => handleExec()}>
123
- <Play className={getSize(5)} />
97
+ <Icon icon='ph--play--regular' size={5} />
124
98
  </Button>
125
99
  </Toolbar.Root>
126
100
  </DensityProvider>
127
101
  )}
128
102
 
129
- <Splitter view={view}>
103
+ <div className='flex'>
130
104
  <ScriptEditor script={script} />
131
105
  {result && <FrameContainer key={script.id} result={result} containerUrl={containerUrl} />}
132
- </Splitter>
106
+ </div>
133
107
  </div>
134
108
  );
135
109
  };
@@ -0,0 +1,22 @@
1
+ //
2
+ // Copyright 2023 DXOS.org
3
+ //
4
+
5
+ import React from 'react';
6
+
7
+ import { fullyQualifiedId } from '@dxos/react-client/echo';
8
+ import { useAttendableAttributes } from '@dxos/react-ui-attention';
9
+ import { mx } from '@dxos/react-ui-theme';
10
+
11
+ import { ScriptEditor, type ScriptEditorProps } from './ScriptEditor';
12
+
13
+ const ScriptContainer = ({ script, ...props }: ScriptEditorProps) => {
14
+ const attendableAttrs = useAttendableAttributes(fullyQualifiedId(script));
15
+ return (
16
+ <div role='none' className={mx('flex row-span-2 w-full overflow-hidden attention-surface')} {...attendableAttrs}>
17
+ <ScriptEditor {...props} script={script} />
18
+ </div>
19
+ );
20
+ };
21
+
22
+ export default ScriptContainer;
@@ -10,23 +10,14 @@ import React, { useCallback, useMemo, useState } from 'react';
10
10
 
11
11
  import { log } from '@dxos/log';
12
12
  import { useClient } from '@dxos/react-client';
13
- import {
14
- create,
15
- createDocAccessor,
16
- Filter,
17
- fullyQualifiedId,
18
- getMeta,
19
- getSpace,
20
- useQuery,
21
- } from '@dxos/react-client/echo';
13
+ import { create, createDocAccessor, Filter, getMeta, getSpace, useQuery } from '@dxos/react-client/echo';
22
14
  import { useIdentity } from '@dxos/react-client/halo';
23
- import { useTranslation } from '@dxos/react-ui';
24
- import { useHasAttention } from '@dxos/react-ui-attention';
15
+ import { useTranslation, type ThemedClassName } from '@dxos/react-ui';
25
16
  import { createDataExtensions, listener } from '@dxos/react-ui-editor';
26
17
  import { mx } from '@dxos/react-ui-theme';
27
18
 
28
19
  import { DebugPanel } from './DebugPanel';
29
- import { Toolbar } from './Toolbar';
20
+ import { type TemplateSelectProps, Toolbar, type ViewType } from './Toolbar';
30
21
  import { TypescriptEditor, type TypescriptEditorProps } from './TypescriptEditor';
31
22
  import { Bundler } from '../bundler';
32
23
  import {
@@ -34,18 +25,19 @@ import {
34
25
  publicKeyToDid,
35
26
  setUserFunctionUrlInMetadata,
36
27
  uploadWorkerFunction,
28
+ USERFUNCTIONS_PRESET_META_KEY,
37
29
  } from '../edge';
38
30
  import { SCRIPT_PLUGIN } from '../meta';
31
+ import { templates } from '../templates';
39
32
  import { FunctionType, type ScriptType } from '../types';
40
33
 
41
- export type ScriptEditorProps = {
34
+ export type ScriptEditorProps = ThemedClassName<{
42
35
  script: ScriptType;
43
- role?: string;
44
- } & Pick<TypescriptEditorProps, 'env'>;
36
+ }> &
37
+ Pick<TypescriptEditorProps, 'env'>;
45
38
 
46
- export const ScriptEditor = ({ env, script, role }: ScriptEditorProps) => {
39
+ export const ScriptEditor = ({ classNames, script, env }: ScriptEditorProps) => {
47
40
  const { t } = useTranslation(SCRIPT_PLUGIN);
48
- const hasAttention = useHasAttention(fullyQualifiedId(script));
49
41
  const client = useClient();
50
42
  const identity = useIdentity();
51
43
  const space = getSpace(script);
@@ -73,7 +65,7 @@ export const ScriptEditor = ({ env, script, role }: ScriptEditorProps) => {
73
65
  [script, script.source, space, identity],
74
66
  );
75
67
 
76
- const [showPanel, setShowPanel] = useState(false);
68
+ const [view, setView] = useState<ViewType>('editor');
77
69
  const initialValue = useMemo(() => script.source?.content, [script.source]);
78
70
  const existingFunctionUrl = fn && getUserFunctionUrlInMetadata(getMeta(fn));
79
71
  const [error, setError] = useState<string>();
@@ -103,6 +95,22 @@ export const ScriptEditor = ({ env, script, role }: ScriptEditorProps) => {
103
95
  }
104
96
  }, [script.source]);
105
97
 
98
+ const handleTemplateChange: TemplateSelectProps['onTemplateSelect'] = (id) => {
99
+ const template = templates.find((template) => template.id === id);
100
+ if (template) {
101
+ script.name = template.name;
102
+ script.source!.content = template.source;
103
+ const metaKeys = getMeta(script).keys;
104
+ const oldPresetIndex = metaKeys.findIndex((key) => key.source === USERFUNCTIONS_PRESET_META_KEY);
105
+ if (oldPresetIndex >= 0) {
106
+ metaKeys.splice(oldPresetIndex, 1);
107
+ }
108
+ if (template.presetId) {
109
+ metaKeys.push({ source: USERFUNCTIONS_PRESET_META_KEY, id: template.presetId });
110
+ }
111
+ }
112
+ };
113
+
106
114
  const handleDeploy = useCallback(async () => {
107
115
  if (!script.source || !identity || !space) {
108
116
  return;
@@ -114,11 +122,7 @@ export const ScriptEditor = ({ env, script, role }: ScriptEditorProps) => {
114
122
  const existingFunctionId = existingFunctionUrl?.split('/').at(-1);
115
123
  const ownerDid = (existingFunctionUrl?.split('/').at(-2) as DID) ?? publicKeyToDid(identity.identityKey);
116
124
 
117
- const bundler = new Bundler({
118
- platform: 'browser',
119
- sandboxedModules: [],
120
- remoteModules: {},
121
- });
125
+ const bundler = new Bundler({ platform: 'browser', sandboxedModules: [], remoteModules: {} });
122
126
  const buildResult = await bundler.bundle(script.source.content);
123
127
  if (buildResult.error || !buildResult.bundle) {
124
128
  throw buildResult.error;
@@ -134,15 +138,18 @@ export const ScriptEditor = ({ env, script, role }: ScriptEditorProps) => {
134
138
  if (result !== 'success' || functionId === undefined || functionVersionNumber === undefined) {
135
139
  throw new Error(errorMessage);
136
140
  }
141
+
137
142
  log.info('function uploaded', { functionId, functionVersionNumber });
138
143
  if (fn) {
139
144
  fn.version = functionVersionNumber;
140
145
  }
146
+
141
147
  const deployedFunction =
142
148
  fn ?? space.db.add(create(FunctionType, { version: functionVersionNumber, source: script }));
143
149
  const meta = getMeta(deployedFunction);
144
- setUserFunctionUrlInMetadata(meta, `/${ownerDid}/${functionId}`);
145
150
  script.changed = false;
151
+ setUserFunctionUrlInMetadata(meta, `/${ownerDid}/${functionId}`);
152
+ setView('split');
146
153
  } catch (err: any) {
147
154
  log.catch(err);
148
155
  setError(t('upload failed label'));
@@ -165,38 +172,32 @@ export const ScriptEditor = ({ env, script, role }: ScriptEditorProps) => {
165
172
  }, [existingFunctionUrl, space]);
166
173
 
167
174
  return (
168
- <div
169
- role='none'
170
- className={mx(
171
- 'flex flex-col is-full bs-full overflow-hidden',
172
- role === 'article' && 'row-span-2',
173
- hasAttention && 'attention-surface',
174
- )}
175
- >
176
- <TypescriptEditor
177
- id={script.id}
178
- env={env}
179
- initialValue={initialValue}
180
- extensions={extensions}
181
- className='flex is-full bs-full overflow-hidden ch-focus-ring-inset-over-all'
182
- />
183
-
175
+ <div role='none' className={mx('flex flex-col w-full overflow-hidden divide-y divide-separator', classNames)}>
184
176
  <Toolbar
185
- classNames='border-t border-separator'
186
177
  deployed={Boolean(existingFunctionUrl) && !script.changed}
187
178
  functionUrl={functionUrl}
188
179
  error={error}
189
- showPanel={showPanel}
180
+ view={view}
181
+ templates={templates}
190
182
  onDeploy={handleDeploy}
191
183
  onFormat={handleFormat}
192
- onTogglePanel={async () => setShowPanel((showDetails) => !showDetails)}
184
+ onViewChange={setView}
185
+ onTemplateSelect={handleTemplateChange}
193
186
  />
194
187
 
195
- {showPanel && (
188
+ {view !== 'preview' && (
189
+ <TypescriptEditor
190
+ id={script.id}
191
+ env={env}
192
+ initialValue={initialValue}
193
+ extensions={extensions}
194
+ className='flex is-full bs-full overflow-hidden ch-focus-ring-inset-over-all'
195
+ />
196
+ )}
197
+
198
+ {view !== 'editor' && (
196
199
  <DebugPanel functionUrl={functionUrl} binding={fn?.binding} onBindingChange={handleBindingChange} />
197
200
  )}
198
201
  </div>
199
202
  );
200
203
  };
201
-
202
- export default ScriptEditor;
@@ -0,0 +1,47 @@
1
+ //
2
+ // Copyright 2023 DXOS.org
3
+ //
4
+
5
+ import React from 'react';
6
+
7
+ import { SettingsValue } from '@dxos/plugin-settings';
8
+ import { type Credential } from '@dxos/protocols/proto/dxos/halo/credentials';
9
+ import { useClient } from '@dxos/react-client';
10
+ import { Button, useTranslation } from '@dxos/react-ui';
11
+
12
+ import { SCRIPT_PLUGIN } from '../../meta';
13
+ import { type ScriptSettingsProps } from '../../types';
14
+
15
+ export const ScriptSettings = ({ settings }: { settings: ScriptSettingsProps }) => {
16
+ const { t } = useTranslation(SCRIPT_PLUGIN);
17
+ const client = useClient();
18
+
19
+ // TODO(burdon): Check token.
20
+ const handleAuthenticate = async () => {
21
+ const { identityKey } = client.halo.identity.get()!;
22
+ await client.halo.writeCredentials([
23
+ {
24
+ issuer: identityKey,
25
+ issuanceDate: new Date(),
26
+ subject: {
27
+ id: identityKey,
28
+ assertion: {
29
+ '@type': 'dxos.halo.credentials.ServiceAccess',
30
+ serverName: 'hub.dxos.network',
31
+ serverKey: identityKey,
32
+ identityKey,
33
+ capabilities: ['composer:beta'],
34
+ },
35
+ },
36
+ } satisfies Credential,
37
+ ]);
38
+ };
39
+
40
+ return (
41
+ <>
42
+ <SettingsValue label={t('authenticate action label')}>
43
+ <Button onClick={handleAuthenticate}>{t('authenticate button label')}</Button>
44
+ </SettingsValue>
45
+ </>
46
+ );
47
+ };
@@ -0,0 +1,5 @@
1
+ //
2
+ // Copyright 2024 DXOS.org
3
+ //
4
+
5
+ export * from './ScriptSettings';
@@ -7,6 +7,7 @@ import '@dxos-theme';
7
7
  import { withLayout, withTheme } from '@dxos/storybook-utils';
8
8
 
9
9
  import { Toolbar } from './Toolbar';
10
+ import { templates } from '../../templates';
10
11
 
11
12
  export default {
12
13
  title: 'plugin-script/Toolbar',
@@ -14,4 +15,12 @@ export default {
14
15
  decorators: [withTheme, withLayout({ tooltips: true })],
15
16
  };
16
17
 
17
- export const Default = { args: { binding: 'example', deployed: true } };
18
+ export const Default = {
19
+ args: {
20
+ binding: 'example',
21
+ deployed: true,
22
+ templates,
23
+ onFormat: () => console.log('Format'),
24
+ onTogglePanel: () => {},
25
+ },
26
+ };
@@ -0,0 +1,180 @@
1
+ //
2
+ // Copyright 2024 DXOS.org
3
+ //
4
+
5
+ import React, { useState } from 'react';
6
+
7
+ import {
8
+ DensityProvider,
9
+ ElevationProvider,
10
+ Icon,
11
+ Select,
12
+ type ThemedClassName,
13
+ ToggleGroup,
14
+ ToggleGroupItem,
15
+ type ToolbarButtonProps,
16
+ Toolbar as NaturalToolbar,
17
+ Tooltip,
18
+ useTranslation,
19
+ } from '@dxos/react-ui';
20
+
21
+ import { SCRIPT_PLUGIN } from '../../meta';
22
+ import { type Template } from '../../templates';
23
+
24
+ export type ToolbarProps = ThemedClassName<
25
+ {
26
+ deployed?: boolean;
27
+ error?: string;
28
+ functionUrl?: string;
29
+ onDeploy?: () => Promise<void>;
30
+ onFormat?: () => Promise<void>;
31
+ } & TemplateSelectProps &
32
+ ViewSelectorProps
33
+ >;
34
+
35
+ export const Toolbar = ({
36
+ classNames,
37
+ deployed,
38
+ error,
39
+ functionUrl,
40
+ view,
41
+ templates,
42
+ onDeploy,
43
+ onFormat,
44
+ onViewChange,
45
+ onTemplateSelect,
46
+ }: ToolbarProps) => {
47
+ const { t } = useTranslation(SCRIPT_PLUGIN);
48
+ const [pending, setPending] = useState(false);
49
+
50
+ const handleCopyLink = async () => {
51
+ if (!functionUrl) {
52
+ return;
53
+ }
54
+
55
+ await navigator.clipboard.writeText(functionUrl);
56
+ };
57
+
58
+ const handleDeploy = async () => {
59
+ if (!pending) {
60
+ setPending(true);
61
+ await onDeploy?.();
62
+ setPending(false);
63
+ }
64
+ };
65
+
66
+ // TODO(burdon): Factor out common toolbar components with sheet/editor?
67
+ return (
68
+ <DensityProvider density='fine'>
69
+ <ElevationProvider elevation='chrome'>
70
+ <NaturalToolbar.Root classNames={['p-1', classNames]} style={{ contain: 'layout' }}>
71
+ {templates && <TemplateSelect templates={templates} onTemplateSelect={onTemplateSelect} />}
72
+ {onFormat && <ToolbarIconButton icon='ph--magic-wand--regular' text={t('format label')} onClick={onFormat} />}
73
+
74
+ <div role='separator' className='grow' />
75
+
76
+ {error ? (
77
+ <Tooltip.Root>
78
+ <Tooltip.Trigger>
79
+ <Icon icon='ph--warning-circle--regular' size={4} />
80
+ </Tooltip.Trigger>
81
+ <Tooltip.Portal>
82
+ <Tooltip.Content>
83
+ <Tooltip.Arrow />
84
+ {error}
85
+ </Tooltip.Content>
86
+ </Tooltip.Portal>
87
+ </Tooltip.Root>
88
+ ) : null}
89
+
90
+ {functionUrl && deployed && (
91
+ <ToolbarIconButton icon='ph--link--regular' text={t('copy link label')} onClick={handleCopyLink} />
92
+ )}
93
+
94
+ {onDeploy && (
95
+ <ToolbarIconButton
96
+ icon={pending ? 'ph--spinner-gap--regular' : 'ph--cloud-arrow-up--regular'}
97
+ classNames={[pending && 'animate-spin']}
98
+ text={pending ? t('pending label') : t('deploy label')}
99
+ onClick={handleDeploy}
100
+ />
101
+ )}
102
+
103
+ <ViewSelector view={view} onViewChange={onViewChange} />
104
+ </NaturalToolbar.Root>
105
+ </ElevationProvider>
106
+ </DensityProvider>
107
+ );
108
+ };
109
+
110
+ const ToolbarIconButton = ({
111
+ icon,
112
+ text,
113
+ classNames,
114
+ disabled,
115
+ onClick,
116
+ }: { icon: string; text: string } & Pick<ToolbarButtonProps, 'classNames' | 'disabled' | 'onClick'>) => {
117
+ return (
118
+ <Tooltip.Root>
119
+ <Tooltip.Trigger asChild>
120
+ <NaturalToolbar.Button variant='ghost' disabled={disabled} onClick={onClick}>
121
+ <Icon icon={icon} size={4} classNames={classNames} />
122
+ </NaturalToolbar.Button>
123
+ </Tooltip.Trigger>
124
+ <Tooltip.Portal>
125
+ <Tooltip.Content>
126
+ <Tooltip.Arrow />
127
+ {text}
128
+ </Tooltip.Content>
129
+ </Tooltip.Portal>
130
+ </Tooltip.Root>
131
+ );
132
+ };
133
+
134
+ export type ViewType = 'editor' | 'split' | 'preview';
135
+
136
+ export type TemplateSelectProps = {
137
+ templates?: Template[];
138
+ onTemplateSelect?: (id: string) => void;
139
+ };
140
+
141
+ export const TemplateSelect = ({ templates = [], onTemplateSelect }: TemplateSelectProps) => {
142
+ return (
143
+ <Select.Root value={''} onValueChange={onTemplateSelect}>
144
+ <Select.TriggerButton />
145
+ <Select.Portal>
146
+ <Select.Content>
147
+ <Select.Viewport>
148
+ <Select.Option value={''}>Template</Select.Option>
149
+ {templates.map(({ id, name }) => (
150
+ <Select.Option key={id} value={id}>
151
+ {name}
152
+ </Select.Option>
153
+ ))}
154
+ </Select.Viewport>
155
+ </Select.Content>
156
+ </Select.Portal>
157
+ </Select.Root>
158
+ );
159
+ };
160
+
161
+ export type ViewSelectorProps = {
162
+ view?: ViewType;
163
+ onViewChange?: (view: ViewType) => void;
164
+ };
165
+
166
+ export const ViewSelector = ({ view, onViewChange }: ViewSelectorProps) => {
167
+ return (
168
+ <ToggleGroup type='single' value={view} onValueChange={(value) => onViewChange?.(value as ViewType)}>
169
+ <ToggleGroupItem value='editor' classNames='bg-transparent text-subdued'>
170
+ <Icon icon='ph--code--regular' size={4} />
171
+ </ToggleGroupItem>
172
+ <ToggleGroupItem value='split' classNames='bg-transparent text-subdued'>
173
+ <Icon icon='ph--square-split-vertical--regular' size={4} />
174
+ </ToggleGroupItem>
175
+ <ToggleGroupItem value='preview' classNames='bg-transparent text-subdued'>
176
+ <Icon icon='ph--eye--regular' size={4} />
177
+ </ToggleGroupItem>
178
+ </ToggleGroup>
179
+ );
180
+ };
@@ -2,4 +2,4 @@
2
2
  // Copyright 2023 DXOS.org
3
3
  //
4
4
 
5
- export * from './Splitter';
5
+ export * from './Toolbar';
@@ -10,45 +10,24 @@ import { createDocAccessor, createEchoObject } from '@dxos/react-client/echo';
10
10
  import { createDataExtensions } from '@dxos/react-ui-editor';
11
11
 
12
12
  import { TypescriptEditor } from './TypescriptEditor';
13
+ import { templates } from '../../templates';
13
14
 
14
- // TODO(burdon): Typescript.
15
- // TODO(burdon): Effect schema.
16
- // TODO(burdon): JSX.
17
- // TODO(burdon): react-buddy for storybook?
18
-
19
- // Marijn
15
+ // TODO(burdon): Features:
20
16
  // - language support for S
21
17
  // - hierarchical editor (DND)
22
18
  // - virtual document image rendering
23
19
  // - mobile rendering error
24
20
 
25
- const examples: string[] = [
26
- [
27
- '// Example function.',
28
- 'export default function() {',
29
- ' const value = 100',
30
- ' return <div>{value}</div>;',
31
- '}',
32
- ].join('\n'),
33
- [
34
- '// Example schema.',
35
- 'S.Struct({',
36
- ' timestamp: S.Date,',
37
- ' title: S.String,',
38
- ' content: R.Text,',
39
- "}).pipe(S.identifier('dxos.org/schema/Test'))",
40
- '',
41
- ].join('\n'),
42
- ];
21
+ // TODO(burdon): JSX.
22
+ // TODO(burdon): Effect schema.
23
+ // TODO(burdon): react-buddy for storybook?
43
24
 
44
25
  const Story = () => {
45
- const object = useMemo(() => createEchoObject({ content: examples[0] }), []);
26
+ const object = useMemo(() => createEchoObject({ content: templates[0].source }), []);
46
27
  const initialValue = useMemo(() => object.content, [object]);
47
28
  const accessor = useMemo(() => createDocAccessor(object, ['content']), [object]);
48
29
  const extensions = useMemo(() => [createDataExtensions({ id: object.id, text: accessor })], [object.id, accessor]);
49
-
50
- // TODO(wittjosiah): Scroll past end breaks the editor.
51
- return <TypescriptEditor id='test' initialValue={initialValue} extensions={extensions} scrollPastEnd={false} />;
30
+ return <TypescriptEditor id='test' initialValue={initialValue} extensions={extensions} />;
52
31
  };
53
32
 
54
33
  export default {
@@ -23,13 +23,11 @@ import { nonNullable } from '@dxos/util';
23
23
 
24
24
  export type TypescriptEditorProps = {
25
25
  id: string;
26
- scrollPastEnd?: boolean;
27
26
  env?: VirtualTypeScriptEnvironment;
28
27
  } & Pick<UseTextEditorProps, 'className' | 'initialValue' | 'extensions' | 'scrollTo' | 'selection'>;
29
28
 
30
29
  export const TypescriptEditor = ({
31
30
  id,
32
- scrollPastEnd,
33
31
  env,
34
32
  className,
35
33
  initialValue,
@@ -49,7 +47,7 @@ export const TypescriptEditor = ({
49
47
  indentWithTab: true,
50
48
  lineNumbers: true,
51
49
  lineWrapping: false,
52
- scrollPastEnd,
50
+ scrollPastEnd: true,
53
51
  }),
54
52
  createThemeExtensions({
55
53
  themeMode,
@@ -0,0 +1,5 @@
1
+ //
2
+ // Copyright 2024 DXOS.org
3
+ //
4
+
5
+ export * from './TypescriptEditor';
@@ -4,5 +4,6 @@
4
4
 
5
5
  import React from 'react';
6
6
 
7
- export const ScriptEditor = React.lazy(() => import('./ScriptEditor'));
8
- export const TypescriptEditor = React.lazy(() => import('./TypescriptEditor'));
7
+ export * from './ScriptSettings';
8
+
9
+ export const ScriptContainer = React.lazy(() => import('./ScriptContainer'));
@@ -17,6 +17,7 @@ import { matchServiceCredential } from './hub-protocol';
17
17
 
18
18
  // TODO: use URL scheme for source?
19
19
  const USERFUNCTIONS_META_KEY = 'dxos.org/service/function';
20
+ export const USERFUNCTIONS_PRESET_META_KEY = 'dxos.org/service/function-preset';
20
21
  const USERFUNCTIONS_CREDENTIAL_CAPABILITY = 'composer:beta';
21
22
 
22
23
  // TODO: Synchronize API types with server