@dxos/plugin-script 0.6.12-main.ed7cda7 → 0.6.12-staging.0b4bb48

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 (179) hide show
  1. package/dist/lib/browser/{ScriptContainer-7DE7USCY.mjs → ScriptEditor-5Y3EROG3.mjs} +106 -257
  2. package/dist/lib/browser/ScriptEditor-5Y3EROG3.mjs.map +7 -0
  3. package/dist/lib/browser/TypescriptEditor-GPE72Y6I.mjs +9 -0
  4. package/dist/lib/browser/{chunk-G6FWCTGT.mjs → chunk-DL5XQRQM.mjs} +5 -7
  5. package/dist/lib/browser/chunk-DL5XQRQM.mjs.map +7 -0
  6. package/dist/lib/browser/{chunk-MDX7ACIK.mjs → chunk-JBACGDOJ.mjs} +6 -3
  7. package/dist/lib/browser/chunk-JBACGDOJ.mjs.map +7 -0
  8. package/dist/lib/browser/chunk-MRGOT5RF.mjs +78 -0
  9. package/dist/lib/browser/chunk-MRGOT5RF.mjs.map +7 -0
  10. package/dist/lib/browser/{chunk-CAEANETO.mjs → chunk-O4LWBX6A.mjs} +2 -2
  11. package/dist/lib/browser/{chunk-CAEANETO.mjs.map → chunk-O4LWBX6A.mjs.map} +2 -2
  12. package/dist/lib/browser/{chunk-G6H56WFS.mjs → chunk-RHEZ5LUU.mjs} +2 -51
  13. package/dist/lib/browser/{chunk-G6H56WFS.mjs.map → chunk-RHEZ5LUU.mjs.map} +4 -4
  14. package/dist/lib/browser/edge/index.mjs +1 -3
  15. package/dist/lib/browser/index.mjs +61 -80
  16. package/dist/lib/browser/index.mjs.map +4 -4
  17. package/dist/lib/browser/meta.json +1 -1
  18. package/dist/lib/browser/meta.mjs +1 -1
  19. package/dist/lib/browser/types/index.mjs +2 -2
  20. package/dist/lib/node/ScriptEditor-YCUQFWQM.cjs +526 -0
  21. package/dist/lib/node/ScriptEditor-YCUQFWQM.cjs.map +7 -0
  22. package/dist/lib/node/{chunk-OJ77BZRH.cjs → TypescriptEditor-M3WWFIP2.cjs} +8 -19
  23. package/dist/lib/node/TypescriptEditor-M3WWFIP2.cjs.map +7 -0
  24. package/dist/lib/node/{chunk-ACLCRWDB.cjs → chunk-4GTXIELQ.cjs} +6 -44
  25. package/dist/lib/node/chunk-4GTXIELQ.cjs.map +7 -0
  26. package/dist/lib/node/chunk-4VMNBXWU.cjs +54 -0
  27. package/dist/lib/node/chunk-4VMNBXWU.cjs.map +7 -0
  28. package/dist/lib/node/{chunk-2PZKJXNP.cjs → chunk-5RBVXKXT.cjs} +6 -6
  29. package/dist/lib/node/{chunk-2PZKJXNP.cjs.map → chunk-5RBVXKXT.cjs.map} +2 -2
  30. package/dist/lib/node/chunk-IMLDKD5Y.cjs +111 -0
  31. package/dist/lib/node/chunk-IMLDKD5Y.cjs.map +7 -0
  32. package/dist/lib/node/{chunk-UZ3ZXEBF.cjs → chunk-PFH3ROEH.cjs} +8 -11
  33. package/dist/lib/node/chunk-PFH3ROEH.cjs.map +7 -0
  34. package/dist/lib/node/edge/index.cjs +8 -10
  35. package/dist/lib/node/edge/index.cjs.map +2 -2
  36. package/dist/lib/node/index.cjs +79 -93
  37. package/dist/lib/node/index.cjs.map +4 -4
  38. package/dist/lib/node/meta.cjs +3 -3
  39. package/dist/lib/node/meta.cjs.map +1 -1
  40. package/dist/lib/node/meta.json +1 -1
  41. package/dist/lib/node/types/index.cjs +5 -5
  42. package/dist/lib/node/types/index.cjs.map +1 -1
  43. package/dist/types/src/ScriptPlugin.d.ts.map +1 -1
  44. package/dist/types/src/components/DebugPanel.d.ts.map +1 -0
  45. package/dist/types/src/components/{FrameContainer → ScriptBlock/FrameContainer}/FrameContainer.d.ts +1 -1
  46. package/dist/types/src/components/ScriptBlock/FrameContainer/FrameContainer.d.ts.map +1 -0
  47. package/dist/types/src/components/ScriptBlock/FrameContainer/frame.d.ts.map +1 -0
  48. package/dist/types/src/components/ScriptBlock/FrameContainer/index.d.ts.map +1 -0
  49. package/dist/types/src/components/ScriptBlock/ScriptBlock.d.ts +3 -1
  50. package/dist/types/src/components/ScriptBlock/ScriptBlock.d.ts.map +1 -1
  51. package/dist/types/src/components/ScriptBlock/ScriptBlock.stories.d.ts +1 -1
  52. package/dist/types/src/components/ScriptBlock/ScriptBlock.stories.d.ts.map +1 -1
  53. package/dist/types/src/components/ScriptBlock/Splitter/Splitter.d.ts +15 -0
  54. package/dist/types/src/components/ScriptBlock/Splitter/Splitter.d.ts.map +1 -0
  55. package/dist/types/src/components/ScriptBlock/Splitter/Splitter.stories.d.ts +13 -0
  56. package/dist/types/src/components/ScriptBlock/Splitter/Splitter.stories.d.ts.map +1 -0
  57. package/dist/types/src/components/ScriptBlock/Splitter/index.d.ts +2 -0
  58. package/dist/types/src/components/ScriptBlock/Splitter/index.d.ts.map +1 -0
  59. package/dist/types/src/components/ScriptEditor.d.ts +5 -4
  60. package/dist/types/src/components/ScriptEditor.d.ts.map +1 -1
  61. package/dist/types/src/components/Toolbar.d.ts +13 -0
  62. package/dist/types/src/components/Toolbar.d.ts.map +1 -0
  63. package/dist/types/src/components/Toolbar.stories.d.ts +14 -0
  64. package/dist/types/src/components/Toolbar.stories.d.ts.map +1 -0
  65. package/dist/types/src/components/{TypescriptEditor/TypescriptEditor.d.ts → TypescriptEditor.d.ts} +2 -1
  66. package/dist/types/src/components/TypescriptEditor.d.ts.map +1 -0
  67. package/dist/types/src/components/TypescriptEditor.stories.d.ts +10 -0
  68. package/dist/types/src/components/TypescriptEditor.stories.d.ts.map +1 -0
  69. package/dist/types/src/components/index.d.ts +2 -2
  70. package/dist/types/src/components/index.d.ts.map +1 -1
  71. package/dist/types/src/edge/functions.d.ts +0 -1
  72. package/dist/types/src/edge/functions.d.ts.map +1 -1
  73. package/dist/types/src/meta.d.ts +4 -1
  74. package/dist/types/src/meta.d.ts.map +1 -1
  75. package/dist/types/src/translations.d.ts +0 -2
  76. package/dist/types/src/translations.d.ts.map +1 -1
  77. package/dist/types/src/types/script.d.ts +4 -4
  78. package/dist/types/src/types/types.d.ts +3 -4
  79. package/dist/types/src/types/types.d.ts.map +1 -1
  80. package/package.json +49 -54
  81. package/src/ScriptPlugin.tsx +36 -21
  82. package/src/bundler/bundler.test.ts +2 -12
  83. package/src/components/{DebugPanel/DebugPanel.tsx → DebugPanel.tsx} +41 -48
  84. package/src/components/{FrameContainer → ScriptBlock/FrameContainer}/FrameContainer.tsx +1 -1
  85. package/src/components/ScriptBlock/ScriptBlock.stories.tsx +3 -4
  86. package/src/components/ScriptBlock/ScriptBlock.tsx +36 -10
  87. package/src/components/ScriptBlock/Splitter/Splitter.stories.tsx +37 -0
  88. package/src/components/ScriptBlock/Splitter/Splitter.tsx +52 -0
  89. package/src/components/{Toolbar → ScriptBlock/Splitter}/index.ts +1 -1
  90. package/src/components/ScriptEditor.tsx +46 -47
  91. package/src/components/{Toolbar/Toolbar.stories.tsx → Toolbar.stories.tsx} +1 -10
  92. package/src/components/Toolbar.tsx +147 -0
  93. package/src/components/{TypescriptEditor/TypescriptEditor.stories.tsx → TypescriptEditor.stories.tsx} +28 -7
  94. package/src/components/{TypescriptEditor/TypescriptEditor.tsx → TypescriptEditor.tsx} +3 -1
  95. package/src/components/index.ts +2 -3
  96. package/src/edge/functions.ts +0 -1
  97. package/src/meta.tsx +6 -2
  98. package/src/translations.ts +0 -2
  99. package/src/types/types.ts +5 -9
  100. package/dist/lib/browser/ScriptContainer-7DE7USCY.mjs.map +0 -7
  101. package/dist/lib/browser/chunk-G6FWCTGT.mjs.map +0 -7
  102. package/dist/lib/browser/chunk-MDX7ACIK.mjs.map +0 -7
  103. package/dist/lib/node/ScriptContainer-J76LKPAM.cjs +0 -667
  104. package/dist/lib/node/ScriptContainer-J76LKPAM.cjs.map +0 -7
  105. package/dist/lib/node/chunk-ACLCRWDB.cjs.map +0 -7
  106. package/dist/lib/node/chunk-OJ77BZRH.cjs.map +0 -7
  107. package/dist/lib/node/chunk-UZ3ZXEBF.cjs.map +0 -7
  108. package/dist/lib/node-esm/ScriptContainer-F6ACEA63.mjs +0 -665
  109. package/dist/lib/node-esm/ScriptContainer-F6ACEA63.mjs.map +0 -7
  110. package/dist/lib/node-esm/chunk-2NX3HWPM.mjs +0 -245
  111. package/dist/lib/node-esm/chunk-2NX3HWPM.mjs.map +0 -7
  112. package/dist/lib/node-esm/chunk-7BZDZF7Z.mjs +0 -47
  113. package/dist/lib/node-esm/chunk-7BZDZF7Z.mjs.map +0 -7
  114. package/dist/lib/node-esm/chunk-LDLNTVAK.mjs +0 -280
  115. package/dist/lib/node-esm/chunk-LDLNTVAK.mjs.map +0 -7
  116. package/dist/lib/node-esm/chunk-TZ3Z4SII.mjs +0 -20
  117. package/dist/lib/node-esm/chunk-TZ3Z4SII.mjs.map +0 -7
  118. package/dist/lib/node-esm/edge/index.mjs +0 -22
  119. package/dist/lib/node-esm/index.mjs +0 -311
  120. package/dist/lib/node-esm/index.mjs.map +0 -7
  121. package/dist/lib/node-esm/meta.json +0 -1
  122. package/dist/lib/node-esm/meta.mjs +0 -10
  123. package/dist/lib/node-esm/meta.mjs.map +0 -7
  124. package/dist/lib/node-esm/types/index.mjs +0 -13
  125. package/dist/lib/node-esm/types/index.mjs.map +0 -7
  126. package/dist/types/src/components/DebugPanel/DebugPanel.d.ts.map +0 -1
  127. package/dist/types/src/components/DebugPanel/index.d.ts +0 -2
  128. package/dist/types/src/components/DebugPanel/index.d.ts.map +0 -1
  129. package/dist/types/src/components/FrameContainer/FrameContainer.d.ts.map +0 -1
  130. package/dist/types/src/components/FrameContainer/frame.d.ts.map +0 -1
  131. package/dist/types/src/components/FrameContainer/index.d.ts.map +0 -1
  132. package/dist/types/src/components/ScriptContainer.d.ts +0 -5
  133. package/dist/types/src/components/ScriptContainer.d.ts.map +0 -1
  134. package/dist/types/src/components/ScriptSettings/ScriptSettings.d.ts +0 -6
  135. package/dist/types/src/components/ScriptSettings/ScriptSettings.d.ts.map +0 -1
  136. package/dist/types/src/components/ScriptSettings/index.d.ts +0 -2
  137. package/dist/types/src/components/ScriptSettings/index.d.ts.map +0 -1
  138. package/dist/types/src/components/Toolbar/Toolbar.d.ts +0 -23
  139. package/dist/types/src/components/Toolbar/Toolbar.d.ts.map +0 -1
  140. package/dist/types/src/components/Toolbar/Toolbar.stories.d.ts +0 -17
  141. package/dist/types/src/components/Toolbar/Toolbar.stories.d.ts.map +0 -1
  142. package/dist/types/src/components/Toolbar/index.d.ts +0 -2
  143. package/dist/types/src/components/Toolbar/index.d.ts.map +0 -1
  144. package/dist/types/src/components/TypescriptEditor/TypescriptEditor.d.ts.map +0 -1
  145. package/dist/types/src/components/TypescriptEditor/TypescriptEditor.stories.d.ts +0 -10
  146. package/dist/types/src/components/TypescriptEditor/TypescriptEditor.stories.d.ts.map +0 -1
  147. package/dist/types/src/components/TypescriptEditor/index.d.ts +0 -2
  148. package/dist/types/src/components/TypescriptEditor/index.d.ts.map +0 -1
  149. package/dist/types/src/templates/chat.d.ts +0 -6
  150. package/dist/types/src/templates/chat.d.ts.map +0 -1
  151. package/dist/types/src/templates/chess.d.ts +0 -6
  152. package/dist/types/src/templates/chess.d.ts.map +0 -1
  153. package/dist/types/src/templates/echo.d.ts +0 -6
  154. package/dist/types/src/templates/echo.d.ts.map +0 -1
  155. package/dist/types/src/templates/email.d.ts +0 -6
  156. package/dist/types/src/templates/email.d.ts.map +0 -1
  157. package/dist/types/src/templates/forex.d.ts +0 -6
  158. package/dist/types/src/templates/forex.d.ts.map +0 -1
  159. package/dist/types/src/templates.d.ts +0 -8
  160. package/dist/types/src/templates.d.ts.map +0 -1
  161. package/src/components/DebugPanel/index.ts +0 -5
  162. package/src/components/ScriptContainer.tsx +0 -22
  163. package/src/components/ScriptSettings/ScriptSettings.tsx +0 -47
  164. package/src/components/ScriptSettings/index.ts +0 -5
  165. package/src/components/Toolbar/Toolbar.tsx +0 -180
  166. package/src/components/TypescriptEditor/index.ts +0 -5
  167. package/src/templates/chat.ts +0 -102
  168. package/src/templates/chess.ts +0 -41
  169. package/src/templates/echo.ts +0 -15
  170. package/src/templates/email.ts +0 -38
  171. package/src/templates/forex.ts +0 -21
  172. package/src/templates.ts +0 -54
  173. /package/dist/lib/{node-esm/edge/index.mjs.map → browser/TypescriptEditor-GPE72Y6I.mjs.map} +0 -0
  174. /package/dist/types/src/components/{DebugPanel/DebugPanel.d.ts → DebugPanel.d.ts} +0 -0
  175. /package/dist/types/src/components/{FrameContainer → ScriptBlock/FrameContainer}/frame.d.ts +0 -0
  176. /package/dist/types/src/components/{FrameContainer → ScriptBlock/FrameContainer}/index.d.ts +0 -0
  177. /package/src/components/{FrameContainer → ScriptBlock/FrameContainer}/frame.html +0 -0
  178. /package/src/components/{FrameContainer → ScriptBlock/FrameContainer}/frame.tsx +0 -0
  179. /package/src/components/{FrameContainer → ScriptBlock/FrameContainer}/index.ts +0 -0
@@ -17,9 +17,8 @@ import { loadObjectReferences } from '@dxos/react-client/echo';
17
17
 
18
18
  import { initializeBundler } from './bundler';
19
19
  import { Compiler } from './compiler';
20
- import { ScriptContainer, ScriptSettings } from './components';
20
+ import { ScriptEditor } from './components';
21
21
  import meta, { SCRIPT_PLUGIN } from './meta';
22
- import { templates } from './templates';
23
22
  import translations from './translations';
24
23
  import { FunctionType, ScriptType } from './types';
25
24
  import { ScriptAction, type ScriptPluginProvides } from './types';
@@ -46,12 +45,12 @@ export const ScriptPlugin = (): PluginDefinition<ScriptPluginProvides> => {
46
45
  await initializeBundler({ wasmUrl });
47
46
  },
48
47
  provides: {
49
- settings: {},
50
48
  metadata: {
51
49
  records: {
52
50
  [ScriptType.typename]: {
53
51
  placeholder: ['object title placeholder', { ns: SCRIPT_PLUGIN }],
54
52
  icon: (props: IconProps) => <Code {...props} />,
53
+ iconSymbol: 'ph--code--regular',
55
54
  // TODO(wittjosiah): Move out of metadata.
56
55
  loadReferences: (script: ScriptType) => loadObjectReferences(script, (script) => [script.source]),
57
56
  },
@@ -61,12 +60,6 @@ export const ScriptPlugin = (): PluginDefinition<ScriptPluginProvides> => {
61
60
  echo: {
62
61
  schema: [ScriptType, FunctionType],
63
62
  },
64
- space: {
65
- onSpaceCreate: {
66
- label: ['create object label', { ns: SCRIPT_PLUGIN }],
67
- action: ScriptAction.CREATE,
68
- },
69
- },
70
63
  graph: {
71
64
  builder: (plugins) => {
72
65
  const client = resolvePlugin(plugins, parseClientPlugin)?.provides.client;
@@ -100,7 +93,8 @@ export const ScriptPlugin = (): PluginDefinition<ScriptPluginProvides> => {
100
93
  },
101
94
  properties: {
102
95
  label: ['create object label', { ns: SCRIPT_PLUGIN }],
103
- icon: 'ph--code--regular',
96
+ icon: (props: IconProps) => <Code {...props} />,
97
+ iconSymbol: 'ph--code--regular',
104
98
  testId: 'scriptPlugin.createObject',
105
99
  },
106
100
  },
@@ -109,19 +103,29 @@ export const ScriptPlugin = (): PluginDefinition<ScriptPluginProvides> => {
109
103
  });
110
104
  },
111
105
  },
106
+ stack: {
107
+ creators: [
108
+ {
109
+ id: 'create-stack-section-script',
110
+ testId: 'scriptPlugin.createSection',
111
+ type: ['plugin name', { ns: SCRIPT_PLUGIN }],
112
+ label: ['create stack section label', { ns: SCRIPT_PLUGIN }],
113
+ icon: (props: any) => <Code {...props} />,
114
+ intent: {
115
+ plugin: SCRIPT_PLUGIN,
116
+ action: ScriptAction.CREATE,
117
+ },
118
+ },
119
+ ],
120
+ },
112
121
  surface: {
113
122
  component: ({ data, role }) => {
114
- switch (role) {
115
- case 'settings': {
116
- return data.plugin === meta.id ? <ScriptSettings settings={{}} /> : null;
117
- }
123
+ if (role && !['article', 'section'].includes(role)) {
124
+ return null;
125
+ }
118
126
 
119
- case 'article': {
120
- if (data.object instanceof ScriptType) {
121
- return <ScriptContainer script={data.object} env={compiler.environment} />;
122
- }
123
- break;
124
- }
127
+ if (data.object instanceof ScriptType) {
128
+ return <ScriptEditor env={compiler.environment} script={data.object} role={role} />;
125
129
  }
126
130
 
127
131
  return null;
@@ -131,7 +135,7 @@ export const ScriptPlugin = (): PluginDefinition<ScriptPluginProvides> => {
131
135
  resolver: (intent) => {
132
136
  switch (intent.action) {
133
137
  case ScriptAction.CREATE: {
134
- return { data: create(ScriptType, { source: create(TextType, { content: templates[0].source }) }) };
138
+ return { data: create(ScriptType, { source: create(TextType, { content: example }) }) };
135
139
  }
136
140
  }
137
141
  },
@@ -139,3 +143,14 @@ export const ScriptPlugin = (): PluginDefinition<ScriptPluginProvides> => {
139
143
  },
140
144
  };
141
145
  };
146
+
147
+ // TODO(burdon): Import.
148
+ const example = [
149
+ 'export default async ({ ',
150
+ ' event: { data: { request } },',
151
+ ' context: { space, ai } ',
152
+ '}: any) => {',
153
+ " return new Response('Hello DXOS!');",
154
+ '};',
155
+ '',
156
+ ].join('\n');
@@ -2,21 +2,11 @@
2
2
  // Copyright 2022 DXOS.org
3
3
  //
4
4
 
5
- // @ts-ignore
6
- import wasmUrl from 'esbuild-wasm/esbuild.wasm?url';
7
- import { beforeAll, describe, expect, test } from 'vitest';
5
+ import { describe, expect, test } from 'vitest';
8
6
 
9
- import { isNode } from '@dxos/util';
10
-
11
- import { Bundler, initializeBundler } from './bundler';
7
+ import { Bundler } from './bundler';
12
8
 
13
9
  describe('Bundler', () => {
14
- beforeAll(async () => {
15
- if (!isNode()) {
16
- await initializeBundler({ wasmUrl });
17
- }
18
- });
19
-
20
10
  test('Basic', async () => {
21
11
  const bundler = new Bundler({ platform: 'node', sandboxedModules: [], remoteModules: {} });
22
12
  const result = await bundler.bundle('const x = 100'); // TODO(burdon): Test import.
@@ -18,7 +18,7 @@ import {
18
18
  import { SyntaxHighlighter } from '@dxos/react-ui-syntax-highlighter';
19
19
  import { mx } from '@dxos/react-ui-theme';
20
20
 
21
- import { SCRIPT_PLUGIN } from '../../meta';
21
+ import { SCRIPT_PLUGIN } from '../meta';
22
22
 
23
23
  type State = 'pending' | 'responding';
24
24
 
@@ -47,7 +47,7 @@ export const DebugPanel = ({ classNames, functionUrl, binding: _binding, onBindi
47
47
  const [result, setResult] = useState('');
48
48
  const [state, setState] = useState<State | null>(null);
49
49
 
50
- // TODO(burdon): Persistent history -- at least for session (non-space ECHO data?)
50
+ // TODO(burdon): Persistent history (non-space ECHO data?)
51
51
  const [history, setHistory] = useState<Message[]>([]);
52
52
 
53
53
  const scrollerRef = useRef<HTMLDivElement>(null);
@@ -215,62 +215,55 @@ export const DebugPanel = ({ classNames, functionUrl, binding: _binding, onBindi
215
215
  );
216
216
  };
217
217
 
218
+ const RobotAvatar = () => (
219
+ <Avatar.Root size={6} variant='circle'>
220
+ <Avatar.Frame>
221
+ <Avatar.Icon icon='ph--robot--regular' />
222
+ </Avatar.Frame>
223
+ </Avatar.Root>
224
+ );
225
+
226
+ const MessageItem = ({ classNames, message }: ThemedClassName<{ message: Message }>) => {
227
+ const { text, data, error } = message;
228
+ if (error) {
229
+ return <span className={mx(classNames, 'whitespace-pre text-error')}>{String(error)}</span>;
230
+ } else if (data) {
231
+ return (
232
+ <SyntaxHighlighter language='json' className={mx('text-xs', classNames)}>
233
+ {JSON.stringify(data, null, 2)}
234
+ </SyntaxHighlighter>
235
+ );
236
+ }
237
+
238
+ return <span className={mx(classNames)}>{text || '\u00D8'}</span>;
239
+ };
240
+
218
241
  // TODO(burdon): Replace with thread?
219
242
  // TODO(burdon): Button to delete individual messages.
220
- // TODO(burdon): Button to edit/re-run question.
243
+ // TODO(burdon): Button to re-run question.
221
244
  const MessageThread = ({ state, result, history }: { state: State | null; result: string; history: Message[] }) => {
222
245
  return (
223
246
  <>
224
247
  {history.map((message, i) => (
225
- <div key={i} className='grid grid-cols-[3rem_1fr_3rem]'>
226
- <div className='p-1'>{message.type === 'response' && <RobotAvatar />}</div>
227
- <div className='overflow-auto'>
228
- <MessageItem message={message} />
248
+ <div key={i} className='grid grid-cols-[3rem_1fr]'>
249
+ <div>{message.type === 'response' && <RobotAvatar />}</div>
250
+ <div className={mx('flex', message.type === 'request' && 'justify-end')}>
251
+ <MessageItem
252
+ classNames={[message.type === 'request' && 'p-1 px-2 rounded-lg bg-hoverSurface']}
253
+ message={message}
254
+ />
229
255
  </div>
230
256
  </div>
231
257
  ))}
232
- {result?.length > 0 && (
233
- <div className='grid grid-cols-[3rem_1fr_3rem]'>
234
- <div className='p-1'>
235
- {(state === 'pending' && <Icon icon='ph--spinner--regular' size={6} classNames='animate-spin' />) || (
236
- <Icon icon='ph--robot--regular' size={6} classNames='animate-[pulse_1s_ease-in-out_infinite]' />
237
- )}
238
- </div>
239
- <div className='overflow-auto'>
240
- <MessageItem message={{ type: 'response', text: result }} />
241
- </div>
258
+ <div className='grid grid-cols-[3rem_1fr]'>
259
+ <div>
260
+ {(state === 'pending' && <Icon icon='ph--spinner--regular' size={6} classNames='animate-spin-slow' />) ||
261
+ (result.length > 0 && <RobotAvatar />)}
242
262
  </div>
243
- )}
244
- </>
245
- );
246
- };
247
-
248
- const MessageItem = ({ classNames, message }: ThemedClassName<{ message: Message }>) => {
249
- const { type, text, data, error } = message;
250
- const wrapper = 'p-1 px-2 rounded-lg bg-hoverSurface overflow-auto';
251
- return (
252
- <div className={mx('flex', type === 'request' ? 'ml-[8rem] justify-end' : 'mr-[8rem]', classNames)}>
253
- {error && <div className={mx(wrapper, 'whitespace-pre text-error')}>{String(error)}</div>}
254
-
255
- {text !== undefined && (
256
- <div className={mx(wrapper, type === 'request' && 'bg-primary-400 dark:bg-primary-600')}>
257
- {text || '\u00D8'}
263
+ <div>
264
+ <span>{result}</span>
258
265
  </div>
259
- )}
260
-
261
- {data && (
262
- <SyntaxHighlighter language='json' className={mx(wrapper, 'px-8 py-2 text-xs')}>
263
- {JSON.stringify(data, null, 2)}
264
- </SyntaxHighlighter>
265
- )}
266
- </div>
266
+ </div>
267
+ </>
267
268
  );
268
269
  };
269
-
270
- const RobotAvatar = () => (
271
- <Avatar.Root size={6} variant='circle'>
272
- <Avatar.Frame>
273
- <Avatar.Icon icon='ph--robot--regular' />
274
- </Avatar.Frame>
275
- </Avatar.Root>
276
- );
@@ -11,7 +11,7 @@ import { baseSurface, mx } from '@dxos/react-ui-theme';
11
11
  import { createProtoRpcPeer } from '@dxos/rpc';
12
12
  import { createIFramePort } from '@dxos/rpc-tunnel';
13
13
 
14
- import { type BundlerResult } from '../../bundler';
14
+ import { type BundlerResult } from '../../../bundler';
15
15
 
16
16
  export type FrameContainerProps = {
17
17
  containerUrl: string;
@@ -3,7 +3,6 @@
3
3
  //
4
4
 
5
5
  import '@dxos-theme';
6
-
7
6
  import React, { useEffect, useMemo } from 'react';
8
7
 
9
8
  import { createSpaceObjectGenerator, TestSchemaType } from '@dxos/echo-generator';
@@ -13,10 +12,10 @@ import { create, createEchoObject, useSpaces } from '@dxos/react-client/echo';
13
12
  import { withClientProvider } from '@dxos/react-client/testing';
14
13
  import { withLayout, withTheme } from '@dxos/storybook-utils';
15
14
 
15
+ // @ts-ignore
16
+ import mainUrl from './FrameContainer/frame?url';
16
17
  import { ScriptBlock } from './ScriptBlock';
17
18
  import { ScriptType } from '../../types';
18
- // @ts-ignore
19
- import mainUrl from '../FrameContainer/frame?url';
20
19
 
21
20
  const code = [
22
21
  "import { Filter, useQuery, useSpaces} from '@dxos/react-client/echo';",
@@ -65,7 +64,7 @@ const Story = () => {
65
64
  export default {
66
65
  title: 'plugin-script/ScriptBlock',
67
66
  component: ScriptBlock,
68
- decorators: [withClientProvider({ createSpace: true }), withTheme, withLayout({ fullscreen: true, tooltips: true })],
67
+ decorators: [withTheme, withLayout({ fullscreen: true, tooltips: true }), withClientProvider({ createSpace: true })],
69
68
  render: Story,
70
69
  };
71
70
 
@@ -2,17 +2,19 @@
2
2
  // Copyright 2023 DXOS.org
3
3
  //
4
4
 
5
+ import { Check, Play, Warning } from '@phosphor-icons/react';
5
6
  // @ts-ignore
6
7
  import wasmUrl from 'esbuild-wasm/esbuild.wasm?url';
7
8
  import React, { useCallback, useEffect, useMemo, useState } from 'react';
8
9
 
9
10
  import { createDocAccessor, DocAccessor } from '@dxos/react-client/echo';
10
- import { DensityProvider, Toolbar, Button, Icon } from '@dxos/react-ui';
11
- import { mx } from '@dxos/react-ui-theme';
11
+ import { DensityProvider, Toolbar, Button } from '@dxos/react-ui';
12
+ import { mx, getSize } from '@dxos/react-ui-theme';
12
13
 
14
+ import { FrameContainer } from './FrameContainer';
15
+ import { Splitter, SplitterSelector, type View } from './Splitter';
13
16
  import { Bundler, type BundlerResult, initializeBundler } from '../../bundler';
14
17
  import { type ScriptType } from '../../types';
15
- import { FrameContainer } from '../FrameContainer';
16
18
  import { ScriptEditor } from '../ScriptEditor';
17
19
 
18
20
  // Keep in sync with packages/apps/composer-app/script-frame/main.tsx .
@@ -27,6 +29,7 @@ const PROVIDED_MODULES = [
27
29
 
28
30
  export type ScriptBlockProps = {
29
31
  script: ScriptType;
32
+ view?: View;
30
33
  hideSelector?: boolean;
31
34
  classes?: {
32
35
  root?: string;
@@ -41,8 +44,17 @@ export type ScriptBlockProps = {
41
44
  * @deprecated
42
45
  */
43
46
  // TODO(burdon): Cache compiled results in context.
44
- export const ScriptBlock = ({ script, hideSelector, classes, containerUrl }: ScriptBlockProps) => {
47
+ export const ScriptBlock = ({
48
+ script,
49
+ view: controlledView,
50
+ hideSelector,
51
+ classes,
52
+ containerUrl,
53
+ }: ScriptBlockProps) => {
45
54
  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
+
46
58
  const [result, setResult] = useState<BundlerResult>();
47
59
  const bundler = useMemo(
48
60
  () => new Bundler({ platform: 'browser', sandboxedModules: PROVIDED_MODULES, remoteModules: {} }),
@@ -66,6 +78,16 @@ export const ScriptBlock = ({ script, hideSelector, classes, containerUrl }: Scr
66
78
  return () => clearTimeout(t);
67
79
  }, [source]);
68
80
 
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
+
69
91
  const handleExec = useCallback(
70
92
  async (auto = true) => {
71
93
  if (!source) {
@@ -73,8 +95,11 @@ export const ScriptBlock = ({ script, hideSelector, classes, containerUrl }: Scr
73
95
  }
74
96
  const result = await bundler.bundle(DocAccessor.getValue(source));
75
97
  setResult(result);
98
+ if (auto && view === 'editor') {
99
+ setView('preview');
100
+ }
76
101
  },
77
- [source],
102
+ [source, view],
78
103
  );
79
104
 
80
105
  return (
@@ -82,28 +107,29 @@ export const ScriptBlock = ({ script, hideSelector, classes, containerUrl }: Scr
82
107
  {!hideSelector && (
83
108
  <DensityProvider density='fine'>
84
109
  <Toolbar.Root classNames={mx('mb-2', classes?.toolbar)}>
110
+ <SplitterSelector view={view} onChange={handleSetView} />
85
111
  <div className='grow' />
86
112
  {result?.bundle && !result?.error && (
87
113
  <div title={String(result.error)}>
88
- <Icon icon='ph--check--regular' size={5} classNames='text-green-500' />
114
+ <Check className={mx(getSize(5), 'text-green-500')} />
89
115
  </div>
90
116
  )}
91
117
  {result?.error && (
92
118
  <div title={String(result.error)}>
93
- <Icon icon='ph--warning--regular' size={5} classNames='text-orange-500' />
119
+ <Warning className={mx(getSize(5), 'text-orange-500')} />
94
120
  </div>
95
121
  )}
96
122
  <Button variant='ghost' onClick={() => handleExec()}>
97
- <Icon icon='ph--play--regular' size={5} />
123
+ <Play className={getSize(5)} />
98
124
  </Button>
99
125
  </Toolbar.Root>
100
126
  </DensityProvider>
101
127
  )}
102
128
 
103
- <div className='flex'>
129
+ <Splitter view={view}>
104
130
  <ScriptEditor script={script} />
105
131
  {result && <FrameContainer key={script.id} result={result} containerUrl={containerUrl} />}
106
- </div>
132
+ </Splitter>
107
133
  </div>
108
134
  );
109
135
  };
@@ -0,0 +1,37 @@
1
+ //
2
+ // Copyright 2023 DXOS.org
3
+ //
4
+
5
+ import '@dxos-theme';
6
+
7
+ import React, { useState } from 'react';
8
+
9
+ import { DensityProvider } from '@dxos/react-ui';
10
+
11
+ import { Splitter, SplitterSelector, type View } from './Splitter';
12
+
13
+ const Story = () => {
14
+ const [view, setView] = useState<View>();
15
+ return (
16
+ <div>
17
+ <DensityProvider density='fine'>
18
+ <SplitterSelector view={view} onChange={setView} />
19
+ </DensityProvider>
20
+ <Splitter view={view}>
21
+ <div>1</div>
22
+ <div>2</div>
23
+ </Splitter>
24
+ </div>
25
+ );
26
+ };
27
+
28
+ export default {
29
+ title: 'plugin-script/Splitter',
30
+ component: Splitter,
31
+ render: Story,
32
+ parameters: {
33
+ layout: 'fullscreen',
34
+ },
35
+ };
36
+
37
+ export const Default = {};
@@ -0,0 +1,52 @@
1
+ //
2
+ // Copyright 2023 DXOS.org
3
+ //
4
+
5
+ import { Code, SquareSplitHorizontal, Eye } from '@phosphor-icons/react';
6
+ import React, { type ReactNode } from 'react';
7
+
8
+ import { ToggleGroup, ToggleGroupItem, Toolbar } from '@dxos/react-ui';
9
+ import { getSize, mx } from '@dxos/react-ui-theme';
10
+
11
+ export type View = 'editor' | 'preview' | 'split';
12
+
13
+ export type SplitterProps = {
14
+ view?: View;
15
+ className?: string;
16
+ children?: ReactNode[];
17
+ };
18
+
19
+ // TODO(burdon): Surfaces?
20
+ export const Splitter = ({ view = 'split', className, children = [] }: SplitterProps) => {
21
+ const [left, right] = children;
22
+ return (
23
+ <div className={mx('flex overflow-hidden grow', className)}>
24
+ {left && view !== 'preview' && <div className='flex flex-1 shrink-0 overflow-hidden'>{left}</div>}
25
+ {right && view !== 'editor' && <div className='flex flex-1 shrink-0 overflow-hidden'>{right}</div>}
26
+ </div>
27
+ );
28
+ };
29
+
30
+ export type SplitterSelectorProps = {
31
+ view?: View;
32
+ onChange?: (view: View) => void;
33
+ className?: string;
34
+ };
35
+
36
+ export const SplitterSelector = ({ view, onChange, className }: SplitterSelectorProps) => {
37
+ return (
38
+ <Toolbar.Root classNames={className}>
39
+ <ToggleGroup type='single' value={view} onValueChange={(value) => onChange?.(value as View)}>
40
+ <ToggleGroupItem value='editor'>
41
+ <Code className={getSize(5)} />
42
+ </ToggleGroupItem>
43
+ <ToggleGroupItem value='split'>
44
+ <SquareSplitHorizontal className={getSize(5)} />
45
+ </ToggleGroupItem>
46
+ <ToggleGroupItem value='preview'>
47
+ <Eye className={getSize(5)} />
48
+ </ToggleGroupItem>
49
+ </ToggleGroup>
50
+ </Toolbar.Root>
51
+ );
52
+ };
@@ -2,4 +2,4 @@
2
2
  // Copyright 2023 DXOS.org
3
3
  //
4
4
 
5
- export * from './Toolbar';
5
+ export * from './Splitter';
@@ -10,14 +10,23 @@ 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 { create, createDocAccessor, Filter, getMeta, getSpace, useQuery } from '@dxos/react-client/echo';
13
+ import {
14
+ create,
15
+ createDocAccessor,
16
+ Filter,
17
+ fullyQualifiedId,
18
+ getMeta,
19
+ getSpace,
20
+ useQuery,
21
+ } from '@dxos/react-client/echo';
14
22
  import { useIdentity } from '@dxos/react-client/halo';
15
- import { useTranslation, type ThemedClassName } from '@dxos/react-ui';
23
+ import { useTranslation } from '@dxos/react-ui';
24
+ import { useHasAttention } from '@dxos/react-ui-attention';
16
25
  import { createDataExtensions, listener } from '@dxos/react-ui-editor';
17
26
  import { mx } from '@dxos/react-ui-theme';
18
27
 
19
28
  import { DebugPanel } from './DebugPanel';
20
- import { type TemplateSelectProps, Toolbar, type ViewType } from './Toolbar';
29
+ import { Toolbar } from './Toolbar';
21
30
  import { TypescriptEditor, type TypescriptEditorProps } from './TypescriptEditor';
22
31
  import { Bundler } from '../bundler';
23
32
  import {
@@ -25,19 +34,18 @@ import {
25
34
  publicKeyToDid,
26
35
  setUserFunctionUrlInMetadata,
27
36
  uploadWorkerFunction,
28
- USERFUNCTIONS_PRESET_META_KEY,
29
37
  } from '../edge';
30
38
  import { SCRIPT_PLUGIN } from '../meta';
31
- import { templates } from '../templates';
32
39
  import { FunctionType, type ScriptType } from '../types';
33
40
 
34
- export type ScriptEditorProps = ThemedClassName<{
41
+ export type ScriptEditorProps = {
35
42
  script: ScriptType;
36
- }> &
37
- Pick<TypescriptEditorProps, 'env'>;
43
+ role?: string;
44
+ } & Pick<TypescriptEditorProps, 'env'>;
38
45
 
39
- export const ScriptEditor = ({ classNames, script, env }: ScriptEditorProps) => {
46
+ export const ScriptEditor = ({ env, script, role }: ScriptEditorProps) => {
40
47
  const { t } = useTranslation(SCRIPT_PLUGIN);
48
+ const hasAttention = useHasAttention(fullyQualifiedId(script));
41
49
  const client = useClient();
42
50
  const identity = useIdentity();
43
51
  const space = getSpace(script);
@@ -65,7 +73,7 @@ export const ScriptEditor = ({ classNames, script, env }: ScriptEditorProps) =>
65
73
  [script, script.source, space, identity],
66
74
  );
67
75
 
68
- const [view, setView] = useState<ViewType>('editor');
76
+ const [showPanel, setShowPanel] = useState(false);
69
77
  const initialValue = useMemo(() => script.source?.content, [script.source]);
70
78
  const existingFunctionUrl = fn && getUserFunctionUrlInMetadata(getMeta(fn));
71
79
  const [error, setError] = useState<string>();
@@ -95,22 +103,6 @@ export const ScriptEditor = ({ classNames, script, env }: ScriptEditorProps) =>
95
103
  }
96
104
  }, [script.source]);
97
105
 
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
-
114
106
  const handleDeploy = useCallback(async () => {
115
107
  if (!script.source || !identity || !space) {
116
108
  return;
@@ -122,7 +114,11 @@ export const ScriptEditor = ({ classNames, script, env }: ScriptEditorProps) =>
122
114
  const existingFunctionId = existingFunctionUrl?.split('/').at(-1);
123
115
  const ownerDid = (existingFunctionUrl?.split('/').at(-2) as DID) ?? publicKeyToDid(identity.identityKey);
124
116
 
125
- const bundler = new Bundler({ platform: 'browser', sandboxedModules: [], remoteModules: {} });
117
+ const bundler = new Bundler({
118
+ platform: 'browser',
119
+ sandboxedModules: [],
120
+ remoteModules: {},
121
+ });
126
122
  const buildResult = await bundler.bundle(script.source.content);
127
123
  if (buildResult.error || !buildResult.bundle) {
128
124
  throw buildResult.error;
@@ -138,18 +134,15 @@ export const ScriptEditor = ({ classNames, script, env }: ScriptEditorProps) =>
138
134
  if (result !== 'success' || functionId === undefined || functionVersionNumber === undefined) {
139
135
  throw new Error(errorMessage);
140
136
  }
141
-
142
137
  log.info('function uploaded', { functionId, functionVersionNumber });
143
138
  if (fn) {
144
139
  fn.version = functionVersionNumber;
145
140
  }
146
-
147
141
  const deployedFunction =
148
142
  fn ?? space.db.add(create(FunctionType, { version: functionVersionNumber, source: script }));
149
143
  const meta = getMeta(deployedFunction);
150
- script.changed = false;
151
144
  setUserFunctionUrlInMetadata(meta, `/${ownerDid}/${functionId}`);
152
- setView('split');
145
+ script.changed = false;
153
146
  } catch (err: any) {
154
147
  log.catch(err);
155
148
  setError(t('upload failed label'));
@@ -172,32 +165,38 @@ export const ScriptEditor = ({ classNames, script, env }: ScriptEditorProps) =>
172
165
  }, [existingFunctionUrl, space]);
173
166
 
174
167
  return (
175
- <div role='none' className={mx('flex flex-col w-full overflow-hidden divide-y divide-separator', classNames)}>
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
+
176
184
  <Toolbar
185
+ classNames='border-t border-separator'
177
186
  deployed={Boolean(existingFunctionUrl) && !script.changed}
178
187
  functionUrl={functionUrl}
179
188
  error={error}
180
- view={view}
181
- templates={templates}
189
+ showPanel={showPanel}
182
190
  onDeploy={handleDeploy}
183
191
  onFormat={handleFormat}
184
- onViewChange={setView}
185
- onTemplateSelect={handleTemplateChange}
192
+ onTogglePanel={async () => setShowPanel((showDetails) => !showDetails)}
186
193
  />
187
194
 
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' && (
195
+ {showPanel && (
199
196
  <DebugPanel functionUrl={functionUrl} binding={fn?.binding} onBindingChange={handleBindingChange} />
200
197
  )}
201
198
  </div>
202
199
  );
203
200
  };
201
+
202
+ export default ScriptEditor;